PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.5.2
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.5.2
4.5.4 4.2.1 4.2.2 4.2.3 4.5.0 4.5.2 4.5.3 4.2.0 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 All 146 releases
ultimate-post-kit / admin / class-settings-api.php

class-settings-api.php in Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets 4.5.2, at admin/class-settings-api.php

1,237 lines 49.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit; // Exit if accessed directly.
5 }
6
7 if (!class_exists('UltimatePostKit_Settings_API')) :
8
9 class UltimatePostKit_Settings_API {
10
11 /**
12 * settings sections array
13 *
14 * @var array
15 */
16 protected $settings_sections = array();
17
18 /**
19 * Settings fields array
20 *
21 * @var array
22 */
23 protected $settings_fields = array();
24
25 public function __construct() {
26 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
27
28 add_action('wp_ajax_ultimate_post_kit_settings_save', [$this, "ultimate_post_kit_settings_save"]);
29 }
30
31 /**
32 * Enqueue scripts and styles
33 */
34 function admin_enqueue_scripts() {
35 wp_enqueue_script('jquery');
36 }
37
38 /**
39 * Set settings sections
40 *
41 * @param array $sections setting sections array
42 */
43 function set_sections($sections) {
44 $this->settings_sections = $sections;
45
46 return $this;
47 }
48
49 /**
50 * Add a single section
51 *
52 * @param array $section
53 */
54 function add_section($section) {
55 $this->settings_sections[] = $section;
56
57 return $this;
58 }
59
60 /**
61 * Set settings fields
62 *
63 * @param array $fields settings fields array
64 */
65 function set_fields($fields) {
66 $this->settings_fields = $fields;
67
68 return $this;
69 }
70
71 function add_field($section, $field) {
72 $defaults = array(
73 'name' => '',
74 'label' => '',
75 'desc' => '',
76 'type' => 'text'
77 );
78
79 $arg = wp_parse_args($field, $defaults);
80 $this->settings_fields[$section][] = $arg;
81
82 return $this;
83 }
84
85 function do_settings_sections($page) {
86 global $wp_settings_sections, $wp_settings_fields;
87
88 if (!isset($wp_settings_sections[$page])) {
89 return;
90 }
91
92 // $matched_height = ' bdt-grid bdt-height-match="target: > div > .upk-option-item-inner"';
93 $data_settings = '';
94
95 foreach ((array) $wp_settings_sections[$page] as $section) {
96
97 if ($section['id'] == 'ultimate_post_kit_api_settings') {
98 $section_class = ' bdt-grid-small bdt-child-width-1-3@xl';
99 } elseif ($section['id'] == 'ultimate_post_kit_other_settings') {
100 // $data_settings = $matched_height;
101 $section_class = ' bdt-grid-small bdt-child-width-1-3@xl';
102 } else {
103 $section_class = ' bdt-grid-small bdt-child-width-1-4@xl';
104 }
105
106
107
108 if ($section['callback']) {
109 call_user_func($section['callback'], $section);
110 }
111
112 if (!isset($wp_settings_fields) || !isset($wp_settings_fields[$page]) || !isset($wp_settings_fields[$page][$section['id']])) {
113 continue;
114 }
115 echo '<div class="upk-options" role="presentation" ' . esc_attr($data_settings) . '>';
116
117 echo '<p class="upk-no-result bdt-text-center bdt-width-1-1 bdt-margin-small-top bdt-h4">'.esc_html__('Ops! Your Searched widget not found! Do you have any idea? If yes, ', 'ultimate-post-kit').'<a href="https://feedback.ultimatepostkit.pro/b/3v2gg80n/feature-requests/idea/new" target="_blank">'.esc_html__('Submit here', 'ultimate-post-kit').'</a></p>';
118
119 $this->do_settings_fields($page, $section['id']);
120
121 echo '</div>';
122 }
123 }
124
125
126 function do_settings_fields($page, $section) {
127 global $wp_settings_fields;
128
129 if (!isset($wp_settings_fields[$page][$section])) {
130 return;
131 }
132
133
134 foreach ((array) $wp_settings_fields[$page][$section] as $field) {
135 $class = '';
136
137 if (!empty($field['args']['class'])) {
138 $class .= ' ' . esc_attr($field['args']['class']);
139 }
140
141 if (!empty($field['args']['widget_type'])) {
142 $class .= ' upk-widget-' . esc_attr($field['args']['widget_type']);
143 }
144
145 if (!empty($field['args']['widget_type']) && 'pro' == $field['args']['widget_type'] && true !== upk_license_validation()) {
146 $class .= ' upk-pro-inactive';
147 }
148
149
150 $used_widgets = self::get_used_widgets_obj();
151 $widget_name = 'upk-' . str_replace(' ', '-', strtolower($field['args']['id']));
152 $used_widgets_count = 0;
153
154 if (isset($used_widgets)) {
155 $used_widgets_count = (in_array($widget_name, array_keys($used_widgets)) ? $used_widgets[$widget_name] : 0);
156 if ($used_widgets_count === 0) {
157 $widget_name = str_replace('_', '-', $widget_name);
158 $used_widgets_count = (in_array($widget_name, array_keys($used_widgets)) ? $used_widgets[$widget_name] : 0);
159 }
160 }
161
162 $widget_used_status = ' upk-used';
163 if ($used_widgets_count === 0) {
164 $widget_used_status = ' upk-unused';
165 }
166
167 $data_type = ' data-widget-type="' . esc_attr($field['args']['widget_type']) . '" data-content-type="' . esc_attr($field['args']['content_type']) . esc_attr($widget_used_status) . '" data-widget-name="' . strtolower($field['args']['name']) . '"';
168
169
170 if (!empty($field['args']['widget_type']) && 'pro' == $field['args']['widget_type'] && true !== _is_upk_pro_activated()) {
171 $data_type .= ' bdt-tooltip="'.esc_html__('Pro widget only works with Pro version.', 'ultimate-post-kit').'"';
172 }
173
174 echo wp_kses("<div class='upk-option-item {$class} {$widget_used_status}' {$data_type}>", $this->get_allowed_field_html());
175
176 call_user_func($field['callback'], $field['args']);
177
178 echo '</div>';
179 }
180 }
181
182 /**
183 * Initialize and registers the settings sections and fileds to WordPress
184 *
185 * Usually this should be called at `admin_init` hook.
186 *
187 * This function gets the initiated settings sections and fields. Then
188 * registers them to WordPress and ready for use.
189 */
190 function admin_init() {
191 //register settings sections
192 foreach ($this->settings_sections as $section) {
193 if (false == get_option($section['id'])) {
194 add_option($section['id']);
195 }
196
197 if (isset($section['desc']) && !empty($section['desc'])) {
198 $section['desc'] = '<div class="inside">' . $section['desc'] . '</div>';
199 $callback = function () use ($section) {
200 echo wp_kses_post($section['desc']);
201 };
202 } else if (isset($section['callback'])) {
203 $callback = $section['callback'];
204 } else {
205 $callback = null;
206 }
207
208 add_settings_section($section['id'], $section['title'], $callback, $section['id']);
209 }
210
211 //register settings fields
212 foreach ($this->settings_fields as $section => $field) {
213 foreach ($field as $option) {
214
215 $name = $option['name'];
216 $type = isset($option['type']) ? $option['type'] : 'text';
217 $label = isset($option['label']) ? $option['label'] : '';
218 $callback = isset($option['callback']) ? $option['callback'] : array($this, 'callback_' . $type);
219
220 $args = array(
221 'id' => $name,
222 'class' => isset($option['class']) ? 'upk-' . $name . ' ' . $option['class'] : 'upk-' . $name,
223 'label_for' => "upk-{$section}[{$name}]",
224 'desc' => isset($option['desc']) ? $option['desc'] : '',
225 'name' => $label,
226 'section' => $section,
227 'size' => isset($option['size']) ? $option['size'] : null,
228 'options' => isset($option['options']) ? $option['options'] : '',
229 'std' => isset($option['default']) ? $option['default'] : '',
230 'sanitize_callback' => isset($option['sanitize_callback']) ? $option['sanitize_callback'] : '',
231 'type' => $type,
232 'placeholder' => isset($option['placeholder']) ? $option['placeholder'] : '',
233 'min' => isset($option['min']) ? $option['min'] : '',
234 'max' => isset($option['max']) ? $option['max'] : '',
235 'step' => isset($option['step']) ? $option['step'] : '',
236 'plugin_name' => !empty($option['plugin_name']) ? $option['plugin_name'] : null,
237 'plugin_path' => !empty($option['plugin_path']) ? $option['plugin_path'] : null,
238 'paid' => !empty($option['paid']) ? $option['paid'] : null,
239 'widget_type' => !empty($option['widget_type']) ? $option['widget_type'] : null,
240 'content_type' => !empty($option['content_type']) ? $option['content_type'] : null,
241 'demo_url' => !empty($option['demo_url']) ? $option['demo_url'] : null,
242 'video_url' => !empty($option['video_url']) ? $option['video_url'] : null,
243 );
244
245 add_settings_field("{$section}[{$name}]", $label, $callback, $section, $section, $args);
246 }
247 }
248
249 // creates our settings in the options table
250 foreach ($this->settings_sections as $section) {
251 register_setting(
252 $section['id'],
253 $section['id'],
254 array(
255 'type' => 'array',
256 'sanitize_callback' => array($this, 'sanitize_options'),
257 )
258 );
259 }
260 }
261
262 /**
263 * Get field description for display
264 *
265 * @param array $args settings field args
266 */
267 public function get_field_description($args) {
268 if (!empty($args['desc'])) {
269 $desc = sprintf('<p class="description">%s</p>', $args['desc']);
270 } else {
271 $desc = '';
272 }
273
274 return $desc;
275 }
276
277 /**
278 * Allowed HTML for settings-field output. Covers every form control this
279 * class renders so field markup survives wp_kses() intact.
280 *
281 * @return array
282 */
283 public function get_allowed_field_html() {
284 $attr = array(
285 'class' => array(),
286 'id' => array(),
287 'name' => array(),
288 'value' => array(),
289 'type' => array(),
290 'checked' => array(),
291 'selected' => array(),
292 'multiple' => array(),
293 'disabled' => array(),
294 'readonly' => array(),
295 'placeholder' => array(),
296 'min' => array(),
297 'max' => array(),
298 'step' => array(),
299 'rows' => array(),
300 'cols' => array(),
301 'for' => array(),
302 'scope' => array(),
303 'style' => array(),
304 'title' => array(),
305 'target' => array(),
306 'href' => array(),
307 'src' => array(),
308 'rel' => array(),
309 'aria-hidden' => array(),
310 'bdt-tooltip' => array(),
311 'bdt-grid' => array(),
312 'data-default-color' => array(),
313 'data-type' => array(),
314 'data-widget-type' => array(),
315 'data-content-type' => array(),
316 'data-widget-name' => array(),
317 );
318
319 return array(
320 'div' => $attr,
321 'span' => $attr,
322 'label' => $attr,
323 'input' => $attr,
324 'select' => $attr,
325 'option' => $attr,
326 'textarea' => $attr,
327 'fieldset' => $attr,
328 'a' => $attr,
329 'i' => $attr,
330 'p' => $attr,
331 'h3' => $attr,
332 'hr' => $attr,
333 'br' => array(),
334 'strong' => array(),
335 'em' => array(),
336 );
337 }
338
339 /**
340 * Displays a text field for a settings field
341 *
342 * @param array $args settings field args
343 */
344 function callback_text($args) {
345
346 $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
347 $class = 'bdt-input';
348 $type = isset($args['type']) ? $args['type'] : 'text';
349 $placeholder = empty($args['placeholder']) ? '' : ' placeholder="' . $args['placeholder'] . '"';
350 $html = '';
351
352
353 $html .= '<div class="upk-option-item-inner">';
354 if ($args['video_url']) {
355 $html .= '<a href="' . $args['video_url'] . '" target="_blank" class="upk-option-video" bdt-tooltip="View ' . $args['name'] . ' Video Tutorial"><i class="upk-icon-tutorial" aria-hidden="true"></i></a>';
356 }
357 $html .= sprintf('<label for="bdt_upk_%1$s[%2$s]">', $args['section'], $args['id']);
358 $html .= '<span scope="row" class="upk-option-label">' . $args['name'] . '</span>';
359 $html .= '</label>';
360
361
362 $html .= sprintf('<input type="%1$s" class="%2$s" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s/>', $type, $class, $args['section'], $args['id'], $value, $placeholder);
363
364 $html .= $this->get_field_description($args);
365
366 $html .= '</div>';
367
368 echo wp_kses($html, $this->get_allowed_field_html());
369 }
370
371 /**
372 * Displays a url field for a settings field
373 *
374 * @param array $args settings field args
375 */
376 function callback_url($args) {
377 $this->callback_text($args);
378 }
379
380 /**
381 * Displays a number field for a settings field
382 *
383 * @param array $args settings field args
384 */
385 function callback_number($args) {
386 $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
387 $size = isset($args['size']) && !is_null($args['size']) ? $args['size'] : 'regular';
388 $type = isset($args['type']) ? $args['type'] : 'number';
389 $placeholder = empty($args['placeholder']) ? '' : ' placeholder="' . $args['placeholder'] . '"';
390 $min = ($args['min'] == '') ? '' : ' min="' . $args['min'] . '"';
391 $max = ($args['max'] == '') ? '' : ' max="' . $args['max'] . '"';
392 $step = ($args['step'] == '') ? '' : ' step="' . $args['step'] . '"';
393
394 $html = sprintf('<input type="%1$s" class="%2$s-number" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s%8$s%9$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder, $min, $max, $step);
395 $html .= $this->get_field_description($args);
396
397 echo wp_kses($html, $this->get_allowed_field_html());
398 }
399
400 /**
401 * Get used widgets.
402 *
403 * @access public
404 * @since 6.0.0
405 *
406 * @return array
407 */
408
409 public static function get_used_widgets_obj() {
410 return UltimatePostKit_Admin_Settings::get_used_widgets();
411 }
412
413 /**
414 * Get unused widgets.
415 *
416 * @access public
417 * @since 6.0.0
418 *
419 * @return array
420 */
421
422 public static function get_unused_widgets_obj() {
423 return UltimatePostKit_Admin_Settings::get_unused_widgets();
424 }
425
426 /**
427 * Displays a checkbox for a settings field
428 *
429 * @param array $args settings field args
430 */
431 function callback_checkbox($args) {
432
433 $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
434 $plugin_name = isset($args['plugin_name']) ? $args['plugin_name'] : '';
435 $plugin_path = isset($args['plugin_path']) ? $args['plugin_path'] : '';
436 $paid = isset($args['paid']) ? $args['paid'] : '';
437
438 $parent_class = isset($args['ep_parent_switcher']) ? ' upk-feature-option-parent' : '';
439
440 $used_widgets = self::get_used_widgets_obj();
441 $widget_name = 'bdt-' . $args['id'];
442 $used_widgets_count = 0;
443
444
445 if (isset($used_widgets)) {
446 $used_widgets_count = (in_array($widget_name, array_keys($used_widgets)) ? $used_widgets[$widget_name] : 0);
447 if ($used_widgets_count === 0) {
448 $widget_name = str_replace('_', '-', $widget_name);
449 $used_widgets_count = (in_array($widget_name, array_keys($used_widgets)) ? $used_widgets[$widget_name] : 0);
450 }
451 }
452
453 $html = '';
454
455 $html .= '<div class="upk-option-item-inner">';
456 $html .= '<div class="bdt-grid bdt-grid-collapse bdt-flex bdt-flex-middle">';
457
458 $html .= '<div class="bdt-width-expand bdt-flex-inline bdt-flex-middle">';
459
460
461 $html .= '<i class="upk-icon-' . esc_attr($args['id']) . '" aria-hidden="true"></i>';
462 $html .= '<div class="upk-option-label-wrap">';
463 $html .= sprintf('<label for="bdt_upk_%1$s[%2$s]">', $args['section'], $args['id']);
464 $html .= '<span scope="row" class="upk-option-label">' . $args['name'] . '</span>';
465 $html .= '</label>';
466
467 $html .= '<div class="upk-option-links">';
468 if ($args['demo_url']) {
469 /* translators: %s: widget name */
470 $demo_title = sprintf(esc_html__('View %s Widget Demo', 'ultimate-post-kit'), $args['name']);
471 $html .= '<a href=' . $args['demo_url'] . ' target="_blank" class="upk-option-demo" title="' . $demo_title . '">' . esc_html__('Demo', 'ultimate-post-kit') . '<i class="upk-icon-preview" aria-hidden="true"></i></a>';
472 }
473 if ($args['video_url']) {
474 $html .= '<a href=' . $args['video_url'] . ' target="_blank" class="upk-option-video" title="View ' . $args['name'] . ' Video Tutorial">Video<i class="upk-icon-tutorial" aria-hidden="true"></i></a>';
475 }
476 $html .= '</div>';
477 $html .= '</div>';
478 $html .= '</div>';
479
480 $html .= '<div class="bdt-width-auto">';
481
482
483
484 // 3rd party widgets
485 if ($plugin_name and $plugin_path) {
486
487 if ($this->_is_plugin_installed($plugin_name, $plugin_path)) {
488 if (!current_user_can('activate_plugins')) {
489 return;
490 }
491 if (!is_plugin_active($plugin_path)) {
492 $active_link = wp_nonce_url('plugins.php?action=activate&amp;plugin=' . $plugin_path . '&amp;plugin_status=all&amp;paged=1&amp;s', 'activate-plugin_' . $plugin_path);
493 $html .= '<a href="' . $active_link . '" class="ultimate-post-kit-3pp-active" bdt-tooltip="' . esc_html__('Activate the plugin first then you can activate this widget.', 'ultimate-post-kit') . '"><span class="dashicons dashicons-admin-plugins"></span></a>';
494 }
495 } else {
496 if ($paid) {
497 $html .= '<a href="' . $paid . '" class="ultimate-post-kit-3pp-download" bdt-tooltip="' . esc_html__('Download and install plugin first then you can activate this widget.', 'ultimate-post-kit') . '"><span class="dashicons dashicons-download"></span></a>';
498 } else {
499 $install_link = wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=' . $plugin_name), 'install-plugin_' . $plugin_name);
500 $html .= '<a href="' . $install_link . '" class="ultimate-post-kit-3pp-install" bdt-tooltip="' . esc_html__('Install the plugin first then you can activate this widget.', 'ultimate-post-kit') . '"><span class="dashicons dashicons-download"></span></a>';
501 }
502 }
503 if ($this->_is_plugin_installed($plugin_name, $plugin_path) and is_plugin_active($plugin_path)) {
504
505 $html .= '<fieldset>';
506 $html .= sprintf('<label for="bdt_upk_%1$s[%2$s]">', $args['section'], $args['id']);
507 $html .= sprintf('<input type="hidden" name="%1$s[%2$s]" value="off" />', $args['section'], $args['id']);
508 $html .= sprintf('<input type="checkbox" class="checkbox' . $parent_class . '" id="bdt_upk_%1$s[%2$s]" name="%1$s[%2$s]" value="on" %3$s />', $args['section'], $args['id'], checked($value, 'on', false));
509 $html .= '<span class="switch"></span>';
510 $html .= '</label>';
511 $html .= '</fieldset>';
512 }
513 } else { // core widgets
514
515 $html .= '<fieldset>';
516 $html .= sprintf('<label for="bdt_upk_%1$s[%2$s]">', $args['section'], $args['id']);
517 $html .= sprintf('<input type="hidden" name="%1$s[%2$s]" value="off" />', $args['section'], $args['id']);
518 $html .= sprintf('<input type="checkbox" class="checkbox" id="bdt_upk_%1$s[%2$s]" name="%1$s[%2$s]" value="on" %3$s />', $args['section'], $args['id'], checked($value, 'on', false));
519 $html .= '<span class="switch"></span>';
520 $html .= '</label>';
521 $html .= '</fieldset>';
522 }
523
524 $html .= '</div>';
525 $html .= '</div>';
526 $html .= '</div>';
527
528 echo wp_kses($html, array(
529 'div' => array(
530 'class' => array(),
531 ),
532 'span' => array(
533 'class' => array(),
534 ),
535 'label' => array(
536 'for' => array(),
537 ),
538 'input' => array(
539 'type' => array(),
540 'class' => array(),
541 'id' => array(),
542 'name' => array(),
543 'value' => array(),
544 'checked' => array(),
545 ),
546 'i' => array(
547 'class' => array(),
548 'aria-hidden' => array(),
549 ),
550 'a' => array(
551 'href' => array(),
552 'target' => array(),
553 'class' => array(),
554 'bdt-tooltip' => array(),
555 ),
556 'fieldset' => array(),
557 ));
558 }
559
560 function _is_plugin_installed($plugin, $plugin_path) {
561 $installed_plugins = get_plugins();
562 return isset($installed_plugins[$plugin_path]);
563 }
564
565
566 /**
567 * Displays a multicheckbox for a settings field
568 *
569 * @param array $args settings field args
570 */
571 function callback_multicheck($args) {
572
573 $value = $this->get_option($args['id'], $args['section'], $args['std']);
574 $html = '<fieldset>';
575 $html .= sprintf('<input type="hidden" name="%1$s[%2$s]" value="" />', $args['section'], $args['id']);
576 foreach ($args['options'] as $key => $label) {
577 $checked = isset($value[$key]) ? $value[$key] : '0';
578 $html .= sprintf('<label for="bdt_upk_%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key);
579 $html .= sprintf('<input type="checkbox" class="checkbox" id="bdt_upk_%1$s[%2$s][%3$s]" name="%1$s[%2$s][%3$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked($checked, $key, false));
580 $html .= '<span class="switch"></span>';
581 $html .= sprintf('%1$s</label><br>', $label);
582 }
583
584 $html .= $this->get_field_description($args);
585 $html .= '</fieldset>';
586
587 echo wp_kses($html, $this->get_allowed_field_html());
588 }
589
590 /**
591 * Displays a radio button for a settings field
592 *
593 * @param array $args settings field args
594 */
595 function callback_radio($args) {
596
597 $value = $this->get_option($args['id'], $args['section'], $args['std']);
598 $html = '<fieldset>';
599
600 foreach ($args['options'] as $key => $label) {
601 $html .= sprintf('<label for="bdt_upk_%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key);
602 $html .= sprintf('<input type="radio" class="radio" id="bdt_upk_%1$s[%2$s][%3$s]" name="%1$s[%2$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked($value, $key, false));
603 $html .= sprintf('%1$s</label><br>', $label);
604 }
605
606 $html .= $this->get_field_description($args);
607 $html .= '</fieldset>';
608
609 echo wp_kses($html, $this->get_allowed_field_html());
610 }
611
612 /**
613 * Displays a selectbox for a settings field
614 *
615 * @param array $args settings field args
616 */
617 function callback_select($args) {
618
619 $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
620 $size = isset($args['size']) && !is_null($args['size']) ? $args['size'] : 'regular';
621 $html = sprintf('<select class="%1$s" name="%2$s[%3$s]" id="%2$s[%3$s]">', $size, $args['section'], $args['id']);
622
623 foreach ($args['options'] as $key => $label) {
624 $html .= sprintf('<option value="%s"%s>%s</option>', $key, selected($value, $key, false), $label);
625 }
626
627 $html .= sprintf('</select>');
628 $html .= $this->get_field_description($args);
629
630 echo wp_kses($html, $this->get_allowed_field_html());
631 }
632
633 /**
634 * Displays a textarea for a settings field
635 *
636 * @param array $args settings field args
637 */
638 function callback_textarea($args) {
639
640 $value = esc_textarea($this->get_option($args['id'], $args['section'], $args['std']));
641 $size = isset($args['size']) && !is_null($args['size']) ? $args['size'] : 'regular';
642 $placeholder = empty($args['placeholder']) ? '' : ' placeholder="' . $args['placeholder'] . '"';
643
644 $html = '';
645 $html .= sprintf('<label for="bdt_upk_%1$s[%2$s]">', $args['section'], $args['id']);
646 $html .= '<span scope="row" class="upk-option-label">' . $args['name'] . '</span>';
647 $html .= '</label>';
648
649 $html .= sprintf('<textarea rows="5" cols="55" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" %4$s >%5$s</textarea>', $size, $args['section'], $args['id'], $placeholder, $value);
650 $html .= $this->get_field_description($args);
651
652 echo wp_kses($html, $this->get_allowed_field_html());
653 }
654
655 /**
656 * Displays the html for a settings field
657 *
658 * @param array $args settings field args
659 * @return string
660 */
661 function callback_html($args) {
662 echo wp_kses($args['desc'], $this->get_allowed_field_html());
663 }
664
665 /**
666 * Displays a file upload field for a settings field
667 *
668 * @param array $args settings field args
669 */
670 function callback_file($args) {
671
672 $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
673 $size = isset($args['size']) && !is_null($args['size']) ? $args['size'] : 'regular';
674 $id = $args['section'] . '[' . $args['id'] . ']';
675 $label = isset($args['options']['button_label']) ? $args['options']['button_label'] : __('Choose File', 'ultimate-post-kit');
676
677 $html = sprintf('<input type="text" class="%1$s-text wpsa-url" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value);
678 $html .= '<input type="button" class="button wpsa-browse" value="' . $label . '" />';
679 $html .= $this->get_field_description($args);
680
681 echo wp_kses($html, $this->get_allowed_field_html());
682 }
683
684 /**
685 * Displays a password field for a settings field
686 *
687 * @param array $args settings field args
688 */
689 function callback_password($args) {
690
691 $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
692 $size = isset($args['size']) && !is_null($args['size']) ? $args['size'] : 'regular';
693
694 $html = sprintf('<input type="password" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value);
695 $html .= $this->get_field_description($args);
696
697 echo wp_kses($html, $this->get_allowed_field_html());
698 }
699
700 /**
701 * Displays a color picker field for a settings field
702 *
703 * @param array $args settings field args
704 */
705 function callback_color($args) {
706
707 $value = esc_attr($this->get_option($args['id'], $args['section'], $args['std']));
708 $size = isset($args['size']) && !is_null($args['size']) ? $args['size'] : 'regular';
709
710 $html = sprintf('<input type="text" class="%1$s-text wp-color-picker-field" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s" data-default-color="%5$s" />', $size, $args['section'], $args['id'], $value, $args['std']);
711 $html .= $this->get_field_description($args);
712
713 echo wp_kses($html, $this->get_allowed_field_html());
714 }
715
716 /**
717 * Displays a 2 colspan subheading field for a settings field
718 *
719 * @param array $args settings field args
720 */
721 function callback_subheading($args) {
722
723 $html = '<h3 class="setting_subheading column-merge">' . $args['name'] . '</h3>';
724 $html .= $this->get_field_description($args);
725 $html .= '<hr class="setting_separator">';
726
727 echo wp_kses($html, $this->get_allowed_field_html());
728 }
729
730 function callback_start_group($args) {
731
732 $html = '<div class="upk-option-item-inner upk-option-group">';
733
734 $html .= sprintf('<label for="bdt_upk_%1$s[%2$s]">', $args['section'], $args['id']);
735 $html .= '<span scope="row" class="upk-option-label">' . $args['name'] . '</span>';
736 $html .= '</label>';
737
738 if ($args['video_url']) {
739 $html .= '<a href="' . $args['video_url'] . '" target="_blank" class="upk-option-video" bdt-tooltip="View ' . $args['name'] . ' Video Tutorial"><i class="upk-icon-tutorial" aria-hidden="true"></i></a>';
740 }
741
742 $html .= $this->get_field_description($args);
743
744 $html .= '<div class="bdt-grid" bdt-grid>';
745
746 echo wp_kses($html, $this->get_allowed_field_html());
747 }
748
749 function callback_end_group($args) {
750
751 $html = '</div>';
752 $html .= '</div>';
753
754 echo wp_kses($html, $this->get_allowed_field_html());
755 }
756
757 /**
758 * Displays a 2 colspan separator field for a settings field
759 *
760 * @param array $args settings field args
761 */
762 function callback_separator($args) {
763
764 $html = '<hr class="setting_separator column-merge">';
765 $html .= $this->get_field_description($args);
766
767
768 echo wp_kses($html, $this->get_allowed_field_html());
769 }
770
771
772 /**
773 * Displays a select box for creating the pages select box
774 *
775 * @param array $args settings field args
776 */
777 function callback_pages($args) {
778
779 $dropdown_args = array(
780 'selected' => esc_attr($this->get_option($args['id'], $args['section'], $args['std'])),
781 'name' => $args['section'] . '[' . $args['id'] . ']',
782 'id' => $args['section'] . '[' . $args['id'] . ']',
783 'echo' => 0
784 );
785 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- 'echo' => 0 makes wp_dropdown_pages() return (not print) the markup, which is escaped via wp_kses() below.
786 $html = wp_dropdown_pages($dropdown_args);
787 echo wp_kses($html, $this->get_allowed_field_html());
788 }
789
790 /**
791 * Sanitize callback for Settings API
792 *
793 * @return mixed
794 */
795 function sanitize_options($options) {
796
797 if (!$options) {
798 return $options;
799 }
800
801 foreach ($options as $option_slug => $option_value) {
802 $sanitize_callback = $this->get_sanitize_callback($option_slug);
803
804 // If a field-specific callback is set, use it.
805 if ($sanitize_callback) {
806 $options[$option_slug] = call_user_func($sanitize_callback, $option_value);
807 continue;
808 }
809
810 // Otherwise never store the value raw — apply a safe default so no
811 // submitted field escapes sanitization (wp.org register_setting rule).
812 $options[$option_slug] = $this->sanitize_default($option_value);
813 }
814
815 return $options;
816 }
817
818 /**
819 * Default sanitizer for settings values without a field-specific callback.
820 *
821 * @param mixed $value Raw value.
822 * @return mixed
823 */
824 private function sanitize_default($value) {
825 if (is_array($value)) {
826 return array_map(array($this, 'sanitize_default'), $value);
827 }
828
829 return is_scalar($value) ? sanitize_text_field((string) $value) : '';
830 }
831
832 /**
833 * Get sanitization callback for given option slug
834 *
835 * @param string $slug option slug
836 *
837 * @return mixed string or bool false
838 */
839 function get_sanitize_callback($slug = '') {
840 if (empty($slug)) {
841 return false;
842 }
843
844 // Iterate over registered fields and see if we can find proper callback
845 foreach ($this->settings_fields as $section => $options) {
846 foreach ($options as $option) {
847 if ($option['name'] != $slug) {
848 continue;
849 }
850
851 // Return the callback name
852 return isset($option['sanitize_callback']) && is_callable($option['sanitize_callback']) ? $option['sanitize_callback'] : false;
853 }
854 }
855
856 return false;
857 }
858
859 /**
860 * Get the value of a settings field
861 *
862 * @param string $option settings field name
863 * @param string $section the section name this field belongs to
864 * @param string $default default text if it's not found
865 * @return string
866 */
867 function get_option($option, $section, $default = '') {
868
869 $options = get_option($section);
870
871 if (isset($options[$option])) {
872 return $options[$option];
873 }
874
875 return $default;
876 }
877
878 /**
879 * Show navigations as tab
880 *
881 * Shows all the settings section labels as tab
882 */
883 function show_navigation() {
884 $html = '<div class="bdt-dashboard-navigation">';
885 $html .= '<ul class="bdt-tab bdt-flex-column" bdt-tab="animation: bdt-animation-slide-bottom-small;connect: .bdt-tab-container;">';
886
887 // Dashboard - always first
888 $html .= sprintf('<li><a href="#%1$s" class="bdt-tab-item" id="bdt-%1$s" data-tab-index="0"><i class="dashicons dashicons-admin-home"></i>%2$s</a></li>', 'ultimate_post_kit_welcome', esc_html__('Dashboard', 'ultimate-post-kit'));
889
890 $count = 1;
891
892 // Get all sections including manually created ones
893 $all_sections = $this->get_all_sections();
894
895 foreach ($all_sections as $tab) {
896 $icon = isset($tab['icon']) ? $tab['icon'] : 'dashicons dashicons-screenoptions';
897 $html .= sprintf('<li><a href="#%1$s" class="bdt-tab-item" id="bdt-%1$s" data-tab-index="%2$s"><i class="%4$s"></i>%3$s</a></li>', $tab['id'], $count++, $tab['title'], $icon);
898 }
899
900 // Extension tabs registered by add-ons (e.g. Ultimate Post Kit Pro). The
901 // core plugin only provides the extension point; it ships no tabs of its own.
902 foreach ($this->get_extra_dashboard_tabs() as $tab) {
903 if (empty($tab['id']) || empty($tab['title'])) {
904 continue;
905 }
906 $icon = isset($tab['icon']) ? $tab['icon'] : 'dashicons dashicons-screenoptions';
907 $html .= sprintf('<li><a href="#%1$s" class="bdt-tab-item" id="bdt-%1$s" data-tab-index="%2$s"><i class="%4$s"></i>%3$s</a></li>', $tab['id'], $count++, esc_html($tab['title']), esc_attr($icon));
908 }
909
910 // License section
911 $license_wl_status = UltimatePostKit_Admin_Settings::license_wl_status();
912
913 if (!defined('BDTUPK_LO') || false == $license_wl_status) {
914 // On the free version this tab shows the "Get Pro" page, not a license form,
915 // so label it accordingly.
916 $is_pro_activated = function_exists('_is_upk_pro_activated') ? _is_upk_pro_activated() : false;
917 $license_tab_title = (true === $is_pro_activated)
918 ? esc_html__('License', 'ultimate-post-kit')
919 : esc_html__('Get Pro', 'ultimate-post-kit');
920
921 $html .= sprintf('<li><a href="#%1$s" class="bdt-tab-item" id="bdt-%1$s" data-tab-index="%2$s"><i class="dashicons dashicons-admin-network"></i>%3$s</a></li>', 'ultimate_post_kit_license_settings', $count, $license_tab_title);
922 }
923
924 $html .= '</ul>';
925 $html .= '</div>';
926
927 echo wp_kses($html, array(
928 'div' => array(
929 'class' => true,
930 ),
931 'ul' => array(
932 'class' => true,
933 'bdt-tab' => true,
934 ),
935 'li' => array(
936 'class' => true,
937 ),
938 'a' => array(
939 'href' => true,
940 'class' => true,
941 'id' => true,
942 'data-tab-index' => true,
943 ),
944 'i' => array(
945 'class' => true,
946 )
947 ));
948 }
949
950 /**
951 * Extra dashboard tabs contributed by add-on plugins.
952 *
953 * Neutral extension point: the core plugin renders whatever tabs an add-on
954 * registers here and ships none of its own. Each item is an array
955 * [ 'id' => string, 'title' => string, 'icon' => string, 'callback' =>
956 * callable ] where the callback echoes the tab body.
957 *
958 * @return array
959 */
960 public function get_extra_dashboard_tabs() {
961 return (array) apply_filters( 'ultimate_post_kit_dashboard_extra_tabs', array() );
962 }
963
964 function ultimate_post_kit_settings_save() {
965
966 if (!check_ajax_referer('ultimate-post-kit-settings-save-nonce')) {
967 wp_send_json_error();
968 }
969
970 if (!current_user_can('manage_options')) {
971 return;
972 }
973
974 $moudle_id = isset($_POST['id']) ? sanitize_text_field(wp_unslash($_POST['id'])) : '';
975
976 unset($_POST['id']);
977
978 // Only ever write options inside this plugin's own namespace. Without
979 // this the option name was fully attacker-chosen, letting a request
980 // overwrite arbitrary core options (default_role, siteurl, ...).
981 if ('' === $moudle_id || 0 !== strpos($moudle_id, 'ultimate_post_kit')) {
982 wp_send_json_error();
983 }
984
985 if (isset($_POST[$moudle_id])) {
986 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below via sanitize_options()/sanitize_default().
987 $raw_value = wp_unslash($_POST[$moudle_id]);
988
989 // Route the value through the registered per-field sanitizers
990 // instead of storing raw request data.
991 $value = is_array($raw_value)
992 ? $this->sanitize_options($raw_value)
993 : sanitize_text_field($raw_value);
994
995 update_option($moudle_id, $value);
996 }
997
998 wp_send_json_success();
999 }
1000
1001 /**
1002 * Get all sections including manually created content pages
1003 */
1004 private function get_all_sections() {
1005 // Start with the settings sections that have forms
1006 $all_sections = $this->settings_sections;
1007
1008 // Add manually created content sections that don't have settings forms
1009 $content_only_sections = [
1010 [
1011 'id' => 'ultimate_post_kit_analytics_system_req',
1012 'title' => esc_html__('System Status', 'ultimate-post-kit'),
1013 'icon' => 'dashicons dashicons-chart-bar',
1014 ],
1015 [
1016 'id' => 'ultimate_post_kit_other_plugins',
1017 'title' => esc_html__('Other Plugins', 'ultimate-post-kit'),
1018 'icon' => 'dashicons dashicons-admin-plugins',
1019 ],
1020 // [
1021 // 'id' => 'ultimate_post_kit_affiliate',
1022 // 'title' => esc_html__('Get Up to 60%', 'ultimate-post-kit'),
1023 // 'icon' => 'dashicons dashicons-money-alt',
1024 // ],
1025 ];
1026
1027 if (true == _is_upk_pro_activated()) {
1028 $content_only_sections[] = [
1029 'id' => 'ultimate_post_kit_rollback_version',
1030 'title' => esc_html__('Rollback Version', 'ultimate-post-kit'),
1031 'icon' => 'dashicons dashicons-update',
1032 ];
1033 }
1034
1035 // Check if each content section exists in settings sections, if not add it
1036 foreach ($content_only_sections as $content_section) {
1037 $exists = false;
1038 foreach ($all_sections as $existing_section) {
1039 if ($existing_section['id'] === $content_section['id']) {
1040 $exists = true;
1041 break;
1042 }
1043 }
1044 if (!$exists) {
1045 $all_sections[] = $content_section;
1046 }
1047 }
1048
1049 return $all_sections;
1050 }
1051
1052 /**
1053 * Show the section settings forms
1054 *
1055 * This function displays every sections in a different form
1056 */
1057 function show_forms() {
1058 ?>
1059
1060 <?php $i = 0;
1061 foreach ($this->settings_sections as $form) {
1062 $i++; ?>
1063 <div id="<?php echo esc_attr($form['id']); ?>_page" class="upk-option-page">
1064
1065 <div bdt-filter="target: .upk-options" class="upk-options-parent" id="upk-options-parent-<?php echo esc_attr($i); ?>">
1066
1067
1068 <?php if ($form['id'] == 'ultimate_post_kit_active_modules' or $form['id'] == 'ultimate_post_kit_elementor_extend'): ?>
1069
1070 <div class="bdt-widget-filter-wrapper bdt-flex bdt-flex-column bdt-flex-wrap"
1071 bdt-sticky="end: !.upk-dashboard-container; offset: 115; animation: bdt-animation-slide-top-small; duration: 300">
1072
1073 <!-- Filter Shape Elements -->
1074 <div class="upk-filter-elements">
1075 <span class="upk-filter-element upk-filter-circle"></span>
1076 <span class="upk-filter-element upk-filter-dots"></span>
1077 <span class="upk-filter-element upk-filter-wave"></span>
1078 <span class="upk-filter-element upk-filter-hexagon"></span>
1079 <span class="upk-filter-element upk-filter-zigzag"></span>
1080 </div>
1081
1082 <div class="bdt-widget-filter-header">
1083
1084 <div class="bdt-flex bdt-flex-wrap">
1085
1086 <div class="bdt-width-expand@l upk-widget-filter-nav bdt-visible@l">
1087 <div class="bdt-flex-inline bdt-flex-middle">
1088
1089 <div>
1090 <ul
1091 class="bdt-subnav bdt-subnav-pill upk-widget-filter bdt-widget-type-content bdt-flex-inline">
1092 <li class="upk-widget-all" bdt-filter-control="*"><a
1093 href="#"><?php esc_html_e('All', 'ultimate-post-kit'); ?></a></li>
1094 <li class="upk-widget-free bdt-active"
1095 bdt-filter-control="filter: [data-widget-type='free']; group: data-content-type">
1096 <a href="#"><?php esc_html_e('Free', 'ultimate-post-kit'); ?></a>
1097 </li>
1098 <li class="upk-widget-pro"
1099 bdt-filter-control="filter: [data-widget-type='pro']; group: data-content-type">
1100 <a href="#"><?php esc_html_e('Pro', 'ultimate-post-kit'); ?></a>
1101 </li>
1102
1103 </ul>
1104 </div>
1105
1106 <?php if ($form['id'] == 'ultimate_post_kit_active_modules' or $form['id'] == 'ultimate_post_kit_third_party_widget'): ?>
1107
1108
1109 <?php if ($form['id'] != 'ultimate_post_kit_elementor_extend' or $form['id'] == 'ultimate_post_kit_third_party_widget'): ?>
1110
1111 <div>
1112 <ul
1113 class="bdt-subnav bdt-subnav-pill upk-widget-filter upk-used-unused-widgets bdt-flex-inline ">
1114 <li class="upk-widget--"
1115 bdt-filter-control="filter: [data-content-type*='upk-used']; group: data-content-type">
1116 <a href="#"><?php esc_html_e('Used', 'ultimate-post-kit'); ?>
1117 <span class="bdt-badge upk-used-widget"></span>
1118 </a>
1119 </li>
1120 <li class="upk-widget--"
1121 bdt-filter-control="filter: [data-content-type*='upk-unused']; group: data-content-type">
1122 <a href="#"
1123 bdt-tooltip="<?php esc_html_e('Don\'t need unused widget? Click on the Deactivate All button.', 'ultimate-post-kit'); ?>"><?php esc_html_e('Unused', 'ultimate-post-kit'); ?>
1124 <span class="bdt-badge upk-unused-widget bdt-danger"></span>
1125 </a>
1126 </li>
1127 </ul>
1128
1129 </div>
1130 <?php endif; ?>
1131
1132 <?php endif; ?>
1133 </div>
1134 </div>
1135
1136
1137 <div class="bdt-width-auto@l bdt-search-active-wrap bdt-flex bdt-flex-middle bdt-flex-between">
1138 <div class="bdt-widget-search">
1139 <input data-id="upk-options-parent-<?php echo esc_attr($i); ?>" onkeyup="filterSearch(this);"
1140 bdt-filter-control="" class="bdt-search-input bdt-flex-middle" type="search"
1141 placeholder="<?php esc_html_e('Search widget...', 'ultimate-post-kit'); ?>"
1142 autofocus>
1143 </div>
1144
1145 <div>
1146 <ul class="bdt-subnav bdt-subnav-pill upk-widget-onoff">
1147 <li>
1148 <a href="#" class="upk-active-all-widget">
1149 <?php esc_html_e('Activate All', 'ultimate-post-kit'); ?>
1150 </a>
1151 </li>
1152 <li>
1153 <a href="#" class="upk-deactive-all-widget">
1154 <?php esc_html_e('Deactivate All', 'ultimate-post-kit'); ?>
1155 </a>
1156 </li>
1157 </ul>
1158 </div>
1159 </div>
1160 </div>
1161
1162 <?php if ($form['id'] == 'ultimate_post_kit_active_modules'): ?>
1163 <div class="upk-content-type-filter bdt-margin-top">
1164 <div class="bdt-flex bdt-flex-wrap bdt-flex-middle bdt-visible@l">
1165 <div class="upk-filter-by-text bdt-visible@xl">
1166 <?php esc_html_e('Filter By: ', 'ultimate-post-kit'); ?>
1167 </div>
1168 <ul class="bdt-nav xbdt-subnav-pill xbdt-dropdown-nav upk-widget-filter upk-widget-content-type bdt-flex bdt-flex-wrap ">
1169 <li class="upk-widget-new" bdt-filter-control="filter: [data-content-type*='new']; group: data-widget-type">
1170 <a href="#"><?php esc_html_e('New', 'ultimate-post-kit'); ?></a>
1171 </li>
1172 <li class="upk-widget-grid" bdt-filter-control="filter: [data-content-type*='grid']; group: data-widget-type">
1173 <a href="#"><?php esc_html_e('Grid', 'ultimate-post-kit'); ?></a>
1174 </li>
1175 <li class="upk-widget-list" bdt-filter-control="filter: [data-content-type*='list']; group: data-widget-type">
1176 <a href="#"><?php esc_html_e('List', 'ultimate-post-kit'); ?></a>
1177 </li>
1178 <li class="upk-widget-carousel" bdt-filter-control="filter: [data-content-type*='carousel']; group: data-widget-type">
1179 <a href="#"><?php esc_html_e('Carousel', 'ultimate-post-kit'); ?></a>
1180 </li>
1181 <li class="upk-widget-slider" bdt-filter-control="filter: [data-content-type*='slider']; group: data-widget-type">
1182 <a href="#"><?php esc_html_e('Slider', 'ultimate-post-kit'); ?></a>
1183 </li>
1184 <li class="upk-widget-tabs" bdt-filter-control="filter: [data-content-type*='tabs']; group: data-widget-type">
1185 <a href="#"><?php esc_html_e('Tabs', 'ultimate-post-kit'); ?></a>
1186 </li>
1187 <li class="upk-widget-timeline" bdt-filter-control="filter: [data-content-type*='timeline']; group: data-widget-type">
1188 <a href="#"><?php esc_html_e('Timeline', 'ultimate-post-kit'); ?></a>
1189 </li>
1190 <li class="upk-widget-template-builder" bdt-filter-control="filter: [data-content-type*='template-builder']; group: data-widget-type">
1191 <a href="#"><?php esc_html_e('Template Builder', 'ultimate-post-kit'); ?></a>
1192 </li>
1193 <li class="upk-widget-loop" bdt-filter-control="filter: [data-content-type*='loop']; group: data-widget-type">
1194 <a href="#"><?php esc_html_e('Loop Builder', 'ultimate-post-kit'); ?></a>
1195 </li>
1196 <li class="upk-widget-others" bdt-filter-control="filter: [data-content-type*='others']; group: data-widget-type">
1197 <a href="#"><?php esc_html_e('Others', 'ultimate-post-kit'); ?></a>
1198 </li>
1199 </ul>
1200 </div>
1201 </div>
1202 <?php endif; ?>
1203
1204 </div>
1205
1206 </div>
1207
1208 <?php endif; ?>
1209
1210 <form class="settings-save" method="post" action="admin-ajax.php?action=ultimate_post_kit_settings_save">
1211 <input type="hidden" name="id" value="<?php echo esc_attr($form['id']); ?>">
1212
1213 <?php
1214
1215 if (!current_user_can('manage_options')) {
1216 return;
1217 }
1218
1219 wp_nonce_field('ultimate-post-kit-settings-save-nonce');
1220
1221 do_action('ultimate_post_kit_form_top_' . $form['id'], $form);
1222
1223 $this->do_settings_sections($form['id']);
1224
1225 do_action('ultimate_post_kit_form_bottom_' . $form['id'], $form);
1226
1227 ?>
1228
1229 </form>
1230 </div>
1231 </div>
1232 <?php }
1233 }
1234 }
1235
1236 endif;
1237