PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 5.3.1
WP Popular Posts v5.3.1
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 5 years ago
Front.php
517 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 'ID' => (int) $is_single,
150 'token' => wp_create_nonce('wp_rest'),
151 'lang' => function_exists('PLL') ? $this->translate->get_current_language() : 0,
152 'debug' => (int) WP_DEBUG
153 ];
154 wp_enqueue_script('wpp-js');
155 wp_add_inline_script('wpp-js', json_encode($params), 'before');
156 }
157
158 /**
159 * Converts inline script tag into type=application/json.
160 *
161 * This function mods the original script tag as printed
162 * by WordPress which contains the data for the wpp_params
163 * object into a JSON script. This improves compatibility
164 * with Content Security Policy (CSP).
165 *
166 * @since 5.2.0
167 * @param string $tag
168 * @param string $handle
169 * @param string $src
170 * @return string $tag
171 */
172 function convert_inline_js_into_json($tag, $handle, $src)
173 {
174 if ( 'wpp-js' === $handle ) {
175 // id attribute found, replace it
176 if ( false !== strpos($tag, 'wpp-js-js-before') ) {
177 $tag = str_replace('wpp-js-js-before', 'wpp-json', $tag);
178 } // id attribute missing, let's add it
179 else {
180 $pos = strpos($tag, '>');
181 $tag = substr_replace($tag, ' id="wpp-json">', $pos, 1);
182 }
183
184 // type attribute found, replace it
185 if ( false !== strpos($tag, 'type') ) {
186 $pos = strpos($tag, 'text/javascript');
187
188 if ( false !== $pos )
189 $tag = substr_replace($tag, 'application/json', $pos, strlen('text/javascript'));
190 } // type attribute missing, let's add it
191 else {
192 $pos = strpos($tag, '>');
193 $tag = substr_replace($tag, ' type="application/json">', $pos, 1);
194 }
195 }
196
197 return $tag;
198 }
199
200 /**
201 * Updates views count on page load via AJAX.
202 *
203 * @since 2.0.0
204 */
205 public function update_views()
206 {
207 if ( ! wp_verify_nonce($_POST['token'], 'wpp-token') || ! Helper::is_number($_POST['wpp_id']) ) {
208 die( "WPP: Oops, invalid request!" );
209 }
210
211 $nonce = $_POST['token'];
212 $post_ID = $_POST['wpp_id'];
213 $exec_time = 0;
214
215 $start = Helper::microtime_float();
216 $result = $this->update_views_count($post_ID);
217 $end = Helper::microtime_float();
218 $exec_time += round($end - $start, 6);
219
220 if ( $result ) {
221 die("WPP: OK. Execution time: " . $exec_time . " seconds");
222 }
223
224 die("WPP: Oops, could not update the views count!");
225 }
226
227 /**
228 * Updates views count.
229 *
230 * @since 1.4.0
231 * @access private
232 * @global object $wpdb
233 * @param int $post_ID
234 * @return bool|int FALSE if query failed, TRUE on success
235 */
236 private function update_views_count($post_ID) {
237 /*
238 TODO:
239 For WordPress Multisite, we must define the DIEONDBERROR constant for database errors to display like so:
240 <?php define( 'DIEONDBERROR', true ); ?>
241 */
242 global $wpdb;
243 $table = $wpdb->prefix . "popularposts";
244 $wpdb->show_errors();
245
246 // Get translated object ID
247 $post_ID = $this->translate->get_object_id(
248 $post_ID,
249 get_post_type($post_ID),
250 true,
251 $this->translate->get_default_language()
252 );
253 $now = Helper::now();
254 $curdate = Helper::curdate();
255 $views = ( $this->config['tools']['sampling']['active'] )
256 ? $this->config['tools']['sampling']['rate']
257 : 1;
258
259 // Allow WP themers / coders perform an action
260 // before updating views count
261 if ( has_action('wpp_pre_update_views') )
262 do_action('wpp_pre_update_views', $post_ID, $views);
263
264 // Update all-time table
265 $result1 = $wpdb->query($wpdb->prepare(
266 "INSERT INTO {$table}data
267 (postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
268 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, last_viewed = %s;",
269 $post_ID,
270 $now,
271 $now,
272 $views,
273 $views,
274 $now
275 ));
276
277 // Update range (summary) table
278 $result2 = $wpdb->query($wpdb->prepare(
279 "INSERT INTO {$table}summary
280 (postid, pageviews, view_date, view_datetime) VALUES (%d, %d, %s, %s)
281 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, view_datetime = %s;",
282 $post_ID,
283 $views,
284 $curdate,
285 $now,
286 $views,
287 $now
288 ));
289
290 if ( !$result1 || !$result2 )
291 return false;
292
293 // Allow WP themers / coders perform an action
294 // after updating views count
295 if ( has_action('wpp_post_update_views' ))
296 do_action('wpp_post_update_views', $post_ID);
297
298 return true;
299 }
300
301 /**
302 * WPP shortcode handler.
303 *
304 * @since 1.4.0
305 * @param array $atts User defined attributes in shortcode tag
306 * @return string
307 */
308 public function wpp_shortcode($atts = null) {
309 /**
310 * @var string $header
311 * @var int $limit
312 * @var int $offset
313 * @var string $range
314 * @var bool $freshness
315 * @var string $order_by
316 * @var string $post_type
317 * @var string $pid
318 * @var string $cat
319 * @var string $author
320 * @var int $title_length
321 * @var int $title_by_words
322 * @var int $excerpt_length
323 * @var int $excerpt_format
324 * @var int $excerpt_by_words
325 * @var int $thumbnail_width
326 * @var int $thumbnail_height
327 * @var bool $rating
328 * @var bool $stats_comments
329 * @var bool $stats_views
330 * @var bool $stats_author
331 * @var bool $stats_date
332 * @var string $stats_date_format
333 * @var bool $stats_category
334 * @var string $wpp_start
335 * @var string $wpp_end
336 * @var string $header_start
337 * @var string $header_end
338 * @var string $post_html
339 * @var bool $php
340 */
341 extract(shortcode_atts([
342 'header' => '',
343 'limit' => 10,
344 'offset' => 0,
345 'range' => 'daily',
346 'time_unit' => 'hour',
347 'time_quantity' => 24,
348 'freshness' => false,
349 'order_by' => 'views',
350 'post_type' => 'post',
351 'pid' => '',
352 'cat' => '',
353 'taxonomy' => 'category',
354 'term_id' => '',
355 'author' => '',
356 'title_length' => 0,
357 'title_by_words' => 0,
358 'excerpt_length' => 0,
359 'excerpt_format' => 0,
360 'excerpt_by_words' => 0,
361 'thumbnail_width' => 0,
362 'thumbnail_height' => 0,
363 'rating' => false,
364 'stats_comments' => false,
365 'stats_views' => true,
366 'stats_author' => false,
367 'stats_date' => false,
368 'stats_date_format' => 'F j, Y',
369 'stats_category' => false,
370 'stats_taxonomy' => false,
371 'wpp_start' => '<ul class="wpp-list">',
372 'wpp_end' => '</ul>',
373 'header_start' => '<h2>',
374 'header_end' => '</h2>',
375 'post_html' => '',
376 'php' => false
377 ], $atts, 'wpp'));
378
379 // possible values for "Time Range" and "Order by"
380 $time_units = ["minute", "hour", "day", "week", "month"];
381 $range_values = ["daily", "last24hours", "weekly", "last7days", "monthly", "last30days", "all", "custom"];
382 $order_by_values = ["comments", "views", "avg"];
383
384 $shortcode_ops = [
385 'title' => strip_tags($header),
386 'limit' => ( ! empty($limit ) && Helper::is_number($limit) && $limit > 0 ) ? $limit : 10,
387 'offset' => ( ! empty($offset) && Helper::is_number($offset) && $offset >= 0 ) ? $offset : 0,
388 'range' => ( in_array($range, $range_values) ) ? $range : 'daily',
389 'time_quantity' => ( ! empty($time_quantity ) && Helper::is_number($time_quantity) && $time_quantity > 0 ) ? $time_quantity : 24,
390 'time_unit' => ( in_array($time_unit, $time_units) ) ? $time_unit : 'hour',
391 'freshness' => empty($freshness) ? false : $freshness,
392 'order_by' => ( in_array($order_by, $order_by_values) ) ? $order_by : 'views',
393 'post_type' => empty($post_type) ? 'post,page' : $post_type,
394 'pid' => rtrim(preg_replace('|[^0-9,]|', '', $pid), ","),
395 'cat' => rtrim(preg_replace('|[^0-9,-]|', '', $cat), ","),
396 'taxonomy' => empty($taxonomy) ? 'category' : $taxonomy,
397 'term_id' => rtrim(preg_replace('|[^0-9,;-]|', '', $term_id), ","),
398 'author' => rtrim(preg_replace('|[^0-9,]|', '', $author), ","),
399 'shorten_title' => [
400 'active' => ( ! empty($title_length) && Helper::is_number($title_length) && $title_length > 0 ),
401 'length' => ( ! empty($title_length) && Helper::is_number($title_length) ) ? $title_length : 0,
402 'words' => ( ! empty($title_by_words) && Helper::is_number($title_by_words) && $title_by_words > 0 ),
403 ],
404 'post-excerpt' => [
405 'active' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) && $excerpt_length > 0 ),
406 'length' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) ) ? $excerpt_length : 0,
407 'keep_format' => ( ! empty($excerpt_format) && Helper::is_number($excerpt_format) && $excerpt_format > 0 ),
408 'words' => ( ! empty($excerpt_by_words) && Helper::is_number($excerpt_by_words) && $excerpt_by_words > 0 ),
409 ],
410 'thumbnail' => [
411 'active' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ),
412 'width' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ) ? $thumbnail_width : 0,
413 'height' => ( ! empty($thumbnail_height) && Helper::is_number($thumbnail_height) && $thumbnail_height > 0 ) ? $thumbnail_height : 0,
414 ],
415 'rating' => empty($rating) ? false : $rating,
416 'stats_tag' => [
417 'comment_count' => empty($stats_comments) ? false : $stats_comments,
418 'views' => empty($stats_views) ? false : $stats_views,
419 'author' => empty($stats_author) ? false : $stats_author,
420 'date' => [
421 'active' => empty($stats_date) ? false : $stats_date,
422 'format' => empty($stats_date_format) ? 'F j, Y' : $stats_date_format
423 ],
424 'category' => empty($stats_category) ? false : $stats_category,
425 'taxonomy' => [
426 'active' => empty($stats_taxonomy) ? false : $stats_taxonomy,
427 'name' => empty($taxonomy) ? 'category' : $taxonomy,
428 ]
429 ],
430 'markup' => [
431 'custom_html' => true,
432 'wpp-start' => empty($wpp_start) ? '' : $wpp_start,
433 'wpp-end' => empty($wpp_end) ? '' : $wpp_end,
434 'title-start' => empty($header_start) ? '' : $header_start,
435 'title-end' => empty($header_end) ? '' : $header_end,
436 'post-html' => empty($post_html) ? '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>' : $post_html
437 ]
438 ];
439
440 // Post / Page / CTP filter
441 $ids = array_filter(explode(",", $shortcode_ops['pid']), 'is_numeric');
442 // Got no valid IDs, clear
443 if ( empty($ids) ) {
444 $shortcode_ops['pid'] = '';
445 }
446
447 // Category filter
448 $ids = array_filter(explode(",", $shortcode_ops['cat']), 'is_numeric');
449 // Got no valid IDs, clear
450 if ( empty($ids) ) {
451 $shortcode_ops['cat'] = '';
452 }
453
454 // Author filter
455 $ids = array_filter(explode( ",", $shortcode_ops['author']), 'is_numeric');
456 // Got no valid IDs, clear
457 if ( empty($ids) ) {
458 $shortcode_ops['author'] = '';
459 }
460
461 $shortcode_content = '';
462
463 // is there a title defined by user?
464 if (
465 ! empty($header)
466 && ! empty($header_start)
467 && ! empty($header_end)
468 ) {
469 $shortcode_content .= htmlspecialchars_decode($header_start, ENT_QUOTES) . $header . htmlspecialchars_decode($header_end, ENT_QUOTES);
470 }
471
472 $cached = false;
473
474 // Return cached results
475 if ( $this->config['tools']['cache']['active'] ) {
476
477 $key = md5(json_encode($shortcode_ops));
478 $popular_posts = \WordPressPopularPosts\Cache::get($key);
479
480 if ( false === $popular_posts ) {
481 $popular_posts = new Query($shortcode_ops);
482
483 $time_value = $this->config['tools']['cache']['interval']['value']; // eg. 5
484 $time_unit = $this->config['tools']['cache']['interval']['time']; // eg. 'minute'
485
486 // No popular posts found, check again in 1 minute
487 if ( ! $popular_posts->get_posts() ) {
488 $time_value = 1;
489 $time_unit = 'minute';
490 }
491
492 \WordPressPopularPosts\Cache::set(
493 $key,
494 $popular_posts,
495 $time_value,
496 $time_unit
497 );
498 }
499
500 $cached = true;
501
502 } // Get popular posts
503 else {
504 $popular_posts = new Query($shortcode_ops);
505 }
506
507 $this->output->set_data($popular_posts->get_posts());
508 $this->output->set_public_options($shortcode_ops);
509 $this->output->build_output();
510
511 $shortcode_content .= $this->output->get_output();
512
513 return $shortcode_content;
514 }
515
516 }
517