PluginProbe
WP Reading Progress / 1.6.1
WP Reading Progress v1.6.1
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.6.1, at wp-reading-progress.php

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