PluginProbe ʕ •ᴥ•ʔ
Post Views Counter / 1.6.1
Post Views Counter v1.6.1
1.7.13 1.7.12 1.7.11 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.2 1.3.2.1 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.7.10 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9
post-views-counter / post-views-counter.php
post-views-counter Last commit date
assets 6 months ago blocks 6 months ago css 6 months ago includes 6 months ago js 6 months ago languages 6 months ago index.php 6 years ago post-views-counter.php 6 months ago readme.txt 6 months ago
post-views-counter.php
994 lines
1 <?php
2 /*
3 Plugin Name: Post Views Counter
4 Description: Post Views Counter allows you to collect and display how many times a post, page, or other content has been viewed in a simple, fast and reliable way.
5 Version: 1.6.1
6 Author: dFactory
7 Author URI: https://dfactory.co/
8 Plugin URI: https://postviewscounter.com/
9 License: MIT License
10 License URI: https://opensource.org/licenses/MIT
11 Text Domain: post-views-counter
12 Domain Path: /languages
13
14 Post Views Counter
15 Copyright (C) 2014-2025, Digital Factory - info@digitalfactory.pl
16
17 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 // exit if accessed directly
25 if ( ! defined( 'ABSPATH' ) )
26 exit;
27
28 if ( ! class_exists( 'Post_Views_Counter' ) ) {
29 /**
30 * Post Views Counter final class.
31 *
32 * @class Post_Views_Counter
33 * @version 1.6.1
34 */
35 final class Post_Views_Counter {
36
37 private static $instance;
38 private $notices;
39 public $options;
40 public $defaults = [
41 'general' => [
42 'post_types_count' => [ 'post' ],
43 'taxonomies_count' => false,
44 'users_count' => false,
45 'other_count' => false,
46 'technology_count' => false,
47 'data_storage' => 'cookies',
48 'amp_support' => false,
49 'counter_mode' => 'php',
50 'time_between_counts' => [
51 'number' => 24,
52 'type' => 'hours'
53 ],
54 'count_time' => 'gmt',
55 'reset_counts' => [
56 'number' => 0,
57 'type' => 'days'
58 ],
59 'caching_compatibility' => false,
60 'object_cache' => false,
61 'flush_interval' => [
62 'number' => 5,
63 'type' => 'minutes'
64 ],
65 'exclude' => [
66 'groups' => [],
67 'roles' => []
68 ],
69 'exclude_ips' => [],
70 'strict_counts' => false,
71 'cron_run' => true,
72 'cron_update' => true,
73 'update_version' => 1,
74 'update_notice' => true,
75 'update_delay_date' => 0
76 ],
77 'display' => [
78 'label' => 'Post Views:',
79 'display_period' => 'total',
80 'taxonomies_display' => [],
81 'user_display' => false,
82 'post_types_display' => [ 'post' ],
83 'page_types_display' => [ 'singular' ],
84 'restrict_display' => [
85 'groups' => [],
86 'roles' => []
87 ],
88 'position' => 'after',
89 'post_views_column' => true,
90 'restrict_edit_views' => false,
91 'dynamic_loading' => false,
92 'use_format' => true,
93 'display_style' => [
94 'icon' => true,
95 'text' => true
96 ],
97 'icon_class' => 'dashicons-chart-bar',
98 'toolbar_statistics' => true,
99 'menu_position' => 'top'
100 ],
101 'other' => [
102 'import_meta_key' => 'views',
103 'deactivation_delete' => false,
104 'license' => ''
105 ],
106 'version' => '1.6.1'
107 ];
108
109 // instances
110 public $counter;
111 public $crawler;
112 public $cron;
113 public $dashboard;
114 public $frontend;
115 public $functions;
116 public $settings;
117 public $settings_api;
118 public $import;
119
120 /**
121 * Disable object cloning.
122 *
123 * @return void
124 */
125 public function __clone() {}
126
127 /**
128 * Disable unserializing of the class.
129 *
130 * @return void
131 */
132 public function __wakeup() {}
133
134 /**
135 * Main plugin instance, insures that only one instance of the class exists in memory at one time.
136 *
137 * @return object
138 */
139 public static function instance() {
140 if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Post_Views_Counter ) ) {
141 self::$instance = new Post_Views_Counter();
142
143 // short init?
144 if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
145 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-counter.php' );
146 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-crawler-detect.php' );
147 include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
148
149 self::$instance->counter = new Post_Views_Counter_Counter();
150 self::$instance->crawler = new Post_Views_Counter_Crawler_Detect();
151
152 // we need to initialize crawler here since it is not called in SHORTINIT mode
153 self::$instance->crawler->init();
154 // regular setup
155 } else {
156 self::$instance->includes();
157
158 // create settings API
159 self::$instance->settings_api = new Post_Views_Counter_Settings_API(
160 [
161 'object' => self::$instance,
162 'prefix' => 'post_views_counter',
163 'slug' => 'post-views-counter',
164 'domain' => 'post-views-counter',
165 'plugin' => 'Post Views Counter',
166 'plugin_url' => POST_VIEWS_COUNTER_URL
167 ]
168 );
169
170 // initialize other classes
171 self::$instance->functions = new Post_Views_Counter_Functions();
172
173 new Post_Views_Counter_Update();
174
175 self::$instance->settings = new Post_Views_Counter_Settings();
176 self::$instance->import = new Post_Views_Counter_Import();
177
178 new Post_Views_Counter_Admin();
179 new Post_Views_Counter_Query();
180
181 self::$instance->cron = new Post_Views_Counter_Cron();
182 self::$instance->counter = new Post_Views_Counter_Counter();
183
184 new Post_Views_Counter_Columns();
185 new Post_Views_Counter_Toolbar();
186
187 self::$instance->crawler = new Post_Views_Counter_Crawler_Detect();
188 self::$instance->frontend = new Post_Views_Counter_Frontend();
189 self::$instance->dashboard = new Post_Views_Counter_Dashboard();
190
191 new Post_Views_Counter_Widgets();
192 }
193 }
194
195 return self::$instance;
196 }
197
198 /**
199 * Setup plugin constants.
200 *
201 * @return void
202 */
203 private function define_constants() {
204 // fix plugin_basename empty $wp_plugin_paths var
205 if ( ! ( defined( 'SHORTINIT' ) && SHORTINIT ) ) {
206 define( 'POST_VIEWS_COUNTER_URL', plugins_url( '', __FILE__ ) );
207 define( 'POST_VIEWS_COUNTER_BASENAME', plugin_basename( __FILE__ ) );
208 define( 'POST_VIEWS_COUNTER_REL_PATH', dirname( POST_VIEWS_COUNTER_BASENAME ) );
209 }
210
211 define( 'POST_VIEWS_COUNTER_PATH', plugin_dir_path( __FILE__ ) );
212 }
213
214 /**
215 * Include required files.
216 *
217 * @return void
218 */
219 private function includes() {
220 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-functions.php' );
221 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-update.php' );
222 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-api.php' );
223 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings.php' );
224 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-import.php' );
225 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-admin.php' );
226 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-columns.php' );
227 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-toolbar.php' );
228 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-query.php' );
229 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-cron.php' );
230 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-counter.php' );
231 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-crawler-detect.php' );
232 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-frontend.php' );
233 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-dashboard.php' );
234 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-widgets.php' );
235 }
236
237 /**
238 * Class constructor.
239 *
240 * @return void
241 */
242 private function __construct() {
243 // define plugin constants
244 $this->define_constants();
245
246 // load translations early to avoid just-in-time loading warnings in WP 6.7+
247 if ( ! ( defined( 'SHORTINIT' ) && SHORTINIT ) )
248 $this->load_textdomain();
249
250 // short init?
251 if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
252 $this->options = [
253 'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
254 'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) ),
255 'other' => array_merge( $this->defaults['other'], get_option( 'post_views_counter_settings_other', $this->defaults['other'] ) )
256 ];
257
258 return;
259 }
260
261 // activation hooks
262 register_activation_hook( __FILE__, [ $this, 'activation' ] );
263 register_deactivation_hook( __FILE__, [ $this, 'deactivation' ] );
264
265 // settings
266 $this->options = [
267 'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
268 'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) ),
269 'other' => array_merge( $this->defaults['other'], get_option( 'post_views_counter_settings_other', $this->defaults['other'] ) )
270 ];
271
272 // 1.5.3+
273 if ( ! isset( $this->options['general']['post_views_column'] ) )
274 $this->options['general']['post_views_column'] = $this->options['display']['post_views_column'];
275
276 if ( ! isset( $this->options['general']['restrict_edit_views'] ) )
277 $this->options['general']['restrict_edit_views'] = $this->options['display']['restrict_edit_views'];
278
279 // actions
280 add_action( 'plugins_loaded', [ $this, 'extend_caching_plugins' ], -1 );
281 add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
282 add_action( 'wp_loaded', [ $this, 'load_pluggable_functions' ] );
283 add_action( 'init', [ $this, 'register_blocks' ] );
284 add_action( 'admin_init', [ $this, 'update_notice' ] );
285 add_action( 'wp_initialize_site', [ $this, 'init_new_network_site' ] );
286 add_action( 'wp_ajax_pvc_dismiss_notice', [ $this, 'dismiss_notice' ] );
287
288 // filters
289 add_filter( 'plugin_action_links_' . POST_VIEWS_COUNTER_BASENAME, [ $this, 'plugin_settings_link' ] );
290 }
291
292 /**
293 * Extend list of caching plugins.
294 *
295 * @return void
296 */
297 public function extend_caching_plugins() {
298 // add new caching plugins
299 add_filter( 'pvc_active_caching_plugins', [ $this->settings, 'extend_active_caching_plugins' ] );
300 add_filter( 'pvc_is_plugin_active', [ $this->settings, 'extend_is_plugin_active' ], 10, 2 );
301 }
302
303 /**
304 * Register blocks.
305 *
306 * @global object $wp_version
307 *
308 * @return void
309 */
310 public function register_blocks() {
311 global $wp_version;
312
313 // actions
314 add_action( 'enqueue_block_editor_assets', [ $this, 'block_editor_enqueue_scripts' ] );
315
316 // filters
317 if ( version_compare( $wp_version, '5.8', '>=' ) )
318 add_filter( 'block_categories_all', [ $this, 'add_block_category' ] );
319 else
320 add_filter( 'block_categories', [ $this, 'add_block_category' ] );
321
322 add_filter( 'register_block_type_args', [ $this, 'update_block_args' ], 10, 2 );
323
324 register_block_type( __DIR__ . '/blocks/most-viewed-posts/build' );
325 register_block_type( __DIR__ . '/blocks/post-views/build' );
326 }
327
328 /**
329 * Enqueue block scripts.
330 *
331 * @global object $wp_version
332 *
333 * @return void
334 */
335 public function block_editor_enqueue_scripts() {
336 // enqueue script
337 wp_enqueue_script( 'post-views-counter-block-editor-script', POST_VIEWS_COUNTER_URL . '/js/dummy.js', [] );
338
339 $block_image_sizes = [];
340
341 // image sizes
342 $image_sizes = array_merge( [ 'full' ], get_intermediate_image_sizes() );
343
344 // sort image sizes by name, ascending
345 sort( $image_sizes, SORT_STRING );
346
347 foreach ( $image_sizes as $image_size ) {
348 $block_image_sizes[] = [
349 'label' => $image_size,
350 'value' => $image_size
351 ];
352 }
353
354 $post_types = Post_Views_Counter()->functions->get_post_types();
355
356 // prepare script data
357 $script_data = [
358 'postTypesKeys' => array_combine( array_keys( $post_types ), array_fill( 0, count( $post_types ), false ) ),
359 'postTypes' => $post_types,
360 'imageSizes' => $block_image_sizes
361 ];
362
363 // force post as enabled
364 $script_data['postTypesKeys']['post'] = true;
365
366 // prepare script data
367 $script_data['periods'] = [ [
368 'label' => __( 'Total Views', 'post-views-counter' ),
369 'value' => 'total'
370 ] ];
371
372 $script_data = apply_filters( 'pvc_block_editor_data', $script_data );
373
374 wp_add_inline_script( 'post-views-counter-block-editor-script', 'var pvcBlockEditorData = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
375 }
376
377 /**
378 * Update block arguments.
379 *
380 * @param array $args
381 * @param string $block_type
382 *
383 * @return array
384 */
385 function update_block_args( $args, $block_type ) {
386 // most viewed posts block
387 if ( $block_type === 'post-views-counter/most-viewed-posts' )
388 $args['render_callback'] = [ $this, 'most_viewed_posts_render_callback' ];
389 // post views block
390 elseif ( $block_type === 'post-views-counter/post-views' )
391 $args['render_callback'] = [ $this, 'post_views_render_callback' ];
392
393 return $args;
394 }
395
396 /**
397 * Server side block renderer for most viewed posts.
398 *
399 * @param array $attributes
400 * @param string $content
401 *
402 * @return array
403 */
404 function most_viewed_posts_render_callback( $attributes, $content ) {
405 $post_types = [];
406
407 foreach ( $attributes['postTypes'] as $post_type => $enabled ) {
408 if ( $enabled === true || $enabled === 'true' )
409 $post_types[] = $post_type;
410 }
411
412 // map block attributes
413 $args = [
414 'number_of_posts' => max( 1, (int) $attributes['numberOfPosts'] ),
415 'post_type' => $post_types,
416 'period' => $attributes['period'],
417 'order' => $attributes['order'],
418 'thumbnail_size' => $attributes['thumbnailSize'],
419 'list_type' => $attributes['listType'],
420 'show_post_views' => (bool) $attributes['displayPostViews'],
421 'show_post_thumbnail' => (bool) $attributes['displayPostThumbnail'],
422 'show_post_author' => (bool) $attributes['displayPostAuthor'],
423 'show_post_excerpt' => (bool) $attributes['displayPostExcerpt'],
424 'no_posts_message' => $attributes['noPostsMessage']
425 ];
426
427 $title = trim( $attributes['title'] );
428
429 $html = '<div ' . get_block_wrapper_attributes() . '>';
430
431 if ( $title !== '' )
432 $html .= '<h2 class="block-title">' . esc_html( $title ) . '</h2>';
433
434 $html .= pvc_most_viewed_posts( $args, false );
435 $html .= '</div>';
436
437 return $html;
438 }
439
440 /**
441 * Server side block renderer for post views.
442 *
443 * @param array $attributes
444 * @param string $content
445 *
446 * @return array
447 */
448 function post_views_render_callback( $attributes, $content ) {
449 $html = '<div ' . get_block_wrapper_attributes() . '>';
450 $html .= pvc_post_views( (int) $attributes['postID'], false, $attributes['period'] );
451 $html .= '</div>';
452
453 return $html;
454 }
455
456 /**
457 * Add new blocks category.
458 *
459 * @param array $categories
460 *
461 * @return array
462 */
463 function add_block_category( $categories ) {
464 $categories[] = [
465 'slug' => 'post-views-counter',
466 'title' => 'Post Views Counter'
467 ];
468
469 return $categories;
470 }
471
472 /**
473 * Update notice.
474 *
475 * @return void
476 */
477 public function update_notice() {
478 if ( ! current_user_can( 'install_plugins' ) )
479 return;
480
481 $current_update = 2;
482
483 // get current time
484 $current_time = time();
485
486 if ( $this->options['general']['update_version'] < $current_update ) {
487 // check version, if update version is lower than plugin version, set update notice to true
488 $this->options['general'] = array_merge(
489 $this->options['general'],
490 [
491 'update_version' => $current_update,
492 'update_notice' => true
493 ]
494 );
495
496 update_option( 'post_views_counter_settings_general', $this->options['general'] );
497
498 // set activation date
499 $activation_date = get_option( 'post_views_counter_activation_date' );
500
501 if ( $activation_date === false )
502 update_option( 'post_views_counter_activation_date', $current_time );
503 }
504
505 // display current version notice
506 if ( $this->options['general']['update_notice'] === true ) {
507 // include notice js, only if needed
508 add_action( 'admin_print_scripts', [ $this, 'admin_inline_js' ], 999 );
509
510 // get activation date
511 $activation_date = get_option( 'post_views_counter_activation_date' );
512
513 if ( (int) $this->options['general']['update_delay_date'] === 0 ) {
514 if ( $activation_date + 2 * WEEK_IN_SECONDS > $current_time )
515 $this->options['general']['update_delay_date'] = $activation_date + 2 * WEEK_IN_SECONDS;
516 else
517 $this->options['general']['update_delay_date'] = $current_time;
518
519 update_option( 'post_views_counter_settings_general', $this->options['general'] );
520 }
521
522 if ( ( ! empty( $this->options['general']['update_delay_date'] ) ? (int) $this->options['general']['update_delay_date'] : $current_time ) <= $current_time )
523 $this->add_notice( sprintf( __( "Hey, you've been using <strong>Post Views Counter</strong> for more than %s.", 'post-views-counter' ), human_time_diff( $activation_date, $current_time ) ) . '<br />' . __( 'Could you please do me a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation.', 'post-views-counter' ) . '<br /><br />' . __( 'Your help is much appreciated. Thank you very much', 'post-views-counter' ) . ' ~ <strong>Bartosz Arendt</strong>, ' . __( 'founder of', 'post-views-counter' ) . ' <a href="https://postviewscounter.com/" target="_blank">Post Views Counter</a>.' . '<br /><br />' . '<a href="https://wordpress.org/support/plugin/post-views-counter/reviews/?filter=5#new-post" class="pvc-dismissible-notice" target="_blank" rel="noopener">' . __( 'Ok, you deserve it', 'post-views-counter' ) . '</a><br /><a href="#" class="pvc-dismissible-notice pvc-delay-notice" rel="noopener">' . __( 'Nope, maybe later', 'post-views-counter' ) . '</a><br /><a href="#" class="pvc-dismissible-notice" rel="noopener">' . __( 'I already did', 'post-views-counter' ) . '</a>', 'notice notice-info is-dismissible pvc-notice' );
524 }
525 }
526
527 /**
528 * Add admin notices.
529 *
530 * @param string $html Notice HTML
531 * @param string $status Notice status
532 * @param bool $paragraph Whether to use paragraph
533 * @param bool $network
534 *
535 * @return void
536 */
537 public function add_notice( $html = '', $status = 'error', $paragraph = true, $network = false ) {
538 $this->notices[] = [
539 'html' => $html,
540 'status' => $status,
541 'paragraph' => $paragraph
542 ];
543
544 add_action( 'admin_notices', [ $this, 'display_notice' ] );
545
546 if ( $network )
547 add_action( 'network_admin_notices', [ $this, 'display_notice' ] );
548 }
549
550 /**
551 * Print admin notices.
552 *
553 * @return void
554 */
555 public function display_notice() {
556 $allowed_html = array_merge(
557 wp_kses_allowed_html( 'post' ),
558 [
559 'input' => [
560 'type' => true,
561 'name' => true,
562 'class' => true,
563 'value' => true
564 ],
565 'form' => [
566 'action' => true,
567 'method' => true
568 ]
569 ]
570 );
571
572 foreach ( $this->notices as $notice ) {
573 echo '
574 <div class="' . esc_attr( $notice['status'] ) . '">
575 ' . ( $notice['paragraph'] ? '<p>' : '' ) . '
576 ' . wp_kses( $notice['html'], $allowed_html ) . '
577 ' . ( $notice['paragraph'] ? '</p>' : '' ) . '
578 </div>';
579 }
580 }
581
582 /**
583 * Print admin scripts.
584 *
585 * @return void
586 */
587 public function admin_inline_js() {
588 if ( ! current_user_can( 'install_plugins' ) )
589 return;
590
591 // register and enqueue styles
592 wp_register_script( 'pvc-notices', false );
593 wp_enqueue_script( 'pvc-notices' );
594
595 // add styles
596 wp_add_inline_script( 'pvc-notices', "
597 ( function( $ ) {
598 // ready event
599 $( function() {
600 // save dismiss state
601 $( '.pvc-notice.is-dismissible' ).on( 'click', '.notice-dismiss, .pvc-dismissible-notice', function( e ) {
602 if ( e.currentTarget.target !== '_blank' )
603 e.preventDefault();
604
605 var notice_action = 'hide';
606
607 if ( e.currentTarget.classList.contains( 'pvc-delay-notice' ) )
608 notice_action = 'delay';
609
610 $.post( ajaxurl, {
611 action: 'pvc_dismiss_notice',
612 notice_action: notice_action,
613 url: '" . esc_url_raw( admin_url( 'admin-ajax.php' ) ) . "',
614 nonce: '" . esc_attr( wp_create_nonce( 'pvc_dismiss_notice' ) ) . "'
615 } );
616
617 $( e.delegateTarget ).slideUp( 'fast' );
618 } );
619 } );
620 } )( jQuery );
621 ", 'after' );
622 }
623
624 /**
625 * Dismiss notice.
626 *
627 * @return void
628 */
629 public function dismiss_notice() {
630 if ( ! current_user_can( 'install_plugins' ) )
631 return;
632
633 if ( isset( $_REQUEST['nonce'] ) && wp_verify_nonce( $_REQUEST['nonce'], 'pvc_dismiss_notice' ) ) {
634 $notice_action = empty( $_REQUEST['notice_action'] ) || $_REQUEST['notice_action'] === 'hide' ? 'hide' : sanitize_text_field( $_REQUEST['notice_action'] );
635
636 switch ( $notice_action ) {
637 // delay notice
638 case 'delay':
639 // set delay period to 1 week from now
640 $this->options['general'] = array_merge(
641 $this->options['general'],
642 [
643 'update_delay_date' => time() + 2 * WEEK_IN_SECONDS
644 ]
645 );
646 update_option( 'post_views_counter_settings_general', $this->options['general'] );
647 break;
648
649 // hide notice
650 default:
651 $this->options['general'] = array_merge(
652 $this->options['general'],
653 [
654 'update_notice' => false
655 ]
656 );
657 $this->options['general'] = array_merge(
658 $this->options['general'],
659 [
660 'update_delay_date' => 0
661 ]
662 );
663
664 update_option( 'post_views_counter_settings_general', $this->options['general'] );
665 }
666 }
667
668 exit;
669 }
670
671 /**
672 * Plugin activation.
673 *
674 * @global object $wpdb
675 *
676 * @param bool $network
677 *
678 * @return void
679 */
680 public function activation( $network ) {
681 // network activation?
682 if ( is_multisite() && $network ) {
683 global $wpdb;
684
685 // get all available sites
686 $blogs_ids = $wpdb->get_col( 'SELECT blog_id FROM ' . $wpdb->blogs );
687
688 foreach ( $blogs_ids as $blog_id ) {
689 // change to another site
690 switch_to_blog( (int) $blog_id );
691
692 // run current site activation process
693 $this->activate_site();
694
695 restore_current_blog();
696 }
697 } else
698 $this->activate_site();
699 }
700
701 /**
702 * Single site activation.
703 *
704 * @global object $wpdb
705 * @global string $charset_collate
706 *
707 * @return void
708 */
709 public function activate_site() {
710 global $wpdb, $charset_collate;
711
712 // required for dbdelta
713 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
714
715 // create post views table
716 dbDelta( '
717 CREATE TABLE IF NOT EXISTS ' . $wpdb->prefix . 'post_views (
718 `id` bigint unsigned NOT NULL,
719 `type` tinyint(1) unsigned NOT NULL,
720 `period` varchar(8) NOT NULL,
721 `count` bigint unsigned NOT NULL,
722 PRIMARY KEY (type, period, id),
723 UNIQUE INDEX id_type_period_count (id, type, period, count) USING BTREE,
724 INDEX type_period_count (type, period, count) USING BTREE
725 ) ' . $charset_collate . ';'
726 );
727
728 // add default options
729 add_option( 'post_views_counter_settings_general', $this->defaults['general'], null, false );
730 add_option( 'post_views_counter_settings_display', $this->defaults['display'], null, false );
731 add_option( 'post_views_counter_settings_other', $this->defaults['other'], null, false );
732 add_option( 'post_views_counter_version', $this->defaults['version'], null, false );
733 }
734
735 /**
736 * Plugin deactivation.
737 *
738 * @global object $wpdb
739 *
740 * @param bool $network
741 *
742 * @return void
743 */
744 public function deactivation( $network ) {
745 // network deactivation?
746 if ( is_multisite() && $network ) {
747 global $wpdb;
748
749 // get all available sites
750 $blogs_ids = $wpdb->get_col( 'SELECT blog_id FROM ' . $wpdb->blogs );
751
752 foreach ( $blogs_ids as $blog_id ) {
753 // change to another site
754 switch_to_blog( (int) $blog_id );
755
756 // run current site deactivation process
757 $this->deactivate_site( true );
758
759 restore_current_blog();
760 }
761 } else
762 $this->deactivate_site();
763 }
764
765 /**
766 * Single site deactivation.
767 *
768 * @global object $wpdb
769 *
770 * @param bool $multi
771 *
772 * @return void
773 */
774 public function deactivate_site( $multi = false ) {
775 if ( $multi === true ) {
776 $options = get_option( 'post_views_counter_settings_other' );
777 $check = $options['deactivation_delete'];
778 } else
779 $check = $this->options['other']['deactivation_delete'];
780
781 // delete options if needed
782 if ( $check ) {
783 // delete options
784 delete_option( 'post_views_counter_settings_general' );
785 delete_option( 'post_views_counter_settings_display' );
786 delete_option( 'post_views_counter_settings_other' );
787 delete_option( 'post_views_counter_activation_date' );
788 delete_option( 'post_views_counter_version' );
789
790 // delete transients
791 delete_transient( 'post_views_counter_ip_cache' );
792
793 global $wpdb;
794
795 // delete table from database
796 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'post_views' );
797 }
798
799 // remove schedule
800 wp_clear_scheduled_hook( 'pvc_reset_counts' );
801
802 remove_action( 'pvc_reset_counts', [ $this->cron, 'reset_counts' ] );
803 }
804
805 /**
806 * Initialize new network site.
807 *
808 * @param object $site
809 *
810 * @return void
811 */
812 public function init_new_network_site( $site ) {
813 if ( is_multisite() ) {
814 // change to another site
815 switch_to_blog( $site->blog_id );
816
817 // run current site activation process
818 $this->activate_site();
819
820 restore_current_blog();
821 }
822 }
823
824 /**
825 * Load text domain.
826 *
827 * @return void
828 */
829 public function load_textdomain() {
830 load_plugin_textdomain( 'post-views-counter', false, POST_VIEWS_COUNTER_REL_PATH . '/languages/' );
831 }
832
833 /**
834 * Load pluggable template functions.
835 *
836 * @return void
837 */
838 public function load_pluggable_functions() {
839 include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
840 }
841
842 /**
843 * Enqueue admin scripts and styles.
844 *
845 * @global string $post_type
846 * @global string $wp_version
847 *
848 * @param string $page
849 *
850 * @return void
851 */
852 public function admin_enqueue_scripts( $page ) {
853 global $post_type;
854 global $wp_version;
855
856 // register styles
857 wp_register_style( 'pvc-admin', POST_VIEWS_COUNTER_URL . '/css/admin.css', [], $this->defaults['version'] );
858
859 // register scripts
860 wp_register_script( 'pvc-admin-settings', POST_VIEWS_COUNTER_URL . '/js/admin-settings.js', [ 'jquery' ], $this->defaults['version'] );
861 wp_register_script( 'pvc-admin-post', POST_VIEWS_COUNTER_URL . '/js/admin-post.js', [ 'jquery' ], $this->defaults['version'] );
862 wp_register_script( 'pvc-admin-quick-edit', POST_VIEWS_COUNTER_URL . '/js/admin-quick-edit.js', [ 'jquery' ], $this->defaults['version'] );
863
864 // load on pvc settings page
865 if ( in_array( $page, [ 'toplevel_page_post-views-counter', 'settings_page_post-views-counter' ], true ) ) {
866 wp_enqueue_script( 'pvc-admin-settings' );
867
868 // prepare script data
869 $script_data = [
870 'resetToDefaults' => esc_html__( 'Are you sure you want to reset these settings to defaults?', 'post-views-counter' ),
871 'resetViews' => esc_html__( 'Are you sure you want to delete all existing data?', 'post-views-counter' ),
872 'importViews' => esc_html__( 'Are you sure you want to import views now?', 'post-views-counter' )
873 ];
874
875 wp_add_inline_script( 'pvc-admin-settings', 'var pvcArgsSettings = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
876
877 wp_enqueue_style( 'pvc-admin' );
878 // load on single post page
879 } elseif ( $page === 'post.php' || $page === 'post-new.php' ) {
880 $post_types = Post_Views_Counter()->options['general']['post_types_count'];
881
882 if ( ! in_array( $post_type, (array) $post_types ) )
883 return;
884
885 wp_enqueue_style( 'pvc-admin' );
886 wp_enqueue_script( 'pvc-admin-post' );
887 // edit post
888 } elseif ( $page === 'edit.php' ) {
889 $post_types = Post_Views_Counter()->options['general']['post_types_count'];
890
891 if ( ! in_array( $post_type, (array) $post_types ) )
892 return;
893
894 wp_enqueue_style( 'pvc-admin' );
895
896 // woocommerce
897 if ( get_post_type() !== 'product' ) {
898 wp_enqueue_script( 'pvc-admin-quick-edit' );
899
900 // prepare script data
901 $script_data = [
902 'nonce' => wp_create_nonce( 'pvc_save_bulk_post_views' ),
903 'wpVersion59' => version_compare( $wp_version, '5.9', '>=' )
904 ];
905
906 wp_add_inline_script( 'pvc-admin-quick-edit', 'var pvcArgsQuickEdit = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
907 }
908 // widgets
909 } elseif ( $page === 'widgets.php' )
910 wp_enqueue_script( 'pvc-admin-widgets', POST_VIEWS_COUNTER_URL . '/js/admin-widgets.js', [ 'jquery' ], $this->defaults['version'] );
911 // media
912 elseif ( $page === 'upload.php' )
913 wp_enqueue_style( 'pvc-admin' );
914
915 // register and enqueue styles
916 wp_register_style( 'pvc-pro-style', false );
917 wp_enqueue_style( 'pvc-pro-style' );
918
919 // add styles
920 wp_add_inline_style( 'pvc-pro-style', '
921 .post-views-counter-settings tr.pvc-pro th:after, .nav-tab-wrapper a.nav-tab.nav-tab-disabled.pvc-pro:after, .post-views-counter-settings tr.pvc-pro-extended label[for="post_views_counter_general_counter_mode_ajax"]:after,
922 .post-views-counter-settings tr.pvc-pro-extended label[for="pvc_exclude-ai_bots"]:after {
923 content: \'PRO\';
924 display: inline;
925 background-color: #ffc107;
926 color: white;
927 padding: 2px 4px;
928 text-align: center;
929 border-radius: 4px;
930 margin-left: 4px;
931 font-weight: bold;
932 font-size: 11px;
933 }' );
934 }
935
936 /**
937 * Get the preferred admin menu position.
938 *
939 * @return string
940 */
941 public function get_menu_position() {
942 $position = isset( $this->options['display']['menu_position'] ) ? $this->options['display']['menu_position'] : '';
943
944 if ( in_array( $position, [ 'top', 'sub' ], true ) )
945 return $position;
946
947 if ( isset( $this->options['other']['menu_position'] ) && in_array( $this->options['other']['menu_position'], [ 'top', 'sub' ], true ) )
948 return $this->options['other']['menu_position'];
949
950 return 'top';
951 }
952
953 /**
954 * Add link to Settings page.
955 *
956 * @param array $links
957 *
958 * @return array
959 */
960 public function plugin_settings_link( $links ) {
961 if ( ! current_user_can( 'manage_options' ) )
962 return $links;
963
964 // submenu?
965 if ( $this->get_menu_position() === 'sub' )
966 $url = admin_url( 'options-general.php?page=post-views-counter' );
967 // topmenu?
968 else
969 $url = admin_url( 'admin.php?page=post-views-counter' );
970
971 array_unshift( $links, sprintf( '<a href="%s">%s</a>', esc_url_raw( $url ), esc_html__( 'Settings', 'post-views-counter' ) ) );
972
973 return $links;
974 }
975 }
976 }
977
978 /**
979 * Initialize Post Views Counter.
980 *
981 * @return object
982 */
983 function Post_Views_Counter() {
984 static $instance;
985
986 // first call to instance() initializes the plugin
987 if ( $instance === null || ! ( $instance instanceof Post_Views_Counter ) )
988 $instance = Post_Views_Counter::instance();
989
990 return $instance;
991 }
992
993 Post_Views_Counter();
994