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