> Read the accompanying readme.txt file for instructions and documentation. * =>> Also, visit the plugin's homepage for additional information and updates. * =>> Or visit: https://wordpress.org/plugins/admin-post-navigation/ * * @package Admin_Post_Navigation * @author Scott Reilly * @version 2.0 */ /* * TODO: * - Add ability for navigation to save current post before navigating away. * - Hide screen option checkbox for metabox if metabox is being hidden * - Add screen option allowing user selection of post navigation order * - Add more unit tests * - Add dropdown to post nav links to allow selecting different types of things * to navigate to (e.g. next draft (if looking at a draft), next in category X) */ /* Copyright (c) 2008-2016 by Scott Reilly (aka coffee2code) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ defined( 'ABSPATH' ) or die(); if ( ! class_exists( 'c2c_AdminPostNavigation' ) ) : class c2c_AdminPostNavigation { /** * Translated text for previous link. * * @access private * * @var string */ private static $prev_text = ''; /** * Translated text for next link. * * @access private * * @var string */ private static $next_text = ''; /** * Default post statuses for navigation. * * Filterable later. * * @access private * * @var array */ private static $post_statuses = array( 'draft', 'future', 'pending', 'private', 'publish', 'inherit' ); /** * Post status query fragment. * * @access private * * @var string */ private static $post_statuses_sql = ''; /** * Returns version of the plugin. * * @since 1.7 */ public static function version() { return '2.0'; } /** * Class constructor: initializes class variables and adds actions and filters. */ public static function init() { add_action( 'load-post.php', array( __CLASS__, 'register_post_page_hooks' ) ); } /** * Filters/actions to hook on the admin post.php page. * * @since 1.7 */ public static function register_post_page_hooks() { // Load textdomain. load_plugin_textdomain( 'admin-post-navigation' ); // Set translatable strings. self::$prev_text = apply_filters( 'c2c_admin_post_navigation_prev_text', __( '← Previous', 'admin-post-navigation' ) ); self::$next_text = apply_filters( 'c2c_admin_post_navigation_next_text', __( 'Next →', 'admin-post-navigation' ) ); // Register hooks. add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts_and_styles' ) ); add_action( 'do_meta_boxes', array( __CLASS__, 'do_meta_box' ), 10, 3 ); } /** * Enqueues scripts and stylesheets on post edit admin pages. * * @since 2.0 */ public static function admin_enqueue_scripts_and_styles() { wp_register_style( 'admin-post-navigation-admin', plugins_url( 'assets/admin-post-navigation.css', __FILE__ ), array(), self::version() ); wp_enqueue_style( 'admin-post-navigation-admin' ); wp_register_script( 'admin-post-navigation-admin', plugins_url( 'assets/admin-post-navigation.js', __FILE__ ), array( 'jquery' ), self::version(), true ); // Localize script. wp_localize_script( 'admin-post-navigation-admin', 'c2c_apn', array( 'tag' => version_compare( $GLOBALS['wp_version'], '4.3', '>=' ) ? 'h1' : 'h2', ) ); wp_enqueue_script( 'admin-post-navigation-admin' ); } /** * Register meta box. * * By default, the navigation is present for all post types. Filter * 'c2c_admin_post_navigation_post_types' to limit its use. * * @param string $post_type The post type. * @param string $type The mode for the meta box (normal, advanced, or side). * @param WP_Post $post The post. */ public static function do_meta_box( $post_type, $type, $post ) { $post_types = apply_filters( 'c2c_admin_post_navigation_post_types', get_post_types() ); if ( ! in_array( $post_type, $post_types ) ) { return; } $post_statuses = (array) apply_filters( 'c2c_admin_post_navigation_post_statuses', self::$post_statuses, $post_type, $post ); if ( $post_statuses ) { foreach( $post_statuses as $i => $v ) { $GLOBALS['wpdb']->escape_by_ref( $v ); $post_statuses[ $i ] = $v; } self::$post_statuses_sql = "'" . implode( "', '", $post_statuses ) . "'"; } if ( in_array( $post->post_status, $post_statuses ) ) { add_meta_box( 'adminpostnav', sprintf( __( '%s Navigation', 'admin-post-navigation' ), ucfirst( $post_type ) ), array( __CLASS__, 'add_meta_box' ), $post_type, 'side', 'core' ); } } /** * Adds the content for the post navigation meta_box. * * @param object $object * @param array $box */ public static function add_meta_box( $object, $box ) { $display = ''; $context = self::_get_post_type_label( $object->post_type ); $prev = self::previous_post(); if ( $prev ) { $post_title = the_title_attribute( array( 'echo' => false, 'post' => $prev->ID ) ); $display .= '' . self::$prev_text . ''; } $next = self::next_post(); if ( $next ) { if ( $display ) { $display .= ' '; } $post_title = the_title_attribute( array( 'echo' => false, 'post' => $next->ID ) ); $display .= '' . self::$next_text . ''; } $display = '' . $display . ''; $display = apply_filters( 'admin_post_nav', $display ); /* Deprecated as of v1.5 */ echo apply_filters( 'c2c_admin_post_navigation_display', $display ); } /** * Gets label for post type. * * @since 1.7 * * @param string $post_type The post_type. * @return string The label for the post_type. */ public static function _get_post_type_label( $post_type ) { $label = $post_type; $post_type_object = get_post_type_object( $label ); if ( is_object( $post_type_object ) ) { $label = $post_type_object->labels->singular_name; } return strtolower( $label ); } /** * Returns the previous or next post relative to the current post. * * Currently, a previous/next post is determined by the next lower/higher * valid post based on relative sequential post ID and which the user can * edit. Other post criteria such as post type (draft, pending, etc), * publish date, post author, category, etc, are not taken into * consideration when determining the previous or next post. * * @param string $type Optional. Either '<' or '>', indicating previous or next post, respectively. Default '<'. * @param int $offset Optional. Offset. Primarily for internal, self-referencial use. Default 0. * @param int $limit Optional. Number of posts to get in the query. Not just the next post because a few might * need to be traversed to find a post the user has the capability to edit. Default 15. * @return WP_Post|false */ public static function query( $type = '<', $offset = 0, $limit = 15 ) { global $post_ID, $wpdb; if ( $type != '<' ) { $type = '>'; } $offset = (int) $offset; $limit = (int) $limit; $post = get_post( $post_ID ); if ( ! $post || ! self::$post_statuses_sql ) { return false; } $post_type = esc_sql( get_post_type( $post_ID ) ); $sql = "SELECT ID, post_title FROM $wpdb->posts WHERE post_type = '$post_type' AND post_status IN (" . self::$post_statuses_sql . ') '; // Determine order. if ( is_post_type_hierarchical( $post_type ) ) { $orderby = 'post_title'; } else { $orderby = 'post_date'; } $default_orderby = $orderby; // Restrict orderby to actual post fields. $orderby = esc_sql( apply_filters( 'c2c_admin_post_navigation_orderby', $orderby, $post_type ) ); if ( ! in_array( $orderby, array( 'comment_count', 'ID', 'menu_order', 'post_author', 'post_content', 'post_content_filtered', 'post_date', 'post_excerpt', 'post_date_gmt', 'post_mime_type', 'post_modified', 'post_modified_gmt', 'post_name', 'post_parent', 'post_status', 'post_title', 'post_type' ) ) ) { $orderby = $default_orderby; } $sql .= "AND {$orderby} {$type} '{$post->$orderby}' "; $sort = $type == '<' ? 'DESC' : 'ASC'; $sql .= "ORDER BY {$orderby} {$sort} LIMIT {$offset}, {$limit}"; // Find the first post the user can actually edit. $posts = $wpdb->get_results( $sql ); $result = false; if ( $posts ) { foreach ( $posts as $post ) { if ( current_user_can( 'edit_post', $post->ID ) ) { $result = $post; break; } } if ( ! $result ) { // The fetch did not yield a post editable by user, so query again. $offset += $limit; // Double the limit each time (if haven't found a post yet, chances are we may not, so try to get through posts quicker). $limit += $limit; return self::query( $type, $offset, $limit ); } } return $result; } /** * Returns the next post relative to the current post. * * A convenience function that calls query(). * * @return object The next post object. */ public static function next_post() { return self::query( '>' ); } /** * Returns the previous post relative to the current post. * * A convenience function that calls query(). * * @return object The previous post object. */ public static function previous_post() { return self::query( '<' ); } } // end c2c_AdminPostNavigation c2c_AdminPostNavigation::init(); endif; // end if !class_exists()