@rglabs/butterfly 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CLAUDE.md +201 -0
  2. package/README.md +371 -0
  3. package/dist/commands/add.d.ts +23 -0
  4. package/dist/commands/add.js +303 -0
  5. package/dist/commands/code.d.ts +11 -0
  6. package/dist/commands/code.js +72 -0
  7. package/dist/commands/create-object.d.ts +6 -0
  8. package/dist/commands/create-object.js +293 -0
  9. package/dist/commands/create-report.d.ts +6 -0
  10. package/dist/commands/create-report.js +154 -0
  11. package/dist/commands/diff.d.ts +4 -0
  12. package/dist/commands/diff.js +238 -0
  13. package/dist/commands/download.d.ts +4 -0
  14. package/dist/commands/download.js +374 -0
  15. package/dist/commands/layout.d.ts +12 -0
  16. package/dist/commands/layout.js +83 -0
  17. package/dist/commands/record.d.ts +21 -0
  18. package/dist/commands/record.js +483 -0
  19. package/dist/commands/run-poc.d.ts +3 -0
  20. package/dist/commands/run-poc.js +18 -0
  21. package/dist/commands/setup.d.ts +3 -0
  22. package/dist/commands/setup.js +66 -0
  23. package/dist/commands/start-poc.d.ts +3 -0
  24. package/dist/commands/start-poc.js +55 -0
  25. package/dist/commands/sync-docs.d.ts +3 -0
  26. package/dist/commands/sync-docs.js +27 -0
  27. package/dist/commands/translate.d.ts +13 -0
  28. package/dist/commands/translate.js +401 -0
  29. package/dist/commands/upload.d.ts +3 -0
  30. package/dist/commands/upload.js +150 -0
  31. package/dist/commands/workflow-info.d.ts +13 -0
  32. package/dist/commands/workflow-info.js +161 -0
  33. package/dist/components/ConflictResolver.d.ts +12 -0
  34. package/dist/components/ConflictResolver.js +77 -0
  35. package/dist/components/DiffView.d.ts +11 -0
  36. package/dist/components/DiffView.js +101 -0
  37. package/dist/components/DownloadProgress.d.ts +11 -0
  38. package/dist/components/DownloadProgress.js +29 -0
  39. package/dist/components/RecordPreview.d.ts +11 -0
  40. package/dist/components/RecordPreview.js +91 -0
  41. package/dist/components/SetupForm.d.ts +8 -0
  42. package/dist/components/SetupForm.js +56 -0
  43. package/dist/components/UploadProgress.d.ts +13 -0
  44. package/dist/components/UploadProgress.js +42 -0
  45. package/dist/diff/adapters/index.d.ts +8 -0
  46. package/dist/diff/adapters/index.js +18 -0
  47. package/dist/diff/adapters/objectsAdapter.d.ts +13 -0
  48. package/dist/diff/adapters/objectsAdapter.js +177 -0
  49. package/dist/diff/adapters/reportsAdapter.d.ts +14 -0
  50. package/dist/diff/adapters/reportsAdapter.js +212 -0
  51. package/dist/diff/adapters/types.d.ts +19 -0
  52. package/dist/diff/adapters/types.js +2 -0
  53. package/dist/diff/engine.d.ts +19 -0
  54. package/dist/diff/engine.js +57 -0
  55. package/dist/diff/types.d.ts +34 -0
  56. package/dist/diff/types.js +110 -0
  57. package/dist/index.d.ts +3 -0
  58. package/dist/index.js +117 -0
  59. package/dist/types/index.d.ts +18 -0
  60. package/dist/types/index.js +2 -0
  61. package/dist/utils/api.d.ts +85 -0
  62. package/dist/utils/api.js +1031 -0
  63. package/dist/utils/auth.d.ts +4 -0
  64. package/dist/utils/auth.js +22 -0
  65. package/dist/utils/bfySplitter.d.ts +12 -0
  66. package/dist/utils/bfySplitter.js +151 -0
  67. package/dist/utils/docs.d.ts +16 -0
  68. package/dist/utils/docs.js +186 -0
  69. package/dist/utils/errorLogger.d.ts +6 -0
  70. package/dist/utils/errorLogger.js +29 -0
  71. package/dist/utils/files.d.ts +14 -0
  72. package/dist/utils/files.js +772 -0
  73. package/dist/utils/lockManager.d.ts +15 -0
  74. package/dist/utils/lockManager.js +126 -0
  75. package/dist/utils/resourceHandlers.d.ts +50 -0
  76. package/dist/utils/resourceHandlers.js +684 -0
  77. package/dist/utils/resourceMapping.d.ts +32 -0
  78. package/dist/utils/resourceMapping.js +210 -0
  79. package/dist/utils/singleResourceDownload.d.ts +14 -0
  80. package/dist/utils/singleResourceDownload.js +261 -0
  81. package/dist/utils/summaryGenerator.d.ts +2 -0
  82. package/dist/utils/summaryGenerator.js +183 -0
  83. package/dist/utils/uploadHandler.d.ts +31 -0
  84. package/dist/utils/uploadHandler.js +263 -0
  85. package/docs/AI_API.md +93 -0
  86. package/docs/CLAUDE.md +216 -0
  87. package/docs/PROJECT_SPECIFIC.md +1 -0
  88. package/docs/RECORD_COMMAND.md +262 -0
  89. package/docs/WORKFLOW_API.md +480 -0
  90. package/docs/bfy-splitting.md +126 -0
  91. package/docs/cli-commands.md +333 -0
  92. package/docs/examples/README.md +95 -0
  93. package/docs/examples/order-system.md +147 -0
  94. package/docs/examples/product-catalog.md +195 -0
  95. package/docs/examples/reports.md +187 -0
  96. package/docs/excel-export.md +216 -0
  97. package/docs/field-types/README.md +29 -0
  98. package/docs/field-types/calculated.md +147 -0
  99. package/docs/field-types/code-mappings.md +84 -0
  100. package/docs/field-types/custom.md +340 -0
  101. package/docs/object-specs/README.md +136 -0
  102. package/docs/object-specs/code-parameters.md +151 -0
  103. package/docs/object-specs/creating.md +203 -0
  104. package/docs/object-specs/js-code-examples.md +208 -0
  105. package/docs/object-specs/js-field-updates.md +168 -0
  106. package/docs/objects/README.md +89 -0
  107. package/docs/objects/creating.md +127 -0
  108. package/docs/page-layout.md +361 -0
  109. package/docs/permissions.md +260 -0
  110. package/docs/reports.md +197 -0
  111. package/docs/state-machines.md +544 -0
  112. package/docs/tasks/create-object.md +81 -0
  113. package/docs/translations.md +346 -0
  114. package/docs/twig-helpers.md +283 -0
  115. package/docs/webservices.md +159 -0
  116. package/docs/workspaces.md +176 -0
  117. package/package.json +59 -0
@@ -0,0 +1,361 @@
1
+ # Object Page Layout
2
+
3
+ This document explains how to organize object add/edit page layouts using tabs and sections.
4
+
5
+ ## Overview
6
+
7
+ Object pages can be organized into groups (tabs) to better structure fields in the add/edit forms. Despite being called "tabs", these are actually visual groups within the page that organize related fields together.
8
+
9
+ ## Tables Involved
10
+
11
+ | Table | Purpose |
12
+ |-------|---------|
13
+ | `object_tabs` | Defines groups/tabs for organizing fields |
14
+ | `object_sections` | Defines sections (higher-level grouping) |
15
+ | `object_specs` | Fields assigned to tabs via `edit_place_no` |
16
+
17
+ ## Object Tabs
18
+
19
+ ### Table: `object_tabs`
20
+
21
+ | Column | Type | Required | Description |
22
+ |--------|------|----------|-------------|
23
+ | `object_id` | int | Yes | The parent object ID |
24
+ | `title` | string | Yes | Display title of the tab/group |
25
+ | `position` | int | No | `0` = Left, `1` = Right |
26
+ | `object_section_id` | int | No | Parent section ID (`0` for no section) |
27
+
28
+ ### Creating a Tab
29
+
30
+ Use the API to create a new tab:
31
+
32
+ ```
33
+ POST /admin/ajax/cms_object/operation?do=object_tabs__add
34
+ Content-Type: application/x-www-form-urlencoded
35
+
36
+ object_id=<OBJECT_ID>&title=<TAB_TITLE>&position=0&object_section_id=0
37
+ ```
38
+
39
+ > **Important:** Set `object_section_id=0` when creating tabs without a parent section.
40
+
41
+ **Example:**
42
+ ```
43
+ POST /admin/ajax/cms_object/operation?do=object_tabs__add
44
+ Content-Type: application/x-www-form-urlencoded
45
+
46
+ object_id=128&title=Basic Information&position=0&object_section_id=0
47
+ ```
48
+
49
+ ### Editing a Tab
50
+
51
+ ```
52
+ POST /admin/ajax/cms_object/operation?do=object_tabs__edit
53
+ Content-Type: application/x-www-form-urlencoded
54
+
55
+ id=<TAB_ID>&title=<NEW_TITLE>&position=<POSITION>
56
+ ```
57
+
58
+ ## Assigning Fields to Tabs
59
+
60
+ Fields (`object_specs`) are assigned to tabs using the `edit_place_no` column, which references the `object_tabs.id`.
61
+
62
+ ### Object Specs Tab Assignment
63
+
64
+ | Column | Type | Description |
65
+ |--------|------|-------------|
66
+ | `edit_place_no` | int | References `object_tabs.id` - assigns field to a tab |
67
+ | `edit_position` | int | `0` = Left column, `1` = Right column (within the tab) |
68
+ | `edit_order_no` | int | Order of the field within its position |
69
+
70
+ ### Assigning a Field to a Tab
71
+
72
+ To assign an existing field to a tab, update the `edit_place_no`:
73
+
74
+ ```
75
+ POST /admin/ajax/cms_object/operation?do=object_specs__edit
76
+ Content-Type: application/x-www-form-urlencoded
77
+
78
+ id=<SPEC_ID>&edit_place_no=<TAB_ID>
79
+ ```
80
+
81
+ **Example:**
82
+ ```
83
+ POST /admin/ajax/cms_object/operation?do=object_specs__edit
84
+ Content-Type: application/x-www-form-urlencoded
85
+
86
+ id=1071&edit_place_no=5
87
+ ```
88
+
89
+ ## Object Sections (Optional)
90
+
91
+ Sections provide a higher-level grouping above tabs.
92
+
93
+ ### Table: `object_sections`
94
+
95
+ | Column | Type | Required | Description |
96
+ |--------|------|----------|-------------|
97
+ | `object_id` | int | No | Parent object ID |
98
+ | `title` | string | No | Section display title |
99
+ | `slug` | string | No | URL-friendly identifier |
100
+
101
+ ## Layout Example
102
+
103
+ ```
104
+ Object Edit Page
105
+ ├── Section: "Product Details" (object_sections)
106
+ │ ├── Tab: "Basic Info" (object_tabs, position=0)
107
+ │ │ ├── Left Column (edit_position=0)
108
+ │ │ │ ├── Name (edit_order_no=1)
109
+ │ │ │ └── SKU (edit_order_no=2)
110
+ │ │ └── Right Column (edit_position=1)
111
+ │ │ ├── Price (edit_order_no=1)
112
+ │ │ └── Stock (edit_order_no=2)
113
+ │ └── Tab: "Description" (object_tabs, position=0)
114
+ │ └── Full Description (edit_order_no=1)
115
+ └── Tab: "Settings" (object_tabs, position=1)
116
+ └── Is Active (edit_order_no=1)
117
+ ```
118
+
119
+ ## Workflow
120
+
121
+ ### 1. Create Tabs for an Object
122
+
123
+ First, identify the object ID and create the tabs needed:
124
+
125
+ ```
126
+ # Create "Basic Information" tab
127
+ POST /admin/ajax/cms_object/operation?do=object_tabs__add
128
+ object_id=128&title=Basic Information&position=0&object_section_id=0
129
+
130
+ # Create "Additional Details" tab
131
+ POST /admin/ajax/cms_object/operation?do=object_tabs__add
132
+ object_id=128&title=Additional Details&position=0&object_section_id=0
133
+
134
+ # Create "Settings" tab (right side)
135
+ POST /admin/ajax/cms_object/operation?do=object_tabs__add
136
+ object_id=128&title=Settings&position=1&object_section_id=0
137
+ ```
138
+
139
+ ### 2. Assign Fields to Tabs
140
+
141
+ Update each field's `edit_place_no` to assign it to the appropriate tab:
142
+
143
+ ```
144
+ # Assign "name" field (spec_id=1071) to "Basic Information" tab (tab_id=5)
145
+ POST /admin/ajax/cms_object/operation?do=object_specs__edit
146
+ id=1071&edit_place_no=5
147
+
148
+ # Assign "description" field (spec_id=1072) to "Additional Details" tab (tab_id=6)
149
+ POST /admin/ajax/cms_object/operation?do=object_specs__edit
150
+ id=1072&edit_place_no=6
151
+ ```
152
+
153
+ ### 3. Update Tab and Field Order (Bulk)
154
+
155
+ After creating tabs and assigning fields, use the CLI or API to set the complete layout order.
156
+
157
+ #### Using CLI (Recommended)
158
+
159
+ ```bash
160
+ # From a JSON file
161
+ butterfly-cli layout -f layout.json
162
+
163
+ # Inline JSON data
164
+ butterfly-cli layout -d '[{"id":0,"tabs":[{"id":0,"specs":[{"id":1323,"order_no":1}],"order_no":1}]}]'
165
+ ```
166
+
167
+ #### Using API
168
+
169
+ ```
170
+ POST /admin/ajax/cms_object_specs/edit_order
171
+ Content-Type: application/json
172
+
173
+ [
174
+ {
175
+ "id": 0,
176
+ "tabs": [
177
+ {
178
+ "id": 0,
179
+ "specs": [
180
+ {"id": 1323, "order_no": 1},
181
+ {"id": 1329, "order_no": 2},
182
+ {"id": 1331, "order_no": 3}
183
+ ],
184
+ "order_no": 1
185
+ },
186
+ {
187
+ "id": 159,
188
+ "specs": [
189
+ {"id": 1328, "order_no": 10}
190
+ ],
191
+ "order_no": 2
192
+ }
193
+ ]
194
+ },
195
+ {
196
+ "id": 1,
197
+ "tabs": [
198
+ {
199
+ "id": -1,
200
+ "specs": [],
201
+ "order_no": 4
202
+ },
203
+ {
204
+ "id": 160,
205
+ "specs": [],
206
+ "order_no": 5
207
+ }
208
+ ]
209
+ }
210
+ ]
211
+ ```
212
+
213
+ **Structure explanation:**
214
+
215
+ | Level | Field | Description |
216
+ |-------|-------|-------------|
217
+ | Root array | `id` | Position: `0` = Left, `1` = Right |
218
+ | Root array | `tabs` | Array of tabs in this position |
219
+ | Tab | `id` | Tab ID (`0` = default/no tab, `-1` = empty placeholder) |
220
+ | Tab | `order_no` | Order of the tab within the position |
221
+ | Tab | `specs` | Array of specs (fields) in this tab |
222
+ | Spec | `id` | Spec ID (from `object_specs.id`) |
223
+ | Spec | `order_no` | Order of the field within the tab |
224
+
225
+ > **Note:** This endpoint updates all tab orders and field orders in a single request. Use this after creating tabs to organize the complete page layout.
226
+
227
+ ### 4. Set Field Order (Individual)
228
+
229
+ Alternatively, control the order of individual fields using `edit_order_no`:
230
+
231
+ ```
232
+ POST /admin/ajax/cms_object/operation?do=object_specs__edit
233
+ id=1071&edit_place_no=5&edit_order_no=1&edit_position=0
234
+
235
+ POST /admin/ajax/cms_object/operation?do=object_specs__edit
236
+ id=1073&edit_place_no=5&edit_order_no=2&edit_position=0
237
+ ```
238
+
239
+ ## Field Positioning Summary
240
+
241
+ | Property | Values | Description |
242
+ |----------|--------|-------------|
243
+ | `edit_place_no` | Tab ID | Which tab the field belongs to |
244
+ | `edit_position` | `0` (left), `1` (right) | Column within the tab |
245
+ | `edit_order_no` | Integer | Order within the column |
246
+ | `column_size` | Size value | Field width (see below) |
247
+ | `section_title` | String | Visual section header above field |
248
+
249
+ ## Column Sizes
250
+
251
+ ### Object Left Column Size (`left_column_size`)
252
+
253
+ Controls the width of the left column area in the object edit page. Set on the `objects` table.
254
+
255
+ | Value | Description |
256
+ |-------|-------------|
257
+ | `null` | Default width |
258
+ | `"1"` | Full width |
259
+ | `"1/3"` | One-third width |
260
+ | `"1/4"` | Quarter width |
261
+
262
+ **Example:**
263
+ ```
264
+ POST /admin/ajax/cms_object/operation?do=objects__edit
265
+ Content-Type: application/x-www-form-urlencoded
266
+
267
+ id=128&left_column_size=1/4
268
+ ```
269
+
270
+ ### Field Column Size (`column_size`)
271
+
272
+ Controls the width of individual fields within their container. Set on `object_specs` table.
273
+
274
+ | Value | Description |
275
+ |-------|-------------|
276
+ | `null` or `""` | Default width (auto) |
277
+ | `"1"` | Full width (100%) |
278
+ | `"1/2"` | Half width (50%) |
279
+ | `"1/3"` | One-third width (33%) |
280
+ | `"1/4"` | Quarter width (25%) |
281
+
282
+ **Example:**
283
+ ```
284
+ POST /admin/ajax/cms_object/operation?do=object_specs__edit
285
+ Content-Type: application/x-www-form-urlencoded
286
+
287
+ id=1071&column_size=1/2
288
+ ```
289
+
290
+ ### Visual Layout Example
291
+
292
+ ```
293
+ ┌─────────────────────────────────────────────────────────────┐
294
+ │ Object Edit Page (left_column_size=1/4) │
295
+ ├───────────────┬─────────────────────────────────────────────┤
296
+ │ Left Column │ Main Content Area │
297
+ │ (1/4 width) │ │
298
+ │ │ ┌─────────────────┬─────────────────┐ │
299
+ │ - Status │ │ Name (1/2) │ SKU (1/2) │ │
300
+ │ - Category │ ├─────────────────┴─────────────────┤ │
301
+ │ │ │ Description (1) │ │
302
+ │ │ ├───────────┬───────────┬───────────┤ │
303
+ │ │ │ Price │ Stock │ Weight │ │
304
+ │ │ │ (1/3) │ (1/3) │ (1/3) │ │
305
+ │ │ └───────────┴───────────┴───────────┘ │
306
+ └───────────────┴─────────────────────────────────────────────┘
307
+ ```
308
+
309
+ ## CLI Commands
310
+
311
+ ### Layout Command
312
+
313
+ Update object page layout (tab and field ordering) using the `layout` command:
314
+
315
+ ```bash
316
+ butterfly-cli layout [options]
317
+ ```
318
+
319
+ **Options:**
320
+ - `-f, --file <path>` - Path to JSON file with layout data
321
+ - `-d, --data <json>` - Inline JSON layout data
322
+
323
+ **Examples:**
324
+
325
+ ```bash
326
+ # Update layout from a JSON file
327
+ butterfly-cli layout -f layout.json
328
+
329
+ # Update layout with inline JSON
330
+ butterfly-cli layout -d '[{"id":0,"tabs":[{"id":0,"specs":[{"id":1323,"order_no":1},{"id":1329,"order_no":2}],"order_no":1}]},{"id":1,"tabs":[]}]'
331
+ ```
332
+
333
+ **Layout JSON Structure:**
334
+
335
+ ```json
336
+ [
337
+ {
338
+ "id": 0,
339
+ "tabs": [
340
+ {
341
+ "id": 159,
342
+ "specs": [
343
+ {"id": 1323, "order_no": 1},
344
+ {"id": 1329, "order_no": 2}
345
+ ],
346
+ "order_no": 1
347
+ }
348
+ ]
349
+ },
350
+ {
351
+ "id": 1,
352
+ "tabs": []
353
+ }
354
+ ]
355
+ ```
356
+
357
+ ## Related
358
+
359
+ - [Object Specs Reference](./object-specs/README.md)
360
+ - [Objects Reference](./objects/README.md)
361
+ - [Creating Object Specs](./object-specs/creating.md)
@@ -0,0 +1,260 @@
1
+ # Object Permissions
2
+
3
+ This guide covers how to configure default permissions for objects and manage user group permissions.
4
+
5
+ ## Object Default Permissions
6
+
7
+ Objects have a `usergroup_id` field that sets the default (maximum possible) permissions for that object across the entire system.
8
+
9
+ ### Setting Default Permissions on an Object
10
+
11
+ To assign a user group as the default permission template for an object:
12
+
13
+ ```bash
14
+ butterfly-cli record edit objects --id <object_id> --data '{"usergroup_id": <usergroup_id>}'
15
+ ```
16
+
17
+ **What this does:**
18
+ - Sets the maximum possible permissions that any user can have for this object
19
+ - Acts as a system-wide permission ceiling
20
+ - Individual user/group permissions cannot exceed this default
21
+
22
+ > **Important:** If `usergroup_id` is set on an object but no matching permission record exists in `object_permissions`, users will receive a **404 error**. Always create corresponding permission records after setting the object's `usergroup_id`.
23
+
24
+ ### Example
25
+
26
+ ```bash
27
+ # Set user group 5 as the default permission template for object 123
28
+ butterfly-cli record edit objects --id 123 --data '{"usergroup_id": 5}'
29
+ ```
30
+
31
+ ## User Groups
32
+
33
+ User groups are the main permission system in Butterfly.
34
+
35
+ ### LDAP Integration
36
+
37
+ If LDAP integration is enabled, user groups are automatically synced for users through the `ldap_id` column in the `usergroups` table.
38
+
39
+ ## User Permission Assignment
40
+
41
+ Users are assigned to groups via two columns in the `users` table:
42
+
43
+ | Column | Description |
44
+ |--------|-------------|
45
+ | `role_id` | Main user group ID (references `usergroups.id`) |
46
+ | `usergroup_ids` | Comma-separated list of additional group IDs |
47
+
48
+ ### How Permissions Work
49
+
50
+ - `role_id` is the user's primary group but has **no additional power** over groups in `usergroup_ids`
51
+ - All groups (primary + additional) contribute equally to the user's permissions
52
+ - **Exception:** `role_id = 1` grants access to everything (superadmin)
53
+ - **Important:** Even superadmin (`role_id = 1`) is limited by the `usergroup_id` set on an object, which defines the maximum possible permissions
54
+
55
+ ### Example: Assigning User to Groups
56
+
57
+ ```bash
58
+ # Set primary group and additional groups for a user
59
+ butterfly-cli record edit users --id <user_id> --data '{"role_id": 5, "usergroup_ids": "3,7,12"}'
60
+ ```
61
+
62
+ ### Permission Hierarchy
63
+
64
+ ```
65
+ Object's usergroup_id (ceiling)
66
+
67
+ Sets maximum possible permissions
68
+
69
+ User's role_id + usergroup_ids (combined)
70
+
71
+ Actual permissions = intersection of user groups and object ceiling
72
+ ```
73
+
74
+ ## Permission Levels
75
+
76
+ Butterfly has two levels of permissions:
77
+
78
+ ### Level 1: Object-Based Generic Permissions
79
+
80
+ The `object_permissions` table defines generic CRUD permissions for a user group on an object.
81
+
82
+ | Column | Type | Description |
83
+ |--------|------|-------------|
84
+ | `object_id` | int | Target object ID |
85
+ | `usergroup_id` | int | User group ID |
86
+ | `add_permission` | bool | Can add records |
87
+ | `edit_permission` | bool | Can edit records |
88
+ | `delete_permission` | bool | Can delete records |
89
+ | `approve_permission` | bool | Can approve records |
90
+ | `view_permission` | bool | Can view records |
91
+ | `object_spec_ids` | string | Comma-separated field IDs (empty = all fields) |
92
+
93
+ **Field-Level Access (`object_spec_ids`):**
94
+ - If empty: User has access to all fields
95
+ - If defined: User can only see/access the specified field IDs
96
+
97
+ #### Example: Create Object Permission
98
+
99
+ ```bash
100
+ # Grant view and edit permissions to user group 5 for object 123
101
+ butterfly-cli record add object_permissions --data '{
102
+ "object_id": 123,
103
+ "usergroup_id": 5,
104
+ "add_permission": 0,
105
+ "edit_permission": 1,
106
+ "delete_permission": 0,
107
+ "approve_permission": 0,
108
+ "view_permission": 1
109
+ }'
110
+
111
+ # Grant access to only specific fields (IDs 45, 46, 47)
112
+ butterfly-cli record add object_permissions --data '{
113
+ "object_id": 123,
114
+ "usergroup_id": 5,
115
+ "view_permission": 1,
116
+ "object_spec_ids": "45,46,47"
117
+ }'
118
+ ```
119
+
120
+ ### Level 2: Permission Exceptions
121
+
122
+ The `cms_permission_exceptions` table provides granular control over field and record-level permissions.
123
+
124
+ **Common Fields:**
125
+
126
+ | Column | Description |
127
+ |--------|-------------|
128
+ | `usergroup_id` | User group ID |
129
+ | `object_id` | Target object ID |
130
+ | `permission_type` | Type of exception: `default_value`, `fields`, or `condition` |
131
+ | `description` | Description of the exception |
132
+
133
+ > **Note:** `permission_type` is a `from_list` field. Always use the string keys (`default_value`, `fields`, `condition`) in CLI commands.
134
+
135
+ ---
136
+
137
+ #### Default Value (`permission_type: "default_value"`)
138
+
139
+ Forces a field value on add and prevents editing.
140
+
141
+ | Column | Description |
142
+ |--------|-------------|
143
+ | `object_spec_id` | Field ID to control |
144
+ | `default_value` | Value to force |
145
+
146
+ **Behavior:**
147
+ - On **add**: Field is set to `default_value` automatically
148
+ - On **edit**: Field becomes read-only (cannot be changed)
149
+
150
+ ```bash
151
+ # Force status field (ID 50) to "pending" for user group 5
152
+ butterfly-cli record add cms_permission_exceptions --data '{
153
+ "usergroup_id": 5,
154
+ "object_id": 123,
155
+ "permission_type": "default_value",
156
+ "object_spec_id": 50,
157
+ "default_value": "pending",
158
+ "description": "Force pending status for this group"
159
+ }'
160
+ ```
161
+
162
+ ---
163
+
164
+ #### Field Restriction (`permission_type: "fields"`)
165
+
166
+ Field-based restrictions for hiding or making fields read-only.
167
+
168
+ | Column | Description |
169
+ |--------|-------------|
170
+ | `restriction_type` | `only_fields` (whitelist), `hide_fields` (blacklist), or `readonly` |
171
+ | `restriction_condition` | Twig condition - call `{{ valid() }}` to enable exception |
172
+ | `object_specs` | Comma-separated field IDs the rule applies to |
173
+
174
+ **Restriction Types:**
175
+
176
+ | Value | Description |
177
+ |-------|-------------|
178
+ | `only_fields` | Whitelist - show ONLY these fields |
179
+ | `hide_fields` | Blacklist - hide these specific fields |
180
+ | `readonly` | Make these fields read-only |
181
+
182
+ **Condition:**
183
+ - Has access to `{{ info.COLUMN_NAME }}` for current record data
184
+ - Call `{{ valid() }}` to activate the exception
185
+ - If `valid()` is not called, the exception does not apply
186
+
187
+ ```bash
188
+ # Hide fields 45,46 when status is "closed"
189
+ butterfly-cli record add cms_permission_exceptions --data '{
190
+ "usergroup_id": 5,
191
+ "object_id": 123,
192
+ "permission_type": "fields",
193
+ "restriction_type": "hide_fields",
194
+ "restriction_condition": "{% if info.status == \"closed\" %}{{ valid() }}{% endif %}",
195
+ "object_specs": "45,46",
196
+ "description": "Hide sensitive fields when closed"
197
+ }'
198
+
199
+ # Make all fields read-only except 10,11,12
200
+ butterfly-cli record add cms_permission_exceptions --data '{
201
+ "usergroup_id": 5,
202
+ "object_id": 123,
203
+ "permission_type": "fields",
204
+ "restriction_type": "only_fields",
205
+ "object_specs": "10,11,12",
206
+ "description": "Only allow editing these fields"
207
+ }'
208
+ ```
209
+
210
+ ---
211
+
212
+ #### Record Condition (`permission_type: "condition"`)
213
+
214
+ Record-based restriction that filters which records users can see/edit/delete.
215
+
216
+ | Column | Description |
217
+ |--------|-------------|
218
+ | `object_spec_id` | Field ID to filter on |
219
+ | `condition_value` | Value(s) to match (comma-separated). Supports Twig with `{{ info.XXX }}` |
220
+
221
+ **Behavior:**
222
+ - Filters listing, edit, and delete actions
223
+ - Only records matching the condition are accessible
224
+ - `condition_value` can be static or dynamic (Twig)
225
+
226
+ ```bash
227
+ # User group 5 can only see records where department_id = 3
228
+ butterfly-cli record add cms_permission_exceptions --data '{
229
+ "usergroup_id": 5,
230
+ "object_id": 123,
231
+ "permission_type": "condition",
232
+ "object_spec_id": 55,
233
+ "condition_value": "3",
234
+ "description": "Restrict to department 3"
235
+ }'
236
+
237
+ # User can only see their own records (using current user ID)
238
+ butterfly-cli record add cms_permission_exceptions --data '{
239
+ "usergroup_id": 5,
240
+ "object_id": 123,
241
+ "permission_type": "condition",
242
+ "object_spec_id": 60,
243
+ "condition_value": "{{ info.created_by }}",
244
+ "description": "Users see only their own records"
245
+ }'
246
+
247
+ # Allow multiple values
248
+ butterfly-cli record add cms_permission_exceptions --data '{
249
+ "usergroup_id": 5,
250
+ "object_id": 123,
251
+ "permission_type": "condition",
252
+ "object_spec_id": 55,
253
+ "condition_value": "1,2,3",
254
+ "description": "Restrict to departments 1, 2, or 3"
255
+ }'
256
+ ```
257
+
258
+ ## See Also
259
+
260
+ - [docs/CLAUDE.md](CLAUDE.md) - Quick reference