PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 5.4.0
WP Popular Posts v5.4.0
4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 5.0.0 5.0.1 5.0.2 5.1.0 5.2.0 5.2.1 5.2.2 5.2.3 5.2.4 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.1.0 6.1.1 6.1.2 6.1.3 6.1.4 6.2.0 6.2.1 6.3.0 6.3.1 6.3.2 6.3.3 6.3.4 6.4.0 6.4.1 6.4.2 7.0.0 7.0.1 7.1.0 7.2.0 7.3.0 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.3.7 7.3.8 7.4.0 trunk 2.3.7 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.2 4.0.3 4.0.5 4.0.6
wordpress-popular-posts / src / Front / Front.php
wordpress-popular-posts / src / Front Last commit date
Front.php 4 years ago
Front.php
535 lines
1 <?php
2 /**
3 * The public-facing functionality of the plugin.
4 *
5 * Defines hooks to enqueue the public-specific stylesheet and JavaScript.
6 *
7 * @package WordPressPopularPosts
8 * @subpackage WordPressPopularPosts/Front
9 * @author Hector Cabrera <me@cabrerahector.com>
10 */
11
12 namespace WordPressPopularPosts\Front;
13
14 use WordPressPopularPosts\Helper;
15 use WordPressPopularPosts\Output;
16 use WordPressPopularPosts\Query;
17
18 class Front {
19
20 /**
21 * Plugin options.
22 *
23 * @var array $config
24 * @access private
25 */
26 private $config;
27
28 /**
29 * Translate object.
30 *
31 * @var \WordPressPopularPosts\Translate $translate
32 * @access private
33 */
34 private $translate;
35
36 /**
37 * Output object.
38 *
39 * @var \WordPressPopularPosts\Output $output
40 * @access private
41 */
42 private $output;
43
44 /**
45 * Construct.
46 *
47 * @since 5.0.0
48 * @param array $config Admin settings.
49 * @param \WordPressPopularPosts\Translate $translate Translate class.
50 */
51 public function __construct(array $config, \WordPressPopularPosts\Translate $translate, \WordPressPopularPosts\Output $output)
52 {
53 $this->config = $config;
54 $this->translate = $translate;
55 $this->output = $output;
56 }
57
58 /**
59 * WordPress public-facing hooks.
60 *
61 * @since 5.0.0
62 */
63 public function hooks()
64 {
65 add_shortcode('wpp', [$this, 'wpp_shortcode']);
66 add_action('wp_head', [$this, 'inline_loading_css']);
67 add_action('wp_ajax_update_views_ajax', [$this, 'update_views']);
68 add_action('wp_ajax_nopriv_update_views_ajax', [$this, 'update_views']);
69 add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']);
70 add_filter('script_loader_tag', [$this, 'convert_inline_js_into_json'], 10, 3);
71 }
72
73 /**
74 *
75 */
76 public function inline_loading_css()
77 {
78 ?>
79 <style>
80 @-webkit-keyframes bgslide {
81 from {
82 background-position-x: 0;
83 }
84 to {
85 background-position-x: -200%;
86 }
87 }
88
89 @keyframes bgslide {
90 from {
91 background-position-x: 0;
92 }
93 to {
94 background-position-x: -200%;
95 }
96 }
97
98 .wpp-widget-placeholder {
99 margin: 0 auto;
100 width: 60px;
101 height: 3px;
102 background: #dd3737;
103 background: -webkit-gradient(linear, left top, right top, from(#dd3737), color-stop(10%, #571313), to(#dd3737));
104 background: linear-gradient(90deg, #dd3737 0%, #571313 10%, #dd3737 100%);
105 background-size: 200% auto;
106 border-radius: 3px;
107 -webkit-animation: bgslide 1s infinite linear;
108 animation: bgslide 1s infinite linear;
109 }
110 </style>
111 <?php
112 }
113
114 /**
115 * Enqueues public facing assets.
116 *
117 * @since 5.0.0
118 */
119 public function enqueue_assets()
120 {
121 // Enqueue WPP's stylesheet.
122 if ( $this->config['tools']['css'] ) {
123 $theme_file = get_stylesheet_directory() . '/wpp.css';
124
125 if ( @is_file($theme_file) ) {
126 wp_enqueue_style('wordpress-popular-posts-css', get_stylesheet_directory_uri() . "/wpp.css", [], WPP_VERSION, 'all');
127 } // Load stock stylesheet
128 else {
129 wp_enqueue_style('wordpress-popular-posts-css', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/css/wpp.css', [], WPP_VERSION, 'all');
130 }
131 }
132
133 // Enqueue WPP's library.
134 $is_single = 0;
135
136 if (
137 ( 0 == $this->config['tools']['log']['level'] && ! is_user_logged_in() )
138 || ( 1 == $this->config['tools']['log']['level'] )
139 || ( 2 == $this->config['tools']['log']['level'] && is_user_logged_in() )
140 ) {
141 $is_single = Helper::is_single();
142 }
143
144 wp_register_script('wpp-js', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/wpp.min.js', [], WPP_VERSION, false);
145 $params = [
146 'sampling_active' => (int) $this->config['tools']['sampling']['active'],
147 'sampling_rate' => (int) $this->config['tools']['sampling']['rate'],
148 'ajax_url' => esc_url_raw(rest_url('wordpress-popular-posts/v1/popular-posts')),
149 'api_url' => esc_url_raw(rest_url('wordpress-popular-posts')),
150 'ID' => (int) $is_single,
151 'token' => wp_create_nonce('wp_rest'),
152 'lang' => function_exists('PLL') ? $this->translate->get_current_language() : 0,
153 'debug' => (int) WP_DEBUG
154 ];
155 wp_enqueue_script('wpp-js');
156 wp_add_inline_script('wpp-js', json_encode($params), 'before');
157 }
158
159 /**
160 * Converts inline script tag into type=application/json.
161 *
162 * This function mods the original script tag as printed
163 * by WordPress which contains the data for the wpp_params
164 * object into a JSON script. This improves compatibility
165 * with Content Security Policy (CSP).
166 *
167 * @since 5.2.0
168 * @param string $tag
169 * @param string $handle
170 * @param string $src
171 * @return string $tag
172 */
173 function convert_inline_js_into_json($tag, $handle, $src)
174 {
175 if ( 'wpp-js' === $handle ) {
176 // id attribute found, replace it
177 if ( false !== strpos($tag, 'wpp-js-js-before') ) {
178 $tag = str_replace('wpp-js-js-before', 'wpp-json', $tag);
179 } // id attribute missing, let's add it
180 else {
181 $pos = strpos($tag, '>');
182 $tag = substr_replace($tag, ' id="wpp-json">', $pos, 1);
183 }
184
185 // type attribute found, replace it
186 if ( false !== strpos($tag, 'type') ) {
187 $pos = strpos($tag, 'text/javascript');
188
189 if ( false !== $pos )
190 $tag = substr_replace($tag, 'application/json', $pos, strlen('text/javascript'));
191 } // type attribute missing, let's add it
192 else {
193 $pos = strpos($tag, '>');
194 $tag = substr_replace($tag, ' type="application/json">', $pos, 1);
195 }
196 }
197
198 return $tag;
199 }
200
201 /**
202 * Updates views count on page load via AJAX.
203 *
204 * @since 2.0.0
205 */
206 public function update_views()
207 {
208 if ( ! wp_verify_nonce($_POST['token'], 'wpp-token') || ! Helper::is_number($_POST['wpp_id']) ) {
209 die( "WPP: Oops, invalid request!" );
210 }
211
212 $nonce = $_POST['token'];
213 $post_ID = $_POST['wpp_id'];
214 $exec_time = 0;
215
216 $start = Helper::microtime_float();
217 $result = $this->update_views_count($post_ID);
218 $end = Helper::microtime_float();
219 $exec_time += round($end - $start, 6);
220
221 if ( $result ) {
222 die("WPP: OK. Execution time: " . $exec_time . " seconds");
223 }
224
225 die("WPP: Oops, could not update the views count!");
226 }
227
228 /**
229 * Updates views count.
230 *
231 * @since 1.4.0
232 * @access private
233 * @global object $wpdb
234 * @param int $post_ID
235 * @return bool|int FALSE if query failed, TRUE on success
236 */
237 private function update_views_count($post_ID) {
238 /*
239 TODO:
240 For WordPress Multisite, we must define the DIEONDBERROR constant for database errors to display like so:
241 <?php define( 'DIEONDBERROR', true ); ?>
242 */
243 global $wpdb;
244 $table = $wpdb->prefix . "popularposts";
245 $wpdb->show_errors();
246
247 // Get translated object ID
248 $post_ID = $this->translate->get_object_id(
249 $post_ID,
250 get_post_type($post_ID),
251 true,
252 $this->translate->get_default_language()
253 );
254 $now = Helper::now();
255 $curdate = Helper::curdate();
256 $views = ( $this->config['tools']['sampling']['active'] )
257 ? $this->config['tools']['sampling']['rate']
258 : 1;
259
260 // Allow WP themers / coders perform an action
261 // before updating views count
262 if ( has_action('wpp_pre_update_views') )
263 do_action('wpp_pre_update_views', $post_ID, $views);
264
265 // Update all-time table
266 $result1 = $wpdb->query($wpdb->prepare(
267 "INSERT INTO {$table}data
268 (postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
269 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, last_viewed = %s;",
270 $post_ID,
271 $now,
272 $now,
273 $views,
274 $views,
275 $now
276 ));
277
278 // Update range (summary) table
279 $result2 = $wpdb->query($wpdb->prepare(
280 "INSERT INTO {$table}summary
281 (postid, pageviews, view_date, view_datetime) VALUES (%d, %d, %s, %s)
282 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, view_datetime = %s;",
283 $post_ID,
284 $views,
285 $curdate,
286 $now,
287 $views,
288 $now
289 ));
290
291 if ( !$result1 || !$result2 )
292 return false;
293
294 // Allow WP themers / coders perform an action
295 // after updating views count
296 if ( has_action('wpp_post_update_views' ))
297 do_action('wpp_post_update_views', $post_ID);
298
299 return true;
300 }
301
302 /**
303 * WPP shortcode handler.
304 *
305 * @since 1.4.0
306 * @param array $atts User defined attributes in shortcode tag
307 * @return string
308 */
309 public function wpp_shortcode($atts = null) {
310 /**
311 * @var string $header
312 * @var int $limit
313 * @var int $offset
314 * @var string $range
315 * @var bool $freshness
316 * @var string $order_by
317 * @var string $post_type
318 * @var string $pid
319 * @var string $cat
320 * @var string $author
321 * @var int $title_length
322 * @var int $title_by_words
323 * @var int $excerpt_length
324 * @var int $excerpt_format
325 * @var int $excerpt_by_words
326 * @var int $thumbnail_width
327 * @var int $thumbnail_height
328 * @var bool $rating
329 * @var bool $stats_comments
330 * @var bool $stats_views
331 * @var bool $stats_author
332 * @var bool $stats_date
333 * @var string $stats_date_format
334 * @var bool $stats_category
335 * @var string $wpp_start
336 * @var string $wpp_end
337 * @var string $header_start
338 * @var string $header_end
339 * @var string $post_html
340 * @var bool $php
341 */
342 extract(shortcode_atts([
343 'header' => '',
344 'limit' => 10,
345 'offset' => 0,
346 'range' => 'daily',
347 'time_unit' => 'hour',
348 'time_quantity' => 24,
349 'freshness' => false,
350 'order_by' => 'views',
351 'post_type' => 'post',
352 'pid' => '',
353 'cat' => '',
354 'taxonomy' => 'category',
355 'term_id' => '',
356 'author' => '',
357 'title_length' => 0,
358 'title_by_words' => 0,
359 'excerpt_length' => 0,
360 'excerpt_format' => 0,
361 'excerpt_by_words' => 0,
362 'thumbnail_width' => 0,
363 'thumbnail_height' => 0,
364 'rating' => false,
365 'stats_comments' => false,
366 'stats_views' => true,
367 'stats_author' => false,
368 'stats_date' => false,
369 'stats_date_format' => 'F j, Y',
370 'stats_category' => false,
371 'stats_taxonomy' => false,
372 'wpp_start' => '<ul class="wpp-list">',
373 'wpp_end' => '</ul>',
374 'header_start' => '<h2>',
375 'header_end' => '</h2>',
376 'post_html' => '',
377 'theme' => '',
378 'php' => false
379 ], $atts, 'wpp'));
380
381 // possible values for "Time Range" and "Order by"
382 $time_units = ["minute", "hour", "day", "week", "month"];
383 $range_values = ["daily", "last24hours", "weekly", "last7days", "monthly", "last30days", "all", "custom"];
384 $order_by_values = ["comments", "views", "avg"];
385
386 $shortcode_ops = [
387 'title' => strip_tags($header),
388 'limit' => ( ! empty($limit ) && Helper::is_number($limit) && $limit > 0 ) ? $limit : 10,
389 'offset' => ( ! empty($offset) && Helper::is_number($offset) && $offset >= 0 ) ? $offset : 0,
390 'range' => ( in_array($range, $range_values) ) ? $range : 'daily',
391 'time_quantity' => ( ! empty($time_quantity ) && Helper::is_number($time_quantity) && $time_quantity > 0 ) ? $time_quantity : 24,
392 'time_unit' => ( in_array($time_unit, $time_units) ) ? $time_unit : 'hour',
393 'freshness' => empty($freshness) ? false : $freshness,
394 'order_by' => ( in_array($order_by, $order_by_values) ) ? $order_by : 'views',
395 'post_type' => empty($post_type) ? 'post,page' : $post_type,
396 'pid' => rtrim(preg_replace('|[^0-9,]|', '', $pid), ","),
397 'cat' => rtrim(preg_replace('|[^0-9,-]|', '', $cat), ","),
398 'taxonomy' => empty($taxonomy) ? 'category' : $taxonomy,
399 'term_id' => rtrim(preg_replace('|[^0-9,;-]|', '', $term_id), ","),
400 'author' => rtrim(preg_replace('|[^0-9,]|', '', $author), ","),
401 'shorten_title' => [
402 'active' => ( ! empty($title_length) && Helper::is_number($title_length) && $title_length > 0 ),
403 'length' => ( ! empty($title_length) && Helper::is_number($title_length) ) ? $title_length : 0,
404 'words' => ( ! empty($title_by_words) && Helper::is_number($title_by_words) && $title_by_words > 0 ),
405 ],
406 'post-excerpt' => [
407 'active' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) && $excerpt_length > 0 ),
408 'length' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) ) ? $excerpt_length : 0,
409 'keep_format' => ( ! empty($excerpt_format) && Helper::is_number($excerpt_format) && $excerpt_format > 0 ),
410 'words' => ( ! empty($excerpt_by_words) && Helper::is_number($excerpt_by_words) && $excerpt_by_words > 0 ),
411 ],
412 'thumbnail' => [
413 'active' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ),
414 'width' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ) ? $thumbnail_width : 0,
415 'height' => ( ! empty($thumbnail_height) && Helper::is_number($thumbnail_height) && $thumbnail_height > 0 ) ? $thumbnail_height : 0,
416 ],
417 'rating' => empty($rating) ? false : $rating,
418 'stats_tag' => [
419 'comment_count' => empty($stats_comments) ? false : $stats_comments,
420 'views' => empty($stats_views) ? false : $stats_views,
421 'author' => empty($stats_author) ? false : $stats_author,
422 'date' => [
423 'active' => empty($stats_date) ? false : $stats_date,
424 'format' => empty($stats_date_format) ? 'F j, Y' : $stats_date_format
425 ],
426 'category' => empty($stats_category) ? false : $stats_category,
427 'taxonomy' => [
428 'active' => empty($stats_taxonomy) ? false : $stats_taxonomy,
429 'name' => empty($taxonomy) ? 'category' : $taxonomy,
430 ]
431 ],
432 'markup' => [
433 'custom_html' => true,
434 'wpp-start' => empty($wpp_start) ? '' : $wpp_start,
435 'wpp-end' => empty($wpp_end) ? '' : $wpp_end,
436 'title-start' => empty($header_start) ? '' : $header_start,
437 'title-end' => empty($header_end) ? '' : $header_end,
438 'post-html' => empty($post_html) ? '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>' : $post_html
439 ],
440 'theme' => [
441 'name' => trim($theme)
442 ]
443 ];
444
445 // Post / Page / CTP filter
446 $ids = array_filter(explode(",", $shortcode_ops['pid']), 'is_numeric');
447 // Got no valid IDs, clear
448 if ( empty($ids) ) {
449 $shortcode_ops['pid'] = '';
450 }
451
452 // Category filter
453 $ids = array_filter(explode(",", $shortcode_ops['cat']), 'is_numeric');
454 // Got no valid IDs, clear
455 if ( empty($ids) ) {
456 $shortcode_ops['cat'] = '';
457 }
458
459 // Author filter
460 $ids = array_filter(explode( ",", $shortcode_ops['author']), 'is_numeric');
461 // Got no valid IDs, clear
462 if ( empty($ids) ) {
463 $shortcode_ops['author'] = '';
464 }
465
466 $shortcode_content = '';
467 $cached = false;
468
469 // is there a title defined by user?
470 if (
471 ! empty($header)
472 && ! empty($header_start)
473 && ! empty($header_end)
474 ) {
475 $shortcode_content .= htmlspecialchars_decode($header_start, ENT_QUOTES) . $header . htmlspecialchars_decode($header_end, ENT_QUOTES);
476 }
477
478 // Return cached results
479 if ( $this->config['tools']['cache']['active'] ) {
480
481 $key = md5(json_encode($shortcode_ops));
482 $popular_posts = \WordPressPopularPosts\Cache::get($key);
483
484 if ( false === $popular_posts ) {
485 $popular_posts = new Query($shortcode_ops);
486
487 $time_value = $this->config['tools']['cache']['interval']['value']; // eg. 5
488 $time_unit = $this->config['tools']['cache']['interval']['time']; // eg. 'minute'
489
490 // No popular posts found, check again in 1 minute
491 if ( ! $popular_posts->get_posts() ) {
492 $time_value = 1;
493 $time_unit = 'minute';
494 }
495
496 \WordPressPopularPosts\Cache::set(
497 $key,
498 $popular_posts,
499 $time_value,
500 $time_unit
501 );
502 }
503
504 $cached = true;
505
506 } // Get popular posts
507 else {
508 $popular_posts = new Query($shortcode_ops);
509 }
510
511 $this->output->set_data($popular_posts->get_posts());
512 $this->output->set_public_options($shortcode_ops);
513 $this->output->build_output();
514
515 $shortcode_content .= $this->output->get_output();
516
517 // Sanitize and return shortcode HTML
518 $allowed_tags = wp_kses_allowed_html('post');
519
520 if ( isset($allowed_tags['form']) ) {
521 unset($allowed_tags['form']);
522 }
523
524 if ( ! empty($shortcode_ops['theme']['name']) ) {
525 $allowed_tags['style'] = [
526 'id' => 1,
527 'nonce' => 1,
528 ];
529 }
530
531 return wp_kses($shortcode_content, $allowed_tags);
532 }
533
534 }
535