PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 5.0.2
WP Popular Posts v5.0.2
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 6 years ago
Front.php
568 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_ajax_update_views_ajax', [$this, 'update_views']);
67 add_action('wp_ajax_nopriv_update_views_ajax', [$this, 'update_views']);
68 add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']);
69
70 add_action('wp_footer', [$this, 'theme_widgets']);
71
72 if ( $this->config['tools']['thumbnail']['lazyload'] ) {
73 add_action('wp_footer', [$this, 'lazyload_images']);
74 }
75 }
76
77 /**
78 * Enqueues public facing assets.
79 *
80 * @since 5.0.0
81 */
82 public function enqueue_assets()
83 {
84 // Enqueue WPP's stylesheet.
85 if ( $this->config['tools']['css'] ) {
86 $theme_file = get_stylesheet_directory() . '/wpp.css';
87
88 if ( @is_file($theme_file) ) {
89 wp_enqueue_style('wordpress-popular-posts-css', get_stylesheet_directory_uri() . "/wpp.css", [], WPP_VERSION, 'all');
90 } // Load stock stylesheet
91 else {
92 wp_enqueue_style('wordpress-popular-posts-css', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/css/wpp.css', [], WPP_VERSION, 'all');
93 }
94 }
95
96 // Enqueue WPP's library.
97 $is_single = 0;
98
99 if (
100 ( 0 == $this->config['tools']['log']['level'] && !is_user_logged_in() )
101 || ( 1 == $this->config['tools']['log']['level'] )
102 || ( 2 == $this->config['tools']['log']['level'] && is_user_logged_in() )
103 ) {
104 $is_single = Helper::is_single();
105 }
106
107 wp_register_script('wpp-js', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/wpp-5.0.0.min.js', [], WPP_VERSION, false);
108 $params = [
109 'sampling_active' => (int) $this->config['tools']['sampling']['active'],
110 'sampling_rate' => $this->config['tools']['sampling']['rate'],
111 'ajax_url' => esc_url_raw(rest_url('wordpress-popular-posts/v1/popular-posts')),
112 'ID' => $is_single,
113 'token' => wp_create_nonce('wp_rest'),
114 'debug' => WP_DEBUG
115 ];
116 wp_localize_script('wpp-js', 'wpp_params', $params);
117 wp_enqueue_script('wpp-js');
118 }
119
120 /**
121 * Updates views count on page load via AJAX.
122 *
123 * @since 2.0.0
124 */
125 public function update_views()
126 {
127 if ( ! wp_verify_nonce($_POST['token'], 'wpp-token') || ! Helper::is_number($_POST['wpp_id']) ) {
128 die( "WPP: Oops, invalid request!" );
129 }
130
131 $nonce = $_POST['token'];
132 $post_ID = $_POST['wpp_id'];
133 $exec_time = 0;
134
135 $start = Helper::microtime_float();
136 $result = $this->update_views_count($post_ID);
137 $end = Helper::microtime_float();
138 $exec_time += round($end - $start, 6);
139
140 if ( $result ) {
141 die("WPP: OK. Execution time: " . $exec_time . " seconds");
142 }
143
144 die("WPP: Oops, could not update the views count!");
145 }
146
147 /**
148 * Updates views count.
149 *
150 * @since 1.4.0
151 * @access private
152 * @global object $wpdb
153 * @param int $post_ID
154 * @return bool|int FALSE if query failed, TRUE on success
155 */
156 private function update_views_count($post_ID) {
157 /*
158 TODO:
159 For WordPress Multisite, we must define the DIEONDBERROR constant for database errors to display like so:
160 <?php define( 'DIEONDBERROR', true ); ?>
161 */
162 global $wpdb;
163 $table = $wpdb->prefix . "popularposts";
164 $wpdb->show_errors();
165
166 // Get translated object ID
167 $post_ID = $this->translate->get_object_id(
168 $post_ID,
169 get_post_type($post_ID),
170 true,
171 $this->translate->get_default_language()
172 );
173 $now = Helper::now();
174 $curdate = Helper::curdate();
175 $views = ( $this->config['tools']['sampling']['active'] )
176 ? $this->config['tools']['sampling']['rate']
177 : 1;
178
179 // Allow WP themers / coders perform an action
180 // before updating views count
181 if ( has_action('wpp_pre_update_views') )
182 do_action('wpp_pre_update_views', $post_ID, $views);
183
184 // Update all-time table
185 $result1 = $wpdb->query($wpdb->prepare(
186 "INSERT INTO {$table}data
187 (postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
188 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, last_viewed = %s;",
189 $post_ID,
190 $now,
191 $now,
192 $views,
193 $views,
194 $now
195 ));
196
197 // Update range (summary) table
198 $result2 = $wpdb->query($wpdb->prepare(
199 "INSERT INTO {$table}summary
200 (postid, pageviews, view_date, view_datetime) VALUES (%d, %d, %s, %s)
201 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, view_datetime = %s;",
202 $post_ID,
203 $views,
204 $curdate,
205 $now,
206 $views,
207 $now
208 ));
209
210 if ( !$result1 || !$result2 )
211 return false;
212
213 // Allow WP themers / coders perform an action
214 // after updating views count
215 if ( has_action('wpp_post_update_views' ))
216 do_action('wpp_post_update_views', $post_ID);
217
218 return true;
219 }
220
221 /**
222 * WPP shortcode handler.
223 *
224 * @since 1.4.0
225 * @param array $atts User defined attributes in shortcode tag
226 * @return string
227 */
228 public function wpp_shortcode($atts = null) {
229 /**
230 * @var string $header
231 * @var int $limit
232 * @var int $offset
233 * @var string $range
234 * @var bool $freshness
235 * @var string $order_by
236 * @var string $post_type
237 * @var string $pid
238 * @var string $cat
239 * @var string $author
240 * @var int $title_length
241 * @var int $title_by_words
242 * @var int $excerpt_length
243 * @var int $excerpt_format
244 * @var int $excerpt_by_words
245 * @var int $thumbnail_width
246 * @var int $thumbnail_height
247 * @var bool $rating
248 * @var bool $stats_comments
249 * @var bool $stats_views
250 * @var bool $stats_author
251 * @var bool $stats_date
252 * @var string $stats_date_format
253 * @var bool $stats_category
254 * @var string $wpp_start
255 * @var string $wpp_end
256 * @var string $header_start
257 * @var string $header_end
258 * @var string $post_html
259 * @var bool $php
260 */
261 extract(shortcode_atts([
262 'header' => '',
263 'limit' => 10,
264 'offset' => 0,
265 'range' => 'daily',
266 'time_unit' => 'hour',
267 'time_quantity' => 24,
268 'freshness' => false,
269 'order_by' => 'views',
270 'post_type' => 'post',
271 'pid' => '',
272 'cat' => '',
273 'taxonomy' => 'category',
274 'term_id' => '',
275 'author' => '',
276 'title_length' => 0,
277 'title_by_words' => 0,
278 'excerpt_length' => 0,
279 'excerpt_format' => 0,
280 'excerpt_by_words' => 0,
281 'thumbnail_width' => 0,
282 'thumbnail_height' => 0,
283 'rating' => false,
284 'stats_comments' => false,
285 'stats_views' => true,
286 'stats_author' => false,
287 'stats_date' => false,
288 'stats_date_format' => 'F j, Y',
289 'stats_category' => false,
290 'stats_taxonomy' => false,
291 'wpp_start' => '<ul class="wpp-list">',
292 'wpp_end' => '</ul>',
293 'header_start' => '<h2>',
294 'header_end' => '</h2>',
295 'post_html' => '',
296 'php' => false
297 ], $atts, 'wpp'));
298
299 // possible values for "Time Range" and "Order by"
300 $time_units = ["minute", "hour", "day", "week", "month"];
301 $range_values = ["daily", "last24hours", "weekly", "last7days", "monthly", "last30days", "all", "custom"];
302 $order_by_values = ["comments", "views", "avg"];
303
304 $shortcode_ops = [
305 'title' => strip_tags($header),
306 'limit' => ( ! empty($limit ) && Helper::is_number($limit) && $limit > 0 ) ? $limit : 10,
307 'offset' => ( ! empty($offset) && Helper::is_number($offset) && $offset >= 0 ) ? $offset : 0,
308 'range' => ( in_array($range, $range_values) ) ? $range : 'daily',
309 'time_quantity' => ( ! empty($time_quantity ) && Helper::is_number($time_quantity) && $time_quantity > 0 ) ? $time_quantity : 24,
310 'time_unit' => ( in_array($time_unit, $time_units) ) ? $time_unit : 'hour',
311 'freshness' => empty($freshness) ? false : $freshness,
312 'order_by' => ( in_array($order_by, $order_by_values) ) ? $order_by : 'views',
313 'post_type' => empty($post_type) ? 'post,page' : $post_type,
314 'pid' => rtrim(preg_replace('|[^0-9,]|', '', $pid), ","),
315 'cat' => rtrim(preg_replace('|[^0-9,-]|', '', $cat), ","),
316 'taxonomy' => empty($taxonomy) ? 'category' : $taxonomy,
317 'term_id' => rtrim(preg_replace('|[^0-9,;-]|', '', $term_id), ","),
318 'author' => rtrim(preg_replace('|[^0-9,]|', '', $author), ","),
319 'shorten_title' => [
320 'active' => ( ! empty($title_length) && Helper::is_number($title_length) && $title_length > 0 ),
321 'length' => ( ! empty($title_length) && Helper::is_number($title_length) ) ? $title_length : 0,
322 'words' => ( ! empty($title_by_words) && Helper::is_number($title_by_words) && $title_by_words > 0 ),
323 ],
324 'post-excerpt' => [
325 'active' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) && $excerpt_length > 0 ),
326 'length' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) ) ? $excerpt_length : 0,
327 'keep_format' => ( ! empty($excerpt_format) && Helper::is_number($excerpt_format) && $excerpt_format > 0 ),
328 'words' => ( ! empty($excerpt_by_words) && Helper::is_number($excerpt_by_words) && $excerpt_by_words > 0 ),
329 ],
330 'thumbnail' => [
331 'active' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ),
332 'width' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ) ? $thumbnail_width : 0,
333 'height' => ( ! empty($thumbnail_height) && Helper::is_number($thumbnail_height) && $thumbnail_height > 0 ) ? $thumbnail_height : 0,
334 ],
335 'rating' => empty($rating) ? false : $rating,
336 'stats_tag' => [
337 'comment_count' => empty($stats_comments) ? false : $stats_comments,
338 'views' => empty($stats_views) ? false : $stats_views,
339 'author' => empty($stats_author) ? false : $stats_author,
340 'date' => [
341 'active' => empty($stats_date) ? false : $stats_date,
342 'format' => empty($stats_date_format) ? 'F j, Y' : $stats_date_format
343 ],
344 'category' => empty($stats_category) ? false : $stats_category,
345 'taxonomy' => [
346 'active' => empty($stats_taxonomy) ? false : $stats_taxonomy,
347 'name' => empty($taxonomy) ? 'category' : $taxonomy,
348 ]
349 ],
350 'markup' => [
351 'custom_html' => true,
352 'wpp-start' => empty($wpp_start) ? '' : $wpp_start,
353 'wpp-end' => empty($wpp_end) ? '' : $wpp_end,
354 'title-start' => empty($header_start) ? '' : $header_start,
355 'title-end' => empty($header_end) ? '' : $header_end,
356 'post-html' => empty($post_html) ? '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>' : $post_html
357 ]
358 ];
359
360 // Post / Page / CTP filter
361 $ids = array_filter(explode(",", $shortcode_ops['pid']), 'is_numeric');
362 // Got no valid IDs, clear
363 if ( empty($ids) ) {
364 $shortcode_ops['pid'] = '';
365 }
366
367 // Category filter
368 $ids = array_filter(explode(",", $shortcode_ops['cat']), 'is_numeric');
369 // Got no valid IDs, clear
370 if ( empty($ids) ) {
371 $shortcode_ops['cat'] = '';
372 }
373
374 // Author filter
375 $ids = array_filter(explode( ",", $shortcode_ops['author']), 'is_numeric');
376 // Got no valid IDs, clear
377 if ( empty($ids) ) {
378 $shortcode_ops['author'] = '';
379 }
380
381 $shortcode_content = '';
382
383 // is there a title defined by user?
384 if (
385 ! empty($header)
386 && ! empty($header_start)
387 && ! empty($header_end)
388 ) {
389 $shortcode_content .= htmlspecialchars_decode($header_start, ENT_QUOTES) . $header . htmlspecialchars_decode($header_end, ENT_QUOTES);
390 }
391
392 $cached = false;
393
394 // Return cached results
395 if ( $this->config['tools']['cache']['active'] ) {
396
397 $key = md5(json_encode($shortcode_ops));
398 $popular_posts = \WordPressPopularPosts\Cache::get($key);
399
400 if ( false === $popular_posts ) {
401 $popular_posts = new Query($shortcode_ops);
402
403 $time_value = $this->config['tools']['cache']['interval']['value']; // eg. 5
404 $time_unit = $this->config['tools']['cache']['interval']['time']; // eg. 'minute'
405
406 // No popular posts found, check again in 1 minute
407 if ( ! $popular_posts->get_posts() ) {
408 $time_value = 1;
409 $time_unit = 'minute';
410 }
411
412 \WordPressPopularPosts\Cache::set(
413 $key,
414 $popular_posts,
415 $time_value,
416 $time_unit
417 );
418 }
419
420 $cached = true;
421
422 } // Get popular posts
423 else {
424 $popular_posts = new Query($shortcode_ops);
425 }
426
427 $this->output->set_data($popular_posts->get_posts());
428 $this->output->set_public_options($shortcode_ops);
429 $this->output->build_output();
430
431 $shortcode_content .= $this->output->get_output();
432
433 return $shortcode_content;
434 }
435
436 /**
437 * Themes widgets.
438 *
439 * @since 5.0.0
440 */
441 public function theme_widgets()
442 {
443 ?>
444 <script type="text/javascript">
445 (function(){
446 document.addEventListener('DOMContentLoaded', function(){
447 let wpp_widgets = document.querySelectorAll('.popular-posts-sr');
448
449 if ( wpp_widgets ) {
450 for (let i = 0; i < wpp_widgets.length; i++) {
451 let wpp_widget = wpp_widgets[i];
452 WordPressPopularPosts.theme(wpp_widget);
453 }
454 }
455 });
456 })();
457 </script>
458 <?php
459 }
460
461 /**
462 * Lazy loads WPP's images.
463 *
464 * @since 5.0.0
465 */
466 public function lazyload_images()
467 {
468 ?>
469 <script>
470 var WPPImageObserver = null;
471
472 function wpp_load_img(img) {
473 if ( ! 'imgSrc' in img.dataset || ! img.dataset.imgSrc )
474 return;
475
476 img.src = img.dataset.imgSrc;
477
478 if ( 'imgSrcset' in img.dataset ) {
479 img.srcset = img.dataset.imgSrcset;
480 img.removeAttribute('data-img-srcset');
481 }
482
483 img.classList.remove('wpp-lazyload');
484 img.removeAttribute('data-img-src');
485 img.classList.add('wpp-lazyloaded');
486 }
487
488 function wpp_observe_imgs(){
489 let wpp_images = document.querySelectorAll('img.wpp-lazyload'),
490 wpp_widgets = document.querySelectorAll('.popular-posts-sr');
491
492 if ( wpp_images.length || wpp_widgets.length ) {
493 if ( 'IntersectionObserver' in window ) {
494 WPPImageObserver = new IntersectionObserver(function(entries, observer) {
495 entries.forEach(function(entry) {
496 if (entry.isIntersecting) {
497 let img = entry.target;
498 wpp_load_img(img);
499 WPPImageObserver.unobserve(img);
500 }
501 });
502 });
503
504 if ( wpp_images.length ) {
505 wpp_images.forEach(function(image) {
506 WPPImageObserver.observe(image);
507 });
508 }
509
510 if ( wpp_widgets.length ) {
511 for (var i = 0; i < wpp_widgets.length; i++) {
512 let wpp_widget_images = wpp_widgets[i].querySelectorAll('img.wpp-lazyload');
513
514 if ( ! wpp_widget_images.length && wpp_widgets[i].shadowRoot ) {
515 wpp_widget_images = wpp_widgets[i].shadowRoot.querySelectorAll('img.wpp-lazyload');
516 }
517
518 if ( wpp_widget_images.length ) {
519 wpp_widget_images.forEach(function(image) {
520 WPPImageObserver.observe(image);
521 });
522 }
523 }
524 }
525 } /** Fallback for older browsers */
526 else {
527 if ( wpp_images.length ) {
528 for (var i = 0; i < wpp_images.length; i++) {
529 wpp_load_img(wpp_images[i]);
530 wpp_images[i].classList.remove('wpp-lazyloaded');
531 }
532 }
533
534 if ( wpp_widgets.length ) {
535 for (var j = 0; j < wpp_widgets.length; j++) {
536 let wpp_widget = wpp_widgets[j],
537 wpp_widget_images = wpp_widget.querySelectorAll('img.wpp-lazyload');
538
539 if ( ! wpp_widget_images.length && wpp_widget.shadowRoot ) {
540 wpp_widget_images = wpp_widget.shadowRoot.querySelectorAll('img.wpp-lazyload');
541 }
542
543 if ( wpp_widget_images.length ) {
544 for (var k = 0; k < wpp_widget_images.length; k++) {
545 wpp_load_img(wpp_widget_images[k]);
546 wpp_widget_images[k].classList.remove('wpp-lazyloaded');
547 }
548 }
549 }
550 }
551 }
552 }
553 }
554
555 document.addEventListener('DOMContentLoaded', function() {
556 wpp_observe_imgs();
557
558 // When an ajaxified WPP widget loads,
559 // Lazy load its images
560 document.addEventListener('wpp-onload', function(){
561 wpp_observe_imgs();
562 });
563 });
564 </script>
565 <?php
566 }
567 }
568