-
Notifications
You must be signed in to change notification settings - Fork 10
/
rt-reading-time.php
385 lines (322 loc) · 12.6 KB
/
rt-reading-time.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
<?php
/**
* The plugin creation file.
*
* @package Reading_Time_WP
*
* Plugin Name: Reading Time WP
* Plugin URI: https://jasonyingling.me/reading-time-wp/
* Description: Add an estimated reading time to your posts.
* Version: 2.0.16
* Author: Jason Yingling
* Author URI: https://jasonyingling.me
* License: GPL2
* Text Domain: reading-time-wp
* Domain Path: /languages
*
* Copyright 2019 Jason Yingling (email : yingling017@gmail.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class for calculating reading time.
*
* The class that contains all functions for calculating reading time.
*
* @since 1.0.0
*/
class Reading_Time_WP {
/**
* Add label option using add_option if it does not already exist.
*
* @var string
*/
public $reading_time;
/**
* Allowed HTML tags for setting fields.
*
* @var array
*/
public $rtwp_kses = array(
'br' => array(),
'em' => array(),
'b' => array(),
'strong' => array(),
);
/**
* Construct function for Reading Time WP.
*
* Create default settings on plugin activation. Add shortcode. Add options.
* Add menu page.
*
* @since 1.0.0
*/
public function __construct() {
load_plugin_textdomain( 'reading-time-wp', false, basename( dirname( __FILE__ ) ) . '/languages/' );
$default_settings = array(
'label' => __( 'Reading Time: ', 'reading-time-wp' ),
'postfix' => __( 'minutes', 'reading-time-wp' ),
'postfix_singular' => __( 'minute', 'reading-time-wp' ),
'wpm' => 300,
'before_content' => true,
'before_excerpt' => true,
'exclude_images' => false,
'include_shortcodes' => false,
);
$rtwp_post_type_args = array(
'public' => true,
);
$rtwp_post_type_args = apply_filters( 'rtwp_post_type_args', $rtwp_post_type_args );
$rtwp_post_types = get_post_types( $rtwp_post_type_args );
foreach ( $rtwp_post_types as $rtwp_post_type ) {
if ( 'attachment' === $rtwp_post_type ) {
continue;
}
$default_settings['post_types'][ $rtwp_post_type ] = true;
}
$rt_reading_time_options = get_option( 'rt_reading_time_options' );
add_shortcode( 'rt_reading_time', array( $this, 'rt_reading_time' ) );
add_option( 'rt_reading_time_options', $default_settings );
add_action( 'admin_menu', array( $this, 'rt_reading_time_admin_actions' ) );
$rt_before_content = isset($rt_reading_time_options['before_content'] ) ? $this->rt_convert_boolean( $rt_reading_time_options['before_content'] ) : false;
if ( isset( $rt_before_content ) && true === $rt_before_content ) {
add_filter( 'the_content', array( $this, 'rt_add_reading_time_before_content' ) );
}
$rt_before_excerpt = isset( $rt_reading_time_options['before_excerpt'] ) ? $this->rt_convert_boolean( $rt_reading_time_options['before_excerpt'] ) : false;
if ( isset( $rt_before_excerpt ) && true === $rt_before_excerpt ) {
add_filter( 'get_the_excerpt', array( $this, 'rt_add_reading_time_before_excerpt' ), 1000 );
}
}
/**
* Calculate the reading time of a post.
*
* Gets the post content, counts the images, strips shortcodes, and strips tags.
* Then counts the words. Converts images into a word count. And outputs the
* total reading time.
*
* @since 1.0.0
*
* @param int $rt_post_id The Post ID.
* @param array $rt_options The options selected for the plugin.
* @return string|int The total reading time for the article or string if it's 0.
*/
public function rt_calculate_reading_time( $rt_post_id, $rt_options ) {
$rt_content = get_post_field( 'post_content', $rt_post_id );
$number_of_images = substr_count( strtolower( $rt_content ), '<img ' );
if ( ! isset( $rt_options['include_shortcodes'] ) ) {
$rt_content = strip_shortcodes( $rt_content );
}
$rt_content = wp_strip_all_tags( $rt_content );
$word_count = count( preg_split( '/\s+/', $rt_content ) );
if ( isset( $rt_options['exclude_images'] ) && ! $rt_options['exclude_images'] ) {
// Calculate additional time added to post by images.
$additional_words_for_images = $this->rt_calculate_images( $number_of_images, $rt_options['wpm'] );
$word_count += $additional_words_for_images;
}
$word_count = apply_filters( 'rtwp_filter_wordcount', $word_count );
$this->reading_time = $word_count / $rt_options['wpm'];
// If the reading time is 0 then return it as < 1 instead of 0.
if ( 1 > $this->reading_time ) {
$this->reading_time = __( '< 1', 'reading-time-wp' );
} else {
$this->reading_time = ceil( $this->reading_time );
}
return $this->reading_time;
}
/**
* Adds additional reading time for images
*
* Calculate additional reading time added by images in posts. Based on calculations by Medium. https://blog.medium.com/read-time-and-you-bc2048ab620c
*
* @since 1.1.0
*
* @param int $total_images number of images in post.
* @param array $wpm words per minute.
* @return int Additional time added to the reading time by images.
*/
public function rt_calculate_images( $total_images, $wpm ) {
$additional_time = 0;
// For the first image add 12 seconds, second image add 11, ..., for image 10+ add 3 seconds.
for ( $i = 1; $i <= $total_images; $i++ ) {
if ( $i >= 10 ) {
$additional_time += 3 * (int) $wpm / 60;
} else {
$additional_time += ( 12 - ( $i - 1 ) ) * (int) $wpm / 60;
}
}
return $additional_time;
}
/**
* Output the proper postfix for the reading time.
*
* @since 2.0.5
*
* @param string|int $time The total reading time for the article or string if it's 0.
* @param string $singular The postfix singular label.
* @param string $multiple The postfix label.
*
* @return string $postfix The calculated postfix.
*/
public function rt_add_postfix( $time, $singular, $multiple ) {
if ( (int) $time > 1 ) {
$postfix = $multiple;
} else {
$postfix = $singular;
}
$postfix = apply_filters( 'rt_edit_postfix', $postfix, $time, $singular, $multiple );
return $postfix;
}
/**
* Creates the [rt_reading_time] shortcode.
*
* @since 1.0.0
*
* @param array $atts The attributes of the shortcode.
* @param string $content The content of the shortcode.
* @return string.
*/
public function rt_reading_time( $atts, $content = null ) {
$atts = shortcode_atts(
array(
'label' => '',
'postfix' => '',
'postfix_singular' => '',
'post_id' => '',
),
$atts,
'rt_reading_time'
);
$rt_reading_time_options = get_option( 'rt_reading_time_options' );
// If post_id attribute was specified that exists, then use that to calculate read time, else use the current post ID.
$rt_post = $atts['post_id'] && ( get_post_status( $atts['post_id'] ) ) ? $atts['post_id'] : get_the_ID();
$this->rt_calculate_reading_time( $rt_post, $rt_reading_time_options );
$calculated_postfix = $this->rt_add_postfix( $this->reading_time, $atts['postfix_singular'], $atts['postfix'] );
return '<span class="span-reading-time rt-reading-time"><span class="rt-label rt-prefix">' . wp_kses( $atts['label'], $this->rtwp_kses ) . '</span> <span class="rt-time"> ' . esc_html( $this->reading_time ) . '</span> <span class="rt-label rt-postfix">' . wp_kses( $calculated_postfix, $this->rtwp_kses ) . '</span></span>';
}
/**
* Include the Reading Time Admin page.
*
* The reading-time-admin.php contains everything needed to handle
* the options in the admin screen.
*
* @since 1.0.0
*/
public function rt_reading_time_admin() {
include 'rt-reading-time-admin.php';
}
/**
* Create the options page for the admin screen.
*
* @since 1.0.0
*/
public function rt_reading_time_admin_actions() {
add_options_page(
__( 'Reading Time WP Settings', 'reading-time-wp' ),
__( 'Reading Time WP', 'reading-time-wp' ),
'manage_options',
'rt-reading-time-settings',
array( $this, 'rt_reading_time_admin' )
);
}
/**
* Adds the reading time before the_content.
*
* If the option is selected to automatically add the reading time before
* the_content, the reading time is calculated and added to the beginning of the_content.
*
* @since 1.0.0
*
* @param string $content The original post content.
* @return string The post content with reading time prepended.
*/
public function rt_add_reading_time_before_content( $content ) {
$rt_reading_time_options = get_option( 'rt_reading_time_options' );
// Get the post type of the current post.
$rtwp_current_post_type = get_post_type();
if ( ! isset( $rt_reading_time_options['post_types'] ) ) {
$rt_reading_time_options['post_types'] = array();
}
// If the current post type isn't included in the array of post types or it is and set to false, don't display it.
if ( ! isset( $rt_reading_time_options['post_types'][ $rtwp_current_post_type ] ) || ! $rt_reading_time_options['post_types'][ $rtwp_current_post_type ] ) {
return $content;
}
$original_content = $content;
$rt_post = get_the_ID();
$this->rt_calculate_reading_time( $rt_post, $rt_reading_time_options );
$label = $rt_reading_time_options['label'];
$postfix = $rt_reading_time_options['postfix'];
$postfix_singular = $rt_reading_time_options['postfix_singular'];
if ( in_array( 'get_the_excerpt', $GLOBALS['wp_current_filter'], true ) ) {
return $content;
}
$calculated_postfix = $this->rt_add_postfix( $this->reading_time, $postfix_singular, $postfix );
$content = '<span class="rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">' . wp_kses( $label, $this->rtwp_kses ) . '</span> <span class="rt-time">' . esc_html( $this->reading_time ) . '</span> <span class="rt-label rt-postfix">' . wp_kses( $calculated_postfix, $this->rtwp_kses ) . '</span></span>';
$content .= $original_content;
return $content;
}
/**
* Adds the reading time before the_excerpt.
*
* If the options is selected to automatically add the reading time before
* the_excerpt, the reading time is calculated and added to the beginning of the_excerpt.
*
* @since 1.0.0
*
* @param string $content The original content of the_excerpt.
* @return string The excerpt content with reading time prepended.
*/
public function rt_add_reading_time_before_excerpt( $content ) {
$rt_reading_time_options = get_option( 'rt_reading_time_options' );
// Get the post type of the current post.
$rtwp_current_post_type = get_post_type();
if ( ! isset( $rt_reading_time_options['post_types'] ) ) {
$rt_reading_time_options['post_types'] = array();
}
// If the current post type isn't included in the array of post types or it is and set to false, don't display it.
if ( ! isset( $rt_reading_time_options['post_types'][ $rtwp_current_post_type ] ) || ! $rt_reading_time_options['post_types'][ $rtwp_current_post_type ] ) {
return $content;
}
$original_content = $content;
$rt_post = get_the_ID();
$this->rt_calculate_reading_time( $rt_post, $rt_reading_time_options );
$label = $rt_reading_time_options['label'];
$postfix = $rt_reading_time_options['postfix'];
$postfix_singular = $rt_reading_time_options['postfix_singular'];
$calculated_postfix = $this->rt_add_postfix( $this->reading_time, $postfix_singular, $postfix );
$content = '<span class="rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">' . wp_kses( $label, $this->rtwp_kses ) . '</span> <span class="rt-time">' . esc_html( $this->reading_time ) . '</span> <span class="rt-label rt-postfix">' . wp_kses( $calculated_postfix, $this->rtwp_kses ) . '</span></span> ';
$content .= $original_content;
return $content;
}
/**
* A function to fix some bad legacy code using a string for true and false.
*
* @param string $value A string set to either 'true' or 'false'.
*/
public function rt_convert_boolean( $value ) {
if ( 'true' === $value || true === $value ) {
return true;
} else {
return false;
}
}
}
function rtwp_init() {
global $reading_time_wp;
$reading_time_wp = new Reading_Time_WP();
}
add_action( 'init', 'rtwp_init' );