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