Test Failed
Push — develop ( db5b9c...b55dd4 )
by Paul
17:52
created

ElementControlsTrait::bdContentControlPaths()   B

Complexity

Conditions 8
Paths 20

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 27
rs 8.4444
cc 8
nc 20
nop 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Integrations\Breakdance;
4
5
use GeminiLabs\SiteReviews\Arguments;
6
use GeminiLabs\SiteReviews\Contracts\ShortcodeContract;
7
use GeminiLabs\SiteReviews\Helper;
8
use GeminiLabs\SiteReviews\Helpers\Arr;
9
use GeminiLabs\SiteReviews\Integrations\Breakdance\Defaults\ControlDefaults;
10
use GeminiLabs\SiteReviews\Integrations\Breakdance\Defaults\SectionDefaults;
11
12
use function Breakdance\Elements\c;
0 ignored issues
show
introduced by
The function Breakdance\Elements\c was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
13
14
trait ElementControlsTrait
15
{
16
    public static function bdSectionGroups(): array
17
    {
18
        $config = static::bdShortcode()->settings();
19
        $groups = [ // order is intentional
20
            'rating_group' => glsr(SectionDefaults::class)->merge([
21
                'children' => [
22
                    static::bdControl([
23
                        'slug' => 'rating_field_notice',
24
                        'options' => [
25
                            'alertBoxOptions' => [
26
                                'style' => 'info',
27
                                'content' => $config['rating_field']['description'] ?? '',
28
                            ],
29
                            'layout' => 'vertical',
30
                            'type' => 'alert_box',
31
                        ],
32
                    ]),
33
                ],
34
                'fields' => ['rating', 'rating_field'],
35
                'label' => esc_html_x('Rating Options', 'admin-text', 'site-reviews'),
36
                'options' => [
37
                    'sectionOptions' => [
38
                        'type' => 'popout',
39
                    ],
40
                    'type' => 'section',
41
                ],
42
            ]),
43
            'pagination_group' => glsr(SectionDefaults::class)->merge([
44
                'fields' => ['display', 'pagination'],
45
                'label' => esc_html_x('Pagination Options', 'admin-text', 'site-reviews'),
46
                'options' => [
47
                    'sectionOptions' => [
48
                        'type' => 'popout',
49
                    ],
50
                    'type' => 'section',
51
                ],
52
            ]),
53
            'schema_group' => glsr(SectionDefaults::class)->merge([
54
                'children' => [
55
                    static::bdControl([
56
                        'slug' => 'schema_notice',
57
                        'options' => [
58
                            'alertBoxOptions' => [
59
                                'style' => 'warning',
60
                                'content' => $config['schema']['description'] ?? '',
61
                            ],
62
                            'layout' => 'vertical',
63
                            'type' => 'alert_box',
64
                        ],
65
                    ]),
66
                ],
67
                'fields' => ['schema'],
68
                'label' => esc_html_x('Schema Options', 'admin-text', 'site-reviews'),
69
                'options' => [
70
                    'sectionOptions' => [
71
                        'type' => 'popout',
72
                    ],
73
                    'type' => 'section',
74
                ],
75
            ]),
76
            'hide_group' => glsr(SectionDefaults::class)->merge([
77
                'fields' => ['hide'],
78
                'label' => esc_html_x('Hide Options', 'admin-text', 'site-reviews'),
79
                'options' => [
80
                    'sectionOptions' => [
81
                        'type' => 'popout',
82
                    ],
83
                    'type' => 'section',
84
                ],
85
            ]),
86
        ];
87
        $groups = glsr()->filterArray('breakdance/groups', $groups, static::class);
88
        return $groups;
89
    }
90
91
    public static function bdSections(): array
92
    {
93
        $config = static::bdShortcode()->settings();
94
        $sections = [ // order is intentional
95
            'general' => glsr(SectionDefaults::class)->merge([
96
                'label' => esc_html_x('General', 'admin-text', 'site-reviews'),
97
                'options' => [
98
                    'layout' => 'vertical',
99
                    'type' => 'section',
100
                ],
101
            ]),
102
            'advanced' => glsr(SectionDefaults::class)->merge([
103
                'children' => [
104
                    static::bdControl([
105
                        'slug' => 'reviews_id_notice',
106
                        'options' => [
107
                            'alertBoxOptions' => [
108
                                'style' => 'default',
109
                                'content' => $config['reviews_id']['description'] ?? '',
110
                            ],
111
                            'layout' => 'vertical',
112
                            'type' => 'alert_box',
113
                        ],
114
                    ]),
115
                ],
116
                'label' => esc_html_x('Advanced', 'admin-text', 'site-reviews'),
117
                'options' => [
118
                    'layout' => 'vertical',
119
                    'type' => 'section',
120
                ],
121
            ]),
122
        ];
123
        $sections = glsr()->filterArray('breakdance/sections', $sections, static::class);
124
        return $sections;
125
    }
126
127
    abstract public static function bdShortcode(): ShortcodeContract;
128
129
    /**
130
     * @return array[]
131
     */
132
    public static function contentControls()
133
    {
134
        $config = static::bdProcessConfig();
135
        $controls = [];
136
        foreach ($config as $slug => $section) {
137
            $children = array_filter($section['children']);
138
            if (empty($children)) {
139
                continue;
140
            }
141
            $controls[] = static::bdControl([
142
                'children' => $section['children'],
143
                'label' => $section['label'],
144
                'options' => $section['options'],
145
                'slug' => $slug,
146
            ]);
147
        }
148
        return $controls;
149
    }
150
151
    /**
152
     * This must return false if the element has no default properties
153
     * otherwise SSR will not trigger when control values are changed.
154
     *
155
     * @return array|false
156
     */
157
    public static function defaultProperties()
158
    {
159
        $config = static::bdShortcode()->settings();
160
        $paths = array_keys(Arr::flatten(static::bdContentControlPaths()));
161
        $props = array_combine($paths,
162
            array_map(fn ($item) => substr($item, strrpos($item, '.') + 1), $paths)
163
        );
164
        foreach ($props as $path => $slug) {
165
            $defaults[$path] = $config[$slug]['default'] ?? null;
166
        }
167
        $defaults = array_filter($defaults, fn ($value) => !is_null($value));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $defaults seems to be defined by a foreach iteration on line 164. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
168
        if (empty($defaults)) {
169
            return false;
170
        }
171
        return Arr::unflatten($defaults);
172
    }
173
174
    public static function ssrArgs(array $data): array
175
    {
176
        $content = Arr::getAs('array', $data, 'content');
177
        $sections = array_keys(static::bdSections());
178
        $groups = array_keys(static::bdSectionGroups());
179
        $controls = array_merge(...array_map(fn ($key) => $content[$key] ?? [], $sections));
180
        $ungrouped = array_filter($controls, fn ($val) => !is_array($val) || wp_is_numeric_array($val));
181
        $grouped = array_map(fn ($groupKey) => $controls[$groupKey] ?? [], $groups);
182
        $args = array_merge($ungrouped, ...$grouped);
183
        $hide = Arr::getAs('array', $args, 'hide');
184
        $args['hide'] = array_keys(array_filter($hide));
185
        if (is_array($args['assigned_posts'] ?? null)) {
186
            $replacements = [ // the post_chooser control requires integer keys
187
                -10 => 'post_id',
188
                -20 => 'parent_id',
189
            ];
190
            $args['assigned_posts'] = array_map(
191
                fn ($value) => $replacements[$value] ?? $value,
192
                $args['assigned_posts']
193
            );
194
        }
195
        if (is_array($args['assigned_users'] ?? null)) {
196
            $replacements = [ // the post_chooser control requires integer keys
197
                -10 => 'user_id',
198
                -20 => 'author_id',
199
                -30 => 'profile_id',
200
            ];
201
            $args['assigned_users'] = array_map(
202
                fn ($value) => $replacements[$value] ?? $value,
203
                $args['assigned_users']
204
            );
205
        }
206
        $args = glsr()->filterArray('breakdance/ssr', $args, $data);
207
        return $args;
208
    }
209
210
    /**
211
     * @return string[]
212
     */
213
    public static function bdContentControlPaths(): array
214
    {
215
        $config = static::bdShortcode()->settings();
216
        $props = [];
217
        foreach (static::contentControls() as $section) {
218
            $slug = $section['slug'];
219
            foreach ($section['children'] as $child) {
220
                if (!empty($child['children'])) {
221
                    continue; // this is a section group
222
                }
223
                $field = $child['slug'];
224
                if (!array_key_exists($field, $config)) {
225
                    continue;
226
                }
227
                $props['content'][$slug][$field] = $config[$field]['default'] ?? null;
228
            }
229
        }
230
        $groupedFields = wp_list_pluck(static::bdSectionGroups(), 'fields');
231
        foreach ($groupedFields as $slug => $fields) {
232
            foreach ($fields as $field) {
233
                if (!array_key_exists($field, $config)) {
234
                    continue;
235
                }
236
                $props['content']['general'][$slug][$field] = $config[$field]['default'] ?? null;
237
            }
238
        }
239
        return $props;
240
    }
241
242
    protected static function bdControl(array $args): array
243
    {
244
        $args = glsr(ControlDefaults::class)->merge($args);
245
        return c(
0 ignored issues
show
Bug Best Practice introduced by
The expression return c($args['slug'], ...r'], $args['keywords']) returns the type null which is incompatible with the type-hinted return array.
Loading history...
Bug introduced by
Are you sure the usage of c($args['slug'], $args['...r'], $args['keywords']) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
246
            $args['slug'],
0 ignored issues
show
Unused Code introduced by
The call to c() has too many arguments starting with $args['slug']. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

246
        return /** @scrutinizer ignore-call */ c(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
247
            $args['label'],
248
            $args['children'],
249
            $args['options'],
250
            $args['enableMediaQueries'],
251
            $args['enableHover'],
252
            $args['keywords']
253
        );
254
    }
255
256
    protected static function bdControlCheckbox(Arguments $args): array
257
    {
258
        $control = [
259
            'label' => $args->label,
260
            'options' => [
261
                'layout' => 'inline',
262
                'type' => 'toggle',
263
            ],
264
            'slug' => $args->slug,
265
        ];
266
        if ($items = static::bdTransformDropdownItems($args->array('options'))) {
267
            $control['children'] = array_map(fn ($item) => static::bdControl([
268
                'slug' => $item['value'],
269
                'label' => $item['text'],
270
                'options' => [
271
                    'layout' => 'inline',
272
                    'type' => 'toggle',
273
                ],
274
            ]), $items);
275
            $control['options']['type'] = 'section';
276
        }
277
        return static::bdControl($control);
278
    }
279
280
    protected static function bdControlNumber(Arguments $args): array
281
    {
282
        return static::bdControl([
283
            'slug' => $args->slug,
284
            'label' => $args->label,
285
            'options' => [
286
                'layout' => 'inline',
287
                'rangeOptions' => [
288
                    'min' => $args->cast('min', 'int', 0),
289
                    'max' => $args->cast('max', 'int', 1),
290
                    'step' => $args->cast('step', 'int', 1),
291
                ],
292
                'type' => 'number',
293
            ],
294
        ]);
295
    }
296
297
    /**
298
     * If multiple select is required in an AJAX-populated dropdown then we have
299
     * to use the post_chooser control because:
300
     * a) The dropdown control does not support multiple selection.
301
     * b) The multiselect control does not support AJAX searching.
302
     *
303
     * @see Controller::searchAssignedPosts
304
     * @see Controller::searchAssignedTerms
305
     * @see Controller::searchAssignedUsers
306
     * @see \GeminiLabs\SiteReviews\Controllers\MainController::parseAssignedPostTypesInQuery
307
     */
308
    protected static function bdControlSelect(Arguments $args): array
309
    {
310
        $control = [
311
            'label' => $args->label,
312
            'slug' => $args->slug,
313
            'options' => [
314
                'layout' => 'vertical',
315
                'placeholder' => $args->placeholder ?: esc_html_x('Select...', 'admin-text', 'site-reviews'),
316
                'type' => 'dropdown',
317
            ],
318
        ];
319
        if (!$args->exists('options')) {
320
            // Unfortunately we can't use this because Breakdance does not support
321
            // AJAX searching in dropdown controls.
322
            // if (true !== $args->multiple) {
323
            //     $control['options']['dropdownOptions']['populate'] = [
324
            //         'fetchDataAction' => glsr()->prefix.'breakdance_'.$args->slug,
325
            //     ];
326
            //     return static::bdControl($control);
327
            // }
328
            $control['options']['type'] = 'post_chooser';
329
            $control['options']['postChooserOptions'] = [
330
                'multiple' => $args->multiple,
331
                'postType' => glsr()->prefix.$args->slug,
332
                'showThumbnails' => 'assigned_users' === $args->slug,
333
            ];
334
            // Unfortunately we can't use this yet because Breakdance does not support
335
            // AJAX searching in multiselect controls.
336
            // $control['options']['type'] = 'multiselect';
337
            // $control['options']['searchable'] = true;
338
            // $control['options']['multiselectOptions']['populate'] = [
339
            //     'fetchDataAction' => glsr()->prefix.'breakdance_'.$args->slug,
340
            //     'fetchContextPath' => 'content.general.'.$args->slug,
341
            // ];
342
            return static::bdControl($control);
343
        }
344
        $items = static::bdTransformDropdownItems($args->array('options'));
345
        if (!empty($items)) {
346
            $control['options']['items'] = $items;
347
            return static::bdControl($control);
348
        }
349
        return [];
350
    }
351
352
    protected static function bdControlText(Arguments $args): array
353
    {
354
        return static::bdControl([
355
            'slug' => $args->slug,
356
            'label' => $args->label,
357
            'options' => [
358
                'layout' => 'vertical',
359
                'type' => 'text',
360
            ],
361
        ]);
362
    }
363
364
    /**
365
     * Find the group slug for the given slug in grouped fields.
366
     */
367
    protected static function bdFindGroupSlug(string $slug, array $groupedFields): string
368
    {
369
        return (string) array_search(true, array_map(
370
            fn ($fields) => in_array($slug, $fields),
371
            $groupedFields
372
        ));
373
    }
374
375
    /**
376
     * Process the configuration settings and group controls.
377
     */
378
    protected static function bdProcessConfig(): array
379
    {
380
        $config = static::bdShortcode()->settings();
381
        $groups = static::bdSectionGroups();
382
        $groupedFields = wp_list_pluck($groups, 'fields');
383
        $sections = static::bdSections();
384
        foreach ($config as $slug => $args) {
385
            $args = wp_parse_args($args, [
386
                'group' => 'general',
387
                'type' => 'text',
388
                'slug' => $slug,
389
            ]);
390
            $args['group'] = array_key_exists($args['group'], $sections) ? $args['group'] : 'general';
391
            $method = Helper::buildMethodName('bd', 'control', $args['type']);
392
            if (!method_exists(static::class, $method)) {
393
                continue;
394
            }
395
            $control = call_user_func([static::class, $method], glsr()->args($args));
396
            if (empty($control)) {
397
                continue;
398
            }
399
            $groupSlug = static::bdFindGroupSlug($slug, $groupedFields);
400
            if (!empty($groupSlug)) {
401
                $groups[$groupSlug]['children'][] = $control;
402
                continue;
403
            }
404
            $sections[$args['group']]['children'][] = $control;
405
        }
406
        return static::bdProcessControlGroups($groups, $sections);
407
    }
408
409
    /**
410
     * Process groups and add them to sections if they have valid children.
411
     */
412
    protected static function bdProcessControlGroups(array $groups, array $sections): array
413
    {
414
        foreach ($groups as $slug => $group) {
415
            $filteredChildren = array_filter($group['children'], function ($child) {
416
                return 'alert_box' !== ($child['options']['type'] ?? 'alert_box');
417
            });
418
            if (!empty($filteredChildren)) {
419
                $sections['general']['children'][] = static::bdControl([
420
                    'children' => $group['children'],
421
                    'label' => $group['label'],
422
                    'options' => $group['options'],
423
                    'slug' => $slug,
424
                ]);
425
            }
426
        }
427
        return $sections;
428
    }
429
430
    protected static function bdTransformDropdownItems(array $options): array
431
    {
432
        $callback = fn ($k, $v) => [
433
            'text' => $v,
434
            'value' => $k,
435
        ];
436
        return array_map($callback, array_keys($options), $options);
437
    }
438
}
439