PluginProbe
WP Reading Progress / 1.7.0
WP Reading Progress v1.7.0
trunk 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.6.0 1.6.1 1.7.0
wp-reading-progress / wp-reading-progress.php

wp-reading-progress.php in WP Reading Progress 1.7.0, at wp-reading-progress.php

554 lines 18.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: WP Reading Progress
4 Plugin URI: https://github.com/joerivanveen/wp-reading-progress
5 Description: Light weight customizable reading progress bar. Great UX on longreads. Includes estimated reading time (beta).
6 Version: 1.7.0
7 Requires at least: 4.9
8 Tested up to: 7.1
9 Requires PHP: 5.6
10 Author: Joeri van Veen
11 Author URI: https://wp-developer.eu
12 License: GPLv3
13 Text Domain: wp-reading-progress
14 Domain Path: /languages/
15 */
16 defined( 'ABSPATH' ) || die();
17 // This is plugin nr. 6 by Ruige hond. It identifies as: ruigehond006.
18 const RUIGEHOND006_VERSION = '1.7.0';
19 // Register install hook
20 register_activation_hook( __FILE__, 'ruigehond006_install' );
21 // Startup the plugin
22 add_action( 'init', 'ruigehond006_run' );
23 add_action( 'wp', 'ruigehond006_start' );
24 /**
25 * the actual plugin on the frontend
26 */
27 function ruigehond006_run() {
28 if ( is_admin() ) {
29 load_plugin_textdomain( 'wp-reading-progress', '', dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
30 wp_enqueue_style( 'wp-color-picker' );
31 wp_enqueue_script( 'wp-color-picker' );
32 wp_enqueue_script( 'ruigehond006_admin_javascript', plugin_dir_url( __FILE__ ) . 'admin.min.js', 'wp-color-picker', RUIGEHOND006_VERSION, true );
33 add_action( 'admin_init', 'ruigehond006_settings' );
34 add_action( 'admin_menu', 'ruigehond006_menuitem' );
35 add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'ruigehond006_settingslink' ); // settings link on plugins page
36 add_action( 'add_meta_boxes', 'ruigehond006_meta_box_add' ); // in the box the user can activate the bar for a single post
37 add_action( 'save_post', 'ruigehond006_meta_box_save' );
38 }
39 }
40
41 function ruigehond006_start() {
42 if ( is_admin() ) {
43 return;
44 }
45 $post_identifier = null;
46 // check if we're using the progress bar here
47 $options = get_option( 'ruigehond006' );
48 $post_id = get_the_ID();
49 if ( is_singular() ) {
50 if ( ( isset( $options['post_types'] ) && in_array( get_post_type( $post_id ), $options['post_types'] ) )
51 || 'yes' === get_post_meta( $post_id, '_ruigehond006_show', true )
52 ) {
53 if ( isset( $options['include_comments'] ) ) {
54 $post_identifier = 'body';
55 } else {
56 $post_identifier = '.' . implode( '.', get_post_class( '', $post_id ) );
57 }
58 }
59 } elseif ( isset( $options['archives'] ) && isset( $options['post_types'] ) && in_array( get_post_type(), $options['post_types'] ) ) {
60 $post_identifier = 'body';
61 }
62 if ( null !== $post_identifier ) {
63 wp_enqueue_script( 'ruigehond006_javascript', plugin_dir_url( __FILE__ ) . 'wp-reading-progress.min.js', false, RUIGEHOND006_VERSION, true );
64 wp_localize_script( 'ruigehond006_javascript', 'ruigehond006_c', array_merge(
65 $options, array(
66 'post_identifier' => $post_identifier,
67 'post_id' => $post_id,
68 )
69 ) );
70 if ( ! isset( $options['no_css'] ) ) {
71 add_action( 'wp_head', 'ruigehond006_stylesheet' );
72 }
73 }
74 /**
75 * separate ert section...
76 */
77 if ( isset( $options['use_ert'] ) ) {
78 if ( isset( $options['ert_speed'] ) && (int) $options['ert_speed'] > 0 ) {
79 if ( isset( $options['use_ert_shortcode'] ) ) {
80 add_shortcode( 'wp-reading-progress-ert', 'ruigehond006_shortcode' );
81 }
82 if ( isset( $options['use_ert_excerpt'] ) ) {
83 add_filter( 'get_the_excerpt', 'ruigehond006_ert', 99 );
84 }
85 //add_filter( 'get_the_content', 'ruigehond006_ert', 99 );
86 //add_filter( 'the_content', 'ruigehond006_ert', 99 );
87 }
88 }
89 }
90
91 function ruigehond006_stylesheet() {
92 echo '<style>#ruigehond006_wrap{z-index:10001;position:fixed;display:block;left:0;width:100%;margin:0;overflow:visible}#ruigehond006_inner{position:absolute;height:0;width:inherit;background-color:rgba(255,255,255,.2);-webkit-transition:height .4s;transition:height .4s}html[dir=rtl] #ruigehond006_wrap{text-align:right}#ruigehond006_bar{width:0;height:100%;background-color:transparent}</style>';
93 }
94
95 function ruigehond006_shortcode( $attributes = [], $content = null, $short_code = 'wp-reading-progress-ert' ) {
96 return ruigehond006_ert();
97 }
98
99 function ruigehond006_ert( $content = null, $args = null ) {
100 global $post;
101 if ( ! $post ) {
102 return '';
103 }
104 $speed = 250;
105 $options = get_option( 'ruigehond006' );
106 if ( isset( $options['ert_speed'] ) && (int) $options['ert_speed'] > 0 ) {
107 $speed = (int) $options['ert_speed'];
108 }
109 $minutes = max( 1, round( $time = ( str_word_count( wp_strip_all_tags( $post->post_content ) ) / $speed ), 0 ) );
110 if (
111 isset( $options['ert_snippet'] )
112 && 1 === substr_count( ( $str = $options['ert_snippet'] ), '%d' )
113 ) {
114 $str = sprintf( $str, $minutes );
115 } else {
116 $str = sprintf( '%d” read', $minutes );
117 }
118 $snippet = sprintf( "<span class='wp-reading-progress-ert post-$post->ID' data-ert='$time' data-minutes='$minutes'>%s</span>", $str );
119
120 if ( $content ) {
121 return "$snippet $content";
122 }
123
124 return $snippet;
125 }
126
127 // meta box exposes setting to display reading progress for an individual post
128 // https://developer.wordpress.org/reference/functions/add_meta_box/
129 function ruigehond006_meta_box_add( $post_type = null ) {
130 if ( ! get_the_ID() ) {
131 return;
132 }
133 $option = get_option( 'ruigehond006' );
134 if ( isset( $option['post_types'] ) && in_array( $post_type, $option['post_types'] ) ) {
135 return; // you can't set this if the bar is displayed by default on this post type
136 }
137 add_meta_box( // WP function.
138 'ruigehond006', // Unique ID
139 'WP Reading Progress', // Box title
140 'ruigehond006_meta_box', // Content callback, must be of type callable
141 $post_type, // Post type
142 'normal',
143 'low',
144 array( 'option' => $option )
145 );
146 }
147
148 function ruigehond006_meta_box( $post, $obj ) {
149 //$option = $obj['args']['option']; // not used at this moment
150 wp_nonce_field( 'ruigehond006_save', 'ruigehond006_nonce' );
151 echo '<input type="checkbox" id="ruigehond006_checkbox" name="ruigehond006_show"';
152 if ( 'yes' === get_post_meta( $post->ID, '_ruigehond006_show', true ) ) {
153 echo ' checked="checked"';
154 }
155 echo '/> <label for="ruigehond006_checkbox">';
156 echo esc_html__( 'display reading progress bar', 'wp-reading-progress' );
157 echo '</label>';
158 }
159
160 function ruigehond006_meta_box_save( $post_id ) {
161 if ( ! isset( $_POST['ruigehond006_nonce'] )
162 || ! wp_verify_nonce( sanitize_title( wp_unslash( $_POST['ruigehond006_nonce'] ) ), 'ruigehond006_save' )
163 ) {
164 return;
165 }
166 if ( ! current_user_can( 'edit_post', $post_id ) ) {
167 return;
168 }
169 if ( isset( $_POST['ruigehond006_show'] ) ) {
170 add_post_meta( $post_id, '_ruigehond006_show', 'yes', true );
171 } else {
172 delete_post_meta( $post_id, '_ruigehond006_show' );
173 }
174 }
175
176 /**
177 * manage global settings
178 */
179 function ruigehond006_settings() {
180 /**
181 * register a new setting, call this function for each setting
182 * Arguments: (Array)
183 * - group, the same as in settings_fields, for security / nonce etc.
184 * - the name of the options
185 * - the function that will validate the options, valid options are automatically saved by WP
186 */
187 register_setting( 'ruigehond006', 'ruigehond006', 'ruigehond006_settings_validate' );
188 // register a new section in the page
189 add_settings_section(
190 'progress_bar_settings', // section id
191 esc_html__( 'Set your options', 'wp-reading-progress' ), // title
192 function () {
193 echo '<p>';
194 echo esc_html__( 'This plugin displays a reading progress bar on your selected post types.', 'wp-reading-progress' );
195 echo ' ';
196 echo esc_html__( 'When it does not find a single article, it uses the whole page to calculate reading progress.', 'wp-reading-progress' );
197 echo '<br>';
198 echo esc_html__( 'For post types which are switched off in the settings, you can activate the bar per post in the post-edit screen.', 'wp-reading-progress' );
199 echo '<br>';
200 echo esc_html__( 'Please use valid input or the bar might not display.', 'wp-reading-progress' );
201 echo '</p>';
202 }, //callback
203 'ruigehond006' // page
204 );
205 if ( false === ( $option = get_option( 'ruigehond006' ) ) ) {
206 ruigehond006_add_defaults();
207 $option = get_option( 'ruigehond006' );
208 }
209 /* @since 1.5.4: check if the required placeholders are in the translated string */
210 /* translators: %1$s: link for top, %1$s: link for bottom */
211 $string = esc_html__( 'Use %1$s or %2$s, or any VALID selector of a fixed element where the bar can be appended to, e.g. a sticky menu.', 'wp-reading-progress' );
212 if ( false === strpos( $string, '%1$s' ) || false === strpos( $string, '%2$s' ) ) {
213 $string = 'Use %1$s or %2$s, or any VALID selector of a fixed element where the bar can be appended to, e.g. a sticky menu.';
214 }
215 ruigehond006_add_settings_field(
216 'bar_attach',
217 'text',
218 esc_html__( 'Stick the bar to this element', 'wp-reading-progress' ), // title
219 $option,
220 sprintf( $string, '<a>top</a>', '<a>bottom</a>' ) . ' ' . esc_html__( 'Multiple selectors can be separated by commas, the bar will be attached to the first one visible.', 'wp-reading-progress' )
221 );
222 ruigehond006_add_settings_field(
223 'stick_relative',
224 'checkbox',
225 esc_html__( 'How to stick', 'wp-reading-progress' ),
226 $option,
227 esc_html__( 'If the bar is too wide, try relative positioning by checking this box, or attach it to another element.', 'wp-reading-progress' )
228 );
229 ruigehond006_add_settings_field(
230 'bar_color',
231 'wp-color',
232 esc_html__( 'Color of the progress bar', 'wp-reading-progress' ), // title
233 $option
234 );
235 // ruigehond006_add_settings_field(
236 // 'bar_color_dark_mode',
237 // 'color',
238 // esc_html__('Color when in dark mode', 'wp-reading-progress'), // title
239 // $option,
240 // sprintf(__('Depends on a certain class added to the body or html container, including one of the following strings: %s', 'wp-reading-progress'), '*dark-mode*, *night-mode*')
241 // );
242 /* @since 1.5.4: check if the required placeholders are in the translated string */
243 /* translators: %1$s: link to insert .5vh, %2$s: link to insert 6px */
244 $string = esc_html__( 'Thickness based on screen height is recommended, e.g. %1$s. But you can also use pixels, e.g. %2$s.', 'wp-reading-progress' );
245 if ( false === strpos( $string, '%1$s' ) || false === strpos( $string, '%2$s' ) ) {
246 $string = 'Thickness based on screen height is recommended, e.g. %s. But you can also use pixels, e.g. %s.';
247 }
248 ruigehond006_add_settings_field(
249 'bar_height',
250 'text-short',
251 esc_html__( 'Progress bar thickness', 'wp-reading-progress' ), // title
252 $option,
253 sprintf( $string, '<a>.5vh</a>', '<a>6px</a>' )
254 );
255 ruigehond006_add_settings_field(
256 'aria_label',
257 'text',
258 esc_html__( 'Aria label', 'wp-reading-progress' ), // title
259 $option,
260 esc_html__( 'Explain the purpose of this reading bar to screenreaders', 'wp-reading-progress' )
261 );
262 ruigehond006_add_settings_field(
263 'mark_it_zero',
264 'checkbox',
265 esc_html__( 'Make bar start at 0%', 'wp-reading-progress' ),
266 $option,
267 esc_html__( 'Yes please', 'wp-reading-progress' )
268 );
269 ruigehond006_add_settings_field(
270 'include_comments',
271 'checkbox',
272 esc_html__( 'On single post page', 'wp-reading-progress' ),
273 $option,
274 esc_html__( 'use whole page to calculate reading progress', 'wp-reading-progress' )
275 );
276 add_settings_field(
277 'ruigehond006_post_types',
278 // #TRANSLATORS: this is followed by a list of the available post_types
279 esc_html__( 'Show reading progress on', 'wp-reading-progress' ),
280 function ( $args ) {
281 $post_types = [];
282 if ( isset( $args['option']['post_types'] ) ) {
283 $post_types = $args['option']['post_types'];
284 }
285 foreach ( get_post_types( array( 'public' => true ) ) as $post_type ) {
286 echo '<label><input type="checkbox" name="ruigehond006[post_types][]" value="', esc_html( $post_type ), '"';
287 if ( in_array( $post_type, $post_types ) ) {
288 echo ' checked="checked"';
289 }
290 echo '/>', esc_html( $post_type ), '</label><br>';
291 }
292 echo '<div class="ruigehond006 explanation"><em>';
293 echo esc_html__( 'For unchecked post types you can enable the reading progress bar per post on the post edit page.', 'wp-reading-progress' );
294 echo '</em></div>';
295 },
296 'ruigehond006',
297 'progress_bar_settings',
298 [ 'option' => $option ] // args
299 );
300 ruigehond006_add_settings_field(
301 'archives',
302 'checkbox',
303 esc_html__( 'And on their archives', 'wp-reading-progress' ),
304 $option
305 );
306 ruigehond006_add_settings_field(
307 'no_css',
308 'checkbox',
309 'No css',
310 $option,
311 esc_html__( 'necessary css for the reading bar is included elsewhere', 'wp-reading-progress' )
312 );
313 add_settings_section(
314 'ert_settings', // section id
315 esc_html__( 'Estimated reading time', 'wp-reading-progress' ) . ' (BETA)', // title
316 function () {
317 echo '<p>';
318 echo esc_html__( 'If you want to display estimated reading time (ert) and your theme does not support it, you can activate it here.', 'wp-reading-progress' );
319 echo '<br>';
320 echo esc_html__( 'When activated, you need to set some extra options. Upon deactivation, those options will be removed as well.', 'wp-reading-progress' );
321 echo '<br>';
322 echo esc_html__( 'The ert (snippet) will be output in a span with css class `wp-reading-progress-ert` for you to style.', 'wp-reading-progress' );
323 echo '</p>';
324 }, //callback
325 'ruigehond006' // page
326 );
327 ruigehond006_add_settings_field(
328 'use_ert',
329 'checkbox',
330 esc_html__( 'Activate ert', 'wp-reading-progress' ),
331 $option,
332 esc_html__( 'Check to activate ert, leave unchecked if you have ert in your theme.', 'wp-reading-progress' ),
333 'ert_settings'
334 );
335 if ( isset( $option['use_ert'] ) ) {
336 ruigehond006_add_settings_field(
337 'use_ert_shortcode',
338 'checkbox',
339 esc_html__( 'Use shortcode', 'wp-reading-progress' ),
340 $option,
341 esc_html__( 'Switch this on to display ert using the shortcode: [wp-reading-progress-ert].', 'wp-reading-progress' ),
342 'ert_settings'
343 );
344 ruigehond006_add_settings_field(
345 'use_ert_excerpt',
346 'checkbox',
347 esc_html__( 'Add before excerpt', 'wp-reading-progress' ),
348 $option,
349 esc_html__( 'This will add the snippet before any excerpt. Note that some themes strip the html.', 'wp-reading-progress' ),
350 'ert_settings'
351 );
352 ruigehond006_add_settings_field(
353 'ert_speed',
354 'text-short',
355 esc_html__( 'Reading speed', 'wp-reading-progress' ), // title
356 $option,
357 esc_html__( 'Average reading speed in words per minute, integers only. Used to estimate reading time. Usual is something between 200 and 300.', 'wp-reading-progress' ),
358 'ert_settings'
359 );
360 ruigehond006_add_settings_field(
361 'ert_snippet',
362 'text-short',
363 esc_html__( 'Snippet text', 'wp-reading-progress' ), // title
364 $option,
365 /* translators: %d is literal %d, will not be replaced */
366 esc_html__( 'Define your own text here. Mandatory placeholder `%d` will display the minutes.', 'wp-reading-progress' ),
367 'ert_settings'
368 );
369 }
370 }
371
372 function ruigehond006_add_settings_field( $name, $type, $title, $option, $explanation = null, $section = 'progress_bar_settings' ) {
373 add_settings_field(
374 "ruigehond006_$name",
375 $title,
376 function ( $args ) {
377 switch ( $args['type'] ) {
378 case 'checkbox':
379 echo '<label><input type="checkbox" name="ruigehond006[';
380 echo esc_attr( $args['name'] );
381 echo ']"';
382 if ( $args['value'] ) {
383 echo ' checked="checked"';
384 }
385 echo '/> ';
386 if ( isset( $args['explanation'] ) ) {
387 echo wp_kses_post( $args['explanation'] );
388 }
389 echo '</label>';
390
391 return;
392 // case 'wp-color':
393 // echo '<input type="text" class="ruigehond006_colorpicker" name="ruigehond006[';
394 // echo esc_attr( $args['name'] );
395 // echo ']" value="';
396 // echo esc_attr( $args['value'] );
397 // echo '"/>';
398 // break;
399 case 'color':
400 echo '<input type="color" name="ruigehond006[';
401 echo esc_attr( $args['name'] );
402 echo ']" value="';
403 echo esc_attr( $args['value'] );
404 echo '"/>';
405 break;
406 default: // regular input
407 echo '<input type="text" name="ruigehond006[';
408 echo esc_attr( $args['name'] );
409 echo ']" value="';
410 echo esc_attr( $args['value'] );
411 if ( 'text-short' !== $args['type'] ) {
412 echo '" class="regular-text';
413 }
414 echo '"/>';
415 }
416 if ( isset( $args['explanation'] ) ) {
417 echo '<div class="ruigehond006 explanation"><em>';
418 echo wp_kses_post( $args['explanation'] );
419 echo '</em></div>';
420 }
421 },
422 'ruigehond006',
423 $section,
424 array(
425 'name' => $name,
426 'type' => $type,
427 'value' => isset( $option[ $name ] ) ? $option[ $name ] : '',
428 'explanation' => $explanation,
429 ) // args
430 );
431 }
432
433 function ruigehond006_settingspage() {
434 if ( ! current_user_can( 'manage_options' ) ) {
435 return;
436 }
437 echo '<div class="wrap"><h1>';
438 echo esc_html( get_admin_page_title() );
439 echo '</h1><form action="options.php" method="post">';
440 // output security fields for the registered setting
441 settings_fields( 'ruigehond006' );
442 // output setting sections and their fields
443 do_settings_sections( 'ruigehond006' );
444 // output save settings button
445 submit_button( esc_html__( 'Save Settings', 'wp-reading-progress' ) );
446 echo '</form></div>';
447 }
448
449 function ruigehond006_settingslink( $links ) {
450 $url = get_admin_url();
451 $txt = esc_html__( 'Settings', 'wp-reading-progress' );
452 $settings_link = "<a href=\"{$url}options-general.php?page=wp-reading-progress\">$txt</a>";
453 array_unshift( $links, $settings_link );
454
455 return $links;
456 }
457
458 function ruigehond006_menuitem() {
459 add_options_page(
460 'WP Reading Progress',
461 'WP Reading Progress',
462 'manage_options',
463 'wp-reading-progress',
464 'ruigehond006_settingspage'
465 );
466 }
467
468 function ruigehond006_settings_validate( $input ) {
469 $options = (array) get_option( 'ruigehond006' );
470 if ( false === is_array( $input ) ) {
471 return $options;
472 }
473
474 // these are all the settings we have
475 $settings = array(
476 'stick_relative',
477 'mark_it_zero',
478 'include_comments',
479 'archives',
480 'no_css',
481 'bar_color',
482 'bar_height',
483 'bar_attach',
484 'aria_label',
485 'post_types',
486 'use_ert',
487 'ert_speed',
488 'ert_snippet',
489 'use_ert_shortcode',
490 'use_ert_excerpt',
491 );
492
493 foreach ( $settings as $index => $key ) {
494 $value = null;
495 // note: this only works because we have 1 settings page.
496 if ( isset( $input[ $key ] ) ) {
497 $value = $input[ $key ];
498 }
499 switch ( $key ) {
500 // on / off flags
501 case 'stick_relative':
502 case 'mark_it_zero':
503 case 'include_comments':
504 case 'archives':
505 case 'no_css':
506 case 'use_ert':
507 case 'use_ert_shortcode':
508 case 'use_ert_excerpt':
509 // IMPORTANT: this is backwards compatible, 'false' options must not be present
510 if ( 'on' === $value ) {
511 $options[ $key ] = 'on';
512 } else {
513 unset( $options[ $key ] );
514 }
515 break;
516 case 'bar_color':
517 case 'bar_height':
518 case 'bar_attach':
519 case 'ert_snippet':
520 case 'aria_label':
521 $options[ $key ] = wp_strip_all_tags( $value );
522 break;
523 case 'ert_speed':
524 $options[ $key ] = (int) $value;
525 break;
526 case 'post_types': // array of strings
527 $options[ $key ] = array_map( static function ( $value ) {
528 return sanitize_key( $value );
529 }, $value );
530 break;
531 }
532 }
533
534 return $options;
535 }
536
537 /**
538 * plugin management functions
539 */
540 function ruigehond006_add_defaults() {
541 add_option( 'ruigehond006', array(
542 'bar_attach' => 'top',
543 'bar_color' => '#f1592a',
544 'bar_height' => '.5vh',
545 'post_types' => array( 'post' ),
546 ), '', true );
547 }
548
549 function ruigehond006_install() {
550 if ( ! get_option( 'ruigehond006' ) ) { // insert default settings:
551 ruigehond006_add_defaults();
552 }
553 }
554