PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 6.2.1
WP Popular Posts v6.2.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 2 years ago
Front.php
482 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, Output, Translate };
15 use WordPressPopularPosts\Traits\QueriesPosts;
16
17 class Front {
18
19 use QueriesPosts;
20
21 /**
22 * Plugin options.
23 *
24 * @var array $config
25 * @access private
26 */
27 private $config;
28
29 /**
30 * Translate object.
31 *
32 * @var \WordPressPopularPosts\Translate $translate
33 * @access private
34 */
35 private $translate;
36
37 /**
38 * Output object.
39 *
40 * @var \WordPressPopularPosts\Output $output
41 * @access private
42 */
43 private $output;
44
45 /**
46 * Construct.
47 *
48 * @since 5.0.0
49 * @param array $config Admin settings.
50 * @param \WordPressPopularPosts\Translate $translate Translate class.
51 * @param \WordPressPopularPosts\Output $output Output class.
52 */
53 public function __construct(array $config, Translate $translate, Output $output)
54 {
55 $this->config = $config;
56 $this->translate = $translate;
57 $this->output = $output;
58 }
59
60 /**
61 * WordPress public-facing hooks.
62 *
63 * @since 5.0.0
64 */
65 public function hooks()
66 {
67 add_shortcode('wpp', [$this, 'wpp_shortcode']);
68 add_action('wp_head', [$this, 'inline_loading_css']);
69 add_action('wp_ajax_update_views_ajax', [$this, 'update_views']);
70 add_action('wp_ajax_nopriv_update_views_ajax', [$this, 'update_views']);
71 add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']);
72 add_filter('script_loader_tag', [$this, 'convert_inline_js_into_json'], 10, 3);
73 }
74
75 /**
76 * Inserts CSS related to the loading animation into <head>
77 *
78 * @since 5.3.0
79 */
80 public function inline_loading_css()
81 {
82 $wpp_insert_loading_animation_styles = apply_filters('wpp_insert_loading_animation_styles', true);
83
84 if ( $wpp_insert_loading_animation_styles ) :
85 ?>
86 <style id="wpp-loading-animation-styles">@-webkit-keyframes bgslide{from{background-position-x:0}to{background-position-x:-200%}}@keyframes bgslide{from{background-position-x:0}to{background-position-x:-200%}}.wpp-widget-placeholder,.wpp-widget-block-placeholder,.wpp-shortcode-placeholder{margin:0 auto;width:60px;height:3px;background:#dd3737;background:linear-gradient(90deg,#dd3737 0%,#571313 10%,#dd3737 100%);background-size:200% auto;border-radius:3px;-webkit-animation:bgslide 1s infinite linear;animation:bgslide 1s infinite linear}</style>
87 <?php
88 endif;
89 }
90
91 /**
92 * Enqueues public facing assets.
93 *
94 * @since 5.0.0
95 */
96 public function enqueue_assets()
97 {
98 // Enqueue WPP's stylesheet.
99 if ( $this->config['tools']['css'] ) {
100 $theme_file = get_stylesheet_directory() . '/wpp.css';
101
102 if ( @is_file($theme_file) ) {
103 wp_enqueue_style('wordpress-popular-posts-css', get_stylesheet_directory_uri() . '/wpp.css', [], WPP_VERSION, 'all');
104 } // Load stock stylesheet
105 else {
106 wp_enqueue_style('wordpress-popular-posts-css', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/css/wpp.css', [], WPP_VERSION, 'all');
107 }
108 }
109
110 // Enqueue WPP's library.
111 $is_single = 0;
112
113 if (
114 ( 0 == $this->config['tools']['log']['level'] && ! is_user_logged_in() )
115 || ( 1 == $this->config['tools']['log']['level'] )
116 || ( 2 == $this->config['tools']['log']['level'] && is_user_logged_in() )
117 ) {
118 $is_single = Helper::is_single();
119 }
120
121 $wpp_js = ( defined('WP_DEBUG') && WP_DEBUG )
122 ? plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/wpp.js'
123 : plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/wpp.min.js';
124
125 wp_register_script('wpp-js', $wpp_js, [], WPP_VERSION, false);
126 $params = [
127 'sampling_active' => (int) $this->config['tools']['sampling']['active'],
128 'sampling_rate' => (int) $this->config['tools']['sampling']['rate'],
129 'ajax_url' => esc_url_raw(rest_url('wordpress-popular-posts/v1/popular-posts')),
130 'api_url' => esc_url_raw(rest_url('wordpress-popular-posts')),
131 'ID' => (int) $is_single,
132 'token' => wp_create_nonce('wp_rest'),
133 'lang' => function_exists('PLL') ? $this->translate->get_current_language() : 0,
134 'debug' => (int) WP_DEBUG
135 ];
136 wp_enqueue_script('wpp-js');
137 wp_add_inline_script('wpp-js', json_encode($params), 'before');
138 }
139
140 /**
141 * Converts inline script tag into type=application/json.
142 *
143 * This function mods the original script tag as printed
144 * by WordPress which contains the data for the wpp_params
145 * object into a JSON script. This improves compatibility
146 * with Content Security Policy (CSP).
147 *
148 * @since 5.2.0
149 * @param string $tag
150 * @param string $handle
151 * @param string $src
152 * @return string $tag
153 */
154 public function convert_inline_js_into_json(string $tag, string $handle, string $src)
155 {
156 if ( 'wpp-js' === $handle ) {
157 // id attribute found, replace it
158 if ( false !== strpos($tag, 'wpp-js-js-before') ) {
159 $tag = str_replace('wpp-js-js-before', 'wpp-json', $tag);
160 } // id attribute missing, let's add it
161 else {
162 $pos = strpos($tag, '>');
163 $tag = substr_replace($tag, ' id="wpp-json">', $pos, 1);
164 }
165
166 // type attribute found, replace it
167 if ( false !== strpos($tag, 'type') ) {
168 $pos = strpos($tag, 'text/javascript');
169
170 if ( false !== $pos ) {
171 $tag = substr_replace($tag, 'application/json', $pos, strlen('text/javascript'));
172 }
173 } // type attribute missing, let's add it
174 else {
175 $pos = strpos($tag, '>');
176 $tag = substr_replace($tag, ' type="application/json">', $pos, 1);
177 }
178 }
179
180 return $tag;
181 }
182
183 /**
184 * Updates views count on page load via AJAX.
185 *
186 * @since 2.0.0
187 */
188 public function update_views()
189 {
190 if ( ! wp_verify_nonce($_POST['token'], 'wpp-token') || ! Helper::is_number($_POST['wpp_id']) ) {
191 die( 'WPP: Oops, invalid request!' );
192 }
193
194 $nonce = $_POST['token'];
195 $post_ID = $_POST['wpp_id'];
196 $exec_time = 0;
197
198 $start = Helper::microtime_float();
199 $result = $this->update_views_count($post_ID);
200 $end = Helper::microtime_float();
201 $exec_time += round($end - $start, 6);
202
203 if ( $result ) {
204 die('WPP: OK. Execution time: ' . $exec_time . ' seconds'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
205 }
206
207 die('WPP: Oops, could not update the views count!');
208 }
209
210 /**
211 * Updates views count.
212 *
213 * @since 1.4.0
214 * @access private
215 * @global object $wpdb
216 * @param int $post_ID
217 * @return bool|int FALSE if query failed, TRUE on success
218 */
219 private function update_views_count(int $post_ID) {
220 global $wpdb;
221 $table = $wpdb->prefix . 'popularposts';
222 $wpdb->show_errors();
223
224 // Get translated object ID
225 $post_ID = $this->translate->get_object_id(
226 $post_ID,
227 get_post_type($post_ID),
228 true,
229 $this->translate->get_default_language()
230 );
231 $now = Helper::now();
232 $curdate = Helper::curdate();
233 $views = ( $this->config['tools']['sampling']['active'] )
234 ? $this->config['tools']['sampling']['rate']
235 : 1;
236
237 // Allow WP themers / coders perform an action
238 // before updating views count
239 if ( has_action('wpp_pre_update_views') ) {
240 do_action('wpp_pre_update_views', $post_ID, $views);
241 }
242
243 // Update all-time table
244 //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table is safe to use
245 $result1 = $wpdb->query($wpdb->prepare(
246 "INSERT INTO {$table}data
247 (postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
248 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, last_viewed = %s;",
249 $post_ID,
250 $now,
251 $now,
252 $views,
253 $views,
254 $now
255 ));
256 //phpcs:enable
257
258 // Update range (summary) table
259 //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table is safe to use
260 $result2 = $wpdb->query($wpdb->prepare(
261 "INSERT INTO {$table}summary
262 (postid, pageviews, view_date, view_datetime) VALUES (%d, %d, %s, %s)
263 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, view_datetime = %s;",
264 $post_ID,
265 $views,
266 $curdate,
267 $now,
268 $views,
269 $now
270 ));
271 //phpcs:enable
272
273 if ( ! $result1 || ! $result2 ) {
274 return false;
275 }
276
277 // Allow WP themers / coders perform an action
278 // after updating views count
279 if ( has_action('wpp_post_update_views' )) {
280 do_action('wpp_post_update_views', $post_ID);
281 }
282
283 return true;
284 }
285
286 /**
287 * WPP shortcode handler.
288 *
289 * @since 1.4.0
290 * @param array $atts User defined attributes in shortcode tag
291 * @return string
292 */
293 public function wpp_shortcode($atts = null) {
294 /** @TODO: starting PHP 8.0 $atts can be declared as mixed $meta_value (if not set WP gives an string, and it set we get an array) */
295 /**
296 * @var string $header
297 * @var int $limit
298 * @var int $offset
299 * @var string $range
300 * @var bool $freshness
301 * @var string $order_by
302 * @var string $post_type
303 * @var string $pid
304 * @var string $cat
305 * @var string $author
306 * @var int $title_length
307 * @var int $title_by_words
308 * @var int $excerpt_length
309 * @var int $excerpt_format
310 * @var int $excerpt_by_words
311 * @var int $thumbnail_width
312 * @var int $thumbnail_height
313 * @var bool $rating
314 * @var bool $stats_comments
315 * @var bool $stats_views
316 * @var bool $stats_author
317 * @var bool $stats_date
318 * @var string $stats_date_format
319 * @var bool $stats_category
320 * @var string $wpp_start
321 * @var string $wpp_end
322 * @var string $header_start
323 * @var string $header_end
324 * @var string $post_html
325 */
326 extract(shortcode_atts([
327 'header' => '',
328 'limit' => 10,
329 'offset' => 0,
330 'range' => 'daily',
331 'time_unit' => 'hour',
332 'time_quantity' => 24,
333 'freshness' => false,
334 'order_by' => 'views',
335 'post_type' => 'post',
336 'pid' => '',
337 'cat' => '',
338 'taxonomy' => 'category',
339 'term_id' => '',
340 'author' => '',
341 'title_length' => 0,
342 'title_by_words' => 0,
343 'excerpt_length' => 0,
344 'excerpt_format' => 0,
345 'excerpt_by_words' => 0,
346 'thumbnail_width' => 0,
347 'thumbnail_height' => 0,
348 'rating' => false,
349 'stats_comments' => false,
350 'stats_views' => true,
351 'stats_author' => false,
352 'stats_date' => false,
353 'stats_date_format' => 'F j, Y',
354 'stats_category' => false,
355 'stats_taxonomy' => false,
356 'wpp_start' => '<ul class="wpp-list">',
357 'wpp_end' => '</ul>',
358 'header_start' => '<h2>',
359 'header_end' => '</h2>',
360 'post_html' => '',
361 'theme' => ''
362 ], $atts, 'wpp'));
363
364 // possible values for "Time Range" and "Order by"
365 $time_units = ['minute', 'hour', 'day', 'week', 'month'];
366 $range_values = ['daily', 'last24hours', 'weekly', 'last7days', 'monthly', 'last30days', 'all', 'custom'];
367 $order_by_values = ['comments', 'views', 'avg'];
368
369 $shortcode_ops = [
370 'title' => strip_tags($header),
371 'limit' => ( ! empty($limit ) && Helper::is_number($limit) && $limit > 0 ) ? $limit : 10,
372 'offset' => ( ! empty($offset) && Helper::is_number($offset) && $offset >= 0 ) ? $offset : 0,
373 'range' => ( in_array($range, $range_values) ) ? $range : 'daily',
374 'time_quantity' => ( ! empty($time_quantity ) && Helper::is_number($time_quantity) && $time_quantity > 0 ) ? $time_quantity : 24,
375 'time_unit' => ( in_array($time_unit, $time_units) ) ? $time_unit : 'hour',
376 'freshness' => empty($freshness) ? false : $freshness,
377 'order_by' => ( in_array($order_by, $order_by_values) ) ? $order_by : 'views',
378 'post_type' => empty($post_type) ? 'post' : $post_type,
379 'pid' => rtrim(preg_replace('|[^0-9,]|', '', $pid), ','),
380 'cat' => rtrim(preg_replace('|[^0-9,-]|', '', $cat), ','),
381 'taxonomy' => empty($taxonomy) ? 'category' : $taxonomy,
382 'term_id' => rtrim(preg_replace('|[^0-9,;-]|', '', $term_id), ','),
383 'author' => rtrim(preg_replace('|[^0-9,]|', '', $author), ','),
384 'shorten_title' => [
385 'active' => ( ! empty($title_length) && Helper::is_number($title_length) && $title_length > 0 ),
386 'length' => ( ! empty($title_length) && Helper::is_number($title_length) ) ? $title_length : 0,
387 'words' => ( ! empty($title_by_words) && Helper::is_number($title_by_words) && $title_by_words > 0 ),
388 ],
389 'post-excerpt' => [
390 'active' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) && $excerpt_length > 0 ),
391 'length' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) ) ? $excerpt_length : 0,
392 'keep_format' => ( ! empty($excerpt_format) && Helper::is_number($excerpt_format) && $excerpt_format > 0 ),
393 'words' => ( ! empty($excerpt_by_words) && Helper::is_number($excerpt_by_words) && $excerpt_by_words > 0 ),
394 ],
395 'thumbnail' => [
396 'active' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ),
397 'width' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ) ? $thumbnail_width : 0,
398 'height' => ( ! empty($thumbnail_height) && Helper::is_number($thumbnail_height) && $thumbnail_height > 0 ) ? $thumbnail_height : 0,
399 ],
400 'rating' => empty($rating) ? false : $rating,
401 'stats_tag' => [
402 'comment_count' => empty($stats_comments) ? false : $stats_comments,
403 'views' => empty($stats_views) ? false : $stats_views,
404 'author' => empty($stats_author) ? false : $stats_author,
405 'date' => [
406 'active' => empty($stats_date) ? false : $stats_date,
407 'format' => empty($stats_date_format) ? 'F j, Y' : $stats_date_format
408 ],
409 'category' => empty($stats_category) ? false : $stats_category,
410 'taxonomy' => [
411 'active' => empty($stats_taxonomy) ? false : $stats_taxonomy,
412 'name' => empty($taxonomy) ? 'category' : $taxonomy,
413 ]
414 ],
415 'markup' => [
416 'custom_html' => true,
417 'wpp-start' => empty($wpp_start) ? '' : $wpp_start,
418 'wpp-end' => empty($wpp_end) ? '' : $wpp_end,
419 'title-start' => empty($header_start) ? '' : $header_start,
420 'title-end' => empty($header_end) ? '' : $header_end,
421 'post-html' => empty($post_html) ? '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>' : $post_html
422 ],
423 'theme' => [
424 'name' => trim($theme)
425 ]
426 ];
427
428 // Post / Page / CTP filter
429 $ids = array_filter(explode(',', $shortcode_ops['pid']), 'is_numeric');
430 // Got no valid IDs, clear
431 if ( empty($ids) ) {
432 $shortcode_ops['pid'] = '';
433 }
434
435 // Category filter
436 $ids = array_filter(explode(',', $shortcode_ops['cat']), 'is_numeric');
437 // Got no valid IDs, clear
438 if ( empty($ids) ) {
439 $shortcode_ops['cat'] = '';
440 }
441
442 // Author filter
443 $ids = array_filter(explode( ',', $shortcode_ops['author']), 'is_numeric');
444 // Got no valid IDs, clear
445 if ( empty($ids) ) {
446 $shortcode_ops['author'] = '';
447 }
448
449 $shortcode_content = '';
450 $cached = false;
451
452 // is there a title defined by user?
453 if (
454 ! empty($header)
455 && ! empty($header_start)
456 && ! empty($header_end)
457 ) {
458 $shortcode_content .= htmlspecialchars_decode($header_start, ENT_QUOTES) . $header . htmlspecialchars_decode($header_end, ENT_QUOTES);
459 }
460
461 $isAdmin = isset($_GET['isSelected']) ? $_GET['isSelected'] : false;
462
463 if ( $this->config['tools']['ajax'] && ! is_customize_preview() && ! $isAdmin ) {
464 $shortcode_content .= '<div class="wpp-shortcode">';
465 $shortcode_content .= '<script type="application/json">' . wp_json_encode($shortcode_ops) . '</script>';
466 $shortcode_content .= '<div class="wpp-shortcode-placeholder"></div>';
467 $shortcode_content .= '</div>';
468 } else {
469 $popular_posts = $this->maybe_query($shortcode_ops);
470
471 $this->output->set_data($popular_posts->get_posts());
472 $this->output->set_public_options($shortcode_ops);
473 $this->output->build_output();
474
475 $shortcode_content .= $this->output->get_output();
476 }
477
478 return $shortcode_content;
479 }
480
481 }
482