PluginProbe
Bricksable for Bricks Builder / 1.3.0
Bricksable for Bricks Builder v1.3.0
1.2.9 1.3.0 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.31 1.4.32 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 All 132 releases
← All changes | includes/class-bricksable-settings.php +745 -698 1.5.01.3.0 View file →
@@ -1,698 +1,745 @@
1 -<?php
2 -/**
3 - * Settings class file.
4 - *
5 - * @package Bricksable/Settings
6 - */
7 -
8 -if ( ! defined( 'ABSPATH' ) ) {
9 - exit;
10 -}
11 -
12 -/**
13 - * Settings class.
14 - */
15 -class Bricksable_Settings {
16 -
17 - /**
18 - * The single instance of Bricksable_Settings.
19 - *
20 - * @var object
21 - * @access private
22 - * @since 1.0.0
23 - */
24 - private static $instance = null;
25 -
26 - /**
27 - * The main plugin object.
28 - *
29 - * @var object
30 - * @access public
31 - * @since 1.0.0
32 - */
33 - public $parent = null;
34 -
35 - /**
36 - * Prefix for plugin settings.
37 - *
38 - * @var string
39 - * @access public
40 - * @since 1.0.0
41 - */
42 - public $base = '';
43 -
44 - /**
45 - * Available settings for plugin.
46 - *
47 - * @var array
48 - * @access public
49 - * @since 1.0.0
50 - */
51 - public $settings = array();
52 -
53 - /**
54 - * Constructor function.
55 - *
56 - * @param object $parent Parent object.
57 - */
58 - public function __construct( $parent ) {
59 - $this->parent = $parent;
60 -
61 - $this->base = 'bricksable_';
62 - // Check if Bricks Builder is installed.
63 - if ( 'bricks' !== wp_get_theme()->get( 'Template' ) ) {
64 - if ( 'Bricks' !== wp_get_theme()->get( 'Name' ) ) {
65 - add_action(
66 - 'admin_notices',
67 - function() {
68 - $message = sprintf(
69 - /* translators: 1: Theme name 2: Bricksable */
70 - esc_html__( '%1$s requires %2$s to be installed and activated', 'bricksable' ),
71 - '<strong>Bricksable</strong>',
72 - '<strong>Bricks Builder</strong>'
73 - );
74 - $html = sprintf( '<div class="notice notice-warning">%s</div>', wpautop( $message ) );
75 - echo wp_kses_post( $html );
76 - }
77 - );
78 - return;
79 - }
80 - }
81 -
82 - // Initialise settings.
83 - add_action( 'init', array( $this, 'init_settings' ), 11 );
84 -
85 - // Register plugin settings.
86 - add_action( 'admin_init', array( $this, 'register_settings' ) );
87 -
88 - // Add settings page to menu.
89 - add_action( 'admin_menu', array( $this, 'add_menu_item' ), 11 );
90 -
91 - // Add settings link to plugins page.
92 - $bricksable_file = isset( $this->parent->file ) ? $this->parent->file : false;
93 -
94 - add_filter(
95 - 'plugin_action_links_' . plugin_basename( $bricksable_file ),
96 - array(
97 - $this,
98 - 'add_settings_link',
99 - )
100 - );
101 -
102 - // Configure placement of plugin settings page. See readme for implementation.
103 - add_filter( $this->base . 'menu_settings', array( $this, 'configure_settings' ) );
104 -
105 - // Enable JSON Uploads.
106 - if ( ! empty( get_option( 'bricksable_json_uploads' ) ) ) {
107 - add_filter( 'upload_mimes', array( $this, 'bricksable_file_mime_types' ) );
108 - }
109 -
110 - // Register custom elements.
111 - add_action(
112 - 'init',
113 - function() {
114 - if ( 'on' === get_option( 'bricksable_all_elements' ) || false === get_option( 'bricksable_all_elements' ) ) {
115 - $file_names = $this->bricksable_load_elements();
116 - } else {
117 - $file_names = $this->bricksable_check_elements();
118 - }
119 -
120 - foreach ( $file_names as $file_name ) {
121 - $file = __DIR__ . "/elements/$file_name/element-$file_name.php";
122 - // Register all element in builder and frontend.
123 - \Bricks\Elements::register_element( $file );
124 - }
125 - },
126 - 11
127 - );
128 -
129 - add_filter(
130 - 'bricks/builder/i18n',
131 - function( $i18n ) {
132 - $i18n['bricksable'] = esc_html__( 'Bricksable', 'bricksable' );
133 -
134 - return $i18n;
135 - }
136 - );
137 - }
138 -
139 - /**
140 - * Initialise settings
141 - *
142 - * @return void
143 - */
144 - public function init_settings() {
145 - $this->settings = $this->settings_fields();
146 - }
147 -
148 - /**
149 - * Add settings page to admin menu
150 - *
151 - * @return void
152 - */
153 - public function add_menu_item() {
154 -
155 - $args = $this->menu_settings();
156 -
157 - // Do nothing if wrong location key is set.
158 - if ( is_array( $args ) && isset( $args['location'] ) && function_exists( 'add_' . $args['location'] . '_page' ) ) {
159 - switch ( $args['location'] ) {
160 - case 'options':
161 - case 'submenu':
162 - $page = add_submenu_page( $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
163 - break;
164 - case 'menu':
165 - $page = add_menu_page( $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'], $args['icon_url'], $args['position'] );
166 - break;
167 - default:
168 - return;
169 - }
170 - add_action( 'admin_print_styles-' . $page, array( $this, 'settings_assets' ) );
171 - }
172 - }
173 -
174 - /**
175 - * Prepare default settings page arguments
176 - *
177 - * @return mixed|void
178 - */
179 - private function menu_settings() {
180 - return apply_filters(
181 - $this->base . 'menu_settings',
182 - array(
183 - 'location' => 'submenu', // Possible settings: options, menu, submenu.
184 - 'parent_slug' => 'bricks',
185 - 'page_title' => __( 'Bricksable', 'bricksable' ),
186 - 'menu_title' => __( 'Bricksable', 'bricksable' ),
187 - 'capability' => 'manage_options',
188 - 'menu_slug' => $this->parent->_token . '_settings',
189 - 'function' => array( $this, 'settings_page' ),
190 - 'icon_url' => '',
191 - 'position' => 99,
192 - )
193 - );
194 - }
195 -
196 - /**
197 - * Container for settings page arguments
198 - *
199 - * @param array $settings Settings array.
200 - *
201 - * @return array
202 - */
203 - public function configure_settings( $settings = array() ) {
204 - return $settings;
205 - }
206 -
207 - /**
208 - * Load settings JS & CSS
209 - *
210 - * @return void
211 - */
212 - public function settings_assets() {
213 -
214 - // We're including the farbtastic script & styles here because they're needed for the colour picker
215 - // If you're not including a colour picker field then you can leave these calls out as well as the farbtastic dependency for the wpt-admin-js script below.
216 - wp_enqueue_style( 'farbtastic' );
217 - wp_enqueue_script( 'farbtastic' );
218 -
219 - // We're including the WP media scripts here because they're needed for the image upload field.
220 - // If you're not including an image upload then you can leave this function call out.
221 - // wp_enqueue_media().
222 -
223 - wp_register_script( $this->parent->_token . '-settings', $this->parent->assets_url . 'js/settings' . $this->parent->script_suffix . '.js', array( 'farbtastic', 'jquery' ), '1.0.0', true );
224 - wp_enqueue_script( $this->parent->_token . '-settings' );
225 - }
226 -
227 - /**
228 - * Add settings link to plugin list table
229 - *
230 - * @param array $links Existing links.
231 - * @return array Modified links.
232 - */
233 - public function add_settings_link( $links ) {
234 - $settings_link = '<a href="admin.php?page=' . $this->parent->_token . '_settings">' . __( 'Settings', 'bricksable' ) . '</a>';
235 - array_push( $links, $settings_link );
236 - return $links;
237 - }
238 -
239 - /**
240 - * Get Elements
241 - */
242 - public function get_elements() {
243 - $elements = array();
244 - $elements_dir = __DIR__ . '/elements';
245 - $scan = scandir( $elements_dir );
246 - foreach ( $scan as $result ) {
247 - if ( '.' === $result || '..' === $result ) {
248 - continue;
249 - }
250 - if ( is_dir( $elements_dir . '/' . $result ) ) {
251 - $elements[] = $result;
252 - }
253 - }
254 - return $elements;
255 - }
256 -
257 - /**
258 - * Build settings fields
259 - *
260 - * @return array Fields to be displayed on settings page
261 - */
262 - private function settings_fields() {
263 - $settings['general'] = array(
264 - 'title' => __( 'General', 'bricksable' ),
265 - 'fields' => array(
266 - array(
267 - 'id' => 'json_uploads',
268 - 'label' => __( 'JSON Uploads', 'bricksable' ),
269 - 'description' => __( 'Please be careful about uploading Lottie JSON files from an unknown source. JSON files may potentially include malicious content. You should therefore download JSON files only from trusted sources, and only enable JSON uploads for user roles that you trust to follow this rule.', 'bricksable' ),
270 - 'type' => 'checkbox_multi',
271 - 'options' => $this->get_editable_roles(),
272 - ),
273 - ),
274 - );
275 -
276 - $fields = array(
277 - array(
278 - 'id' => 'all_elements',
279 - 'description' => __( 'Check all Bricks Custom Elements.', 'bricksable' ),
280 - 'type' => 'checkbox',
281 - 'default' => 'on',
282 - ),
283 - );
284 -
285 - $elements = $this->get_elements();
286 - foreach ( $elements as $element ) {
287 - $label = ucwords( str_replace( '-', ' ', $element ) );
288 - $fields[] = array(
289 - 'id' => $element,
290 - 'label' => esc_attr( $label ),
291 - /* translators: %s: enable elements */
292 - 'description' => sprintf( __( 'Enable %1$s Elements', 'bricksable' ), esc_html( $label ) ),
293 - 'type' => 'checkbox',
294 - 'default' => 'on',
295 - );
296 - }
297 - $settings['elements'] = array(
298 - 'title' => __( 'Elements', 'bricksable' ),
299 - 'fields' => $fields,
300 - );
301 -
302 - $settings['misc'] = array(
303 - 'title' => __( 'Misc', 'bricksable' ),
304 - 'fields' => array(
305 - array(
306 - 'id' => 'uninstall_on_delete',
307 - 'label' => __( 'Remove Data on Uninstall?', 'bricksable' ),
308 - 'description' => __( 'Check this box if you would like Bricksable to completely remove all of its data when the plugin is deleted.', 'bricksable' ),
309 - 'type' => 'checkbox',
310 - ),
311 - ),
312 - );
313 -
314 - $settings = apply_filters( $this->parent->_token . '_settings_fields', $settings );
315 -
316 - return $settings;
317 - }
318 -
319 - /**
320 - * Register plugin settings
321 - *
322 - * @return void
323 - */
324 - public function register_settings() {
325 - if ( is_array( $this->settings ) ) {
326 -
327 - // Check posted/selected tab.
328 - //phpcs:disable
329 - $current_section = '';
330 - if ( isset( $_POST['tab'] ) && $_POST['tab'] ) {
331 - $current_section = sanitize_text_field( wp_unslash( $_POST['tab'] ) );
332 - } else {
333 - if ( isset( $_GET['tab'] ) && $_GET['tab'] ) {
334 - $current_section = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
335 - }
336 - }
337 - //phpcs:enable
338 -
339 - foreach ( $this->settings as $section => $data ) {
340 -
341 - if ( $current_section && $current_section !== $section ) {
342 - continue;
343 - }
344 -
345 - // Add section to page.
346 - add_settings_section( $section, $data['title'], array( $this, 'settings_section' ), $this->parent->_token . '_settings' );
347 -
348 - foreach ( $data['fields'] as $field ) {
349 -
350 - // Validation callback for field.
351 - $validation = '';
352 - if ( isset( $field['callback'] ) ) {
353 - $validation = $field['callback'];
354 - }
355 -
356 - // Register field.
357 - $option_name = $this->base . $field['id'];
358 - register_setting( $this->parent->_token . '_settings', $option_name, $validation );
359 -
360 - // Add field to page.
361 - add_settings_field(
362 - $field['id'],
363 - isset( $field['label'] ) ? $field['label'] : '',
364 - array( $this->parent->admin, 'display_field' ),
365 - $this->parent->_token . '_settings',
366 - $section,
367 - array(
368 - 'field' => $field,
369 - 'prefix' => $this->base,
370 - )
371 - );
372 - }
373 -
374 - if ( ! $current_section ) {
375 - break;
376 - }
377 - }
378 - }
379 - }
380 -
381 - /**
382 - * Settings section.
383 - *
384 - * @param array $section Array of section ids.
385 - * @return void
386 - */
387 - public function settings_section( $section ) {
388 - $html = '';
389 - if ( isset( $this->settings[ $section['id'] ]['description'] ) ) {
390 - $html = '<p> ' . $this->settings[ $section['id'] ]['description'] . '</p>' . "\n";
391 - }
392 - echo wp_kses( $html, $this->allowed_htmls );
393 - }
394 -
395 - /**
396 - * Load settings page content.
397 - *
398 - * @return void
399 - */
400 - public function settings_page() {
401 -
402 - // Build page HTML.
403 - $html = '<div class="wrap" id="' . $this->parent->_token . '_settings">' . "\n";
404 - $html .= '<h2>' . __( 'Bricksable Settings', 'bricksable' ) . '</h2>' . "\n";
405 -
406 - $tab = '';
407 -
408 - $nonce_name = 'bricksable_settings_nonce';
409 - $nonce = sanitize_text_field( wp_create_nonce( $nonce_name ) );
410 -
411 - if ( isset( $_POST['tab'] ) ) {
412 - if ( wp_verify_nonce( $nonce, $nonce_name ) ) {
413 - $current_section = sanitize_text_field( wp_unslash( $_POST['tab'] ) );
414 - }
415 - } else {
416 - if ( isset( $_GET['tab'] ) && sanitize_text_field( wp_unslash( $_GET['tab'] ) ) ) {
417 - $current_section = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
418 - }
419 - }
420 -
421 - // Show page tabs.
422 - if ( is_array( $this->settings ) && 1 < count( $this->settings ) ) {
423 -
424 - $html .= '<h2 class="nav-tab-wrapper">' . "\n";
425 -
426 - $c = 0;
427 - foreach ( $this->settings as $section => $data ) {
428 -
429 - // Set tab class.
430 - $class = 'nav-tab';
431 - if ( ! isset( $_GET['tab'] ) ) {
432 - if ( 0 === $c ) {
433 - $class .= ' nav-tab-active';
434 - }
435 - } else {
436 - if ( isset( $_GET['tab'] ) && $section === $_GET['tab'] ) {
437 - $tab = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
438 - $class .= ' nav-tab-active';
439 - }
440 - }
441 -
442 - // Set tab link.
443 - $tab_link = add_query_arg(
444 - array(
445 - 'tab' => $section,
446 - $nonce_name => $nonce,
447 - )
448 - );
449 -
450 - if ( isset( $_GET['settings-updated'] ) ) {
451 - $updated = sanitize_text_field( wp_unslash( $_GET['settings-updated'] ) );
452 -
453 - $tab_link = remove_query_arg( 'settings-updated', $tab_link );
454 - }
455 -
456 - // Output tab.
457 - $html .= '<a href="' . $tab_link . '" class="' . esc_attr( $class ) . '">' . esc_html( $data['title'] ) . '</a>' . "\n";
458 -
459 - ++$c;
460 - }
461 -
462 - $html .= '</h2>' . "\n";
463 - }
464 -
465 - $html .= '<form method="post" action="options.php" enctype="multipart/form-data">' . "\n";
466 -
467 - // Get settings fields.
468 - ob_start();
469 - settings_fields( $this->parent->_token . '_settings' );
470 - do_settings_sections( $this->parent->_token . '_settings' );
471 - $html .= ob_get_clean();
472 -
473 - $html .= '<p class="submit">' . "\n";
474 - $html .= '<input type="hidden" name="tab" value="' . esc_attr( $tab ) . '" />' . "\n";
475 - $html .= '<input name="Submit" type="submit" class="button-primary" value="' . esc_attr( __( 'Save Settings', 'bricksable' ) ) . '" />' . "\n";
476 - $html .= '</p>' . "\n";
477 - $html .= '</form>' . "\n";
478 - $html .= '</div>' . "\n";
479 -
480 - echo wp_kses( $html, $this->allowed_htmls );
481 - }
482 -
483 - /**
484 - * Main Bricksable_Settings Instance
485 - *
486 - * Ensures only one instance of Bricksable_Settings is loaded or can be loaded.
487 - *
488 - * @since 1.0.0
489 - * @static
490 - * @see Bricksable()
491 - * @param object $parent Object instance.
492 - * @return object Bricksable_Settings instance
493 - */
494 - public static function instance( $parent ) {
495 - if ( is_null( self::$instance ) ) {
496 - self::$instance = new self( $parent );
497 - }
498 - return self::$instance;
499 - } // End instance()
500 -
501 - /**
502 - * Cloning is forbidden.
503 - *
504 - * @since 1.0.0
505 - */
506 - public function __clone() {
507 - _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cloning of Bricksable_API is forbidden.' ) ), esc_attr( $this->parent->_version ) );
508 - } // End __clone()
509 -
510 - /**
511 - * Unserializing instances of this class is forbidden.
512 - *
513 - * @since 1.0.0
514 - */
515 - public function __wakeup() {
516 - _doing_it_wrong( __FUNCTION__, esc_html( __( 'Unserializing instances of Bricksable_API is forbidden.' ) ), esc_attr( $this->parent->_version ) );
517 - } // End __wakeup()
518 -
519 - /**
520 - * Allowed html for output.
521 - *
522 - * @var array
523 - */
524 - public $allowed_htmls = array(
525 - 'a' => array(
526 - 'href' => array(),
527 - 'title' => array(),
528 - 'class' => array(),
529 - ),
530 - 'h1' => array(
531 - 'href' => array(),
532 - 'title' => array(),
533 - 'class' => array(),
534 - ),
535 - 'h2' => array(
536 - 'href' => array(),
537 - 'title' => array(),
538 - 'class' => array(),
539 - ),
540 - 'h3' => array(
541 - 'href' => array(),
542 - 'title' => array(),
543 - 'class' => array(),
544 - ),
545 - 'h4' => array(
546 - 'href' => array(),
547 - 'title' => array(),
548 - 'class' => array(),
549 - ),
550 - 'input' => array(
551 - 'id' => array(),
552 - 'type' => array(),
553 - 'name' => array(),
554 - 'placeholder' => array(),
555 - 'value' => array(),
556 - 'class' => array(),
557 - 'checked' => array(),
558 - 'style' => array(),
559 - 'data-uploader_title' => array(),
560 - 'data-uploader_text' => array(),
561 - ),
562 - 'select' => array(
563 - 'id' => array(),
564 - 'type' => array(),
565 - 'name' => array(),
566 - 'placeholder' => array(),
567 - 'value' => array(),
568 - 'multiple' => array(),
569 - 'style' => array(),
570 - ),
571 - 'option' => array(
572 - 'id' => array(),
573 - 'type' => array(),
574 - 'name' => array(),
575 - 'placeholder' => array(),
576 - 'value' => array(),
577 - 'multiple' => array(),
578 - 'selected' => array(),
579 - ),
580 - 'label' => array(
581 - 'for' => array(),
582 - 'title' => array(),
583 - ),
584 - 'span' => array(
585 - 'class' => array(),
586 - 'title' => array(),
587 - ),
588 - 'table' => array(
589 - 'scope' => array(),
590 - 'title' => array(),
591 - 'class' => array(),
592 - 'role' => array(),
593 - ),
594 - 'tbody' => array(
595 - 'scope' => array(),
596 - 'title' => array(),
597 - 'class' => array(),
598 - 'role' => array(),
599 - ),
600 - 'th' => array(
601 - 'scope' => array(),
602 - 'title' => array(),
603 - ),
604 - 'form' => array(
605 - 'method' => array(),
606 - 'type' => array(),
607 - 'name' => array(),
608 - 'placeholder' => array(),
609 - 'value' => array(),
610 - 'multiple' => array(),
611 - 'selected' => array(),
612 - 'action' => array(),
613 - 'enctype' => array(),
614 - ),
615 - 'div' => array(
616 - 'class' => array(),
617 - 'id' => array(),
618 - ),
619 - 'img' => array(
620 - 'class' => array(),
621 - 'id' => array(),
622 - 'src' => array(),
623 - ),
624 - 'textarea' => array(
625 - 'class' => array(),
626 - 'id' => array(),
627 - 'rows' => array(),
628 - 'cols' => array(),
629 - 'name' => array(),
630 - 'placeholder' => array(),
631 - 'spellcheck' => array(),
632 - ),
633 - 'tr' => array(),
634 - 'td' => array(),
635 - 'p' => array(),
636 - 'br' => array(),
637 - 'em' => array(),
638 - 'strong' => array(),
639 - 'th' => array(),
640 - );
641 -
642 - /**
643 - * Get WP Roles
644 - *
645 - * @return array Exclude subscriber and other very limited roles
646 - */
647 - public function get_editable_roles() {
648 - $roles = wp_roles()->get_names();
649 - $has_roles = array();
650 - foreach ( $roles as $key => $label ) {
651 - $role = get_role( $key );
652 - if ( ! $role->has_cap( 'edit_posts' ) ) {
653 - continue;
654 - }
655 - $has_roles[ $key ] = $label;
656 - }
657 - return $has_roles;
658 - }
659 -
660 - /**
661 - * Mine Types
662 - *
663 - * @param return $mimes Check WP roles and allow json upload.
664 - */
665 - public function bricksable_file_mime_types( $mimes ) {
666 - $user = wp_get_current_user();
667 - $current_options = get_option( 'bricksable_json_uploads' );
668 - if ( array_intersect( $current_options, $user->roles ) ) {
669 - $mimes['json'] = 'application/json';
670 - }
671 - return $mimes;
672 - }
673 -
674 - /**
675 - * Bricksable Elements
676 - */
677 - public function bricksable_load_elements() {
678 - $file_names = $this->get_elements();
679 - return $file_names;
680 - }
681 -
682 - /**
683 - * Bricksable Check Elements
684 - */
685 - public function bricksable_check_elements() {
686 - $file_names = array();
687 - $elements = $this->get_elements();
688 -
689 - foreach ( $elements as $element ) {
690 - if ( 'on' === get_option( 'bricksable_' . $element ) || false === get_option( 'bricksable_' . $element ) ) {
691 - $file_names[] = $element;
692 - }
693 - }
694 -
695 - return $file_names;
696 - }
697 -
698 -}
1 +<?php
2 +/**
3 + * Settings class file.
4 + *
5 + * @package Bricksable/Settings
6 + */
7 +
8 +if ( ! defined( 'ABSPATH' ) ) {
9 + exit;
10 +}
11 +
12 +/**
13 + * Settings class.
14 + */
15 +class Bricksable_Settings {
16 +
17 + /**
18 + * The single instance of Bricksable_Settings.
19 + *
20 + * @var object
21 + * @access private
22 + * @since 1.0.0
23 + */
24 + private static $instance = null;
25 +
26 + /**
27 + * The main plugin object.
28 + *
29 + * @var object
30 + * @access public
31 + * @since 1.0.0
32 + */
33 + public $parent = null;
34 +
35 + /**
36 + * Prefix for plugin settings.
37 + *
38 + * @var string
39 + * @access public
40 + * @since 1.0.0
41 + */
42 + public $base = '';
43 +
44 + /**
45 + * Available settings for plugin.
46 + *
47 + * @var array
48 + * @access public
49 + * @since 1.0.0
50 + */
51 + public $settings = array();
52 +
53 + /**
54 + * Constructor function.
55 + *
56 + * @param object $parent Parent object.
57 + */
58 + public function __construct( $parent ) {
59 + $this->parent = $parent;
60 +
61 + $this->base = 'bricksable_';
62 + // Check if Bricks Builder is installed.
63 + if ( 'bricks' !== wp_get_theme()->get( 'Template' ) ) {
64 + if ( 'Bricks' !== wp_get_theme()->get( 'Name' ) ) {
65 + add_action(
66 + 'admin_notices',
67 + function() {
68 + $message = sprintf(
69 + /* translators: 1: Theme name 2: Bricksable */
70 + esc_html__( '%1$s requires %2$s to be installed and activated', 'bricksable' ),
71 + '<strong>Bricksable</strong>',
72 + '<strong>Bricks Builder</strong>'
73 + );
74 + $html = sprintf( '<div class="notice notice-warning">%s</div>', wpautop( $message ) );
75 + echo wp_kses_post( $html );
76 + }
77 + );
78 + return;
79 + }
80 + }
81 +
82 + // Initialise settings.
83 + add_action( 'init', array( $this, 'init_settings' ), 11 );
84 +
85 + // Register plugin settings.
86 + add_action( 'admin_init', array( $this, 'register_settings' ) );
87 +
88 + // Add settings page to menu.
89 + add_action( 'admin_menu', array( $this, 'add_menu_item' ), 11 );
90 +
91 + // Add settings link to plugins page.
92 + $bricksable_file = isset( $this->parent->file ) ? $this->parent->file : false;
93 +
94 + add_filter(
95 + 'plugin_action_links_' . plugin_basename( $bricksable_file ),
96 + array(
97 + $this,
98 + 'add_settings_link',
99 + )
100 + );
101 +
102 + // Configure placement of plugin settings page. See readme for implementation.
103 + add_filter( $this->base . 'menu_settings', array( $this, 'configure_settings' ) );
104 +
105 + // Enable JSON Uploads.
106 + if ( ! empty( get_option( 'bricksable_json_uploads' ) ) ) {
107 + add_filter( 'upload_mimes', array( $this, 'bricksable_file_mime_types' ) );
108 + }
109 +
110 + // Register custom elements.
111 + add_action(
112 + 'init',
113 + function() {
114 + if ( 'on' === get_option( 'bricksable_all_elements' ) || false === get_option( 'bricksable_all_elements' ) ) {
115 + $file_names = $this->bricksable_load_elements();
116 + } else {
117 + $file_names = $this->bricksable_check_elements();
118 + }
119 +
120 + foreach ( $file_names as $file_name ) {
121 + $file = __DIR__ . "/elements/element-$file_name.php";
122 + // Register all element in builder and frontend.
123 + \Bricks\Elements::register_element( $file );
124 + }
125 + },
126 + 11
127 + );
128 +
129 + add_filter(
130 + 'bricks/builder/i18n',
131 + function( $i18n ) {
132 + $i18n['bricksable'] = esc_html__( 'Bricksable', 'bricksable' );
133 +
134 + return $i18n;
135 + }
136 + );
137 + }
138 +
139 + /**
140 + * Initialise settings
141 + *
142 + * @return void
143 + */
144 + public function init_settings() {
145 + $this->settings = $this->settings_fields();
146 + }
147 +
148 + /**
149 + * Add settings page to admin menu
150 + *
151 + * @return void
152 + */
153 + public function add_menu_item() {
154 +
155 + $args = $this->menu_settings();
156 +
157 + // Do nothing if wrong location key is set.
158 + if ( is_array( $args ) && isset( $args['location'] ) && function_exists( 'add_' . $args['location'] . '_page' ) ) {
159 + switch ( $args['location'] ) {
160 + case 'options':
161 + case 'submenu':
162 + $page = add_submenu_page( $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
163 + break;
164 + case 'menu':
165 + $page = add_menu_page( $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'], $args['icon_url'], $args['position'] );
166 + break;
167 + default:
168 + return;
169 + }
170 + add_action( 'admin_print_styles-' . $page, array( $this, 'settings_assets' ) );
171 + }
172 + }
173 +
174 + /**
175 + * Prepare default settings page arguments
176 + *
177 + * @return mixed|void
178 + */
179 + private function menu_settings() {
180 + return apply_filters(
181 + $this->base . 'menu_settings',
182 + array(
183 + 'location' => 'submenu', // Possible settings: options, menu, submenu.
184 + 'parent_slug' => 'bricks',
185 + 'page_title' => __( 'Bricksable', 'bricksable' ),
186 + 'menu_title' => __( 'Bricksable', 'bricksable' ),
187 + 'capability' => 'manage_options',
188 + 'menu_slug' => $this->parent->_token . '_settings',
189 + 'function' => array( $this, 'settings_page' ),
190 + 'icon_url' => '',
191 + 'position' => 99,
192 + )
193 + );
194 + }
195 +
196 + /**
197 + * Container for settings page arguments
198 + *
199 + * @param array $settings Settings array.
200 + *
201 + * @return array
202 + */
203 + public function configure_settings( $settings = array() ) {
204 + return $settings;
205 + }
206 +
207 + /**
208 + * Load settings JS & CSS
209 + *
210 + * @return void
211 + */
212 + public function settings_assets() {
213 +
214 + // We're including the farbtastic script & styles here because they're needed for the colour picker
215 + // If you're not including a colour picker field then you can leave these calls out as well as the farbtastic dependency for the wpt-admin-js script below.
216 + wp_enqueue_style( 'farbtastic' );
217 + wp_enqueue_script( 'farbtastic' );
218 +
219 + // We're including the WP media scripts here because they're needed for the image upload field.
220 + // If you're not including an image upload then you can leave this function call out.
221 + // wp_enqueue_media().
222 +
223 + wp_register_script( $this->parent->_token . '-settings', $this->parent->assets_url . 'js/settings' . $this->parent->script_suffix . '.js', array( 'farbtastic', 'jquery' ), '1.0.0', true );
224 + wp_enqueue_script( $this->parent->_token . '-settings' );
225 + }
226 +
227 + /**
228 + * Add settings link to plugin list table
229 + *
230 + * @param array $links Existing links.
231 + * @return array Modified links.
232 + */
233 + public function add_settings_link( $links ) {
234 + $settings_link = '<a href="admin.php?page=' . $this->parent->_token . '_settings">' . __( 'Settings', 'bricksable' ) . '</a>';
235 + array_push( $links, $settings_link );
236 + return $links;
237 + }
238 +
239 + /**
240 + * Build settings fields
241 + *
242 + * @return array Fields to be displayed on settings page
243 + */
244 + private function settings_fields() {
245 + $settings['general'] = array(
246 + 'title' => __( 'General', 'bricksable' ),
247 + 'fields' => array(
248 + array(
249 + 'id' => 'json_uploads',
250 + 'label' => __( 'JSON Uploads', 'bricksable' ),
251 + 'description' => __( 'Please be careful about uploading Lottie JSON files from an unknown source. JSON files may potentially include malicious content. You should therefore download JSON files only from trusted sources, and only enable JSON uploads for user roles that you trust to follow this rule.', 'bricksable' ),
252 + 'type' => 'checkbox_multi',
253 + 'options' => $this->get_editable_roles(),
254 + ),
255 + ),
256 + );
257 +
258 + $settings['elements'] = array(
259 + 'title' => __( 'Elements', 'bricksable' ),
260 + 'fields' => array(
261 + array(
262 + 'id' => 'all_elements',
263 + 'description' => __( 'Check all Bricks Custom Elements.', 'bricksable' ),
264 + 'type' => 'checkbox',
265 + 'default' => 'on',
266 + ),
267 + array(
268 + 'id' => 'flip-box',
269 + 'label' => __( 'Flipbox', 'bricksable' ),
270 + 'description' => __( 'Enable Flipbox Element.', 'bricksable' ),
271 + 'type' => 'checkbox',
272 + 'default' => 'on',
273 + ),
274 + array(
275 + 'id' => 'tilt-image',
276 + 'label' => __( 'Tilt Image', 'bricksable' ),
277 + 'description' => __( 'Enable Tilt Image Element.', 'bricksable' ),
278 + 'type' => 'checkbox',
279 + 'default' => 'on',
280 + ),
281 + array(
282 + 'id' => 'text-notation',
283 + 'label' => __( 'Text Notation', 'bricksable' ),
284 + 'description' => __( 'Enable Text Notation Element.', 'bricksable' ),
285 + 'type' => 'checkbox',
286 + 'default' => 'on',
287 + ),
288 + array(
289 + 'id' => 'lottie',
290 + 'label' => __( 'Lottie', 'bricksable' ),
291 + 'description' => __( 'Enable Lottie Element.', 'bricksable' ),
292 + 'type' => 'checkbox',
293 + 'default' => 'on',
294 + ),
295 + array(
296 + 'id' => 'floating',
297 + 'label' => __( 'Floating', 'bricksable' ),
298 + 'description' => __( 'Enable Floating Element.', 'bricksable' ),
299 + 'type' => 'checkbox',
300 + 'default' => 'on',
301 + ),
302 + array(
303 + 'id' => 'before-after-image',
304 + 'label' => __( 'Before After Image', 'bricksable' ),
305 + 'description' => __( 'Enable Before After Image Element.', 'bricksable' ),
306 + 'type' => 'checkbox',
307 + 'default' => 'on',
308 + ),
309 + array(
310 + 'id' => 'icon-list',
311 + 'label' => __( 'Icon List', 'bricksable' ),
312 + 'description' => __( 'Enable Icon List Element.', 'bricksable' ),
313 + 'type' => 'checkbox',
314 + 'default' => 'on',
315 + ),
316 + array(
317 + 'id' => 'multi-heading',
318 + 'label' => __( 'Multi Heading', 'bricksable' ),
319 + 'description' => __( 'Enable Multi Heading Element.', 'bricksable' ),
320 + 'type' => 'checkbox',
321 + 'default' => 'on',
322 + ),
323 + array(
324 + 'id' => 'content-toggle',
325 + 'label' => __( 'Content Toggle', 'bricksable' ),
326 + 'description' => __( 'Enable Content Toggle Element.', 'bricksable' ),
327 + 'type' => 'checkbox',
328 + 'default' => 'on',
329 + ),
330 + ),
331 + );
332 +
333 + $settings['misc'] = array(
334 + 'title' => __( 'Misc', 'bricksable' ),
335 + 'fields' => array(
336 + array(
337 + 'id' => 'uninstall_on_delete',
338 + 'label' => __( 'Remove Data on Uninstall?', 'bricksable' ),
339 + 'description' => __( 'Check this box if you would like Bricksable to completely remove all of its data when the plugin is deleted.', 'bricksable' ),
340 + 'type' => 'checkbox',
341 + ),
342 + ),
343 + );
344 +
345 + $settings = apply_filters( $this->parent->_token . '_settings_fields', $settings );
346 +
347 + return $settings;
348 + }
349 +
350 + /**
351 + * Register plugin settings
352 + *
353 + * @return void
354 + */
355 + public function register_settings() {
356 + if ( is_array( $this->settings ) ) {
357 +
358 + // Check posted/selected tab.
359 + //phpcs:disable
360 + $current_section = '';
361 + if ( isset( $_POST['tab'] ) && $_POST['tab'] ) {
362 + $current_section = sanitize_text_field( wp_unslash( $_POST['tab'] ) );
363 + } else {
364 + if ( isset( $_GET['tab'] ) && $_GET['tab'] ) {
365 + $current_section = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
366 + }
367 + }
368 + //phpcs:enable
369 +
370 + foreach ( $this->settings as $section => $data ) {
371 +
372 + if ( $current_section && $current_section !== $section ) {
373 + continue;
374 + }
375 +
376 + // Add section to page.
377 + add_settings_section( $section, $data['title'], array( $this, 'settings_section' ), $this->parent->_token . '_settings' );
378 +
379 + foreach ( $data['fields'] as $field ) {
380 +
381 + // Validation callback for field.
382 + $validation = '';
383 + if ( isset( $field['callback'] ) ) {
384 + $validation = $field['callback'];
385 + }
386 +
387 + // Register field.
388 + $option_name = $this->base . $field['id'];
389 + register_setting( $this->parent->_token . '_settings', $option_name, $validation );
390 +
391 + // Add field to page.
392 + add_settings_field(
393 + $field['id'],
394 + isset( $field['label'] ) ? $field['label'] : '',
395 + array( $this->parent->admin, 'display_field' ),
396 + $this->parent->_token . '_settings',
397 + $section,
398 + array(
399 + 'field' => $field,
400 + 'prefix' => $this->base,
401 + )
402 + );
403 + }
404 +
405 + if ( ! $current_section ) {
406 + break;
407 + }
408 + }
409 + }
410 + }
411 +
412 + /**
413 + * Settings section.
414 + *
415 + * @param array $section Array of section ids.
416 + * @return void
417 + */
418 + public function settings_section( $section ) {
419 + $html = '';
420 + if ( isset( $this->settings[ $section['id'] ]['description'] ) ) {
421 + $html = '<p> ' . $this->settings[ $section['id'] ]['description'] . '</p>' . "\n";
422 + }
423 + echo wp_kses( $html, $this->allowed_htmls );
424 + }
425 +
426 + /**
427 + * Load settings page content.
428 + *
429 + * @return void
430 + */
431 + public function settings_page() {
432 +
433 + // Build page HTML.
434 + $html = '<div class="wrap" id="' . $this->parent->_token . '_settings">' . "\n";
435 + $html .= '<h2>' . __( 'Bricksable Settings', 'bricksable' ) . '</h2>' . "\n";
436 +
437 + $tab = '';
438 +
439 + $nonce_name = 'bricksable_settings_nonce';
440 + $nonce = sanitize_text_field( wp_create_nonce( $nonce_name ) );
441 +
442 + if ( isset( $_POST['tab'] ) ) {
443 + if ( wp_verify_nonce( $nonce, $nonce_name ) ) {
444 + $current_section = sanitize_text_field( wp_unslash( $_POST['tab'] ) );
445 + }
446 + } else {
447 + if ( isset( $_GET['tab'] ) && sanitize_text_field( wp_unslash( $_GET['tab'] ) ) ) {
448 + $current_section = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
449 + }
450 + }
451 +
452 + // Show page tabs.
453 + if ( is_array( $this->settings ) && 1 < count( $this->settings ) ) {
454 +
455 + $html .= '<h2 class="nav-tab-wrapper">' . "\n";
456 +
457 + $c = 0;
458 + foreach ( $this->settings as $section => $data ) {
459 +
460 + // Set tab class.
461 + $class = 'nav-tab';
462 + if ( ! isset( $_GET['tab'] ) ) {
463 + if ( 0 === $c ) {
464 + $class .= ' nav-tab-active';
465 + }
466 + } else {
467 + if ( isset( $_GET['tab'] ) && $section === $_GET['tab'] ) {
468 + $tab = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
469 + $class .= ' nav-tab-active';
470 + }
471 + }
472 +
473 + // Set tab link.
474 + $tab_link = add_query_arg(
475 + array(
476 + 'tab' => $section,
477 + $nonce_name => $nonce,
478 + )
479 + );
480 +
481 + if ( isset( $_GET['settings-updated'] ) ) {
482 + $updated = sanitize_text_field( wp_unslash( $_GET['settings-updated'] ) );
483 +
484 + $tab_link = remove_query_arg( 'settings-updated', $tab_link );
485 + }
486 +
487 + // Output tab.
488 + $html .= '<a href="' . $tab_link . '" class="' . esc_attr( $class ) . '">' . esc_html( $data['title'] ) . '</a>' . "\n";
489 +
490 + ++$c;
491 + }
492 +
493 + $html .= '</h2>' . "\n";
494 + }
495 +
496 + $html .= '<form method="post" action="options.php" enctype="multipart/form-data">' . "\n";
497 +
498 + // Get settings fields.
499 + ob_start();
500 + settings_fields( $this->parent->_token . '_settings' );
501 + do_settings_sections( $this->parent->_token . '_settings' );
502 + $html .= ob_get_clean();
503 +
504 + $html .= '<p class="submit">' . "\n";
505 + $html .= '<input type="hidden" name="tab" value="' . esc_attr( $tab ) . '" />' . "\n";
506 + $html .= '<input name="Submit" type="submit" class="button-primary" value="' . esc_attr( __( 'Save Settings', 'bricksable' ) ) . '" />' . "\n";
507 + $html .= '</p>' . "\n";
508 + $html .= '</form>' . "\n";
509 + $html .= '</div>' . "\n";
510 +
511 + echo wp_kses( $html, $this->allowed_htmls );
512 + }
513 +
514 + /**
515 + * Main Bricksable_Settings Instance
516 + *
517 + * Ensures only one instance of Bricksable_Settings is loaded or can be loaded.
518 + *
519 + * @since 1.0.0
520 + * @static
521 + * @see Bricksable()
522 + * @param object $parent Object instance.
523 + * @return object Bricksable_Settings instance
524 + */
525 + public static function instance( $parent ) {
526 + if ( is_null( self::$instance ) ) {
527 + self::$instance = new self( $parent );
528 + }
529 + return self::$instance;
530 + } // End instance()
531 +
532 + /**
533 + * Cloning is forbidden.
534 + *
535 + * @since 1.0.0
536 + */
537 + public function __clone() {
538 + _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cloning of Bricksable_API is forbidden.' ) ), esc_attr( $this->parent->_version ) );
539 + } // End __clone()
540 +
541 + /**
542 + * Unserializing instances of this class is forbidden.
543 + *
544 + * @since 1.0.0
545 + */
546 + public function __wakeup() {
547 + _doing_it_wrong( __FUNCTION__, esc_html( __( 'Unserializing instances of Bricksable_API is forbidden.' ) ), esc_attr( $this->parent->_version ) );
548 + } // End __wakeup()
549 +
550 + /**
551 + * Allowed html for output.
552 + *
553 + * @var array
554 + */
555 + public $allowed_htmls = array(
556 + 'a' => array(
557 + 'href' => array(),
558 + 'title' => array(),
559 + 'class' => array(),
560 + ),
561 + 'h1' => array(
562 + 'href' => array(),
563 + 'title' => array(),
564 + 'class' => array(),
565 + ),
566 + 'h2' => array(
567 + 'href' => array(),
568 + 'title' => array(),
569 + 'class' => array(),
570 + ),
571 + 'h3' => array(
572 + 'href' => array(),
573 + 'title' => array(),
574 + 'class' => array(),
575 + ),
576 + 'h4' => array(
577 + 'href' => array(),
578 + 'title' => array(),
579 + 'class' => array(),
580 + ),
581 + 'input' => array(
582 + 'id' => array(),
583 + 'type' => array(),
584 + 'name' => array(),
585 + 'placeholder' => array(),
586 + 'value' => array(),
587 + 'class' => array(),
588 + 'checked' => array(),
589 + 'style' => array(),
590 + 'data-uploader_title' => array(),
591 + 'data-uploader_text' => array(),
592 + ),
593 + 'select' => array(
594 + 'id' => array(),
595 + 'type' => array(),
596 + 'name' => array(),
597 + 'placeholder' => array(),
598 + 'value' => array(),
599 + 'multiple' => array(),
600 + 'style' => array(),
601 + ),
602 + 'option' => array(
603 + 'id' => array(),
604 + 'type' => array(),
605 + 'name' => array(),
606 + 'placeholder' => array(),
607 + 'value' => array(),
608 + 'multiple' => array(),
609 + 'selected' => array(),
610 + ),
611 + 'label' => array(
612 + 'for' => array(),
613 + 'title' => array(),
614 + ),
615 + 'span' => array(
616 + 'class' => array(),
617 + 'title' => array(),
618 + ),
619 + 'table' => array(
620 + 'scope' => array(),
621 + 'title' => array(),
622 + 'class' => array(),
623 + 'role' => array(),
624 + ),
625 + 'tbody' => array(
626 + 'scope' => array(),
627 + 'title' => array(),
628 + 'class' => array(),
629 + 'role' => array(),
630 + ),
631 + 'th' => array(
632 + 'scope' => array(),
633 + 'title' => array(),
634 + ),
635 + 'form' => array(
636 + 'method' => array(),
637 + 'type' => array(),
638 + 'name' => array(),
639 + 'placeholder' => array(),
640 + 'value' => array(),
641 + 'multiple' => array(),
642 + 'selected' => array(),
643 + 'action' => array(),
644 + 'enctype' => array(),
645 + ),
646 + 'div' => array(
647 + 'class' => array(),
648 + 'id' => array(),
649 + ),
650 + 'img' => array(
651 + 'class' => array(),
652 + 'id' => array(),
653 + 'src' => array(),
654 + ),
655 + 'textarea' => array(
656 + 'class' => array(),
657 + 'id' => array(),
658 + 'rows' => array(),
659 + 'cols' => array(),
660 + 'name' => array(),
661 + 'placeholder' => array(),
662 + 'spellcheck' => array(),
663 + ),
664 + 'tr' => array(),
665 + 'td' => array(),
666 + 'p' => array(),
667 + 'br' => array(),
668 + 'em' => array(),
669 + 'strong' => array(),
670 + 'th' => array(),
671 + );
672 +
673 + /**
674 + * Get WP Roles
675 + *
676 + * @return array Exclude subscriber and other very limited roles
677 + */
678 + public function get_editable_roles() {
679 + $roles = wp_roles()->get_names();
680 + $has_roles = array();
681 + foreach ( $roles as $key => $label ) {
682 + $role = get_role( $key );
683 + if ( ! $role->has_cap( 'edit_posts' ) ) {
684 + continue;
685 + }
686 + $has_roles[ $key ] = $label;
687 + }
688 + return $has_roles;
689 + }
690 +
691 + /**
692 + * Mine Types
693 + *
694 + * @param return $mimes Check WP roles and allow json upload.
695 + */
696 + public function bricksable_file_mime_types( $mimes ) {
697 + $user = wp_get_current_user();
698 + $current_options = get_option( 'bricksable_json_uploads' );
699 + if ( array_intersect( $current_options, $user->roles ) ) {
700 + $mimes['json'] = 'application/json';
701 + }
702 + return $mimes;
703 + }
704 +
705 + /**
706 + * Bricksable Elements
707 + *
708 + * @param return $file_names All registered elements.
709 + */
710 + public function bricksable_load_elements() {
711 + $file_names = array(
712 + 'flip-box',
713 + 'tilt-image',
714 + 'text-notation',
715 + 'lottie',
716 + 'floating',
717 + 'before-after-image',
718 + 'icon-list',
719 + 'multi-heading',
720 + 'content-toggle',
721 + );
722 + return $file_names;
723 + }
724 +
725 + /**
726 + * Bricksable Check Elements
727 + *
728 + * @param return $file_names check registered elements.
729 + */
730 + public function bricksable_check_elements() {
731 + $file_names = array(
732 + 'on' === get_option( 'bricksable_flip-box' ) || false === get_option( 'bricksable_flip-box' ) ? 'flip-box' : '',
733 + 'on' === get_option( 'bricksable_tilt-image' ) || false === get_option( 'bricksable_tilt-image' ) ? 'tilt-image' : '',
734 + 'on' === get_option( 'bricksable_text-notation' ) || false === get_option( 'bricksable_text-notation' ) ? 'text-notation' : '',
735 + 'on' === get_option( 'bricksable_lottie' ) || false === get_option( 'bricksable_lottie' ) ? 'lottie' : '',
736 + 'on' === get_option( 'bricksable_floating' ) || false === get_option( 'bricksable_floating' ) ? 'floating' : '',
737 + 'on' === get_option( 'bricksable_before-after-image' ) || false === get_option( 'bricksable_before-after-image' ) ? 'before-after-image' : '',
738 + 'on' === get_option( 'bricksable_icon-list' ) || false === get_option( 'bricksable_icon-list' ) ? 'icon-list' : '',
739 + 'on' === get_option( 'bricksable_multi-heading' ) || false === get_option( 'bricksable_multi-heading' ) ? 'multi-heading' : '',
740 + 'on' === get_option( 'bricksable_content-toggle' ) || false === get_option( 'bricksable_content-toggle' ) ? 'content-toggle' : '',
741 + );
742 + return $file_names;
743 + }
744 +
745 +}