Passed
Pull Request — master (#379)
by Brian
04:59
created

WP_Super_Duper::get_instance_hash()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
if ( ! class_exists( 'WP_Super_Duper' ) ) {
7
8
9
	/**
10
	 * A Class to be able to create a Widget, Shortcode or Block to be able to output content for WordPress.
11
	 *
12
	 * Should not be called direct but extended instead.
13
	 *
14
	 * Class WP_Super_Duper
15
	 * @since 1.0.16 change log moved to file change-log.txt - CHANGED
16
	 * @ver 1.0.19
17
	 */
18
	class WP_Super_Duper extends WP_Widget {
19
20
		public $version = "1.0.21";
21
		public $font_awesome_icon_version = "5.11.2";
22
		public $block_code;
23
		public $options;
24
		public $base_id;
25
		public $settings_hash;
26
		public $arguments = array();
27
		public $instance = array();
28
		private $class_name;
29
30
		/**
31
		 * The relative url to the current folder.
32
		 *
33
		 * @var string
34
		 */
35
		public $url = '';
36
37
		/**
38
		 * Take the array options and use them to build.
39
		 */
40
		public function __construct( $options ) {
41
			global $sd_widgets;
42
43
			$sd_widgets[ $options['base_id'] ] = array(
44
				'name'       => $options['name'],
45
				'class_name' => $options['class_name']
46
			);
47
			$this->base_id                     = $options['base_id'];
48
			// lets filter the options before we do anything
49
			$options       = apply_filters( "wp_super_duper_options", $options );
50
			$options       = apply_filters( "wp_super_duper_options_{$this->base_id}", $options );
51
			$options       = $this->add_name_from_key( $options );
52
			$this->options = $options;
53
54
			$this->base_id   = $options['base_id'];
55
			$this->arguments = isset( $options['arguments'] ) ? $options['arguments'] : array();
56
57
			// init parent
58
			parent::__construct( $options['base_id'], $options['name'], $options['widget_ops'] );
59
60
			if ( isset( $options['class_name'] ) ) {
61
				// register widget
62
				$this->class_name = $options['class_name'];
63
64
				// register shortcode
65
				$this->register_shortcode();
66
67
				// Fusion Builder (avada) support
68
				if ( function_exists( 'fusion_builder_map' ) ) {
69
					add_action( 'init', array( $this, 'register_fusion_element' ) );
70
				}
71
72
				// register block
73
				add_action( 'admin_enqueue_scripts', array( $this, 'register_block' ) );
74
			}
75
76
			// add the CSS and JS we need ONCE
77
			global $sd_widget_scripts;
78
79
			if ( ! $sd_widget_scripts ) {
80
				wp_add_inline_script( 'admin-widgets', $this->widget_js() );
81
				wp_add_inline_script( 'customize-controls', $this->widget_js() );
82
				wp_add_inline_style( 'widgets', $this->widget_css() );
83
84
				// maybe add elementor editor styles
85
				add_action( 'elementor/editor/after_enqueue_styles', array( $this, 'elementor_editor_styles' ) );
86
87
				$sd_widget_scripts = true;
88
89
				// add shortcode insert button once
90
				add_action( 'media_buttons', array( $this, 'shortcode_insert_button' ) );
91
				// generatepress theme sections compatibility
92
				if ( function_exists( 'generate_sections_sections_metabox' ) ) {
93
					add_action( 'generate_sections_metabox', array( $this, 'shortcode_insert_button_script' ) );
94
				}
95
				if ( $this->is_preview() ) {
96
					add_action( 'wp_footer', array( $this, 'shortcode_insert_button_script' ) );
97
					// this makes the insert button work for elementor
98
					add_action( 'elementor/editor/after_enqueue_scripts', array(
99
						$this,
100
						'shortcode_insert_button_script'
101
					) ); // for elementor
102
				}
103
				// this makes the insert button work for cornerstone
104
				add_action( 'wp_print_footer_scripts', array( __CLASS__, 'maybe_cornerstone_builder' ) );
105
106
				add_action( 'wp_ajax_super_duper_get_widget_settings', array( __CLASS__, 'get_widget_settings' ) );
107
				add_action( 'wp_ajax_super_duper_get_picker', array( __CLASS__, 'get_picker' ) );
108
109
				// add generator text to admin head
110
				add_action( 'admin_head', array( $this, 'generator' ) );
111
			}
112
113
			do_action( 'wp_super_duper_widget_init', $options, $this );
114
		}
115
116
		/**
117
		 * Add our widget CSS to elementor editor.
118
		 */
119
		public function elementor_editor_styles() {
120
			wp_add_inline_style( 'elementor-editor', $this->widget_css( false ) );
121
		}
122
123
		public function register_fusion_element() {
124
125
			$options = $this->options;
126
127
			if ( $this->base_id ) {
128
129
				$params = $this->get_fusion_params();
130
131
				$args = array(
132
					'name'            => $options['name'],
133
					'shortcode'       => $this->base_id,
134
					'icon'            => $options['block-icon'] ? $options['block-icon'] : 'far fa-square',
135
					'allow_generator' => true,
136
				);
137
138
				if ( ! empty( $params ) ) {
139
					$args['params'] = $params;
140
				}
141
142
				fusion_builder_map( $args );
0 ignored issues
show
Bug introduced by
The function fusion_builder_map 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

142
				/** @scrutinizer ignore-call */ 
143
    fusion_builder_map( $args );
Loading history...
143
			}
144
145
		}
146
147
		public function get_fusion_params() {
148
			$params    = array();
149
			$arguments = $this->get_arguments();
150
151
			if ( ! empty( $arguments ) ) {
152
				foreach ( $arguments as $key => $val ) {
153
					$param = array();
154
					// type
155
					$param['type'] = str_replace(
156
						array(
157
							"text",
158
							"number",
159
							"email",
160
							"color",
161
							"checkbox"
162
						),
163
						array(
164
							"textfield",
165
							"textfield",
166
							"textfield",
167
							"colorpicker",
168
							"select",
169
170
						),
171
						$val['type'] );
172
173
					// multiselect
174
					if ( $val['type'] == 'multiselect' || ( ( $param['type'] == 'select' || $val['type'] == 'select' ) && ! empty( $val['multiple'] ) ) ) {
175
						$param['type']     = 'multiple_select';
176
						$param['multiple'] = true;
177
					}
178
179
					// heading
180
					$param['heading'] = $val['title'];
181
182
					// description
183
					$param['description'] = isset( $val['desc'] ) ? $val['desc'] : '';
184
185
					// param_name
186
					$param['param_name'] = $key;
187
188
					// Default
189
					$param['default'] = isset( $val['default'] ) ? $val['default'] : '';
190
191
					// Group
192
					if ( isset( $val['group'] ) ) {
193
						$param['group'] = $val['group'];
194
					}
195
196
					// value
197
					if ( $val['type'] == 'checkbox' ) {
198
						if ( isset( $val['default'] ) && $val['default'] == '0' ) {
199
							unset( $param['default'] );
200
						}
201
						$param['value'] = array( '' => __( "No" ), '1' => __( "Yes" ) );
202
					} elseif ( $param['type'] == 'select' || $param['type'] == 'multiple_select' ) {
203
						$param['value'] = isset( $val['options'] ) ? $val['options'] : array();
204
					} else {
205
						$param['value'] = isset( $val['default'] ) ? $val['default'] : '';
206
					}
207
208
					// setup the param
209
					$params[] = $param;
210
211
				}
212
			}
213
214
215
			return $params;
216
		}
217
218
		/**
219
		 * Maybe insert the shortcode inserter button in the footer if we are in the cornerstone builder
220
		 */
221
		public static function maybe_cornerstone_builder() {
222
			if ( did_action( 'cornerstone_before_boot_app' ) ) {
223
				self::shortcode_insert_button_script();
224
			}
225
		}
226
227
		/**
228
		 * A function to ge the shortcode builder picker html.
229
		 *
230
		 * @param string $editor_id
231
		 *
232
		 * @return string
233
		 */
234
		public static function get_picker( $editor_id = '' ) {
235
236
			ob_start();
237
			if ( isset( $_POST['editor_id'] ) ) {
238
				$editor_id = esc_attr( $_POST['editor_id'] );
239
			} elseif ( isset( $_REQUEST['et_fb'] ) ) {
240
				$editor_id = 'main_content_content_vb_tiny_mce';
241
			}
242
243
			global $sd_widgets;
244
			?>
245
246
			<div class="sd-shortcode-left-wrap">
247
				<?php
248
				ksort( $sd_widgets );
249
				//				print_r($sd_widgets);exit;
250
				if ( ! empty( $sd_widgets ) ) {
251
					echo '<select class="widefat" onchange="sd_get_shortcode_options(this);">';
252
					echo "<option>" . __( 'Select shortcode' ) . "</option>";
253
					foreach ( $sd_widgets as $shortcode => $class ) {
254
						echo "<option value='" . esc_attr( $shortcode ) . "'>" . esc_attr( $shortcode ) . " (" . esc_attr( $class['name'] ) . ")</option>";
255
					}
256
					echo "</select>";
257
258
				}
259
				?>
260
				<div class="sd-shortcode-settings"></div>
261
262
			</div>
263
264
			<div class="sd-shortcode-right-wrap">
265
				<textarea id='sd-shortcode-output' disabled></textarea>
266
				<div id='sd-shortcode-output-actions'>
267
					<?php if ( $editor_id != '' ) { ?>
268
						<button class="button sd-insert-shortcode-button"
269
						        onclick="sd_insert_shortcode(<?php if ( ! empty( $editor_id ) ) {
270
							        echo "'" . $editor_id . "'";
271
						        } ?>)"><?php _e( 'Insert shortcode' ); ?></button>
272
					<?php } ?>
273
					<button class="button"
274
					        onclick="sd_copy_to_clipboard()"><?php _e( 'Copy shortcode' ); ?></button>
275
				</div>
276
			</div>
277
			<?php
278
279
			$html = ob_get_clean();
280
281
			if ( wp_doing_ajax() ) {
282
				echo $html;
283
				$should_die = true;
284
285
				// some builder get the editor via ajax so we should not die on those ocasions
286
				$dont_die = array(
287
					'parent_tag',// WP Bakery
288
					'avia_request' // enfold
289
				);
290
291
				foreach ( $dont_die as $request ) {
292
					if ( isset( $_REQUEST[ $request ] ) ) {
293
						$should_die = false;
294
					}
295
				}
296
297
				if ( $should_die ) {
298
					wp_die();
299
				}
300
301
			} else {
302
				return $html;
303
			}
304
305
			return '';
306
307
		}
308
309
		/**
310
		 * Output the version in the admin header.
311
		 */
312
		public function generator() {
313
			echo '<meta name="generator" content="WP Super Duper v' . $this->version . '" />';
314
		}
315
316
		/**
317
		 * Get widget settings.
318
		 *
319
		 * @since 1.0.0
320
		 */
321
		public static function get_widget_settings() {
322
			global $sd_widgets;
323
324
			$shortcode = isset( $_REQUEST['shortcode'] ) && $_REQUEST['shortcode'] ? sanitize_title_with_dashes( $_REQUEST['shortcode'] ) : '';
325
			if ( ! $shortcode ) {
326
				wp_die();
327
			}
328
			$widget_args = isset( $sd_widgets[ $shortcode ] ) ? $sd_widgets[ $shortcode ] : '';
329
			if ( ! $widget_args ) {
330
				wp_die();
331
			}
332
			$class_name = isset( $widget_args['class_name'] ) && $widget_args['class_name'] ? $widget_args['class_name'] : '';
333
			if ( ! $class_name ) {
334
				wp_die();
335
			}
336
337
			// invoke an instance method
338
			$widget = new $class_name;
339
340
			ob_start();
341
			$widget->form( array() );
342
			$form = ob_get_clean();
343
			echo "<form id='$shortcode'>" . $form . "<div class=\"widget-control-save\"></div></form>";
344
			echo "<style>" . $widget->widget_css() . "</style>";
345
			echo "<script>" . $widget->widget_js() . "</script>";
346
			?>
347
			<?php
348
			wp_die();
349
		}
350
351
		/**
352
		 * Insert shortcode builder button to classic editor (not inside Gutenberg, not needed).
353
		 *
354
		 * @since 1.0.0
355
		 *
356
		 * @param string $editor_id Optional. Shortcode editor id. Default null.
357
		 * @param string $insert_shortcode_function Optional. Insert shotcode function. Default null.
358
		 */
359
		public static function shortcode_insert_button( $editor_id = '', $insert_shortcode_function = '' ) {
360
			global $sd_widgets, $shortcode_insert_button_once;
361
			if ( $shortcode_insert_button_once ) {
362
				return;
363
			}
364
			add_thickbox();
365
366
367
			/**
368
			 * Cornerstone makes us play dirty tricks :/
369
			 * All media_buttons are removed via JS unless they are two specific id's so we wrap our content in this ID so it is not removed.
370
			 */
371
			if ( function_exists( 'cornerstone_plugin_init' ) && ! is_admin() ) {
372
				echo '<span id="insert-media-button">';
373
			}
374
375
			echo self::shortcode_button( 'this', 'true' );
376
377
			// see opening note
378
			if ( function_exists( 'cornerstone_plugin_init' ) && ! is_admin() ) {
379
				echo '</span>'; // end #insert-media-button
380
			}
381
382
			// Add separate script for generatepress theme sections
383
			if ( function_exists( 'generate_sections_sections_metabox' ) && did_action( 'generate_sections_metabox' ) ) {
384
			} else {
385
				self::shortcode_insert_button_script( $editor_id, $insert_shortcode_function );
386
			}
387
388
			$shortcode_insert_button_once = true;
389
		}
390
391
		/**
392
		 * Gets the shortcode insert button html.
393
		 *
394
		 * @param string $id
395
		 * @param string $search_for_id
396
		 *
397
		 * @return mixed
398
		 */
399
		public static function shortcode_button( $id = '', $search_for_id = '' ) {
400
			ob_start();
401
			?>
402
			<span class="sd-lable-shortcode-inserter">
403
				<a onclick="sd_ajax_get_picker(<?php echo $id;
404
				if ( $search_for_id ) {
405
					echo "," . $search_for_id;
406
				} ?>);" href="#TB_inline?width=100%&height=550&inlineId=super-duper-content-ajaxed"
407
				   class="thickbox button super-duper-content-open" title="Add Shortcode">
408
					<span style="vertical-align: middle;line-height: 18px;font-size: 20px;"
409
					      class="dashicons dashicons-screenoptions"></span>
410
				</a>
411
				<div id="super-duper-content-ajaxed" style="display:none;">
412
					<span>Loading</span>
413
				</div>
414
			</span>
415
416
			<?php
417
			$html = ob_get_clean();
418
419
			// remove line breaks so we can use it in js
420
			return preg_replace( "/\r|\n/", "", trim( $html ) );
421
		}
422
423
		/**
424
		 * Makes SD work with the siteOrigin page builder.
425
		 *
426
		 * @since 1.0.6
427
		 * @return mixed
428
		 */
429
		public static function siteorigin_js() {
430
			ob_start();
431
			?>
432
			<script>
433
				/**
434
				 * Check a form to see what items shoudl be shown or hidden.
435
				 */
436
				function sd_so_show_hide(form) {
437
					jQuery(form).find(".sd-argument").each(function () {
438
439
						var $element_require = jQuery(this).data('element_require');
440
441
						if ($element_require) {
442
443
							$element_require = $element_require.replace("&#039;", "'"); // replace single quotes
444
							$element_require = $element_require.replace("&quot;", '"'); // replace double quotes
445
446
							if (eval($element_require)) {
447
								jQuery(this).removeClass('sd-require-hide');
448
							} else {
449
								jQuery(this).addClass('sd-require-hide');
450
							}
451
						}
452
					});
453
				}
454
455
				/**
456
				 * Toggle advanced settings visibility.
457
				 */
458
				function sd_so_toggle_advanced($this) {
459
					var form = jQuery($this).parents('form,.form,.so-content');
460
					form.find('.sd-advanced-setting').toggleClass('sd-adv-show');
461
					return false;// prevent form submit
462
				}
463
464
				/**
465
				 * Initialise a individual widget.
466
				 */
467
				function sd_so_init_widget($this, $selector) {
468
					if (!$selector) {
469
						$selector = 'form';
470
					}
471
					// only run once.
472
					if (jQuery($this).data('sd-widget-enabled')) {
473
						return;
474
					} else {
475
						jQuery($this).data('sd-widget-enabled', true);
476
					}
477
478
					var $button = '<button title="<?php _e( 'Advanced Settings' );?>" class="button button-primary right sd-advanced-button" onclick="sd_so_toggle_advanced(this);return false;"><i class="fas fa-sliders-h" aria-hidden="true"></i></button>';
479
					var form = jQuery($this).parents('' + $selector + '');
480
481
					if (jQuery($this).val() == '1' && jQuery(form).find('.sd-advanced-button').length == 0) {
482
						jQuery(form).append($button);
483
					}
484
485
					// show hide on form change
486
					jQuery(form).change(function () {
487
						sd_so_show_hide(form);
488
					});
489
490
					// show hide on load
491
					sd_so_show_hide(form);
492
				}
493
494
				jQuery(function () {
495
					jQuery(document).on('open_dialog', function (w, e) {
496
						setTimeout(function () {
497
							if (jQuery('.so-panels-dialog-wrapper:visible .so-content.panel-dialog .sd-show-advanced').length) {
498
								console.log('exists');
499
								if (jQuery('.so-panels-dialog-wrapper:visible .so-content.panel-dialog .sd-show-advanced').val() == '1') {
500
									console.log('true');
501
									sd_so_init_widget('.so-panels-dialog-wrapper:visible .so-content.panel-dialog .sd-show-advanced', 'div');
502
								}
503
							}
504
						}, 200);
505
					});
506
				});
507
			</script>
508
			<?php
509
			$output = ob_get_clean();
510
511
			/*
512
			 * We only add the <script> tags for code highlighting, so we strip them from the output.
513
			 */
514
515
			return str_replace( array(
516
				'<script>',
517
				'</script>'
518
			), '', $output );
519
		}
520
521
		/**
522
		 * Output the JS and CSS for the shortcode insert button.
523
		 *
524
		 * @since 1.0.6
525
		 *
526
		 * @param string $editor_id
527
		 * @param string $insert_shortcode_function
528
		 */
529
		public static function shortcode_insert_button_script( $editor_id = '', $insert_shortcode_function = '' ) {
530
			?>
531
			<style>
532
				.sd-shortcode-left-wrap {
533
					float: left;
534
					width: 60%;
535
				}
536
537
				.sd-shortcode-left-wrap .gd-help-tip {
538
					float: none;
539
				}
540
541
				.sd-shortcode-left-wrap .widefat {
542
					border-spacing: 0;
543
					width: 100%;
544
					clear: both;
545
					margin: 0;
546
					border: 1px solid #ddd;
547
					box-shadow: inset 0 1px 2px rgba(0, 0, 0, .07);
548
					background-color: #fff;
549
					color: #32373c;
550
					outline: 0;
551
					transition: 50ms border-color ease-in-out;
552
					padding: 3px 5px;
553
				}
554
555
				.sd-shortcode-left-wrap input[type=checkbox].widefat {
556
					border: 1px solid #b4b9be;
557
					background: #fff;
558
					color: #555;
559
					clear: none;
560
					cursor: pointer;
561
					display: inline-block;
562
					line-height: 0;
563
					height: 16px;
564
					margin: -4px 4px 0 0;
565
					margin-top: 0;
566
					outline: 0;
567
					padding: 0 !important;
568
					text-align: center;
569
					vertical-align: middle;
570
					width: 16px;
571
					min-width: 16px;
572
					-webkit-appearance: none;
573
					box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
574
					transition: .05s border-color ease-in-out;
575
				}
576
577
				.sd-shortcode-left-wrap input[type=checkbox]:checked:before {
578
					content: "\f147";
579
					margin: -3px 0 0 -4px;
580
					color: #1e8cbe;
581
					float: left;
582
					display: inline-block;
583
					vertical-align: middle;
584
					width: 16px;
585
					font: normal 21px/1 dashicons;
586
					speak: none;
587
					-webkit-font-smoothing: antialiased;
588
					-moz-osx-font-smoothing: grayscale;
589
				}
590
591
				#sd-shortcode-output-actions button,
592
				.sd-advanced-button {
593
					color: #555;
594
					border-color: #ccc;
595
					background: #f7f7f7;
596
					box-shadow: 0 1px 0 #ccc;
597
					vertical-align: top;
598
					display: inline-block;
599
					text-decoration: none;
600
					font-size: 13px;
601
					line-height: 26px;
602
					height: 28px;
603
					margin: 0;
604
					padding: 0 10px 1px;
605
					cursor: pointer;
606
					border-width: 1px;
607
					border-style: solid;
608
					-webkit-appearance: none;
609
					border-radius: 3px;
610
					white-space: nowrap;
611
					box-sizing: border-box;
612
				}
613
614
				button.sd-advanced-button {
615
					background: #0073aa;
616
					border-color: #006799;
617
					box-shadow: inset 0 2px 0 #006799;
618
					vertical-align: top;
619
					color: #fff;
620
					text-decoration: none;
621
					text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
622
					float: right;
623
					margin-right: 3px !important;
624
					font-size: 20px !important;
625
				}
626
627
				.sd-shortcode-right-wrap {
628
					float: right;
629
					width: 35%;
630
				}
631
632
				#sd-shortcode-output {
633
					background: rgba(255, 255, 255, .5);
634
					border-color: rgba(222, 222, 222, .75);
635
					box-shadow: inset 0 1px 2px rgba(0, 0, 0, .04);
636
					color: rgba(51, 51, 51, .5);
637
					overflow: auto;
638
					padding: 2px 6px;
639
					line-height: 1.4;
640
					resize: vertical;
641
				}
642
643
				#sd-shortcode-output {
644
					height: 250px;
645
					width: 100%;
646
				}
647
648
				<?php if ( function_exists( 'generate_sections_sections_metabox' ) ) { ?>
649
				.generate-sections-modal #custom-media-buttons > .sd-lable-shortcode-inserter {
650
					display: inline;
651
				}
652
653
				<?php } ?>
654
			</style>
655
			<?php
656
			if ( class_exists( 'SiteOrigin_Panels' ) ) {
657
				echo "<script>" . self::siteorigin_js() . "</script>";
658
			}
659
			?>
660
			<script>
661
				<?php
662
				if(! empty( $insert_shortcode_function )){
663
					echo $insert_shortcode_function;
664
				}else{
665
666
				/**
667
				 * Function for super duper insert shortcode.
668
				 *
669
				 * @since 1.0.0
670
				 */
671
				?>
672
				function sd_insert_shortcode($editor_id) {
673
					$shortcode = jQuery('#TB_ajaxContent #sd-shortcode-output').val();
674
					if ($shortcode) {
675
						if (!$editor_id) {
676
							<?php
677
							if ( isset( $_REQUEST['et_fb'] ) ) {
678
								echo '$editor_id = "#main_content_content_vb_tiny_mce";';
679
							} elseif ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor' ) {
680
								echo '$editor_id = "#elementor-controls .wp-editor-container textarea";';
681
							} else {
682
								echo '$editor_id = "#wp-content-editor-container textarea";';
683
							}
684
							?>
685
						} else {
686
							$editor_id = '#' + $editor_id;
687
						}
688
						tmceActive = jQuery($editor_id).attr("aria-hidden") == "true" ? true : false;
689
						/* GeneratePress */
690
						if (jQuery('#generate-sections-modal-dialog ' + $editor_id).length) {
691
							$editor_id = '#generate-sections-modal-dialog ' + $editor_id;
692
							tmceActive = jQuery($editor_id).closest('.wp-editor-wrap').hasClass('tmce-active') ? true : false;
693
						}
694
						if (tinyMCE && tinyMCE.activeEditor && tmceActive) {
695
							tinyMCE.execCommand('mceInsertContent', false, $shortcode);
696
						} else {
697
							var $txt = jQuery($editor_id);
698
							var caretPos = $txt[0].selectionStart;
699
							var textAreaTxt = $txt.val();
700
							var txtToAdd = $shortcode;
701
							var textareaValue = textAreaTxt.substring(0, caretPos) + txtToAdd + textAreaTxt.substring(caretPos);
702
							$txt.focus().val(textareaValue).change().keydown().blur().keyup().keypress().trigger('input').trigger('change');
703
704
							// set Divi react input value
705
							var input = document.getElementById("main_content_content_vb_tiny_mce");
706
							if (input) {
707
								sd_setNativeValue(input, textareaValue);
708
							}
709
710
						}
711
						tb_remove();
712
					}
713
				}
714
715
				/*
716
				 Set the value of elements controled via react.
717
				 */
718
				function sd_setNativeValue(element, value) {
719
					let lastValue = element.value;
720
					element.value = value;
721
					let event = new Event("input", {target: element, bubbles: true});
722
					// React 15
723
					event.simulated = true;
724
					// React 16
725
					let tracker = element._valueTracker;
726
					if (tracker) {
727
						tracker.setValue(lastValue);
728
					}
729
					element.dispatchEvent(event);
730
				}
731
				<?php }?>
732
733
				/*
734
				 Copies the shortcode to the clipboard.
735
				 */
736
				function sd_copy_to_clipboard() {
737
					/* Get the text field */
738
					var copyText = document.querySelector("#TB_ajaxContent #sd-shortcode-output");
739
					//un-disable the field
740
					copyText.disabled = false;
741
					/* Select the text field */
742
					copyText.select();
743
					/* Copy the text inside the text field */
744
					document.execCommand("Copy");
745
					//re-disable the field
746
					copyText.disabled = true;
747
					/* Alert the copied text */
748
					alert("Copied the text: " + copyText.value);
749
				}
750
751
				/*
752
				 Gets the shortcode options.
753
				 */
754
				function sd_get_shortcode_options($this) {
755
756
					$short_code = jQuery($this).val();
757
					if ($short_code) {
758
759
						var data = {
760
							'action': 'super_duper_get_widget_settings',
761
							'shortcode': $short_code,
762
							'attributes': 123,
763
							'post_id': 321,
764
							'_ajax_nonce': '<?php echo wp_create_nonce( 'super_duper_output_shortcode' );?>'
765
						};
766
767
						if (typeof ajaxurl === 'undefined') {
768
							var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' );?>";
769
						}
770
771
						jQuery.post(ajaxurl, data, function (response) {
772
							jQuery('#TB_ajaxContent .sd-shortcode-settings').html(response);
773
774
							jQuery('#' + $short_code).on('change', 'select', function () {
775
								sd_build_shortcode($short_code);
776
							}); // take care of select tags
777
778
							jQuery('#' + $short_code).on('change keypress keyup', 'input,textarea', function () {
779
								sd_build_shortcode($short_code);
780
							});
781
782
							sd_build_shortcode($short_code);
783
784
							// resize the window to fit
785
							setTimeout(function () {
786
								jQuery('#TB_ajaxContent').css('width', 'auto').css('height', '75vh');
787
							}, 200);
788
789
790
							return response;
791
						});
792
					}
793
794
				}
795
796
				/*
797
				 Builds and inserts the shortcode into the viewer.
798
				 */
799
				function sd_build_shortcode($id) {
800
801
					var multiSelects = {};
802
					var multiSelectsRemove = [];
803
804
					$output = "[" + $id;
805
806
					$form_data = jQuery("#" + $id).serializeArray();
807
808
					// run checks for multiselects
809
					jQuery.each($form_data, function (index, element) {
810
						if (element && element.value) {
811
							$field_name = element.name.substr(element.name.indexOf("][") + 2);
812
							$field_name = $field_name.replace("]", "");
813
							// check if its a multiple
814
							if ($field_name.includes("[]")) {
815
								multiSelectsRemove[multiSelectsRemove.length] = index;
816
								$field_name = $field_name.replace("[]", "");
817
								if ($field_name in multiSelects) {
818
									multiSelects[$field_name] = multiSelects[$field_name] + "," + element.value;
819
								} else {
820
									multiSelects[$field_name] = element.value;
821
								}
822
							}
823
						}
824
					});
825
826
					// fix multiselects if any are found
827
					if (multiSelectsRemove.length) {
828
829
						// remove all multiselects
830
						multiSelectsRemove.reverse();
831
						multiSelectsRemove.forEach(function (index) {
832
							$form_data.splice(index, 1);
833
						});
834
835
						$ms_arr = [];
836
						// add multiselets back
837
						jQuery.each(multiSelects, function (index, value) {
838
							$ms_arr[$ms_arr.length] = {"name": "[][" + index + "]", "value": value};
839
						});
840
						$form_data = $form_data.concat($ms_arr);
841
					}
842
843
844
					if ($form_data) {
845
						$content = '';
846
						$form_data.forEach(function (element) {
847
848
							if (element.value) {
849
								$field_name = element.name.substr(element.name.indexOf("][") + 2);
850
								$field_name = $field_name.replace("]", "");
851
								if ($field_name == 'html') {
852
									$content = element.value;
853
								} else {
854
									$output = $output + " " + $field_name + '="' + element.value + '"';
855
								}
856
							}
857
858
						});
859
					}
860
					$output = $output + "]";
861
862
					// check for content field
863
					if ($content) {
864
						$output = $output + $content + "[/" + $id + "]";
865
					}
866
867
					jQuery('#TB_ajaxContent #sd-shortcode-output').html($output);
868
				}
869
870
871
				/*
872
				 Delay the init of the textareas for 1 second.
873
				 */
874
				(function () {
875
					setTimeout(function () {
876
						sd_init_textareas();
877
					}, 1000);
878
				})();
879
880
				/*
881
				 Init the textareas to be able to show the shortcode builder button.
882
				 */
883
				function sd_init_textareas() {
884
885
					// General textareas
886
					jQuery(document).on('focus', 'textarea', function () {
887
888
						if (jQuery(this).hasClass('wp-editor-area')) {
889
							// insert the shortcode button to the textarea lable if not there already
890
							if (!jQuery(this).parent().find('.sd-lable-shortcode-inserter').length) {
891
								jQuery(this).parent().find('.quicktags-toolbar').append(sd_shortcode_button(jQuery(this).attr('id')));
892
							}
893
						} else {
894
							// insert the shortcode button to the textarea lable if not there already
895
							if (!jQuery("label[for='" + jQuery(this).attr('id') + "']").find('.sd-lable-shortcode-inserter').length) {
896
								jQuery("label[for='" + jQuery(this).attr('id') + "']").append(sd_shortcode_button(jQuery(this).attr('id')));
897
							}
898
						}
899
					});
900
901
					// The below tries to add the shorcode builder button to the builders own raw/shortcode sections.
902
903
					// DIVI
904
					jQuery(document).on('focusin', '.et-fb-codemirror', function () {
905
						// insert the shortcode button to the textarea lable if not there already
906
						if (!jQuery(this).closest('.et-fb-form__group').find('.sd-lable-shortcode-inserter').length) {
907
							jQuery(this).closest('.et-fb-form__group').find('.et-fb-form__label-text').append(sd_shortcode_button());
908
						}
909
					});
910
911
					// Beaver
912
					jQuery(document).on('focusin', '.fl-code-field', function () {
913
						// insert the shortcode button to the textarea lable if not there already
914
						if (!jQuery(this).closest('.fl-field-control-wrapper').find('.sd-lable-shortcode-inserter').length) {
915
							jQuery(this).closest('.fl-field-control-wrapper').prepend(sd_shortcode_button());
916
						}
917
					});
918
919
					// Fushion builder (avada)
920
					jQuery(document).on('focusin', '.CodeMirror.cm-s-default', function () {
921
						// insert the shortcode button to the textarea lable if not there already
922
						if (!jQuery(this).parent().find('.sd-lable-shortcode-inserter').length) {
923
							jQuery(sd_shortcode_button()).insertBefore(this);
924
						}
925
					});
926
927
					// Avia builder (enfold)
928
					jQuery(document).on('focusin', '#aviaTBcontent', function () {
929
						// insert the shortcode button to the textarea lable if not there already
930
						if (!jQuery(this).parent().parent().find('.avia-name-description ').find('.sd-lable-shortcode-inserter').length) {
931
							jQuery(this).parent().parent().find('.avia-name-description strong').append(sd_shortcode_button(jQuery(this).attr('id')));
932
						}
933
					});
934
935
					// Cornerstone textareas
936
					jQuery(document).on('focusin', '.cs-control.cs-control-textarea', function () {
937
						// insert the shortcode button to the textarea lable if not there already
938
						if (!jQuery(this).find('.cs-control-header label').find('.sd-lable-shortcode-inserter').length) {
939
							jQuery(this).find('.cs-control-header label').append(sd_shortcode_button());
940
						}
941
					});
942
943
					// Cornerstone main bar
944
					setTimeout(function () {
945
						// insert the shortcode button to the textarea lable if not there already
946
						if (!jQuery('.cs-bar-btns').find('.sd-lable-shortcode-inserter').length) {
947
							jQuery('<li style="text-align: center;padding: 5px;list-style: none;">' + sd_shortcode_button() + '</li>').insertBefore('.cs-action-toggle-custom-css');
948
						}
949
					}, 2000);
950
951
952
					// WP Bakery, code editor does not render shortcodes.
953
//					jQuery(document).on('focusin', '.wpb-textarea_raw_html', function () {
954
//						// insert the shortcode button to the textarea lable if not there already
955
//						if(!jQuery(this).parent().parent().find('.wpb_element_label').find('.sd-lable-shortcode-inserter').length){
956
//							jQuery(this).parent().parent().find('.wpb_element_label').append(sd_shortcode_button());
957
//						}
958
//					});
959
960
				}
961
962
				/**
963
				 * Gets the html for the picker via ajax and updates it on the fly.
964
				 *
965
				 * @param $id
966
				 * @param $search
967
				 */
968
				function sd_ajax_get_picker($id, $search) {
969
					if ($search) {
970
						$this = $id;
971
						$id = jQuery($this).closest('.wp-editor-wrap').find('.wp-editor-container textarea').attr('id');
972
					}
973
974
					var data = {
975
						'action': 'super_duper_get_picker',
976
						'editor_id': $id,
977
						'_ajax_nonce': '<?php echo wp_create_nonce( 'super_duper_picker' );?>'
978
					};
979
980
					if (!ajaxurl) {
981
						var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
982
					}
983
984
					jQuery.post(ajaxurl, data, function (response) {
985
						jQuery('#TB_ajaxContent').html(response);
986
						//return response;
987
					}).then(function (env) {
988
						jQuery('body').on('thickbox:removed', function () {
989
							jQuery('#super-duper-content-ajaxed').html('');
990
						});
991
					});
992
				}
993
994
				/**
995
				 * Get the html for the shortcode inserter button depending on if a textarea id is available.
996
				 *
997
				 * @param $id string The textarea id.
998
				 * @returns {string}
999
				 */
1000
				function sd_shortcode_button($id) {
1001
					if ($id) {
1002
						return '<?php echo self::shortcode_button( "\\''+\$id+'\\'" );?>';
1003
					} else {
1004
						return '<?php echo self::shortcode_button();?>';
1005
					}
1006
				}
1007
1008
			</script>
1009
			<?php
1010
		}
1011
1012
		/**
1013
		 * Gets some CSS for the widgets screen.
1014
		 *
1015
		 * @param bool $advanced If we should include advanced CSS.
1016
		 *
1017
		 * @return mixed
1018
		 */
1019
		public function widget_css( $advanced = true ) {
1020
			ob_start();
1021
			?>
1022
			<style>
1023
				<?php if( $advanced ){ ?>
1024
				.sd-advanced-setting {
1025
					display: none;
1026
				}
1027
1028
				.sd-advanced-setting.sd-adv-show {
1029
					display: block;
1030
				}
1031
1032
				.sd-argument.sd-require-hide,
1033
				.sd-advanced-setting.sd-require-hide {
1034
					display: none;
1035
				}
1036
1037
				button.sd-advanced-button {
1038
					margin-right: 3px !important;
1039
					font-size: 20px !important;
1040
				}
1041
1042
				<?php } ?>
1043
1044
				button.sd-toggle-group-button {
1045
					background-color: #f3f3f3;
1046
					color: #23282d;
1047
					cursor: pointer;
1048
					padding: 10px;
1049
					width: 100%;
1050
					border: none;
1051
					text-align: left;
1052
					outline: none;
1053
					font-size: 13px;
1054
					font-weight: bold;
1055
					margin-bottom: 1px;
1056
				}
1057
			</style>
1058
			<?php
1059
			$output = ob_get_clean();
1060
1061
			/*
1062
			 * We only add the <script> tags for code highlighting, so we strip them from the output.
1063
			 */
1064
1065
			return str_replace( array(
1066
				'<style>',
1067
				'</style>'
1068
			), '', $output );
1069
		}
1070
1071
		/**
1072
		 * Gets some JS for the widgets screen.
1073
		 *
1074
		 * @return mixed
1075
		 */
1076
		public function widget_js() {
1077
			ob_start();
1078
			?>
1079
			<script>
1080
1081
				/**
1082
				 * Toggle advanced settings visibility.
1083
				 */
1084
				function sd_toggle_advanced($this) {
1085
					var form = jQuery($this).parents('form,.form');
1086
					form.find('.sd-advanced-setting').toggleClass('sd-adv-show');
1087
					return false;// prevent form submit
1088
				}
1089
1090
				/**
1091
				 * Check a form to see what items shoudl be shown or hidden.
1092
				 */
1093
				function sd_show_hide(form) {
1094
					console.log('show/hide');
1095
					jQuery(form).find(".sd-argument").each(function () {
1096
1097
						var $element_require = jQuery(this).data('element_require');
1098
1099
						if ($element_require) {
1100
1101
							$element_require = $element_require.replace("&#039;", "'"); // replace single quotes
1102
							$element_require = $element_require.replace("&quot;", '"'); // replace double quotes
1103
1104
							if (eval($element_require)) {
1105
								jQuery(this).removeClass('sd-require-hide');
1106
							} else {
1107
								jQuery(this).addClass('sd-require-hide');
1108
							}
1109
						}
1110
					});
1111
				}
1112
1113
				/**
1114
				 * Initialise widgets from the widgets screen.
1115
				 */
1116
				function sd_init_widgets($selector) {
1117
					jQuery(".sd-show-advanced").each(function (index) {
1118
						sd_init_widget(this, $selector);
1119
					});
1120
				}
1121
1122
				/**
1123
				 * Initialise a individual widget.
1124
				 */
1125
				function sd_init_widget($this, $selector) {
1126
					console.log($selector);
1127
1128
					if (!$selector) {
1129
						$selector = 'form';
1130
					}
1131
					// only run once.
1132
					if (jQuery($this).data('sd-widget-enabled')) {
1133
						return;
1134
					} else {
1135
						jQuery($this).data('sd-widget-enabled', true);
1136
					}
1137
1138
					var $button = '<button title="<?php _e( 'Advanced Settings' );?>" style="line-height: 28px;" class="button button-primary right sd-advanced-button" onclick="sd_toggle_advanced(this);return false;"><span class="dashicons dashicons-admin-settings" style="width: 28px;font-size: 28px;"></span></button>';
1139
					var form = jQuery($this).parents('' + $selector + '');
1140
1141
					if (jQuery($this).val() == '1' && jQuery(form).find('.sd-advanced-button').length == 0) {
1142
						console.log('add advanced button');
1143
1144
						jQuery(form).find('.widget-control-save').after($button);
1145
					} else {
1146
						console.log('no advanced button');
1147
						console.log(jQuery($this).val());
1148
						console.log(jQuery(form).find('.sd-advanced-button').length);
1149
1150
					}
1151
1152
					// show hide on form change
1153
					jQuery(form).change(function () {
1154
						sd_show_hide(form);
1155
					});
1156
1157
					// show hide on load
1158
					sd_show_hide(form);
1159
				}
1160
1161
				/**
1162
				 * Init a customizer widget.
1163
				 */
1164
				function sd_init_customizer_widget(section) {
1165
					if (section.expanded) {
1166
						section.expanded.bind(function (isExpanding) {
1167
							if (isExpanding) {
1168
								// is it a SD widget?
1169
								if (jQuery(section.container).find('.sd-show-advanced').length) {
1170
									// init the widget
1171
									sd_init_widget(jQuery(section.container).find('.sd-show-advanced'), ".form");
1172
								}
1173
							}
1174
						});
1175
					}
1176
				}
1177
1178
				/**
1179
				 * If on widgets screen.
1180
				 */
1181
				jQuery(function () {
1182
					// if not in customizer.
1183
					if (!wp.customize) {
1184
						sd_init_widgets("form");
1185
					}
1186
1187
					// init on widget added
1188
					jQuery(document).on('widget-added', function (e, widget) {
1189
						console.log('widget added');
1190
						// is it a SD widget?
1191
						if (jQuery(widget).find('.sd-show-advanced').length) {
1192
							// init the widget
1193
							sd_init_widget(jQuery(widget).find('.sd-show-advanced'), "form");
1194
						}
1195
					});
1196
1197
					// inint on widget updated
1198
					jQuery(document).on('widget-updated', function (e, widget) {
1199
						console.log('widget updated');
1200
1201
						// is it a SD widget?
1202
						if (jQuery(widget).find('.sd-show-advanced').length) {
1203
							// init the widget
1204
							sd_init_widget(jQuery(widget).find('.sd-show-advanced'), "form");
1205
						}
1206
					});
1207
1208
				});
1209
1210
1211
				/**
1212
				 * We need to run this before jQuery is ready
1213
				 */
1214
				if (wp.customize) {
1215
					wp.customize.bind('ready', function () {
1216
1217
						// init widgets on load
1218
						wp.customize.control.each(function (section) {
1219
							sd_init_customizer_widget(section);
1220
						});
1221
1222
						// init widgets on add
1223
						wp.customize.control.bind('add', function (section) {
1224
							sd_init_customizer_widget(section);
1225
						});
1226
1227
					});
1228
1229
				}
1230
				<?php do_action( 'wp_super_duper_widget_js', $this ); ?>
1231
			</script>
1232
			<?php
1233
			$output = ob_get_clean();
1234
1235
			/*
1236
			 * We only add the <script> tags for code highlighting, so we strip them from the output.
1237
			 */
1238
1239
			return str_replace( array(
1240
				'<script>',
1241
				'</script>'
1242
			), '', $output );
1243
		}
1244
1245
1246
		/**
1247
		 * Set the name from the argument key.
1248
		 *
1249
		 * @param $options
1250
		 *
1251
		 * @return mixed
1252
		 */
1253
		private function add_name_from_key( $options, $arguments = false ) {
1254
			if ( ! empty( $options['arguments'] ) ) {
1255
				foreach ( $options['arguments'] as $key => $val ) {
1256
					$options['arguments'][ $key ]['name'] = $key;
1257
				}
1258
			} elseif ( $arguments && is_array( $options ) && ! empty( $options ) ) {
1259
				foreach ( $options as $key => $val ) {
1260
					$options[ $key ]['name'] = $key;
1261
				}
1262
			}
1263
1264
			return $options;
1265
		}
1266
1267
		/**
1268
		 * Register the parent shortcode.
1269
		 *
1270
		 * @since 1.0.0
1271
		 */
1272
		public function register_shortcode() {
1273
			add_shortcode( $this->base_id, array( $this, 'shortcode_output' ) );
1274
			add_action( 'wp_ajax_super_duper_output_shortcode', array( __CLASS__, 'render_shortcode' ) );
1275
		}
1276
1277
		/**
1278
		 * Render the shortcode via ajax so we can return it to Gutenberg.
1279
		 *
1280
		 * @since 1.0.0
1281
		 */
1282
		public static function render_shortcode() {
1283
1284
			check_ajax_referer( 'super_duper_output_shortcode', '_ajax_nonce', true );
1285
			if ( ! current_user_can( 'manage_options' ) ) {
1286
				wp_die();
1287
			}
1288
1289
			// we might need the $post value here so lets set it.
1290
			if ( isset( $_POST['post_id'] ) && $_POST['post_id'] ) {
1291
				$post_obj = get_post( absint( $_POST['post_id'] ) );
1292
				if ( ! empty( $post_obj ) && empty( $post ) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $post seems to never exist and therefore empty should always be true.
Loading history...
1293
					global $post;
1294
					$post = $post_obj;
1295
				}
1296
			}
1297
1298
			if ( isset( $_POST['shortcode'] ) && $_POST['shortcode'] ) {
1299
				$shortcode_name   = sanitize_title_with_dashes( $_POST['shortcode'] );
1300
				$attributes_array = isset( $_POST['attributes'] ) && $_POST['attributes'] ? $_POST['attributes'] : array();
1301
				$attributes       = '';
1302
				if ( ! empty( $attributes_array ) ) {
1303
					foreach ( $attributes_array as $key => $value ) {
1304
						$attributes .= " " . sanitize_title_with_dashes( $key ) . "='" . wp_slash( $value ) . "' ";
1305
					}
1306
				}
1307
1308
				$shortcode = "[" . $shortcode_name . " " . $attributes . "]";
1309
1310
				echo do_shortcode( $shortcode );
0 ignored issues
show
Security Cross-Site Scripting introduced by
do_shortcode($shortcode) can contain request data and is used in output context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: Read from $_POST, and Data is passed through sanitize_title_with_dashes(), and sanitize_title_with_dashes($_POST['shortcode']) is assigned to $shortcode_name in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1299
  1. Read from $_POST, and Data is passed through sanitize_title_with_dashes(), and sanitize_title_with_dashes($_POST['shortcode']) is assigned to $shortcode_name
    in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1299
  2. '[' . $shortcode_name . ' ' . $attributes . ']' is assigned to $shortcode
    in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1308
  3. Data is passed through do_shortcode()
    in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1310
  2. Path: Read from $_POST, and IssetNode && $_POST['attributes'] ? $_POST['attributes'] : array() is assigned to $attributes_array in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1300
  1. Read from $_POST, and IssetNode && $_POST['attributes'] ? $_POST['attributes'] : array() is assigned to $attributes_array
    in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1300
  2. $attributes_array is assigned to $key
    in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1303
  3. Data is passed through sanitize_title_with_dashes(), and ' ' . sanitize_title_with_dashes($key) . '='' . wp_slash($value) . '' ' is assigned to $attributes
    in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1304
  4. '[' . $shortcode_name . ' ' . $attributes . ']' is assigned to $shortcode
    in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1308
  5. Data is passed through do_shortcode()
    in vendor/ayecode/wp-super-duper/wp-super-duper.php on line 1310

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
1311
1312
			}
1313
			wp_die();
1314
		}
1315
1316
		/**
1317
		 * Output the shortcode.
1318
		 *
1319
		 * @param array $args
1320
		 * @param string $content
1321
		 *
1322
		 * @return string
1323
		 */
1324
		public function shortcode_output( $args = array(), $content = '' ) {
1325
			$args = $this->argument_values( $args );
1326
1327
			// add extra argument so we know its a output to gutenberg
1328
			//$args
1329
			$args = $this->string_to_bool( $args );
1330
1331
			// if we have a enclosed shortcode we add it to the special `html` argument
1332
			if ( ! empty( $content ) ) {
1333
				$args['html'] = $content;
1334
			}
1335
1336
			$class = isset( $this->options['widget_ops']['classname'] ) ? esc_attr( $this->options['widget_ops']['classname'] ) : '';
1337
			$class .= " sdel-".$this->get_instance_hash();
1338
1339
			$class = apply_filters( 'wp_super_duper_div_classname', $class, $args, $this );
1340
			$class = apply_filters( 'wp_super_duper_div_classname_' . $this->base_id, $class, $args, $this );
1341
1342
			$attrs = apply_filters( 'wp_super_duper_div_attrs', '', $args, $this );
0 ignored issues
show
Unused Code introduced by
The assignment to $attrs is dead and can be removed.
Loading history...
1343
			$attrs = apply_filters( 'wp_super_duper_div_attrs_' . $this->base_id, '', $args, $this ); //@todo this does not seem right @kiran?
1344
1345
			$shortcode_args = array();
1346
			$output         = '';
1347
			$no_wrap        = isset( $this->options['no_wrap'] ) && $this->options['no_wrap'] ? true : false;
1348
			if ( isset( $args['no_wrap'] ) && $args['no_wrap'] ) {
1349
				$no_wrap = true;
1350
			}
1351
			$main_content = $this->output( $args, $shortcode_args, $content );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $main_content is correct as $this->output($args, $shortcode_args, $content) targeting WP_Super_Duper::output() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
1352
			if ( $main_content && ! $no_wrap ) {
0 ignored issues
show
introduced by
$main_content is defined implicitly as null, thus it is always evaluated to false.
Loading history...
1353
				// wrap the shortcode in a div with the same class as the widget
1354
				$output .= '<div class="' . $class . '" ' . $attrs . '>';
1355
				if ( ! empty( $args['title'] ) ) {
1356
					// if its a shortcode and there is a title try to grab the title wrappers
1357
					$shortcode_args = array( 'before_title' => '', 'after_title' => '' );
1358
					if ( empty( $instance ) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $instance seems to never exist and therefore empty should always be true.
Loading history...
1359
						global $wp_registered_sidebars;
1360
						if ( ! empty( $wp_registered_sidebars ) ) {
1361
							foreach ( $wp_registered_sidebars as $sidebar ) {
1362
								if ( ! empty( $sidebar['before_title'] ) ) {
1363
									$shortcode_args['before_title'] = $sidebar['before_title'];
1364
									$shortcode_args['after_title']  = $sidebar['after_title'];
1365
									break;
1366
								}
1367
							}
1368
						}
1369
					}
1370
					$output .= $this->output_title( $shortcode_args, $args );
1371
				}
1372
				$output .= $main_content;
1373
				$output .= '</div>';
1374
			} elseif ( $main_content && $no_wrap ) {
0 ignored issues
show
introduced by
$main_content is defined implicitly as null, thus it is always evaluated to false.
Loading history...
1375
				$output .= $main_content;
1376
			}
1377
1378
			// if preview show a placeholder if empty
1379
			if ( $this->is_preview() && $output == '' ) {
1380
				$output = $this->preview_placeholder_text( "{{" . $this->base_id . "}}" );
1381
			}
1382
1383
			return apply_filters( 'wp_super_duper_widget_output', $output, $args, $shortcode_args, $this );
1384
		}
1385
1386
		/**
1387
		 * Placeholder text to show if output is empty and we are on a preview/builder page.
1388
		 *
1389
		 * @param string $name
1390
		 *
1391
		 * @return string
1392
		 */
1393
		public function preview_placeholder_text( $name = '' ) {
1394
			return "<div style='background:#0185ba33;padding: 10px;border: 4px #ccc dashed;'>" . sprintf( __( 'Placeholder for: %s' ), $name ) . "</div>";
1395
		}
1396
1397
		/**
1398
		 * Sometimes booleans values can be turned to strings, so we fix that.
1399
		 *
1400
		 * @param $options
1401
		 *
1402
		 * @return mixed
1403
		 */
1404
		public function string_to_bool( $options ) {
1405
			// convert bool strings to booleans
1406
			foreach ( $options as $key => $val ) {
1407
				if ( $val == 'false' ) {
1408
					$options[ $key ] = false;
1409
				} elseif ( $val == 'true' ) {
1410
					$options[ $key ] = true;
1411
				}
1412
			}
1413
1414
			return $options;
1415
		}
1416
1417
		/**
1418
		 * Get the argument values that are also filterable.
1419
		 *
1420
		 * @param $instance
1421
		 *
1422
		 * @since 1.0.12 Don't set checkbox default value if the value is empty.
1423
		 *
1424
		 * @return array
1425
		 */
1426
		public function argument_values( $instance ) {
1427
			$argument_values = array();
1428
1429
			// set widget instance
1430
			$this->instance = $instance;
1431
1432
			if ( empty( $this->arguments ) ) {
1433
				$this->arguments = $this->get_arguments();
1434
			}
1435
1436
			if ( ! empty( $this->arguments ) ) {
1437
				foreach ( $this->arguments as $key => $args ) {
1438
					// set the input name from the key
1439
					$args['name'] = $key;
1440
					//
1441
					$argument_values[ $key ] = isset( $instance[ $key ] ) ? $instance[ $key ] : '';
1442
					if ( $args['type'] == 'checkbox' && $argument_values[ $key ] == '' ) {
1443
						// don't set default for an empty checkbox
1444
					} elseif ( $argument_values[ $key ] == '' && isset( $args['default'] ) ) {
1445
						$argument_values[ $key ] = $args['default'];
1446
					}
1447
				}
1448
			}
1449
1450
			return $argument_values;
1451
		}
1452
1453
		/**
1454
		 * Set arguments in super duper.
1455
		 *
1456
		 * @since 1.0.0
1457
		 *
1458
		 * @return array Set arguments.
1459
		 */
1460
		public function set_arguments() {
1461
			return $this->arguments;
1462
		}
1463
1464
		/**
1465
		 * Get arguments in super duper.
1466
		 *
1467
		 * @since 1.0.0
1468
		 *
1469
		 * @return array Get arguments.
1470
		 */
1471
		public function get_arguments() {
1472
			if ( empty( $this->arguments ) ) {
1473
				$this->arguments = $this->set_arguments();
1474
			}
1475
1476
			$this->arguments = apply_filters( 'wp_super_duper_arguments', $this->arguments, $this->options, $this->instance );
1477
			$this->arguments = $this->add_name_from_key( $this->arguments, true );
1478
1479
			return $this->arguments;
1480
		}
1481
1482
		/**
1483
		 * This is the main output class for all 3 items, widget, shortcode and block, it is extended in the calling class.
1484
		 *
1485
		 * @param array $args
1486
		 * @param array $widget_args
1487
		 * @param string $content
1488
		 */
1489
		public function output( $args = array(), $widget_args = array(), $content = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $widget_args is not used and could be removed. ( Ignorable by Annotation )

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

1489
		public function output( $args = array(), /** @scrutinizer ignore-unused */ $widget_args = array(), $content = '' ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1490
1491
		}
1492
1493
		/**
1494
		 * Add the dynamic block code inline when the wp-block in enqueued.
1495
		 */
1496
		public function register_block() {
1497
			wp_add_inline_script( 'wp-blocks', $this->block() );
1498
			if ( class_exists( 'SiteOrigin_Panels' ) ) {
1499
				wp_add_inline_script( 'wp-blocks', $this->siteorigin_js() );
1500
			}
1501
		}
1502
1503
		/**
1504
		 * Check if we need to show advanced options.
1505
		 *
1506
		 * @return bool
1507
		 */
1508
		public function block_show_advanced() {
1509
1510
			$show      = false;
1511
			$arguments = $this->arguments;
1512
1513
			if ( empty( $arguments ) ) {
1514
				$arguments = $this->get_arguments();
1515
			}
1516
1517
			if ( ! empty( $arguments ) ) {
1518
				foreach ( $arguments as $argument ) {
1519
					if ( isset( $argument['advanced'] ) && $argument['advanced'] ) {
1520
						$show = true;
1521
						break; // no need to continue if we know we have it
1522
					}
1523
				}
1524
			}
1525
1526
			return $show;
1527
		}
1528
1529
		/**
1530
		 * Get the url path to the current folder.
1531
		 *
1532
		 * @return string
1533
		 */
1534
		public function get_url() {
1535
1536
			$url = $this->url;
1537
1538
			if ( ! $url ) {
1539
				// check if we are inside a plugin
1540
				$file_dir = str_replace( "/includes", "", dirname( __FILE__ ) );
1541
1542
				$dir_parts = explode( "/wp-content/", $file_dir );
1543
				$url_parts = explode( "/wp-content/", plugins_url() );
1544
1545
				if ( ! empty( $url_parts[0] ) && ! empty( $dir_parts[1] ) ) {
1546
					$url       = trailingslashit( $url_parts[0] . "/wp-content/" . $dir_parts[1] );
1547
					$this->url = $url;
1548
				}
1549
			}
1550
1551
1552
			return $url;
1553
		}
1554
1555
		/**
1556
		 * Generate the block icon.
1557
		 *
1558
		 * Enables the use of Font Awesome icons.
1559
		 *
1560
		 * @note xlink:href is actually deprecated but href is not supported by all so we use both.
1561
		 *
1562
		 * @param $icon
1563
		 *
1564
		 * @since 1.1.0
1565
		 * @return string
1566
		 */
1567
		public function get_block_icon( $icon ) {
1568
1569
			// check if we have a Font Awesome icon
1570
			$fa_type = '';
1571
			if ( substr( $icon, 0, 7 ) === "fas fa-" ) {
1572
				$fa_type = 'solid';
1573
			} elseif ( substr( $icon, 0, 7 ) === "far fa-" ) {
1574
				$fa_type = 'regular';
1575
			} elseif ( substr( $icon, 0, 7 ) === "fab fa-" ) {
1576
				$fa_type = 'brands';
1577
			} else {
1578
				$icon = "'" . $icon . "'";
1579
			}
1580
1581
			// set the icon if we found one
1582
			if ( $fa_type ) {
1583
				$fa_icon = str_replace( array( "fas fa-", "far fa-", "fab fa-" ), "", $icon );
1584
				$icon    = "el('svg',{width: 20, height: 20, viewBox: '0 0 20 20'},el('use', {'xlink:href': '" . $this->get_url() . "icons/" . $fa_type . ".svg#" . $fa_icon . "','href': '" . $this->get_url() . "icons/" . $fa_type . ".svg#" . $fa_icon . "'}))";
1585
			}
1586
1587
			return $icon;
1588
		}
1589
1590
		public function group_arguments( $arguments ) {
1591
//			echo '###';print_r($arguments);
1592
			if ( ! empty( $arguments ) ) {
1593
				$temp_arguments = array();
1594
				$general        = __( "General" );
1595
				$add_sections   = false;
1596
				foreach ( $arguments as $key => $args ) {
1597
					if ( isset( $args['group'] ) ) {
1598
						$temp_arguments[ $args['group'] ][ $key ] = $args;
1599
						$add_sections                             = true;
1600
					} else {
1601
						$temp_arguments[ $general ][ $key ] = $args;
1602
					}
1603
				}
1604
1605
				// only add sections if more than one
1606
				if ( $add_sections ) {
1607
					$arguments = $temp_arguments;
1608
				}
1609
			}
1610
1611
//			echo '###';print_r($arguments);
1612
			return $arguments;
1613
		}
1614
1615
1616
		/**
1617
		 * Output the JS for building the dynamic Guntenberg block.
1618
		 *
1619
		 * @since 1.0.4 Added block_wrap property which will set the block wrapping output element ie: div, span, p or empty for no wrap.
1620
		 * @since 1.0.9 Save numbers as numbers and not strings.
1621
		 * @since 1.1.0 Font Awesome classes can be used for icons.
1622
		 * @return mixed
1623
		 */
1624
		public function block() {
1625
			ob_start();
1626
			?>
1627
			<script>
1628
				/**
1629
				 * BLOCK: Basic
1630
				 *
1631
				 * Registering a basic block with Gutenberg.
1632
				 * Simple block, renders and saves the same content without any interactivity.
1633
				 *
1634
				 * Styles:
1635
				 *        editor.css — Editor styles for the block.
1636
				 *        style.css  — Editor & Front end styles for the block.
1637
				 */
1638
				(function () {
1639
					var __ = wp.i18n.__; // The __() for internationalization.
1640
					var el = wp.element.createElement; // The wp.element.createElement() function to create elements.
1641
					var editable = wp.blocks.Editable;
1642
					var blocks = wp.blocks;
1643
					var registerBlockType = wp.blocks.registerBlockType; // The registerBlockType() to register blocks.
1644
					var is_fetching = false;
1645
					var prev_attributes = [];
1646
1647
					var term_query_type = '';
1648
					var post_type_rest_slugs = <?php if(! empty( $this->arguments ) && isset($this->arguments['post_type']['onchange_rest']['values'])){echo "[".json_encode($this->arguments['post_type']['onchange_rest']['values'])."]";}else{echo "[]";} ?>;
1649
					const taxonomies_<?php echo str_replace("-","_", $this->id);?> = [{label: "Please wait", value: 0}];
1650
1651
					/**
1652
					 * Register Basic Block.
1653
					 *
1654
					 * Registers a new block provided a unique name and an object defining its
1655
					 * behavior. Once registered, the block is made available as an option to any
1656
					 * editor interface where blocks are implemented.
1657
					 *
1658
					 * @param  {string}   name     Block name.
1659
					 * @param  {Object}   settings Block settings.
1660
					 * @return {?WPBlock}          The block, if it has been successfully
1661
					 *                             registered; otherwise `undefined`.
1662
					 */
1663
					registerBlockType('<?php echo str_replace( "_", "-", sanitize_title_with_dashes( $this->options['textdomain'] ) . '/' . sanitize_title_with_dashes( $this->options['class_name'] ) );  ?>', { // Block name. Block names must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
1664
						title: '<?php echo addslashes( $this->options['name'] ); ?>', // Block title.
1665
						description: '<?php echo addslashes( $this->options['widget_ops']['description'] )?>', // Block title.
1666
						icon: <?php echo $this->get_block_icon( $this->options['block-icon'] );?>,//'<?php echo isset( $this->options['block-icon'] ) ? esc_attr( $this->options['block-icon'] ) : 'shield-alt';?>', // Block icon from Dashicons → https://developer.wordpress.org/resource/dashicons/.
1667
						supports: {
1668
							<?php
1669
							if ( isset( $this->options['block-supports'] ) ) {
1670
								echo $this->array_to_attributes( $this->options['block-supports'] );
1671
							}
1672
							?>
1673
						},
1674
						category: '<?php echo isset( $this->options['block-category'] ) ? esc_attr( $this->options['block-category'] ) : 'common';?>', // Block category — Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
1675
						<?php if ( isset( $this->options['block-keywords'] ) ) {
1676
						echo "keywords : " . $this->options['block-keywords'] . ",";
1677
					}?>
1678
1679
						<?php
1680
1681
						// maybe set no_wrap
1682
						$no_wrap = isset( $this->options['no_wrap'] ) && $this->options['no_wrap'] ? true : false;
1683
						if ( isset( $this->arguments['no_wrap'] ) && $this->arguments['no_wrap'] ) {
1684
							$no_wrap = true;
1685
						}
1686
						if ( $no_wrap ) {
1687
							$this->options['block-wrap'] = '';
1688
						}
1689
1690
						$show_advanced = $this->block_show_advanced();
1691
1692
						$show_alignment = false;
1693
						// align feature
1694
						/*echo "supports: {";
1695
						echo "	align: true,";
1696
						echo "  html: false";
1697
						echo "},";*/
1698
1699
						if ( ! empty( $this->arguments ) ) {
1700
							echo "attributes : {";
1701
1702
							if ( $show_advanced ) {
1703
								echo "show_advanced: {";
1704
								echo "	type: 'boolean',";
1705
								echo "  default: false,";
1706
								echo "},";
1707
							}
1708
1709
							// block wrap element
1710
							if ( ! empty( $this->options['block-wrap'] ) ) { //@todo we should validate this?
1711
								echo "block_wrap: {";
1712
								echo "	type: 'string',";
1713
								echo "  default: '" . esc_attr( $this->options['block-wrap'] ) . "',";
1714
								echo "},";
1715
							}
1716
1717
							foreach ( $this->arguments as $key => $args ) {
1718
1719
								// set if we should show alignment
1720
								if ( $key == 'alignment' ) {
1721
									$show_alignment = true;
1722
								}
1723
1724
								$extra = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $extra is dead and can be removed.
Loading history...
1725
1726
								if ( $args['type'] == 'checkbox' ) {
1727
									$type    = 'boolean';
1728
									$default = isset( $args['default'] ) && $args['default'] ? 'true' : 'false';
1729
								} elseif ( $args['type'] == 'number' ) {
1730
									$type    = 'number';
1731
									$default = isset( $args['default'] ) ? "'" . $args['default'] . "'" : "''";
1732
								} elseif ( $args['type'] == 'select' && ! empty( $args['multiple'] ) ) {
1733
									$type = 'array';
1734
									if ( is_array( $args['default'] ) ) {
1735
										$default = isset( $args['default'] ) ? "['" . implode( "','", $args['default'] ) . "']" : "[]";
1736
									} else {
1737
										$default = isset( $args['default'] ) ? "'" . $args['default'] . "'" : "''";
1738
									}
1739
								} elseif ( $args['type'] == 'multiselect' ) {
1740
									$type    = 'array';
1741
									$default = isset( $args['default'] ) ? "'" . $args['default'] . "'" : "''";
1742
								} else {
1743
									$type    = 'string';
1744
									$default = isset( $args['default'] ) ? "'" . $args['default'] . "'" : "''";
1745
								}
1746
								echo $key . " : {";
1747
								echo "type : '$type',";
1748
								echo "default : $default,";
1749
								echo "},";
1750
							}
1751
1752
							echo "content : {type : 'string',default: 'Please select the attributes in the block settings'},";
1753
							echo "className: { type: 'string', default: '' },";
1754
1755
							echo "},";
1756
1757
						}
1758
1759
						?>
1760
1761
						// The "edit" property must be a valid function.
1762
						edit: function (props) {
1763
1764
1765
1766
							<?php
1767
							// if we have a post_type and a category then link them
1768
							if( isset($this->arguments['post_type']) && isset($this->arguments['category']) && isset($this->arguments['category']['post_type_linked']) ){
1769
							?>
1770
							if(typeof(prev_attributes[props.id]) != 'undefined' ){
1771
								$pt = props.attributes.post_type;
1772
								if(post_type_rest_slugs.length){
1773
									$value = post_type_rest_slugs[0][$pt];
1774
								}
1775
								if('post_type' in prev_attributes[props.id] && 'category' in prev_attributes[props.id] && $pt != term_query_type ){
1776
									term_query_type = $pt;
1777
									wp.apiFetch({path: "<?php if(isset($this->arguments['post_type']['onchange_rest']['path'])){echo $this->arguments['post_type']['onchange_rest']['path'];}else{'/wp/v2/"+$value+"/categories';} ?>"}).then(terms => {
1778
										while (taxonomies_<?php echo str_replace("-","_", $this->id);?>.length) {
1779
										taxonomies_<?php echo str_replace("-","_", $this->id);?>.pop();
1780
									}
1781
									taxonomies_<?php echo str_replace("-","_", $this->id);?>.push({label: "All", value: 0});
1782
									jQuery.each( terms, function( key, val ) {
1783
										taxonomies_<?php echo str_replace("-","_", $this->id);?>.push({label: val.name, value: val.id});
1784
									});
1785
1786
									// setting the value back and fourth fixes the no update issue that sometimes happens where it won't update the options.
1787
									var $old_cat_value = props.attributes.category
1788
									props.setAttributes({category: [0] });
1789
									props.setAttributes({category: $old_cat_value });
1790
1791
									return taxonomies_<?php echo str_replace("-","_", $this->id);?>;
1792
								});
1793
								}
1794
							}
1795
							<?php }?>
1796
1797
1798
							var content = props.attributes.content;
1799
1800
							function onChangeContent() {
1801
1802
								$refresh = false;
1803
1804
								// Set the old content the same as the new one so we only compare all other attributes
1805
								if(typeof(prev_attributes[props.id]) != 'undefined'){
1806
									prev_attributes[props.id].content = props.attributes.content;
1807
								}else if(props.attributes.content === ""){
1808
									// if first load and content empty then refresh
1809
									$refresh = true;
1810
								}
1811
1812
								if ( ( !is_fetching &&  JSON.stringify(prev_attributes[props.id]) != JSON.stringify(props.attributes) ) || $refresh  ) {
1813
1814
									is_fetching = true;
1815
									var data = {
1816
										'action': 'super_duper_output_shortcode',
1817
										'shortcode': '<?php echo $this->options['base_id'];?>',
1818
										'attributes': props.attributes,
1819
										'post_id': <?php global $post; if ( isset( $post->ID ) ) {
1820
										echo $post->ID;
1821
									}?>,
1822
										'_ajax_nonce': '<?php echo wp_create_nonce( 'super_duper_output_shortcode' );?>'
1823
									};
1824
1825
									jQuery.post(ajaxurl, data, function (response) {
1826
										return response;
1827
									}).then(function (env) {
1828
1829
										// if the content is empty then we place some placeholder text
1830
										if (env == '') {
1831
											env = "<div style='background:#0185ba33;padding: 10px;border: 4px #ccc dashed;'>" + "<?php _e( 'Placeholder for: ' );?>" + props.name + "</div>";
1832
										}
1833
1834
										props.setAttributes({content: env});
1835
										is_fetching = false;
1836
										prev_attributes[props.id] = props.attributes;
1837
1838
										// if AUI is active call the js init function
1839
										if (typeof aui_init === "function") {
1840
											aui_init();
1841
										}
1842
									});
1843
1844
1845
								}
1846
1847
								return props.attributes.content;
1848
1849
							}
1850
1851
							return [
1852
1853
								el(wp.blockEditor.BlockControls, {key: 'controls'},
1854
1855
									<?php if($show_alignment){?>
1856
									el(
1857
										wp.blockEditor.AlignmentToolbar,
1858
										{
1859
											value: props.attributes.alignment,
1860
											onChange: function (alignment) {
1861
												props.setAttributes({alignment: alignment})
1862
											}
1863
										}
1864
									)
1865
									<?php }?>
1866
1867
								),
1868
1869
								el(wp.blockEditor.InspectorControls, {key: 'inspector'},
1870
1871
									<?php
1872
1873
									if(! empty( $this->arguments )){
1874
1875
									if ( $show_advanced ) {
1876
									?>
1877
									el('div', {
1878
											style: {'padding-left': '16px','padding-right': '16px'}
1879
										},
1880
										el(
1881
											wp.components.ToggleControl,
1882
											{
1883
												label: 'Show Advanced Settings?',
1884
												checked: props.attributes.show_advanced,
1885
												onChange: function (show_advanced) {
1886
													props.setAttributes({show_advanced: !props.attributes.show_advanced})
1887
												}
1888
											}
1889
										)
1890
									)
1891
									,
1892
									<?php
1893
1894
									}
1895
1896
									$arguments = $this->group_arguments( $this->arguments );
1897
1898
									// Do we have sections?
1899
									$has_sections = $arguments == $this->arguments ? false : true;
1900
1901
1902
									if($has_sections){
1903
									$panel_count = 0;
1904
									foreach($arguments as $key => $args){
1905
									?>
1906
									el(wp.components.PanelBody, {
1907
											title: '<?php esc_attr_e( $key ); ?>',
1908
											initialOpen: <?php if ( $panel_count ) {
1909
											echo "false";
1910
										} else {
1911
											echo "true";
1912
										}?>
1913
										},
1914
										<?php
1915
1916
										foreach ( $args as $k => $a ) {
1917
											$this->build_block_arguments( $k, $a );
1918
										}
1919
										?>
1920
									),
1921
									<?php
1922
									$panel_count ++;
1923
1924
									}
1925
									}else {
1926
									?>
1927
									el(wp.components.PanelBody, {
1928
											title: '<?php esc_attr_e( "Settings" ); ?>',
1929
											initialOpen: true
1930
										},
1931
										<?php
1932
										foreach ( $this->arguments as $key => $args ) {
1933
											$this->build_block_arguments( $key, $args );
1934
										}
1935
										?>
1936
									),
1937
									<?php
1938
									}
1939
1940
									}
1941
									?>
1942
1943
								),
1944
1945
								<?php
1946
								// If the user sets block-output array then build it
1947
								if ( ! empty( $this->options['block-output'] ) ) {
1948
								$this->block_element( $this->options['block-output'] );
1949
							}else{
1950
								// if no block-output is set then we try and get the shortcode html output via ajax.
1951
								?>
1952
								el('div', {
1953
									dangerouslySetInnerHTML: {__html: onChangeContent()},
1954
									className: props.className,
1955
									style: {'minHeight': '30px'}
1956
								})
1957
								<?php
1958
								}
1959
								?>
1960
							]; // end return
1961
						},
1962
1963
						// The "save" property must be specified and must be a valid function.
1964
						save: function (props) {
1965
1966
							//console.log(props);
1967
1968
1969
							var attr = props.attributes;
1970
							var align = '';
1971
1972
							// build the shortcode.
1973
							var content = "[<?php echo $this->options['base_id'];?>";
1974
							$html = '';
1975
							<?php
1976
1977
							if(! empty( $this->arguments )){
1978
1979
							foreach($this->arguments as $key => $args){
1980
							?>
1981
							if (attr.hasOwnProperty("<?php echo esc_attr( $key );?>")) {
1982
								if ('<?php echo esc_attr( $key );?>' == 'html') {
1983
									$html = attr.<?php echo esc_attr( $key );?>;
1984
								} else {
1985
									content += " <?php echo esc_attr( $key );?>='" + attr.<?php echo esc_attr( $key );?>+ "' ";
1986
								}
1987
							}
1988
							<?php
1989
							}
1990
							}
1991
1992
							?>
1993
							content += "]";
1994
1995
							// if has html element
1996
							if ($html) {
1997
								content += $html + "[/<?php echo $this->options['base_id'];?>]";
1998
							}
1999
2000
2001
							// @todo should we add inline style here or just css classes?
2002
							if (attr.alignment) {
2003
								if (attr.alignment == 'left') {
2004
									align = 'alignleft';
2005
								}
2006
								if (attr.alignment == 'center') {
2007
									align = 'aligncenter';
2008
								}
2009
								if (attr.alignment == 'right') {
2010
									align = 'alignright';
2011
								}
2012
							}
2013
2014
							<?php
2015
							if(isset( $this->options['block-wrap'] ) && $this->options['block-wrap'] == ''){
2016
							?>
2017
							return content;
2018
							<?php
2019
							}else{
2020
							?>
2021
							var block_wrap = 'div';
2022
							if (attr.hasOwnProperty("block_wrap")) {
2023
								block_wrap = attr.block_wrap;
2024
							}
2025
							return el(block_wrap, {dangerouslySetInnerHTML: {__html: content}, className: align});
2026
							<?php
2027
							}
2028
							?>
2029
2030
2031
						}
2032
					});
2033
				})();
2034
			</script>
2035
			<?php
2036
			$output = ob_get_clean();
2037
2038
			/*
2039
			 * We only add the <script> tags for code highlighting, so we strip them from the output.
2040
			 */
2041
2042
			return str_replace( array(
2043
				'<script>',
2044
				'</script>'
2045
			), '', $output );
2046
		}
2047
2048
		public function build_block_arguments( $key, $args ) {
2049
			$custom_attributes = ! empty( $args['custom_attributes'] ) ? $this->array_to_attributes( $args['custom_attributes'] ) : '';
2050
			$options           = '';
2051
			$extra             = '';
2052
			$require           = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $require is dead and can be removed.
Loading history...
2053
2054
			// `content` is a protected and special argument
2055
			if ( $key == 'content' ) {
2056
				return;
2057
			}
2058
2059
			// require advanced
2060
			$require_advanced = ! empty( $args['advanced'] ) ? "props.attributes.show_advanced && " : "";
2061
2062
			// element require
2063
			$element_require = ! empty( $args['element_require'] ) ? $this->block_props_replace( $args['element_require'], true ) . " && " : "";
2064
2065
2066
			$onchange  = "props.setAttributes({ $key: $key } )";
2067
			$onchangecomplete  = "";
2068
			$value     = "props.attributes.$key";
2069
			$text_type = array( 'text', 'password', 'number', 'email', 'tel', 'url', 'colorx' );
2070
			if ( in_array( $args['type'], $text_type ) ) {
2071
				$type = 'TextControl';
2072
				// Save numbers as numbers and not strings
2073
				if ( $args['type'] == 'number' ) {
2074
					$onchange = "props.setAttributes({ $key: Number($key) } )";
2075
				}
2076
			}
2077
/*
2078
 * https://www.wptricks.com/question/set-current-tab-on-a-gutenberg-tabpanel-component-from-outside-that-component/ es5 layout
2079
			elseif($args['type']=='tabs'){
2080
				?>
2081
				<script>
2082
					el(
2083
						wp.components.TabPanel,
2084
						{
2085
							tabs: [
2086
								{
2087
									name: 'show',
2088
									title: __( 'Show', 'my-textdomain' ),
2089
								},
2090
								{
2091
									name: 'edit',
2092
									title: __( 'Edit', 'my-textdomain' ),
2093
								},
2094
							],
2095
						},
2096
						( tab ) => {
2097
2098
						if('show' === tab.name){
2099
						return 123;
2100
					}else if ( 'edit' === tab.name ) {
2101
						return 321;
2102
					}
2103
2104
					}
2105
					),
2106
				</script>
2107
				<?php
2108
				return;
2109
			}
2110
*/
2111
			elseif ( $args['type'] == 'color' ) {
2112
				$type = 'ColorPicker';
2113
				$onchange = "";
2114
				$extra = "color: $value,";
2115
				if(!empty($args['disable_alpha'])){
2116
					$extra .= "disableAlpha: true,";
2117
				}
2118
				$onchangecomplete = "onChangeComplete: function($key) {
2119
				value =  $key.rgb.a && $key.rgb.a < 1 ? \"rgba(\"+$key.rgb.r+\",\"+$key.rgb.g+\",\"+$key.rgb.b+\",\"+$key.rgb.a+\")\" : $key.hex;
2120
                        props.setAttributes({
2121
                            $key: value
2122
                        });
2123
                    },";
2124
			}
2125
			elseif ( $args['type'] == 'checkbox' ) {
2126
				$type = 'CheckboxControl';
2127
				$extra .= "checked: props.attributes.$key,";
2128
				$onchange = "props.setAttributes({ $key: ! props.attributes.$key } )";
2129
			} elseif ( $args['type'] == 'textarea' ) {
2130
				$type = 'TextareaControl';
2131
			} elseif ( $args['type'] == 'select' || $args['type'] == 'multiselect' ) {
2132
				$type = 'SelectControl';
2133
2134
				if($args['name'] == 'category' && !empty($args['post_type_linked'])){
2135
					$options .= "options: taxonomies_".str_replace("-","_", $this->id).",";
2136
				}else {
2137
2138
					if ( ! empty( $args['options'] ) ) {
2139
						$options .= "options: [";
2140
						foreach ( $args['options'] as $option_val => $option_label ) {
2141
							$options .= "{ value: '" . esc_attr( $option_val ) . "', label: '" . addslashes( $option_label ) . "' },";
2142
						}
2143
						$options .= "],";
2144
					}
2145
				}
2146
				if ( isset( $args['multiple'] ) && $args['multiple'] ) { //@todo multiselect does not work at the moment: https://github.com/WordPress/gutenberg/issues/5550
2147
					$extra .= ' multiple: true, ';
2148
				}
2149
			} elseif ( $args['type'] == 'alignment' ) {
2150
				$type = 'AlignmentToolbar'; // @todo this does not seem to work but cant find a example
2151
			} else {
2152
				return;// if we have not implemented the control then don't break the JS.
2153
			}
2154
2155
2156
2157
			// color input does not show the labels so we add them
2158
			if($args['type']=='color'){
2159
				// add show only if advanced
2160
				echo $require_advanced;
2161
				// add setting require if defined
2162
				echo $element_require;
2163
				echo "el('div', {style: {'marginBottom': '8px'}}, '".addslashes( $args['title'] )."'),";
2164
			}
2165
2166
			// add show only if advanced
2167
			echo $require_advanced;
2168
			// add setting require if defined
2169
			echo $element_require;
2170
			?>
2171
			el( wp.components.<?php echo $type; ?>, {
2172
			label: '<?php echo addslashes( $args['title'] ); ?>',
2173
			help: '<?php if ( isset( $args['desc'] ) ) {
2174
				echo addslashes( $args['desc'] );
2175
			} ?>',
2176
			value: <?php echo $value; ?>,
2177
			<?php if ( $type == 'TextControl' && $args['type'] != 'text' ) {
2178
				echo "type: '" . addslashes( $args['type'] ) . "',";
2179
			} ?>
2180
			<?php if ( ! empty( $args['placeholder'] ) ) {
2181
				echo "placeholder: '" . addslashes( $args['placeholder'] ) . "',";
2182
			} ?>
2183
			<?php echo $options; ?>
2184
			<?php echo $extra; ?>
2185
			<?php echo $custom_attributes; ?>
2186
			<?php echo $onchangecomplete;?>
2187
			onChange: function ( <?php echo $key; ?> ) {
2188
			<?php echo $onchange; ?>
2189
			}
2190
			} ),
2191
			<?php
2192
2193
		}
2194
2195
		/**
2196
		 * Convert an array of attributes to block string.
2197
		 *
2198
		 * @todo there is prob a faster way to do this, also we could add some validation here.
2199
		 *
2200
		 * @param $custom_attributes
2201
		 *
2202
		 * @return string
2203
		 */
2204
		public function array_to_attributes( $custom_attributes, $html = false ) {
2205
			$attributes = '';
2206
			if ( ! empty( $custom_attributes ) ) {
2207
2208
				if ( $html ) {
2209
					foreach ( $custom_attributes as $key => $val ) {
2210
						$attributes .= " $key='$val' ";
2211
					}
2212
				} else {
2213
					foreach ( $custom_attributes as $key => $val ) {
2214
						$attributes .= "'$key': '$val',";
2215
					}
2216
				}
2217
			}
2218
2219
			return $attributes;
2220
		}
2221
2222
		/**
2223
		 * A self looping function to create the output for JS block elements.
2224
		 *
2225
		 * This is what is output in the WP Editor visual view.
2226
		 *
2227
		 * @param $args
2228
		 */
2229
		public function block_element( $args ) {
2230
2231
2232
			if ( ! empty( $args ) ) {
2233
				foreach ( $args as $element => $new_args ) {
2234
2235
					if ( is_array( $new_args ) ) { // its an element
2236
2237
2238
						if ( isset( $new_args['element'] ) ) {
2239
2240
							if ( isset( $new_args['element_require'] ) ) {
2241
								echo str_replace( array(
2242
										"'+",
2243
										"+'"
2244
									), '', $this->block_props_replace( $new_args['element_require'] ) ) . " &&  ";
2245
								unset( $new_args['element_require'] );
2246
							}
2247
2248
							echo "\n el( '" . $new_args['element'] . "', {";
2249
2250
							// get the attributes
2251
							foreach ( $new_args as $new_key => $new_value ) {
2252
2253
2254
								if ( $new_key == 'element' || $new_key == 'content' || $new_key == 'element_require' || $new_key == 'element_repeat' || is_array( $new_value ) ) {
2255
									// do nothing
2256
								} else {
2257
									echo $this->block_element( array( $new_key => $new_value ) );
2258
								}
2259
							}
2260
2261
							echo "},";// end attributes
2262
2263
							// get the content
2264
							$first_item = 0;
2265
							foreach ( $new_args as $new_key => $new_value ) {
2266
								if ( $new_key === 'content' || is_array( $new_value ) ) {
2267
2268
									if ( $new_key === 'content' ) {
2269
										echo "'" . $this->block_props_replace( wp_slash( $new_value ) ) . "'";
2270
									}
2271
2272
									if ( is_array( $new_value ) ) {
2273
2274
										if ( isset( $new_value['element_require'] ) ) {
2275
											echo str_replace( array(
2276
													"'+",
2277
													"+'"
2278
												), '', $this->block_props_replace( $new_value['element_require'] ) ) . " &&  ";
2279
											unset( $new_value['element_require'] );
2280
										}
2281
2282
										if ( isset( $new_value['element_repeat'] ) ) {
2283
											$x = 1;
2284
											while ( $x <= absint( $new_value['element_repeat'] ) ) {
2285
												$this->block_element( array( '' => $new_value ) );
2286
												$x ++;
2287
											}
2288
										} else {
2289
											$this->block_element( array( '' => $new_value ) );
2290
										}
2291
									}
2292
									$first_item ++;
2293
								}
2294
							}
2295
2296
							echo ")";// end content
2297
2298
							echo ", \n";
2299
2300
						}
2301
					} else {
2302
2303
						if ( substr( $element, 0, 3 ) === "if_" ) {
2304
							echo str_replace( "if_", "", $element ) . ": " . $this->block_props_replace( $new_args, true ) . ",";
2305
						} elseif ( $element == 'style' ) {
2306
							echo $element . ": " . $this->block_props_replace( $new_args ) . ",";
2307
						} else {
2308
							echo $element . ": '" . $this->block_props_replace( $new_args ) . "',";
2309
						}
2310
2311
					}
2312
				}
2313
			}
2314
		}
2315
2316
		/**
2317
		 * Replace block attributes placeholders with the proper naming.
2318
		 *
2319
		 * @param $string
2320
		 *
2321
		 * @return mixed
2322
		 */
2323
		public function block_props_replace( $string, $no_wrap = false ) {
2324
2325
			if ( $no_wrap ) {
2326
				$string = str_replace( array( "[%", "%]" ), array( "props.attributes.", "" ), $string );
2327
			} else {
2328
				$string = str_replace( array( "[%", "%]" ), array( "'+props.attributes.", "+'" ), $string );
2329
			}
2330
2331
			return $string;
2332
		}
2333
2334
		/**
2335
		 * Outputs the content of the widget
2336
		 *
2337
		 * @param array $args
2338
		 * @param array $instance
2339
		 */
2340
		public function widget( $args, $instance ) {
2341
2342
			// get the filtered values
2343
			$argument_values = $this->argument_values( $instance );
2344
			$argument_values = $this->string_to_bool( $argument_values );
2345
			$output          = $this->output( $argument_values, $args );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $output is correct as $this->output($argument_values, $args) targeting WP_Super_Duper::output() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
2346
2347
			$no_wrap = false;
2348
			if ( isset( $argument_values['no_wrap'] ) && $argument_values['no_wrap'] ) {
2349
				$no_wrap = true;
2350
			}
2351
2352
			ob_start();
2353
			if ( $output && ! $no_wrap ) {
0 ignored issues
show
introduced by
$output is defined implicitly as null, thus it is always evaluated to false.
Loading history...
2354
2355
				$class_original = $this->options['widget_ops']['classname'];
2356
				$class = $this->options['widget_ops']['classname']." sdel-".$this->get_instance_hash();
2357
2358
				// Before widget
2359
				$before_widget = $args['before_widget'];
2360
				$before_widget = str_replace($class_original,$class,$before_widget);
2361
				$before_widget = apply_filters( 'wp_super_duper_before_widget', $before_widget, $args, $instance, $this );
2362
				$before_widget = apply_filters( 'wp_super_duper_before_widget_' . $this->base_id, $before_widget, $args, $instance, $this );
2363
2364
				// After widget
2365
				$after_widget = $args['after_widget'];
2366
				$after_widget = apply_filters( 'wp_super_duper_after_widget', $after_widget, $args, $instance, $this );
2367
				$after_widget = apply_filters( 'wp_super_duper_after_widget_' . $this->base_id, $after_widget, $args, $instance, $this );
2368
2369
				echo $before_widget;
2370
				// elementor strips the widget wrapping div so we check for and add it back if needed
2371
				if ( $this->is_elementor_widget_output() ) {
2372
					echo ! empty( $this->options['widget_ops']['classname'] ) ? "<span class='" . esc_attr( $class  ) . "'>" : '';
2373
				}
2374
				echo $this->output_title( $args, $instance );
2375
				echo $output;
2376
				if ( $this->is_elementor_widget_output() ) {
2377
					echo ! empty( $this->options['widget_ops']['classname'] ) ? "</span>" : '';
2378
				}
2379
				echo $after_widget;
2380
			} elseif ( $this->is_preview() && $output == '' ) {// if preview show a placeholder if empty
2381
				$output = $this->preview_placeholder_text( "{{" . $this->base_id . "}}" );
2382
				echo $output;
2383
			} elseif ( $output && $no_wrap ) {
0 ignored issues
show
introduced by
$output is defined implicitly as null, thus it is always evaluated to false.
Loading history...
2384
				echo $output;
2385
			}
2386
			$output = ob_get_clean();
2387
2388
			$output = apply_filters( 'wp_super_duper_widget_output', $output, $instance, $args, $this );
2389
2390
			echo $output;
2391
		}
2392
2393
		/**
2394
		 * Tests if the current output is inside a elementor container.
2395
		 *
2396
		 * @since 1.0.4
2397
		 * @return bool
2398
		 */
2399
		public function is_elementor_widget_output() {
2400
			$result = false;
2401
			if ( defined( 'ELEMENTOR_VERSION' ) && isset( $this->number ) && $this->number == 'REPLACE_TO_ID' ) {
2402
				$result = true;
2403
			}
2404
2405
			return $result;
2406
		}
2407
2408
		/**
2409
		 * Tests if the current output is inside a elementor preview.
2410
		 *
2411
		 * @since 1.0.4
2412
		 * @return bool
2413
		 */
2414
		public function is_elementor_preview() {
2415
			$result = false;
2416
			if ( isset( $_REQUEST['elementor-preview'] ) || ( is_admin() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor' ) || ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor_ajax' ) ) {
2417
				$result = true;
2418
			}
2419
2420
			return $result;
2421
		}
2422
2423
		/**
2424
		 * Tests if the current output is inside a Divi preview.
2425
		 *
2426
		 * @since 1.0.6
2427
		 * @return bool
2428
		 */
2429
		public function is_divi_preview() {
2430
			$result = false;
2431
			if ( isset( $_REQUEST['et_fb'] ) || isset( $_REQUEST['et_pb_preview'] ) || ( is_admin() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor' ) ) {
2432
				$result = true;
2433
			}
2434
2435
			return $result;
2436
		}
2437
2438
		/**
2439
		 * Tests if the current output is inside a Beaver builder preview.
2440
		 *
2441
		 * @since 1.0.6
2442
		 * @return bool
2443
		 */
2444
		public function is_beaver_preview() {
2445
			$result = false;
2446
			if ( isset( $_REQUEST['fl_builder'] ) ) {
2447
				$result = true;
2448
			}
2449
2450
			return $result;
2451
		}
2452
2453
		/**
2454
		 * Tests if the current output is inside a siteorigin builder preview.
2455
		 *
2456
		 * @since 1.0.6
2457
		 * @return bool
2458
		 */
2459
		public function is_siteorigin_preview() {
2460
			$result = false;
2461
			if ( ! empty( $_REQUEST['siteorigin_panels_live_editor'] ) ) {
2462
				$result = true;
2463
			}
2464
2465
			return $result;
2466
		}
2467
2468
		/**
2469
		 * Tests if the current output is inside a cornerstone builder preview.
2470
		 *
2471
		 * @since 1.0.8
2472
		 * @return bool
2473
		 */
2474
		public function is_cornerstone_preview() {
2475
			$result = false;
2476
			if ( ! empty( $_REQUEST['cornerstone_preview'] ) || basename( $_SERVER['REQUEST_URI'] ) == 'cornerstone-endpoint' ) {
2477
				$result = true;
2478
			}
2479
2480
			return $result;
2481
		}
2482
2483
		/**
2484
		 * Tests if the current output is inside a fusion builder preview.
2485
		 *
2486
		 * @since 1.1.0
2487
		 * @return bool
2488
		 */
2489
		public function is_fusion_preview() {
2490
			$result = false;
2491
			if ( ! empty( $_REQUEST['fb-edit'] ) || ! empty( $_REQUEST['fusion_load_nonce'] ) ) {
2492
				$result = true;
2493
			}
2494
2495
			return $result;
2496
		}
2497
2498
		/**
2499
		 * Tests if the current output is inside a Oxygen builder preview.
2500
		 *
2501
		 * @since 1.0.18
2502
		 * @return bool
2503
		 */
2504
		public function is_oxygen_preview() {
2505
			$result = false;
2506
			if ( ! empty( $_REQUEST['ct_builder'] ) || ( ! empty( $_REQUEST['action'] ) && ( substr( $_REQUEST['action'], 0, 11 ) === "oxy_render_" || substr( $_REQUEST['action'], 0, 10 ) === "ct_render_" ) ) ) {
2507
				$result = true;
2508
			}
2509
2510
			return $result;
2511
		}
2512
2513
		/**
2514
		 * General function to check if we are in a preview situation.
2515
		 *
2516
		 * @since 1.0.6
2517
		 * @return bool
2518
		 */
2519
		public function is_preview() {
2520
			$preview = false;
2521
			if ( $this->is_divi_preview() ) {
2522
				$preview = true;
2523
			} elseif ( $this->is_elementor_preview() ) {
2524
				$preview = true;
2525
			} elseif ( $this->is_beaver_preview() ) {
2526
				$preview = true;
2527
			} elseif ( $this->is_siteorigin_preview() ) {
2528
				$preview = true;
2529
			} elseif ( $this->is_cornerstone_preview() ) {
2530
				$preview = true;
2531
			} elseif ( $this->is_fusion_preview() ) {
2532
				$preview = true;
2533
			} elseif ( $this->is_oxygen_preview() ) {
2534
				$preview = true;
2535
			} elseif( $this->is_block_content_call() ) {
2536
				$preview = true;
2537
			}
2538
2539
			return $preview;
2540
		}
2541
2542
		/**
2543
		 * Output the super title.
2544
		 *
2545
		 * @param $args
2546
		 * @param array $instance
2547
		 *
2548
		 * @return string
2549
		 */
2550
		public function output_title( $args, $instance = array() ) {
2551
			$output = '';
2552
			if ( ! empty( $instance['title'] ) ) {
2553
				/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
2554
				$title  = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
2555
				$output = $args['before_title'] . $title . $args['after_title'];
2556
			}
2557
2558
			return $output;
2559
		}
2560
2561
		/**
2562
		 * Outputs the options form inputs for the widget.
2563
		 *
2564
		 * @param array $instance The widget options.
2565
		 */
2566
		public function form( $instance ) {
2567
2568
			// set widget instance
2569
			$this->instance = $instance;
2570
2571
			// set it as a SD widget
2572
			echo $this->widget_advanced_toggle();
2573
2574
			echo "<p>" . esc_attr( $this->options['widget_ops']['description'] ) . "</p>";
2575
			$arguments_raw = $this->get_arguments();
2576
2577
			if ( is_array( $arguments_raw ) ) {
0 ignored issues
show
introduced by
The condition is_array($arguments_raw) is always true.
Loading history...
2578
2579
				$arguments = $this->group_arguments( $arguments_raw );
2580
2581
				// Do we have sections?
2582
				$has_sections = $arguments == $arguments_raw ? false : true;
2583
2584
2585
				if ( $has_sections ) {
2586
					$panel_count = 0;
2587
					foreach ( $arguments as $key => $args ) {
2588
2589
						?>
2590
						<script>
2591
							//							jQuery(this).find("i").toggleClass("fas fa-chevron-up fas fa-chevron-down");jQuery(this).next().toggle();
2592
						</script>
2593
						<?php
2594
2595
						$hide       = $panel_count ? ' style="display:none;" ' : '';
2596
						$icon_class = $panel_count ? 'fas fa-chevron-up' : 'fas fa-chevron-down';
2597
						echo "<button onclick='jQuery(this).find(\"i\").toggleClass(\"fas fa-chevron-up fas fa-chevron-down\");jQuery(this).next().slideToggle();' type='button' class='sd-toggle-group-button sd-input-group-toggle" . sanitize_title_with_dashes( $key ) . "'>" . esc_attr( $key ) . " <i style='float:right;' class='" . $icon_class . "'></i></button>";
2598
						echo "<div class='sd-toggle-group sd-input-group-" . sanitize_title_with_dashes( $key ) . "' $hide>";
2599
2600
						foreach ( $args as $k => $a ) {
2601
							$this->widget_inputs( $a, $instance );
2602
						}
2603
2604
						echo "</div>";
2605
2606
						$panel_count ++;
2607
2608
					}
2609
				} else {
2610
					foreach ( $arguments as $key => $args ) {
2611
						$this->widget_inputs( $args, $instance );
2612
					}
2613
				}
2614
2615
			}
2616
		}
2617
2618
		/**
2619
		 * Get the hidden input that when added makes the advanced button show on widget settings.
2620
		 *
2621
		 * @return string
2622
		 */
2623
		public function widget_advanced_toggle() {
2624
2625
			$output = '';
2626
			if ( $this->block_show_advanced() ) {
2627
				$val = 1;
2628
			} else {
2629
				$val = 0;
2630
			}
2631
2632
			$output .= "<input type='hidden'  class='sd-show-advanced' value='$val' />";
2633
2634
			return $output;
2635
		}
2636
2637
		/**
2638
		 * Convert require element.
2639
		 *
2640
		 * @since 1.0.0
2641
		 *
2642
		 * @param string $input Input element.
2643
		 *
2644
		 * @return string $output
2645
		 */
2646
		public function convert_element_require( $input ) {
2647
2648
			$input = str_replace( "'", '"', $input );// we only want double quotes
2649
2650
			$output = esc_attr( str_replace( array( "[%", "%]" ), array(
2651
				"jQuery(form).find('[data-argument=\"",
2652
				"\"]').find('input,select,textarea').val()"
2653
			), $input ) );
2654
2655
			return $output;
2656
		}
2657
2658
		/**
2659
		 * Builds the inputs for the widget options.
2660
		 *
2661
		 * @param $args
2662
		 * @param $instance
2663
		 */
2664
		public function widget_inputs( $args, $instance ) {
2665
2666
			$class             = "";
2667
			$element_require   = "";
2668
			$custom_attributes = "";
2669
2670
			// get value
2671
			if ( isset( $instance[ $args['name'] ] ) ) {
2672
				$value = $instance[ $args['name'] ];
2673
			} elseif ( ! isset( $instance[ $args['name'] ] ) && ! empty( $args['default'] ) ) {
2674
				$value = is_array( $args['default'] ) ? array_map( "esc_html", $args['default'] ) : esc_html( $args['default'] );
2675
			} else {
2676
				$value = '';
2677
			}
2678
2679
			// get placeholder
2680
			if ( ! empty( $args['placeholder'] ) ) {
2681
				$placeholder = "placeholder='" . esc_html( $args['placeholder'] ) . "'";
2682
			} else {
2683
				$placeholder = '';
2684
			}
2685
2686
			// get if advanced
2687
			if ( isset( $args['advanced'] ) && $args['advanced'] ) {
2688
				$class .= " sd-advanced-setting ";
2689
			}
2690
2691
			// element_require
2692
			if ( isset( $args['element_require'] ) && $args['element_require'] ) {
2693
				$element_require = $args['element_require'];
2694
			}
2695
2696
			// custom_attributes
2697
			if ( isset( $args['custom_attributes'] ) && $args['custom_attributes'] ) {
2698
				$custom_attributes = $this->array_to_attributes( $args['custom_attributes'], true );
2699
			}
2700
2701
			// before wrapper
2702
			?>
2703
			<p class="sd-argument <?php echo esc_attr( $class ); ?>"
2704
			   data-argument='<?php echo esc_attr( $args['name'] ); ?>'
2705
			   data-element_require='<?php if ( $element_require ) {
2706
				   echo $this->convert_element_require( $element_require );
2707
			   } ?>'
2708
			>
2709
				<?php
2710
2711
				switch ( $args['type'] ) {
2712
					//array('text','password','number','email','tel','url','color')
2713
					case "text":
2714
					case "password":
2715
					case "number":
2716
					case "email":
2717
					case "tel":
2718
					case "url":
2719
					case "color":
2720
						?>
2721
						<label
2722
							for="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"><?php echo esc_attr( $args['title'] ); ?><?php echo $this->widget_field_desc( $args ); ?></label>
2723
						<input <?php echo $placeholder; ?> class="widefat"
2724
							<?php echo $custom_attributes; ?>
2725
							                               id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2726
							                               name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) ); ?>"
2727
							                               type="<?php echo esc_attr( $args['type'] ); ?>"
2728
							                               value="<?php echo esc_attr( $value ); ?>">
2729
						<?php
2730
2731
						break;
2732
					case "select":
2733
						$multiple = isset( $args['multiple'] ) && $args['multiple'] ? true : false;
2734
						if ( $multiple ) {
2735
							if ( empty( $value ) ) {
2736
								$value = array();
2737
							}
2738
						}
2739
						?>
2740
						<label
2741
							for="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"><?php echo esc_attr( $args['title'] ); ?><?php echo $this->widget_field_desc( $args ); ?></label>
2742
						<select <?php echo $placeholder; ?> class="widefat"
2743
							<?php echo $custom_attributes; ?>
2744
							                                id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2745
							                                name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) );
2746
							                                if ( $multiple ) {
2747
								                                echo "[]";
2748
							                                } ?>"
2749
							<?php if ( $multiple ) {
2750
								echo "multiple";
2751
							} //@todo not implemented yet due to gutenberg not supporting it
2752
							?>
2753
						>
2754
							<?php
2755
2756
							if ( ! empty( $args['options'] ) ) {
2757
								foreach ( $args['options'] as $val => $label ) {
2758
									if ( $multiple ) {
2759
										$selected = in_array( $val, $value ) ? 'selected="selected"' : '';
2760
									} else {
2761
										$selected = selected( $value, $val, false );
2762
									}
2763
									echo "<option value='$val' " . $selected . ">$label</option>";
2764
								}
2765
							}
2766
							?>
2767
						</select>
2768
						<?php
2769
						break;
2770
					case "checkbox":
2771
						?>
2772
						<input <?php echo $placeholder; ?>
2773
							<?php checked( 1, $value, true ) ?>
2774
							<?php echo $custom_attributes; ?>
2775
							class="widefat" id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2776
							name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) ); ?>" type="checkbox"
2777
							value="1">
2778
						<label
2779
							for="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"><?php echo esc_attr( $args['title'] ); ?><?php echo $this->widget_field_desc( $args ); ?></label>
2780
						<?php
2781
						break;
2782
					case "textarea":
2783
						?>
2784
						<label
2785
							for="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"><?php echo esc_attr( $args['title'] ); ?><?php echo $this->widget_field_desc( $args ); ?></label>
2786
						<textarea <?php echo $placeholder; ?> class="widefat"
2787
							<?php echo $custom_attributes; ?>
2788
							                                  id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2789
							                                  name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) ); ?>"
2790
						><?php echo esc_attr( $value ); ?></textarea>
2791
						<?php
2792
2793
						break;
2794
					case "hidden":
2795
						?>
2796
						<input id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2797
						       name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) ); ?>" type="hidden"
2798
						       value="<?php echo esc_attr( $value ); ?>">
2799
						<?php
2800
						break;
2801
					default:
2802
						echo "No input type found!"; // @todo we need to add more input types.
2803
				}
2804
2805
				// after wrapper
2806
				?>
2807
			</p>
2808
			<?php
2809
2810
		}
2811
2812
		/**
2813
		 * Get the widget input description html.
2814
		 *
2815
		 * @param $args
2816
		 *
2817
		 * @return string
2818
		 * @todo, need to make its own tooltip script
2819
		 */
2820
		public function widget_field_desc( $args ) {
2821
2822
			$description = '';
2823
			if ( isset( $args['desc'] ) && $args['desc'] ) {
2824
				if ( isset( $args['desc_tip'] ) && $args['desc_tip'] ) {
2825
					$description = $this->desc_tip( $args['desc'] );
2826
				} else {
2827
					$description = '<span class="description">' . wp_kses_post( $args['desc'] ) . '</span>';
2828
				}
2829
			}
2830
2831
			return $description;
2832
		}
2833
2834
		/**
2835
		 * Get the tool tip html.
2836
		 *
2837
		 * @param $tip
2838
		 * @param bool $allow_html
2839
		 *
2840
		 * @return string
2841
		 */
2842
		function desc_tip( $tip, $allow_html = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2843
			if ( $allow_html ) {
2844
				$tip = $this->sanitize_tooltip( $tip );
2845
			} else {
2846
				$tip = esc_attr( $tip );
2847
			}
2848
2849
			return '<span class="gd-help-tip dashicons dashicons-editor-help" title="' . $tip . '"></span>';
2850
		}
2851
2852
		/**
2853
		 * Sanitize a string destined to be a tooltip.
2854
		 *
2855
		 * @param string $var
2856
		 *
2857
		 * @return string
2858
		 */
2859
		public function sanitize_tooltip( $var ) {
2860
			return htmlspecialchars( wp_kses( html_entity_decode( $var ), array(
2861
				'br'     => array(),
2862
				'em'     => array(),
2863
				'strong' => array(),
2864
				'small'  => array(),
2865
				'span'   => array(),
2866
				'ul'     => array(),
2867
				'li'     => array(),
2868
				'ol'     => array(),
2869
				'p'      => array(),
2870
			) ) );
2871
		}
2872
2873
		/**
2874
		 * Processing widget options on save
2875
		 *
2876
		 * @param array $new_instance The new options
2877
		 * @param array $old_instance The previous options
2878
		 *
2879
		 * @return array
2880
		 * @todo we should add some sanitation here.
2881
		 */
2882
		public function update( $new_instance, $old_instance ) {
2883
2884
			//save the widget
2885
			$instance = array_merge( (array) $old_instance, (array) $new_instance );
2886
2887
			// set widget instance
2888
			$this->instance = $instance;
2889
2890
			if ( empty( $this->arguments ) ) {
2891
				$this->get_arguments();
2892
			}
2893
2894
			// check for checkboxes
2895
			if ( ! empty( $this->arguments ) ) {
2896
				foreach ( $this->arguments as $argument ) {
2897
					if ( isset( $argument['type'] ) && $argument['type'] == 'checkbox' && ! isset( $new_instance[ $argument['name'] ] ) ) {
2898
						$instance[ $argument['name'] ] = '0';
2899
					}
2900
				}
2901
			}
2902
2903
			return $instance;
2904
		}
2905
2906
		/**
2907
		 * Checks if the current call is a ajax call to get the block content.
2908
		 *
2909
		 * This can be used in your widget to return different content as the block content.
2910
		 *
2911
		 * @since 1.0.3
2912
		 * @return bool
2913
		 */
2914
		public function is_block_content_call() {
2915
			$result = false;
2916
			if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'super_duper_output_shortcode' ) {
2917
				$result = true;
2918
			}
2919
2920
			return $result;
2921
		}
2922
2923
		/**
2924
		 * Get an instance hash that will be unique to the type and settings.
2925
		 *
2926
		 * @since 1.0.20
2927
		 * @return string
2928
		 */
2929
		public function get_instance_hash(){
2930
			$instance_string = $this->base_id.serialize($this->instance);
2931
			return hash('crc32b',$instance_string);
2932
		}
2933
2934
		/**
2935
		 * Generate and return inline styles from CSS rules that will match the unique class of the instance.
2936
		 *
2937
		 * @param array $rules
2938
		 *
2939
		 * @since 1.0.20
2940
		 * @return string
2941
		 */
2942
		public function get_instance_style($rules = array()){
2943
			$css = '';
2944
2945
			if(!empty($rules)){
2946
				$rules = array_unique($rules);
2947
				$instance_hash = $this->get_instance_hash();
2948
				$css .= "<style>";
2949
				foreach($rules as $rule){
2950
					$css .= ".sdel-$instance_hash $rule";
2951
				}
2952
				$css .= "</style>";
2953
			}
2954
2955
2956
			return $css;
2957
2958
		}
2959
2960
	}
2961
2962
}
2963