-
Erick Hitter authored
Add options screen and filters for number of pages. Refactor how Quicktags are inserted to support variable number of pages. Fixes #2.
Erick Hitter authoredAdd options screen and filters for number of pages. Refactor how Quicktags are inserted to support variable number of pages. Fixes #2.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
automatically-paginate-posts.php 11.89 KiB
<?php
/*
Plugin Name: Automatically Paginate Posts
Plugin URI:
Description: Automatically inserts the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content.
Version: 0.1
Author: Erick Hitter (Oomph, Inc.)
Author URI: http://www.thinkoomph.com/
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Automatically_Paginate_Posts {
/**
* Class variables
*/
private $post_types;
private $post_types_default = array( 'post' );
private $num_pages;
private $num_pages_default = 2;
//Ensure option names match values in this::uninstall
private $option_name_post_types = 'autopaging_post_types';
private $option_name_num_pages = 'autopaging_num_pages';
private $meta_key_disable_autopaging = '_disable_autopaging';
/**
* Register actions and filters
*
* @uses add_action, register_uninstall_hook, add_filter
* @return null
*/
public function __construct() {
//Filters
add_action( 'init', array( $this, 'action_init' ) );
//Admin settings
register_uninstall_hook( __FILE__, array( 'Automatically_Paginate_Posts', 'uninstall' ) );
add_filter( 'plugin_action_links', array( $this, 'filter_plugin_action_links' ), 10, 2 );
add_action( 'admin_init', array( $this, 'action_admin_init' ) );
//Post-type settings
add_action( 'add_meta_boxes', array( $this, 'action_add_meta_boxes' ) );
add_action( 'save_post', array( $this, 'action_save_post' ) );
add_filter( 'the_posts', array( $this, 'filter_the_posts' ) );
}
/**
* Set post types this plugin can act on, either from Reading page or via filter
* Also sets default number of pages to break content over, either from Reading page or via filter
*
* @uses apply_filters, get_option
* @action init
* @return null
*/
public function action_init() {
//Post types
$this->post_types = apply_filters( 'autopaging_post_types', get_option( $this->option_name_post_types, $this->post_types_default ) );
//Number of pages to break over
$this->num_pages = absint( apply_filters( 'autopaging_num_pages_default', get_option( $this->option_name_num_pages, $this->num_pages_default ) ) );
if ( 0 == $this->num_pages )
$this->num_pages = 2;
}
/**
* Delete plugin settings when uninstalled.
* Options names here must match those defined in Class Variables section above.
*
* @uses delete_option
* @action uninstall
* @return null
*/
public function uninstall() {
delete_option( 'autopaging_post_types' );
delete_option( 'autopaging_num_pages' );
}
/**
* Add settings link to plugin's row actions
*
* @param array $actions
* @param string $file
* @filter plugin_action_links,
*/
public function filter_plugin_action_links( $actions, $file ) {
if ( false !== strpos( $file, basename( __FILE__ ) ) )
$actions[ 'settings' ] = '<a href="' . admin_url( 'options-reading.php' ) . '">Settings</a>';
return $actions;
}
/**
* Register settings and settings sections
* Settings appear on the Reading page
*
* @uses register_setting, add_settings_section, add_settings_field
* @action admin_init
* @return null
*/
public function action_admin_init() {
register_setting( 'reading', $this->option_name_post_types, array( $this, 'sanitize_supported_post_types' ) );
register_setting( 'reading', $this->option_name_num_pages, array( $this, 'sanitize_num_pages' ) );
add_settings_section( 'autopaging', 'Automatically Paginate Posts', '__return_false', 'reading' );
add_settings_field( 'autopaging-post-types', __( 'Supported post types:', 'autopaging' ), array( $this, 'settings_field_post_types' ), 'reading', 'autopaging' );
add_settings_field( 'autopaging-num-pages', __( 'Number of pages to split content into:', 'autopaging' ), array( $this, 'settings_field_num_pages' ), 'reading', 'autopaging' );
}
/**
* Render post types options
*
* @uses get_post_types, get_option, esc_attr, checked, esc_html
* @return string
*/
public function settings_field_post_types() {
//Get all public post types
$post_types = get_post_types( array(
'public' => true
), 'objects' );
//Remove attachments
unset( $post_types[ 'attachment' ] );
//Current settings
$current_types = get_option( $this->option_name_post_types, $this->post_types_default );
//Output checkboxes
foreach ( $post_types as $post_type => $atts ) :
?>
<input type="checkbox" name="<?php echo esc_attr( $this->option_name_post_types ); ?>[]" id="post-type-<?php echo esc_attr( $post_type ); ?>" value="<?php echo esc_attr( $post_type ); ?>"<?php checked( in_array( $post_type, $current_types ) ); ?> /> <label for="post-type-<?php echo esc_attr( $post_type ); ?>"><?php echo esc_html( $atts->label ); ?><br />
<?php
endforeach;
}
/**
* Sanitize post type inputs
*
* @param array $post_types_checked
* @uses get_post_types
* @return array
*/
public function sanitize_supported_post_types( $post_types_checked ) {
$post_types_sanitized = array();
//Ensure that only existing, public post types are submitted as valid options
if ( is_array( $post_types_checked ) && ! empty( $post_types_checked ) ) {
//Get all public post types
$post_types = get_post_types( array(
'public' => true
) );
//Remove attachments
unset( $post_types[ 'attachment' ] );
//Check input post types against those registered with WordPress and made available to this plugin
foreach ( $post_types_checked as $post_type ) {
if ( array_key_exists( $post_type, $post_types ) )
$post_types_sanitized[] = $post_type;
}
}
return $post_types_sanitized;
}
/**
* Render dropdown for choosing number of pages to break content over
*
* @uses get_option, apply_filters, esc_attr, selected
* @return string
*/
public function settings_field_num_pages() {
$num_pages = get_option( $this->option_name_num_pages, $this->num_pages_default );
$max_pages = apply_filters( 'autopaging_max_num_pages', 10 );
?>
<select name="<?php echo esc_attr( $this->option_name_num_pages ); ?>">
<?php for( $i = 2; $i <= $max_pages; $i++ ) : ?>
<option value="<?php echo intval( $i ); ?>"<?php selected( (int) $i, (int) $num_pages ); ?>><?php echo intval( $i ); ?></option>
<?php endfor; ?>
</select>
<?php
}
/**
* Sanitize number of pages input
*
* @param int $num_pages
* @uses apply_filters
* @return int
*/
public function sanitize_num_pages( $num_pages ) {
return max( 2, min( intval( $num_pages ), apply_filters( 'autopaging_max_num_pages', 10 ) ) );
}
/**
* Add autopaging metabox
*
* @uses this::get_option, add_metabox
* @action add_meta_box
* @return null
*/
public function action_add_meta_boxes() {
foreach ( $this->post_types as $post_type ) {
add_meta_box( 'autopaging', 'Post Autopaging', array( $this, 'meta_box_autopaging' ), $post_type, 'side' );
}
}
/**
* Render autopaging metabox
*
* @param object $post
* @uses esc_attr, checked, wp_nonce_field
* @return string
*/
public function meta_box_autopaging( $post ) {
?>
<p>
<input type="checkbox" name="<?php echo esc_attr( $this->meta_key_disable_autopaging ); ?>" id="<?php echo esc_attr( $this->meta_key_disable_autopaging ); ?>_checkbox" value="1"<?php checked( (bool) get_post_meta( $post->ID, $this->meta_key_disable_autopaging, true ) ); ?> /> <label for="<?php echo esc_attr( $this->meta_key_disable_autopaging ); ?>_checkbox">Disable autopaging for this post?</label>
</p>
<p class="description">Check the box above to prevent this post from automatically being split over two pages.</p>
<p class="description">Note that if the <code><!--nextpage--></code> Quicktag is used to manually page this post, automatic paging won't be applied, regardless of the setting above.</p>
<?php
wp_nonce_field( $this->meta_key_disable_autopaging, $this->meta_key_disable_autopaging . '_wpnonce' );
}
/**
* Save autopaging metabox
*
* @param int $post_id
* @uses DOING_AUTOSAVE, wp_verify_nonce, update_post_meta, delete_post_meta
* @action save_post
* @return null
*/
public function action_save_post( $post_id ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
if ( isset( $_POST[ $this->meta_key_disable_autopaging . '_wpnonce' ] ) && wp_verify_nonce( $_POST[ $this->meta_key_disable_autopaging . '_wpnonce' ], $this->meta_key_disable_autopaging ) ) {
$disable = isset( $_POST[ $this->meta_key_disable_autopaging ] ) ? true : false;
if ( $disable )
update_post_meta( $post_id, $this->meta_key_disable_autopaging, true );
else
delete_post_meta( $post_id, $this->meta_key_disable_autopaging );
}
}
/**
* Automatically page posts by injecting <!--nextpage--> Quicktag.
* Only applied if the post type matches specified options and post doesn't already contain the Quicktag.
*
* @param array $posts
* @uses is_admin, get_post_meta, absint, apply_filters
* @filter the_posts
* @return array
*/
public function filter_the_posts( $posts ) {
if ( ! is_admin() ) {
foreach( $posts as $the_post ) {
if ( in_array( $the_post->post_type, $this->post_types ) && ! preg_match( '#<!--nextpage-->#i', $the_post->post_content ) && ! (bool) get_post_meta( $the_post->ID, $this->meta_key_disable_autopaging, true ) ) {
//In-time filtering of number of pages to break over, based on post data. If value is less than 2, nothing should be done.
$num_pages = absint( apply_filters( 'autopaging_num_pages', absint( $this->num_pages ), $the_post ) );
if ( $num_pages < 2 )
continue;
//Start with post content, but alias to protect the raw content.
$content = $the_post->post_content;
//Normalize post content to simplify paragraph counting and automatic paging. Accounts for content that hasn't been cleaned up by TinyMCE.
$content = preg_replace( '#<p>(.+?)</p>#i', "$1\r\n\r\n", $content );
$content = preg_replace( '#<br(\s*/)?>#i', "\r\n", $content );
//Count paragraphs
$count = preg_match_all( '#\r\n\r\n#', $content, $matches );
//Keep going, if we have something to count.
if ( is_int( $count ) && 0 < $count ) {
//Explode content at double line breaks
$content = explode( "\r\n\r\n", $content );
//Count number of paragraphs content was exploded to
$count = count( $content );
//Determine when to insert Quicktag
$insert_every = $count / $num_pages;
$insert_every_rounded = round( $insert_every );
//If number of pages is greater than number of paragraphs, put each paragraph on its own page
if ( $num_pages > $count )
$insert_every_rounded = 1;
//Set initial counter position.
$i = $count - 1 == $num_pages ? 2 : 1;
//Loop through content pieces and append Quicktag as is appropriate
foreach( $content as $key => $value ) {
if ( $key + 1 == $count )
break;
if ( ( $key + 1 ) == ( $i * $insert_every_rounded ) ) {
$content[ $key ] = $content[ $key ] . '<!--nextpage-->';
$i++;
}
}
//Reunite content
$content = implode( "\r\n\r\n", $content );
//And, overwrite the original content
$the_post->post_content = $content;
//Clean up
unset( $count );
unset( $insert_every );
unset( $insert_every_rounded );
unset( $key );
unset( $value );
}
//Lastly, clean up.
unset( $num_pages );
unset( $content );
unset( $count );
}
}
}
return $posts;
}
}
new Automatically_Paginate_Posts;
?>