forked from ProcessMaker/processmaker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathScreenTemplateHelper.php
More file actions
405 lines (354 loc) · 17 KB
/
ScreenTemplateHelper.php
File metadata and controls
405 lines (354 loc) · 17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
<?php
namespace ProcessMaker\Helpers;
class ScreenTemplateHelper
{
/**
* Remove screen components from the configuration based on the provided components.
*
* This method iterates over each page in the configuration and filters out the items
* based on the provided components. It then returns the updated configuration with
* the filtered items removed.
*
* @param array $config The configuration containing pages with items.
* @param array $components The components to filter out from the configuration.
* @return array The updated configuration with filtered items removed.
*/
public static function removeScreenComponents($config, $components)
{
$updatedConfig = [];
foreach ($config as $page) {
$filteredPageItems = self::filterPageItems($page['items'] ?? [], $components);
$page['items'] = $filteredPageItems;
$updatedConfig[] = $page;
}
return $updatedConfig;
}
/**
* Filters and retrieves screen components from the provided configuration.
*
* This method processes the given screen configuration, iterating through each page
* to filter its items based on the specified components. It can optionally remove
* components from the configuration or keep only the specified components.
* @param array $config The full screen configuration
* @param array $components An array of component names that will be used to filter
* the items in the configuration.
* @param bool $removeComponents (optional) Determines the filtering behavior:
* - If 'true', the components in the `$components` array will be removed from the configuration
* - If 'false', only the components in the `$components` array will be retained
* Defaults to `true`.
*
* @return array The updated configuration after filtering the components.
*/
public static function getScreenComponents($config, $components, $removeComponents = true)
{
foreach ($config as $page) {
$filteredPageItems = self::filterPageItems($page['items'] ?? [], $components, $removeComponents);
$page['items'] = $filteredPageItems;
$updatedConfig[] = $page;
}
return $updatedConfig;
}
/**
* Filter items of a page based on the provided components.
*
* This method iterates over each item in the page and filters it based on the provided components.
* If an item passes the filter, it is added to the array of filtered items. If the filtered item
* is a nested array, it is flattened and merged with the array of filtered items.
*
* @param array $items The items of a page to filter.
* @param array $components The components to filter the items against.
* @param bool $removeComponents Whether to remove.
* @return array The filtered items of the page.
*/
private static function filterPageItems($items, $components, $removeComponents = true)
{
$filteredItems = [];
foreach ($items as $item) {
$filteredItem = self::filterItemByComponent($item, $components, $removeComponents);
if ($filteredItem !== null) {
if (is_array($filteredItem) && !isset($filteredItem['component'])) {
$filteredItems = array_merge($filteredItems, self::flattenNestedItems($filteredItem));
} else {
$filteredItems[] = $filteredItem;
}
}
}
return $filteredItems;
}
/**
* Filter an item based on its component type.
*
* This method checks the component type of the item and delegates the filtering process
* to a specific method if the component is 'FormMultiColumn'. If the item's component
* is not 'FormMultiColumn', it checks if the item needs to be removed based on the provided
* screen components. If the item doesn't need to be removed, it returns the item; otherwise,
* it returns null.
*
* @param array $item The item to filter.
* @param array $components The components to filter against.
* @param bool $removeComponents Whether to remove.
* @return array|null The filtered item or null if it should be removed.
*/
private static function filterItemByComponent($item, $components, $removeComponents = true)
{
if ($item['component'] === 'FormMultiColumn') {
return self::filterFormMultiColumn($item, $components, $removeComponents);
}
return !self::removeNestedComponents($item, $components, $removeComponents) ? $item : null;
}
/**
* Filter a 'FormMultiColumn' item based on its nested items and the provided screen components.
*
* This method determines whether the entire 'FormMultiColumn' item should be removed based on
* the provided screen components. If the 'FormMultiColumn' item should be removed, it returns
* an empty array. Otherwise, it filters the nested column items based on the provided screen
* components and returns the updated 'FormMultiColumn' item with filtered nested items.
*
* @param array $item The 'FormMultiColumn' item to filter.
* @param array $components The components to filter against.
* @param bool $removeComponents Whether to remove.
* @return array The filtered 'FormMultiColumn' item.
*/
private static function filterFormMultiColumn($item, $components, $removeComponents = true)
{
$removeMultiColumn = self::removeNestedComponents($item, $components, $removeComponents);
$filteredMultiColumnItems = $removeMultiColumn ? [] : $item;
foreach ($item['items'] as $index => $column) {
$filteredColumnItems = self::filterColumnItems($column, $components, $removeMultiColumn, $removeComponents);
if (isset($filteredMultiColumnItems['items'])) {
$filteredMultiColumnItems['items'][$index] = $filteredColumnItems;
} else {
$filteredMultiColumnItems[] = $filteredColumnItems;
}
}
return $filteredMultiColumnItems;
}
/**
* Check if the item should be removed based on the provided screen components.
*
* This method checks if the item's component is in the list of provided components.
* If the item's component is included in the provided screen components,
* it returns true indicating that the item should be removed. Otherwise, it returns false.
*
* @param array $item The item to check for removal.
* @param array $components The screen components to filter against.
* @param bool $removeComponents Whether to remove.
* @return bool Whether the item should be removed.
*/
private static function removeNestedComponents($item, $components, $removeComponents = true)
{
$componentList = ['BFormComponent', 'BWrapperComponent'];
if (in_array($item['component'], $componentList)) {
$bootstrapComponent = $item['config']['bootstrapComponent'] ?? null;
if ($bootstrapComponent && isset($components[$item['component']]['bootstrapComponent'])) {
return in_array($bootstrapComponent, $components[$item['component']]['bootstrapComponent']);
}
} else {
return in_array($item['component'], $components) && $removeComponents ||
!$removeComponents && !in_array($item['component'], $components);
}
return false;
}
/**
* Filter column items based on the provided components and whether to remove the entire 'FormMultiColumn'.
*
* This method iterates over each item in the column and filters it based on the provided components.
* If an item is a 'FormMultiColumn', it filters its nested columns recursively. If the entire 'FormMultiColumn'
* should be removed, it adds only the 'FormMultiColumn' item itself to the array of filtered column items.
* Otherwise, it adds the filtered item to the array of filtered column items. If an item should not be removed
* based on the provided components, it adds it directly to the array of filtered column items.
*
* @param array $column The column items to filter.
* @param array $components The components to filter against.
* @param bool $removeComponents Whether to remove.
* @param bool $removeMultiColumn Whether the entire 'FormMultiColumn' should be removed.
* @return array The filtered column items.
*/
private static function filterColumnItems($column, $components, $removeMultiColumn, $removeComponents = true)
{
$filteredColumnItems = [];
foreach ($column as $colItem) {
if (isset($colItem['component']) && $colItem['component'] === 'FormMultiColumn') {
self::filterNestedMultiColumns($colItem, $components, $removeMultiColumn);
$filteredColumnItems[] = $colItem;
} elseif (!self::removeNestedComponents($colItem, $components, $removeComponents)) {
$filteredColumnItems[] = $colItem;
}
}
return $filteredColumnItems;
}
/**
* Filter nested columns within a 'FormMultiColumn' item based on the provided components.
*
* This method filters the nested columns within a 'FormMultiColumn' item recursively
* based on the provided components and whether the entire 'FormMultiColumn' item should
* be removed. If the entire 'FormMultiColumn' item needs to be removed, it replaces it
* with the filtered nested columns. Otherwise, it updates the 'items' key of the 'FormMultiColumn'
* item with the filtered nested columns.
*
* @param array $item The 'FormMultiColumn' item containing nested columns to filter.
* @param array $components The components to filter against.
* @param bool $removeMultiColumn Whether the entire 'FormMultiColumn' should be removed.
*/
private static function filterNestedMultiColumns(&$item, $components, $removeMultiColumn)
{
$multiColumnItems = self::filterNestedColumns($item['items'], $components, $removeMultiColumn);
if ($removeMultiColumn) {
$item = $multiColumnItems;
} else {
$item['items'] = $multiColumnItems;
}
}
/**
* Filter nested columns based on the provided components and whether to remove the entire 'FormMultiColumn'.
*
* This method iterates over each column containing nested items and filters them based on the provided components.
* If an item within a column should be removed based on the provided components
* or whether the entire 'FormMultiColumn' should be removed, it adds it to the array of filtered columns.
* Otherwise, it adds the item to the array of filtered columns.
* It then returns the array of filtered columns containing filtered items.
*
* @param array $columns The columns containing nested items to filter.
* @param array $components The components to filter against.
* @param bool $removeMultiColumn Whether the entire 'FormMultiColumn' should be removed.
* @return array The filtered columns containing filtered items.
*/
private static function filterNestedColumns($columns, $components, $removeMultiColumn)
{
$filteredColumnItems = [];
foreach ($columns as $column) {
$filteredColumn = [];
foreach ($column as $columnItem) {
if (self::removeNestedComponents($columnItem, $components)) {
if ($columnItem['component'] === 'FormMultiColumn') {
self::filterNestedMultiColumns($columnItem, $components, $removeMultiColumn);
} elseif ($removeMultiColumn) {
$filteredColumn[] = $columnItem;
}
} elseif ($removeMultiColumn) {
$filteredColumn[] = $columnItem;
}
}
$filteredColumnItems[] = $filteredColumn;
}
return $filteredColumnItems;
}
/**
* Flatten nested items into a single-dimensional array.
*
* This method recursively flattens nested items within an array into a single-dimensional array.
* It iterates over each item in the array, and if the item itself is an array and does not have a 'component' key,
* indicating it's a nested array, it recursively calls itself to flatten the nested items.
* Otherwise, it adds the item directly to the flattenedItems array.
*
* @param array $items The array containing nested items to flatten.
* @return array The flattened single-dimensional array containing all items.
*/
private static function flattenNestedItems($items)
{
$flattenedItems = [];
foreach ($items as $item) {
if (is_array($item) && !isset($item['component'])) {
$flattenedItems = array_merge($flattenedItems, self::flattenNestedItems($item));
} else {
$flattenedItems[] = $item;
}
}
return $flattenedItems;
}
/**
* Parses a CSS string into an associative array of selectors and their properties.
*
* @param string $cssString The CSS string to parse.
* @return array An associative array where keys are CSS selectors and values are arrays of properties.
*/
public static function parseCss($cssString)
{
$rules = [];
// Regex to match CSS rules, allowing for comments
preg_match_all('/([^{]+)\s*\{([^}]*)\}/s', $cssString, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$fullSelector = trim($match[1]); // Full CSS selector
$propertiesString = trim($match[2]); // Properties between the brackets
$properties = self::parseProperties($propertiesString);
// Only add to rules if selector and properties are non-empty
if (!empty($fullSelector) && !empty($properties)) {
$rules[$fullSelector] = $properties;
}
}
return $rules;
}
/**
* Parses a string of CSS properties and returns an associative array of property-value pairs.
*
* @param string $propertiesString The string of CSS properties to parse.
* @return array An associative array of properties.
*/
private static function parseProperties($propertiesString)
{
$properties = [];
// Split properties into individual declarations, capturing inline comments
preg_match_all('/([^;]+;)(?:\s*\/\*.*?\*\/)?/s', $propertiesString, $propertyMatches);
foreach ($propertyMatches[0] as $property) {
$property = trim($property);
$keyValue = self::extractKeyValue($property);
if ($keyValue) {
list($key, $value) = $keyValue;
// Only add to properties if both key and value are non-empty
if (!empty($key) && !empty($value)) {
$properties[$key] = $value; // Add key-value pair
}
}
}
return $properties;
}
/**
* Extracts the key and value from a CSS property string.
*
* @param string $property The CSS property string to extract key-value from.
* @return array|null An array containing the key and value, or null if not valid.
*/
private static function extractKeyValue($property)
{
if (preg_match('/(.*?)(\/\*.*?\*\/)?$/s', $property, $parts)) {
$keyValue = explode(':', $parts[1], 2);
if (count($keyValue) == 2) {
$key = trim($keyValue[0]);
$value = trim(rtrim($keyValue[1], ';')); // Trim the trailing semicolon
// Combine value with inline comment if present
if (!empty($parts[2])) {
$value .= ' ' . trim($parts[2]);
}
return [$key, $value]; // Return key and value as an array
}
}
return null; // Return null if the property is not valid
}
// Merge the two CSS arrays
public static function mergeCss($currentCss, $templateCss)
{
foreach ($templateCss as $selector => $properties) {
if (isset($currentCss[$selector])) {
// Merge properties from Template CSS into the Current Screen CSS for the same selector
$currentCss[$selector] = array_merge($currentCss[$selector], $properties);
} else {
// Add new selector and properties from Template CSS
$currentCss[$selector] = $properties;
}
}
return $currentCss;
}
public static function generateCss($cssArray)
{
// Convert the CSS array back into a string and output the final CSS
$cssString = '';
foreach ($cssArray as $selector => $properties) {
$cssString .= "$selector {\n";
foreach ($properties as $key => $value) {
$cssString .= " $key: $value;\n";
}
$cssString .= "}\n\n";
}
return $cssString;
}
}