Changeset 2749430
- Timestamp:
- 06/29/2022 04:36:18 AM (3 years ago)
- Location:
- automatically-paginate-posts
- Files:
-
- 12 added
- 6 edited
- 1 copied
-
tags/0.3 (copied) (copied from automatically-paginate-posts/trunk)
-
tags/0.3/assets (added)
-
tags/0.3/assets/build (added)
-
tags/0.3/assets/build/index.asset.php (added)
-
tags/0.3/assets/build/index.js (added)
-
tags/0.3/automatically-paginate-posts.php (modified) (15 diffs)
-
tags/0.3/inc (added)
-
tags/0.3/inc/class-block-editor.php (added)
-
tags/0.3/languages/automatically-paginate-posts.pot (modified) (4 diffs)
-
tags/0.3/readme.txt (modified) (3 diffs)
-
trunk/assets (added)
-
trunk/assets/build (added)
-
trunk/assets/build/index.asset.php (added)
-
trunk/assets/build/index.js (added)
-
trunk/automatically-paginate-posts.php (modified) (15 diffs)
-
trunk/inc (added)
-
trunk/inc/class-block-editor.php (added)
-
trunk/languages/automatically-paginate-posts.pot (modified) (4 diffs)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
automatically-paginate-posts/tags/0.3/automatically-paginate-posts.php
r869001 r2749430 1 <?php 2 /* 3 Plugin Name: Automatically Paginate Posts 4 Plugin URI: http://www.oomphinc.com/plugins-modules/automatically-paginate-posts/ 5 Description: Automatically inserts the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content. 6 Version: 0.2 7 Author: Erick Hitter & Oomph, Inc. 8 Author URI: http://www.oomphinc.com/ 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName 2 /** 3 * Automatically inserts the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content. 4 * 5 * Plugin Name: Automatically Paginate Posts 6 * Plugin URI: http://www.oomphinc.com/plugins-modules/automatically-paginate-posts/ 7 * Description: Automatically inserts the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content. 8 * Version: 0.3 9 * Author: Erick Hitter & Oomph, Inc. 10 * Author URI: http://www.oomphinc.com/ 11 * Text Domain: autopaging 12 * Domain Path: /languages/ 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation; either version 2 of the License, or 17 * (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, write to the Free Software 26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 27 * 28 * @package automatically-paginate-posts 29 */ 30 31 require_once dirname( __FILE__ ) . '/inc/class-block-editor.php'; 32 33 /** 34 * Class Automatically_Paginate_Posts. 35 */ 25 36 class Automatically_Paginate_Posts { 26 37 /** 27 * Class variables 38 * WordPress Quicktag that creates pagination. 39 */ 40 const QUICKTAG = '<!--nextpage-->'; 41 42 /** 43 * String length of nextpage Quicktag. 44 */ 45 const QUICKTAG_LENGTH = 15; 46 47 /** 48 * Supported post types. 49 * 50 * @var array 28 51 */ 29 52 private $post_types; 53 54 /** 55 * Default supported post types. 56 * 57 * @var array 58 */ 30 59 private $post_types_default = array( 'post' ); 31 60 61 /** 62 * Desired number of pages to split to. 63 * 64 * @var int 65 */ 32 66 private $num_pages; 67 68 /** 69 * Method for splitting content, either words or desired number of pages. 70 * 71 * @var string 72 */ 33 73 private $paging_type_default = 'pages'; 34 private $num_pages_default = 2; 35 private $num_words_default = ''; 36 74 75 /** 76 * Default number of pages to split to. 77 * 78 * @var int 79 */ 80 private $num_pages_default = 2; 81 82 /** 83 * Desired number of words per pages. 84 * 85 * @var int 86 */ 87 private $num_words; 88 89 /** 90 * Default number of words to split on. 91 * 92 * @var string|int 93 */ 94 private $num_words_default = ''; 95 96 /** 97 * When splitting by word counts, these blocks are considered. Tags are 98 * stripped and remaining content is counted. 99 * 100 * @var array 101 */ 102 private $supported_block_types_for_word_counts = array( 103 'core/paragraph', 104 ); 105 106 /** 107 * Allowed split types. 108 * 109 * @var array 110 */ 37 111 private $paging_types_allowed = array( 'pages', 'words' ); 38 112 39 // Ensure option names match values in this::uninstall 40 private $option_name_post_types = 'autopaging_post_types'; 113 /** 114 * Supported-post-types option name. 115 * 116 * @var string 117 */ 118 private $option_name_post_types = 'autopaging_post_types'; 119 120 /** 121 * Split-type option name. 122 * 123 * @var string 124 */ 41 125 private $option_name_paging_type = 'pages'; 42 private $option_name_num_pages = 'autopaging_num_pages'; 43 private $option_name_num_words = 'autopaging_num_words'; 44 126 127 /** 128 * Option holding number of pages to split to. 129 * 130 * @var string 131 */ 132 private $option_name_num_pages = 'autopaging_num_pages'; 133 134 /** 135 * Option holding number of words to split on. 136 * 137 * @var string 138 */ 139 private $option_name_num_words = 'autopaging_num_words'; 140 141 /** 142 * Meta key used to indicate that a post shouldn't be automatically split. 143 * 144 * @var string 145 */ 45 146 private $meta_key_disable_autopaging = '_disable_autopaging'; 46 147 47 148 /** 48 * Register actions and filters 49 * 50 * @uses add_action, register_uninstall_hook, add_filter 51 * @return null 149 * Class constructor. 150 * 151 * @return void 52 152 */ 53 153 public function __construct() { 54 //Filters 154 $this->setup_hooks(); 155 156 new Automatically_Paginate_Posts\Block_Editor( $this ); 157 } 158 159 /** 160 * Register hooks. 161 * 162 * @return void 163 */ 164 protected function setup_hooks() { 165 add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) ); 55 166 add_action( 'init', array( $this, 'action_init' ) ); 56 167 57 // Admin settings168 // Admin settings. 58 169 register_uninstall_hook( __FILE__, array( 'Automatically_Paginate_Posts', 'uninstall' ) ); 59 170 add_filter( 'plugin_action_links', array( $this, 'filter_plugin_action_links' ), 10, 2 ); 60 171 add_action( 'admin_init', array( $this, 'action_admin_init' ) ); 61 172 62 // Post-type settings173 // Post-type settings. 63 174 add_action( 'add_meta_boxes', array( $this, 'action_add_meta_boxes' ) ); 64 175 add_action( 'save_post', array( $this, 'action_save_post' ) ); 65 176 add_filter( 'the_posts', array( $this, 'filter_the_posts' ) ); 66 177 } 67 68 /** 69 * Set post types this plugin can act on, either from Reading page or via filter 70 * Also sets default number of pages to break content over, either from Reading page or via filter 178 /** 179 * Allow access to meta key for disabling autopaging. 180 * 181 * @param string $name Property name. 182 * @return array|string|null 183 */ 184 public function __get( $name ) { 185 if ( 'meta_key' === $name ) { 186 return $this->meta_key_disable_autopaging; 187 } 188 189 if ( 'post_types' === $name ) { 190 if ( ! did_action( 'init' ) ) { 191 _doing_it_wrong( 192 __METHOD__, 193 esc_html__( 194 'Post types can only be retrieved after the "init" hook.', 195 'autopaging' 196 ), 197 '0.3' 198 ); 199 200 return null; 201 } 202 203 return $this->post_types; 204 } 205 206 return null; 207 } 208 209 /** 210 * Prevent setting properties. 211 * 212 * @param string $name Property name. 213 * @param string $value Property value. 214 * @return false 215 */ 216 public function __set( $name, $value ) { 217 return false; 218 } 219 220 /** 221 * Indicate if a property is set. 222 * 223 * @param string $name Property name. 224 * @return bool 225 */ 226 public function __isset( $name ) { 227 if ( 'meta_key' === $name ) { 228 return true; 229 } 230 231 if ( 'post_types' === $name ) { 232 return did_action( 'init' ); 233 } 234 235 return false; 236 } 237 238 /** 239 * Load plugin translations. 240 * 241 * @return void 242 */ 243 public function load_textdomain() { 244 load_plugin_textdomain( 245 'autopaging', 246 false, 247 dirname( plugin_basename( __FILE__ ) ) . '/languages/' 248 ); 249 } 250 251 /** 252 * Set post types this plugin can act on, either from Reading page or via filter. 253 * Also sets default number of pages to break content over, either from Reading page or via filter. 71 254 * 72 255 * @uses apply_filters, get_option 73 256 * @action init 74 * @return null257 * @return void 75 258 */ 76 259 public function action_init() { 77 // Post types260 // Post types. 78 261 $this->post_types = apply_filters( 'autopaging_post_types', get_option( $this->option_name_post_types, $this->post_types_default ) ); 79 262 80 // Number of pages to break over263 // Number of pages to break over. 81 264 $this->num_pages = absint( apply_filters( 'autopaging_num_pages_default', get_option( $this->option_name_num_pages, $this->num_pages_default ) ) ); 82 if ( 0 == $this->num_pages ) 265 if ( 0 == $this->num_pages ) { 83 266 $this->num_pages = $this->num_pages_default; 84 85 //Number of words to break over 267 } 268 269 // Number of words to break over. 86 270 $this->num_words = absint( apply_filters( 'autopaging_num_words_default', get_option( $this->option_name_num_words, $this->num_words_default ) ) ); 87 if ( 0 == $this->num_words ) 271 if ( 0 == $this->num_words ) { 88 272 $this->num_words = $this->num_words_default; 273 } 274 275 // Supported blocks for splitting by words. 276 $this->supported_block_types_for_word_counts = apply_filters( 277 'autopaging_supported_block_types_for_word_counts', 278 $this->supported_block_types_for_word_counts 279 ); 89 280 } 90 281 … … 95 286 * @uses delete_option 96 287 * @action uninstall 97 * @return null288 * @return void 98 289 */ 99 290 public function uninstall() { … … 107 298 * Add settings link to plugin's row actions 108 299 * 109 * @param array $actions110 * @param string $file 300 * @param array $actions Plugin's actions. 301 * @param string $file Plugin filename. 111 302 * @filter plugin_action_links, 112 303 */ 113 304 public function filter_plugin_action_links( $actions, $file ) { 114 if ( false !== strpos( $file, basename( __FILE__ ) ) ) 115 $actions[ 'settings' ] = '<a href="' . admin_url( 'options-reading.php' ) . '">Settings</a>'; 305 if ( false !== strpos( $file, basename( __FILE__ ) ) ) { 306 $actions['settings'] = '<a href="' . admin_url( 'options-reading.php' ) . '">Settings</a>'; 307 } 116 308 117 309 return $actions; … … 119 311 120 312 /** 121 * Register settings and settings sections 122 * Settings appear on the Reading page 313 * Register settings and settings sections. 314 * Settings appear on the Reading page. 123 315 * 124 316 * @uses register_setting, add_settings_section, __, __return_false, add_settings_field 125 317 * @action admin_init 126 * @return null318 * @return void 127 319 */ 128 320 public function action_admin_init() { … … 138 330 139 331 /** 140 * Render post types options 332 * Render post types options. 141 333 * 142 334 * @uses get_post_types, get_option, esc_attr, checked, esc_html 143 * @return string335 * @return void 144 336 */ 145 337 public function settings_field_post_types() { 146 //Get all public post types 147 $post_types = get_post_types( array( 148 'public' => true 149 ), 'objects' ); 150 151 //Remove attachments 152 unset( $post_types[ 'attachment' ] ); 153 154 //Current settings 338 // Get all public post types. 339 $post_types = get_post_types( 340 array( 341 'public' => true, 342 ), 343 'objects' 344 ); 345 346 unset( $post_types['attachment'] ); 347 348 // Current settings. 155 349 $current_types = get_option( $this->option_name_post_types, $this->post_types_default ); 156 350 157 // Output checkboxes351 // Output checkboxes. 158 352 foreach ( $post_types as $post_type => $atts ) : 159 ?>353 ?> 160 354 <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 ); ?></label><br /> 161 <?php355 <?php 162 356 endforeach; 163 357 } 164 358 165 359 /** 166 * Sanitize post type inputs 167 * 168 * @param array $post_types_checked 360 * Sanitize post type inputs. 361 * 362 * @param array $post_types_checked Selected post types to sanitize. 169 363 * @uses get_post_types 170 364 * @return array … … 173 367 $post_types_sanitized = array(); 174 368 175 // Ensure that only existing, public post types are submitted as valid options369 // Ensure that only existing, public post types are submitted as valid options. 176 370 if ( is_array( $post_types_checked ) && ! empty( $post_types_checked ) ) { 177 //Get all public post types 178 $post_types = get_post_types( array( 179 'public' => true 180 ) ); 181 182 //Remove attachments 183 unset( $post_types[ 'attachment' ] ); 184 185 //Check input post types against those registered with WordPress and made available to this plugin 371 // Get all public post types. 372 $post_types = get_post_types( 373 array( 374 'public' => true, 375 ) 376 ); 377 378 unset( $post_types['attachment'] ); 379 380 // Check input post types against those registered with WordPress and made available to this plugin. 186 381 foreach ( $post_types_checked as $post_type ) { 187 if ( array_key_exists( $post_type, $post_types ) ) 382 if ( array_key_exists( $post_type, $post_types ) ) { 188 383 $post_types_sanitized[] = $post_type; 384 } 189 385 } 190 386 } … … 194 390 195 391 /** 196 * Render option to choose paging type and options for that type 392 * Render option to choose paging type and options for that type. 197 393 * 198 394 * @uses get_option() 199 395 * @uses esc_attr() 200 396 * @uses checked() 201 * @return string397 * @return void 202 398 */ 203 399 public function settings_field_paging_type() { … … 208 404 209 405 $labels = array( 210 'pages' => __( 'Total number of pages: ', 'autopaging' ),211 'words' => __( 'Approximate words per page: ', 'autopaging' ),406 'pages' => __( 'Total number of pages:', 'autopaging' ), 407 'words' => __( 'Approximate words per page:', 'autopaging' ), 212 408 ); 213 409 214 410 foreach ( $this->paging_types_allowed as $type ) : 215 $type_escaped = esc_attr( $type );216 411 $func = 'settings_field_num_' . $type; 217 412 ?> 218 <p><input type="radio" name="<?php echo esc_attr( $this->option_name_paging_type ); ?>" id="autopaging-type-<?php echo $type_escaped; ?>" value="<?php echo $type_escaped; ?>"<?php checked( $type, $paging_type ); ?> /> <label for="autopaging-type-<?php echo $type_escaped; ?>"> 219 <strong><?php echo $labels[ $type ]; ?></strong><?php $this->{$func}(); ?> 220 </label></p> 221 <?php endforeach; 222 } 223 224 /** 225 * Validate chosen paging type against allowed values 226 * 227 * @param string 413 <p> 414 <input type="radio" name="<?php echo esc_attr( $this->option_name_paging_type ); ?>" id="autopaging-type-<?php echo esc_attr( $type ); ?>" value="<?php echo esc_attr( $type ); ?>"<?php checked( $type, $paging_type ); ?> /> 415 <label for="autopaging-type-<?php echo esc_attr( $type ); ?>"> 416 <?php echo esc_html( $labels[ $type ] ); ?> 417 418 <?php $this->{$func}(); ?> 419 </label> 420 </p> 421 <br /> 422 <?php 423 endforeach; 424 } 425 426 /** 427 * Validate chosen paging type against allowed values. 428 * 429 * @param string $type Selected paging type. 228 430 * @return string 229 431 */ 230 432 public function sanitize_paging_type( $type ) { 231 return in_array( $type, $this->paging_types_allowed ) ? $type : $this->paging_type_default;232 } 233 234 /** 235 * Render dropdown for choosing number of pages to break content over 433 return in_array( $type, $this->paging_types_allowed, true ) ? $type : $this->paging_type_default; 434 } 435 436 /** 437 * Render dropdown for choosing number of pages to break content over. 236 438 * 237 439 * @uses get_option, apply_filters, esc_attr, selected 238 * @return string440 * @return void 239 441 */ 240 442 public function settings_field_num_pages() { … … 244 446 ?> 245 447 <select name="<?php echo esc_attr( $this->option_name_num_pages ); ?>"> 246 <?php for ( $i = 2; $i <= $max_pages; $i++ ) : ?>448 <?php for ( $i = 2; $i <= $max_pages; $i++ ) : ?> 247 449 <option value="<?php echo intval( $i ); ?>"<?php selected( (int) $i, (int) $num_pages ); ?>><?php echo intval( $i ); ?></option> 248 450 <?php endfor; ?> … … 252 454 253 455 /** 254 * Sanitize number of pages input 255 * 256 * @param int $num_pages 456 * Sanitize number of pages input. 457 * 458 * @param int $num_pages Number of pages to split to. 257 459 * @uses apply_filters 258 460 * @return int … … 263 465 264 466 /** 265 * Render input field for specifying approximate number of words each page should contain 467 * Render input field for specifying approximate number of words each page should contain. 266 468 * 267 469 * @uses get_option, apply_filters, esc_attr, selected 268 * @return string470 * @return void 269 471 */ 270 472 public function settings_field_num_words() { 271 473 $num_words = apply_filters( 'autopaging_num_words', get_option( $this->option_name_num_words ) ) 272 474 ?> 273 <input name="<?php echo esc_attr( $this->option_name_num_words ); ?>" value="<?php echo esc_attr( $num_words ); ?>" size="4" /> 274 275 <p class="description"><?php _e( 'If chosen, each page will contain approximately this many words, depending on paragraph lengths.', 'autopaging' ); ?></p> 475 <input 476 name="<?php echo esc_attr( $this->option_name_num_words ); ?>" 477 value="<?php echo esc_attr( $num_words ); ?>" 478 class="small-text" 479 type="number" 480 step="1" 481 min="1" 482 /> 483 484 <p class="description"><?php esc_html_e( 'If chosen, each page will contain approximately this many words, depending on paragraph lengths.', 'autopaging' ); ?></p> 276 485 <?php 277 486 } 278 487 279 488 /** 280 * Sanitize number of words input. No fewer than 10 by default, filterable by autopaging_max_num_words281 * 282 * @param int $num_words 489 * Sanitize number of words input. No fewer than 10 by default, filterable by `autopaging_max_num_words`. 490 * 491 * @param int $num_words Number of words to split on. 283 492 * @uses apply_filters 284 493 * @return int … … 295 504 296 505 /** 297 * Add autopaging metabox 506 * Add autopaging metabox. 298 507 * 299 508 * @uses add_metabox, __ 300 509 * @action add_meta_box 301 * @return null510 * @return void 302 511 */ 303 512 public function action_add_meta_boxes() { 304 513 foreach ( $this->post_types as $post_type ) { 305 add_meta_box( 'autopaging', __( 'Post Autopaging', 'autopaging' ), array( $this, 'meta_box_autopaging' ), $post_type, 'side' ); 306 } 307 } 308 309 /** 310 * Render autopaging metabox 311 * 312 * @param object $post 514 if ( 515 function_exists( 'use_block_editor_for_post_type' ) && 516 use_block_editor_for_post_type( $post_type ) 517 ) { 518 continue; 519 } 520 521 add_meta_box( 'autopaging', __( 'Autopaging', 'autopaging' ), array( $this, 'meta_box_autopaging' ), $post_type, 'side' ); 522 } 523 } 524 525 /** 526 * Render autopaging metabox. 527 * 528 * @param object $post Post object. 313 529 * @uses esc_attr, checked, _e, __, wp_nonce_field 314 * @return string530 * @return void 315 531 */ 316 532 public function meta_box_autopaging( $post ) { 317 ?>533 ?> 318 534 <p> 319 <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> 535 <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 ) ); ?> /> 536 <label for="<?php echo esc_attr( $this->meta_key_disable_autopaging ); ?>_checkbox"> 537 <?php esc_html_e( 'Disable autopaging for this post?', 'autopaging' ); ?> 538 </label> 320 539 </p> 321 <p class="description"><?php _e( 'Check the box above to prevent this post from automatically being split over multiple pages.', 'autopaging' ); ?></p> 322 <p class="description"><?php printf( __( 'Note that if the %1$s Quicktag is used to manually page this post, automatic paging won\'t be applied, regardless of the setting above.', 'autopaging' ), '<code><!--nextpage--></code>' ); ?></p> 323 <?php 540 <p class="description"><?php esc_html__( 'Check the box above to prevent this post from automatically being split over multiple pages.', 'autopaging' ); ?></p> 541 <p class="description"> 542 <?php 543 printf( 544 /* translators: 1. Quicktag code example. */ 545 esc_html__( 546 'Note that if the %1$s Quicktag is used to manually page this post, automatic paging won\'t be applied, regardless of the setting above.', 547 'autopaging' 548 ), 549 // No need to escape a class constant. 550 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 551 '<code>' . htmlentities( static::QUICKTAG, ENT_QUOTES ) . '</code>' 552 ); 553 ?> 554 </p> 555 556 <?php 324 557 wp_nonce_field( $this->meta_key_disable_autopaging, $this->meta_key_disable_autopaging . '_wpnonce' ); 325 558 } 326 559 327 560 /** 328 * Save autopaging metabox 329 * 330 * @param int $post_id 561 * Save autopaging metabox. 562 * 563 * @param int $post_id Post ID. 331 564 * @uses DOING_AUTOSAVE, wp_verify_nonce, update_post_meta, delete_post_meta 332 565 * @action save_post … … 334 567 */ 335 568 public function action_save_post( $post_id ) { 336 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 569 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 337 570 return; 571 } 572 573 if ( 574 function_exists( 'use_block_editor_for_post' ) 575 && use_block_editor_for_post( $post_id ) 576 ) { 577 return; 578 } 338 579 339 580 if ( isset( $_POST[ $this->meta_key_disable_autopaging . '_wpnonce' ] ) && wp_verify_nonce( $_POST[ $this->meta_key_disable_autopaging . '_wpnonce' ], $this->meta_key_disable_autopaging ) ) { 340 $disable = isset( $_POST[ $this->meta_key_disable_autopaging ] ) ? true : false;341 342 if ( $disable ) 581 $disable = isset( $_POST[ $this->meta_key_disable_autopaging ] ); 582 583 if ( $disable ) { 343 584 update_post_meta( $post_id, $this->meta_key_disable_autopaging, true ); 344 else585 } else { 345 586 delete_post_meta( $post_id, $this->meta_key_disable_autopaging ); 587 } 346 588 } 347 589 } … … 351 593 * Only applied if the post type matches specified options and post doesn't already contain the Quicktag. 352 594 * 353 * @param array $posts 595 * @param array $posts Array of posts retrieved by WP_Query. 354 596 * @uses is_admin, get_post_meta, absint, apply_filters 355 597 * @filter the_posts … … 357 599 */ 358 600 public function filter_the_posts( $posts ) { 359 if ( ! is_admin() ) { 360 foreach( $posts as $the_post ) { 361 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 ) ) { 362 //In-time filtering of number of pages to break over, based on post data. If value is less than 2, nothing should be done. 363 $num_pages = absint( apply_filters( 'autopaging_num_pages', absint( $this->num_pages ), $the_post ) ); 364 $num_words = absint( apply_filters( 'autopaging_num_words', absint( $this->num_words ), $the_post ) ); 365 366 if ( $num_pages < 2 && empty( $num_words ) ) 367 continue; 368 369 //Start with post content, but alias to protect the raw content. 370 $content = $the_post->post_content; 371 372 //Normalize post content to simplify paragraph counting and automatic paging. Accounts for content that hasn't been cleaned up by TinyMCE. 373 $content = preg_replace( '#<p>(.+?)</p>#i', "$1\r\n\r\n", $content ); 374 $content = preg_replace( '#<br(\s*/)?>#i', "\r\n", $content ); 375 376 //Count paragraphs 377 $count = preg_match_all( '#\r\n\r\n#', $content, $matches ); 378 379 //Keep going, if we have something to count. 380 if ( is_int( $count ) && 0 < $count ) { 381 //Explode content at double (or more) line breaks 382 $content = explode( "\r\n\r\n", $content ); 383 384 switch ( get_option( $this->option_name_paging_type, $this->paging_type_default ) ) { 385 case 'words' : 386 $word_counter = 0; 387 388 // Count words per paragraph and break after the paragraph that exceeds the set threshold 389 foreach ( $content as $index => $paragraph ) { 390 $paragraph_words = count( preg_split( '/\s+/', strip_tags( $paragraph ) ) ); 391 $word_counter += $paragraph_words; 392 393 if ( $word_counter >= $num_words ) { 394 $content[ $index ] .= '<!--nextpage-->'; 395 $word_counter = 0; 396 } else { 397 continue; 398 } 399 } 400 401 unset( $word_counter ); 402 unset( $index ); 403 unset( $paragraph ); 404 unset( $paragraph_words ); 405 406 break; 407 408 case 'pages' : 409 default : 410 //Count number of paragraphs content was exploded to 411 $count = count( $content ); 412 413 //Determine when to insert Quicktag 414 $insert_every = $count / $num_pages; 415 $insert_every_rounded = round( $insert_every ); 416 417 //If number of pages is greater than number of paragraphs, put each paragraph on its own page 418 if ( $num_pages > $count ) { 419 $insert_every_rounded = 1; 420 } 421 422 //Set initial counter position. 423 $i = $count - 1 == $num_pages ? 2 : 1; 424 425 //Loop through content pieces and append Quicktag as is appropriate 426 foreach ( $content as $key => $value ) { 427 if ( $key + 1 == $count ) { 428 break; 429 } 430 431 if ( ( $key + 1 ) == ( $i * $insert_every_rounded ) ) { 432 $content[ $key ] = $content[ $key ] . '<!--nextpage-->'; 433 $i++; 434 } 435 } 436 437 //Clean up 438 unset( $count ); 439 unset( $insert_every ); 440 unset( $insert_every_rounded ); 441 unset( $key ); 442 unset( $value ); 443 444 break; 601 if ( is_admin() ) { 602 return $posts; 603 } 604 605 $paging_type = get_option( 606 $this->option_name_paging_type, 607 $this->paging_type_default 608 ); 609 610 foreach ( $posts as &$the_post ) { 611 if ( 612 ! in_array( 613 $the_post->post_type, 614 $this->post_types, 615 true 616 ) 617 ) { 618 continue; 619 } 620 621 if ( 622 preg_match( 623 '#' . static::QUICKTAG . '#i', 624 $the_post->post_content 625 ) 626 ) { 627 continue; 628 } 629 630 if ( 631 (bool) get_post_meta( 632 $the_post->ID, 633 $this->meta_key_disable_autopaging, 634 true 635 ) 636 ) { 637 continue; 638 } 639 640 $num_pages = absint( 641 apply_filters( 642 'autopaging_num_pages', 643 absint( $this->num_pages ), 644 $the_post 645 ) 646 ); 647 $num_words = absint( 648 apply_filters( 649 'autopaging_num_words', 650 absint( $this->num_words ), 651 $the_post 652 ) 653 ); 654 655 if ( $num_pages < 2 && empty( $num_words ) ) { 656 continue; 657 } 658 659 if ( 660 function_exists( 'has_blocks' ) 661 && has_blocks( $the_post ) 662 ) { 663 $this->filter_block_editor_post( 664 $the_post, 665 $paging_type, 666 $num_words, 667 $num_pages 668 ); 669 } else { 670 $this->filter_classic_editor_post( 671 $the_post, 672 $paging_type, 673 $num_words, 674 $num_pages 675 ); 676 } 677 } 678 679 return $posts; 680 } 681 682 /** 683 * Add pagination Quicktag to post authored in the Classic Editor. 684 * 685 * @param WP_Post|object $the_post Post object. 686 * @param string $paging_type How to split post. 687 * @param int $num_words Number of words to split on. 688 * @param int $num_pages Number of pages to split to. 689 * @return void 690 */ 691 protected function filter_classic_editor_post( 692 &$the_post, 693 $paging_type, 694 $num_words, 695 $num_pages 696 ) { 697 // Start with post content, but alias to protect the raw content. 698 $content = $the_post->post_content; 699 700 // Normalize post content to simplify paragraph counting and automatic paging. Accounts for content that hasn't been cleaned up by TinyMCE. 701 $content = preg_replace( '#<p>(.+?)</p>#i', "$1\r\n\r\n", $content ); 702 $content = preg_replace( '#<br(\s*/)?>#i', "\r\n", $content ); 703 $content = explode( "\r\n\r\n", $content ); 704 705 // Count number of paragraphs content was exploded to. 706 $count = count( $content ); 707 708 // Nothing to do, goodbye. 709 if ( $count <= 1 ) { 710 return; 711 } 712 713 switch ( $paging_type ) { 714 case 'words': 715 $word_counter = 0; 716 717 // Count words per paragraph and break after the paragraph that exceeds the set threshold. 718 foreach ( $content as $index => $paragraph ) { 719 $word_counter += mb_strlen( 720 wp_strip_all_tags( 721 $paragraph 722 ) 723 ); 724 725 if ( $word_counter >= $num_words ) { 726 $content[ $index ] .= static::QUICKTAG; 727 $word_counter = 0; 728 } 729 } 730 731 // Prevent the last page from being empty. 732 $last_page = array_pop( $content ); 733 if ( 734 static::QUICKTAG === 735 substr( 736 $last_page, 737 - static::QUICKTAG_LENGTH 738 ) 739 ) { 740 $content[] = substr( 741 $last_page, 742 0, 743 strlen( $last_page ) - static::QUICKTAG_LENGTH 744 ); 745 } else { 746 $content[] = $last_page; 747 } 748 749 break; 750 751 case 'pages': 752 default: 753 $frequency = $this->get_insertion_frequency_by_pages( 754 $count, 755 $num_pages 756 ); 757 758 $i = 1; 759 760 // Loop through content pieces and append Quicktag as is appropriate. 761 foreach ( $content as $key => $value ) { 762 if ( $this->is_at_end_for_pages( $key, $count ) ) { 763 break; 764 } 765 766 if ( 767 $this->is_insertion_point_for_pages( 768 $key, 769 $i, 770 $frequency 771 ) 772 ) { 773 $content[ $key ] .= static::QUICKTAG; 774 $i++; 775 } 776 } 777 778 break; 779 } 780 781 // Reunite content. 782 $content = implode( "\r\n\r\n", $content ); 783 784 // And, overwrite the original content. 785 $the_post->post_content = $content; 786 } 787 788 /** 789 * Add pagination block to post authored in the Block Editor. 790 * 791 * @param WP_Post $the_post Post object. 792 * @param string $paging_type How to split post. 793 * @param int $num_words Number of words to split on. 794 * @param int $num_pages Number of pages to split to. 795 * @return void 796 */ 797 protected function filter_block_editor_post( 798 &$the_post, 799 $paging_type, 800 $num_words, 801 $num_pages 802 ) { 803 $blocks = parse_blocks( $the_post->post_content ); 804 $new_blocks = array(); 805 806 switch ( $paging_type ) { 807 case 'words': 808 $word_count = 0; 809 810 foreach ( $blocks as $block ) { 811 $new_blocks[] = $block; 812 813 if ( 814 in_array( 815 $block['blockName'], 816 $this->supported_block_types_for_word_counts, 817 true 818 ) 819 ) { 820 $word_count += mb_strlen( 821 trim( 822 wp_strip_all_tags( 823 $block['innerHTML'] 824 ) 825 ) 826 ); 827 828 if ( $word_count >= $num_words ) { 829 $new_blocks[] = $this->get_parsed_nextpage_block(); 830 831 $word_count = 0; 445 832 } 446 447 //Reunite content448 $content = implode( "\r\n\r\n", $content );449 450 //And, overwrite the original content451 $the_post->post_content = $content;452 833 } 453 454 //Lastly, clean up.455 unset( $num_pages );456 unset( $num_words );457 unset( $content );458 unset( $count );459 834 } 460 } 461 } 462 463 return $posts; 835 836 $last_block = array_pop( $new_blocks ); 837 if ( $this->get_parsed_nextpage_block() !== $last_block ) { 838 $new_blocks[] = $last_block; 839 } 840 break; 841 842 case 'pages': 843 default: 844 $count = count( $blocks ); 845 846 $frequency = $this->get_insertion_frequency_by_pages( 847 $count, 848 $num_pages 849 ); 850 851 $i = 1; 852 853 foreach ( $blocks as $key => $block ) { 854 $new_blocks[] = $block; 855 856 if ( $this->is_at_end_for_pages( $key, $count ) ) { 857 break; 858 } 859 860 if ( 861 $this->is_insertion_point_for_pages( 862 $key, 863 $i, 864 $frequency 865 ) 866 ) { 867 $new_blocks[] = $this->get_parsed_nextpage_block(); 868 $i++; 869 } 870 } 871 break; 872 } 873 874 $the_post->post_content = serialize_blocks( $new_blocks ); 875 } 876 877 /** 878 * Determine after how many paragraphs a page break should be inserted. 879 * 880 * @param int $count Total number of paragraphs. 881 * @param int $num_pages Desired number of pages. 882 * @return int 883 */ 884 protected function get_insertion_frequency_by_pages( $count, $num_pages ) { 885 $frequency = (int) round( $count / $num_pages ); 886 887 // If number of pages is greater than number of paragraphs, put each paragraph on its own page. 888 if ( $num_pages > $count ) { 889 $frequency = 1; 890 } 891 892 return $frequency; 893 } 894 895 /** 896 * Determine if more page breaks should be inserted. 897 * 898 * @param int $key Current position in array of blocks. 899 * @param int $count Total number of paragraphs. 900 * @return bool 901 */ 902 protected function is_at_end_for_pages( $key, $count ) { 903 return ( $key + 1 ) === $count; 904 } 905 906 /** 907 * Determine if current loop iteration is where a page break is expected. 908 * 909 * @param int $loop_key Current position in array of blocks. 910 * @param int $insertion_iterator Current number of page breaks inserted. 911 * @param int $insertion_frequency After this many blocks a should break be 912 * inserted. 913 * @return bool 914 */ 915 protected function is_insertion_point_for_pages( 916 $loop_key, 917 $insertion_iterator, 918 $insertion_frequency 919 ) { 920 return ( $loop_key + 1 ) === 921 ( $insertion_iterator * $insertion_frequency ); 922 } 923 924 /** 925 * Create parsed representation of block for insertion in list of post's 926 * blocks. 927 * 928 * @return array 929 */ 930 protected function get_parsed_nextpage_block() { 931 static $block; 932 933 if ( ! $block ) { 934 $_block = parse_blocks( 935 '<!-- wp:nextpage --> 936 ' . static::QUICKTAG . ' 937 <!-- /wp:nextpage -->' 938 ); 939 940 $block = array_shift( $_block ); 941 } 942 943 return $block; 464 944 } 465 945 } 466 new Automatically_Paginate_Posts; 946 947 new Automatically_Paginate_Posts(); 467 948 ?> -
automatically-paginate-posts/tags/0.3/languages/automatically-paginate-posts.pot
r2268447 r2749430 1 # Copyright (C) 202 0Erick Hitter & Oomph, Inc.1 # Copyright (C) 2022 Erick Hitter & Oomph, Inc. 2 2 # This file is distributed under the same license as the Automatically Paginate Posts package. 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Automatically Paginate Posts 0. 2\n"5 "Project-Id-Version: Automatically Paginate Posts 0.3\n" 6 6 "Report-Msgid-Bugs-To: " 7 7 "https://wordpress.org/support/plugin/automatically-paginate-posts\n" 8 "POT-Creation-Date: 202 0-03-26 17:10:56+00:00\n"8 "POT-Creation-Date: 2022-06-29 04:16:26+00:00\n" 9 9 "MIME-Version: 1.0\n" 10 10 "Content-Type: text/plain; charset=utf-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "PO-Revision-Date: 202 0-MO-DA HO:MI+ZONE\n"12 "PO-Revision-Date: 2022-MO-DA HO:MI+ZONE\n" 13 13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" 14 14 "Language-Team: LANGUAGE <[email protected]>\n" 15 "X-Generator: grunt-wp-i18n 0.5.4\n"16 "X-Poedit-KeywordsList: "17 "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"18 "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"19 15 "Language: en\n" 20 16 "Plural-Forms: nplurals=2; plural=(n != 1);\n" 21 17 "X-Poedit-Country: United States\n" 22 18 "X-Poedit-SourceCharset: UTF-8\n" 19 "X-Poedit-KeywordsList: " 20 "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_" 21 "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n" 23 22 "X-Poedit-Basepath: ../\n" 24 23 "X-Poedit-SearchPath-0: .\n" 25 24 "X-Poedit-Bookmarks: \n" 26 25 "X-Textdomain-Support: yes\n" 26 "X-Generator: grunt-wp-i18n 1.0.3\n" 27 28 #: automatically-paginate-posts.php:193 29 msgid "Post types can only be retrieved after the \"init\" hook." 30 msgstr "" 27 31 28 32 #. Plugin Name of the plugin/theme … … 30 34 msgstr "" 31 35 32 #: automatically-paginate-posts.php: 13536 #: automatically-paginate-posts.php:327 33 37 msgid "Supported post types:" 34 38 msgstr "" 35 39 36 #: automatically-paginate-posts.php: 13640 #: automatically-paginate-posts.php:328 37 41 msgid "Split post by:" 38 42 msgstr "" 39 43 40 #: automatically-paginate-posts.php: 21041 msgid "Total number of pages: "44 #: automatically-paginate-posts.php:406 45 msgid "Total number of pages:" 42 46 msgstr "" 43 47 44 #: automatically-paginate-posts.php: 21145 msgid "Approximate words per page: "48 #: automatically-paginate-posts.php:407 49 msgid "Approximate words per page:" 46 50 msgstr "" 47 51 48 #: automatically-paginate-posts.php: 27552 #: automatically-paginate-posts.php:484 49 53 msgid "" 50 54 "If chosen, each page will contain approximately this many words, depending " … … 52 56 msgstr "" 53 57 54 #: automatically-paginate-posts.php: 30555 msgid " PostAutopaging"58 #: automatically-paginate-posts.php:521 59 msgid "Autopaging" 56 60 msgstr "" 57 61 58 #: automatically-paginate-posts.php:321 62 #: automatically-paginate-posts.php:537 63 msgid "Disable autopaging for this post?" 64 msgstr "" 65 66 #: automatically-paginate-posts.php:540 59 67 msgid "" 60 68 "Check the box above to prevent this post from automatically being split " … … 62 70 msgstr "" 63 71 64 #: automatically-paginate-posts.php:322 72 #: automatically-paginate-posts.php:545 73 #. translators: 1. Quicktag code example. 65 74 msgid "" 66 75 "Note that if the %1$s Quicktag is used to manually page this post, " 67 76 "automatic paging won't be applied, regardless of the setting above." 77 msgstr "" 78 79 #: inc/class-block-editor.php:65 80 msgid "Whether or not to disable pagination for this post." 68 81 msgstr "" 69 82 -
automatically-paginate-posts/tags/0.3/readme.txt
r2268447 r2749430 4 4 Tags: paginate, nextpage, Quicktag 5 5 Requires at least: 3.4 6 Tested up to: 5.47 Stable tag: 0. 26 Tested up to: 6.0 7 Stable tag: 0.3 8 8 License: GPLv2 or later 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Automatically paginate posts by inserting the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content.11 Automatically paginate posts by inserting the `<!--nextpage-->` Quicktag. 12 12 13 == D ESCRIPTION==13 == Description == 14 14 15 Automatically paginate WordPress content by inserting the <!--nextpage--> Quicktag.15 Automatically paginate WordPress content by inserting the `<!--nextpage-->` Quicktag at intervals controlled by plugin's settings. 16 16 17 Option is provided to control wh at post types are automatically paginated (default is just `post`). Supports `post`, `page`, and any public custom post types.17 Option is provided to control which post types are automatically paginated (default is the "Post" post type). Supports any public custom post types (non-public types are supported via the `autopaging_post_types` filter). 18 18 19 19 Option is also provided to specify how many pages content should be broken out over, or how many words should be included per page. … … 21 21 == Installation == 22 22 23 1. Upload automatically-paginate-posts to /wp-content/plugins/.23 1. Upload automatically-paginate-posts to `/wp-content/plugins/`. 24 24 2. Activate plugin through the WordPress Plugins menu. 25 3. Configure plugin by going to Settings > Reading.25 3. Configure plugin by going to **Settings > Reading**. 26 26 27 27 == Frequently Asked Questions == 28 28 29 = Where do I set the plugin's options =29 = Where do I set the plugin's options? = 30 30 The plugin's options are added to the built-in **Reading** settings page in WordPress. 31 31 32 = Can I disable the plugin's functionality for specific posts, pages, or custom post type objects? =33 Yes, the plugin adds a metabox to individual items in supported post types that allows the autopaging to be disabled on a per-post basis.32 = Can I disable the plugin's functionality for specific content? = 33 Yes, the plugin adds a metabox (Classic Editor) and a sidebar component (Block Editor) to individual items in supported post types that allows the autopaging to be disabled on a per-post basis. 34 34 35 35 = How can I add support for my custom post type? = 36 Navigate to Settings > Readingin WP Admin to enable this plugin for your custom post type.36 Navigate to **Settings > Reading** in WP Admin to enable this plugin for your custom post type. 37 37 38 38 You can also use the filter `autopaging_post_types` to add support by appending your post type's name to the array. … … 45 45 * `autopaging_num_pages` - change the number of pages content is displayed on at runtime. Filter provides access to the full post object in addition to the number of pages. 46 46 * `autopaging_num_words` - change the number of words displayed per page at runtime. Filter provides access to the full post object in addition to the number of words. 47 * `autopaging_supported_block_types_for_word_counts` - specify which block types are considered when splitting a block-editor post by word count. 47 48 48 49 == Changelog == 50 51 = 0.3 = 52 * Add support for content authored in block editor (Gutenberg). 53 * Add native block-editor control to replace legacy metabox. 54 * Fix bug that created empty pages. 55 56 = 0.2 = 57 * Allow for number of words to be specified instead of number of pages. 49 58 50 59 = 0.1 = 51 60 * Initial release. 52 61 62 == Upgrade Notice == 63 64 = 0.3 = 65 Add support for block editor and fix bug that created empty pages. 66 53 67 = 0.2 = 54 * Allow for number of words to be specified instead of number of pages. 55 56 == Upgrade Notice == 68 Allow for number of words to be specified instead of number of pages. 57 69 58 70 = 0.1 = 59 71 Initial release 60 61 = 0.2 =62 * Allow for number of words to be specified instead of number of pages. -
automatically-paginate-posts/trunk/automatically-paginate-posts.php
r869001 r2749430 1 <?php 2 /* 3 Plugin Name: Automatically Paginate Posts 4 Plugin URI: http://www.oomphinc.com/plugins-modules/automatically-paginate-posts/ 5 Description: Automatically inserts the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content. 6 Version: 0.2 7 Author: Erick Hitter & Oomph, Inc. 8 Author URI: http://www.oomphinc.com/ 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName 2 /** 3 * Automatically inserts the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content. 4 * 5 * Plugin Name: Automatically Paginate Posts 6 * Plugin URI: http://www.oomphinc.com/plugins-modules/automatically-paginate-posts/ 7 * Description: Automatically inserts the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content. 8 * Version: 0.3 9 * Author: Erick Hitter & Oomph, Inc. 10 * Author URI: http://www.oomphinc.com/ 11 * Text Domain: autopaging 12 * Domain Path: /languages/ 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation; either version 2 of the License, or 17 * (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, write to the Free Software 26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 27 * 28 * @package automatically-paginate-posts 29 */ 30 31 require_once dirname( __FILE__ ) . '/inc/class-block-editor.php'; 32 33 /** 34 * Class Automatically_Paginate_Posts. 35 */ 25 36 class Automatically_Paginate_Posts { 26 37 /** 27 * Class variables 38 * WordPress Quicktag that creates pagination. 39 */ 40 const QUICKTAG = '<!--nextpage-->'; 41 42 /** 43 * String length of nextpage Quicktag. 44 */ 45 const QUICKTAG_LENGTH = 15; 46 47 /** 48 * Supported post types. 49 * 50 * @var array 28 51 */ 29 52 private $post_types; 53 54 /** 55 * Default supported post types. 56 * 57 * @var array 58 */ 30 59 private $post_types_default = array( 'post' ); 31 60 61 /** 62 * Desired number of pages to split to. 63 * 64 * @var int 65 */ 32 66 private $num_pages; 67 68 /** 69 * Method for splitting content, either words or desired number of pages. 70 * 71 * @var string 72 */ 33 73 private $paging_type_default = 'pages'; 34 private $num_pages_default = 2; 35 private $num_words_default = ''; 36 74 75 /** 76 * Default number of pages to split to. 77 * 78 * @var int 79 */ 80 private $num_pages_default = 2; 81 82 /** 83 * Desired number of words per pages. 84 * 85 * @var int 86 */ 87 private $num_words; 88 89 /** 90 * Default number of words to split on. 91 * 92 * @var string|int 93 */ 94 private $num_words_default = ''; 95 96 /** 97 * When splitting by word counts, these blocks are considered. Tags are 98 * stripped and remaining content is counted. 99 * 100 * @var array 101 */ 102 private $supported_block_types_for_word_counts = array( 103 'core/paragraph', 104 ); 105 106 /** 107 * Allowed split types. 108 * 109 * @var array 110 */ 37 111 private $paging_types_allowed = array( 'pages', 'words' ); 38 112 39 // Ensure option names match values in this::uninstall 40 private $option_name_post_types = 'autopaging_post_types'; 113 /** 114 * Supported-post-types option name. 115 * 116 * @var string 117 */ 118 private $option_name_post_types = 'autopaging_post_types'; 119 120 /** 121 * Split-type option name. 122 * 123 * @var string 124 */ 41 125 private $option_name_paging_type = 'pages'; 42 private $option_name_num_pages = 'autopaging_num_pages'; 43 private $option_name_num_words = 'autopaging_num_words'; 44 126 127 /** 128 * Option holding number of pages to split to. 129 * 130 * @var string 131 */ 132 private $option_name_num_pages = 'autopaging_num_pages'; 133 134 /** 135 * Option holding number of words to split on. 136 * 137 * @var string 138 */ 139 private $option_name_num_words = 'autopaging_num_words'; 140 141 /** 142 * Meta key used to indicate that a post shouldn't be automatically split. 143 * 144 * @var string 145 */ 45 146 private $meta_key_disable_autopaging = '_disable_autopaging'; 46 147 47 148 /** 48 * Register actions and filters 49 * 50 * @uses add_action, register_uninstall_hook, add_filter 51 * @return null 149 * Class constructor. 150 * 151 * @return void 52 152 */ 53 153 public function __construct() { 54 //Filters 154 $this->setup_hooks(); 155 156 new Automatically_Paginate_Posts\Block_Editor( $this ); 157 } 158 159 /** 160 * Register hooks. 161 * 162 * @return void 163 */ 164 protected function setup_hooks() { 165 add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) ); 55 166 add_action( 'init', array( $this, 'action_init' ) ); 56 167 57 // Admin settings168 // Admin settings. 58 169 register_uninstall_hook( __FILE__, array( 'Automatically_Paginate_Posts', 'uninstall' ) ); 59 170 add_filter( 'plugin_action_links', array( $this, 'filter_plugin_action_links' ), 10, 2 ); 60 171 add_action( 'admin_init', array( $this, 'action_admin_init' ) ); 61 172 62 // Post-type settings173 // Post-type settings. 63 174 add_action( 'add_meta_boxes', array( $this, 'action_add_meta_boxes' ) ); 64 175 add_action( 'save_post', array( $this, 'action_save_post' ) ); 65 176 add_filter( 'the_posts', array( $this, 'filter_the_posts' ) ); 66 177 } 67 68 /** 69 * Set post types this plugin can act on, either from Reading page or via filter 70 * Also sets default number of pages to break content over, either from Reading page or via filter 178 /** 179 * Allow access to meta key for disabling autopaging. 180 * 181 * @param string $name Property name. 182 * @return array|string|null 183 */ 184 public function __get( $name ) { 185 if ( 'meta_key' === $name ) { 186 return $this->meta_key_disable_autopaging; 187 } 188 189 if ( 'post_types' === $name ) { 190 if ( ! did_action( 'init' ) ) { 191 _doing_it_wrong( 192 __METHOD__, 193 esc_html__( 194 'Post types can only be retrieved after the "init" hook.', 195 'autopaging' 196 ), 197 '0.3' 198 ); 199 200 return null; 201 } 202 203 return $this->post_types; 204 } 205 206 return null; 207 } 208 209 /** 210 * Prevent setting properties. 211 * 212 * @param string $name Property name. 213 * @param string $value Property value. 214 * @return false 215 */ 216 public function __set( $name, $value ) { 217 return false; 218 } 219 220 /** 221 * Indicate if a property is set. 222 * 223 * @param string $name Property name. 224 * @return bool 225 */ 226 public function __isset( $name ) { 227 if ( 'meta_key' === $name ) { 228 return true; 229 } 230 231 if ( 'post_types' === $name ) { 232 return did_action( 'init' ); 233 } 234 235 return false; 236 } 237 238 /** 239 * Load plugin translations. 240 * 241 * @return void 242 */ 243 public function load_textdomain() { 244 load_plugin_textdomain( 245 'autopaging', 246 false, 247 dirname( plugin_basename( __FILE__ ) ) . '/languages/' 248 ); 249 } 250 251 /** 252 * Set post types this plugin can act on, either from Reading page or via filter. 253 * Also sets default number of pages to break content over, either from Reading page or via filter. 71 254 * 72 255 * @uses apply_filters, get_option 73 256 * @action init 74 * @return null257 * @return void 75 258 */ 76 259 public function action_init() { 77 // Post types260 // Post types. 78 261 $this->post_types = apply_filters( 'autopaging_post_types', get_option( $this->option_name_post_types, $this->post_types_default ) ); 79 262 80 // Number of pages to break over263 // Number of pages to break over. 81 264 $this->num_pages = absint( apply_filters( 'autopaging_num_pages_default', get_option( $this->option_name_num_pages, $this->num_pages_default ) ) ); 82 if ( 0 == $this->num_pages ) 265 if ( 0 == $this->num_pages ) { 83 266 $this->num_pages = $this->num_pages_default; 84 85 //Number of words to break over 267 } 268 269 // Number of words to break over. 86 270 $this->num_words = absint( apply_filters( 'autopaging_num_words_default', get_option( $this->option_name_num_words, $this->num_words_default ) ) ); 87 if ( 0 == $this->num_words ) 271 if ( 0 == $this->num_words ) { 88 272 $this->num_words = $this->num_words_default; 273 } 274 275 // Supported blocks for splitting by words. 276 $this->supported_block_types_for_word_counts = apply_filters( 277 'autopaging_supported_block_types_for_word_counts', 278 $this->supported_block_types_for_word_counts 279 ); 89 280 } 90 281 … … 95 286 * @uses delete_option 96 287 * @action uninstall 97 * @return null288 * @return void 98 289 */ 99 290 public function uninstall() { … … 107 298 * Add settings link to plugin's row actions 108 299 * 109 * @param array $actions110 * @param string $file 300 * @param array $actions Plugin's actions. 301 * @param string $file Plugin filename. 111 302 * @filter plugin_action_links, 112 303 */ 113 304 public function filter_plugin_action_links( $actions, $file ) { 114 if ( false !== strpos( $file, basename( __FILE__ ) ) ) 115 $actions[ 'settings' ] = '<a href="' . admin_url( 'options-reading.php' ) . '">Settings</a>'; 305 if ( false !== strpos( $file, basename( __FILE__ ) ) ) { 306 $actions['settings'] = '<a href="' . admin_url( 'options-reading.php' ) . '">Settings</a>'; 307 } 116 308 117 309 return $actions; … … 119 311 120 312 /** 121 * Register settings and settings sections 122 * Settings appear on the Reading page 313 * Register settings and settings sections. 314 * Settings appear on the Reading page. 123 315 * 124 316 * @uses register_setting, add_settings_section, __, __return_false, add_settings_field 125 317 * @action admin_init 126 * @return null318 * @return void 127 319 */ 128 320 public function action_admin_init() { … … 138 330 139 331 /** 140 * Render post types options 332 * Render post types options. 141 333 * 142 334 * @uses get_post_types, get_option, esc_attr, checked, esc_html 143 * @return string335 * @return void 144 336 */ 145 337 public function settings_field_post_types() { 146 //Get all public post types 147 $post_types = get_post_types( array( 148 'public' => true 149 ), 'objects' ); 150 151 //Remove attachments 152 unset( $post_types[ 'attachment' ] ); 153 154 //Current settings 338 // Get all public post types. 339 $post_types = get_post_types( 340 array( 341 'public' => true, 342 ), 343 'objects' 344 ); 345 346 unset( $post_types['attachment'] ); 347 348 // Current settings. 155 349 $current_types = get_option( $this->option_name_post_types, $this->post_types_default ); 156 350 157 // Output checkboxes351 // Output checkboxes. 158 352 foreach ( $post_types as $post_type => $atts ) : 159 ?>353 ?> 160 354 <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 ); ?></label><br /> 161 <?php355 <?php 162 356 endforeach; 163 357 } 164 358 165 359 /** 166 * Sanitize post type inputs 167 * 168 * @param array $post_types_checked 360 * Sanitize post type inputs. 361 * 362 * @param array $post_types_checked Selected post types to sanitize. 169 363 * @uses get_post_types 170 364 * @return array … … 173 367 $post_types_sanitized = array(); 174 368 175 // Ensure that only existing, public post types are submitted as valid options369 // Ensure that only existing, public post types are submitted as valid options. 176 370 if ( is_array( $post_types_checked ) && ! empty( $post_types_checked ) ) { 177 //Get all public post types 178 $post_types = get_post_types( array( 179 'public' => true 180 ) ); 181 182 //Remove attachments 183 unset( $post_types[ 'attachment' ] ); 184 185 //Check input post types against those registered with WordPress and made available to this plugin 371 // Get all public post types. 372 $post_types = get_post_types( 373 array( 374 'public' => true, 375 ) 376 ); 377 378 unset( $post_types['attachment'] ); 379 380 // Check input post types against those registered with WordPress and made available to this plugin. 186 381 foreach ( $post_types_checked as $post_type ) { 187 if ( array_key_exists( $post_type, $post_types ) ) 382 if ( array_key_exists( $post_type, $post_types ) ) { 188 383 $post_types_sanitized[] = $post_type; 384 } 189 385 } 190 386 } … … 194 390 195 391 /** 196 * Render option to choose paging type and options for that type 392 * Render option to choose paging type and options for that type. 197 393 * 198 394 * @uses get_option() 199 395 * @uses esc_attr() 200 396 * @uses checked() 201 * @return string397 * @return void 202 398 */ 203 399 public function settings_field_paging_type() { … … 208 404 209 405 $labels = array( 210 'pages' => __( 'Total number of pages: ', 'autopaging' ),211 'words' => __( 'Approximate words per page: ', 'autopaging' ),406 'pages' => __( 'Total number of pages:', 'autopaging' ), 407 'words' => __( 'Approximate words per page:', 'autopaging' ), 212 408 ); 213 409 214 410 foreach ( $this->paging_types_allowed as $type ) : 215 $type_escaped = esc_attr( $type );216 411 $func = 'settings_field_num_' . $type; 217 412 ?> 218 <p><input type="radio" name="<?php echo esc_attr( $this->option_name_paging_type ); ?>" id="autopaging-type-<?php echo $type_escaped; ?>" value="<?php echo $type_escaped; ?>"<?php checked( $type, $paging_type ); ?> /> <label for="autopaging-type-<?php echo $type_escaped; ?>"> 219 <strong><?php echo $labels[ $type ]; ?></strong><?php $this->{$func}(); ?> 220 </label></p> 221 <?php endforeach; 222 } 223 224 /** 225 * Validate chosen paging type against allowed values 226 * 227 * @param string 413 <p> 414 <input type="radio" name="<?php echo esc_attr( $this->option_name_paging_type ); ?>" id="autopaging-type-<?php echo esc_attr( $type ); ?>" value="<?php echo esc_attr( $type ); ?>"<?php checked( $type, $paging_type ); ?> /> 415 <label for="autopaging-type-<?php echo esc_attr( $type ); ?>"> 416 <?php echo esc_html( $labels[ $type ] ); ?> 417 418 <?php $this->{$func}(); ?> 419 </label> 420 </p> 421 <br /> 422 <?php 423 endforeach; 424 } 425 426 /** 427 * Validate chosen paging type against allowed values. 428 * 429 * @param string $type Selected paging type. 228 430 * @return string 229 431 */ 230 432 public function sanitize_paging_type( $type ) { 231 return in_array( $type, $this->paging_types_allowed ) ? $type : $this->paging_type_default;232 } 233 234 /** 235 * Render dropdown for choosing number of pages to break content over 433 return in_array( $type, $this->paging_types_allowed, true ) ? $type : $this->paging_type_default; 434 } 435 436 /** 437 * Render dropdown for choosing number of pages to break content over. 236 438 * 237 439 * @uses get_option, apply_filters, esc_attr, selected 238 * @return string440 * @return void 239 441 */ 240 442 public function settings_field_num_pages() { … … 244 446 ?> 245 447 <select name="<?php echo esc_attr( $this->option_name_num_pages ); ?>"> 246 <?php for ( $i = 2; $i <= $max_pages; $i++ ) : ?>448 <?php for ( $i = 2; $i <= $max_pages; $i++ ) : ?> 247 449 <option value="<?php echo intval( $i ); ?>"<?php selected( (int) $i, (int) $num_pages ); ?>><?php echo intval( $i ); ?></option> 248 450 <?php endfor; ?> … … 252 454 253 455 /** 254 * Sanitize number of pages input 255 * 256 * @param int $num_pages 456 * Sanitize number of pages input. 457 * 458 * @param int $num_pages Number of pages to split to. 257 459 * @uses apply_filters 258 460 * @return int … … 263 465 264 466 /** 265 * Render input field for specifying approximate number of words each page should contain 467 * Render input field for specifying approximate number of words each page should contain. 266 468 * 267 469 * @uses get_option, apply_filters, esc_attr, selected 268 * @return string470 * @return void 269 471 */ 270 472 public function settings_field_num_words() { 271 473 $num_words = apply_filters( 'autopaging_num_words', get_option( $this->option_name_num_words ) ) 272 474 ?> 273 <input name="<?php echo esc_attr( $this->option_name_num_words ); ?>" value="<?php echo esc_attr( $num_words ); ?>" size="4" /> 274 275 <p class="description"><?php _e( 'If chosen, each page will contain approximately this many words, depending on paragraph lengths.', 'autopaging' ); ?></p> 475 <input 476 name="<?php echo esc_attr( $this->option_name_num_words ); ?>" 477 value="<?php echo esc_attr( $num_words ); ?>" 478 class="small-text" 479 type="number" 480 step="1" 481 min="1" 482 /> 483 484 <p class="description"><?php esc_html_e( 'If chosen, each page will contain approximately this many words, depending on paragraph lengths.', 'autopaging' ); ?></p> 276 485 <?php 277 486 } 278 487 279 488 /** 280 * Sanitize number of words input. No fewer than 10 by default, filterable by autopaging_max_num_words281 * 282 * @param int $num_words 489 * Sanitize number of words input. No fewer than 10 by default, filterable by `autopaging_max_num_words`. 490 * 491 * @param int $num_words Number of words to split on. 283 492 * @uses apply_filters 284 493 * @return int … … 295 504 296 505 /** 297 * Add autopaging metabox 506 * Add autopaging metabox. 298 507 * 299 508 * @uses add_metabox, __ 300 509 * @action add_meta_box 301 * @return null510 * @return void 302 511 */ 303 512 public function action_add_meta_boxes() { 304 513 foreach ( $this->post_types as $post_type ) { 305 add_meta_box( 'autopaging', __( 'Post Autopaging', 'autopaging' ), array( $this, 'meta_box_autopaging' ), $post_type, 'side' ); 306 } 307 } 308 309 /** 310 * Render autopaging metabox 311 * 312 * @param object $post 514 if ( 515 function_exists( 'use_block_editor_for_post_type' ) && 516 use_block_editor_for_post_type( $post_type ) 517 ) { 518 continue; 519 } 520 521 add_meta_box( 'autopaging', __( 'Autopaging', 'autopaging' ), array( $this, 'meta_box_autopaging' ), $post_type, 'side' ); 522 } 523 } 524 525 /** 526 * Render autopaging metabox. 527 * 528 * @param object $post Post object. 313 529 * @uses esc_attr, checked, _e, __, wp_nonce_field 314 * @return string530 * @return void 315 531 */ 316 532 public function meta_box_autopaging( $post ) { 317 ?>533 ?> 318 534 <p> 319 <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> 535 <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 ) ); ?> /> 536 <label for="<?php echo esc_attr( $this->meta_key_disable_autopaging ); ?>_checkbox"> 537 <?php esc_html_e( 'Disable autopaging for this post?', 'autopaging' ); ?> 538 </label> 320 539 </p> 321 <p class="description"><?php _e( 'Check the box above to prevent this post from automatically being split over multiple pages.', 'autopaging' ); ?></p> 322 <p class="description"><?php printf( __( 'Note that if the %1$s Quicktag is used to manually page this post, automatic paging won\'t be applied, regardless of the setting above.', 'autopaging' ), '<code><!--nextpage--></code>' ); ?></p> 323 <?php 540 <p class="description"><?php esc_html__( 'Check the box above to prevent this post from automatically being split over multiple pages.', 'autopaging' ); ?></p> 541 <p class="description"> 542 <?php 543 printf( 544 /* translators: 1. Quicktag code example. */ 545 esc_html__( 546 'Note that if the %1$s Quicktag is used to manually page this post, automatic paging won\'t be applied, regardless of the setting above.', 547 'autopaging' 548 ), 549 // No need to escape a class constant. 550 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 551 '<code>' . htmlentities( static::QUICKTAG, ENT_QUOTES ) . '</code>' 552 ); 553 ?> 554 </p> 555 556 <?php 324 557 wp_nonce_field( $this->meta_key_disable_autopaging, $this->meta_key_disable_autopaging . '_wpnonce' ); 325 558 } 326 559 327 560 /** 328 * Save autopaging metabox 329 * 330 * @param int $post_id 561 * Save autopaging metabox. 562 * 563 * @param int $post_id Post ID. 331 564 * @uses DOING_AUTOSAVE, wp_verify_nonce, update_post_meta, delete_post_meta 332 565 * @action save_post … … 334 567 */ 335 568 public function action_save_post( $post_id ) { 336 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 569 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 337 570 return; 571 } 572 573 if ( 574 function_exists( 'use_block_editor_for_post' ) 575 && use_block_editor_for_post( $post_id ) 576 ) { 577 return; 578 } 338 579 339 580 if ( isset( $_POST[ $this->meta_key_disable_autopaging . '_wpnonce' ] ) && wp_verify_nonce( $_POST[ $this->meta_key_disable_autopaging . '_wpnonce' ], $this->meta_key_disable_autopaging ) ) { 340 $disable = isset( $_POST[ $this->meta_key_disable_autopaging ] ) ? true : false;341 342 if ( $disable ) 581 $disable = isset( $_POST[ $this->meta_key_disable_autopaging ] ); 582 583 if ( $disable ) { 343 584 update_post_meta( $post_id, $this->meta_key_disable_autopaging, true ); 344 else585 } else { 345 586 delete_post_meta( $post_id, $this->meta_key_disable_autopaging ); 587 } 346 588 } 347 589 } … … 351 593 * Only applied if the post type matches specified options and post doesn't already contain the Quicktag. 352 594 * 353 * @param array $posts 595 * @param array $posts Array of posts retrieved by WP_Query. 354 596 * @uses is_admin, get_post_meta, absint, apply_filters 355 597 * @filter the_posts … … 357 599 */ 358 600 public function filter_the_posts( $posts ) { 359 if ( ! is_admin() ) { 360 foreach( $posts as $the_post ) { 361 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 ) ) { 362 //In-time filtering of number of pages to break over, based on post data. If value is less than 2, nothing should be done. 363 $num_pages = absint( apply_filters( 'autopaging_num_pages', absint( $this->num_pages ), $the_post ) ); 364 $num_words = absint( apply_filters( 'autopaging_num_words', absint( $this->num_words ), $the_post ) ); 365 366 if ( $num_pages < 2 && empty( $num_words ) ) 367 continue; 368 369 //Start with post content, but alias to protect the raw content. 370 $content = $the_post->post_content; 371 372 //Normalize post content to simplify paragraph counting and automatic paging. Accounts for content that hasn't been cleaned up by TinyMCE. 373 $content = preg_replace( '#<p>(.+?)</p>#i', "$1\r\n\r\n", $content ); 374 $content = preg_replace( '#<br(\s*/)?>#i', "\r\n", $content ); 375 376 //Count paragraphs 377 $count = preg_match_all( '#\r\n\r\n#', $content, $matches ); 378 379 //Keep going, if we have something to count. 380 if ( is_int( $count ) && 0 < $count ) { 381 //Explode content at double (or more) line breaks 382 $content = explode( "\r\n\r\n", $content ); 383 384 switch ( get_option( $this->option_name_paging_type, $this->paging_type_default ) ) { 385 case 'words' : 386 $word_counter = 0; 387 388 // Count words per paragraph and break after the paragraph that exceeds the set threshold 389 foreach ( $content as $index => $paragraph ) { 390 $paragraph_words = count( preg_split( '/\s+/', strip_tags( $paragraph ) ) ); 391 $word_counter += $paragraph_words; 392 393 if ( $word_counter >= $num_words ) { 394 $content[ $index ] .= '<!--nextpage-->'; 395 $word_counter = 0; 396 } else { 397 continue; 398 } 399 } 400 401 unset( $word_counter ); 402 unset( $index ); 403 unset( $paragraph ); 404 unset( $paragraph_words ); 405 406 break; 407 408 case 'pages' : 409 default : 410 //Count number of paragraphs content was exploded to 411 $count = count( $content ); 412 413 //Determine when to insert Quicktag 414 $insert_every = $count / $num_pages; 415 $insert_every_rounded = round( $insert_every ); 416 417 //If number of pages is greater than number of paragraphs, put each paragraph on its own page 418 if ( $num_pages > $count ) { 419 $insert_every_rounded = 1; 420 } 421 422 //Set initial counter position. 423 $i = $count - 1 == $num_pages ? 2 : 1; 424 425 //Loop through content pieces and append Quicktag as is appropriate 426 foreach ( $content as $key => $value ) { 427 if ( $key + 1 == $count ) { 428 break; 429 } 430 431 if ( ( $key + 1 ) == ( $i * $insert_every_rounded ) ) { 432 $content[ $key ] = $content[ $key ] . '<!--nextpage-->'; 433 $i++; 434 } 435 } 436 437 //Clean up 438 unset( $count ); 439 unset( $insert_every ); 440 unset( $insert_every_rounded ); 441 unset( $key ); 442 unset( $value ); 443 444 break; 601 if ( is_admin() ) { 602 return $posts; 603 } 604 605 $paging_type = get_option( 606 $this->option_name_paging_type, 607 $this->paging_type_default 608 ); 609 610 foreach ( $posts as &$the_post ) { 611 if ( 612 ! in_array( 613 $the_post->post_type, 614 $this->post_types, 615 true 616 ) 617 ) { 618 continue; 619 } 620 621 if ( 622 preg_match( 623 '#' . static::QUICKTAG . '#i', 624 $the_post->post_content 625 ) 626 ) { 627 continue; 628 } 629 630 if ( 631 (bool) get_post_meta( 632 $the_post->ID, 633 $this->meta_key_disable_autopaging, 634 true 635 ) 636 ) { 637 continue; 638 } 639 640 $num_pages = absint( 641 apply_filters( 642 'autopaging_num_pages', 643 absint( $this->num_pages ), 644 $the_post 645 ) 646 ); 647 $num_words = absint( 648 apply_filters( 649 'autopaging_num_words', 650 absint( $this->num_words ), 651 $the_post 652 ) 653 ); 654 655 if ( $num_pages < 2 && empty( $num_words ) ) { 656 continue; 657 } 658 659 if ( 660 function_exists( 'has_blocks' ) 661 && has_blocks( $the_post ) 662 ) { 663 $this->filter_block_editor_post( 664 $the_post, 665 $paging_type, 666 $num_words, 667 $num_pages 668 ); 669 } else { 670 $this->filter_classic_editor_post( 671 $the_post, 672 $paging_type, 673 $num_words, 674 $num_pages 675 ); 676 } 677 } 678 679 return $posts; 680 } 681 682 /** 683 * Add pagination Quicktag to post authored in the Classic Editor. 684 * 685 * @param WP_Post|object $the_post Post object. 686 * @param string $paging_type How to split post. 687 * @param int $num_words Number of words to split on. 688 * @param int $num_pages Number of pages to split to. 689 * @return void 690 */ 691 protected function filter_classic_editor_post( 692 &$the_post, 693 $paging_type, 694 $num_words, 695 $num_pages 696 ) { 697 // Start with post content, but alias to protect the raw content. 698 $content = $the_post->post_content; 699 700 // Normalize post content to simplify paragraph counting and automatic paging. Accounts for content that hasn't been cleaned up by TinyMCE. 701 $content = preg_replace( '#<p>(.+?)</p>#i', "$1\r\n\r\n", $content ); 702 $content = preg_replace( '#<br(\s*/)?>#i', "\r\n", $content ); 703 $content = explode( "\r\n\r\n", $content ); 704 705 // Count number of paragraphs content was exploded to. 706 $count = count( $content ); 707 708 // Nothing to do, goodbye. 709 if ( $count <= 1 ) { 710 return; 711 } 712 713 switch ( $paging_type ) { 714 case 'words': 715 $word_counter = 0; 716 717 // Count words per paragraph and break after the paragraph that exceeds the set threshold. 718 foreach ( $content as $index => $paragraph ) { 719 $word_counter += mb_strlen( 720 wp_strip_all_tags( 721 $paragraph 722 ) 723 ); 724 725 if ( $word_counter >= $num_words ) { 726 $content[ $index ] .= static::QUICKTAG; 727 $word_counter = 0; 728 } 729 } 730 731 // Prevent the last page from being empty. 732 $last_page = array_pop( $content ); 733 if ( 734 static::QUICKTAG === 735 substr( 736 $last_page, 737 - static::QUICKTAG_LENGTH 738 ) 739 ) { 740 $content[] = substr( 741 $last_page, 742 0, 743 strlen( $last_page ) - static::QUICKTAG_LENGTH 744 ); 745 } else { 746 $content[] = $last_page; 747 } 748 749 break; 750 751 case 'pages': 752 default: 753 $frequency = $this->get_insertion_frequency_by_pages( 754 $count, 755 $num_pages 756 ); 757 758 $i = 1; 759 760 // Loop through content pieces and append Quicktag as is appropriate. 761 foreach ( $content as $key => $value ) { 762 if ( $this->is_at_end_for_pages( $key, $count ) ) { 763 break; 764 } 765 766 if ( 767 $this->is_insertion_point_for_pages( 768 $key, 769 $i, 770 $frequency 771 ) 772 ) { 773 $content[ $key ] .= static::QUICKTAG; 774 $i++; 775 } 776 } 777 778 break; 779 } 780 781 // Reunite content. 782 $content = implode( "\r\n\r\n", $content ); 783 784 // And, overwrite the original content. 785 $the_post->post_content = $content; 786 } 787 788 /** 789 * Add pagination block to post authored in the Block Editor. 790 * 791 * @param WP_Post $the_post Post object. 792 * @param string $paging_type How to split post. 793 * @param int $num_words Number of words to split on. 794 * @param int $num_pages Number of pages to split to. 795 * @return void 796 */ 797 protected function filter_block_editor_post( 798 &$the_post, 799 $paging_type, 800 $num_words, 801 $num_pages 802 ) { 803 $blocks = parse_blocks( $the_post->post_content ); 804 $new_blocks = array(); 805 806 switch ( $paging_type ) { 807 case 'words': 808 $word_count = 0; 809 810 foreach ( $blocks as $block ) { 811 $new_blocks[] = $block; 812 813 if ( 814 in_array( 815 $block['blockName'], 816 $this->supported_block_types_for_word_counts, 817 true 818 ) 819 ) { 820 $word_count += mb_strlen( 821 trim( 822 wp_strip_all_tags( 823 $block['innerHTML'] 824 ) 825 ) 826 ); 827 828 if ( $word_count >= $num_words ) { 829 $new_blocks[] = $this->get_parsed_nextpage_block(); 830 831 $word_count = 0; 445 832 } 446 447 //Reunite content448 $content = implode( "\r\n\r\n", $content );449 450 //And, overwrite the original content451 $the_post->post_content = $content;452 833 } 453 454 //Lastly, clean up.455 unset( $num_pages );456 unset( $num_words );457 unset( $content );458 unset( $count );459 834 } 460 } 461 } 462 463 return $posts; 835 836 $last_block = array_pop( $new_blocks ); 837 if ( $this->get_parsed_nextpage_block() !== $last_block ) { 838 $new_blocks[] = $last_block; 839 } 840 break; 841 842 case 'pages': 843 default: 844 $count = count( $blocks ); 845 846 $frequency = $this->get_insertion_frequency_by_pages( 847 $count, 848 $num_pages 849 ); 850 851 $i = 1; 852 853 foreach ( $blocks as $key => $block ) { 854 $new_blocks[] = $block; 855 856 if ( $this->is_at_end_for_pages( $key, $count ) ) { 857 break; 858 } 859 860 if ( 861 $this->is_insertion_point_for_pages( 862 $key, 863 $i, 864 $frequency 865 ) 866 ) { 867 $new_blocks[] = $this->get_parsed_nextpage_block(); 868 $i++; 869 } 870 } 871 break; 872 } 873 874 $the_post->post_content = serialize_blocks( $new_blocks ); 875 } 876 877 /** 878 * Determine after how many paragraphs a page break should be inserted. 879 * 880 * @param int $count Total number of paragraphs. 881 * @param int $num_pages Desired number of pages. 882 * @return int 883 */ 884 protected function get_insertion_frequency_by_pages( $count, $num_pages ) { 885 $frequency = (int) round( $count / $num_pages ); 886 887 // If number of pages is greater than number of paragraphs, put each paragraph on its own page. 888 if ( $num_pages > $count ) { 889 $frequency = 1; 890 } 891 892 return $frequency; 893 } 894 895 /** 896 * Determine if more page breaks should be inserted. 897 * 898 * @param int $key Current position in array of blocks. 899 * @param int $count Total number of paragraphs. 900 * @return bool 901 */ 902 protected function is_at_end_for_pages( $key, $count ) { 903 return ( $key + 1 ) === $count; 904 } 905 906 /** 907 * Determine if current loop iteration is where a page break is expected. 908 * 909 * @param int $loop_key Current position in array of blocks. 910 * @param int $insertion_iterator Current number of page breaks inserted. 911 * @param int $insertion_frequency After this many blocks a should break be 912 * inserted. 913 * @return bool 914 */ 915 protected function is_insertion_point_for_pages( 916 $loop_key, 917 $insertion_iterator, 918 $insertion_frequency 919 ) { 920 return ( $loop_key + 1 ) === 921 ( $insertion_iterator * $insertion_frequency ); 922 } 923 924 /** 925 * Create parsed representation of block for insertion in list of post's 926 * blocks. 927 * 928 * @return array 929 */ 930 protected function get_parsed_nextpage_block() { 931 static $block; 932 933 if ( ! $block ) { 934 $_block = parse_blocks( 935 '<!-- wp:nextpage --> 936 ' . static::QUICKTAG . ' 937 <!-- /wp:nextpage -->' 938 ); 939 940 $block = array_shift( $_block ); 941 } 942 943 return $block; 464 944 } 465 945 } 466 new Automatically_Paginate_Posts; 946 947 new Automatically_Paginate_Posts(); 467 948 ?> -
automatically-paginate-posts/trunk/languages/automatically-paginate-posts.pot
r2268447 r2749430 1 # Copyright (C) 202 0Erick Hitter & Oomph, Inc.1 # Copyright (C) 2022 Erick Hitter & Oomph, Inc. 2 2 # This file is distributed under the same license as the Automatically Paginate Posts package. 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Automatically Paginate Posts 0. 2\n"5 "Project-Id-Version: Automatically Paginate Posts 0.3\n" 6 6 "Report-Msgid-Bugs-To: " 7 7 "https://wordpress.org/support/plugin/automatically-paginate-posts\n" 8 "POT-Creation-Date: 202 0-03-26 17:10:56+00:00\n"8 "POT-Creation-Date: 2022-06-29 04:16:26+00:00\n" 9 9 "MIME-Version: 1.0\n" 10 10 "Content-Type: text/plain; charset=utf-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "PO-Revision-Date: 202 0-MO-DA HO:MI+ZONE\n"12 "PO-Revision-Date: 2022-MO-DA HO:MI+ZONE\n" 13 13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" 14 14 "Language-Team: LANGUAGE <[email protected]>\n" 15 "X-Generator: grunt-wp-i18n 0.5.4\n"16 "X-Poedit-KeywordsList: "17 "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"18 "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"19 15 "Language: en\n" 20 16 "Plural-Forms: nplurals=2; plural=(n != 1);\n" 21 17 "X-Poedit-Country: United States\n" 22 18 "X-Poedit-SourceCharset: UTF-8\n" 19 "X-Poedit-KeywordsList: " 20 "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_" 21 "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n" 23 22 "X-Poedit-Basepath: ../\n" 24 23 "X-Poedit-SearchPath-0: .\n" 25 24 "X-Poedit-Bookmarks: \n" 26 25 "X-Textdomain-Support: yes\n" 26 "X-Generator: grunt-wp-i18n 1.0.3\n" 27 28 #: automatically-paginate-posts.php:193 29 msgid "Post types can only be retrieved after the \"init\" hook." 30 msgstr "" 27 31 28 32 #. Plugin Name of the plugin/theme … … 30 34 msgstr "" 31 35 32 #: automatically-paginate-posts.php: 13536 #: automatically-paginate-posts.php:327 33 37 msgid "Supported post types:" 34 38 msgstr "" 35 39 36 #: automatically-paginate-posts.php: 13640 #: automatically-paginate-posts.php:328 37 41 msgid "Split post by:" 38 42 msgstr "" 39 43 40 #: automatically-paginate-posts.php: 21041 msgid "Total number of pages: "44 #: automatically-paginate-posts.php:406 45 msgid "Total number of pages:" 42 46 msgstr "" 43 47 44 #: automatically-paginate-posts.php: 21145 msgid "Approximate words per page: "48 #: automatically-paginate-posts.php:407 49 msgid "Approximate words per page:" 46 50 msgstr "" 47 51 48 #: automatically-paginate-posts.php: 27552 #: automatically-paginate-posts.php:484 49 53 msgid "" 50 54 "If chosen, each page will contain approximately this many words, depending " … … 52 56 msgstr "" 53 57 54 #: automatically-paginate-posts.php: 30555 msgid " PostAutopaging"58 #: automatically-paginate-posts.php:521 59 msgid "Autopaging" 56 60 msgstr "" 57 61 58 #: automatically-paginate-posts.php:321 62 #: automatically-paginate-posts.php:537 63 msgid "Disable autopaging for this post?" 64 msgstr "" 65 66 #: automatically-paginate-posts.php:540 59 67 msgid "" 60 68 "Check the box above to prevent this post from automatically being split " … … 62 70 msgstr "" 63 71 64 #: automatically-paginate-posts.php:322 72 #: automatically-paginate-posts.php:545 73 #. translators: 1. Quicktag code example. 65 74 msgid "" 66 75 "Note that if the %1$s Quicktag is used to manually page this post, " 67 76 "automatic paging won't be applied, regardless of the setting above." 77 msgstr "" 78 79 #: inc/class-block-editor.php:65 80 msgid "Whether or not to disable pagination for this post." 68 81 msgstr "" 69 82 -
automatically-paginate-posts/trunk/readme.txt
r2268447 r2749430 4 4 Tags: paginate, nextpage, Quicktag 5 5 Requires at least: 3.4 6 Tested up to: 5.47 Stable tag: 0. 26 Tested up to: 6.0 7 Stable tag: 0.3 8 8 License: GPLv2 or later 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Automatically paginate posts by inserting the <!--nextpage--> Quicktag into WordPress posts, pages, or custom post type content.11 Automatically paginate posts by inserting the `<!--nextpage-->` Quicktag. 12 12 13 == D ESCRIPTION==13 == Description == 14 14 15 Automatically paginate WordPress content by inserting the <!--nextpage--> Quicktag.15 Automatically paginate WordPress content by inserting the `<!--nextpage-->` Quicktag at intervals controlled by plugin's settings. 16 16 17 Option is provided to control wh at post types are automatically paginated (default is just `post`). Supports `post`, `page`, and any public custom post types.17 Option is provided to control which post types are automatically paginated (default is the "Post" post type). Supports any public custom post types (non-public types are supported via the `autopaging_post_types` filter). 18 18 19 19 Option is also provided to specify how many pages content should be broken out over, or how many words should be included per page. … … 21 21 == Installation == 22 22 23 1. Upload automatically-paginate-posts to /wp-content/plugins/.23 1. Upload automatically-paginate-posts to `/wp-content/plugins/`. 24 24 2. Activate plugin through the WordPress Plugins menu. 25 3. Configure plugin by going to Settings > Reading.25 3. Configure plugin by going to **Settings > Reading**. 26 26 27 27 == Frequently Asked Questions == 28 28 29 = Where do I set the plugin's options =29 = Where do I set the plugin's options? = 30 30 The plugin's options are added to the built-in **Reading** settings page in WordPress. 31 31 32 = Can I disable the plugin's functionality for specific posts, pages, or custom post type objects? =33 Yes, the plugin adds a metabox to individual items in supported post types that allows the autopaging to be disabled on a per-post basis.32 = Can I disable the plugin's functionality for specific content? = 33 Yes, the plugin adds a metabox (Classic Editor) and a sidebar component (Block Editor) to individual items in supported post types that allows the autopaging to be disabled on a per-post basis. 34 34 35 35 = How can I add support for my custom post type? = 36 Navigate to Settings > Readingin WP Admin to enable this plugin for your custom post type.36 Navigate to **Settings > Reading** in WP Admin to enable this plugin for your custom post type. 37 37 38 38 You can also use the filter `autopaging_post_types` to add support by appending your post type's name to the array. … … 45 45 * `autopaging_num_pages` - change the number of pages content is displayed on at runtime. Filter provides access to the full post object in addition to the number of pages. 46 46 * `autopaging_num_words` - change the number of words displayed per page at runtime. Filter provides access to the full post object in addition to the number of words. 47 * `autopaging_supported_block_types_for_word_counts` - specify which block types are considered when splitting a block-editor post by word count. 47 48 48 49 == Changelog == 50 51 = 0.3 = 52 * Add support for content authored in block editor (Gutenberg). 53 * Add native block-editor control to replace legacy metabox. 54 * Fix bug that created empty pages. 55 56 = 0.2 = 57 * Allow for number of words to be specified instead of number of pages. 49 58 50 59 = 0.1 = 51 60 * Initial release. 52 61 62 == Upgrade Notice == 63 64 = 0.3 = 65 Add support for block editor and fix bug that created empty pages. 66 53 67 = 0.2 = 54 * Allow for number of words to be specified instead of number of pages. 55 56 == Upgrade Notice == 68 Allow for number of words to be specified instead of number of pages. 57 69 58 70 = 0.1 = 59 71 Initial release 60 61 = 0.2 =62 * Allow for number of words to be specified instead of number of pages.
Note: See TracChangeset
for help on using the changeset viewer.