singleton. * * @since 2.0 */ private function __construct() { if ( is_admin() ) { // Add the sidebar metabox to posts. add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) ); // Save the options from the sidebars-metabox. add_action( 'save_post', array( $this, 'store_replacements' ) ); // Handle ajax requests. add_action( 'cs_ajax_request', array( $this, 'handle_ajax' ) ); } } /** * Handles the ajax requests. */ public function handle_ajax( $action ) { $req = (object) array( 'status' => 'ERR', ); $is_json = true; $handle_it = false; $view_file = ''; $sb_id = ''; if ( isset( $_POST['sb'] ) ) { $sb_id = $_POST['sb']; } switch ( $action ) { case 'get': case 'save': case 'delete': case 'get-location': case 'set-location': case 'replaceable': $handle_it = true; $req->status = 'OK'; $req->action = $action; $req->id = $sb_id; break; } // The ajax request was not meant for us... if ( ! $handle_it ) { return false; } $sb_data = self::get_sidebar( $sb_id ); if ( ! current_user_can( self::$cap_required ) ) { $req = self::req_err( $req, __( 'You do not have permission for this', 'custom-sidebars' ) ); } else { switch ( $action ) { // Return details for the specified sidebar. case 'get': $req->sidebar = $sb_data; break; // Save or insert the specified sidebar. case 'save': $req = $this->save_item( $req, $_POST ); break; // Delete the specified sidebar. case 'delete': $req->sidebar = $sb_data; $req = $this->delete_item( $req ); break; // Get the location data. case 'get-location': $req->sidebar = $sb_data; $req = $this->get_location_data( $req ); break; // Update the location data. case 'set-location': $req->sidebar = $sb_data; $req = $this->set_location_data( $req ); break; // Toggle theme sidebar replaceable-flag. case 'replaceable': $req = $this->set_replaceable( $req ); break; } } // Make the ajax response either as JSON or plain text. if ( $is_json ) { self::json_response( $req ); } else { ob_start(); include CSB_VIEWS_DIR . $view_file; $resp = ob_get_clean(); self::plain_response( $resp ); } } /** * Saves the item specified by $data array and populates the response * object. When $req->id is empty a new sidebar will be created. Otherwise * the existing sidebar is updated. * * @since 2.0 * @param object $req Initial response object. * @param array $data Sidebar data to save (typically this is $_POST). * @return object Updated response object. */ private function save_item( $req, $data ) { $sidebars = self::get_custom_sidebars(); $sb_id = $req->id; $sb_desc = stripslashes( trim( @$_POST['description'] ) ); if ( function_exists( 'mb_substr' ) ) { $sb_name = mb_substr( stripslashes( trim( @$data['name'] ) ), 0, 40 ); } else { $sb_name = substr( stripslashes( trim( @$data['name'] ) ), 0, 40 ); } if ( empty( $sb_name ) ) { return self::req_err( $req, __( 'Sidebar-name cannot be empty', 'custom-sidebars' ) ); } if ( empty( $sb_id ) ) { // Create a new sidebar. $action = 'insert'; $num = count( $sidebars ); do { $num += 1; $sb_id = self::$sidebar_prefix . $num; } while ( self::get_sidebar( $sb_id, 'cust' ) ); $sidebar = array( 'id' => $sb_id, ); } else { // Update existing sidebar $action = 'update'; $sidebar = self::get_sidebar( $sb_id, 'cust' ); if ( ! $sidebar ) { return self::req_err( $req, __( 'The sidebar does not exist', 'custom-sidebars' ) ); } } if ( function_exists( 'mb_strlen' ) ) { if ( mb_strlen( $sb_desc ) > 200 ) { $sb_desc = mb_substr( $sb_desc, 0, 200 ); } } else { if ( strlen( $sb_desc ) > 200 ) { $sb_desc = substr( $sb_desc, 0, 200 ); } } // Populate the sidebar object. if ( ! CSB_IS_PRO ) { $sidebar['name'] = $sb_name; $sidebar['description'] = $sb_desc; } else { $sidebar['name_lang'] = $sb_name; $sidebar['description_lang'] = $sb_desc; } $sidebar['before_widget'] = stripslashes( trim( @$_POST['before_widget'] ) ); $sidebar['after_widget'] = stripslashes( trim( @$_POST['after_widget'] ) ); $sidebar['before_title'] = stripslashes( trim( @$_POST['before_title'] ) ); $sidebar['after_title'] = stripslashes( trim( @$_POST['after_title'] ) ); if ( 'insert' == $action ) { $sidebars[] = $sidebar; $req->message = sprintf( __( 'Created new sidebar %1$s', 'custom-sidebars' ), esc_html( $sidebar['name'] ) ); } else { $found = false; foreach ( $sidebars as $ind => $item ) { if ( $item['id'] == $sb_id ) { $req->message = sprintf( __( 'Updated sidebar %1$s', 'custom-sidebars' ), esc_html( $sidebar['name'] ) ); $sidebars[ $ind ] = $sidebar; $found = true; break; } } if ( ! $found ) { return self::req_err( $req, __( 'The sidebar was not found', 'custom-sidebars' ) ); } } // Save the changes. self::set_custom_sidebars( $sidebars ); self::refresh_sidebar_widgets(); $req->data = $sidebar; $req->action = $action; return $req; } /** * Delete the specified sidebar and update the response object. * * @since 2.0 * @param object $req Initial response object. * @return object Updated response object. */ private function delete_item( $req ) { $sidebars = self::get_custom_sidebars(); $sidebar = self::get_sidebar( $req->id, 'cust' ); if ( ! $sidebar ) { return self::req_err( $req, __( 'The sidebar does not exist', 'custom-sidebars' ) ); } $found = false; foreach ( $sidebars as $ind => $item ) { if ( $item['id'] == $req->id ) { $found = true; $req->message = sprintf( __( 'Deleted sidebar %1$s', 'custom-sidebars' ), esc_html( $req->sidebar['name'] ) ); unset( $sidebars[ $ind ] ); break; } } if ( ! $found ) { return self::req_err( $req, __( 'The sidebar was not found', 'custom-sidebars' ) ); } // Save the changes. self::set_custom_sidebars( $sidebars ); self::refresh_sidebar_widgets(); return $req; } /** * Save the repaceable flag of a theme sidebar. * * @since 2.0 * @param object $req Initial response object. * @return object Updated response object. */ private function set_replaceable( $req ) { $state = @$_POST['state']; $options = self::get_options(); if ( 'true' === $state ) { $req->status = true; if ( ! in_array( $req->id, $options['modifiable'] ) ) { $options['modifiable'][] = $req->id; } } else { $req->status = false; foreach ( $options['modifiable'] as $i => $sb_id ) { if ( $sb_id == $req->id ) { unset( $options['modifiable'][ $i ] ); break; } } } $options['modifiable'] = array_values( $options['modifiable'] ); self::set_options( $options ); $req->replaceable = (object) $options['modifiable']; return $req; } /** * Populates the response object for the "get-location" ajax call. * Location data defines where a custom sidebar is displayed, i.e. on which * pages it is used and which theme-sidebars are replaced. * * @since 2.0 * @param object $req Initial response object. * @return object Updated response object. */ private function get_location_data( $req ) { $defaults = self::get_options(); $raw_posttype = self::get_post_types( 'objects' ); $raw_cat = self::get_all_categories(); $archive_type = array( '_blog' => __( 'Front Page', 'custom-sidebars' ), '_search' => __( 'Search Results', 'custom-sidebars' ), '_404' => __( 'Not found (404)', 'custom-sidebars' ), '_authors' => __( 'Any Author Archive', 'custom-sidebars' ), '_tags' => __( 'Tag Archives', 'custom-sidebars' ), '_date' => __( 'Date Archives', 'custom-sidebars' ), ); $raw_authors = array(); // Collect required data for all posttypes. $posttypes = array(); foreach ( $raw_posttype as $item ) { $sel_single = @$defaults['post_type_single'][ $item->name ]; $posttypes[ $item->name ] = array( 'name' => $item->labels->name, 'single' => self::get_array( $sel_single ), ); } // Extract the data from categories list that we need. $categories = array(); foreach ( $raw_cat as $item ) { $sel_single = @$defaults['category_single'][ $item->term_id ]; $sel_archive = @$defaults['category_archive'][ $item->term_id ]; $categories[ $item->term_id ] = array( 'name' => $item->name, 'count' => $item->count, 'single' => self::get_array( $sel_single ), 'archive' => self::get_array( $sel_archive ), ); } // Build a list of archive types. $archives = array(); // Start with a copy of the posttype list. foreach ( $raw_posttype as $item ) { if ( $item->name == 'post' ) { $label = __( 'Post Index', 'custom-sidebars' ); } else { if ( ! $item->has_archive ) { continue; } $label = sprintf( __( '%1$s Archives', 'custom-sidebars' ), $item->labels->singular_name ); } $sel_archive = @$defaults['post_type_archive'][ $item->name ]; $archives[ $item->name ] = array( 'name' => $label, 'archive' => self::get_array( $sel_archive ), ); } foreach ( $archive_type as $key => $name ) { $sel_archive = @$defaults[ substr( $key, 1 ) ]; $archives[ $key ] = array( 'name' => $name, 'archive' => self::get_array( $sel_archive ), ); } $req->replaceable = $defaults['modifiable']; $req->posttypes = $posttypes; $req->categories = $categories; $req->archives = $archives; return $req; } /** * Save location data for a single sidebar and populate the response object. * Location data defines where a custom sidebar is displayed, i.e. on which * pages it is used and which theme-sidebars are replaced. * * @since 2.0 * @param object $req Initial response object. * @return object Updated response object. */ private function set_location_data( $req ) { $options = self::get_options(); $sidebars = $options['modifiable']; $raw_posttype = self::get_post_types( 'objects' ); $raw_cat = self::get_all_categories(); $data = array(); foreach ( $_POST as $key => $value ) { if ( strlen( $key ) > 8 && '___cs___' == substr( $key, 0, 8 ) ) { list( $prefix, $id ) = explode( '___', substr( $key, 8 ) ); if ( ! isset( $data[ $prefix ] ) ) { $data[ $prefix ] = array(); } $data[ $prefix ][ $id ] = $value; } } $special_arc = array( 'blog', '404', 'tags', 'authors', 'search', 'date', ); $raw_authors = array(); // == Update the options foreach ( $sidebars as $sb_id ) { // Post-type settings. foreach ( $raw_posttype as $item ) { $pt = $item->name; if ( is_array( @$data['pt'][ $sb_id ] ) && in_array( $pt, $data['pt'][ $sb_id ] ) ) { $options['post_type_single'][ $pt ][ $sb_id ] = $req->id; } elseif ( isset( $options['post_type_single'][ $pt ][ $sb_id ] ) && $options['post_type_single'][ $pt ][ $sb_id ] == $req->id ) { unset( $options['post_type_single'][ $pt ][ $sb_id ] ); } if ( is_array( @$data['arc'][ $sb_id ] ) && in_array( $pt, $data['arc'][ $sb_id ] ) ) { $options['post_type_archive'][ $pt ][ $sb_id ] = $req->id; } elseif ( isset( $options['post_type_archive'][ $pt ][ $sb_id ] ) && $options['post_type_archive'][ $pt ][ $sb_id ] == $req->id ) { unset( $options['post_type_archive'][ $pt ][ $sb_id ] ); } } // Category settings. foreach ( $raw_cat as $item ) { $cat = $item->term_id; if ( is_array( @$data['cat'][ $sb_id ] ) && in_array( $cat, $data['cat'][ $sb_id ] ) ) { $options['category_single'][ $cat ][ $sb_id ] = $req->id; } elseif ( isset( $options['category_single'][ $cat ][ $sb_id ] ) && $options['category_single'][ $cat ][ $sb_id ] == $req->id ) { unset( $options['category_single'][ $cat ][ $sb_id ] ); } if ( is_array( @$data['arc-cat'][ $sb_id ] ) && in_array( $cat, $data['arc-cat'][ $sb_id ] ) ) { $options['category_archive'][ $cat ][ $sb_id ] = $req->id; } elseif ( isset( $options['category_archive'][ $cat ][ $sb_id ] ) && $options['category_archive'][ $cat ][ $sb_id ] == $req->id ) { unset( $options['category_archive'][ $cat ][ $sb_id ] ); } } foreach ( $special_arc as $key ) { if ( is_array( @$data['arc'][ $sb_id ] ) && in_array( '_' . $key, $data['arc'][ $sb_id ] ) ) { $options[ $key ][ $sb_id ] = $req->id; } elseif ( isset( $options[ $key ][ $sb_id ] ) && $options[ $key ][ $sb_id ] == $req->id ) { unset( $options[ $key ][ $sb_id ] ); } } } $req->message = sprintf( __( 'Updated sidebar %1$s settings.', 'custom-sidebars' ), esc_html( $req->sidebar['name'] ) ); self::set_options( $options ); return $req; } /** * Registers the "Sidebars" meta box in the post-editor. */ public function add_meta_box() { global $post; $post_type = get_post_type( $post ); if ( ! $post_type ) { return false; } if ( ! self::supported_post_type( $post_type ) ) { return false; } /** * Option that can be set in wp-config.php to remove the custom sidebar * meta box for certain post types. * * @since 2.0 * * @option bool TRUE will hide all meta boxes. */ if ( defined( 'CUSTOM_SIDEBAR_DISABLE_METABOXES' ) && CUSTOM_SIDEBAR_DISABLE_METABOXES == true ) { return false; } $pt_obj = get_post_type_object( $post_type ); if ( $pt_obj->publicly_queryable || $pt_obj->public ) { add_meta_box( 'customsidebars-mb', __( 'Sidebars', 'custom-sidebars' ), array( $this, 'print_metabox_editor' ), $post_type, 'side' ); } } /** * Renders the Custom Sidebars meta box in the post-editor. */ public function print_metabox_editor() { global $post; $this->print_sidebars_form( $post->ID, 'metabox' ); } /** * Renders the sidebar-fields inside the quick-edit form. */ public function print_metabox_quick() { $this->print_sidebars_form( 0, 'quick-edit' ); } /** * Renders the Custom Sidebars form. * * @param int $post_id The post-ID to display * @param string $type Which form to display. 'metabox/quick-edit/col-sidebars'. */ protected function print_sidebars_form( $post_id, $type = 'metabox' ) { global $wp_registered_sidebars; $replacements = self::get_replacements( $post_id ); $available = $wp_registered_sidebars; ksort( $available ); $sidebars = self::get_options( 'modifiable' ); $selected = array(); if ( ! empty( $sidebars ) ) { foreach ( $sidebars as $s ) { if ( isset( $replacements[ $s ] ) ) { $selected[ $s ] = $replacements[ $s ]; } else { $selected[ $s ] = ''; } } } switch ( $type ) { case 'col-sidebars': include CSB_VIEWS_DIR . 'col-sidebars.php'; break; case 'quick-edit': include CSB_VIEWS_DIR . 'quick-edit.php'; break; default: include CSB_VIEWS_DIR . 'metabox.php'; break; } } public function store_replacements( $post_id ) { global $action; if ( ! current_user_can( self::$cap_required ) ) { return; } /* * Verify if this is an auto save routine. If it is our form has not * been submitted, so we dont want to do anything * (Copied and pasted from wordpress add_metabox_tutorial) */ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return $post_id; } /* * 'editpost' .. Saved from full Post-Editor screen. * 'inline-save' .. Saved via the quick-edit form. * We do not (yet) offer a bulk-editing option for custom sidebars. */ if ( ( isset( $_POST['action'] ) && 'inline-save' == $_POST['action'] ) || 'editpost' != $action ) { return $post_id; } // Make sure meta is added to the post, not a revision. if ( $the_post = wp_is_post_revision( $post_id ) ) { $post_id = $the_post; } $sidebars = self::get_options( 'modifiable' ); $data = array(); if ( ! empty( $sidebars ) ) { foreach ( $sidebars as $sb_id ) { if ( isset( $_POST[ 'cs_replacement_' . $sb_id ] ) ) { $replacement = $_POST[ 'cs_replacement_' . $sb_id ]; if ( ! empty( $replacement ) ) { $data[ $sb_id ] = $replacement; } } } } self::set_post_meta( $post_id, $data ); } };