PluginProbe ʕ •ᴥ•ʔ
Custom Sidebars – Dynamic Sidebar Classic Widget Area Manager / 3.38
Custom Sidebars – Dynamic Sidebar Classic Widget Area Manager v3.38
trunk 2.1.2.0 3.0.0.0 3.0.0.1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.7.1 3.0.8 3.0.8.1 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.3 3.31 3.32 3.35 3.36 3.37 3.38
custom-sidebars / inc / class-custom-sidebars.php
custom-sidebars / inc Last commit date
external 1 year ago integrations 7 years ago class-custom-sidebars-checkup-notification.php 1 year ago class-custom-sidebars-cloning.php 1 year ago class-custom-sidebars-editor.php 1 year ago class-custom-sidebars-explain.php 1 year ago class-custom-sidebars-export.php 1 year ago class-custom-sidebars-replacer.php 3 years ago class-custom-sidebars-visibility.php 1 year ago class-custom-sidebars-widgets.php 9 years ago class-custom-sidebars.php 1 year ago
class-custom-sidebars.php
1524 lines
1 <?php
2
3 // Load additional Pro-modules.
4 require_once CSB_INC_DIR . 'class-custom-sidebars-widgets.php';
5 require_once CSB_INC_DIR . 'class-custom-sidebars-editor.php';
6 require_once CSB_INC_DIR . 'class-custom-sidebars-replacer.php';
7 require_once CSB_INC_DIR . 'class-custom-sidebars-cloning.php';
8 require_once CSB_INC_DIR . 'class-custom-sidebars-visibility.php';
9 require_once CSB_INC_DIR . 'class-custom-sidebars-export.php';
10 require_once CSB_INC_DIR . 'class-custom-sidebars-explain.php';
11
12 require_once CSB_INC_DIR . 'class-custom-sidebars-checkup-notification.php';
13
14
15 /**
16 * Main plugin file.
17 * The CustomSidebars class encapsulates all our plugin logic.
18 */
19 class CustomSidebars {
20 /**
21 * Prefix used for the sidebar-ID of custom sidebars. This is also used to
22 * distinguish theme sidebars from custom sidebars.
23 *
24 * @var string
25 */
26 protected static $sidebar_prefix = 'cs-';
27
28 /**
29 * Capability required to use *any* of the plugin features. If user does not
30 * have this capability then he will not see any change on admin dashboard.
31 *
32 * @var string
33 */
34 protected static $cap_required = 'edit_theme_options';
35
36 /**
37 * Flag that specifies if the page is loaded in accessibility mode.
38 * This plugin does not support accessibility mode!
39 *
40 * @var bool
41 * @since 2.0.9
42 */
43 protected static $accessibility_mode = false;
44
45 /**
46 * Flag that specifies if the current user can see the retirement notice.
47 *
48 * @var bool
49 * @since 3.2.4
50 */
51 private $user_can_see_notice = null;
52
53 /**
54 * Returns the singleton instance of the custom sidebars class.
55 *
56 * @since 2.0
57 */
58 public static function instance() {
59 static $Inst = null;
60
61 // We can initialize the plugin once we know the current user:
62 // The lib3()->html->pointer() notification is based on current user...
63 if ( ! did_action( 'set_current_user' ) ) {
64 add_action( 'set_current_user', array( __CLASS__, 'instance' ) );
65 return null;
66 }
67
68 if ( null === $Inst ) {
69 $Inst = new CustomSidebars();
70 }
71
72 return $Inst;
73 }
74
75 /**
76 * Private, since it is a singleton.
77 * We directly initialize sidebar options when class is created.
78 */
79 private function __construct() {
80 add_action( 'init', array( $this, 'integrations' ) );
81 add_action( 'admin_init', array( $this, 'admin_init' ) );
82 // Extensions use this hook to initialize themselves.
83 do_action( 'cs_init' );
84 /**
85 * Add version to media files
86 */
87 add_filter( 'wpmu_style_version', array( $this, 'wp_enqueue_add_version' ), 10, 2 );
88 add_filter( 'wpmu_script_version', array( $this, 'wp_enqueue_add_version' ), 10, 2 );
89
90 /**
91 * Add retirement notice for Block Based Widget Screen
92 */
93 add_action( 'admin_notices', array( $this, 'retirement_admin_notice' ) );
94 add_action( 'wp_ajax_custom_sidebars_retirement_notice_dismiss', array( $this, 'dismiss_retirement_notice' ) );
95 add_action( 'admin_enqueue_scripts', array( $this, 'dismiss_retirement_notice_js' ) );
96 }
97
98 /**
99 * Add retirement admin notice
100 *
101 * @since 3.2.4
102 */
103 public function retirement_admin_notice() {
104 if ( ! $this->can_user_see_notice() ) {
105 return;
106 }
107
108 ?>
109 <div id="wpmudev-cs-retirement-notice" class="notice notice-warning is-dismissible">
110 <p>
111 <?php
112 printf(
113 /* translators: %1$s,%2$s,%3$s is replaced with HTML tags that should not be escaped */
114 esc_html__('%1$sCustom Sidebars Notice %2$sStarting from version 5.7, WordPress will be using Gutenberg\'s block-based Widget Screen. Custom Sidebars is not compatible due to the fact that once Full Site Editing is in place, such plugins will not be required anymore. Therefore Custom Sidebars has been discontinued. If you have existing sidebars that you need to modify, you can switch back to a legacy Widgets screen by adding %3$s in your theme\'s functions.php file.', 'custom-sidebars'),
115 '<strong>',
116 '</strong><br />',
117 "<code>remove_theme_support( 'widgets-block-editor' );</code>",
118 );
119 ?>
120 </p>
121 </div>
122 <?php
123 }
124
125 /**
126 * Dismiss the retirement notice per user
127 *
128 * @since 3.2.4
129 */
130 public function dismiss_retirement_notice() {
131 $user_id = filter_input( INPUT_POST, 'user_id', FILTER_VALIDATE_INT );
132
133 if (
134 ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'wpmudev_sc_dismissed_notice' ) ||
135 $user_id !== get_current_user_id()
136 ) {
137
138 wp_send_json(
139 array(
140 'success' => false,
141 'message' => __( 'Unauthorized', 'custom-sidebars' ),
142 )
143 );
144 }
145
146 update_user_meta( $user_id, 'wpmudev_sc_dismissed_notice', true );
147
148 $return = array(
149 'success' => true,
150 /* translators: %1$s is replaced with the user ID */
151 'message' => sprintf( esc_html__( 'Notice dismissed for user %d', 'custom-sidebars' ), $user_id ),
152 );
153
154 wp_send_json( $return );
155 }
156
157 /**
158 * Enqueue js to dismiss the retirement notice
159 *
160 * @since 3.2.4
161 */
162 public function dismiss_retirement_notice_js() {
163 if ( ! $this->can_user_see_notice() ) {
164 return;
165 }
166
167 $file = 'retirement-admin-notice.js';
168 $csb_info = get_plugin_data( CSB_PLUGIN );
169 $version = $csb_info['Version'];
170 $script_vars = array(
171 'nonce' => wp_create_nonce( "wpmudev_sc_dismissed_notice" ),
172 'user_id' => get_current_user_id(),
173 );
174
175 if ( defined( 'WDEV_UNMINIFIED' ) && WDEV_UNMINIFIED ){
176 $file = str_replace( '.min.js', '.js', $file );
177 $version = time();
178 }
179 wp_enqueue_script(
180 'customsidebars-retirement-notice',
181 CSB_JS_URL . $file,
182 array(),
183 $version,
184 true
185 );
186
187 wp_localize_script( 'customsidebars-retirement-notice', 'CS_Notice', $script_vars );
188 }
189
190 /**
191 * Should the notice be visible or not
192 *
193 * @since 3.2.4
194 */
195 public function can_user_see_notice() {
196 // for now we don't need this because WP 5.7 is not out
197 return false;
198
199 if ( is_null( $this->user_can_see_notice ) ) {
200 $this->user_can_see_notice = true;
201
202 if (
203 ! current_user_can( 'edit_theme_options' ) ||
204 get_user_meta( get_current_user_id(), 'wpmudev_sc_dismissed_notice', true ) )
205 {
206 $this->user_can_see_notice = false;
207 }
208 }
209 return $this->user_can_see_notice;
210 }
211
212 /**
213 * Add version to media files
214 *
215 * @since 3.1.3
216 */
217 public function wp_enqueue_add_version( $version, $handle ) {
218 if ( preg_match( '/^wpmu\-cs\-/', $handle ) ) {
219 return '3.2.4';
220 }
221 return $version;
222 }
223
224 /**
225 * Admin init
226 *
227 * @since 3.0.5
228 */
229 public function admin_init() {
230 $plugin_title = 'Custom Sidebars';/*
231 end:free */
232
233 /**
234 * ID of the WP-Pointer used to introduce the plugin upon activation
235 *
236 * ========== Pointer ==========
237 * Internal ID: wpmudcs1 [WPMUDev CustomSidebars 1]
238 * Point at: #menu-appearance (Appearance menu item)
239 * Title: Custom Sidebars
240 * Description: Create and edit custom sidebars in your widget screen!
241 * -------------------------------------------------------------------------
242 */
243
244 $user_id = get_current_user_id();
245 $dismissed_wp_pointers = get_user_meta( $user_id, 'dismissed_wp_pointers', true );
246 $dismissed_wp_pointers = explode( ',', $dismissed_wp_pointers );
247
248 if ( in_array( 'wpmudcs1', $dismissed_wp_pointers ) || wp_is_mobile() ) {
249 lib3()->ui->add( 'core', 'widgets.php' );
250 } else {
251 lib3()->ui->add( 'core' );
252 lib3()->html->pointer(
253 'wpmudcs1', // Internal Pointer-ID
254 '#menu-appearance', // Point at
255 $plugin_title,
256 /* translators: %1$s is replaced with the link to the Widgets screen */
257 sprintf(__('Now you can create and edit custom sidebars in your <a href="%1$s">Widgets screen</a>!','custom-sidebars'), admin_url( 'widgets.php' ))
258 );
259 }
260
261 // Find out if the page is loaded in accessibility mode.
262 $flag = isset( $_GET['widgets-access'] ) ? sanitize_text_field($_GET['widgets-access']) : get_user_setting( 'widgets_access' );
263 self::$accessibility_mode = ( 'on' == $flag );
264
265 // We don't support accessibility mode. Display a note to the user.
266 if ( true === self::$accessibility_mode ) {
267 $nonce = wp_create_nonce( 'widgets-access' );
268 lib3()->ui->admin_message(
269 /* translators: %1$s is replaced with the URL of the link to disable acessibility mode */
270 sprintf(__('<strong>Accessibility mode is not supported by the Custom Sidebars plugin.</strong><br /><a href="%1$s">Click here</a> to disable accessibility mode and use the Custom Sidebars plugin!', 'custom-sidebars'), $plugin_title, admin_url( 'widgets.php?widgets-access=off&_wpnonce=' . urlencode( $nonce ) )),
271 'err',
272 'widgets'
273 );
274 } else {
275 /**
276 * Main JavaScript file
277 */
278 $javascript_file = 'cs.min.js';
279 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
280 $javascript_file = 'cs.js';
281 }
282 // Load javascripts/css files
283 lib3()->ui->add( 'select', 'widgets.php' );
284 lib3()->ui->add( CSB_JS_URL . $javascript_file, 'widgets.php' );
285 lib3()->ui->add( CSB_CSS_URL . 'cs.css', 'widgets.php' );
286 lib3()->ui->add( CSB_CSS_URL . 'cs.css', 'edit.php' );
287
288 // AJAX actions
289 add_action( 'wp_ajax_cs-ajax', array( $this, 'ajax_handler' ) );
290
291 // Display a message after import.
292 if ( ! empty( $_GET['cs-msg'] ) ) {
293 $msg = base64_decode( sanitize_text_field($_GET['cs-msg']) );
294
295 // Prevent XSS attacks...
296 $kses_args = array(
297 'br' => array(),
298 'b' => array(),
299 'strong' => array(),
300 'i' => array(),
301 'em' => array(),
302 );
303 $msg = wp_kses( $msg, $kses_args );
304
305 if ( ! empty( $msg ) ) {
306 lib3()->ui->admin_message( $msg );
307 }
308 }
309 }
310
311 /**
312 * add links on plugin page.
313 */
314 add_filter( 'plugin_action_links_' . plugin_basename( CSB_PLUGIN ), array( $this, 'add_action_links' ), 10, 4 );
315
316 add_action( 'admin_footer', array( $this, 'print_templates' ) );
317 }
318
319
320 // =========================================================================
321 // == DATA ACCESS
322 // =========================================================================
323
324
325 /**
326 *
327 * ==1== PLUGIN OPTIONS
328 * Option-Key: cs_modifiable
329 *
330 * {
331 * // Sidebars that can be replaced:
332 * 'modifiable': [
333 * 'sidebar_1',
334 * 'sidebar_2'
335 * ],
336 *
337 * // Default replacements:
338 * 'post_type_single': [ // Former "defaults"
339 * 'post_type1': <replacement-def>,
340 * 'post_type2': <replacement-def>
341 * ],
342 * 'post_type_archive': [ // Former "post_type_pages"
343 * 'post_type1': <replacement-def>,
344 * 'post_type2': <replacement-def>
345 * ],
346 * 'category_single': [ // Former "category_posts"
347 * 'category_id1': <replacement-def>,
348 * 'category_id2': <replacement-def>
349 * ],
350 * 'category_archive': [ // Former "category_pages"
351 * 'category_id1': <replacement-def>,
352 * 'category_id2': <replacement-def>
353 * ],
354 * 'blog': <replacement-def>,
355 * 'tags': <replacement-def>,
356 * 'authors': <replacement-def>,
357 * 'search': <replacement-def>,
358 * 'date': <replacement-def>
359 * }
360 *
361 * ==2== REPLACEMENT-DEF
362 * Meta-Key: _cs_replacements
363 * Option-Key: cs_modifiable <replacement-def>
364 *
365 * {
366 * 'sidebar_1': 'custom_sb_id1',
367 * 'sidebar_2': 'custom_sb_id2'
368 * }
369 *
370 * ==3== SIDEBAR DEFINITION
371 * Option-Key: cs_sidebars
372 *
373 * Array of these objects
374 * {
375 * id: '', // sidebar-id
376 * name: '',
377 * description: '',
378 * before_title: '',
379 * after_title: '',
380 * before_widget: '',
381 * after_widget: ''
382 * }
383 *
384 * ==4== WIDGET LIST
385 * Option-Key: sidebars_widgets
386 *
387 * {
388 * 'sidebar_id': [
389 * 'widget_id1',
390 * 'widget_id2'
391 * ],
392 * 'sidebar_2': [
393 * ],
394 * 'sidebar_3': [
395 * 'widget_id1',
396 * 'widget_id3'
397 * ],
398 * }
399 */
400
401
402 /**
403 * If the specified variable is an array it will be returned. Otherwise
404 * an empty array is returned.
405 *
406 * @since 2.0
407 * @param mixed $val1 Value that maybe is an array.
408 * @param mixed $val2 Optional, Second value that maybe is an array.
409 * @return array
410 */
411 public static function get_array( $val1, $val2 = array() ) {
412 if ( is_array( $val1 ) ) {
413 return $val1;
414 } elseif ( is_array( $val2 ) ) {
415 return $val2;
416 } else {
417 return array();
418 }
419 }
420
421 /**
422 * Returns a list with sidebars that were marked as "modifiable".
423 * Also contains information on the default replacements of these sidebars.
424 *
425 * Option-Key: 'cs_modifiable' (1)
426 */
427 public static function get_options( $key = null ) {
428 static $Options = null;
429 $need_update = false;
430
431 if ( null === $Options ) {
432 $Options = get_option( 'cs_modifiable', array() );
433 if ( ! is_array( $Options ) ) {
434 $Options = array();
435 }
436
437 // List of modifiable sidebars.
438 if ( ! isset( $Options['modifiable'] ) || ! is_array( $Options['modifiable'] ) ) {
439 // By default we make ALL theme sidebars replaceable:
440 $all = self::get_sidebars( 'theme' );
441 $Options['modifiable'] = array_keys( $all );
442 $need_update = true;
443 }
444
445 /**
446 * In version 2.0 four config values have been renamed and are
447 * migrated in the following block:
448 */
449
450 /**
451 * set defaults
452 */
453 $keys = array(
454 'authors',
455 'blog',
456 'category_archive',
457 'category_pages',
458 'category_posts',
459 'category_single',
460 'date',
461 'defaults',
462 'post_type_archive',
463 'post_type_pages',
464 'post_type_single',
465 'search',
466 'tags',
467 'screen',
468 );
469
470 foreach ( $keys as $k ) {
471 if ( isset( $Options[ $k ] ) ) {
472 continue;
473 }
474 $Options[ $k ] = null;
475 }
476
477 // Single/Archive pages - new names
478 $Options['post_type_single'] = self::get_array(
479 $Options['post_type_single'], // new name
480 $Options['defaults'] // old name
481 );
482 $Options['post_type_archive'] = self::get_array(
483 $Options['post_type_archive'], // new name
484 $Options['post_type_pages'] // old name
485 );
486 $Options['category_single'] = self::get_array(
487 $Options['category_single'], // new name
488 $Options['category_posts'] // old name
489 );
490 $Options['category_archive'] = self::get_array(
491 $Options['category_archive'], // new name
492 $Options['category_pages'] // old name
493 );
494
495 // Remove old item names from the array.
496 if ( isset( $Options['defaults'] ) ) {
497 unset( $Options['defaults'] );
498 $need_update = true;
499 }
500 if ( isset( $Options['post_type_pages'] ) ) {
501 unset( $Options['post_type_pages'] );
502 $need_update = true;
503 }
504 if ( isset( $Options['category_posts'] ) ) {
505 unset( $Options['category_posts'] );
506 $need_update = true;
507 }
508 if ( isset( $Options['category_pages'] ) ) {
509 unset( $Options['category_pages'] );
510 $need_update = true;
511 }
512
513 // Special archive pages
514 $keys = array( 'blog', 'tags', 'authors', 'search', 'date' );
515 foreach ( $keys as $temporary_key ) {
516 if ( isset( $Options[ $temporary_key ] ) ) {
517 $Options[ $temporary_key ] = self::get_array( $Options[ $temporary_key ] );
518 } else {
519 $Options[ $temporary_key ] = array();
520 }
521 }
522
523 $Options = self::validate_options( $Options );
524 if ( $need_update ) {
525 self::set_options( $Options );
526 }
527 }
528 if ( ! empty( $key ) ) {
529 return isset( $Options[ $key ] ) ? $Options[ $key ] : null;
530 } else {
531 return $Options;
532 }
533 }
534
535 /**
536 * Saves the sidebar options to DB.
537 *
538 * Option-Key: 'cs_modifiable' (1)
539 *
540 * @since 2.0
541 * @param array $value The options array.
542 */
543 public static function set_options( $value ) {
544 // Permission check.
545 if ( ! current_user_can( self::$cap_required ) ) {
546 return;
547 }
548
549 update_option( 'cs_modifiable', $value );
550 }
551
552 /**
553 * Removes invalid settings from the options array.
554 *
555 * @since 1.0.4
556 * @param array $data This array will be validated and returned.
557 * @return array
558 */
559 public static function validate_options( $data = null ) {
560 $data = ( is_object( $data ) ? (array) $data : $data );
561 if ( ! is_array( $data ) ) {
562 return array();
563 }
564 $valid = array_keys( self::get_sidebars( 'theme' ) );
565 $current = array();
566 if ( isset( $data['modifiable'] ) ) {
567 $current = self::get_array( $data['modifiable'] );
568 }
569 // Get all the sidebars that are modifiable AND exist.
570 $modifiable = array_intersect( $valid, $current );
571 $data['modifiable'] = $modifiable;
572 return $data;
573 }
574
575 /**
576 * Returns a list with all custom sidebars that were created by the user.
577 * Array of custom sidebars
578 *
579 * Option-Key: 'cs_sidebars' (3)
580 */
581 public static function get_custom_sidebars() {
582 $sidebars = get_option( 'cs_sidebars', array() );
583 if ( ! is_array( $sidebars ) ) {
584 $sidebars = array();
585 }
586 // Remove invalid items.
587 foreach ( $sidebars as $key => $data ) {
588 if ( ! is_array( $data ) ) {
589 unset( $sidebars[ $key ] );
590 }
591 }
592 return $sidebars;
593 }
594
595 /**
596 * Saves the custom sidebars to DB.
597 *
598 * Option-Key: 'cs_sidebars' (3)
599 *
600 * @since 2.0
601 */
602 public static function set_custom_sidebars( $value ) {
603 // Permission check.
604 if ( ! current_user_can( self::$cap_required ) ) {
605 return;
606 }
607
608 update_option( 'cs_sidebars', $value );
609 }
610
611 /**
612 * Returns a list of all registered sidebars including a list of their
613 * widgets (this is stored inside a WordPress core option).
614 *
615 * Option-Key: 'sidebars_widgets' (4)
616 *
617 * @since 2.0
618 */
619 public static function get_sidebar_widgets() {
620 return get_option( 'sidebars_widgets', array() );
621 }
622
623 /**
624 * Update the WordPress core settings for sidebar widgets:
625 * 1. Add empty widget information for new sidebars.
626 * 2. Remove widget information for sidebars that no longer exist.
627 *
628 * Option-Key: 'sidebars_widgets' (4)
629 */
630 public static function refresh_sidebar_widgets() {
631 // Contains an array of all sidebars and widgets inside each sidebar.
632 $widgetized_sidebars = self::get_sidebar_widgets();
633
634 $cs_sidebars = self::get_custom_sidebars();
635 $delete_widgetized_sidebars = array();
636
637 foreach ( $widgetized_sidebars as $id => $bar ) {
638 if ( substr( $id, 0, 3 ) == self::$sidebar_prefix ) {
639 $found = false;
640 foreach ( $cs_sidebars as $csbar ) {
641 if ( $csbar['id'] == $id ) {
642 $found = true;
643 }
644 }
645 if ( ! $found ) {
646 $delete_widgetized_sidebars[] = $id;
647 }
648 }
649 }
650
651 $all_ids = array_keys( $widgetized_sidebars );
652 foreach ( $cs_sidebars as $cs ) {
653 $sb_id = $cs['id'];
654 if ( ! in_array( $sb_id, $all_ids ) ) {
655 $widgetized_sidebars[ $sb_id ] = array();
656 }
657 }
658
659 foreach ( $delete_widgetized_sidebars as $id ) {
660 unset( $widgetized_sidebars[ $id ] );
661 }
662
663 update_option( 'sidebars_widgets', $widgetized_sidebars );
664 }
665
666 /**
667 * Returns the custom sidebar metadata of a single post.
668 *
669 * Meta-Key: '_cs_replacements' (2)
670 *
671 * @since 2.0
672 */
673 public static function get_post_meta( $post_id ) {
674 $data = get_post_meta( $post_id, '_cs_replacements', true );
675 if ( ! is_array( $data ) ) {
676 $data = array();
677 }
678 return $data;
679 }
680
681 /**
682 * Saves custom sidebar metadata to a single post.
683 *
684 * Meta-Key: '_cs_replacements' (2)
685 *
686 * @since 2.0
687 * @param int $post_id
688 * @param array $data When array is empty the meta data will be deleted.
689 */
690 public static function set_post_meta( $post_id, $data ) {
691 if ( ! empty( $data ) ) {
692 update_post_meta( $post_id, '_cs_replacements', $data );
693 } else {
694 delete_post_meta( $post_id, '_cs_replacements' );
695 }
696 }
697
698 /**
699 * Returns a list of all sidebars available.
700 * Depending on the parameter this will be either all sidebars or only
701 * sidebars defined by the current theme.
702 *
703 * @param string $type [all|cust|theme] What kind of sidebars to return.
704 */
705 public static function get_sidebars( $type = 'theme' ) {
706 global $wp_registered_sidebars;
707 $allsidebars = self::sort_sidebars_by_name( $wp_registered_sidebars );
708 $result = array();
709
710 // Remove inactive sidebars.
711 foreach ( $allsidebars as $sb_id => $sidebar ) {
712 if ( false !== strpos( $sidebar['class'], 'inactive-sidebar' ) ) {
713 unset( $allsidebars[ $sb_id ] );
714 }
715 }
716
717 ksort( $allsidebars );
718 if ( 'all' == $type ) {
719 $result = $allsidebars;
720 } elseif ( 'cust' == $type ) {
721 foreach ( $allsidebars as $key => $sb ) {
722 // Only keep custom sidebars in the results.
723 if ( substr( $key, 0, 3 ) == self::$sidebar_prefix ) {
724 $result[ $key ] = $sb;
725 }
726 }
727 } elseif ( 'theme' == $type ) {
728 foreach ( $allsidebars as $key => $sb ) {
729 // Remove custom sidebars from results.
730 if ( substr( $key, 0, 3 ) != self::$sidebar_prefix ) {
731 $result[ $key ] = $sb;
732 }
733 }
734 }
735
736 return $result;
737 }
738
739 /**
740 * Returns the sidebar with the specified ID.
741 * Sidebar can be both a custom sidebar or theme sidebar.
742 *
743 * @param string $id Sidebar-ID.
744 * @param string $type [all|cust|theme] What kind of sidebars to check.
745 */
746 public static function get_sidebar( $id, $type = 'all' ) {
747 if ( empty( $id ) ) {
748 return false; }
749
750 // Get all sidebars
751 $sidebars = self::get_sidebars( $type );
752
753 if ( isset( $sidebars[ $id ] ) ) {
754 return $sidebars[ $id ];
755 } else {
756 return false;
757 }
758 }
759
760 /**
761 * Get sidebar replacement information for a single post.
762 */
763 public static function get_replacements( $postid ) {
764 $replacements = self::get_post_meta( $postid );
765 if ( ! is_array( $replacements ) ) {
766 $replacements = array();
767 } else {
768 $replacements = $replacements;
769 }
770 return $replacements;
771 }
772
773 /**
774 * Returns true, when the specified post type supports custom sidebars.
775 *
776 * @since 2.0
777 * @param object|string $posttype The posttype to validate. Either the
778 * posttype name or the full posttype object.
779 * @return bool
780 */
781 public static function supported_post_type( $posttype ) {
782 $Ignored_types = null;
783 $Response = array();
784
785 if ( null === $Ignored_types ) {
786 $Ignored_types = get_post_types(
787 array( 'public' => false ),
788 'names'
789 );
790 $Ignored_types['attachment'] = 'attachment';
791 }
792
793 if ( is_object( $posttype ) ) {
794 $posttype = $posttype->name;
795 }
796
797 if ( ! isset( $Response[ $posttype ] ) ) {
798 $response = ! in_array( $posttype, $Ignored_types );
799
800 /**
801 * Filters the support-flag. The flag defines if the posttype supports
802 * custom sidebars or not.
803 *
804 * @since 2.0
805 *
806 * @param bool $response Flag if the posttype is supported.
807 * @param string $posttype Name of the posttype that is checked.
808 */
809 $response = apply_filters( 'cs_support_posttype', $response, $posttype );
810 $Response[ $posttype ] = $response;
811 }
812 return $Response[ $posttype ];
813 }
814
815 /**
816 * Returns a list of all post types that support custom sidebars.
817 *
818 * @uses self::supported_post_type()
819 * @param string $type [names|objects] Defines details of return data.
820 * @return array List of posttype names or objects, depending on the param.
821 */
822 public static function get_post_types( $type = 'names' ) {
823 $Valid = array();
824 if ( 'objects' != $type ) {
825 $type = 'names';
826 }
827 if ( ! isset( $Valid[ $type ] ) ) {
828 $all = get_post_types( array(), $type );
829 $Valid[ $type ] = array();
830 foreach ( $all as $post_type ) {
831 $suports = self::supported_post_type( $post_type );
832 if ( $suports ) {
833 $Valid[ $type ][] = $post_type;
834 }
835 }
836 }
837 return $Valid[ $type ];
838 }
839
840 /**
841 * Returns a list of all taxonomies that support custom sidebars.
842 *
843 * @since 3.0.7
844 *
845 * @uses self::supported_post_type()
846 * @param string $type [names|objects] Defines details of return data.
847 * @return array List of posttype names or objects, depending on the param.
848 */
849 public static function get_taxonomies( $type = 'names', $_builtin = true ) {
850 $Valid = array();
851 if ( 'objects' != $type ) {
852 $type = 'names';
853 }
854 if ( ! isset( $Valid[ $type ] ) ) {
855 $all = get_taxonomies(
856 array(
857 'public' => true,
858 '_builtin' => $_builtin,
859 ),
860 $type
861 );
862 $Valid[ $type ] = array();
863 foreach ( $all as $one ) {
864 $Valid[ $type ][] = $one;
865 }
866 }
867 return $Valid[ $type ];
868 }
869
870 /**
871 * Returns an array of all categories.
872 *
873 * @since 2.0
874 * @return array List of categories, including empty ones.
875 */
876 public static function get_all_categories() {
877 $args = array(
878 'hide_empty' => 0,
879 'taxonomy' => 'category',
880 );
881
882 return get_categories( $args );
883 }
884
885 /**
886 * Returns a sorted list of all category terms of the current post.
887 * This information is used to find sidebar replacements.
888 *
889 * @uses self::cmp_cat_level()
890 */
891 public static function get_sorted_categories( $post_id = null ) {
892 static $Sorted = array();
893
894 // Return categories of current post when no post_id is specified.
895 $post_id = empty( $post_id ) ? get_the_ID() : $post_id;
896
897 if ( ! isset( $Sorted[ $post_id ] ) ) {
898 $Sorted[ $post_id ] = get_the_category( $post_id );
899 usort( $Sorted[ $post_id ], array( __CLASS__, 'cmp_cat_level' ) );
900 }
901 return $Sorted[ $post_id ];
902 }
903
904 /**
905 * Helper function used to sort categories.
906 *
907 * @uses self::get_category_level()
908 */
909 public static function cmp_cat_level( $cat1, $cat2 ) {
910 $l1 = self::get_category_level( $cat1->cat_ID );
911 $l2 = self::get_category_level( $cat2->cat_ID );
912 if ( $l1 == $l2 ) {
913 return strcasecmp( $cat1->name, $cat1->name );
914 } else {
915 return $l1 < $l2 ? 1 : -1;
916 }
917 }
918
919 /**
920 * Helper function used to sort categories.
921 */
922 public static function get_category_level( $catid ) {
923 if ( ! $catid ) {
924 return 0;
925 }
926
927 $cat = get_category( $catid );
928 return 1 + self::get_category_level( $cat->category_parent );
929 }
930
931 // =========================================================================
932 // == AJAX FUNCTIONS
933 // =========================================================================
934
935 /**
936 * Output JSON data and die()
937 *
938 * @since 1.0.0
939 */
940 protected static function json_response( $obj ) {
941 // Flush any output that was made prior to this function call
942 while ( 0 < ob_get_level() ) {
943 ob_end_clean(); }
944
945 header( 'Content-Type: application/json' );
946 echo json_encode( (object) $obj );
947 die();
948 }
949
950 /**
951 * Output HTML data and die()
952 *
953 * @since 2.0
954 */
955 protected static function plain_response( $data ) {
956 // Flush any output that was made prior to this function call
957 while ( 0 < ob_get_level() ) {
958 ob_end_clean(); }
959
960 header( 'Content-Type: text/plain' );
961 CustomSidebars::wp_kses_wf('' . $data);
962 die();
963 }
964
965 /**
966 * Sets the response object to ERR state with the specified message/reason.
967 *
968 * @since 2.0
969 * @param object $req Initial response object.
970 * @param string $message Error message or reason; already translated.
971 * @return object Updated response object.
972 */
973 protected static function req_err( $req, $message ) {
974 $req->status = 'ERR';
975 $req->message = $message;
976 return $req;
977 }
978
979 /**
980 * All Ajax request are handled by this function.
981 * It analyzes the post-data and calls the required functions to execute
982 * the requested action.
983 *
984 * --------------------------------
985 *
986 * IMPORTANT! ANY SERVER RESPONSE MUST BE MADE VIA ONE OF THESE FUNCTIONS!
987 * Using direct `echo` or include an html file will not work.
988 *
989 * self::json_response( $obj )
990 * self::plain_response( $text )
991 *
992 * --------------------------------
993 *
994 * @since 1.0.0
995 */
996 public function ajax_handler() {
997 // Permission check.
998 if ( ! current_user_can( self::$cap_required ) ) {
999 return;
1000 }
1001
1002 // Try to disable debug output for ajax handlers of this plugin.
1003 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
1004 defined( 'WP_DEBUG_DISPLAY' ) || define( 'WP_DEBUG_DISPLAY', false );
1005 defined( 'WP_DEBUG_LOG' ) || define( 'WP_DEBUG_LOG', true );
1006 }
1007 // Catch any unexpected output via output buffering.
1008 ob_start();
1009
1010 $action = isset( $_POST['do'] ) ? sanitize_key($_POST['do']) : null;
1011 $get_action = isset( $_GET['do'] ) ? sanitize_key($_GET['do']) : null;
1012
1013 /**
1014 * Notify all extensions about the ajax call.
1015 *
1016 * @since 2.0
1017 * @param string $action The specified ajax action.
1018 */
1019 do_action( 'cs_ajax_request', $action );
1020
1021 /**
1022 * Notify all extensions about the GET ajax call.
1023 *
1024 * @since 2.0.9.7
1025 * @param string $action The specified ajax action.
1026 */
1027 do_action( 'cs_ajax_request_get', $get_action );
1028 }
1029
1030 /**
1031 * This function will sort an array by key 'name'.
1032 *
1033 * @since 2.1.1.2
1034 *
1035 * @param $a Mixed - first value to compare.
1036 * @param $b Mixed - secound value to compare.
1037 * @return integer value of comparation.
1038 */
1039 public static function sort_sidebars_cmp_function( $a, $b ) {
1040 if ( ! isset( $a['name'] ) || ! isset( $b['name'] ) ) {
1041 return 0;
1042 }
1043 if ( function_exists( 'mb_strtolower' ) ) {
1044 $a_name = mb_strtolower( $a['name'] );
1045 $b_name = mb_strtolower( $b['name'] );
1046 } else {
1047 $a_name = strtolower( $a['name'] );
1048 $b_name = strtolower( $b['name'] );
1049 }
1050 if ( $a_name == $b_name ) {
1051 return 0;
1052 }
1053 return ( $a_name < $b_name ) ? -1 : 1;
1054 }
1055
1056 /**
1057 * Returns sidebars sorted by name.
1058 *
1059 * @since 2.1.1.2
1060 *
1061 * @param array $available Array of sidebars.
1062 * @return array Sorted array of sidebars.
1063 */
1064 public static function sort_sidebars_by_name( $available ) {
1065 if ( empty( $available ) ) {
1066 return $available;
1067 }
1068 foreach ( $available as $key => $data ) {
1069 $available[ $key ]['cs-key'] = $key;
1070 }
1071 usort( $available, array( __CLASS__, 'sort_sidebars_cmp_function' ) );
1072 $sorted = array();
1073 foreach ( $available as $data ) {
1074 $sorted[ $data['cs-key'] ] = $data;
1075 }
1076 return $sorted;
1077 }
1078
1079 /**
1080 * Add "support" and (configure) "widgets" on plugin list page
1081 *
1082 * @since 2.1.1.8
1083 */
1084 public function add_action_links( $actions, $plugin_file, $plugin_data, $context ) {
1085 if ( current_user_can( 'edit_theme_options' ) ) {
1086 $widgets = sprintf(
1087 '<a href="%s">%s</a>',
1088 esc_url( admin_url( 'widgets.php' ) ),
1089 __( 'Configure Custom Sidebars', 'custom-sidebars' )
1090 );
1091 array_unshift($actions, $widgets);
1092 }
1093
1094 return $actions;
1095 }
1096
1097 /**
1098 * Print JavaScript template.
1099 *
1100 * @since 3.0.1
1101 */
1102 public function print_templates() {
1103 if ( false == $this->check_screen() ) {
1104 return;
1105 }
1106 wp_enqueue_script( 'wp-util' );
1107 ?>
1108 <script type="text/html" id="tmpl-custom-sidebars-new">
1109 <div class="custom-sidebars-add-new">
1110 <p><?php esc_html_e( 'Create a custom sidebar to get started.', 'custom-sidebars' ); ?></p>
1111 </div>
1112 </script>
1113 <script type="text/html" id="tmpl-custom-sidebars-new-rule-row">
1114 <tr>
1115 <td>
1116 <select name="cs-screen[minmax][]">
1117 <option value="max"<# if( 'max' == data.minmax ) { #> selected="selected"<# } #>><?php esc_html_e( 'max', 'custom-sidebars' ); ?></option>
1118 <option value="min"<# if( 'min' == data.minmax ) { #> ' selected="selected"<# } #>><?php esc_html_e( 'min', 'custom-sidebars' ); ?></option>
1119 </select>
1120 </td>
1121 <td>
1122 <select name="cs-screen[mode][]">
1123 <option value="hide"<# if( 'hide' == data.mode ) { #> selected="selected"<# } #>><?php esc_html_e( 'Hide', 'custom-sidebars' ); ?></option>
1124 <option value="show"<# if( 'show' == data.mode ) { #> selected="selected"<# } #>><?php esc_html_e( 'Show', 'custom-sidebars' ); ?></option>
1125 </select>
1126 </td>
1127 <td><input type="number" name="cs-screen[size][]" min="0" value="{{{data.size}}}" class="textright" /></td>
1128 <td class="num"><span class="dashicons dashicons-trash"></span></td>
1129 </tr>
1130 </script>
1131 <?php
1132 }
1133
1134 /**
1135 * Inicjalize integrations.
1136 *
1137 * @since 3.1.2
1138 */
1139 public function integrations() {
1140 /**
1141 * 3rd party plugins integration: WPML
1142 */
1143 if ( function_exists( 'icl_object_id' ) && ! defined( 'POLYLANG_VERSION' ) ) {
1144 require_once CSB_INC_DIR . 'integrations/class-custom-sidebars-integration-wpml.php';
1145 }
1146 /**
1147 * 3rd party plugins integration: Polylang
1148 */
1149 if ( defined( 'POLYLANG_VERSION' ) && POLYLANG_VERSION ) {
1150 require_once CSB_INC_DIR . 'integrations/class-custom-sidebars-integration-polylang.php';
1151 }
1152 /**
1153 * 3rd party plugins integration: WP Multilang
1154 */
1155 if ( defined( 'WPM_PLUGIN_FILE' ) && WPM_PLUGIN_FILE && file_exists( WPM_PLUGIN_FILE ) ) {
1156 require_once CSB_INC_DIR . 'integrations/class-custom-sidebars-integration-wml.php';
1157 }
1158 do_action( 'cs_integrations' );
1159 }
1160
1161 private function check_screen() {
1162 if ( ! function_exists( 'get_current_screen' ) ) {
1163 return false;
1164 }
1165 $screen = get_current_screen();
1166 if ( ! is_a( $screen, 'WP_Screen' ) ) {
1167 return false;
1168 }
1169 if ( 'widgets' != $screen->id ) {
1170 return false;
1171 }
1172 return true;
1173 }
1174
1175 /**
1176 * get custom taxonomies
1177 *
1178 * @since 3.1.4
1179 *
1180 * @returns array Array of object of custom, public taxonomies
1181 */
1182 public static function get_custom_taxonomies( $state = 'all' ) {
1183 $args = array(
1184 'public' => true,
1185 '_builtin' => false,
1186 );
1187 $taxonomies = get_taxonomies( $args, 'objects' );
1188 if ( empty( $taxonomies ) ) {
1189 return array();
1190 }
1191 /**
1192 * if we need only allowed taxonomies, then remove not needed from
1193 * $taxonomies array
1194 */
1195 if ( 'allowed' === $state ) {
1196 $editor = CustomSidebarsEditor::instance();
1197 $allowed = $editor->get_allowed_custom_taxonmies();
1198 if ( empty( $allowed ) ) {
1199 return array();
1200 }
1201 foreach ( $taxonomies as $slug => $taxonomy ) {
1202 if ( in_array( $slug, $allowed ) ) {
1203 continue;
1204 }
1205 unset( $taxonomies[ $slug ] );
1206 }
1207 }
1208
1209 uasort( $taxonomies, array( __CLASS__, 'sort_by_label' ) );
1210 return $taxonomies;
1211 }
1212
1213 /**
1214 * Sort helper for get_custom_taxonomies() function.
1215 *
1216 * @since 3.1.4
1217 */
1218 private static function sort_by_label( $a, $b ) {
1219 return strcmp( $a->label, $b->label );
1220 }
1221
1222 public static function wp_kses_wf($html)
1223 {
1224 add_filter('safe_style_css', function ($styles) {
1225 $styles_wf = array(
1226 'text-align',
1227 'margin',
1228 'color',
1229 'float',
1230 'border',
1231 'background',
1232 'background-color',
1233 'border-bottom',
1234 'border-bottom-color',
1235 'border-bottom-style',
1236 'border-bottom-width',
1237 'border-collapse',
1238 'border-color',
1239 'border-left',
1240 'border-left-color',
1241 'border-left-style',
1242 'border-left-width',
1243 'border-right',
1244 'border-right-color',
1245 'border-right-style',
1246 'border-right-width',
1247 'border-spacing',
1248 'border-style',
1249 'border-top',
1250 'border-top-color',
1251 'border-top-style',
1252 'border-top-width',
1253 'border-width',
1254 'caption-side',
1255 'clear',
1256 'cursor',
1257 'direction',
1258 'font',
1259 'font-family',
1260 'font-size',
1261 'font-style',
1262 'font-variant',
1263 'font-weight',
1264 'height',
1265 'letter-spacing',
1266 'line-height',
1267 'margin-bottom',
1268 'margin-left',
1269 'margin-right',
1270 'margin-top',
1271 'overflow',
1272 'padding',
1273 'padding-bottom',
1274 'padding-left',
1275 'padding-right',
1276 'padding-top',
1277 'text-decoration',
1278 'text-indent',
1279 'vertical-align',
1280 'width',
1281 'display',
1282 );
1283
1284 foreach ($styles_wf as $style_wf) {
1285 $styles[] = $style_wf;
1286 }
1287 return $styles;
1288 });
1289
1290 $allowed_tags = wp_kses_allowed_html('post');
1291 $allowed_tags['input'] = array(
1292 'type' => true,
1293 'style' => true,
1294 'class' => true,
1295 'id' => true,
1296 'checked' => true,
1297 'disabled' => true,
1298 'name' => true,
1299 'size' => true,
1300 'placeholder' => true,
1301 'value' => true,
1302 'data-*' => true,
1303 'size' => true,
1304 'disabled' => true
1305 );
1306
1307 $allowed_tags['textarea'] = array(
1308 'type' => true,
1309 'style' => true,
1310 'class' => true,
1311 'id' => true,
1312 'checked' => true,
1313 'disabled' => true,
1314 'name' => true,
1315 'size' => true,
1316 'placeholder' => true,
1317 'value' => true,
1318 'data-*' => true,
1319 'cols' => true,
1320 'rows' => true,
1321 'disabled' => true,
1322 'autocomplete' => true
1323 );
1324
1325 $allowed_tags['select'] = array(
1326 'type' => true,
1327 'style' => true,
1328 'class' => true,
1329 'id' => true,
1330 'checked' => true,
1331 'disabled' => true,
1332 'name' => true,
1333 'size' => true,
1334 'placeholder' => true,
1335 'value' => true,
1336 'data-*' => true,
1337 'multiple' => true,
1338 'disabled' => true
1339 );
1340
1341 $allowed_tags['option'] = array(
1342 'type' => true,
1343 'style' => true,
1344 'class' => true,
1345 'id' => true,
1346 'checked' => true,
1347 'disabled' => true,
1348 'name' => true,
1349 'size' => true,
1350 'placeholder' => true,
1351 'value' => true,
1352 'selected' => true,
1353 'data-*' => true
1354 );
1355 $allowed_tags['optgroup'] = array(
1356 'type' => true,
1357 'style' => true,
1358 'class' => true,
1359 'id' => true,
1360 'checked' => true,
1361 'disabled' => true,
1362 'name' => true,
1363 'size' => true,
1364 'placeholder' => true,
1365 'value' => true,
1366 'selected' => true,
1367 'data-*' => true,
1368 'label' => true
1369 );
1370
1371 $allowed_tags['a'] = array(
1372 'href' => true,
1373 'data-*' => true,
1374 'class' => true,
1375 'style' => true,
1376 'id' => true,
1377 'target' => true,
1378 'data-*' => true,
1379 'role' => true,
1380 'aria-controls' => true,
1381 'aria-selected' => true,
1382 'disabled' => true
1383 );
1384
1385 $allowed_tags['div'] = array(
1386 'style' => true,
1387 'class' => true,
1388 'id' => true,
1389 'data-*' => true,
1390 'role' => true,
1391 'aria-labelledby' => true,
1392 'value' => true,
1393 'aria-modal' => true,
1394 'tabindex' => true
1395 );
1396
1397 $allowed_tags['li'] = array(
1398 'style' => true,
1399 'class' => true,
1400 'id' => true,
1401 'data-*' => true,
1402 'role' => true,
1403 'aria-labelledby' => true,
1404 'value' => true,
1405 'aria-modal' => true,
1406 'tabindex' => true
1407 );
1408
1409 $allowed_tags['span'] = array(
1410 'style' => true,
1411 'class' => true,
1412 'id' => true,
1413 'data-*' => true,
1414 'aria-hidden' => true
1415 );
1416
1417 $allowed_tags['style'] = array(
1418 'class' => true,
1419 'id' => true,
1420 'type' => true
1421 );
1422
1423 $allowed_tags['fieldset'] = array(
1424 'class' => true,
1425 'id' => true,
1426 'type' => true
1427 );
1428
1429 $allowed_tags['link'] = array(
1430 'class' => true,
1431 'id' => true,
1432 'type' => true,
1433 'rel' => true,
1434 'href' => true,
1435 'media' => true
1436 );
1437
1438 $allowed_tags['form'] = array(
1439 'style' => true,
1440 'class' => true,
1441 'id' => true,
1442 'method' => true,
1443 'action' => true,
1444 'data-*' => true
1445 );
1446
1447 $allowed_tags['script'] = array(
1448 'class' => true,
1449 'id' => true,
1450 'type' => true,
1451 'src' => true
1452 );
1453
1454 echo wp_kses($html, $allowed_tags);
1455
1456 add_filter('safe_style_css', function ($styles) {
1457 $styles_wf = array(
1458 'text-align',
1459 'margin',
1460 'color',
1461 'float',
1462 'border',
1463 'background',
1464 'background-color',
1465 'border-bottom',
1466 'border-bottom-color',
1467 'border-bottom-style',
1468 'border-bottom-width',
1469 'border-collapse',
1470 'border-color',
1471 'border-left',
1472 'border-left-color',
1473 'border-left-style',
1474 'border-left-width',
1475 'border-right',
1476 'border-right-color',
1477 'border-right-style',
1478 'border-right-width',
1479 'border-spacing',
1480 'border-style',
1481 'border-top',
1482 'border-top-color',
1483 'border-top-style',
1484 'border-top-width',
1485 'border-width',
1486 'caption-side',
1487 'clear',
1488 'cursor',
1489 'direction',
1490 'font',
1491 'font-family',
1492 'font-size',
1493 'font-style',
1494 'font-variant',
1495 'font-weight',
1496 'height',
1497 'letter-spacing',
1498 'line-height',
1499 'margin-bottom',
1500 'margin-left',
1501 'margin-right',
1502 'margin-top',
1503 'overflow',
1504 'padding',
1505 'padding-bottom',
1506 'padding-left',
1507 'padding-right',
1508 'padding-top',
1509 'text-decoration',
1510 'text-indent',
1511 'vertical-align',
1512 'width'
1513 );
1514
1515 foreach ($styles_wf as $style_wf) {
1516 if (($key = array_search($style_wf, $styles)) !== false) {
1517 unset($styles[$key]);
1518 }
1519 }
1520 return $styles;
1521 });
1522 }
1523 };
1524