/** * Create a new WordPress Archive. * * If the query mode is set to "layouts-loop", also automatically create new Loop template. * * @param string $title New WPA title. Must be unique and valid (see validate_title()). * @param array $args ( * @type array $view_settings View settings that should override the default ones. Optional. * @type array $loop_settings Loop settings that should override the default ones. Optional. * @type bool $forbid_loop_template Never create a Loop template for this View. Optional, default is false. * ) * * @return WPV_WordPress_Archive New WPA object. * * @throws InvalidArgumentException * @throws RuntimeException * @throws WPV_RuntimeExceptionWithMessage * * @note overriding default Views settings and layout settings must provide complete data when the element is an * array, because it overrides them all. For example, $args['settings']['pagination'] can not override just the * "postsper page" options: it must provide a complete pagination implementation. This might change and be corrected * in the future, keeping backwards compatibility. * * @since 1.10 */ public static function create( $title, $args ) { $wpa_id = WPV_View_Base::create_post( $title ); $wpa = new WPV_WordPress_Archive( $wpa_id ); $wpa->defer_after_update_actions(); // Construct default View settings and Loop settings $view_settings = wpv_getarr( $args, 'view_settings', array() ); $query_mode = wpv_getarr( $view_settings, WPV_View_Base::VIEW_SETTINGS_QUERY_MODE, 'archive', array( 'archive', 'layouts-loop' ) ); $view_settings[ WPV_View_Base::VIEW_SETTINGS_QUERY_MODE ] = $query_mode; $is_layouts_loop = ( 'layouts-loop' == $query_mode ); $view_settings_default = wpv_wordpress_archives_defaults( 'view_settings' ); $view_settings = wp_parse_args( $view_settings, $view_settings_default ); $wpa->update_postmeta( WPV_View_Base::POSTMETA_VIEW_SETTINGS, $view_settings ); $loop_settings_default = wpv_wordpress_archives_defaults( 'view_layout_settings' ); // Modify default loop output for Layouts loop if ( $is_layouts_loop ) { $loop_settings_default[ WPV_View_Base::LOOP_SETTINGS_META_HTML ] = str_replace( "[/wpv-items-found]", "[wpv-archive-pager-prev-page]\n" . "\t\t[wpml-string context=\"wpv-views\"]Older posts[/wpml-string]\n" . "\t[/wpv-archive-pager-prev-page]\n" . "\t[wpv-archive-pager-next-page]\n" . "\t\t[wpml-string context=\"wpv-views\"]Newer posts[/wpml-string]\n" . "\t[/wpv-archive-pager-next-page]\n" . "\t[/wpv-items-found]", $loop_settings_default[ WPV_View_Base::LOOP_SETTINGS_META_HTML ] ); } $loop_settings = wpv_getarr( $args, 'loop_settings', array() ); $loop_settings = wp_parse_args( $loop_settings, $loop_settings_default ); $wpa->update_postmeta( WPV_View_Base::POSTMETA_LOOP_SETTINGS, $loop_settings ); // Create Loop template for Layouts loop $forbid_loop_template = wpv_getarr( $args, 'forbid_loop_template', false ); if( ! $forbid_loop_template && $is_layouts_loop ) { $ct_title = sprintf( '%s - %s', $title, __( 'loop item', 'wpv-views' ) ); $ct_content = sprintf( "<h1>[wpv-post-title]</h1>\n[wpv-post-body view_template=\"None\"]\n[wpv-post-featured-image]\n%s", sprintf(__('Posted by %s on %s', 'wpv-views'), '[wpv-post-author]', '[wpv-post-date]' ) ); $wpa->create_loop_template( $ct_title, $ct_content ); } $wpa->resume_after_update_actions(); return $wpa; }
/** * Show information about how a CT is being used. * * @param $item WPV_Content_Template_Embedded Content template. * * @return string Content of the table cell. */ public function column_used_on($item) { if ($item->is_owned_by_view) { // This CT is used as a template for Loop Output in a View or WPA. // Get a View or WPA object. We'll be using only methods from their base, so it doesn't matter which one is it. $owner_view = WPV_View_Base::create($item->loop_output_id); if ($owner_view == null) { // Something is wrong - most probably the owner doesn't exist. return ''; } // Display the appropriate message. if ($owner_view->is_published) { return sprintf('<span>%s</span>', sprintf(__('This Content Template is used as the loop block for the %s <a href="%s" target="_blank">%s</a>', 'wpv-views'), $owner_view->query_mode_display_name, esc_url(add_query_arg(array('page' => 'views-embedded', 'view_id' => $owner_view->id), admin_url('admin.php'))), $owner_view->title)); } else { return sprintf('<span>%s</span>', sprintf(__('This Content Template is used as the loop block for the trashed %s <strong>%s</strong>', 'wpv-views'), $owner_view->query_mode_display_name, $owner_view->title)); } } else { // This is a normal CT. Obtain information about assignments and display them in a tag-like list. $list = array(); // "single posts" $assigned_single_pts = $item->get_assigned_single_post_types(); foreach ($assigned_single_pts as $loop) { $list[] = sprintf('<li>%s%s</li>', $loop['display_name'], __(' (single)', 'wpv-views')); } // post type archives $assigned_pt_loops = $item->get_assigned_loops('post_type'); foreach ($assigned_pt_loops as $loop) { $list[] = sprintf('<li>%s%s</li>', $loop['display_name'], __(' (post type archive)', 'wpv-views')); } // taxonomy archives $assigned_ta_loops = $item->get_assigned_loops('taxonomy'); foreach ($assigned_ta_loops as $loop) { $list[] = sprintf('<li>%s%s</li>', $loop['display_name'], __(' (taxonomy archive)', 'wpv-views')); } if (!empty($list)) { return sprintf('<ul class="wpv-taglike-list">%s</ul>', implode($list)); } else { return sprintf('<span>%s</span>', __('No Post types/Taxonomies assigned', 'wpv-views')); } } }
/** * Create a duplicate of this View. * * Clone the View and most of it's postmeta. If there is a Loop Template assigned, * duplicate that as well and update references (in the appropriate postmeta, * in shortcodes in loop output, etc.) in the duplicated View. * * @todo more detailed description * * @param string $new_post_title Title of the new View. Must not be used in any * existing View or WPA. * * @return bool|int ID of the new View or false on error. */ public function duplicate( $new_post_title ) { // Sanitize and validate $new_post_title = sanitize_text_field( $new_post_title ); if( empty( $new_post_title ) ) { return false; } if( WPV_View_Base::is_name_used( $new_post_title ) ) { return false; } // Clone existing View post object $new_post = (array) clone( $this->post() ); $new_post['post_title'] = $new_post_title; $keys_to_unset = array( 'ID', 'post_name', 'post_date', 'post_date_gmt' ); foreach( $keys_to_unset as $key ) { unset( $new_post[ $key ] ); } $new_post_id = wp_insert_post( $new_post ); // Clone existing View postmeta $postmeta_keys_to_copy = array( '_wpv_settings', '_wpv_layout_settings', '_wpv_description' ); $new_postmeta_values = array(); foreach ( $postmeta_keys_to_copy as $key ) { $new_postmeta_values[ $key ] = $this->get_postmeta( $key ); } // If this View has a loop Template, we need to clone it and adjust the layout settings. if ( $this->has_loop_template ) { $new_postmeta_values = $this->duplicate_loop_template( $new_postmeta_values, $new_post_id, $new_post_title ); } // Update postmeta of the new View. foreach ( $new_postmeta_values as $meta_key => $meta_value ) { update_post_meta( $new_post_id, $meta_key, $meta_value ); } return $new_post_id; }
/** * View duplicate callback function. * * Expects following POST arguments: * - wpnonce: A valid wpv_duplicate_view_nonce. * - id: View ID. * - name: Name of the new View. * * Refer to WPV_View::duplicate() for more information about the duplication itself. * * @since unknown */ function wpv_duplicate_this_view_callback() { wpv_ajax_authenticate( 'wpv_duplicate_view_nonce', array( 'parameter_source' => 'post', 'type_of_death' => 'data' ) ); $post_id = (int) wpv_getpost( 'id', 0 ); $post_name= sanitize_text_field( wpv_getpost( 'name', '' ) ); if ( ( 0 == $post_id ) || empty( $post_name ) ) { $data = array( 'message' => __('Wrong data', 'wpv-views') ); wp_send_json_error( $data ); } if ( WPV_View_Base::is_name_used( $post_name ) ) { $data = array( 'message' => __( 'A View with that name already exists. Please use another name', 'wpv-views' ) ); wp_send_json_error( $data ); } // Get the original View. $original_view = WPV_View::get_instance( $post_id ); if( null == $original_view ) { $data = array( 'message' => __('Wrong data', 'wpv-views') ); wp_send_json_error( $data ); } $duplicate_view_id = $original_view->duplicate( $post_name ); if ( $duplicate_view_id ) { // original post id (shouldn't we rather return new id?) wp_send_json_success(); } else { $data = array( 'message' => __( 'Unexpected error', 'wpv-views' ) ); wp_send_json_error( $data ); } }
/** * Render list items with information about usage of this Content Template. * * Also render "Bind posts" buttons where applicable. * Different info shows when CT is a loop template of some View/WPA. * * @param int $ct_id Content template ID * @return string Rendered HTML code. * * @since unknown * * @todo this needs refactoring to get rid of wpv_get_pt_tax_array() etc. */ function wpv_content_template_used_for_list( $ct_id ) { global $WPV_settings; $list = ''; $ct = WPV_Content_Template::get_instance( $ct_id ); if( null == $ct ) { // this should never happen; still, there is a serious lack of error handling return ''; } if ( ! $ct->is_owned_by_view ) { $post_types_array = wpv_get_pt_tax_array(); $count_single_post = count( $post_types_array['single_post'] ); $count_archive_post = count( $post_types_array['archive_post'] ); $count_taxonomy_post = count( $post_types_array['taxonomy_post'] ); for ( $i=0; $i<$count_single_post; $i++ ) { $type = $post_types_array['single_post'][$i][0]; $label = $post_types_array['single_post'][$i][1]; if ( isset( $WPV_settings['views_template_for_' . $type] ) && $WPV_settings['views_template_for_' . $type] == $ct_id ) { $list .= '<li>' . $label . __(' (single)', 'wpv-views'); // @todo We do not need the exact number here, let's create a has_dissident_posts method instead with a LIMITed query $dissident_post_count = $ct->get_dissident_posts( $type, 'count' ); if ( $dissident_post_count > 0 ) { $list .= sprintf( '<span class="%s"><a class="%s" data-type="%s" data-id="%s" data-nonce="%s"> %s</a></span>', 'js-wpv-apply-ct-to-cpt-single-' . $type, 'button button-small button-leveled icon-warning-sign js-wpv-apply-ct-to-all-cpt-single-dialog', $type, $ct_id, wp_create_nonce( 'work_view_template' ), sprintf( __( 'Bind %u %s ', 'wpv-views' ), $dissident_post_count, $label ) ); } $list .= '</li>'; } } for ( $i=0; $i < $count_archive_post; $i++ ) { $type = $post_types_array['archive_post'][$i][0]; $label = $post_types_array['archive_post'][$i][1]; if ( isset( $WPV_settings['views_template_archive_for_' . $type] ) && $WPV_settings['views_template_archive_for_' . $type] == $ct_id ) { $list .= '<li>' . $label . __(' (post type archive)','wpv-views') . '</li>'; } } for ( $i=0; $i < $count_taxonomy_post; $i++ ) { $type = $post_types_array['taxonomy_post'][$i][0]; $label = $post_types_array['taxonomy_post'][$i][1]; if ( isset( $WPV_settings['views_template_loop_' . $type] ) && $WPV_settings['views_template_loop_' . $type] == $ct_id ) { $list .= '<li>' . $label . __(' (taxonomy archive)','wpv-views') . '</li>'; } } if ( ! empty( $list ) ) { $list = '<ul class="wpv-taglike-list">' . $list . '</ul>'; } else { $list = '<span>' . __( 'No Post types/Taxonomies assigned', 'wpv-views' ) . '</span>'; } } else { // This CT is owned by a View/WPA and used as a loop template $owner_view = WPV_View_Base::get_instance( $ct->loop_output_id ); if( null == $owner_view ) { // again, there was no check for missing View before! return ''; } // Show usage information depending on owner View post status. if ( $owner_view->is_published ) { $edit_page = 'views-editor'; if ( WPV_View_Base::is_archive_view( $owner_view->id ) ) { $edit_page = 'view-archives-editor'; } $list = sprintf( __( 'This Content Template is used as the loop block for the %s <a href="%s" target="_blank">%s</a>', 'wpv-views' ), $owner_view->query_mode_display_name, add_query_arg( array( 'page' => $edit_page, 'view_id' => $owner_view->id ), admin_url( 'admin.php' ) ), $owner_view->title ); } else { $list = sprintf( __( 'This Content Template is used as the loop block for the trashed %s <strong>%s</strong>', 'wpv-views' ), $owner_view->query_mode_display_name, $owner_view->title ); } } return "<span>$list</span>"; }
/** * Get default postmeta for a View. * * Combine self::$postmeta_defaults with defaults common for Views and WPAs. * * @return array */ protected function get_postmeta_defaults() { $parent_postmeta = parent::get_postmeta_defaults(); $this_postmeta = WPV_View_Embedded::$postmeta_defaults; return wpv_array_merge_recursive_distinct( $parent_postmeta, $this_postmeta ); }
function wpv_generate_view_loop_output_callback() { if ( ! current_user_can( 'manage_options' ) ) { $data = array( 'type' => 'capability', 'message' => __( 'You do not have permissions for that.', 'wpv-views' ) ); wp_send_json_error( $data ); } if ( ! isset( $_POST["wpnonce"] ) || ! wp_verify_nonce( $_POST["wpnonce"], 'layout_wizard_nonce' ) ) { $data = array( 'type' => 'nonce', 'message' => __( 'Your security credentials have expired. Please reload the page to get new ones.', 'wpv-views' ) ); wp_send_json_error( $data ); } if ( ! isset( $_POST["view_id"] ) || ! is_numeric( $_POST["view_id"] ) || intval( $_POST['view_id'] ) < 1 ) { $data = array( 'type' => 'id', 'message' => __( 'Wrong or missing ID.', 'wpv-views' ) ); wp_send_json_error( $data ); } $view_id = $_POST['view_id']; $style = sanitize_text_field( $_POST['style'] ); // @todo better validation $fields = json_decode( stripslashes( $_POST['fields'] ), true ); $args = json_decode( stripslashes( $_POST['args'] ), true ); // Translate field data from non-associative arrays into something that WPV_View_Base::generate_loop_output() understands. $fields_normalized = array(); foreach( $fields as $field ) { $fields_normalized[] = array( 'prefix' => $field[0], 'shortcode' => $field[1], 'suffix' => $field[2], 'field_name' => $field[3], 'header_name' => $field[4], 'row_title' => $field[5] ); } $loop_output = WPV_View_Base::generate_loop_output( $style, $fields_normalized, $args ); // Forward the fail when loop output couldn't have been generated. if ( null == $loop_output ) { $data = array( 'type' => 'error', 'message' => __( 'Could not generate the Loop Output. Please reload and try again.', 'wpv-views' ) ); wp_send_json_error( $data ); } // Merge new settings to existing ones (overwrite keys from $layout_settings but keep the rest). $loop_output_settings = $loop_output['loop_output_settings']; $prev_settings = get_post_meta( $view_id, '_wpv_layout_settings', true ); if( ! is_array( $prev_settings ) ) { // Handle missing _wpv_layout_settings for given View. $prev_settings = array(); } $loop_output_settings = array_merge( $prev_settings, $loop_output_settings ); if ( isset( $loop_output_settings['fields'] ) && is_array( $loop_output_settings['fields'] ) ) { $loop_output_settings['fields'] = array_values( $loop_output_settings['fields'] ); } // Return the results. $data = array( 'loop_output_settings' => $loop_output_settings, 'ct_content' => $loop_output['ct_content'] ); wp_send_json_success( $data ); }
/** * Generate Bootstrap grid View layout. * * @see generate_view_loop_output() * * @param array $fields Array of fields to be used inside this layout. * @param array $args Additional arguments (expected: bootstrap_column_count, bootstrap_version, add_container, * add_row_class, render_individual_columns). * * @return null|array Null on error (missing bootstrap version), otherwise the array: * array ( * @type string $loop_template Loop Output code. * @type string $ct_content Content of the Content Template or an empty string if it's not being used. * ) * * @since 1.10 */ private static function generate_bootstrap_grid_layout( $fields, $args ) { $column_count = $args['bootstrap_column_count']; // Fail if we don't have valid bootstrap version $bootstrap_version = wpv_getarr( $args, 'bootstrap_version', 'undefined', array( 2, 3 ) ); if( 'undefined' == $bootstrap_version ) { return null; } $indent = $args['use_loop_template'] ? "" : "\t\t\t\t"; $field_codes = WPV_View_Base::generate_field_codes( $fields, $indent ); // Prevent division by zero if( $column_count < 1 ) { return null; } $column_offset = 12 / $column_count; $output = ''; // Row style and cols class for bootstrap 2 $row_style = ( $bootstrap_version == 2 ) ? ' row-fluid' : ''; $col_style = ( $bootstrap_version == 2 ) ? 'span' : 'col-sm-'; $col_class = $col_style . $column_offset; // Add row class (optional for bootstrap 2) $row_class = ( $args['add_row_class'] || ( 3 == $bootstrap_version ) ) ? 'row' : ''; if( $args['use_loop_template'] ) { $ct_content = $field_codes; $loop_item = "<div class=\"$col_class\">[wpv-post-body view_template=\"{$args['loop_template_title']}\"]</div>"; } else { $ct_content = ''; $loop_item = "<div class=\"$col_class\">\n$field_codes\n\t\t\t</div>"; } if( $args['add_container'] ) { $output .= "\t<div class=\"container\">\n"; } $output .= "\t<wpv-loop wrap=\"{$column_count}\" pad=\"true\">\n"; // If the first column is also a last column, close the div tag. $ifone = ( 1 == $column_count ) ? "\n\t\t</div>" : ''; if( $args['render_individual_columns'] ) { // Render items for each column. $output .= "\t\t[wpv-item index=1]\n" . "\t\t<div class=\"{$row_class} {$row_style}\">\n" . "\t\t\t$loop_item$ifone\n"; for( $i = 2; $i < $column_count; ++$i ) { $output .= "\t\t[wpv-item index=$i]\n" . "\t\t\t$loop_item\n"; } } else { // Render compact HTML $output .= "\t\t[wpv-item index=1]\n" . "\t\t<div class=\"{$row_class} {$row_style}\">\n" . "\t\t\t$loop_item$ifone\n" . "\t\t[wpv-item index=other]\n" . "\t\t\t$loop_item\n"; } // Render item for last column. if ( $column_count > 1) { $output .= "\t\t[wpv-item index=$column_count]\n" . "\t\t\t$loop_item\n" . "\t\t</div>\n"; } // Padding items $output .= "\t\t[wpv-item index=pad]\n" . "\t\t\t<div class=\"{$col_class}\"></div>\n" . "\t\t[wpv-item index=pad-last]\n" . "\t\t\t<div class=\"{$col_class}\"></div>\n" . "\t\t</div>\n" . "\t</wpv-loop>\n\t"; if ( $args['add_container'] ) { $output .= "</div>\n\t"; } return array( 'loop_template' => $output, 'ct_content' => $ct_content ); }
/** * Get the post object representing this Content Template. * * @return WP_Post Post object. * * @throws InvalidArgumentException if the post object cannot be retrieved or is invalid. */ protected function &post() { if (null == $this->post) { // Requesting WP_Post object, but we haven't got it yet. $post = WP_Post::get_instance($this->object_id); if (WPV_View_Base::is_wppost_ct($post)) { $this->post = $post; } else { throw new InvalidArgumentException('Invalid Content Template ID'); } } return $this->post; }
/** * Set default WordPress Archives settings and layout settings * * @param string $settings field: view_settings or view_layout_settings * @return array with desired values * @since unknown */ function wpv_wordpress_archives_defaults( $settings = 'view_settings' ) { $empty_loop_output = WPV_View_Base::generate_loop_output(); $defaults = array( 'view_settings' => array( 'view-query-mode' => 'archive', 'sections-show-hide' => array( 'content' => 'off', ) ), 'view_layout_settings' => array( // almost all of this settings are only needed to create the layout on the fly, so they are not needed here 'additional_js' => '', 'layout_meta_html' => $empty_loop_output['loop_output_settings']['layout_meta_html'], ), ); return $defaults[ $settings ]; }
function wpv_update_layout_extra_callback() { // Authentication if ( ! current_user_can( 'manage_options' ) ) { $data = array( 'type' => 'capability', 'message' => __( 'You do not have permissions for that.', 'wpv-views' ) ); wp_send_json_error( $data ); } if ( ! isset( $_POST["wpnonce"] ) || ! wp_verify_nonce( $_POST["wpnonce"], 'wpv_view_layout_extra_nonce' ) ) { $data = array( 'type' => 'nonce', 'message' => __( 'Your security credentials have expired. Please reload the page to get new ones.', 'wpv-views' ) ); wp_send_json_error( $data ); } $view_id = (int) wpv_getpost( 'id', 0 ); // This will give us a View, a WPA or null. $view = WPV_View_Base::get_instance( $view_id ); if ( $view_id < 1 || ( null == $view ) ) { $data = array( 'type' => 'id', 'message' => __( 'Wrong or missing ID.', 'wpv-views' ) ); wp_send_json_error( $data ); } try { // We're updating multiple properties at once. $view->defer_after_update_actions(); // Actually we're changing only View settings and loop settings here. // If any of those changes fails, the database will not be updated. $view->begin_modifying_view_settings(); $view->begin_modifying_loop_settings(); $view->css = wpv_getpost( 'layout_css_val' ); $view->js = wpv_getpost( 'layout_js_val' ); $view->loop_meta_html = wpv_getpost( 'layout_val' ); // Save the wizard settings if ( isset( $_POST['include_wizard_data'] ) ) { $view->loop_style = wpv_getpost( 'style' ); $view->loop_table_column_count = wpv_getpost( 'table_cols' ); $view->loop_bs_column_count = wpv_getpost( 'bootstrap_grid_cols' ); $view->loop_bs_grid_container = wpv_getpost( 'bootstrap_grid_container' ); $view->loop_row_class = wpv_getpost( 'bootstrap_grid_row_class' ); $view->loop_bs_individual = wpv_getpost( 'bootstrap_grid_individual' ); $view->loop_include_field_names = wpv_getpost( 'include_field_names' ); $view->loop_fields = wpv_getpost( 'fields' ); // @todo sanitize this $view->loop_real_fields = wpv_getpost( 'real_fields' ); // @todo sanitize this // Remove unused Content Template $ct_to_delete = (int) wpv_getpost( 'delete_view_loop_template', 0 ); if( $ct_to_delete > 0 ) { $view->delete_unused_loop_template( $ct_to_delete ); } } // Now store changes. $view->finish_modifying_view_settings(); $view->finish_modifying_loop_settings(); $view->resume_after_update_actions(); } catch ( WPV_RuntimeExceptionWithMessage $e ) { // Validation errors go here. wp_send_json_error( array( 'type' => 'update', 'message' => $e->getUserMessage() ) ); } catch ( Exception $e ) { wp_send_json_error( array( 'type' => 'update', 'message' => __( 'An unexpected error ocurred.', 'wpv-views' ) ) ); } // Success! $data = array( 'id' => $view_id, 'message' => __( 'Loop Output saved', 'wpv-views' ) ); wp_send_json_success( $data ); }
function wpv_ct_editor_usage_section( $ct ) { ob_start(); $parent_view = null; if( $ct->is_owned_by_view ) { $parent_view = WPV_View_Base::get_instance($ct->loop_output_id); } if( null != $parent_view ) { if( $parent_view->is_published ) { $edit_page = 'views-editor'; if ( WPV_View_Base::is_archive_view( $parent_view->id ) ) { $edit_page = 'view-archives-editor'; } $loop_template_notice = sprintf( __( 'This Content Template is used as the loop block for the %s <a href="%s" target="_blank">%s</a>.', 'wpv-views' ), $parent_view->query_mode_display_name, esc_attr( add_query_arg( array( 'page' => $edit_page, 'view_id' => $parent_view->id ), admin_url( 'admin.php' ) ) ), $parent_view->title ); } else { $loop_template_notice = sprintf( __( 'This Content Template is used as the loop block for the trashed %s %s.', 'wpv-views' ), $parent_view->query_mode_display_name, "<strong>{$parent_view->title}</strong>" ); } printf( '<div class="wpv-advanced-setting"><p>%s</p></div>', $loop_template_notice ); } else { $asterisk_explanation = '<span data-bind="fadeVisibility: isAsteriskExplanationVisible(\'%s\', \'%s\')"><span style="color:red">*</span> ' . __('A different Content Template is already assigned to this item.', 'wpv-views') . '</span>'; // Render checkboxes for each type of assignment. $single_post_types_with_other_ct = wpv_ct_editor_usage_section_single_pages($ct, $asterisk_explanation); $cpt_archives_with_other_ct = wpv_ct_editor_usage_section_post_archives($ct, $asterisk_explanation); $taxonomy_archives_with_other_ct = wpv_ct_editor_usage_section_taxonomy_archives($ct, $asterisk_explanation); // Print information about other CT assignments for JS $other_assignments = array( 'single_posts' => $single_post_types_with_other_ct, 'cpt_archives' => $cpt_archives_with_other_ct, 'taxonomy_archives' => $taxonomy_archives_with_other_ct ); printf( '<span style="visibility: hidden" class="js-wpv-usage-other-assignments" data-value="%s"></span>', htmlentities(json_encode($other_assignments)) ); ?> <p class="update-button-wrap"> <span class="update-action-wrap"> <span class="js-wpv-message-container"></span> <span class="spinner ajax-loader" data-bind="spinnerActive: isUsageSectionUpdating"></span> </span> <button data-bind=" enable: isUsageSectionUpdateNeeded, attr: { class: isUsageSectionUpdateNeeded() ? 'button-primary' : 'button-secondary' }, click: usageSectionUpdate"> <?php _e('Update', 'wpv-views'); ?> </button> </p> <?php } $content = ob_get_contents(); ob_end_clean(); wpv_ct_editor_render_section( __( 'Usage', 'wpv-views' ), 'js-wpv-usage-section', $content, false, '', '', array( 'section' => 'usage_section', 'pointer_slug' => 'ptr_section' ) ); }