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