PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 5.4.2
WP Popular Posts v5.4.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 / Admin / Admin.php
wordpress-popular-posts / src / Admin Last commit date
Admin.php 4 years ago admin-page.php 4 years ago
Admin.php
1447 lines
1 <?php
2 /**
3 * The admin-facing functionality of the plugin.
4 *
5 * Defines hooks to enqueue the admin-specific stylesheet and JavaScript,
6 * plugin settings and other admin stuff.
7 *
8 * @package WordPressPopularPosts
9 * @subpackage WordPressPopularPosts/Admin
10 * @author Hector Cabrera <me@cabrerahector.com>
11 */
12
13 namespace WordPressPopularPosts\Admin;
14
15 use WordPressPopularPosts\Helper;
16 use WordPressPopularPosts\Output;
17 use WordPressPopularPosts\Query;
18
19 class Admin {
20
21 /**
22 * Slug of the plugin screen.
23 *
24 * @since 3.0.0
25 * @var string
26 */
27 protected $screen_hook_suffix = NULL;
28
29 /**
30 * Plugin options.
31 *
32 * @var array $config
33 * @access private
34 */
35 private $config;
36
37 /**
38 * Image object
39 *
40 * @since 4.0.2
41 * @var WordPressPopularPosts\Image
42 */
43 private $thumbnail;
44
45 /**
46 * Construct.
47 *
48 * @since 5.0.0
49 * @param array $config Admin settings.
50 * @param \WordPressPopularPosts\Image $thumbnail Image class.
51 */
52 public function __construct(array $config, \WordPressPopularPosts\Image $thumbnail)
53 {
54 $this->config = $config;
55 $this->thumbnail = $thumbnail;
56
57 // Delete old data on demand
58 if ( 1 == $this->config['tools']['log']['limit'] ) {
59 if ( ! wp_next_scheduled('wpp_cache_event') ) {
60 $midnight = strtotime('midnight') - ( get_option('gmt_offset') * HOUR_IN_SECONDS ) + DAY_IN_SECONDS;
61 wp_schedule_event($midnight, 'daily', 'wpp_cache_event');
62 }
63 } else {
64 // Remove the scheduled event if exists
65 if ( $timestamp = wp_next_scheduled('wpp_cache_event') ) {
66 wp_unschedule_event($timestamp, 'wpp_cache_event');
67 }
68 }
69
70 // Allow WP themers / coders to override data sampling status (active/inactive)
71 $this->config['tools']['sampling']['active'] = apply_filters('wpp_data_sampling', $this->config['tools']['sampling']['active']);
72
73 if (
74 ! ( wp_using_ext_object_cache() && defined('WPP_CACHE_VIEWS') && WPP_CACHE_VIEWS ) // Not using a persistent object cache
75 && ! $this->config['tools']['sampling']['active'] // Not using Data Sampling
76 ) {
77 // Schedule performance nag
78 if ( ! wp_next_scheduled('wpp_maybe_performance_nag') ) {
79 wp_schedule_event(time(), 'hourly', 'wpp_maybe_performance_nag');
80 }
81 } else {
82 // Remove the scheduled performance nag if found
83 if ( $timestamp = wp_next_scheduled('wpp_maybe_performance_nag') ) {
84 wp_unschedule_event($timestamp, 'wpp_maybe_performance_nag');
85 }
86 }
87 }
88
89 /**
90 * WordPress public-facing hooks.
91 *
92 * @since 5.0.0
93 */
94 public function hooks()
95 {
96 // Upgrade check
97 add_action('init', [$this, 'upgrade_check']);
98 // Hook fired when a new blog is activated on WP Multisite
99 add_action('wpmu_new_blog', [$this, 'activate_new_site']);
100 // Hook fired when a blog is deleted on WP Multisite
101 add_filter('wpmu_drop_tables', [$this, 'delete_site_data'], 10, 2);
102 // At-A-Glance
103 add_filter('dashboard_glance_items', [$this, 'at_a_glance_stats']);
104 add_action('admin_head', [$this, 'at_a_glance_stats_css']);
105 // Dashboard Trending Now widget
106 add_action('wp_dashboard_setup', [$this, 'add_dashboard_widgets']);
107 // Load WPP's admin styles and scripts
108 add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
109 // Add admin screen
110 add_action('admin_menu', [$this, 'add_plugin_admin_menu']);
111 // Contextual help
112 add_action('admin_head', [$this, 'add_contextual_help']);
113 // Add plugin settings link
114 add_filter('plugin_action_links', [$this, 'add_plugin_settings_link'], 10, 2);
115 // Update chart
116 add_action('wp_ajax_wpp_update_chart', [$this, 'update_chart']);
117 // Get lists
118 add_action('wp_ajax_wpp_get_most_viewed', [$this, 'get_popular_items']);
119 add_action('wp_ajax_wpp_get_most_commented', [$this, 'get_popular_items']);
120 add_action('wp_ajax_wpp_get_trending', [$this, 'get_popular_items']);
121 // Delete plugin data
122 add_action('wp_ajax_wpp_clear_data', [$this, 'clear_data']);
123 // Empty plugin's images cache
124 add_action('wp_ajax_wpp_clear_thumbnail', [$this, 'clear_thumbnails']);
125 // Flush cached thumbnail on featured image change/deletion
126 add_action('updated_post_meta', [$this, 'updated_post_meta'], 10, 4);
127 add_action('deleted_post_meta', [$this, 'deleted_post_meta'], 10, 4);
128 // Purge post data on post/page deletion
129 add_action('admin_init', [$this, 'purge_post_data']);
130 // Purge old data on demand
131 add_action('wpp_cache_event', [$this, 'purge_data']);
132 // Maybe performance nag
133 add_action('wpp_maybe_performance_nag', [$this, 'performance_check']);
134 add_action('wp_ajax_wpp_handle_performance_notice', [$this, 'handle_performance_notice']);
135 // Show notices
136 add_action('admin_notices', [$this, 'notices']);
137 }
138
139 /**
140 * Checks if an upgrade procedure is required.
141 *
142 * @since 2.4.0
143 */
144 public function upgrade_check()
145 {
146 $this->upgrade_site();
147 }
148
149 /**
150 * Checks whether a performance tweak may be necessary.
151 *
152 * @since 5.0.2
153 */
154 public function performance_check()
155 {
156 $performance_nag = get_option('wpp_performance_nag');
157
158 if ( ! $performance_nag ) {
159 $performance_nag = [
160 'status' => 0,
161 'last_checked' => null
162 ];
163 add_option('wpp_performance_nag', $performance_nag);
164 }
165
166 if ( 3 != $performance_nag['status'] ) { // 0 = inactive, 1 = active, 2 = remind me later, 3 = dismissed
167 global $wpdb;
168
169 $views_count = $wpdb->get_var(
170 $wpdb->prepare(
171 "SELECT IFNULL(SUM(pageviews), 0) AS views FROM {$wpdb->prefix}popularpostssummary WHERE view_datetime > DATE_SUB(%s, INTERVAL 1 HOUR);",
172 Helper::now()
173 )
174 );
175
176 // This site is probably a mid/high traffic one,
177 // display performance nag
178 if ( $views_count >= 420 ) {
179 if ( 0 == $performance_nag['status'] ) {
180 $performance_nag['status'] = 1;
181 $performance_nag['last_checked'] = Helper::timestamp();
182 update_option('wpp_performance_nag', $performance_nag);
183 }
184 }
185 }
186 }
187
188 /**
189 * Upgrades single site.
190 *
191 * @since 4.0.7
192 */
193 private function upgrade_site()
194 {
195 // Get WPP version
196 $wpp_ver = get_option('wpp_ver');
197
198 if ( ! $wpp_ver ) {
199 add_option('wpp_ver', WPP_VERSION);
200 } elseif ( version_compare($wpp_ver, WPP_VERSION, '<') ) {
201 $this->upgrade();
202 }
203 }
204
205 /**
206 * On plugin upgrade, performs a number of actions: update WPP database tables structures (if needed),
207 * run the setup wizard (if needed), and some other checks.
208 *
209 * @since 2.4.0
210 * @access private
211 * @global object $wpdb
212 */
213 private function upgrade()
214 {
215 $now = Helper::now();
216
217 // Keep the upgrade process from running too many times
218 if ( $wpp_update = get_option('wpp_update') ) {
219 $from_time = strtotime($wpp_update);
220 $to_time = strtotime($now);
221 $difference_in_minutes = round(abs($to_time - $from_time)/60, 2);
222
223 // Upgrade flag is still valid, abort
224 if ( $difference_in_minutes <= 15 )
225 return;
226 // Upgrade flag expired, delete it and continue
227 delete_option('wpp_update');
228 }
229
230 global $wpdb;
231
232 // Upgrade flag
233 add_option('wpp_update', $now);
234
235 // Set table name
236 $prefix = $wpdb->prefix . "popularposts";
237
238 // Update data table structure and indexes
239 $dataFields = $wpdb->get_results("SHOW FIELDS FROM {$prefix}data;");
240
241 foreach ( $dataFields as $column ) {
242 if ( "day" == $column->Field ) {
243 $wpdb->query("ALTER TABLE {$prefix}data ALTER COLUMN day DROP DEFAULT;");
244 }
245
246 if ( "last_viewed" == $column->Field ) {
247 $wpdb->query("ALTER TABLE {$prefix}data ALTER COLUMN last_viewed DROP DEFAULT;");
248 }
249 }
250
251 // Update summary table structure and indexes
252 $summaryFields = $wpdb->get_results("SHOW FIELDS FROM {$prefix}summary;");
253
254 foreach ( $summaryFields as $column ) {
255 if ( "last_viewed" == $column->Field ) {
256 $wpdb->query("ALTER TABLE {$prefix}summary CHANGE last_viewed view_datetime datetime NOT NULL, ADD KEY view_datetime (view_datetime);");
257 }
258
259 if ( "view_date" == $column->Field ) {
260 $wpdb->query("ALTER TABLE {$prefix}summary ALTER COLUMN view_date DROP DEFAULT;");
261 }
262
263 if ( "view_datetime" == $column->Field ) {
264 $wpdb->query("ALTER TABLE {$prefix}summary ALTER COLUMN view_datetime DROP DEFAULT;");
265 }
266 }
267
268 $summaryIndexes = $wpdb->get_results("SHOW INDEX FROM {$prefix}summary;");
269
270 foreach( $summaryIndexes as $index ) {
271 if ( 'ID_date' == $index->Key_name ) {
272 $wpdb->query("ALTER TABLE {$prefix}summary DROP INDEX ID_date;");
273 }
274
275 if ( 'last_viewed' == $index->Key_name ) {
276 $wpdb->query("ALTER TABLE {$prefix}summary DROP INDEX last_viewed;");
277 }
278 }
279
280 // Validate the structure of the tables, create missing tables / fields if necessary
281 \WordPressPopularPosts\Activation\Activator::track_new_site();
282
283 // Check storage engine
284 $storage_engine_data = $wpdb->get_var("SELECT `ENGINE` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA`='{$wpdb->dbname}' AND `TABLE_NAME`='{$prefix}data';");
285
286 if ( 'InnoDB' != $storage_engine_data ) {
287 $wpdb->query("ALTER TABLE {$prefix}data ENGINE=InnoDB;");
288 }
289
290 $storage_engine_summary = $wpdb->get_var("SELECT `ENGINE` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA`='{$wpdb->dbname}' AND `TABLE_NAME`='{$prefix}summary';");
291
292 if ( 'InnoDB' != $storage_engine_summary ) {
293 $wpdb->query("ALTER TABLE {$prefix}summary ENGINE=InnoDB;");
294 }
295
296 // Update WPP version
297 update_option('wpp_ver', WPP_VERSION);
298 // Remove upgrade flag
299 delete_option('wpp_update');
300 }
301
302 /**
303 * Fired when a new blog is activated on WP Multisite.
304 *
305 * @since 3.0.0
306 * @param int $blog_id New blog ID
307 */
308 public function activate_new_site($blog_id)
309 {
310 if ( 1 !== did_action('wpmu_new_blog') )
311 return;
312
313 // run activation for the new blog
314 switch_to_blog($blog_id);
315 \WordPressPopularPosts\Activation\Activator::track_new_site();
316 // switch back to current blog
317 restore_current_blog();
318 }
319
320 /**
321 * Fired when a blog is deleted on WP Multisite.
322 *
323 * @since 4.0.0
324 * @param array $tables
325 * @param int $blog_id
326 * @return array
327 */
328 public function delete_site_data($tables, $blog_id)
329 {
330 global $wpdb;
331
332 $tables[] = $wpdb->prefix . 'popularpostsdata';
333 $tables[] = $wpdb->prefix . 'popularpostssummary';
334
335 return $tables;
336 }
337
338 /**
339 * Display some statistics at the "At a Glance" box from the Dashboard.
340 *
341 * @since 4.1.0
342 */
343 public function at_a_glance_stats()
344 {
345 global $wpdb;
346
347 $glances = [];
348 $args = ['post', 'page'];
349 $post_type_placeholders = '%s, %s';
350
351 if (
352 isset($this->config['stats']['post_type'])
353 && ! empty($this->config['stats']['post_type'])
354 ) {
355 $args = array_map('trim', explode(',', $this->config['stats']['post_type']));
356 $post_type_placeholders = implode(', ', array_fill(0, count($args), '%s'));
357 }
358
359 $args[] = Helper::now();
360
361 $query = $wpdb->prepare(
362 "SELECT SUM(pageviews) AS total
363 FROM `{$wpdb->prefix}popularpostssummary` v LEFT JOIN `{$wpdb->prefix}posts` p ON v.postid = p.ID
364 WHERE p.post_type IN({$post_type_placeholders}) AND p.post_status = 'publish' AND p.post_password = '' AND v.view_datetime > DATE_SUB(%s, INTERVAL 1 HOUR);"
365 , $args
366 );
367
368 $total_views = $wpdb->get_var($query);
369
370 $pageviews = sprintf(
371 _n('%s view in the last hour', '%s views in the last hour', $total_views, 'wordpress-popular-posts'),
372 number_format_i18n($total_views)
373 );
374
375 if ( current_user_can('edit_published_posts') ) {
376 $glances[] = '<a class="wpp-views-count" href="' . admin_url('options-general.php?page=wordpress-popular-posts') . '">' . $pageviews . '</a>';
377 }
378 else {
379 $glances[] = '<span class="wpp-views-count">' . $pageviews . '</a>';
380 }
381
382 return $glances;
383 }
384
385 /**
386 * Add custom inline CSS styles for At a Glance stats.
387 *
388 * @since 4.1.0
389 */
390 public function at_a_glance_stats_css()
391 {
392 echo '<style>#dashboard_right_now a.wpp-views-count:before, #dashboard_right_now span.wpp-views-count:before {content: "\f177";}</style>';
393 }
394
395 /**
396 * Adds a widget to the dashboard.
397 *
398 * @since 5.0.0
399 */
400 public function add_dashboard_widgets()
401 {
402 if ( current_user_can('edit_published_posts') ) {
403 wp_add_dashboard_widget(
404 'wpp_trending_dashboard_widget',
405 __('Trending now', 'wordpress-popular-posts'),
406 [$this, 'trending_dashboard_widget']
407 );
408 }
409 }
410
411 /**
412 * Outputs the contents of our Trending Dashboard Widget.
413 *
414 * @since 5.0.0
415 */
416 function trending_dashboard_widget()
417 {
418 ?>
419 <style>
420 #wpp_trending_dashboard_widget .inside {
421 overflow: hidden;
422 position: relative;
423 min-height: 150px;
424 padding-bottom: 22px;
425 }
426
427 #wpp_trending_dashboard_widget .inside::after {
428 position: absolute;
429 top: 0;
430 left: 0;
431 opacity: 0.2;
432 display: block;
433 content: '';
434 width: 100%;
435 height: 100%;
436 z-index: 1;
437 background-image: url('<?php echo plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/images/flame.png'; ?>');
438 background-position: right bottom;
439 background-repeat: no-repeat;
440 background-size: 34% auto;
441 }
442
443 #wpp_trending_dashboard_widget .inside .no-data {
444 position: absolute;
445 top: calc(50% - 11px);
446 left: 50%;
447 z-index: 2;
448 margin: 0;
449 padding: 0;
450 width: 96%;
451 transform: translate(-50.0001%, -50.0001%);
452 }
453
454 #wpp_trending_dashboard_widget .inside .popular-posts-list,
455 #wpp_trending_dashboard_widget .inside p#wpp_read_more {
456 position: relative;
457 z-index: 2;
458 }
459
460 #wpp_trending_dashboard_widget .inside .popular-posts-list {
461 margin: 1em 0;
462 }
463
464 #wpp_trending_dashboard_widget .inside p#wpp_read_more {
465 position: absolute;
466 left: 0;
467 bottom: 0;
468 width: 100%;
469 text-align: center;
470 }
471 </style>
472 <?php
473 $args = [
474 'range' => 'custom',
475 'time_quantity' => 1,
476 'time_unit' => 'HOUR',
477 'post_type' => $this->config['stats']['post_type'],
478 'limit' => 5,
479 'stats_tag' => [
480 'views' => 1,
481 'comment_count' => 1
482 ]
483 ];
484 $options = apply_filters('wpp_trending_dashboard_widget_args', []);
485
486 if ( is_array($options) && ! empty($options) )
487 $args = Helper::merge_array_r($args, $options);
488
489 $trending = new Query($args);
490 $posts = $trending->get_posts();
491
492 $this->render_list($posts, 'trending');
493 echo '<p id="wpp_read_more"><a href="' . admin_url('options-general.php?page=wordpress-popular-posts') . '">' . __('View more', 'wordpress-popular-posts') . '</a><p>';
494
495 }
496
497 /**
498 * Enqueues admin facing assets.
499 *
500 * @since 5.0.0
501 */
502 public function enqueue_assets()
503 {
504 $screen = get_current_screen();
505
506 if ( isset($screen->id) ) {
507 if ( $screen->id == $this->screen_hook_suffix ) {
508 wp_enqueue_style('wpp-datepicker-theme', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/css/datepicker.css', [], WPP_VERSION, 'all');
509
510 wp_enqueue_media();
511 wp_enqueue_script('jquery-ui-datepicker');
512 wp_enqueue_script('chartjs', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/vendor/Chart.min.js', [], WPP_VERSION);
513
514 wp_register_script('wpp-chart', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/chart.js', ['chartjs'], WPP_VERSION);
515 wp_localize_script('wpp-chart', 'wpp_chart_params', [
516 'colors' => $this->get_admin_color_scheme()
517 ]);
518 wp_enqueue_script('wpp-chart');
519
520 wp_register_script('wordpress-popular-posts-admin-script', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/admin.js', ['jquery'], WPP_VERSION, true);
521 wp_localize_script('wordpress-popular-posts-admin-script', 'wpp_admin_params', [
522 'label_media_upload_button' => __("Use this image", "wordpress-popular-posts"),
523 'nonce' => wp_create_nonce("wpp_admin_nonce"),
524 'nonce_reset_data' => wp_create_nonce("wpp_nonce_reset_data"),
525 'nonce_reset_thumbnails' => wp_create_nonce("wpp_nonce_reset_thumbnails"),
526 'text_confirm_reset_cache_table' => __("This operation will delete all entries from WordPress Popular Posts' cache table and cannot be undone.", 'wordpress-popular-posts'),
527 'text_cache_table_cleared' => __('Success! The cache table has been cleared!', 'wordpress-popular-posts'),
528 'text_cache_table_missing' => __('Error: cache table does not exist.', 'wordpress-popular-posts'),
529 'text_confirm_reset_all_tables' => __("This operation will delete all stored info from WordPress Popular Posts' data tables and cannot be undone.", 'wordpress-popular-posts'),
530 'text_all_table_cleared' => __('Success! All data have been cleared!', 'wordpress-popular-posts'),
531 'text_tables_missing' => __('Error: one or both data tables are missing.', 'wordpress-popular-posts'),
532 'text_confirm_image_cache_reset' => __('This operation will delete all cached thumbnails and cannot be undone.', 'wordpress-popular-posts'),
533 'text_image_cache_cleared' => __('Success! All files have been deleted!', 'wordpress-popular-posts'),
534 'text_image_cache_already_empty' => __('The thumbnail cache is already empty!', 'wordpress-popular-posts'),
535 'text_continue' => __('Do you want to continue?', 'wordpress-popular-posts'),
536 'text_insufficient_permissions' => __('Sorry, you do not have enough permissions to do this. Please contact the site administrator for support.', 'wordpress-popular-posts'),
537 'text_invalid_action' => __('Invalid action.', 'wordpress-popular-posts')
538 ]);
539 wp_enqueue_script('wordpress-popular-posts-admin-script');
540 }
541
542 if ( $screen->id == $this->screen_hook_suffix || 'dashboard' == $screen->id ) {
543 // Fontello icons
544 wp_enqueue_style('wpp-fontello', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/css/fontello.css', [], WPP_VERSION, 'all');
545 wp_enqueue_style('wordpress-popular-posts-admin-styles', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/css/admin.css', [], WPP_VERSION, 'all');
546 }
547 }
548
549 $performance_nag = get_option('wpp_performance_nag');
550
551 if (
552 isset($performance_nag['status'])
553 && 3 != $performance_nag['status'] // 0 = inactive, 1 = active, 2 = remind me later, 3 = dismissed
554 ) {
555 $now = Helper::timestamp();
556
557 // How much time has passed since the notice was last displayed?
558 $last_checked = isset($performance_nag['last_checked']) ? $performance_nag['last_checked'] : 0;
559
560 if ( $last_checked ) {
561 $last_checked = ($now - $last_checked) / (60 * 60);
562 }
563
564 if (
565 1 == $performance_nag['status']
566 || ( 2 == $performance_nag['status'] && $last_checked && $last_checked >= 24 )
567 ) {
568 wp_register_script('wpp-admin-notices', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/admin-notices.js', [], WPP_VERSION);
569 wp_localize_script('wpp-admin-notices', 'wpp_admin_notices_params', [
570 'nonce_performance_nag' => wp_create_nonce("wpp_nonce_performance_nag")
571 ]);
572 wp_enqueue_script('wpp-admin-notices');
573 }
574 }
575 }
576
577 /**
578 * Register the administration menu for this plugin into the WordPress Dashboard menu.
579 *
580 * @since 1.0.0
581 */
582 public function add_plugin_admin_menu()
583 {
584 $this->screen_hook_suffix = add_options_page(
585 'WordPress Popular Posts',
586 'WordPress Popular Posts',
587 'edit_published_posts',
588 'wordpress-popular-posts',
589 [$this, 'display_plugin_admin_page']
590 );
591 }
592
593 /**
594 * Render the settings page for this plugin.
595 *
596 * @since 1.0.0
597 */
598 public function display_plugin_admin_page()
599 {
600 include_once plugin_dir_path(__FILE__) . 'admin-page.php';
601 }
602
603 /**
604 * Adds contextual help menu.
605 *
606 * @since 4.0.0
607 */
608 public function add_contextual_help()
609 {
610 $screen = get_current_screen();
611
612 if ( isset($screen->id) && $screen->id == $this->screen_hook_suffix ){
613 $screen->add_help_tab(
614 [
615 'id' => 'wpp_help_overview',
616 'title' => __('Overview', 'wordpress-popular-posts'),
617 'content' => "<p>" . __("Welcome to WordPress Popular Posts' Dashboard! In this screen you will find statistics on what's popular on your site, tools to further tweak WPP to your needs, and more!", "wordpress-popular-posts") . "</p>"
618 ]
619 );
620 $screen->add_help_tab(
621 [
622 'id' => 'wpp_help_donate',
623 'title' => __('Like this plugin?', 'wordpress-popular-posts'),
624 'content' => '
625 <p style="text-align: center;">' . __('Each donation motivates me to keep releasing free stuff for the WordPress community!', 'wordpress-popular-posts') . '</p>
626 <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top" style="margin: 0; padding: 0; text-align: center;">
627 <input type="hidden" name="cmd" value="_s-xclick">
628 <input type="hidden" name="hosted_button_id" value="RP9SK8KVQHRKS">
629 <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" style="display: inline; margin: 0;">
630 <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
631 </form>
632 <p style="text-align: center;">' . sprintf(__('You can <a href="%s" target="_blank">leave a review</a>, too!', 'wordpress-popular-posts'), 'https://wordpress.org/support/view/plugin-reviews/wordpress-popular-posts?rate=5#postform') . '</p>'
633 ]
634 );
635
636 // Help sidebar
637 $screen->set_help_sidebar(
638 sprintf(
639 __('<p><strong>For more information:</strong></p><ul><li><a href="%1$s">Documentation</a></li><li><a href="%2$s">Support</a></li></ul>', 'wordpress-popular-posts'),
640 "https://github.com/cabrerahector/wordpress-popular-posts/",
641 "https://wordpress.org/support/plugin/wordpress-popular-posts/"
642 )
643 );
644 }
645 }
646
647 /**
648 * Registers Settings link on plugin description.
649 *
650 * @since 2.3.3
651 * @param array $links
652 * @param string $file
653 * @return array
654 */
655 public function add_plugin_settings_link($links, $file)
656 {
657 $plugin_file = 'wordpress-popular-posts/wordpress-popular-posts.php';
658
659 if (
660 is_plugin_active($plugin_file)
661 && $plugin_file == $file
662 ) {
663 array_unshift(
664 $links,
665 '<a href="' . admin_url('options-general.php?page=wordpress-popular-posts') . '">' . __('Settings') . '</a>',
666 '<a href="https://wordpress.org/support/plugin/wordpress-popular-posts/">' . __('Support', 'wordpress-popular-posts') . '</a>'
667 );
668 }
669
670 return $links;
671 }
672
673 /**
674 * Gets current admin color scheme.
675 *
676 * @since 4.0.0
677 * @return array
678 */
679 private function get_admin_color_scheme()
680 {
681 global $_wp_admin_css_colors;
682
683 if (
684 is_array($_wp_admin_css_colors)
685 && count($_wp_admin_css_colors)
686 ) {
687 $current_user = wp_get_current_user();
688 $color_scheme = get_user_option('admin_color', $current_user->ID);
689
690 if (
691 empty($color_scheme)
692 || ! isset($_wp_admin_css_colors[ $color_scheme])
693 ) {
694 $color_scheme = 'fresh';
695 }
696
697 if ( isset($_wp_admin_css_colors[$color_scheme]) && isset($_wp_admin_css_colors[$color_scheme]->colors) ) {
698 return $_wp_admin_css_colors[$color_scheme]->colors;
699 }
700
701 }
702
703 // Fallback, just in case
704 return ['#333', '#999', '#881111', '#a80000'];
705 }
706
707 /**
708 * Fetches chart data.
709 *
710 * @since 4.0.0
711 * @return string
712 */
713 public function get_chart_data($range = 'last7days', $time_unit = 'HOUR', $time_quantity = 24)
714 {
715 $dates = $this->get_dates($range, $time_unit, $time_quantity);
716 $start_date = $dates[0];
717 $end_date = $dates[count($dates) - 1];
718 $date_range = Helper::get_date_range($start_date, $end_date, 'Y-m-d H:i:s');
719 $views_data = $this->get_range_item_count($start_date, $end_date, 'views');
720 $views = [];
721 $comments_data = $this->get_range_item_count($start_date, $end_date, 'comments');
722 $comments = [];
723
724 if ( 'today' != $range ) {
725 foreach($date_range as $date) {
726 $key = date('Y-m-d', strtotime($date));
727 $views[] = ( ! isset($views_data[$key]) ) ? 0 : $views_data[$key]->pageviews;
728 $comments[] = ( ! isset($comments_data[$key]) ) ? 0 : $comments_data[$key]->comments;
729 }
730 } else {
731 $key = date('Y-m-d', strtotime($dates[0]));
732 $views[] = ( ! isset($views_data[$key]) ) ? 0 : $views_data[$key]->pageviews;
733 $comments[] = ( ! isset($comments_data[$key]) ) ? 0 : $comments_data[$key]->comments;
734 }
735
736 if ( $start_date != $end_date )
737 $label_date_range = date_i18n('M, D d', strtotime($start_date)) . ' &mdash; ' . date_i18n('M, D d', strtotime($end_date));
738 else
739 $label_date_range = date_i18n('M, D d', strtotime($start_date));
740
741 $total_views = array_sum($views);
742 $total_comments = array_sum($comments);
743
744 $label_summary = sprintf(_n('%s view', '%s views', $total_views, 'wordpress-popular-posts'), '<strong>' . number_format_i18n($total_views) . '</strong>') . '<br style="display: none;" /> / ' . sprintf(_n('%s comment', '%s comments', $total_comments, 'wordpress-popular-posts'), '<strong>' . number_format_i18n($total_comments) . '</strong>');
745
746 // Format labels
747 if ( 'today' != $range ) {
748 $date_range = array_map(function($d){
749 return date_i18n('D d', strtotime($d));
750 }, $date_range);
751 } else {
752 $date_range = [date_i18n('D d', strtotime($date_range[0]))];
753 $comments = [array_sum($comments)];
754 $views = [array_sum($views)];
755 }
756
757 $response = [
758 'totals' => [
759 'label_summary' => $label_summary,
760 'label_date_range' => $label_date_range,
761 ],
762 'labels' => $date_range,
763 'datasets' => [
764 [
765 'label' => __("Comments", "wordpress-popular-posts"),
766 'data' => $comments
767 ],
768 [
769 'label' => __("Views", "wordpress-popular-posts"),
770 'data' => $views
771 ]
772 ]
773 ];
774
775 return json_encode($response);
776 }
777
778 /**
779 * Returns an array of dates.
780 *
781 * @since 5.0.0
782 * @return array|bool
783 */
784 private function get_dates($range = 'last7days', $time_unit = 'HOUR', $time_quantity = 24)
785 {
786 $valid_ranges = ['today', 'daily', 'last24hours', 'weekly', 'last7days', 'monthly', 'last30days', 'all', 'custom'];
787 $range = in_array($range, $valid_ranges) ? $range : 'last7days';
788 $now = new \DateTime(Helper::now(), new \DateTimeZone(Helper::get_timezone()));
789
790 // Determine time range
791 switch( $range ){
792 case "last24hours":
793 case "daily":
794 $end_date = $now->format('Y-m-d H:i:s');
795 $start_date = $now->modify('-1 day')->format('Y-m-d H:i:s');
796 break;
797
798 case "today":
799 $start_date = $now->format('Y-m-d') . ' 00:00:00';
800 $end_date = $now->format('Y-m-d') . ' 23:59:59';
801 break;
802
803 case "last7days":
804 case "weekly":
805 $end_date = $now->format('Y-m-d') . ' 23:59:59';
806 $start_date = $now->modify('-6 day')->format('Y-m-d') . ' 00:00:00';
807 break;
808
809 case "last30days":
810 case "monthly":
811 $end_date = $now->format('Y-m-d') . ' 23:59:59';
812 $start_date = $now->modify('-29 day')->format('Y-m-d') . ' 00:00:00';
813 break;
814
815 case "custom":
816 $end_date = $now->format('Y-m-d H:i:s');
817
818 if (
819 Helper::is_number($time_quantity)
820 && $time_quantity >= 1
821 ) {
822 $end_date = $now->format('Y-m-d H:i:s');
823 $time_unit = strtoupper($time_unit);
824
825 if ( 'MINUTE' == $time_unit ) {
826 $start_date = $now->sub(new \DateInterval('PT' . (60 * $time_quantity) . 'S'))->format('Y-m-d H:i:s');
827 } elseif ( 'HOUR' == $time_unit ) {
828 $start_date = $now->sub(new \DateInterval('PT' . ((60 * $time_quantity) - 1) . 'M59S'))->format('Y-m-d H:i:s');
829 } else {
830 $end_date = $now->format('Y-m-d') . ' 23:59:59';
831 $start_date = $now->sub(new \DateInterval('P' . ($time_quantity - 1) . 'D'))->format('Y-m-d') . ' 00:00:00';
832 }
833 } // fallback to last 24 hours
834 else {
835 $start_date = $now->modify('-1 day')->format('Y-m-d H:i:s');
836 }
837
838 // Check if custom date range has been requested
839 $dates = null;
840
841 if ( isset($_GET['dates']) ) {
842 $dates = explode(" ~ ", $_GET['dates']);
843
844 if (
845 ! is_array($dates)
846 || empty($dates)
847 || ! Helper::is_valid_date($dates[0])
848 ) {
849 $dates = null;
850 } else {
851 if (
852 ! isset($dates[1])
853 || ! Helper::is_valid_date($dates[1])
854 ) {
855 $dates[1] = $dates[0];
856 }
857
858 $start_date = $dates[0] . ' 00:00:00';
859 $end_date = $dates[1] . ' 23:59:59';
860 }
861 }
862
863 break;
864
865 default:
866 $end_date = $now->format('Y-m-d') . ' 23:59:59';
867 $start_date = $now->modify('-6 day')->format('Y-m-d') . ' 00:00:00';
868 break;
869 }
870
871 return [$start_date, $end_date];
872 }
873
874 /**
875 * Returns an array of dates with views/comments count.
876 *
877 * @since 5.0.0
878 * @param string $start_date
879 * @param string $end_date
880 * @param string $item
881 * @return array
882 */
883 public function get_range_item_count($start_date, $end_date, $item = 'views')
884 {
885 global $wpdb;
886
887 $args = array_map('trim', explode(',', $this->config['stats']['post_type']));
888
889 if ( empty($args) ) {
890 $args = ['post', 'page'];
891 }
892
893 $post_type_placeholders = array_fill(0, count($args), '%s');
894
895 if ( $this->config['stats']['freshness'] ) {
896 $args[] = $start_date;
897 }
898
899 // Append dates to arguments list
900 array_unshift($args, $start_date, $end_date);
901
902 if ( $item == 'comments' ) {
903 $query = $wpdb->prepare(
904 "SELECT DATE(`c`.`comment_date_gmt`) AS `c_date`, COUNT(*) AS `comments`
905 FROM `{$wpdb->comments}` c INNER JOIN `{$wpdb->posts}` p ON `c`.`comment_post_ID` = `p`.`ID`
906 WHERE (`c`.`comment_date_gmt` BETWEEN %s AND %s) AND `c`.`comment_approved` = '1' AND `p`.`post_type` IN (". implode(", ", $post_type_placeholders) . ") AND `p`.`post_status` = 'publish' AND `p`.`post_password` = ''
907 " . ( $this->config['stats']['freshness'] ? " AND `p`.`post_date` >= %s" : "" ) . "
908 GROUP BY `c_date` ORDER BY `c_date` DESC;",
909 $args
910 );
911 } else {
912 $query = $wpdb->prepare(
913 "SELECT `v`.`view_date`, SUM(`v`.`pageviews`) AS `pageviews`
914 FROM `{$wpdb->prefix}popularpostssummary` v INNER JOIN `{$wpdb->posts}` p ON `v`.`postid` = `p`.`ID`
915 WHERE (`v`.`view_datetime` BETWEEN %s AND %s) AND `p`.`post_type` IN (". implode(", ", $post_type_placeholders) . ") AND `p`.`post_status` = 'publish' AND `p`.`post_password` = ''
916 " . ( $this->config['stats']['freshness'] ? " AND `p`.`post_date` >= %s" : "" ) . "
917 GROUP BY `v`.`view_date` ORDER BY `v`.`view_date` DESC;",
918 $args
919 );
920
921 //error_log($query);
922 }
923
924 return $wpdb->get_results($query, OBJECT_K);
925 }
926
927 /**
928 * Updates chart via AJAX.
929 *
930 * @since 4.0.0
931 */
932 public function update_chart()
933 {
934 $response = [
935 'status' => 'error'
936 ];
937 $nonce = isset($_GET['nonce']) ? $_GET['nonce'] : null;
938
939 if ( wp_verify_nonce($nonce, 'wpp_admin_nonce') ) {
940
941 $valid_ranges = ['today', 'daily', 'last24hours', 'weekly', 'last7days', 'monthly', 'last30days', 'all', 'custom'];
942 $time_units = ["MINUTE", "HOUR", "DAY"];
943
944 $range = ( isset($_GET['range']) && in_array($_GET['range'], $valid_ranges) ) ? $_GET['range'] : 'last7days';
945 $time_quantity = ( isset($_GET['time_quantity']) && filter_var($_GET['time_quantity'], FILTER_VALIDATE_INT) ) ? $_GET['time_quantity'] : 24;
946 $time_unit = ( isset($_GET['time_unit']) && in_array(strtoupper($_GET['time_unit']), $time_units) ) ? $_GET['time_unit'] : 'hour';
947
948 $this->config['stats']['range'] = $range;
949 $this->config['stats']['time_quantity'] = $time_quantity;
950 $this->config['stats']['time_unit'] = $time_unit;
951
952 update_option('wpp_settings_config', $this->config);
953
954 $response = [
955 'status' => 'ok',
956 'data' => json_decode(
957 $this->get_chart_data($this->config['stats']['range'], $this->config['stats']['time_unit'], $this->config['stats']['time_quantity']),
958 true
959 )
960 ];
961 }
962
963 wp_send_json($response);
964 }
965
966 /**
967 * Fetches most viewed/commented/trending posts via AJAX.
968 *
969 * @since 5.0.0
970 */
971 public function get_popular_items()
972 {
973 $items = isset($_GET['items']) ? $_GET['items'] : null;
974 $nonce = isset($_GET['nonce']) ? $_GET['nonce'] : null;
975
976 if ( wp_verify_nonce($nonce, 'wpp_admin_nonce') ) {
977 $args = [
978 'range' => $this->config['stats']['range'],
979 'time_quantity' => $this->config['stats']['time_quantity'],
980 'time_unit' => $this->config['stats']['time_unit'],
981 'post_type' => $this->config['stats']['post_type'],
982 'freshness' => $this->config['stats']['freshness'],
983 'limit' => $this->config['stats']['limit'],
984 'stats_tag' => [
985 'date' => [
986 'active' => 1
987 ]
988 ]
989 ];
990
991 if ( 'most-commented' == $items ) {
992 $args['order_by'] = 'comments';
993 $args['stats_tag']['comment_count'] = 1;
994 $args['stats_tag']['views'] = 0;
995 } elseif ( 'trending' == $items ) {
996 $args['range'] = 'custom';
997 $args['time_quantity'] = 1;
998 $args['time_unit'] = 'HOUR';
999 $args['stats_tag']['comment_count'] = 1;
1000 $args['stats_tag']['views'] = 1;
1001 } else {
1002 $args['stats_tag']['comment_count'] = 0;
1003 $args['stats_tag']['views'] = 1;
1004 }
1005
1006 if ( 'trending' != $items ) {
1007
1008 add_filter('wpp_query_join', function($join, $options) use ($items)
1009 {
1010 global $wpdb;
1011 $dates = null;
1012
1013 if ( isset($_GET['dates']) ) {
1014 $dates = explode(" ~ ", $_GET['dates']);
1015
1016 if (
1017 ! is_array($dates)
1018 || empty($dates)
1019 || ! Helper::is_valid_date($dates[0])
1020 ) {
1021 $dates = null;
1022 } else {
1023 if (
1024 ! isset($dates[1])
1025 || ! Helper::is_valid_date($dates[1])
1026 ) {
1027 $dates[1] = $dates[0];
1028 }
1029
1030 $start_date = $dates[0];
1031 $end_date = $dates[1];
1032 }
1033
1034 }
1035
1036 if ( $dates ) {
1037 if ( 'most-commented' == $items ) {
1038 return "INNER JOIN (SELECT comment_post_ID, COUNT(comment_post_ID) AS comment_count, comment_date_gmt FROM `{$wpdb->comments}` WHERE comment_date_gmt BETWEEN '{$dates[0]} 00:00:00' AND '{$dates[1]} 23:59:59' AND comment_approved = '1' GROUP BY comment_post_ID) c ON p.ID = c.comment_post_ID";
1039 }
1040
1041 return "INNER JOIN (SELECT SUM(pageviews) AS pageviews, view_date, postid FROM `{$wpdb->prefix}popularpostssummary` WHERE view_datetime BETWEEN '{$dates[0]} 00:00:00' AND '{$dates[1]} 23:59:59' GROUP BY postid) v ON p.ID = v.postid";
1042 }
1043
1044 $now = Helper::now();
1045
1046 // Determine time range
1047 switch( $options['range'] ){
1048 case "last24hours":
1049 case "daily":
1050 $interval = "24 HOUR";
1051 break;
1052
1053 case "today":
1054 $hours = date('H', strtotime($now));
1055 $minutes = $hours * 60 + (int) date( 'i', strtotime($now) );
1056 $interval = "{$minutes} MINUTE";
1057 break;
1058
1059 case "last7days":
1060 case "weekly":
1061 $interval = "6 DAY";
1062 break;
1063
1064 case "last30days":
1065 case "monthly":
1066 $interval = "29 DAY";
1067 break;
1068
1069 case "custom":
1070 $time_units = ["MINUTE", "HOUR", "DAY"];
1071 $interval = "24 HOUR";
1072
1073 // Valid time unit
1074 if (
1075 isset($options['time_unit'])
1076 && in_array(strtoupper($options['time_unit']), $time_units)
1077 && isset($options['time_quantity'])
1078 && filter_var($options['time_quantity'], FILTER_VALIDATE_INT)
1079 && $options['time_quantity'] > 0
1080 ) {
1081 $interval = "{$options['time_quantity']} " . strtoupper($options['time_unit']);
1082 }
1083
1084 break;
1085
1086 default:
1087 $interval = "1 DAY";
1088 break;
1089 }
1090
1091 if ( 'most-commented' == $items ) {
1092 return "INNER JOIN (SELECT comment_post_ID, COUNT(comment_post_ID) AS comment_count, comment_date_gmt FROM `{$wpdb->comments}` WHERE comment_date_gmt > DATE_SUB('{$now}', INTERVAL {$interval}) AND comment_approved = '1' GROUP BY comment_post_ID) c ON p.ID = c.comment_post_ID";
1093 }
1094
1095 return "INNER JOIN (SELECT SUM(pageviews) AS pageviews, view_date, postid FROM `{$wpdb->prefix}popularpostssummary` WHERE view_datetime > DATE_SUB('{$now}', INTERVAL {$interval}) GROUP BY postid) v ON p.ID = v.postid";
1096 }, 1, 2);
1097
1098 }
1099
1100 $popular_items = new \WordPressPopularPosts\Query($args);
1101 $posts = $popular_items->get_posts();
1102
1103 if ( 'trending' != $items ) {
1104 remove_all_filters('wpp_query_join', 1);
1105 }
1106
1107 $this->render_list($posts, $items);
1108 }
1109
1110 wp_die();
1111 }
1112
1113 /**
1114 * Renders popular posts lists.
1115 *
1116 * @since 5.0.0
1117 * @param array
1118 */
1119 public function render_list($posts, $list = 'most-viewed')
1120 {
1121 if (
1122 is_array($posts)
1123 && ! empty($posts)
1124 ) {
1125 ?>
1126 <ol class="popular-posts-list">
1127 <?php
1128 foreach( $posts as $post ) { ?>
1129 <li>
1130 <a href="<?php echo get_permalink($post->id); ?>" class="wpp-title"><?php echo sanitize_text_field(apply_filters('the_title', $post->title, $post->id)); ?></a>
1131 <div>
1132 <?php if ( 'most-viewed' == $list ) : ?>
1133 <span><?php printf(_n('%s view', '%s views', $post->pageviews, 'wordpress-popular-posts' ), number_format_i18n($post->pageviews)); ?></span>
1134 <?php elseif ( 'most-commented' == $list ) : ?>
1135 <span><?php printf(_n('%s comment', '%s comments', $post->comment_count, 'wordpress-popular-posts'), number_format_i18n($post->comment_count)); ?></span>
1136 <?php else : ?>
1137 <span><?php printf(_n('%s view', '%s views', $post->pageviews, 'wordpress-popular-posts' ), number_format_i18n($post->pageviews)); ?></span>, <span><?php printf(_n('%s comment', '%s comments', $post->comment_count, 'wordpress-popular-posts'), number_format_i18n($post->comment_count)); ?></span>
1138 <?php endif; ?>
1139 <small> &mdash; <a href="<?php echo get_permalink($post->id); ?>"><?php _e("View"); ?></a><?php if ( current_user_can('edit_others_posts') ): ?> | <a href="<?php echo get_edit_post_link($post->id); ?>"><?php _e("Edit"); ?></a><?php endif; ?></small>
1140 </div>
1141 </li>
1142 <?php
1143 }
1144 ?>
1145 </ol>
1146 <?php
1147 }
1148 else {
1149 ?>
1150 <p class="no-data" style="text-align: center;"><?php _e("Looks like your site's activity is a little low right now. <br />Spread the word and come back later!", "wordpress-popular-posts"); ?></p>
1151 <?php
1152 }
1153 }
1154
1155 /**
1156 * Truncates data and cache on demand.
1157 *
1158 * @since 2.0.0
1159 * @global object $wpdb
1160 */
1161 public function clear_data()
1162 {
1163 $token = isset($_POST['token']) ? $_POST['token'] : null;
1164 $clear = isset($_POST['clear']) ? $_POST['clear'] : null;
1165
1166 if (
1167 current_user_can('manage_options')
1168 && wp_verify_nonce($token, 'wpp_nonce_reset_data')
1169 && $clear
1170 ) {
1171 global $wpdb;
1172
1173 // set table name
1174 $prefix = $wpdb->prefix . "popularposts";
1175
1176 if ( $clear == 'cache' ) {
1177 if ( $wpdb->get_var("SHOW TABLES LIKE '{$prefix}summary'") ) {
1178 $wpdb->query("TRUNCATE TABLE {$prefix}summary;");
1179 $this->flush_transients();
1180
1181 echo 1;
1182 } else {
1183 echo 2;
1184 }
1185 } elseif ( $clear == 'all' ) {
1186 if ( $wpdb->get_var("SHOW TABLES LIKE '{$prefix}data'") && $wpdb->get_var("SHOW TABLES LIKE '{$prefix}summary'") ) {
1187 $wpdb->query("TRUNCATE TABLE {$prefix}data;");
1188 $wpdb->query("TRUNCATE TABLE {$prefix}summary;");
1189 $this->flush_transients();
1190
1191 echo 1;
1192 } else {
1193 echo 2;
1194 }
1195 } else {
1196 echo 3;
1197 }
1198 } else {
1199 echo 4;
1200 }
1201
1202 wp_die();
1203 }
1204
1205 /**
1206 * Deletes cached (transient) data.
1207 *
1208 * @since 3.0.0
1209 * @access private
1210 */
1211 private function flush_transients()
1212 {
1213 $wpp_transients = get_option('wpp_transients');
1214
1215 if ( $wpp_transients && is_array($wpp_transients) && ! empty($wpp_transients) ) {
1216 for ( $t=0; $t < count($wpp_transients); $t++ )
1217 delete_transient($wpp_transients[$t]);
1218
1219 update_option('wpp_transients', []);
1220 }
1221 }
1222
1223 /**
1224 * Truncates thumbnails cache on demand.
1225 *
1226 * @since 2.0.0
1227 * @global object wpdb
1228 */
1229 public function clear_thumbnails()
1230 {
1231 $wpp_uploads_dir = $this->thumbnail->get_plugin_uploads_dir();
1232
1233 if ( is_array($wpp_uploads_dir) && ! empty($wpp_uploads_dir) ) {
1234 $token = isset($_POST['token']) ? $_POST['token'] : null;
1235
1236 if (
1237 current_user_can('edit_published_posts')
1238 && wp_verify_nonce($token, 'wpp_nonce_reset_thumbnails')
1239 ) {
1240 if ( is_dir($wpp_uploads_dir['basedir']) ) {
1241 $files = glob("{$wpp_uploads_dir['basedir']}/*"); // get all related images
1242
1243 if ( is_array($files) && ! empty($files) ) {
1244 foreach( $files as $file ){ // iterate files
1245 if ( is_file($file) ) {
1246 @unlink($file); // delete file
1247 }
1248 }
1249 echo 1;
1250 } else {
1251 echo 2;
1252 }
1253 } else {
1254 echo 3;
1255 }
1256 } else {
1257 echo 4;
1258 }
1259 }
1260
1261 wp_die();
1262 }
1263
1264 /**
1265 * Fires immediately after deleting metadata of a post.
1266 *
1267 * @since 5.0.0
1268 *
1269 * @param int $meta_id Metadata ID.
1270 * @param int $post_id Post ID.
1271 * @param string $meta_key Meta key.
1272 * @param mixed $meta_value Meta value.
1273 */
1274 public function updated_post_meta($meta_id, $post_id, $meta_key, $meta_value)
1275 {
1276 if ( '_thumbnail_id' == $meta_key ) {
1277 $this->flush_post_thumbnail($post_id);
1278 }
1279 }
1280
1281 /**
1282 * Fires immediately after deleting metadata of a post.
1283 *
1284 * @since 5.0.0
1285 *
1286 * @param array $meta_ids An array of deleted metadata entry IDs.
1287 * @param int $post_id Post ID.
1288 * @param string $meta_key Meta key.
1289 * @param mixed $meta_value Meta value.
1290 */
1291 public function deleted_post_meta($meta_ids, $post_id, $meta_key, $meta_value)
1292 {
1293 if ( '_thumbnail_id' == $meta_key ) {
1294 $this->flush_post_thumbnail($post_id);
1295 }
1296 }
1297
1298 /**
1299 * Flushes post's cached thumbnail(s).
1300 *
1301 * @since 3.3.4
1302 *
1303 * @param integer $post_id Post ID
1304 */
1305 public function flush_post_thumbnail($post_id)
1306 {
1307 $wpp_uploads_dir = $this->thumbnail->get_plugin_uploads_dir();
1308
1309 if ( is_array($wpp_uploads_dir) && ! empty($wpp_uploads_dir) ) {
1310 $files = glob("{$wpp_uploads_dir['basedir']}/{$post_id}-*.*"); // get all related images
1311
1312 if ( is_array($files) && ! empty($files) ) {
1313 foreach( $files as $file ){ // iterate files
1314 if ( is_file($file) ) {
1315 @unlink($file); // delete file
1316 }
1317 }
1318 }
1319 }
1320 }
1321
1322 /**
1323 * Purges post from data/summary tables.
1324 *
1325 * @since 3.3.0
1326 */
1327 public function purge_post_data()
1328 {
1329 if ( current_user_can('delete_posts') )
1330 add_action('delete_post', [$this, 'purge_post']);
1331 }
1332
1333 /**
1334 * Purges post from data/summary tables.
1335 *
1336 * @since 3.3.0
1337 * @global object $wpdb
1338 */
1339 public function purge_post($post_ID)
1340 {
1341 global $wpdb;
1342
1343 if ( $wpdb->get_var($wpdb->prepare("SELECT postid FROM {$wpdb->prefix}popularpostsdata WHERE postid = %d", $post_ID)) ) {
1344 // Delete from data table
1345 $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}popularpostsdata WHERE postid = %d;", $post_ID));
1346 // Delete from summary table
1347 $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}popularpostssummary WHERE postid = %d;", $post_ID));
1348 }
1349
1350 // Delete cached thumbnail(s) as well
1351 $this->flush_post_thumbnail($post_ID);
1352 }
1353
1354 /**
1355 * Purges old post data from summary table.
1356 *
1357 * @since 2.0.0
1358 * @global object $wpdb
1359 */
1360 public function purge_data()
1361 {
1362 global $wpdb;
1363 $wpdb->query("DELETE FROM {$wpdb->prefix}popularpostssummary WHERE view_date < DATE_SUB('" . Helper::curdate() . "', INTERVAL {$this->config['tools']['log']['expires_after']} DAY);");
1364 }
1365
1366 /**
1367 * Displays admin notices.
1368 *
1369 * @since 5.0.2
1370 */
1371 public function notices()
1372 {
1373 /** Performance nag */
1374 $performance_nag = get_option('wpp_performance_nag');
1375
1376 if (
1377 isset($performance_nag['status'])
1378 && 3 != $performance_nag['status'] // 0 = inactive, 1 = active, 2 = remind me later, 3 = dismissed
1379 ) {
1380 $now = Helper::timestamp();
1381
1382 // How much time has passed since the notice was last displayed?
1383 $last_checked = isset($performance_nag['last_checked']) ? $performance_nag['last_checked'] : 0;
1384
1385 if ( $last_checked ) {
1386 $last_checked = ($now - $last_checked) / (60 * 60);
1387 }
1388
1389 if (
1390 1 == $performance_nag['status']
1391 || ( 2 == $performance_nag['status'] && $last_checked && $last_checked >= 24 )
1392 ) {
1393 ?>
1394 <div class="notice notice-warning">
1395 <p><?php printf(
1396 __("<strong>WordPress Popular Posts:</strong> It seems your site is popular (great!) You may want to check <a href=\"%s\">these suggestions</a> to make sure your website's performance stays up to par.", 'wordpress-popular-posts'),
1397 'https://github.com/cabrerahector/wordpress-popular-posts/wiki/7.-Performance'
1398 ) ?></p>
1399 <?php if ( current_user_can('manage_options') ) : ?>
1400 <p><a class="button button-primary wpp-dismiss-performance-notice" href="<?php echo add_query_arg('wpp_dismiss_performance_notice', '1'); ?>"><?php _e("Dismiss", "wordpress-popular-posts"); ?></a> <a class="button wpp-remind-performance-notice" href="<?php echo add_query_arg('wpp_remind_performance_notice', '1'); ?>"><?php _e("Remind me later", "wordpress-popular-posts"); ?></a> <span class="spinner" style="float: none;"></span></p>
1401 <?php endif; ?>
1402 </div>
1403 <?php
1404 }
1405 }
1406 }
1407
1408 /**
1409 * Handles performance notice click event.
1410 *
1411 * @since
1412 */
1413 public function handle_performance_notice()
1414 {
1415 $response = [
1416 'status' => 'error'
1417 ];
1418 $token = isset($_POST['token']) ? $_POST['token'] : null;
1419 $dismiss = isset($_POST['dismiss']) ? $_POST['dismiss'] : 0;
1420
1421 if (
1422 current_user_can('manage_options')
1423 && wp_verify_nonce($token, 'wpp_nonce_performance_nag')
1424 ) {
1425 $now = Helper::timestamp();
1426
1427 // User dismissed the notice
1428 if ( 1 == $dismiss ) {
1429 $performance_nag['status'] = 3;
1430 } // User asked us to remind them later
1431 else {
1432 $performance_nag['status'] = 2;
1433 }
1434
1435 $performance_nag['last_checked'] = $now;
1436
1437 update_option('wpp_performance_nag', $performance_nag);
1438
1439 $response = [
1440 'status' => 'success'
1441 ];
1442 }
1443
1444 wp_send_json($response);
1445 }
1446 }
1447