/** * 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; }
function wpv_admin_content_template_listing_name() { $mod_url = array( // array of URL modifiers 'orderby' => '', 'order' => '', 's' => '', 'items_per_page' => '', 'paged' => '', 'status' => '' ); $wpv_args = array( 'post_type' => 'view-template', 'posts_per_page' => WPV_ITEMS_PER_PAGE, 'order' => 'ASC', 'orderby' => 'title', 'post_status' => 'publish' ); // Apply post_status coming from the URL parameters. $post_status = wpv_getget( 'status', 'publish', array( 'publish', 'trash' ) ); $wpv_args['post_status'] = $post_status; $mod_url['status'] = $post_status; $the_other_post_status = ( 'publish' == $post_status ) ? 'trash' : 'publish'; if ( isset( $_GET["s"] ) && '' != $_GET["s"] ) { $wpv_args = wpv_modify_wpquery_for_search( $_GET["s"], $wpv_args ); $mod_url['s'] = sanitize_text_field( $_GET["s"] ); } if ( isset( $_GET["items_per_page"] ) && '' != $_GET["items_per_page"] ) { $wpv_args['posts_per_page'] = (int) $_GET["items_per_page"]; $mod_url['items_per_page'] = (int) $_GET["items_per_page"]; } if ( isset( $_GET["orderby"] ) && '' != $_GET["orderby"] ) { $wpv_args['orderby'] = sanitize_text_field($_GET["orderby"]); $mod_url['orderby'] = sanitize_text_field($_GET["orderby"]); if ( isset( $_GET["order"] ) && '' != $_GET["order"] ) { $wpv_args['order'] = sanitize_text_field($_GET["order"]); $mod_url['order'] = sanitize_text_field($_GET["order"]); } } if ( isset( $_GET["paged"] ) && '' != $_GET["paged"]) { $wpv_args['paged'] = (int) $_GET["paged"]; $mod_url['paged'] = (int) $_GET["paged"]; } // Build a query for the other post status. We're interested only in post count $other_post_status_args = $wpv_args; $other_post_status_args['post_status'] = $the_other_post_status; $other_post_status_args['fields'] = 'ids'; // All querying must be done between those two switch_lang() calls otherwise CT translations // will be also (wrongly) included. global $sitepress; $default_language = ''; if( isset( $sitepress ) ) { //changes to the default language $default_language = $sitepress->get_default_language(); $sitepress->switch_lang( $default_language ); } $query = new WP_Query( $wpv_args ); $other_post_status_query = new WP_Query( $other_post_status_args ); if( isset( $sitepress ) ) { //changes to the current language $sitepress->switch_lang( ICL_LANGUAGE_CODE ); } // Number of posts that are being displayed. $wpv_count_posts = $query->post_count; // Total number of posts matching the query. $wpv_found_posts = $query->found_posts; // to hold the number of Views in each status $ct_counts_by_post_status = array( $post_status => $wpv_found_posts, $the_other_post_status => $other_post_status_query->found_posts ); // True if some content templates (even those not matching current query) exist. $some_posts_exist = ( $ct_counts_by_post_status['publish'] > 0 || $ct_counts_by_post_status['trash'] > 0 ); $active_nondefault_languages = array(); $add_translation_icon = ''; $edit_translation_icon = ''; $are_cts_translatable = WPV_Content_Template_Embedded::is_translatable(); if( $are_cts_translatable ) { $active_languages = apply_filters( 'wpml_active_languages', array() ); // just remove the default language $active_nondefault_languages = $active_languages; unset( $active_nondefault_languages[ $default_language ] ); // store urls to add/edit translaton icons if( defined( 'ICL_PLUGIN_URL' ) ) { $add_translation_icon = ICL_PLUGIN_URL . '/res/img/add_translation.png'; $edit_translation_icon = ICL_PLUGIN_URL . '/res/img/edit_translation.png'; } } ?> <?php if ( $some_posts_exist ) { ?> <ul class="subsubsub" style="clear:both"><!-- links to lists WPA in different statuses --> <li> <?php $is_plain_publish_current_status = ( $wpv_args['post_status'] == 'publish' && !isset( $_GET["s"] ) ); printf( '<a href="%s" %s>%s</a> (%s) | ', esc_url( add_query_arg( array( 'page' => 'view-templates', 'status' => 'publish' ), admin_url( 'admin.php' ) ) ), $is_plain_publish_current_status ? ' class="current" ' : '', __( 'Published', 'wpv-views' ), $ct_counts_by_post_status['publish'] ); ?> </li> <li> <?php $is_plain_trash_current_status = ( $wpv_args['post_status'] == 'trash' && !isset( $_GET["s"] ) ); printf( '<a href="%s" %s>%s</a> (%s)', esc_url( add_query_arg( array( 'page' => 'view-templates', 'status' => 'trash' ), admin_url( 'admin.php' ) ) ), $is_plain_trash_current_status ? ' class="current" ' : '', __( 'Trash', 'wpv-views' ), $ct_counts_by_post_status['trash'] ); ?> </li> </ul> <?php } else { // No post exist at all ?> <p class="wpv-view-not-exist"> <?php _e('Content Templates let you design single pages.','wpv-views'); ?> </p> <p class="add-new-view"> <button class="button js-add-new-content-template" data-target="<?php echo esc_url( add_query_arg( array( 'action' => 'wpv_ct_create_new' ), admin_url( 'admin-ajax.php' ) ) ); ?>"> <i class="icon-plus"></i><?php _e('Add new Content Template','wpv-views') ?> </button> </p><?php } // A nonce for CT action - used for individual as well as for bulk actions. // It will have a value only if some posts exist. $ct_action_nonce = ''; if( $some_posts_exist ) { $ct_action_nonce = wp_create_nonce( 'wpv_view_listing_actions_nonce' ); // === Render "tablenav" section (Bulk actions and Search box) === echo '<div class="tablenav top">'; if( $wpv_count_posts > 0 ) { // Prepare to render bulk actions dropdown. if( 'publish' == $wpv_args['post_status'] ) { $bulk_actions = array( 'trash' => __( 'Move to trash', 'wpv-views' ) ); } else { $bulk_actions = array( 'restore-from-trash' => __( 'Restore from trash', 'wpv-views' ), 'delete' => __( 'Delete permanently', 'wpv-views' ) ); } $bulk_actions_args = array( 'data-viewactionnonce' => $ct_action_nonce ); $bulk_actions_class = 'js-wpv-ct-listing-bulk-action'; echo wpv_admin_table_bulk_actions( $bulk_actions, $bulk_actions_class, $bulk_actions_args, 'top' ); } // Show search box if ( $wpv_found_posts > 0 ) { ?> <div class="alignright"> <form id="posts-filter" action="" method="get"> <p class="search-box"> <label class="screen-reader-text" for="post-search-input"><?php _e('Search Views:', 'wpv-views') ?></label> <?php $search_term = isset( $_GET["s"] ) ? urldecode( sanitize_text_field($_GET["s"]) ) : ''; ?> <input type="search" id="ct-post-search-input" name="s" value="<?php echo $search_term; ?>"> <input type="submit" name="" id="ct-search-submit" class="button" value="<?php echo htmlentities( __('Search Content Templates', 'wpv-views'), ENT_QUOTES ); ?>"> <input type="hidden" name="paged" value="1" /> </p> </form> </div> <?php } echo '</div>'; // End of tablenav section } if ( $wpv_count_posts == 0 && $some_posts_exist ) { // No posts are displayed, but some exist if ( isset( $_GET["s"] ) && '' != $_GET["s"] ) { if ( $wpv_args['post_status'] == 'trash' ) { // Searching in trash ?> <div class="wpv-views-listing views-empty-list"> <p> <?php printf( '<p>%s</p><p><a class="button-secondary" href="%s">%s</a></p>', __( 'No Content Templates in trash matched your criteria.', 'wpv-views' ), wpv_maybe_add_query_arg( array( 'page' => 'view-templates', 'orderby' => $mod_url['orderby'], 'order' => $mod_url['order'], 'items_per_page' => $mod_url['items_per_page'], 'paged' => '1', 'status' => 'trash' ), admin_url( 'admin.php' ) ), __( 'Return', 'wpv-views' ) ); ?> </p> </div> <?php } else { // Normal search ?> <div class="wpv-views-listing views-empty-list"> <p> <?php printf( '<p>%s</p><p><a class="button-secondary" href="%s">%s</a></p>', __( 'No Content Templates matched your criteria.', 'wpv-views' ), wpv_maybe_add_query_arg( array( 'page' => 'view-templates', 'orderby' => $mod_url['orderby'], 'order' => $mod_url['order'], 'items_per_page' => $mod_url['items_per_page'], 'paged' => '1' ), admin_url( 'admin.php' ) ), __( 'Return', 'wpv-views' ) ); ?> </p> </div> <?php } } else { if ( $wpv_args['post_status'] == 'trash' ) { // No items in trash ?> <div class="wpv-views-listing views-empty-list"> <p> <?php printf( '<p>%s</p><p><a class="button-secondary" href="%s">%s</a></p>', __( 'No Content Templates in trash.', 'wpv-views' ), wpv_maybe_add_query_arg( array( 'page' => 'view-templates', 'orderby' => $mod_url['orderby'], 'order' => $mod_url['order'], 'items_per_page' => $mod_url['items_per_page'], 'paged' => '1' ), admin_url( 'admin.php' ) ), __( 'Return', 'wpv-views' ) ); ?> </p> </div> <?php } else { ?> <p class="wpv-view-not-exist"> <?php _e('Content Templates let you design single pages.','wpv-views'); ?> </p> <p class="add-new-view"> <button class="button js-add-new-content-template" data-target="<?php echo esc_url( add_query_arg( array( 'action' => 'wpv_ct_create_new' ), admin_url( 'admin-ajax.php' ) ) ); ?>"> <i class="icon-plus"></i><?php _e( 'Add new Content Template', 'wpv-views') ?> </button> </p> <?php } } } else if ( $wpv_count_posts != 0 ) { // We have some results to display. global $wpdb; ?> <table class="wpv-views-listing widefat"> <!-- section for: sort by name --> <thead> <?php /* To avoid code duplication, table header is stored in output buffer and echoed twice - within * thead and tfoot tags. */ ob_start(); ?> <tr> <th class="wpv-admin-listing-col-bulkactions check-column"> <input type="checkbox" /> </th> <?php $column_active = ''; $column_sort_to = 'ASC'; $column_sort_now = 'ASC'; $status = ''; if ( $wpv_args['orderby'] === 'title' ) { $column_active = ' views-list-sort-active'; $column_sort_to = ( $wpv_args['order'] === 'ASC' ) ? 'DESC' : 'ASC'; $column_sort_now = $wpv_args['order']; } if ( isset($_GET['status']) && $_GET['status'] == 'trash' ){ $status = 'trash'; } ?> <th class="wpv-admin-listing-col-title"> <?php printf( '<a href="%s" class="%s" data-orderby="title">%s <i class="%s"></i></a>', wpv_maybe_add_query_arg( array( 'page' => 'view-templates', 'status' => $status, 'orderby' => 'title', 'order' => $column_sort_to, 's' => $mod_url['s'], 'items_per_page' => $mod_url['items_per_page'], 'paged' => $mod_url['paged'] ), admin_url( 'admin.php' ) ), 'js-views-list-sort views-list-sort ' . $column_active, __( 'Title', 'wpv-views' ), ( 'DESC' === $column_sort_now ) ? 'icon-sort-by-alphabet-alt' : 'icon-sort-by-alphabet' ); ?> </th> <?php if( $are_cts_translatable ) { $flag_images = array(); foreach( $active_nondefault_languages as $language_info ) { $flag_images[] = sprintf( '<img style="padding: 2px;" src="%s" title="%s" alt="%s" />', $language_info['country_flag_url'], $language_info['translated_name'], $language_info['code'] ); } if( empty( $flag_images ) ) { $translation_column_header = __( 'Translations', 'wpv-views' ); } else { $translation_column_header = implode( '', $flag_images ); } printf( '<th>%s</th>', $translation_column_header ); } ?> <th class="wpv-admin-listing-col-usage js-wpv-col-two"><?php _e('Used on','wpv-views') ?></th> <?php $column_active = ''; $column_sort_to = 'DESC'; $column_sort_now = 'DESC'; if ( $wpv_args['orderby'] === 'date' ) { $column_active = ' views-list-sort-active'; $column_sort_to = ( $wpv_args['order'] === 'ASC' ) ? 'DESC' : 'ASC'; $column_sort_now = $wpv_args['order']; } ?> <th class="wpv-admin-listing-col-date"> <?php printf( '<a href="%s" class="%s" data-orderby="date">%s <i class="%s"></i></a>', wpv_maybe_add_query_arg( array( 'page' => 'view-templates', 'status' => $status, 'orderby' => 'date', 'order' => $column_sort_to, 's' => $mod_url['s'], 'items_per_page' => $mod_url['items_per_page'], 'paged' => $mod_url['paged'] ), admin_url( 'admin.php' ) ), 'js-views-list-sort views-list-sort ' . $column_active, __( 'Date', 'wpv-views' ), ( 'DESC' === $column_sort_now ) ? 'icon-sort-by-attributes-alt' : 'icon-sort-by-attributes' ); ?> </th> </tr> <?php // Get table header from output buffer and stop buffering $table_header = ob_get_contents(); ob_end_clean(); echo $table_header; ?> </thead> <tfoot> <?php echo $table_header; ?> </tfoot> <tbody class="js-wpv-views-listing-body"> <?php $alternate = ''; while ( $query->have_posts() ) : $query->the_post(); $post = get_post( get_the_id() ); $template_id = $post->ID; $ct = WPV_Content_Template::get_instance( $template_id ); $wpv_content_template_decription = get_post_meta( $template_id, '_wpv-content-template-decription', true ); $layout_loop_template_for_view_id = get_post_meta( $template_id, '_view_loop_id', true ); $alternate = ( ' alternate' == $alternate ) ? '' : ' alternate'; ?> <tr id="wpv_ct_list_row_<?php echo $template_id; ?>" class="js-wpv-ct-list-row<?php echo $alternate; ?>"> <th class="wpv-admin-listing-col-bulkactions check-column"> <?php if ( empty( $layout_loop_template_for_view_id ) ) { printf( '<input type="checkbox" value="%s" name="view[]" />', $template_id ); } ?> </th> <td class="wpv-admin-listing-col-title post-title page-title column-title"> <span class="row-title"> <?php if ( $wpv_args['post_status'] == 'trash' ) { echo esc_html( $post->post_title ); } else { wpv_ct_editor_render_link( $template_id, esc_html( $post->post_title ) ); } ?> </span> <?php if ( ! empty( $wpv_content_template_decription ) ) { ?> <p class="desc"> <?php echo nl2br( $wpv_content_template_decription )?> </p> <?php } /* Generate and show row actions. * Note that we want to add also 'simple' action names to the action list because * they get echoed as a class of the span tag and get styled from WordPress core css * accordingly (e.g. trash in different colour than the rest) */ $row_actions = array(); $asigned_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(post_id) FROM {$wpdb->postmeta} JOIN {$wpdb->posts} p WHERE meta_key = '_views_template' AND meta_value = %s AND post_id = p.ID AND p.post_status NOT IN ('auto-draft') AND p.post_type != 'revision'", $template_id ) ); if ( 'publish' == $wpv_args['post_status'] ) { $row_actions['edit'] = sprintf( '<a href="%s">%s</a>', esc_url( add_query_arg( array( 'page' => WPV_CT_EDITOR_PAGE_NAME, 'ct_id' => $template_id ), admin_url( 'admin.php' ) ) ), __( 'Edit', 'wpv-views' ) ); if ( empty( $layout_loop_template_for_view_id ) ) { $row_actions['change js-wpv-ct-change-usage-popup'] = sprintf( '<a href="#">%s</a>', __( 'Change template usage', 'wpv-views' ) ); } $row_actions['duplicate js-list-ct-action-duplicate'] = sprintf( '<a href="#">%s</a>', __( 'Duplicate', 'wpv-views' ) ); if ( empty( $layout_loop_template_for_view_id ) ) { $row_actions['trash js-wpv-ct-action-trash'] = sprintf( '<a href="#">%s</a>', __( 'Move to trash', 'wpv-views' ) ); } } else if ( 'trash' == $wpv_args['post_status'] ) { $row_actions['restore-from-trash js-wpv-ct-action-restore-from-trash'] = sprintf( '<a href="#">%s</a>', __( 'Restore from trash', 'wpv-views' ) ); $row_actions['delete js-list-ct-action-delete'] = sprintf( '<a href="#">%s</a>', __( 'Delete', 'wpv-views' ) ); } echo wpv_admin_table_row_actions( $row_actions, array( "data-ct-id" => $template_id, "data-postcount" => $asigned_count, "data-ct-name" => htmlentities( $post->post_title, ENT_QUOTES ), "data-viewactionnonce" => $ct_action_nonce, // Used by the "duplicate" action "data-msg" => htmlentities( __( 'Enter new title','wpv-views'), ENT_QUOTES ) ) ); ?> </td> <?php if( $are_cts_translatable ) { echo '<td>'; $ct_translations = $ct->wpml_translations; foreach( $active_nondefault_languages as $language_info ) { $translation = wpv_getarr( $ct_translations, $language_info['code'], null ); if( null == $translation ) { $translation_text = __( 'Add translation', 'wpv-views' ); $translation_icon = $add_translation_icon; } else { $translation_text = __( 'Edit translation', 'wpv-views' ); $translation_icon = $edit_translation_icon; } $translation_editor_link = $ct->get_wpml_tm_link( $language_info['code'] ); if( null != $translation_editor_link ) { printf( '<a style="padding: 2px;" href="%s"><img alt="%s" src="%s" title="%s" /></a>', $translation_editor_link, $language_info['code'], $translation_icon, $translation_text ); } else { /** @noinspection CssInvalidFunction */ /** @noinspection CssUnknownProperty */ printf( '<span style="padding: 2px"> <img alt="%s" src="%s" title="%s" style="-webkit-filter: grayscale(100%%); filter: grayscale(100%%)"/> </span>', $language_info['code'], $translation_icon, __( 'WPML Translation Management must be active for this link to work.', 'wpv-view' ) ); } } echo '</td>'; } ?> <td class="wpv-admin-listing-col-usage"> <?php echo wpv_content_template_used_for_list( $template_id ); ?> </td> <td class="wpv-admin-listing-col-date"> <?php echo get_the_time( get_option( 'date_format' ), $template_id ); ?> </td> </tr> <?php endwhile; ?> </tbody> </table> <div class="tablenav bottom"> <?php echo wpv_admin_table_bulk_actions( $bulk_actions, $bulk_actions_class, $bulk_actions_args, 'bottom' ); ?> </div> <p class="add-new-view"> <button class="button js-add-new-content-template" data-target="<?php echo esc_url( add_query_arg( array( 'action' => 'wpv_ct_create_new' ), admin_url( 'admin-ajax.php' ) ) ); ?>"> <i class="icon-plus"></i><?php _e( 'Add new Content Template','wpv-views' ) ?> </button> </p> <?php } wpv_admin_listing_pagination( 'view-templates', $wpv_found_posts, $wpv_args["posts_per_page"], $mod_url ); // Render dialog templates. wpv_render_ct_listing_dialog_templates_arrangeby_name(); }
/** * Print column headers. */ protected function print_column_headers() { $columns = $this->get_column_info(); /* Get the current 'orderby' URL parameter. Obtain it either from $_GET or from column definitions (if there is * a pre-sorted column). If items aren't ordered, it will be an empty string. */ $current_orderby = wpv_getget('orderby', ''); if ('' == $current_orderby) { // Try to get information from column definitions. $current_orderby_column = $this->get_presorted_column(); $current_orderby = $columns[$current_orderby_column]['orderby']; $is_presorted = '' != $current_orderby; } else { $is_presorted = false; $current_orderby_column = $this->get_column_slug_by_orderby($current_orderby); } // Get the current 'order' URL parameter from $_GET or from column definitions. Defaults to 'ASC' if both methods fail. $current_order = wpv_getget('order', '', array('ASC', 'DESC')); if ('' == $current_order) { if ($is_presorted) { $current_order = wpv_getarr($columns[$current_orderby_column], 'default_order', 'ASC', array('ASC', 'DESC')); } else { $current_order = 'ASC'; } } $reverse_order = 'ASC' == $current_order ? 'DESC' : 'ASC'; // URL that will be used to build sorting links. All URL parameters except 'paged' and 'last_page' will be preserved. $current_url = set_url_scheme('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); $current_url = esc_url(remove_query_arg(array('paged', 'last_page'), $current_url)); // Print table header cell for each column foreach ($columns as $column_slug => $column) { $class = array("column-{$column_slug}"); $is_sortable = wpv_getarr($column, 'is_sortable', false); if ($is_sortable) { // Sortable column // URL 'orderby' parameter for this column $column_orderby = wpv_getarr($column, 'orderby', ''); if ($column_orderby == $current_orderby) { // We're sorting by this column. Build link to reverse order. $new_order = $reverse_order; $new_orderby = $current_orderby; // Append to the title according to current order. $title_order = wpv_getarr($column, 'ASC' == $current_order ? 'title_asc' : 'title_desc', ''); } else { // Not sorting by this column. Build link to sort by this column, ascending. $new_order = 'ASC'; $new_orderby = $column_orderby; $title_order = ''; } $title = sprintf('<a href="%s">%s%s</a>', esc_url(add_query_arg(array('order' => $new_order, 'orderby' => $new_orderby), $current_url)), wpv_getarr($column, 'title', $column_slug), $title_order); } else { // Non-sortable column, just print the title text. $title = wpv_getarr($column, 'title', $column_slug); } printf('<th scope="col" class="%s">%s</th>', join(' ', $class), $title); } }
/** * Modify edit URLs for Content Templates on Translation Queue in WPML Translation Management. * * For CT, return URL to CT edit page. * * @param string $edit_url Current edit URL * @param string $content_type For posts, this will be post_{$post_type}. * @param int $element_id Post ID if the element is a post. * * @return string Edit URL. * * @since 1.10 */ public function wpml_document_edit_item_url_ct( $edit_url, $content_type, $element_id ) { if ( 'post_' . WPV_Content_Template_Embedded::POST_TYPE == $content_type ) { if ( $element_id ) { $link = apply_filters( 'icl_post_link', array(), WPV_Content_Template_Embedded::POST_TYPE, $element_id, 'edit' ); $url = wpv_getarr( $link, 'url' ); $is_disabled = wpv_getarr( $link, 'is_disabled', false ); if( $is_disabled ) { $edit_url = ''; // todo check if this works well } else if( !empty( $url ) ) { $edit_url = $url; } } } return $edit_url; }
/** * Safely retrieve a key from $_GET variable. * * This is a wrapper for wpv_get_from_array(). See that for more information. * * @param string $key See wpv_getarr(). * @param mixed $default See wpv_getarr(). * @param null|array $valid See wpv_getarr(). * * @return mixed See wpv_getarr(). * * @since 1.8 */ function wpv_getget( $key, $default = '', $valid = null ) { return wpv_getarr( $_GET, $key, $default, $valid ); }
/** * This is a simplified version of wpv_prepare_view_listing_query(). * * Please look up the original function description in order to understand what it does and why. * * It is equivalent to wpv_prepare_view_listing_query( $view_query_mode, 'publish', array(), false, array() ), * with few minor differences: * 1. Returned array contains only published_count and post__in keys. * 2. Trashed posts are omitted entirely (which makes the query a little bit lighter). * 3. For Views settings we don't call $WP_Views->get_views_settings(), which is available only in full version. * Instead we rely on _wpv_settings being present and already containing the 'view-query-mode' key. * According to how this is being accessed in Views embedded now, it shouldn't be a problem. * * @param $view_query_mode string View query mode. @see wpv_prepare_view_listing_query(). * * @return array @see wpv_prepare_view_listing_query(). * * @since 1.8 */ private function prepare_view_listing_query( $view_query_mode ) { global $wpdb; /* Queries rows with post id and value of _wpv_settings meta (or null if * it doesn't exist, notice the LEFT JOIN). */ $query = "SELECT ID AS id, postmeta.meta_value AS view_settings FROM {$wpdb->posts} AS posts LEFT JOIN {$wpdb->postmeta} AS postmeta ON ( posts.ID = postmeta.post_id AND postmeta.meta_key = '_wpv_settings' ) WHERE ( posts.post_type = 'view' AND post_status = 'publish' )"; $views = $wpdb->get_results( $query ); $post_in = array(); // Ensure $view_query_mode is an array if( !is_array( $view_query_mode ) ) { $view_query_mode = array( $view_query_mode ); } /* For each result we need to determine if it's a View or a WPA. If it's what we want, decide by * it's post_status which counter to increment and whether to include into post__in (that means possible result * in the final listing query). */ foreach( $views as $view ) { // Prepare the value of _wpv_settings postmeta in the same way get_post_meta( ..., ..., true ) would. $view_settings = ( null == $view->view_settings ) ? null: maybe_unserialize( $view->view_settings ); $this_view_query_mode = wpv_getarr( $view_settings, 'view-query-mode', '' ); // It is the right kind of View? if ( in_array( $this_view_query_mode, $view_query_mode ) ) { // This is a possible result of the final listing query $post_in[] = $view->id; } } $total_count = count( $post_in ); // If there are no results, we don't want any post to match anything in post__in. if( count( $post_in ) == 0 ) { $post_in[] = 0; } $ret = array( 'published_count' => $total_count, 'post__in' => $post_in ); return $ret; }
/** * 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 ); }
/** * Render the listing page. * * Use decorators (which MUST be all provided by child classes) to render the page. The logic is the same for * all embedded listings. */ function display() { $args = $this->get_args(); // Open page, show the title $this->page_decorator->render_page_start(); $this->title_decorator->render_title( wpv_getarr( $args, 'search', '' ) ); // If there are some items, render a table. Otherwise render a "no items" message. if( $this->item_provider_decorator->has_items( $args ) ) { $this->search_form_decorator->render_search_form( $args ); $items = $this->item_provider_decorator->get_items( $args ); $this->table_decorator->render_table( $items, $args ); } else { $this->noitems_decorator->render_no_items_content( $args ); } // Pagination $this->pagination_decorator->render_pagination( $this->item_provider_decorator->get_total_item_count(), $args ); // Close page $this->page_decorator->render_page_end(); }
/** * API function to create a new View * * @param $args array set of arguments for the new View * 'title' (string) (semi-mandatory) Title for the View * 'settings' (array) (optional) Array compatible with the View settings to override the defaults * 'layout_settings' (array) (optional) Array compatible with the View layout settings to override the defaults * * @return array response of the operation, one of the following * $return['success] = View ID * $return['error'] = 'Error message' * * @since 1.6.0 * * @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. * * @todo once we create a default layout for a View, we need to make sure that: * - the _view_loop_template postmeta is created and updated - DONE * - the fields added to that loop Template are stored in the layout settings - PENDING * - check how Layouts can apply this all to their Views, to create a Bootstrap loop by default - PENDING * * @deprecated Since 1.10. Consider using WPV_View::create() or WPV_WordPress_Archive::create() instead. */ function wpv_create_view( $args ) { $title = wpv_getarr( $args, 'title' ); $creation_args = $args; $view_settings = wpv_getarr( $args, 'settings', array() ); $creation_args['view_settings'] = $view_settings; $query_mode = wpv_getarr( $view_settings, WPV_View_Base::VIEW_SETTINGS_QUERY_MODE, 'normal', array( 'normal', 'archive', 'layouts-loop' ) ); try { if( 'normal' == $query_mode ) { $view = WPV_View::create( $title, $creation_args ); $id = $view->id; } else { $wpa = WPV_WordPress_Archive::create( $title, $creation_args ); $id = $wpa->id; } return array( 'success' => $id ); } catch( WPV_RuntimeExceptionWithMessage $e ) { return array( 'error' => $e->getUserMessage() ); } catch( Exception $e ) { return array( 'error' => __( 'The View could not be created.', 'wpv-views' ) ); } }
/** * Get "query mode", a value determining what kind of object this is. * * Allowed values are 'normal', 'archive' and 'layouts-loop'. The value will be an empty string if the query mode * is not set for this object (which should never happen, though). * * @return string * * @since 1.8 */ protected function _get_query_mode() { $settings = $this->settings; // to avoid PHP notice return wpv_getarr($settings, 'view-query-mode', '', array('normal', 'archive', 'layouts-loop')); }
/** * First time it is called, create the query, save it to $this->query and fill $this->items. * * @param array $args Query arguments. See class description. */ private function ensure_query( $args ) { if( $this->query instanceof WP_Query ) { // We've already done this. return; } // Build arguments for WP_Query $posts_per_page = (int) wpv_getarr( $args, 'items_per_page', 0 ); $posts_per_page = ( 0 == $posts_per_page ) ? WPV_ITEMS_PER_PAGE : $posts_per_page; $paged = (int) wpv_getarr( $args, 'paged', 0 ); $paged = ( 0 == $paged ) ? 1 : $paged; $query_args = array( 'post_type' => 'view-template', 'posts_per_page' => $posts_per_page, 'paged' => $paged, 'order' => wpv_getarr( $args, 'order', 'ASC', array( 'ASC', 'DESC' ) ), 'orderby' => wpv_getarr( $args, 'orderby', 'post_title' ), 'post_status' => 'publish' ); $search = wpv_getarr( $args, 'search', '' ); if( !empty( $search ) ) { $query_args['s'] = $search; } $this->query = new WP_Query( $query_args ); // Create WPV_Content_Template_Embedded objects from WP_Post objects. $this->items = array(); $posts = $this->query->posts; foreach( $posts as $post ) { $this->items[] = new WPV_Content_Template_Embedded( $post ); } }
/** * Get information about currently existing archive loops. * * @param string $loop_type Optional. Desired type of loops. 'native'|'post_type'|'taxonomy'|'all'. Default is 'all'. * @param bool $include_wpa Optional. Determines whether the information about WPA assigned to this loop should be * retrieved (the $wpa element). Default is false. * @param bool $include_ct Optional. Determines whether the information about CT assigned to given post type archive * or taxonomy archive should be retrieved (the $ct element). Default is false. * @param bool $noexclude Optional. If true, no loops of given type will be excluded. Default is false. * * @return array An array of information about native archive loops and loops for custom post types and taxonomies. * Each element is an array representing one loop: * array( * @type string $slug Unique slug (within loop type) as used in other parts of Views. * @type string $display_name Display name for the loop. * @type string $post_type_name For 'post_type' loop type, this will contain "raw" post type slug. * @type string $loop_type 'native'|'post_type'|'taxonomy' * @type int $wpa If $include_wpa is true, this contains an ID of WPA assigned to this loop, or zero if * no WPA is assigned. * @type int $ct If $include_ct is true, this contains an ID of CT assigned to this custom post type * archive or taxonomy archive, or zero if no CT is assigned. This element isn't present for native loops. * @type int $single_ct If $include_ct is true, this contains an ID of CT assigned to single posts of * this custom post type, or zero if no CT is assigned. This element is present only for post types. * ) * * @since 1.7 * * @todo consider implementing caching mechanism */ function get_archive_loops( $loop_type = 'all', $include_wpa = false, $include_ct = false, $noexclude = false ) { global $WPV_settings; switch( $loop_type ) { case 'native': $loops = array( array( 'slug' => 'home-blog-page', 'option' => 'view_home-blog-page', 'loop_type' => 'native', 'display_name' => __( 'Home/Blog', 'wpv-views' ) ), array( 'slug' => 'search-page', 'option' => 'view_search-page', 'loop_type' => 'native', 'display_name' => __( 'Search results', 'wpv-views' ) ), array( 'slug' => 'author-page', 'option' => 'view_author-page', 'loop_type' => 'native', 'display_name' => __( 'Author archives', 'wpv-views' ) ), array( 'slug' => 'year-page', 'option' => 'view_year-page', 'loop_type' => 'native', 'display_name' => __( 'Year archives', 'wpv-views' ) ), array( 'slug' => 'month-page', 'option' => 'view_month-page', 'loop_type' => 'native', 'display_name' => __( 'Month archives', 'wpv-views' ) ), array( 'slug' => 'day-page', 'option' => 'view_day-page', 'loop_type' => 'native', 'display_name' => __( 'Day archives', 'wpv-views' ) ) ); if( $include_wpa ) { $loop_count = count( $loops ); for( $i = 0; $i < $loop_count; ++$i ) { $option = $loops[ $i ]['option']; $loops[ $i ]['wpa'] = isset( $WPV_settings[ $option ] ) ? $WPV_settings[ $option ] : 0; } } return $loops; case 'post_type': $pt_loops = array(); // Only offer loops for post types that already have an archive, unless $noexclude is given $pt_query_args = array( 'public' => true ); if( !$noexclude ) { $pt_query_args['has_archive'] = true; } $post_types = get_post_types( $pt_query_args, 'objects' ); foreach ( $post_types as $post_type ) { if ( $noexclude || !in_array( $post_type->name, array( 'post', 'page', 'attachment' ) ) ) { $loop = array( 'slug' => 'cpt_' . $post_type->name, 'post_type_name' => $post_type->name, 'option' => 'view_cpt_' . $post_type->name, 'display_name' => $post_type->labels->name, 'singular_name' => $post_type->labels->singular_name, 'loop_type' => 'post_type' ); if( $include_wpa ) { $loop['wpa'] = isset( $WPV_settings[ $loop['option'] ] ) ? $WPV_settings[ $loop['option'] ] : 0; } if( $include_ct ) { $loop['ct'] = wpv_getarr( $WPV_settings, "views_template_archive_for_{$post_type->name}", 0 ); $loop['single_ct'] = wpv_getarr( $WPV_settings, "views_template_for_{$post_type->name}", 0 ); } $pt_loops[] = $loop; } } return $pt_loops; case 'taxonomy': $tx_loops = array(); $taxonomies = get_taxonomies( '', 'objects' ); $exclude_tax_slugs = array(); if( !$noexclude ) { $exclude_tax_slugs = apply_filters('wpv_admin_exclude_tax_slugs', $exclude_tax_slugs); } foreach ( $taxonomies as $taxonomy_slug => $taxonomy ) { if ( in_array( $taxonomy_slug, $exclude_tax_slugs ) ) { continue; } // Only show taxonomies with show_ui set to TRUE if ( !$taxonomy->show_ui ) { continue; } $loop = array( 'slug' => $taxonomy->name, 'option' => 'view_taxonomy_loop_' . $taxonomy->name, 'display_name' => $taxonomy->labels->singular_name, 'loop_type' => 'taxonomy' ); if( $include_wpa ) { $loop['wpa'] = isset( $WPV_settings[ $loop['option'] ] ) ? $WPV_settings[ $loop['option'] ] : 0; } if( $include_ct ) { $loop['ct'] = wpv_getarr( $WPV_settings, "views_template_loop_{$taxonomy->name}", 0 ); } $tx_loops[] = $loop; } return $tx_loops; case 'all': default: return array_merge( $this->get_archive_loops( 'native', $include_wpa ), $this->get_archive_loops( 'post_type', $include_wpa ), $this->get_archive_loops( 'taxonomy', $include_wpa ) ); } }
/** * Update one or more properties of a Content Template. * * Note: I've put this into ct-editor.php instead of wpv-admin-ajax.php, because it will most probably * be used *only* by Content Template edit page. No need to further bloat that file with single-purpose * call handlers. If the usage should change in the future, just move the code to a more appropriate place. * --Jan * * Following POST parameters are expected: * - id: Content Template ID * - wpnonce: A valid wpv_ct_{$id}_update_properties_by_{$user_id} nonce. * - properties: An array of objects (that will be decoded from JSON to associative arrays), * each of them representing a property with "name" and "value" keys. * * A WPV_Content_Template object will be instantiated and this function will try to update values of * it's properties as defined in the "properties" POST parameter. The "update transaction" mechansim * is used for this purpose (see WPV_Post_Object_Wrapper::update_transaction() for details * about update logic). * * It always returns JSON object with a 'success' key. If an "generic" error (like invalid * nonce or some invalid arguments) happens, success will be false. Otherwise, if success is true, * there will be a 'data' key containing: * - 'all_succeeded' - boolean * - 'results', an object with property names as keys and booleans indicating that particular * property has been saved successfully (which depends on the logic in WPV_Content_Template), * optionally also containing a "message" property that should be displayed to the user. * * @since 1.9 */ function wpv_ct_update_properties_callback() { // Authentication and validation if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( 'Untrusted user' ); } $ct_id = (int) wpv_getpost( 'id' ); $uid = get_current_user_id(); $nonce_name = "wpv_ct_{$ct_id}_update_properties_by_{$uid}"; if ( ! wp_verify_nonce( wpv_getpost( 'wpnonce' ), $nonce_name ) ) { wp_send_json_error( "Security check ($nonce_name)" ); } $ct = WPV_Content_Template::get_instance( $ct_id ); if( null == $ct ) { wp_send_json_error( 'Invalid Content Template' ); } $properties = wpv_getpost( 'properties' ); if( !is_array( $properties ) ) { wp_send_json_error( 'Invalid arguments (' . print_r( $properties, true ) . ')' ); } // Try to save data as a transaction (all at once or nothing). // Refer to WPV_Post_Object_Wrapper::update_transaction() for details. $transaction_data = array(); foreach( $properties as $property ) { // Missing property value defaults to empty array because of jQuery.ajax issues with empty arrays. // If it's invalid value for the property, it should be rejected during validation - no harm done here. $property_value = wpv_getarr( $property, 'value', array() ); $transaction_data[ $property['name'] ] = $property_value; } // Run the update transaction. // Second parameter is false mostly because vm.processTitleSectionUpdateResults in JS. $transaction_result = $ct->update_transaction( $transaction_data, false ); // Parse the translation result into per-property results that will be returned. $results = array(); foreach( $properties as $property ) { $propery_name = $property['name']; $result = array( 'name' => $propery_name ); if( true == $transaction_result['success'] ) { // Transaction success == all was updated without errors. $result['success'] = true; } else if( true == $transaction_result['partial'] && in_array( $propery_name, $transaction_result['updated_properties'] ) ) { // The least desired situation (but rare) where some properties have been updated // and some haven't. $result['success'] = true; } else { // Failure, for one or the other reason. Look for an optional error message. $result['success'] = false; if( array_key_exists( $propery_name, $transaction_result['error_messages'] ) ) { $error = $transaction_result['error_messages'][ $propery_name ]; $result['message'] = $error['message']; $result['code'] = $error['code']; } } $results[] = $result; } // Report success (because the AJAX call succeeded in general) and attach information // about each property update. wp_send_json_success( array( 'results' => $results ) ); }
/** * Perform basic authentication check. * * Check user capability and nonce. Dies with an error message (wp_json_error() by default) if the authentization * is not successful. * * @param string $nonce_name Name of the nonce that should be verified. * @param array $args Arguments ( * @type string $nonce_parameter Name of the parameter containing nonce value. * Optional, defaults to "wpnonce". * @type string $parameter_source Determines where the function should look for the nonce parameter. * Allowed values are 'get' and 'post'. Optional, defaults to 'post'. * @type string $capability_needed Capability that user has to have in order to pass the check. * Optional, default is "manage_options". * @type string $type_of_death How to indicate failure: * - 'die': The error message will be echoed as a simple string. Default behaviour. * - 'false': Do not die, just return false. * - 'message': Call wp_json_error() and pass message as data. * - 'data': Call wp_json_error with array( 'type' => 'capability'|'nonce', 'message' => $error_message ) * Optional, default is 'die'. * ) * * @return bool|void * * @since 1.9 */ function wpv_ajax_authenticate( $nonce_name, $args = array() ) { // Read arguments $type_of_death = wpv_getarr( $args, 'type_of_death', 'die', array( 'die', 'false', 'message', 'data' ) ); $nonce_parameter = wpv_getarr( $args, 'nonce_parameter', 'wpnonce' ); $capability_needed = wpv_getarr( $args, 'capability_needed', 'manage_options' ); $parameter_source_name = wpv_getarr( $args, 'parameter_source', 'post', array( 'get', 'post' ) ); $parameter_source = ( $parameter_source_name == 'get' ) ? $_GET : $_POST; $is_error = false; $error_message = null; $error_type = null; // Check permissions if ( ! current_user_can( $capability_needed ) ) { $error_message = __( 'You do not have permissions for that.', 'wpv-views' ); $error_type = 'capability'; $is_error = true; } // Check nonce if ( !$is_error && !wp_verify_nonce( wpv_getarr( $parameter_source, $nonce_parameter, '' ), $nonce_name ) ) { $error_message = __( 'Your security credentials have expired. Please reload the page to get new ones.', 'wpv-views' ); $error_type = 'nonce'; $is_error = true; } if( $is_error ) { switch( $type_of_death ) { case 'message': wp_send_json_error( $error_message ); break; case 'data': wp_send_json_error( array( 'type' => $error_type, 'message' => $error_message ) ); break; case 'false': return false; case 'die': default: die( $error_message ); break; } } return true; }
/** * Render the search form. * * @param array $args */ public function render_search_form( $args ) { $search_term = wpv_getarr( $args, 'search', '' ); $hidden_fields = array( 'page' => wpv_getarr( $args, 'page', '' ), 'items_per_page' => (int) wpv_getarr( $args, 'items_per_page', '' ), 'paged' => 1 ); $hidden_fields_flat = array(); foreach( $hidden_fields as $field_name => $field_value ) { $hidden_fields_flat[] = sprintf( '<input type="hidden" name="%s" value="%s" />', $field_name, $field_value ); } $hidden_fields = implode( $hidden_fields_flat ); ?> <div class="alignright"> <form id="posts-filter" method="get"> <?php echo $hidden_fields; ?> <p class="search-box"> <label class="screen-reader-text" for="post-search-input"><?php echo $this->label; ?></label> <input type="search" id="post-search-input" name="search" value="<?php echo $search_term; ?>" /> <input type="submit" name="" id="search-submit" class="button" value="<?php echo htmlentities( $this->label, ENT_QUOTES ); ?>" /> </p> </form> </div> <?php }
/** * Duplicate a loop template of a View and update references to it. * * @todo detailed description * * @param array $new_postmeta_values Array of postmeta of the View. * @param int $new_post_id ID of the View. * @param string $new_post_title Post title of the View. * * @return array Updated array of postmeta values of the View. */ private function duplicate_loop_template( $new_postmeta_values, $new_post_id, $new_post_title ) { // This will throw an exception if the original CT can't be accessed $original_ct = new WPV_Content_Template( $this->loop_template_id ); // Clone the Content Template acting as a Loop template $cloned_ct = $original_ct->clone_this( sprintf( __( 'Loop item in %s', 'wpv-views' ), $new_post_title ), true ); if( null == $cloned_ct ) { throw new RuntimeException( 'unable to clone loop template' ); } // Cloning was successful. // Create reference from new View to new Loop template. $new_postmeta_values[ WPV_View_Base::POSTMETA_LOOP_TEMPLATE_ID ] = $cloned_ct->id; // Create reference from new Loop template to new View. $cloned_ct->loop_output_id = $new_post_id; // Process inline Content templates if there are any. // @todo can this be done cleaner? $inline_templates = wpv_getarr( $new_postmeta_values['_wpv_layout_settings'], 'included_ct_ids', '' ); if ( !empty( $inline_templates ) ) { $inline_templates = explode( ',', $inline_templates ); // Go through all inline templates (referenced in original View) and if we find a reference // to original Loop template, we will replace it with new one. foreach ( $inline_templates as $inline_template_key => $inline_template_id ) { if ( $inline_template_id == $this->loop_template_id ) { // Replace with new Loop template. $inline_templates[ $inline_template_key ] = $cloned_ct->id; } } // Update the array of inline Content templates. $new_postmeta_values['_wpv_layout_settings']['included_ct_ids'] = implode( ',', $inline_templates ); } // Replace name of the old Loop template with new name in Loop output. $loop_output = wpv_getarr( $new_postmeta_values['_wpv_layout_settings'], 'layout_meta_html', '' ); if ( !empty( $loop_output ) ) { // Search and replace Loop template titles // todo we also need to check for slugs // todo consider allways replacing by slug, title could contain quotes at this point $new_loop_output = str_replace( sprintf( 'view_template="%s"', $original_ct->title ), sprintf( 'view_template="%s"', sanitize_text_field( $cloned_ct->title ) ), $loop_output ); // Save new value $new_postmeta_values['_wpv_layout_settings']['layout_meta_html'] = $new_loop_output; } return $new_postmeta_values; }
/** * Render pagination controls. * * @param int $total_item_count Total count of items disregarding pagination. * @param array $args Arguments for displaying the page. Currently only 'order' and 'orderby' are supported. */ function render_pagination( $total_item_count, $args ) { $pages_count = ( $this->items_per_page > 0 ) ? ceil( (int) $total_item_count / $this->items_per_page ) : -1; if ( $pages_count > 1 ) { // Calculate offsets of first and last items displayed $items_start = ( ( ( $this->paged - 1 ) * $this->items_per_page ) + 1 ); $items_end = ( ( ( $this->paged - 1 ) * $this->items_per_page ) + $this->items_per_page ); if ( $this->paged == $pages_count ) { $items_end = $total_item_count; } // Array of URL parameters to preserve $mod_url = array( 'page' => $this->page_name, 'orderby' => wpv_getarr( $args, 'orderby', '' ), 'order' => wpv_getarr( $args, 'order', '' ), 'search' => wpv_getarr( $args, 'search', '' ), 'items_per_page' => $this->items_per_page ); ?> <div class="wpv-listing-pagination tablenav"> <div class="tablenav-pages"> <span class="displaying-num"> <?php echo __( 'Displaying ', 'wpv-views' ) . "$items_start - $items_end" . __( ' of ', 'wpv-views' ) . $total_item_count; ?> </span> <?php // "Previous page" link if ( $this->paged > 1 ) { $previous_page_args = array_merge( $mod_url, array( 'paged' => $this->paged - 1 ) ); printf( '<a href="%s" class="wpv-filter-navigation-link">« %s</a>', wpv_maybe_add_query_arg( $previous_page_args, admin_url( 'admin.php' ) ), __( 'Previous page', 'wpv-views' ) ); } // Page number links for ( $i = 1; $i <= $pages_count; $i++ ) { $classes = ( $this->paged == $i ) ? 'active current' : ''; // If this is a last page, we'll add an argument indicating that. $is_last_page = ( $i == $pages_count ); $page_number_args = array_merge( $mod_url, array( 'paged' => $i, 'last_page' => $is_last_page ? '1' : '' ) ); printf( '<a href="%s" class="%s">%s</a>', wpv_maybe_add_query_arg( $page_number_args, admin_url( 'admin.php' ) ), $classes, $i ); } // "Next page" link if ( $this->paged < $pages_count ) { $is_last_page = ( ( $this->paged + 1 ) == $pages_count ); $next_page_args = array_merge( $mod_url, array( 'paged' => $this->paged + 1, 'last_page' => $is_last_page ? '1' : '' ) ); printf( '<a href="%s" class="wpv-filter-navigation-link">%s »</a>', wpv_maybe_add_query_arg( $next_page_args, admin_url( 'admin.php' ) ), __( 'Next page','wpv-views' ) ); } // Items per page switcher _e( 'Items per page', 'wpv-views' ); ?> <select class="js-items-per-page"> <option value="10" <?php selected( $this->items_per_page == '10' ); ?> >10</option> <option value="20" <?php selected( $this->items_per_page == '20' ); ?> >20</option> <option value="50" <?php selected( $this->items_per_page == '50' ); ?> >50</option> </select> <a href="#" class="js-wpv-display-all-items"><?php _e( 'Display all items', 'wpv-views' ); ?></a> </div> </div> <?php } else if ( ( WPV_ITEMS_PER_PAGE != $this->items_per_page ) && ( $total_item_count > WPV_ITEMS_PER_PAGE ) ) { // We have only one page, non-default items_per_page setting and more items than we can show in a default setting. // Only show a link to reset to defaults. ?> <div class="wpv-listing-pagination tablenav"> <div class="tablenav-pages"> <a href="#" class="js-wpv-display-default-items"><?php _e( 'Display 20 items per page', 'wpv-views' ); ?></a> </div> </div> <?php } ?> <?php }