Passed
Push — master ( ce0a54...b1b148 )
by Stiofan
07:08 queued 01:28
created

Super_Duper_Bricks_Element::sd_convert_arguments()   C

Complexity

Conditions 13
Paths 2

Size

Total Lines 48
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 23
nc 2
nop 0
dl 0
loc 48
rs 6.6166
c 1
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
4
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
5
6
7
class Super_Duper_Bricks_Element extends \Bricks\Element {
0 ignored issues
show
Bug introduced by
The type Bricks\Element was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
9
	public $widget;
10
11
	public function __construct( $element = null ) {
12
13
14
		$block_icon = !empty($this->widget->options['block-icon']) ? $this->widget->options['block-icon'] : '';
15
16
17
		$this->category = !empty($this->widget->options['textdomain']) ? esc_attr( $this->widget->options['textdomain'] ) : 'Super Duper';
0 ignored issues
show
Bug Best Practice introduced by
The property category does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
18
		$this->name     = $this->widget->id_base;
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
19
		$this->icon     = (strpos($block_icon, 'fa') === 0) ? esc_attr($this->widget->options['block-icon']) : 'fas fa-globe-americas';
0 ignored issues
show
Bug Best Practice introduced by
The property icon does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
20
21
		parent::__construct($element);
22
	}
23
24
	/**
25
	 * Set the element name.
26
	 *
27
	 * @return array|string|string[]|null
28
	 */
29
	public function get_label() {
30
		$escaped_text = esc_attr( $this->widget->name );
31
		return str_replace( ' &gt; ', ' > ', $escaped_text ); // keep our > but have it safe
32
	}
33
34
	/**
35
	 * Bricks function to set the controls
36
	 *
37
	 * @return void
38
	 */
39
	public function set_controls() {
40
		$args = $this->sd_convert_arguments($this->widget);
0 ignored issues
show
Unused Code introduced by
The call to Super_Duper_Bricks_Element::sd_convert_arguments() has too many arguments starting with $this->widget. ( Ignorable by Annotation )

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

40
		/** @scrutinizer ignore-call */ 
41
  $args = $this->sd_convert_arguments($this->widget);

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...
41
42
		if (!empty($args)) {
43
			$this->controls = $this->controls + $args;
0 ignored issues
show
Bug Best Practice introduced by
The property controls does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
44
		}
45
46
	}
47
48
	/**
49
	 * Set the bricks control groups from the GD ones.
50
	 *
51
	 * @return void
52
	 */
53
	public function set_control_groups() {
54
		$args = $this->sd_get_arguments();
55
56
		$groups = array();
57
		if(!empty($args)) {
58
			foreach ($args as $k => $v) {
59
				$g_slug = !empty($v['group']) ? sanitize_title( $v['group'] ) : '';
60
				if($g_slug && empty($groups[$g_slug])) {
61
					$groups[$g_slug] = array(
62
						'title' => esc_html( $v['group'] ),
63
						'tab' => 'content',
64
					);
65
				}
66
			}
67
		}
68
69
		if(!empty($groups)) {
70
			$this->control_groups = $this->control_groups + $groups;
0 ignored issues
show
Bug Best Practice introduced by
The property control_groups does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
71
		}
72
73
	}
74
75
	/**
76
	 * Get the setting input arguments.
77
	 *
78
	 * @return mixed
79
	 */
80
	public function sd_get_arguments() {
81
		$args = $this->widget->set_arguments();
82
83
		$widget_options = ! empty( $this->widget->options ) ? $this->widget->options : array();
84
		$widget_instance = ! empty( $this->widget->instance ) ? $this->widget->instance : array();
85
86
		$args = apply_filters( 'wp_super_duper_arguments', $args, $widget_options, $widget_instance );
87
88
		$arg_keys_subtract = $this->sd_remove_arguments();
89
90
		if ( ! empty( $arg_keys_subtract ) ) {
91
			foreach($arg_keys_subtract as $key ){
92
				unset($args[$key]);
93
			}
94
		}
95
96
		return $args;
97
	}
98
99
100
	/**
101
	 * Simply use our own render function for the output.
102
	 *
103
	 * @return void
104
	 */
105
	public function render() {
106
		$settings = $this->sd_maybe_convert_values( $this->settings );
107
108
		// Set the AyeCode UI calss on the wrapper
109
		$this->set_attribute( '_root', 'class', 'bsui' );
110
111
		// We might need to add a placeholder here for previews.
112
113
		do_action( 'super_duper_before_render_bricks_element', $settings, $this->widget, $this );
114
115
		// Add the bricks attributes to wrapper
116
		echo "<div {$this->render_attributes( '_root' )}>";
117
		echo $this->widget->output( $settings );
118
		echo '</div>';
119
	}
120
121
	/**
122
	 * Values can never be arrays so convert if bricks setting make it an array.
123
	 *
124
	 * @param $settings
125
	 * @return mixed
126
	 */
127
	public function sd_maybe_convert_values( $settings ) {
128
129
130
		if (!empty($settings)) {
131
			foreach( $settings as $k => $v ) {
132
				if(is_array($v)) {
133
					$value = '';
134
					// is color
135
					if (isset($v['hex'])) {
136
						$value = $v['hex'];
137
					} elseif (isset($v['icon'])) {
138
						$value = $v['icon'];
139
					}
140
141
142
					// set the value
143
					$settings[$k] = $value;
144
				}
145
146
			}
147
		}
148
149
		return $settings;
150
	}
151
152
	/**
153
	 * Convert SD arguments to Bricks arguments.
154
	 *
155
	 * @param $widget
156
	 *
157
	 * @return array
158
	 */
159
	public function sd_convert_arguments() {
160
		$bricks_args = array();
161
162
		$args = $this->sd_get_arguments();
163
164
		if ( ! empty( $args ) ) {
165
			foreach ( $args as $key => $arg ) {
166
				// convert title
167
				if ( ! empty( $arg['title'] ) ) {
168
					$arg['label'] = $arg['title'];
169
					unset( $arg['title'] );
170
				}
171
172
				// set fields not to use dynamic data
173
				$arg['hasDynamicData'] = false;
174
175
				if ( ! empty( $arg['group'] ) ) {
176
					$arg['group'] =  sanitize_title( $arg['group'] );
177
				}
178
179
				$arg['rerender'] = true;
180
181
				// required
182
				if( ! empty( $arg['element_require'] ) ) {
183
					$arg['required'] = $this->sd_convert_required( $arg['element_require'] );
184
					unset( $arg['element_require'] );
185
				}
186
187
				// icons
188
				if ( 'icon' === $key ) {
189
					$arg['type'] = 'icon';
190
				}
191
192
				// Bricks don't render dropdown when first option key is 0.
193
				if ( in_array( $key, array( 'zoom', 'mapzoom' ) ) && ! empty( $arg['options'] ) && is_array( $arg['options'] ) && ( $option_keys = array_keys( $arg['options'] ) ) ) {
194
					// Move first element to last.
195
					if ( $option_keys[0] === 0 || $option_keys[0] === '0' ) {
196
						$options = $arg['options'];
197
						unset( $arg['options'][0] );
198
						$arg['options'][0] = $options[0];
199
					}
200
				}
201
202
				$bricks_args[$key] = $arg;
203
			}
204
		}
205
206
		return $bricks_args;
207
	}
208
209
	/**
210
	 * Convert the SD element_required to the Bricks required syntax.
211
	 *
212
	 * @param $element_require
213
	 * @return array
214
	 */
215
	public function sd_convert_required($element_require) {
216
		$bricks_required = [];
217
218
		// Handle logical OR (||) for multiple values
219
		if (strpos($element_require, '||') !== false) {
220
			preg_match('/\[%(.+?)%\] *== *"(.*?)"/', $element_require, $matches);
221
			if ($matches) {
222
				$control_id = $matches[1];
223
				preg_match_all('/\[%.*?%\] *== *"(.*?)"/', $element_require, $value_matches);
224
				$values = $value_matches[1];
225
				$bricks_required[] = [$control_id, '=', $values];
226
			}
227
			return $bricks_required;
228
		}
229
230
		// Match individual conditions
231
		preg_match_all('/(!)?\[%(.*?)%\](?:\s*([!=<>]=?)\s*(".*?"|\'.*?\'|\d+))?/', $element_require, $matches, PREG_SET_ORDER);
232
233
		foreach ($matches as $match) {
234
			$is_negation = isset($match[1]) && $match[1] === '!';
235
			$control_id = $match[2];
236
			$operator = isset($match[3]) ? str_replace('==', '=', $match[3]) : ($is_negation ? '=' : '!=');
237
			$value = isset($match[4]) ? trim($match[4], '"\'') : ($is_negation ? '' : '');
238
239
			// Adjust for negation without explicit operator
240
			if ($is_negation && !isset($match[3])) {
241
				$operator = '=';
242
				$value = '';
243
			}
244
245
			$bricks_required[] = [$control_id, $operator, $value];
246
		}
247
248
		return $bricks_required;
249
	}
250
251
252
	/**
253
	 * A way to remove some settings by keys.
254
	 *
255
	 * @return array
256
	 */
257
	public function sd_remove_arguments()
258
	{
259
		return array();
260
	}
261
262
}
263
264
265
/**
266
 * This implements the desktop, tablet and mobile breakpoints views with our fields that are hidden on these types and adda the icon after the label to show which it applies to.
267
 */
268
add_action( 'wp_enqueue_scripts', function() {
269
270
	// Check if we're in the Bricks Editor
271
	if ( isset( $_GET['bricks'] ) && $_GET['bricks'] && bricks_is_builder_main() ) {
0 ignored issues
show
Bug introduced by
The function bricks_is_builder_main was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

271
	if ( isset( $_GET['bricks'] ) && $_GET['bricks'] && /** @scrutinizer ignore-call */ bricks_is_builder_main() ) {
Loading history...
272
		// Add inline script to the 'bricks-builder' script
273
		wp_add_inline_script(
274
			'bricks-builder',
275
			"
276
277
(function () {
278
    // Function to get the current breakpoint from the #bricks-preview class
279
    function getCurrentBreakpoint() {
280
        const bricksPreview = document.getElementById('bricks-preview');
281
        if (!bricksPreview) return null;
282
283
        // Check which breakpoint class is active
284
        if (bricksPreview.classList.contains('desktop')) {
285
            return 'desktop';
286
        } else if (bricksPreview.classList.contains('tablet_portrait')) {
287
            return 'tablet';
288
        } else if (bricksPreview.classList.contains('mobile_landscape') || bricksPreview.classList.contains('mobile_portrait')) {
289
            return 'phone';
290
        }
291
        return null;
292
    }
293
294
    // Function to group fields by base key
295
    function groupFields() {
296
        const controls = document.querySelectorAll('[data-controlkey]');
297
        const grouped = {};
298
299
        controls.forEach((control) => {
300
            const controlKey = control.getAttribute('data-controlkey');
301
            const baseKey = controlKey.replace(/(_sm|_md|_lg)$/, ''); // Remove _sm, _md, or _lg suffix
302
303
            if (!grouped[baseKey]) {
304
                grouped[baseKey] = { base: null, md: null, lg: null };
305
            }
306
307
            if (controlKey.endsWith('_lg')) {
308
                grouped[baseKey].lg = control;
309
            } else if (controlKey.endsWith('_md')) {
310
                grouped[baseKey].md = control;
311
            } else {
312
                grouped[baseKey].base = control; // No suffix is treated as base (sm)
313
            }
314
        });
315
316
        return grouped;
317
    }
318
319
    // Function to update visibility of controls
320
    function updateControlVisibility() {
321
        const breakpoint = getCurrentBreakpoint();
322
        const groupedFields = groupFields();
323
324
        Object.keys(groupedFields).forEach((baseKey) => {
325
            const { base, md, lg } = groupedFields[baseKey];
326
327
            // Skip fields that have no `_md` or `_lg` counterparts
328
            if (!md && !lg) {
329
                if (base) base.style.display = ''; // Ensure base field is always visible
330
                return;
331
            }
332
333
            // Apply hide/show logic based on the breakpoint
334
            if (breakpoint === 'desktop') {
335
                if (base) base.style.display = 'none';
336
                if (md) md.style.display = 'none';
337
                if (lg) lg.style.display = ''; // Show _lg
338
            } else if (breakpoint === 'tablet') {
339
                if (base) base.style.display = 'none';
340
                if (md) md.style.display = ''; // Show _md
341
                if (lg) lg.style.display = 'none';
342
            } else if (breakpoint === 'phone') {
343
                if (base) base.style.display = ''; // Show base (sm)
344
                if (md) md.style.display = 'none';
345
                if (lg) lg.style.display = 'none';
346
            }
347
        });
348
    }
349
350
    // Observe changes in the #bricks-panel-element content
351
    const panelElementObserver = new MutationObserver(() => {
352
        updateControlVisibility();
353
    });
354
355
    const bricksPanelElement = document.getElementById('bricks-panel-element');
356
    if (bricksPanelElement) {
357
        panelElementObserver.observe(bricksPanelElement, { childList: true, subtree: true });
358
    }
359
360
    // Also observe changes to the #bricks-preview element for breakpoint changes
361
    const bricksPreviewObserver = new MutationObserver(() => {
362
        updateControlVisibility();
363
    });
364
365
    const bricksPreview = document.getElementById('bricks-preview');
366
    if (bricksPreview) {
367
        bricksPreviewObserver.observe(bricksPreview, { attributes: true, attributeFilter: ['class'] });
368
    }
369
370
    // Run on initial load
371
    updateControlVisibility();
372
})();
373
374
375
(function () {
376
    // Function to get the current breakpoint from the #bricks-preview class
377
    function getCurrentBreakpoint() {
378
        const bricksPreview = document.getElementById('bricks-preview');
379
        if (!bricksPreview) return null;
380
381
        if (bricksPreview.classList.contains('desktop')) {
382
            return 'desktop';
383
        } else if (bricksPreview.classList.contains('tablet_portrait')) {
384
            return 'tablet';
385
        } else if (bricksPreview.classList.contains('mobile_landscape') || bricksPreview.classList.contains('mobile_portrait')) {
386
            return 'phone';
387
        }
388
        return null;
389
    }
390
391
    // SVG icons
392
   const icons = {
393
    lg: `
394
        <span class=\"bricks-svg-wrapper\" data-name=\"laptop\" style=\"padding-top:3px;\" title=\"Desktop\">
395
            <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 14 14\" class=\"bricks-svg\">
396
                <g id=\"laptop--device-laptop-electronics-computer-notebook\">
397
                    <path id=\"Vector\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3.08 1.61H10.9136C11.4549 1.61 11.8936 2.0488 11.8936 2.59V7.98H2.1V2.59C2.1 2.002 2.492 1.61 3.08 1.61Z\" stroke-width=\"1\"></path>
398
                    <path id=\"Vector 3945\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M0.6957 11.2566L2.1 7.98H11.9L13.3042 11.2566C13.3477 11.3578 13.37 11.4667 13.37 11.5769C13.37 12.0259 13.0059 12.39 12.5569 12.39H1.4431C0.994 12.39 0.63 12.0259 0.63 11.5769C0.63 11.4667 0.6524 11.3578 0.6957 11.2566Z\" stroke-width=\"1\"></path>
399
                </g>
400
            </svg>
401
        </span>
402
    `,
403
    md: `
404
        <span class=\"bricks-svg-wrapper\" data-name=\"tablet-portrait\" style=\"padding-top:3px;\" title=\"Tablet\"><svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 14 14\" class=\"bricks-svg\"><g id=\"one-handed-holding-tablet-handheld\"><path id=\"Rectangle 2038\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9.64593 1.23658C9.47168 1.089 9.24623 1 9 1H2c-0.55228 0 -1 0.44771 -1 1v9.0938c0 0.5522 0.44772 1 1 1h3.75\" stroke-width=\"1\"></path><path id=\"vector 296\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m12.3106 13 0.6383 -3.15223c0.0742 -0.36672 0.0675 -0.7452 -0.0197 -1.10906l-0.9088 -3.79119c-0.1682 -0.70134 -0.7013 -1.25797 -1.3954 -1.45681l-0.6221 -0.17821 -0.0002 5.23879c0 0.35407 -0.35839 0.59595 -0.68734 0.46392l-1.6994 -0.68209c-0.3105 -0.12463 -0.66467 -0.06608 -0.91839 0.15183 -0.3824 0.32842 -0.41818 0.90721 -0.07914 1.28012l1.24302 1.36723L8.89958 13\" stroke-width=\"1\"></path></g></svg></span>
405
    `,
406
    sm: `
407
        <span class=\"bricks-svg-wrapper\" data-name=\"phone-portrait\" style=\"padding-top:3px;\" title=\"Mobile\">
408
            <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 14 14\" class=\"bricks-svg\">
409
                <g id=\"phone-mobile-phone--android-phone-mobile-device-smartphone-iphone\">
410
                    <path id=\"Vector\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M10.5 0.5h-7c-0.55228 0 -1 0.447715 -1 1v11c0 0.5523 0.44772 1 1 1h7c0.5523 0 1 -0.4477 1 -1v-11c0 -0.552285 -0.4477 -1 -1 -1Z\" stroke-width=\"1\"></path>
411
                    <path id=\"Vector_2\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6.5 11h1\" stroke-width=\"1\"></path>
412
                </g>
413
            </svg>
414
        </span>
415
    `,
416
};
417
418
419
    // Function to group fields by base key
420
    function groupFields() {
421
        const controls = document.querySelectorAll('[data-controlkey]');
422
        const grouped = {};
423
424
        controls.forEach((control) => {
425
            const controlKey = control.getAttribute('data-controlkey');
426
            const baseKey = controlKey.replace(/(_sm|_md|_lg)$/, ''); // Remove _sm, _md, or _lg suffix
427
428
            if (!grouped[baseKey]) {
429
                grouped[baseKey] = { base: null, md: null, lg: null };
430
            }
431
432
            if (controlKey.endsWith('_lg')) {
433
                grouped[baseKey].lg = control;
434
            } else if (controlKey.endsWith('_md')) {
435
                grouped[baseKey].md = control;
436
            } else {
437
                grouped[baseKey].base = control; // No suffix is treated as base (sm)
438
            }
439
        });
440
441
        return grouped;
442
    }
443
444
    // Function to add icons to labels
445
    function addIconsToLabels() {
446
        const groupedFields = groupFields();
447
448
        Object.keys(groupedFields).forEach((baseKey) => {
449
            const { base, md, lg } = groupedFields[baseKey];
450
451
            // Skip fields that do not have `_md` or `_lg` counterparts
452
            if (!md && !lg) return;
453
454
            if (base) appendIcon(base, 'sm');
455
            if (md) appendIcon(md, 'md');
456
            if (lg) appendIcon(lg, 'lg');
457
        });
458
    }
459
460
    // Append an icon to the label of a control
461
    function appendIcon(control, type) {
462
        const label = control.querySelector('label');
463
        if (label && !label.querySelector('.bricks-svg-wrapper')) {
464
            label.insertAdjacentHTML('beforeend', icons[type]);
465
        }
466
    }
467
468
    // Observe changes in the #bricks-panel-element content
469
    const panelElementObserver = new MutationObserver(() => {
470
        addIconsToLabels(); // Ensure icons are added when the panel updates
471
    });
472
473
    const bricksPanelElement = document.getElementById('bricks-panel-element');
474
    if (bricksPanelElement) {
475
        panelElementObserver.observe(bricksPanelElement, { childList: true, subtree: true });
476
    }
477
478
    // Initial run to add icons
479
    addIconsToLabels();
480
})();
481
"
482
		);
483
	}
484
});
485