Passed
Pull Request — master (#351)
by Brian
456:50 queued 353:15
created

WP_Super_Duper::argument_values()   B

Complexity

Conditions 9
Paths 4

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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...
1310
1311
			}
1312
			wp_die();
1313
		}
1314
1315
		/**
1316
		 * Output the shortcode.
1317
		 *
1318
		 * @param array $args
1319
		 * @param string $content
1320
		 *
1321
		 * @return string
1322
		 */
1323
		public function shortcode_output( $args = array(), $content = '' ) {
1324
			$args = self::argument_values( $args );
0 ignored issues
show
Bug Best Practice introduced by
The method WP_Super_Duper::argument_values() is not static, but was called statically. ( Ignorable by Annotation )

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

1324
			/** @scrutinizer ignore-call */ 
1325
   $args = self::argument_values( $args );
Loading history...
1325
1326
			// add extra argument so we know its a output to gutenberg
1327
			//$args
1328
			$args = $this->string_to_bool( $args );
1329
1330
			// if we have a enclosed shortcode we add it to the special `html` argument
1331
			if ( ! empty( $content ) ) {
1332
				$args['html'] = $content;
1333
			}
1334
1335
			$class = isset( $this->options['widget_ops']['classname'] ) ? esc_attr( $this->options['widget_ops']['classname'] ) : '';
1336
1337
			$class = apply_filters( 'wp_super_duper_div_classname', $class, $args, $this );
1338
			$class = apply_filters( 'wp_super_duper_div_classname_' . $this->base_id, $class, $args, $this );
1339
1340
			$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...
1341
			$attrs = apply_filters( 'wp_super_duper_div_attrs_' . $this->base_id, '', $args, $this ); //@todo this does not seem right @kiran?
1342
1343
			$shortcode_args = array();
1344
			$output         = '';
1345
			$no_wrap        = isset( $this->options['no_wrap'] ) && $this->options['no_wrap'] ? true : false;
1346
			if ( isset( $args['no_wrap'] ) && $args['no_wrap'] ) {
1347
				$no_wrap = true;
1348
			}
1349
			$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...
1350
			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...
1351
				// wrap the shortcode in a div with the same class as the widget
1352
				$output .= '<div class="' . $class . '" ' . $attrs . '>';
1353
				if ( ! empty( $args['title'] ) ) {
1354
					// if its a shortcode and there is a title try to grab the title wrappers
1355
					$shortcode_args = array( 'before_title' => '', 'after_title' => '' );
1356
					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...
1357
						global $wp_registered_sidebars;
1358
						if ( ! empty( $wp_registered_sidebars ) ) {
1359
							foreach ( $wp_registered_sidebars as $sidebar ) {
1360
								if ( ! empty( $sidebar['before_title'] ) ) {
1361
									$shortcode_args['before_title'] = $sidebar['before_title'];
1362
									$shortcode_args['after_title']  = $sidebar['after_title'];
1363
									break;
1364
								}
1365
							}
1366
						}
1367
					}
1368
					$output .= $this->output_title( $shortcode_args, $args );
1369
				}
1370
				$output .= $main_content;
1371
				$output .= '</div>';
1372
			} 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...
1373
				$output .= $main_content;
1374
			}
1375
1376
			// if preview show a placeholder if empty
1377
			if ( $this->is_preview() && $output == '' ) {
1378
				$output = $this->preview_placeholder_text( "[{" . $this->base_id . "}]" );
1379
			}
1380
1381
			return apply_filters( 'wp_super_duper_widget_output', $output, $args, $shortcode_args, $this );
1382
		}
1383
1384
		/**
1385
		 * Placeholder text to show if output is empty and we are on a preview/builder page.
1386
		 *
1387
		 * @param string $name
1388
		 *
1389
		 * @return string
1390
		 */
1391
		public function preview_placeholder_text( $name = '' ) {
1392
			return "<div style='background:#0185ba33;padding: 10px;border: 4px #ccc dashed;'>" . sprintf( __( 'Placeholder for: %s' ), $name ) . "</div>";
1393
		}
1394
1395
		/**
1396
		 * Sometimes booleans values can be turned to strings, so we fix that.
1397
		 *
1398
		 * @param $options
1399
		 *
1400
		 * @return mixed
1401
		 */
1402
		public function string_to_bool( $options ) {
1403
			// convert bool strings to booleans
1404
			foreach ( $options as $key => $val ) {
1405
				if ( $val == 'false' ) {
1406
					$options[ $key ] = false;
1407
				} elseif ( $val == 'true' ) {
1408
					$options[ $key ] = true;
1409
				}
1410
			}
1411
1412
			return $options;
1413
		}
1414
1415
		/**
1416
		 * Get the argument values that are also filterable.
1417
		 *
1418
		 * @param $instance
1419
		 *
1420
		 * @since 1.0.12 Don't set checkbox default value if the value is empty.
1421
		 *
1422
		 * @return array
1423
		 */
1424
		public function argument_values( $instance ) {
1425
			$argument_values = array();
1426
1427
			// set widget instance
1428
			$this->instance = $instance;
1429
1430
			if ( empty( $this->arguments ) ) {
1431
				$this->arguments = $this->get_arguments();
1432
			}
1433
1434
			if ( ! empty( $this->arguments ) ) {
1435
				foreach ( $this->arguments as $key => $args ) {
1436
					// set the input name from the key
1437
					$args['name'] = $key;
1438
					//
1439
					$argument_values[ $key ] = isset( $instance[ $key ] ) ? $instance[ $key ] : '';
1440
					if ( $args['type'] == 'checkbox' && $argument_values[ $key ] == '' ) {
1441
						// don't set default for an empty checkbox
1442
					} elseif ( $argument_values[ $key ] == '' && isset( $args['default'] ) ) {
1443
						$argument_values[ $key ] = $args['default'];
1444
					}
1445
				}
1446
			}
1447
1448
			return $argument_values;
1449
		}
1450
1451
		/**
1452
		 * Set arguments in super duper.
1453
		 *
1454
		 * @since 1.0.0
1455
		 *
1456
		 * @return array Set arguments.
1457
		 */
1458
		public function set_arguments() {
1459
			return $this->arguments;
1460
		}
1461
1462
		/**
1463
		 * Get arguments in super duper.
1464
		 *
1465
		 * @since 1.0.0
1466
		 *
1467
		 * @return array Get arguments.
1468
		 */
1469
		public function get_arguments() {
1470
			if ( empty( $this->arguments ) ) {
1471
				$this->arguments = $this->set_arguments();
1472
			}
1473
1474
			$this->arguments = apply_filters( 'wp_super_duper_arguments', $this->arguments, $this->options, $this->instance );
1475
			$this->arguments = $this->add_name_from_key( $this->arguments, true );
1476
1477
			return $this->arguments;
1478
		}
1479
1480
		/**
1481
		 * This is the main output class for all 3 items, widget, shortcode and block, it is extended in the calling class.
1482
		 *
1483
		 * @param array $args
1484
		 * @param array $widget_args
1485
		 * @param string $content
1486
		 */
1487
		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

1487
		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...
1488
1489
		}
1490
1491
		/**
1492
		 * Add the dynamic block code inline when the wp-block in enqueued.
1493
		 */
1494
		public function register_block() {
1495
			wp_add_inline_script( 'wp-blocks', $this->block() );
1496
			if ( class_exists( 'SiteOrigin_Panels' ) ) {
1497
1498
				wp_add_inline_script( 'wp-blocks', $this->siteorigin_js() );
1499
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
					/**
1648
					 * Register Basic Block.
1649
					 *
1650
					 * Registers a new block provided a unique name and an object defining its
1651
					 * behavior. Once registered, the block is made available as an option to any
1652
					 * editor interface where blocks are implemented.
1653
					 *
1654
					 * @param  {string}   name     Block name.
1655
					 * @param  {Object}   settings Block settings.
1656
					 * @return {?WPBlock}          The block, if it has been successfully
1657
					 *                             registered; otherwise `undefined`.
1658
					 */
1659
					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.
1660
						title: '<?php echo addslashes( $this->options['name'] ); ?>', // Block title.
1661
						description: '<?php echo addslashes( $this->options['widget_ops']['description'] )?>', // Block title.
1662
						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/.
1663
						supports: {
1664
							<?php
1665
							if ( isset( $this->options['block-supports'] ) ) {
1666
								echo $this->array_to_attributes( $this->options['block-supports'] );
1667
							}
1668
							?>
1669
						},
1670
						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.
1671
						<?php if ( isset( $this->options['block-keywords'] ) ) {
1672
						echo "keywords : " . $this->options['block-keywords'] . ",";
1673
					}?>
1674
1675
						<?php
1676
1677
						// maybe set no_wrap
1678
						$no_wrap = isset( $this->options['no_wrap'] ) && $this->options['no_wrap'] ? true : false;
1679
						if ( isset( $this->arguments['no_wrap'] ) && $this->arguments['no_wrap'] ) {
1680
							$no_wrap = true;
1681
						}
1682
						if ( $no_wrap ) {
1683
							$this->options['block-wrap'] = '';
1684
						}
1685
1686
						$show_advanced = $this->block_show_advanced();
1687
1688
						$show_alignment = false;
1689
						// align feature
1690
						/*echo "supports: {";
1691
						echo "	align: true,";
1692
						echo "  html: false";
1693
						echo "},";*/
1694
1695
						if ( ! empty( $this->arguments ) ) {
1696
							echo "attributes : {";
1697
1698
							if ( $show_advanced ) {
1699
								echo "show_advanced: {";
1700
								echo "	type: 'boolean',";
1701
								echo "  default: false,";
1702
								echo "},";
1703
							}
1704
1705
							// block wrap element
1706
							if ( ! empty( $this->options['block-wrap'] ) ) { //@todo we should validate this?
1707
								echo "block_wrap: {";
1708
								echo "	type: 'string',";
1709
								echo "  default: '" . esc_attr( $this->options['block-wrap'] ) . "',";
1710
								echo "},";
1711
							}
1712
1713
							foreach ( $this->arguments as $key => $args ) {
1714
1715
								// set if we should show alignment
1716
								if ( $key == 'alignment' ) {
1717
									$show_alignment = true;
1718
								}
1719
1720
								$extra = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $extra is dead and can be removed.
Loading history...
1721
1722
								if ( $args['type'] == 'checkbox' ) {
1723
									$type    = 'boolean';
1724
									$default = isset( $args['default'] ) && $args['default'] ? 'true' : 'false';
1725
								} elseif ( $args['type'] == 'number' ) {
1726
									$type    = 'number';
1727
									$default = isset( $args['default'] ) ? "'" . $args['default'] . "'" : "''";
1728
								} elseif ( $args['type'] == 'select' && ! empty( $args['multiple'] ) ) {
1729
									$type = 'array';
1730
									if ( is_array( $args['default'] ) ) {
1731
										$default = isset( $args['default'] ) ? "['" . implode( "','", $args['default'] ) . "']" : "[]";
1732
									} else {
1733
										$default = isset( $args['default'] ) ? "'" . $args['default'] . "'" : "''";
1734
									}
1735
								} elseif ( $args['type'] == 'multiselect' ) {
1736
									$type    = 'array';
1737
									$default = isset( $args['default'] ) ? "'" . $args['default'] . "'" : "''";
1738
								} else {
1739
									$type    = 'string';
1740
									$default = isset( $args['default'] ) ? "'" . $args['default'] . "'" : "''";
1741
								}
1742
								echo $key . " : {";
1743
								echo "type : '$type',";
1744
								echo "default : $default,";
1745
								echo "},";
1746
							}
1747
1748
							echo "content : {type : 'string',default: 'Please select the attributes in the block settings'},";
1749
							echo "className: { type: 'string', default: '' },";
1750
1751
							echo "},";
1752
1753
						}
1754
1755
						?>
1756
1757
						// The "edit" property must be a valid function.
1758
						edit: function (props) {
1759
1760
							var content = props.attributes.content;
1761
1762
							function onChangeContent() {
1763
1764
								if (!is_fetching && prev_attributes[props.id] != props.attributes) {
1765
1766
									//console.log(props);
1767
1768
									is_fetching = true;
1769
									var data = {
1770
										'action': 'super_duper_output_shortcode',
1771
										'shortcode': '<?php echo $this->options['base_id'];?>',
1772
										'attributes': props.attributes,
1773
										'post_id': <?php global $post; if ( isset( $post->ID ) ) {
1774
										echo $post->ID;
1775
									}?>,
1776
										'_ajax_nonce': '<?php echo wp_create_nonce( 'super_duper_output_shortcode' );?>'
1777
									};
1778
1779
									jQuery.post(ajaxurl, data, function (response) {
1780
										return response;
1781
									}).then(function (env) {
1782
1783
										// if the content is empty then we place some placeholder text
1784
										if (env == '') {
1785
											env = "<div style='background:#0185ba33;padding: 10px;border: 4px #ccc dashed;'>" + "<?php _e( 'Placeholder for: ' );?>" + props.name + "</div>";
1786
										}
1787
1788
										props.setAttributes({content: env});
1789
										is_fetching = false;
1790
										prev_attributes[props.id] = props.attributes;
1791
									});
1792
1793
1794
								}
1795
1796
								return props.attributes.content;
1797
1798
							}
1799
1800
							return [
1801
1802
								el(wp.blockEditor.BlockControls, {key: 'controls'},
1803
1804
									<?php if($show_alignment){?>
1805
									el(
1806
										wp.blockEditor.AlignmentToolbar,
1807
										{
1808
											value: props.attributes.alignment,
1809
											onChange: function (alignment) {
1810
												props.setAttributes({alignment: alignment})
1811
											}
1812
										}
1813
									)
1814
									<?php }?>
1815
1816
								),
1817
1818
								el(wp.blockEditor.InspectorControls, {key: 'inspector'},
1819
1820
									<?php
1821
1822
									if(! empty( $this->arguments )){
1823
1824
									if ( $show_advanced ) {
1825
									?>
1826
									el('div', {
1827
										style: {'padding-left': '16px','padding-right': '16px'}
1828
									},
1829
										el(
1830
											wp.components.ToggleControl,
1831
											{
1832
												label: 'Show Advanced Settings?',
1833
												checked: props.attributes.show_advanced,
1834
												onChange: function (show_advanced) {
1835
													props.setAttributes({show_advanced: !props.attributes.show_advanced})
1836
												}
1837
											}
1838
										)
1839
									)
1840
									,
1841
									<?php
1842
1843
									}
1844
1845
									$arguments = $this->group_arguments( $this->arguments );
1846
1847
									// Do we have sections?
1848
									$has_sections = $arguments == $this->arguments ? false : true;
1849
1850
1851
									if($has_sections){
1852
									$panel_count = 0;
1853
									foreach($arguments as $key => $args){
1854
									?>
1855
									el(wp.components.PanelBody, {
1856
											title: '<?php esc_attr_e( $key ); ?>',
1857
											initialOpen: <?php if ( $panel_count ) {
1858
											echo "false";
1859
										} else {
1860
											echo "true";
1861
										}?>
1862
										},
1863
										<?php
1864
1865
										foreach ( $args as $k => $a ) {
1866
											$this->build_block_arguments( $k, $a );
1867
										}
1868
										?>
1869
									),
1870
									<?php
1871
									$panel_count ++;
1872
1873
									}
1874
									}else {
1875
									?>
1876
									el(wp.components.PanelBody, {
1877
											title: '<?php esc_attr_e( "Settings" ); ?>',
1878
											initialOpen: true
1879
										},
1880
										<?php
1881
										foreach ( $this->arguments as $key => $args ) {
1882
											$this->build_block_arguments( $key, $args );
1883
										}
1884
										?>
1885
									),
1886
									<?php
1887
									}
1888
1889
									}
1890
									?>
1891
1892
								),
1893
1894
								<?php
1895
								// If the user sets block-output array then build it
1896
								if ( ! empty( $this->options['block-output'] ) ) {
1897
								$this->block_element( $this->options['block-output'] );
1898
							}else{
1899
								// if no block-output is set then we try and get the shortcode html output via ajax.
1900
								?>
1901
								el('div', {
1902
									dangerouslySetInnerHTML: {__html: onChangeContent()},
1903
									className: props.className,
1904
									style: {'minHeight': '30px'}
1905
								})
1906
								<?php
1907
								}
1908
								?>
1909
							]; // end return
1910
						},
1911
1912
						// The "save" property must be specified and must be a valid function.
1913
						save: function (props) {
1914
1915
							//console.log(props);
1916
1917
1918
							var attr = props.attributes;
1919
							var align = '';
1920
1921
							// build the shortcode.
1922
							var content = "[<?php echo $this->options['base_id'];?>";
1923
							$html = '';
1924
							<?php
1925
1926
							if(! empty( $this->arguments )){
1927
1928
							foreach($this->arguments as $key => $args){
1929
							?>
1930
							if (attr.hasOwnProperty("<?php echo esc_attr( $key );?>")) {
1931
								if ('<?php echo esc_attr( $key );?>' == 'html') {
1932
									$html = attr.<?php echo esc_attr( $key );?>;
1933
								} else {
1934
									content += " <?php echo esc_attr( $key );?>='" + attr.<?php echo esc_attr( $key );?>+ "' ";
1935
								}
1936
							}
1937
							<?php
1938
							}
1939
							}
1940
1941
							?>
1942
							content += "]";
1943
1944
							// if has html element
1945
							if ($html) {
1946
								content += $html + "[/<?php echo $this->options['base_id'];?>]";
1947
							}
1948
1949
1950
							// @todo should we add inline style here or just css classes?
1951
							if (attr.alignment) {
1952
								if (attr.alignment == 'left') {
1953
									align = 'alignleft';
1954
								}
1955
								if (attr.alignment == 'center') {
1956
									align = 'aligncenter';
1957
								}
1958
								if (attr.alignment == 'right') {
1959
									align = 'alignright';
1960
								}
1961
							}
1962
1963
							<?php
1964
							if(isset( $this->options['block-wrap'] ) && $this->options['block-wrap'] == ''){
1965
							?>
1966
							return content;
1967
							<?php
1968
							}else{
1969
							?>
1970
							var block_wrap = 'div';
1971
							if (attr.hasOwnProperty("block_wrap")) {
1972
								block_wrap = attr.block_wrap;
1973
							}
1974
							return el(block_wrap, {dangerouslySetInnerHTML: {__html: content}, className: align});
1975
							<?php
1976
							}
1977
							?>
1978
1979
1980
						}
1981
					});
1982
				})();
1983
			</script>
1984
			<?php
1985
			$output = ob_get_clean();
1986
1987
			/*
1988
			 * We only add the <script> tags for code highlighting, so we strip them from the output.
1989
			 */
1990
1991
			return str_replace( array(
1992
				'<script>',
1993
				'</script>'
1994
			), '', $output );
1995
		}
1996
1997
		public function build_block_arguments( $key, $args ) {
1998
			$custom_attributes = ! empty( $args['custom_attributes'] ) ? $this->array_to_attributes( $args['custom_attributes'] ) : '';
1999
			$options           = '';
2000
			$extra             = '';
2001
			$require           = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $require is dead and can be removed.
Loading history...
2002
2003
			// `content` is a protected and special argument
2004
			if ( $key == 'content' ) {
2005
				return;
2006
			}
2007
2008
			$onchange  = "props.setAttributes({ $key: $key } )";
2009
			$value     = "props.attributes.$key";
2010
			$text_type = array( 'text', 'password', 'number', 'email', 'tel', 'url', 'color' );
2011
			if ( in_array( $args['type'], $text_type ) ) {
2012
				$type = 'TextControl';
2013
				// Save numbers as numbers and not strings
2014
				if ( $args['type'] == 'number' ) {
2015
					$onchange = "props.setAttributes({ $key: Number($key) } )";
2016
				}
2017
			}
2018
			//									elseif ( $args['type'] == 'color' ) { //@todo ColorPicker labels are not shown yet, we use html5 color input for now https://github.com/WordPress/gutenberg/issues/14378
2019
			//										$type = 'ColorPicker';
2020
			//									}
2021
			elseif ( $args['type'] == 'checkbox' ) {
2022
				$type = 'CheckboxControl';
2023
				$extra .= "checked: props.attributes.$key,";
2024
				$onchange = "props.setAttributes({ $key: ! props.attributes.$key } )";
2025
			} elseif ( $args['type'] == 'textarea' ) {
2026
				$type = 'TextareaControl';
2027
			} elseif ( $args['type'] == 'select' || $args['type'] == 'multiselect' ) {
2028
				$type = 'SelectControl';
2029
				if ( ! empty( $args['options'] ) ) {
2030
					$options .= "options: [";
2031
					foreach ( $args['options'] as $option_val => $option_label ) {
2032
						$options .= "{ value: '" . esc_attr( $option_val ) . "', label: '" . addslashes( $option_label ) . "' },";
2033
					}
2034
					$options .= "],";
2035
				}
2036
				if ( isset( $args['multiple'] ) && $args['multiple'] ) { //@todo multiselect does not work at the moment: https://github.com/WordPress/gutenberg/issues/5550
2037
					$extra .= ' multiple: true, ';
2038
					//$onchange = "props.setAttributes({ $key: ['edit'] } )";
2039
					//$value = "['edit', 'delete']";
2040
				}
2041
			} elseif ( $args['type'] == 'alignment' ) {
2042
				$type = 'AlignmentToolbar'; // @todo this does not seem to work but cant find a example
2043
			} else {
2044
				return;// if we have not implemented the control then don't break the JS.
2045
			}
2046
2047
			// add show only if advanced
2048
			if ( ! empty( $args['advanced'] ) ) {
2049
				echo "props.attributes.show_advanced && ";
2050
			}
2051
			// add setting require if defined
2052
			if ( ! empty( $args['element_require'] ) ) {
2053
				echo $this->block_props_replace( $args['element_require'], true ) . " && ";
2054
			}
2055
			?>
2056
			el( wp.components.<?php echo $type; ?>, {
2057
			label: '<?php echo addslashes( $args['title'] ); ?>',
2058
			help: '<?php if ( isset( $args['desc'] ) ) {
2059
				echo addslashes( $args['desc'] );
2060
			} ?>',
2061
			value: <?php echo $value; ?>,
2062
			<?php if ( $type == 'TextControl' && $args['type'] != 'text' ) {
2063
				echo "type: '" . addslashes( $args['type'] ) . "',";
2064
			} ?>
2065
			<?php if ( ! empty( $args['placeholder'] ) ) {
2066
				echo "placeholder: '" . addslashes( $args['placeholder'] ) . "',";
2067
			} ?>
2068
			<?php echo $options; ?>
2069
			<?php echo $extra; ?>
2070
			<?php echo $custom_attributes; ?>
2071
			onChange: function ( <?php echo $key; ?> ) {
2072
			<?php echo $onchange; ?>
2073
			}
2074
			} ),
2075
			<?php
2076
		}
2077
2078
		/**
2079
		 * Convert an array of attributes to block string.
2080
		 *
2081
		 * @todo there is prob a faster way to do this, also we could add some validation here.
2082
		 *
2083
		 * @param $custom_attributes
2084
		 *
2085
		 * @return string
2086
		 */
2087
		public function array_to_attributes( $custom_attributes, $html = false ) {
2088
			$attributes = '';
2089
			if ( ! empty( $custom_attributes ) ) {
2090
2091
				if ( $html ) {
2092
					foreach ( $custom_attributes as $key => $val ) {
2093
						$attributes .= " $key='$val' ";
2094
					}
2095
				} else {
2096
					foreach ( $custom_attributes as $key => $val ) {
2097
						$attributes .= "'$key': '$val',";
2098
					}
2099
				}
2100
			}
2101
2102
			return $attributes;
2103
		}
2104
2105
		/**
2106
		 * A self looping function to create the output for JS block elements.
2107
		 *
2108
		 * This is what is output in the WP Editor visual view.
2109
		 *
2110
		 * @param $args
2111
		 */
2112
		public function block_element( $args ) {
2113
2114
2115
			if ( ! empty( $args ) ) {
2116
				foreach ( $args as $element => $new_args ) {
2117
2118
					if ( is_array( $new_args ) ) { // its an element
2119
2120
2121
						if ( isset( $new_args['element'] ) ) {
2122
2123
							if ( isset( $new_args['element_require'] ) ) {
2124
								echo str_replace( array(
2125
										"'+",
2126
										"+'"
2127
									), '', $this->block_props_replace( $new_args['element_require'] ) ) . " &&  ";
2128
								unset( $new_args['element_require'] );
2129
							}
2130
2131
							echo "\n el( '" . $new_args['element'] . "', {";
2132
2133
							// get the attributes
2134
							foreach ( $new_args as $new_key => $new_value ) {
2135
2136
2137
								if ( $new_key == 'element' || $new_key == 'content' || $new_key == 'element_require' || $new_key == 'element_repeat' || is_array( $new_value ) ) {
2138
									// do nothing
2139
								} else {
2140
									echo $this->block_element( array( $new_key => $new_value ) );
2141
								}
2142
							}
2143
2144
							echo "},";// end attributes
2145
2146
							// get the content
2147
							$first_item = 0;
2148
							foreach ( $new_args as $new_key => $new_value ) {
2149
								if ( $new_key === 'content' || is_array( $new_value ) ) {
2150
2151
									if ( $new_key === 'content' ) {
2152
										echo "'" . $this->block_props_replace( wp_slash( $new_value ) ) . "'";
2153
									}
2154
2155
									if ( is_array( $new_value ) ) {
2156
2157
										if ( isset( $new_value['element_require'] ) ) {
2158
											echo str_replace( array(
2159
													"'+",
2160
													"+'"
2161
												), '', $this->block_props_replace( $new_value['element_require'] ) ) . " &&  ";
2162
											unset( $new_value['element_require'] );
2163
										}
2164
2165
										if ( isset( $new_value['element_repeat'] ) ) {
2166
											$x = 1;
2167
											while ( $x <= absint( $new_value['element_repeat'] ) ) {
2168
												$this->block_element( array( '' => $new_value ) );
2169
												$x ++;
2170
											}
2171
										} else {
2172
											$this->block_element( array( '' => $new_value ) );
2173
										}
2174
									}
2175
									$first_item ++;
2176
								}
2177
							}
2178
2179
							echo ")";// end content
2180
2181
							echo ", \n";
2182
2183
						}
2184
					} else {
2185
2186
						if ( substr( $element, 0, 3 ) === "if_" ) {
2187
							echo str_replace( "if_", "", $element ) . ": " . $this->block_props_replace( $new_args, true ) . ",";
2188
						} elseif ( $element == 'style' ) {
2189
							echo $element . ": " . $this->block_props_replace( $new_args ) . ",";
2190
						} else {
2191
							echo $element . ": '" . $this->block_props_replace( $new_args ) . "',";
2192
						}
2193
2194
					}
2195
				}
2196
			}
2197
		}
2198
2199
		/**
2200
		 * Replace block attributes placeholders with the proper naming.
2201
		 *
2202
		 * @param $string
2203
		 *
2204
		 * @return mixed
2205
		 */
2206
		public function block_props_replace( $string, $no_wrap = false ) {
2207
2208
			if ( $no_wrap ) {
2209
				$string = str_replace( array( "[%", "%]" ), array( "props.attributes.", "" ), $string );
2210
			} else {
2211
				$string = str_replace( array( "[%", "%]" ), array( "'+props.attributes.", "+'" ), $string );
2212
			}
2213
2214
			return $string;
2215
		}
2216
2217
		/**
2218
		 * Outputs the content of the widget
2219
		 *
2220
		 * @param array $args
2221
		 * @param array $instance
2222
		 */
2223
		public function widget( $args, $instance ) {
2224
2225
			// get the filtered values
2226
			$argument_values = $this->argument_values( $instance );
2227
			$argument_values = $this->string_to_bool( $argument_values );
2228
			$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...
2229
2230
			$no_wrap = false;
2231
			if ( isset( $argument_values['no_wrap'] ) && $argument_values['no_wrap'] ) {
2232
				$no_wrap = true;
2233
			}
2234
2235
			ob_start();
2236
			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...
2237
				// Before widget
2238
				$before_widget = $args['before_widget'];
2239
				$before_widget = apply_filters( 'wp_super_duper_before_widget', $before_widget, $args, $instance, $this );
2240
				$before_widget = apply_filters( 'wp_super_duper_before_widget_' . $this->base_id, $before_widget, $args, $instance, $this );
2241
2242
				// After widget
2243
				$after_widget = $args['after_widget'];
2244
				$after_widget = apply_filters( 'wp_super_duper_after_widget', $after_widget, $args, $instance, $this );
2245
				$after_widget = apply_filters( 'wp_super_duper_after_widget_' . $this->base_id, $after_widget, $args, $instance, $this );
2246
2247
				echo $before_widget;
2248
				// elementor strips the widget wrapping div so we check for and add it back if needed
2249
				if ( $this->is_elementor_widget_output() ) {
2250
					echo ! empty( $this->options['widget_ops']['classname'] ) ? "<span class='" . esc_attr( $this->options['widget_ops']['classname'] ) . "'>" : '';
2251
				}
2252
				echo $this->output_title( $args, $instance );
2253
				echo $output;
2254
				if ( $this->is_elementor_widget_output() ) {
2255
					echo ! empty( $this->options['widget_ops']['classname'] ) ? "</span>" : '';
2256
				}
2257
				echo $after_widget;
2258
			} elseif ( $this->is_preview() && $output == '' ) {// if preview show a placeholder if empty
2259
				$output = $this->preview_placeholder_text( "{{" . $this->base_id . "}}" );
2260
				echo $output;
2261
			} 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...
2262
				echo $output;
2263
			}
2264
			$output = ob_get_clean();
2265
2266
			$output = apply_filters( 'wp_super_duper_widget_output', $output, $instance, $args, $this );
2267
2268
			echo $output;
2269
		}
2270
2271
		/**
2272
		 * Tests if the current output is inside a elementor container.
2273
		 *
2274
		 * @since 1.0.4
2275
		 * @return bool
2276
		 */
2277
		public function is_elementor_widget_output() {
2278
			$result = false;
2279
			if ( defined( 'ELEMENTOR_VERSION' ) && isset( $this->number ) && $this->number == 'REPLACE_TO_ID' ) {
2280
				$result = true;
2281
			}
2282
2283
			return $result;
2284
		}
2285
2286
		/**
2287
		 * Tests if the current output is inside a elementor preview.
2288
		 *
2289
		 * @since 1.0.4
2290
		 * @return bool
2291
		 */
2292
		public function is_elementor_preview() {
2293
			$result = false;
2294
			if ( isset( $_REQUEST['elementor-preview'] ) || ( is_admin() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor' ) || ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor_ajax' ) ) {
2295
				$result = true;
2296
			}
2297
2298
			return $result;
2299
		}
2300
2301
		/**
2302
		 * Tests if the current output is inside a Divi preview.
2303
		 *
2304
		 * @since 1.0.6
2305
		 * @return bool
2306
		 */
2307
		public function is_divi_preview() {
2308
			$result = false;
2309
			if ( isset( $_REQUEST['et_fb'] ) || isset( $_REQUEST['et_pb_preview'] ) || ( is_admin() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'elementor' ) ) {
2310
				$result = true;
2311
			}
2312
2313
			return $result;
2314
		}
2315
2316
		/**
2317
		 * Tests if the current output is inside a Beaver builder preview.
2318
		 *
2319
		 * @since 1.0.6
2320
		 * @return bool
2321
		 */
2322
		public function is_beaver_preview() {
2323
			$result = false;
2324
			if ( isset( $_REQUEST['fl_builder'] ) ) {
2325
				$result = true;
2326
			}
2327
2328
			return $result;
2329
		}
2330
2331
		/**
2332
		 * Tests if the current output is inside a siteorigin builder preview.
2333
		 *
2334
		 * @since 1.0.6
2335
		 * @return bool
2336
		 */
2337
		public function is_siteorigin_preview() {
2338
			$result = false;
2339
			if ( ! empty( $_REQUEST['siteorigin_panels_live_editor'] ) ) {
2340
				$result = true;
2341
			}
2342
2343
			return $result;
2344
		}
2345
2346
		/**
2347
		 * Tests if the current output is inside a cornerstone builder preview.
2348
		 *
2349
		 * @since 1.0.8
2350
		 * @return bool
2351
		 */
2352
		public function is_cornerstone_preview() {
2353
			$result = false;
2354
			if ( ! empty( $_REQUEST['cornerstone_preview'] ) || basename( $_SERVER['REQUEST_URI'] ) == 'cornerstone-endpoint' ) {
2355
				$result = true;
2356
			}
2357
2358
			return $result;
2359
		}
2360
2361
		/**
2362
		 * Tests if the current output is inside a fusion builder preview.
2363
		 *
2364
		 * @since 1.1.0
2365
		 * @return bool
2366
		 */
2367
		public function is_fusion_preview() {
2368
			$result = false;
2369
			if ( ! empty( $_REQUEST['fb-edit'] ) || ! empty( $_REQUEST['fusion_load_nonce'] ) ) {
2370
				$result = true;
2371
			}
2372
2373
			return $result;
2374
		}
2375
2376
		/**
2377
		 * Tests if the current output is inside a Oxygen builder preview.
2378
		 *
2379
		 * @since 1.0.18
2380
		 * @return bool
2381
		 */
2382
		public function is_oxygen_preview() {
2383
			$result = false;
2384
			if ( ! empty( $_REQUEST['ct_builder'] ) || ( ! empty( $_REQUEST['action'] ) && ( substr( $_REQUEST['action'], 0, 11 ) === "oxy_render_" || substr( $_REQUEST['action'], 0, 10 ) === "ct_render_" ) ) ) {
2385
				$result = true;
2386
			}
2387
2388
			return $result;
2389
		}
2390
2391
		/**
2392
		 * General function to check if we are in a preview situation.
2393
		 *
2394
		 * @since 1.0.6
2395
		 * @return bool
2396
		 */
2397
		public function is_preview() {
2398
			$preview = false;
2399
			if ( $this->is_divi_preview() ) {
2400
				$preview = true;
2401
			} elseif ( $this->is_elementor_preview() ) {
2402
				$preview = true;
2403
			} elseif ( $this->is_beaver_preview() ) {
2404
				$preview = true;
2405
			} elseif ( $this->is_siteorigin_preview() ) {
2406
				$preview = true;
2407
			} elseif ( $this->is_cornerstone_preview() ) {
2408
				$preview = true;
2409
			} elseif ( $this->is_fusion_preview() ) {
2410
				$preview = true;
2411
			} elseif ( $this->is_oxygen_preview() ) {
2412
				$preview = true;
2413
			}
2414
2415
			return $preview;
2416
		}
2417
2418
		/**
2419
		 * Output the super title.
2420
		 *
2421
		 * @param $args
2422
		 * @param array $instance
2423
		 *
2424
		 * @return string
2425
		 */
2426
		public function output_title( $args, $instance = array() ) {
2427
			$output = '';
2428
			if ( ! empty( $instance['title'] ) ) {
2429
				/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
2430
				$title  = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
2431
				$output = $args['before_title'] . $title . $args['after_title'];
2432
			}
2433
2434
			return $output;
2435
		}
2436
2437
		/**
2438
		 * Outputs the options form inputs for the widget.
2439
		 *
2440
		 * @param array $instance The widget options.
2441
		 */
2442
		public function form( $instance ) {
2443
2444
			// set widget instance
2445
			$this->instance = $instance;
2446
2447
			// set it as a SD widget
2448
			echo $this->widget_advanced_toggle();
2449
2450
			echo "<p>" . esc_attr( $this->options['widget_ops']['description'] ) . "</p>";
2451
			$arguments_raw = $this->get_arguments();
2452
2453
			if ( is_array( $arguments_raw ) ) {
0 ignored issues
show
introduced by
The condition is_array($arguments_raw) is always true.
Loading history...
2454
2455
				$arguments = $this->group_arguments( $arguments_raw );
2456
2457
				// Do we have sections?
2458
				$has_sections = $arguments == $arguments_raw ? false : true;
2459
2460
2461
				if ( $has_sections ) {
2462
					$panel_count = 0;
2463
					foreach ( $arguments as $key => $args ) {
2464
2465
						?>
2466
						<script>
2467
							//							jQuery(this).find("i").toggleClass("fas fa-chevron-up fas fa-chevron-down");jQuery(this).next().toggle();
2468
						</script>
2469
						<?php
2470
2471
						$hide       = $panel_count ? ' style="display:none;" ' : '';
2472
						$icon_class = $panel_count ? 'fas fa-chevron-up' : 'fas fa-chevron-down';
2473
						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>";
2474
						echo "<div class='sd-toggle-group sd-input-group-" . sanitize_title_with_dashes( $key ) . "' $hide>";
2475
2476
						foreach ( $args as $k => $a ) {
2477
							$this->widget_inputs( $a, $instance );
2478
						}
2479
2480
						echo "</div>";
2481
2482
						$panel_count ++;
2483
2484
					}
2485
				} else {
2486
					foreach ( $arguments as $key => $args ) {
2487
						$this->widget_inputs( $args, $instance );
2488
					}
2489
				}
2490
2491
			}
2492
		}
2493
2494
		/**
2495
		 * Get the hidden input that when added makes the advanced button show on widget settings.
2496
		 *
2497
		 * @return string
2498
		 */
2499
		public function widget_advanced_toggle() {
2500
2501
			$output = '';
2502
			if ( $this->block_show_advanced() ) {
2503
				$val = 1;
2504
			} else {
2505
				$val = 0;
2506
			}
2507
2508
			$output .= "<input type='hidden'  class='sd-show-advanced' value='$val' />";
2509
2510
			return $output;
2511
		}
2512
2513
		/**
2514
		 * Convert require element.
2515
		 *
2516
		 * @since 1.0.0
2517
		 *
2518
		 * @param string $input Input element.
2519
		 *
2520
		 * @return string $output
2521
		 */
2522
		public function convert_element_require( $input ) {
2523
2524
			$input = str_replace( "'", '"', $input );// we only want double quotes
2525
2526
			$output = esc_attr( str_replace( array( "[%", "%]" ), array(
2527
				"jQuery(form).find('[data-argument=\"",
2528
				"\"]').find('input,select,textarea').val()"
2529
			), $input ) );
2530
2531
			return $output;
2532
		}
2533
2534
		/**
2535
		 * Builds the inputs for the widget options.
2536
		 *
2537
		 * @param $args
2538
		 * @param $instance
2539
		 */
2540
		public function widget_inputs( $args, $instance ) {
2541
2542
			$class             = "";
2543
			$element_require   = "";
2544
			$custom_attributes = "";
2545
2546
			// get value
2547
			if ( isset( $instance[ $args['name'] ] ) ) {
2548
				$value = $instance[ $args['name'] ];
2549
			} elseif ( ! isset( $instance[ $args['name'] ] ) && ! empty( $args['default'] ) ) {
2550
				$value = is_array( $args['default'] ) ? array_map( "esc_html", $args['default'] ) : esc_html( $args['default'] );
2551
			} else {
2552
				$value = '';
2553
			}
2554
2555
			// get placeholder
2556
			if ( ! empty( $args['placeholder'] ) ) {
2557
				$placeholder = "placeholder='" . esc_html( $args['placeholder'] ) . "'";
2558
			} else {
2559
				$placeholder = '';
2560
			}
2561
2562
			// get if advanced
2563
			if ( isset( $args['advanced'] ) && $args['advanced'] ) {
2564
				$class .= " sd-advanced-setting ";
2565
			}
2566
2567
			// element_require
2568
			if ( isset( $args['element_require'] ) && $args['element_require'] ) {
2569
				$element_require = $args['element_require'];
2570
			}
2571
2572
			// custom_attributes
2573
			if ( isset( $args['custom_attributes'] ) && $args['custom_attributes'] ) {
2574
				$custom_attributes = $this->array_to_attributes( $args['custom_attributes'], true );
2575
			}
2576
2577
			// before wrapper
2578
			?>
2579
			<p class="sd-argument <?php echo esc_attr( $class ); ?>"
2580
			   data-argument='<?php echo esc_attr( $args['name'] ); ?>'
2581
			   data-element_require='<?php if ( $element_require ) {
2582
				   echo $this->convert_element_require( $element_require );
2583
			   } ?>'
2584
			>
2585
				<?php
2586
2587
				switch ( $args['type'] ) {
2588
					//array('text','password','number','email','tel','url','color')
2589
					case "text":
2590
					case "password":
2591
					case "number":
2592
					case "email":
2593
					case "tel":
2594
					case "url":
2595
					case "color":
2596
						?>
2597
						<label
2598
							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>
2599
						<input <?php echo $placeholder; ?> class="widefat"
2600
							<?php echo $custom_attributes; ?>
2601
							                               id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2602
							                               name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) ); ?>"
2603
							                               type="<?php echo esc_attr( $args['type'] ); ?>"
2604
							                               value="<?php echo esc_attr( $value ); ?>">
2605
						<?php
2606
2607
						break;
2608
					case "select":
2609
						$multiple = isset( $args['multiple'] ) && $args['multiple'] ? true : false;
2610
						if ( $multiple ) {
2611
							if ( empty( $value ) ) {
2612
								$value = array();
2613
							}
2614
						}
2615
						?>
2616
						<label
2617
							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>
2618
						<select <?php echo $placeholder; ?> class="widefat"
2619
							<?php echo $custom_attributes; ?>
2620
							                                id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2621
							                                name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) );
2622
							                                if ( $multiple ) {
2623
								                                echo "[]";
2624
							                                } ?>"
2625
							<?php if ( $multiple ) {
2626
								echo "multiple";
2627
							} //@todo not implemented yet due to gutenberg not supporting it
2628
							?>
2629
						>
2630
							<?php
2631
2632
							if ( ! empty( $args['options'] ) ) {
2633
								foreach ( $args['options'] as $val => $label ) {
2634
									if ( $multiple ) {
2635
										$selected = in_array( $val, $value ) ? 'selected="selected"' : '';
2636
									} else {
2637
										$selected = selected( $value, $val, false );
2638
									}
2639
									echo "<option value='$val' " . $selected . ">$label</option>";
2640
								}
2641
							}
2642
							?>
2643
						</select>
2644
						<?php
2645
						break;
2646
					case "checkbox":
2647
						?>
2648
						<input <?php echo $placeholder; ?>
2649
							<?php checked( 1, $value, true ) ?>
2650
							<?php echo $custom_attributes; ?>
2651
							class="widefat" id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2652
							name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) ); ?>" type="checkbox"
2653
							value="1">
2654
						<label
2655
							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>
2656
						<?php
2657
						break;
2658
					case "textarea":
2659
						?>
2660
						<label
2661
							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>
2662
						<textarea <?php echo $placeholder; ?> class="widefat"
2663
							<?php echo $custom_attributes; ?>
2664
							                                  id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2665
							                                  name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) ); ?>"
2666
						><?php echo esc_attr( $value ); ?></textarea>
2667
						<?php
2668
2669
						break;
2670
					case "hidden":
2671
						?>
2672
						<input id="<?php echo esc_attr( $this->get_field_id( $args['name'] ) ); ?>"
2673
						       name="<?php echo esc_attr( $this->get_field_name( $args['name'] ) ); ?>" type="hidden"
2674
						       value="<?php echo esc_attr( $value ); ?>">
2675
						<?php
2676
						break;
2677
					default:
2678
						echo "No input type found!"; // @todo we need to add more input types.
2679
				}
2680
2681
				// after wrapper
2682
				?>
2683
			</p>
2684
			<?php
2685
2686
		}
2687
2688
		/**
2689
		 * Get the widget input description html.
2690
		 *
2691
		 * @param $args
2692
		 *
2693
		 * @return string
2694
		 * @todo, need to make its own tooltip script
2695
		 */
2696
		public function widget_field_desc( $args ) {
2697
2698
			$description = '';
2699
			if ( isset( $args['desc'] ) && $args['desc'] ) {
2700
				if ( isset( $args['desc_tip'] ) && $args['desc_tip'] ) {
2701
					$description = $this->desc_tip( $args['desc'] );
2702
				} else {
2703
					$description = '<span class="description">' . wp_kses_post( $args['desc'] ) . '</span>';
2704
				}
2705
			}
2706
2707
			return $description;
2708
		}
2709
2710
		/**
2711
		 * Get the tool tip html.
2712
		 *
2713
		 * @param $tip
2714
		 * @param bool $allow_html
2715
		 *
2716
		 * @return string
2717
		 */
2718
		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...
2719
			if ( $allow_html ) {
2720
				$tip = $this->sanitize_tooltip( $tip );
2721
			} else {
2722
				$tip = esc_attr( $tip );
2723
			}
2724
2725
			return '<span class="gd-help-tip dashicons dashicons-editor-help" title="' . $tip . '"></span>';
2726
		}
2727
2728
		/**
2729
		 * Sanitize a string destined to be a tooltip.
2730
		 *
2731
		 * @param string $var
2732
		 *
2733
		 * @return string
2734
		 */
2735
		public function sanitize_tooltip( $var ) {
2736
			return htmlspecialchars( wp_kses( html_entity_decode( $var ), array(
2737
				'br'     => array(),
2738
				'em'     => array(),
2739
				'strong' => array(),
2740
				'small'  => array(),
2741
				'span'   => array(),
2742
				'ul'     => array(),
2743
				'li'     => array(),
2744
				'ol'     => array(),
2745
				'p'      => array(),
2746
			) ) );
2747
		}
2748
2749
		/**
2750
		 * Processing widget options on save
2751
		 *
2752
		 * @param array $new_instance The new options
2753
		 * @param array $old_instance The previous options
2754
		 *
2755
		 * @return array
2756
		 * @todo we should add some sanitation here.
2757
		 */
2758
		public function update( $new_instance, $old_instance ) {
2759
2760
			//save the widget
2761
			$instance = array_merge( (array) $old_instance, (array) $new_instance );
2762
2763
			// set widget instance
2764
			$this->instance = $instance;
2765
2766
			if ( empty( $this->arguments ) ) {
2767
				$this->get_arguments();
2768
			}
2769
2770
			// check for checkboxes
2771
			if ( ! empty( $this->arguments ) ) {
2772
				foreach ( $this->arguments as $argument ) {
2773
					if ( isset( $argument['type'] ) && $argument['type'] == 'checkbox' && ! isset( $new_instance[ $argument['name'] ] ) ) {
2774
						$instance[ $argument['name'] ] = '0';
2775
					}
2776
				}
2777
			}
2778
2779
			return $instance;
2780
		}
2781
2782
		/**
2783
		 * Checks if the current call is a ajax call to get the block content.
2784
		 *
2785
		 * This can be used in your widget to return different content as the block content.
2786
		 *
2787
		 * @since 1.0.3
2788
		 * @return bool
2789
		 */
2790
		public function is_block_content_call() {
2791
			$result = false;
2792
			if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'super_duper_output_shortcode' ) {
2793
				$result = true;
2794
			}
2795
2796
			return $result;
2797
		}
2798
2799
	}
2800
2801
}