PluginProbe ʕ •ᴥ•ʔ
Post Views Counter / 1.7.0
Post Views Counter v1.7.0
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
1041 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.7.0
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.7.0
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 'integrations' => [
107 'integrations' => []
108 ],
109 'version' => '1.7.0'
110 ];
111
112 // instances
113 public $counter;
114 public $crawler;
115 public $cron;
116 public $dashboard;
117 public $frontend;
118 public $functions;
119 public $settings;
120 public $settings_api;
121 public $import;
122
123 /**
124 * Disable object cloning.
125 *
126 * @return void
127 */
128 public function __clone() {}
129
130 /**
131 * Disable unserializing of the class.
132 *
133 * @return void
134 */
135 public function __wakeup() {}
136
137 /**
138 * Main plugin instance, insures that only one instance of the class exists in memory at one time.
139 *
140 * @return object
141 */
142 public static function instance() {
143 if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Post_Views_Counter ) ) {
144 self::$instance = new Post_Views_Counter();
145
146 // short init?
147 if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
148 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-counter.php' );
149 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-crawler-detect.php' );
150 include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
151
152 self::$instance->counter = new Post_Views_Counter_Counter();
153 self::$instance->crawler = new Post_Views_Counter_Crawler_Detect();
154
155 // we need to initialize crawler here since it is not called in SHORTINIT mode
156 self::$instance->crawler->init();
157 // regular setup
158 } else {
159 self::$instance->includes();
160
161 // create settings API
162 self::$instance->settings_api = new Post_Views_Counter_Settings_API(
163 [
164 'object' => self::$instance,
165 'prefix' => 'post_views_counter',
166 'slug' => 'post-views-counter',
167 'domain' => 'post-views-counter',
168 'plugin' => 'Post Views Counter',
169 'plugin_url' => POST_VIEWS_COUNTER_URL
170 ]
171 );
172
173 // initialize other classes
174 self::$instance->functions = new Post_Views_Counter_Functions();
175
176 new Post_Views_Counter_Update();
177
178 self::$instance->settings = new Post_Views_Counter_Settings();
179 self::$instance->import = new Post_Views_Counter_Import();
180
181 new Post_Views_Counter_Admin();
182 new Post_Views_Counter_Query();
183
184 self::$instance->cron = new Post_Views_Counter_Cron();
185 self::$instance->counter = new Post_Views_Counter_Counter();
186
187 new Post_Views_Counter_Columns();
188 new Post_Views_Counter_Toolbar();
189
190 self::$instance->crawler = new Post_Views_Counter_Crawler_Detect();
191 self::$instance->frontend = new Post_Views_Counter_Frontend();
192 self::$instance->dashboard = new Post_Views_Counter_Dashboard();
193
194 new Post_Views_Counter_Widgets();
195 }
196 }
197
198 return self::$instance;
199 }
200
201 /**
202 * Setup plugin constants.
203 *
204 * @return void
205 */
206 private function define_constants() {
207 // fix plugin_basename empty $wp_plugin_paths var
208 if ( ! ( defined( 'SHORTINIT' ) && SHORTINIT ) ) {
209 define( 'POST_VIEWS_COUNTER_URL', plugins_url( '', __FILE__ ) );
210 define( 'POST_VIEWS_COUNTER_BASENAME', plugin_basename( __FILE__ ) );
211 define( 'POST_VIEWS_COUNTER_REL_PATH', dirname( POST_VIEWS_COUNTER_BASENAME ) );
212 }
213
214 define( 'POST_VIEWS_COUNTER_PATH', plugin_dir_path( __FILE__ ) );
215 }
216
217 /**
218 * Include required files.
219 *
220 * @return void
221 */
222 private function includes() {
223 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-functions.php' );
224 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-update.php' );
225 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-api.php' );
226 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings.php' );
227 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-general.php' );
228 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-display.php' );
229 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-reports.php' );
230 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-integrations.php' );
231 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-integrations.php' );
232 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-other.php' );
233 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-import.php' );
234 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-admin.php' );
235 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-columns.php' );
236 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-toolbar.php' );
237 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-query.php' );
238 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-cron.php' );
239 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-counter.php' );
240 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-crawler-detect.php' );
241 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-frontend.php' );
242 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-dashboard.php' );
243 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-widgets.php' );
244 include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-integration-gutenberg.php' );
245 }
246
247 /**
248 * Class constructor.
249 *
250 * @return void
251 */
252 private function __construct() {
253 // define plugin constants
254 $this->define_constants();
255
256 // load translations early to avoid just-in-time loading warnings in WP 6.7+
257 if ( ! ( defined( 'SHORTINIT' ) && SHORTINIT ) )
258 $this->load_textdomain();
259
260 // short init?
261 if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
262 $this->options = [
263 'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
264 'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) ),
265 'other' => array_merge( $this->defaults['other'], get_option( 'post_views_counter_settings_other', $this->defaults['other'] ) )
266 ];
267
268 return;
269 }
270
271 // activation hooks
272 register_activation_hook( __FILE__, [ $this, 'activation' ] );
273 register_deactivation_hook( __FILE__, [ $this, 'deactivation' ] );
274
275 // settings
276 $this->options = [
277 'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
278 'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) ),
279 'other' => array_merge( $this->defaults['other'], get_option( 'post_views_counter_settings_other', $this->defaults['other'] ) ),
280 'integrations' => array_merge( $this->defaults['integrations'], get_option( 'post_views_counter_settings_integrations', $this->defaults['integrations'] ) )
281 ];
282
283 // 1.5.3+
284 if ( ! isset( $this->options['general']['post_views_column'] ) )
285 $this->options['general']['post_views_column'] = $this->options['display']['post_views_column'];
286
287 if ( ! isset( $this->options['general']['restrict_edit_views'] ) )
288 $this->options['general']['restrict_edit_views'] = $this->options['display']['restrict_edit_views'];
289
290 // actions
291 add_action( 'plugins_loaded', [ $this, 'extend_caching_plugins' ], -1 );
292 add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
293 add_action( 'admin_print_styles', [ $this, 'admin_print_styles' ] );
294 add_action( 'wp_loaded', [ $this, 'load_pluggable_functions' ] );
295 add_action( 'init', [ $this, 'register_blocks' ] );
296 add_action( 'admin_init', [ $this, 'update_notice' ] );
297 add_action( 'wp_initialize_site', [ $this, 'init_new_network_site' ] );
298 add_action( 'wp_ajax_pvc_dismiss_notice', [ $this, 'dismiss_notice' ] );
299
300 // filters
301 add_filter( 'plugin_action_links_' . POST_VIEWS_COUNTER_BASENAME, [ $this, 'plugin_settings_link' ] );
302 }
303
304 /**
305 * Extend list of caching plugins.
306 *
307 * @return void
308 */
309 public function extend_caching_plugins() {
310 // add new caching plugins
311 add_filter( 'pvc_active_caching_plugins', [ $this->settings, 'extend_active_caching_plugins' ] );
312 add_filter( 'pvc_is_plugin_active', [ $this->settings, 'extend_is_plugin_active' ], 10, 2 );
313 }
314
315 /**
316 * Register blocks.
317 *
318 * @global object $wp_version
319 *
320 * @return void
321 */
322 public function register_blocks() {
323 global $wp_version;
324
325 // actions
326 add_action( 'enqueue_block_editor_assets', [ $this, 'block_editor_enqueue_scripts' ] );
327
328 // filters
329 if ( version_compare( $wp_version, '5.8', '>=' ) )
330 add_filter( 'block_categories_all', [ $this, 'add_block_category' ] );
331 else
332 add_filter( 'block_categories', [ $this, 'add_block_category' ] );
333
334 add_filter( 'register_block_type_args', [ $this, 'update_block_args' ], 10, 2 );
335
336 register_block_type( __DIR__ . '/blocks/most-viewed-posts/build' );
337 register_block_type( __DIR__ . '/blocks/post-views/build' );
338 }
339
340 /**
341 * Enqueue block scripts.
342 *
343 * @global object $wp_version
344 *
345 * @return void
346 */
347 public function block_editor_enqueue_scripts() {
348 // enqueue script
349 wp_enqueue_script( 'post-views-counter-block-editor-script', POST_VIEWS_COUNTER_URL . '/js/dummy.js', [] );
350
351 $block_image_sizes = [];
352
353 // image sizes
354 $image_sizes = array_merge( [ 'full' ], get_intermediate_image_sizes() );
355
356 // sort image sizes by name, ascending
357 sort( $image_sizes, SORT_STRING );
358
359 foreach ( $image_sizes as $image_size ) {
360 $block_image_sizes[] = [
361 'label' => $image_size,
362 'value' => $image_size
363 ];
364 }
365
366 $post_types = Post_Views_Counter()->functions->get_post_types();
367
368 // prepare script data
369 $script_data = [
370 'postTypesKeys' => array_combine( array_keys( $post_types ), array_fill( 0, count( $post_types ), false ) ),
371 'postTypes' => $post_types,
372 'imageSizes' => $block_image_sizes
373 ];
374
375 // force post as enabled
376 $script_data['postTypesKeys']['post'] = true;
377
378 // prepare script data
379 $script_data['periods'] = [ [
380 'label' => __( 'Total Views', 'post-views-counter' ),
381 'value' => 'total'
382 ] ];
383
384 $script_data = apply_filters( 'pvc_block_editor_data', $script_data );
385
386 wp_add_inline_script( 'post-views-counter-block-editor-script', 'var pvcBlockEditorData = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
387 }
388
389 /**
390 * Update block arguments.
391 *
392 * @param array $args
393 * @param string $block_type
394 *
395 * @return array
396 */
397 function update_block_args( $args, $block_type ) {
398 // most viewed posts block
399 if ( $block_type === 'post-views-counter/most-viewed-posts' )
400 $args['render_callback'] = [ $this, 'most_viewed_posts_render_callback' ];
401 // post views block
402 elseif ( $block_type === 'post-views-counter/post-views' )
403 $args['render_callback'] = [ $this, 'post_views_render_callback' ];
404
405 return $args;
406 }
407
408 /**
409 * Server side block renderer for most viewed posts.
410 *
411 * @param array $attributes
412 * @param string $content
413 *
414 * @return array
415 */
416 function most_viewed_posts_render_callback( $attributes, $content ) {
417 $post_types = [];
418
419 foreach ( $attributes['postTypes'] as $post_type => $enabled ) {
420 if ( $enabled === true || $enabled === 'true' )
421 $post_types[] = $post_type;
422 }
423
424 // map block attributes
425 $args = [
426 'number_of_posts' => max( 1, (int) $attributes['numberOfPosts'] ),
427 'post_type' => $post_types,
428 'period' => $attributes['period'],
429 'order' => $attributes['order'],
430 'thumbnail_size' => $attributes['thumbnailSize'],
431 'list_type' => $attributes['listType'],
432 'show_post_views' => (bool) $attributes['displayPostViews'],
433 'show_post_thumbnail' => (bool) $attributes['displayPostThumbnail'],
434 'show_post_author' => (bool) $attributes['displayPostAuthor'],
435 'show_post_excerpt' => (bool) $attributes['displayPostExcerpt'],
436 'no_posts_message' => $attributes['noPostsMessage']
437 ];
438
439 $title = trim( $attributes['title'] );
440
441 $html = '<div ' . get_block_wrapper_attributes() . '>';
442
443 if ( $title !== '' )
444 $html .= '<h2 class="block-title">' . esc_html( $title ) . '</h2>';
445
446 $html .= pvc_most_viewed_posts( $args, false );
447 $html .= '</div>';
448
449 return $html;
450 }
451
452 /**
453 * Server side block renderer for post views.
454 *
455 * @param array $attributes
456 * @param string $content
457 *
458 * @return array
459 */
460 function post_views_render_callback( $attributes, $content ) {
461 $html = '<div ' . get_block_wrapper_attributes() . '>';
462 $html .= pvc_post_views( (int) $attributes['postID'], false, $attributes['period'] );
463 $html .= '</div>';
464
465 return $html;
466 }
467
468 /**
469 * Add new blocks category.
470 *
471 * @param array $categories
472 *
473 * @return array
474 */
475 function add_block_category( $categories ) {
476 $categories[] = [
477 'slug' => 'post-views-counter',
478 'title' => 'Post Views Counter'
479 ];
480
481 return $categories;
482 }
483
484 /**
485 * Update notice.
486 *
487 * @return void
488 */
489 public function update_notice() {
490 if ( ! current_user_can( 'install_plugins' ) )
491 return;
492
493 $current_update = 2;
494
495 // get current time
496 $current_time = time();
497
498 if ( $this->options['general']['update_version'] < $current_update ) {
499 // check version, if update version is lower than plugin version, set update notice to true
500 $this->options['general'] = array_merge(
501 $this->options['general'],
502 [
503 'update_version' => $current_update,
504 'update_notice' => true
505 ]
506 );
507
508 update_option( 'post_views_counter_settings_general', $this->options['general'] );
509
510 // set activation date
511 $activation_date = get_option( 'post_views_counter_activation_date' );
512
513 if ( $activation_date === false )
514 update_option( 'post_views_counter_activation_date', $current_time );
515 }
516
517 // display current version notice
518 if ( $this->options['general']['update_notice'] === true ) {
519 // include notice js, only if needed
520 add_action( 'admin_print_scripts', [ $this, 'admin_inline_js' ], 999 );
521
522 // get activation date
523 $activation_date = get_option( 'post_views_counter_activation_date' );
524
525 if ( (int) $this->options['general']['update_delay_date'] === 0 ) {
526 if ( $activation_date + 2 * WEEK_IN_SECONDS > $current_time )
527 $this->options['general']['update_delay_date'] = $activation_date + 2 * WEEK_IN_SECONDS;
528 else
529 $this->options['general']['update_delay_date'] = $current_time;
530
531 update_option( 'post_views_counter_settings_general', $this->options['general'] );
532 }
533
534 if ( ( ! empty( $this->options['general']['update_delay_date'] ) ? (int) $this->options['general']['update_delay_date'] : $current_time ) <= $current_time )
535 $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' );
536 }
537 }
538
539 /**
540 * Add admin notices.
541 *
542 * @param string $html Notice HTML
543 * @param string $status Notice status
544 * @param bool $paragraph Whether to use paragraph
545 * @param bool $network
546 *
547 * @return void
548 */
549 public function add_notice( $html = '', $status = 'error', $paragraph = true, $network = false ) {
550 $this->notices[] = [
551 'html' => $html,
552 'status' => $status,
553 'paragraph' => $paragraph
554 ];
555
556 add_action( 'admin_notices', [ $this, 'display_notice' ] );
557
558 if ( $network )
559 add_action( 'network_admin_notices', [ $this, 'display_notice' ] );
560 }
561
562 /**
563 * Print admin notices.
564 *
565 * @return void
566 */
567 public function display_notice() {
568 $allowed_html = array_merge(
569 wp_kses_allowed_html( 'post' ),
570 [
571 'input' => [
572 'type' => true,
573 'name' => true,
574 'class' => true,
575 'value' => true
576 ],
577 'form' => [
578 'action' => true,
579 'method' => true
580 ]
581 ]
582 );
583
584 foreach ( $this->notices as $notice ) {
585 echo '
586 <div class="' . esc_attr( $notice['status'] ) . '">
587 ' . ( $notice['paragraph'] ? '<p>' : '' ) . '
588 ' . wp_kses( $notice['html'], $allowed_html ) . '
589 ' . ( $notice['paragraph'] ? '</p>' : '' ) . '
590 </div>';
591 }
592 }
593
594 /**
595 * Print admin scripts.
596 *
597 * @return void
598 */
599 public function admin_inline_js() {
600 if ( ! current_user_can( 'install_plugins' ) )
601 return;
602
603 // register and enqueue styles
604 wp_register_script( 'pvc-notices', false );
605 wp_enqueue_script( 'pvc-notices' );
606
607 // add styles
608 wp_add_inline_script( 'pvc-notices', "
609 ( function( $ ) {
610 // ready event
611 $( function() {
612 // save dismiss state
613 $( '.pvc-notice.is-dismissible' ).on( 'click', '.notice-dismiss, .pvc-dismissible-notice', function( e ) {
614 if ( e.currentTarget.target !== '_blank' )
615 e.preventDefault();
616
617 var notice_action = 'hide';
618
619 if ( e.currentTarget.classList.contains( 'pvc-delay-notice' ) )
620 notice_action = 'delay';
621
622 $.post( ajaxurl, {
623 action: 'pvc_dismiss_notice',
624 notice_action: notice_action,
625 url: '" . esc_url_raw( admin_url( 'admin-ajax.php' ) ) . "',
626 nonce: '" . esc_attr( wp_create_nonce( 'pvc_dismiss_notice' ) ) . "'
627 } );
628
629 $( e.delegateTarget ).slideUp( 'fast' );
630 } );
631 } );
632 } )( jQuery );
633 ", 'after' );
634 }
635
636 /**
637 * Dismiss notice.
638 *
639 * @return void
640 */
641 public function dismiss_notice() {
642 if ( ! current_user_can( 'install_plugins' ) )
643 return;
644
645 if ( isset( $_REQUEST['nonce'] ) && wp_verify_nonce( $_REQUEST['nonce'], 'pvc_dismiss_notice' ) ) {
646 $notice_action = empty( $_REQUEST['notice_action'] ) || $_REQUEST['notice_action'] === 'hide' ? 'hide' : sanitize_text_field( $_REQUEST['notice_action'] );
647
648 switch ( $notice_action ) {
649 // delay notice
650 case 'delay':
651 // set delay period to 1 week from now
652 $this->options['general'] = array_merge(
653 $this->options['general'],
654 [
655 'update_delay_date' => time() + 2 * WEEK_IN_SECONDS
656 ]
657 );
658 update_option( 'post_views_counter_settings_general', $this->options['general'] );
659 break;
660
661 // hide notice
662 default:
663 $this->options['general'] = array_merge(
664 $this->options['general'],
665 [
666 'update_notice' => false
667 ]
668 );
669 $this->options['general'] = array_merge(
670 $this->options['general'],
671 [
672 'update_delay_date' => 0
673 ]
674 );
675
676 update_option( 'post_views_counter_settings_general', $this->options['general'] );
677 }
678 }
679
680 exit;
681 }
682
683 /**
684 * Plugin activation.
685 *
686 * @global object $wpdb
687 *
688 * @param bool $network
689 *
690 * @return void
691 */
692 public function activation( $network ) {
693 // network activation?
694 if ( is_multisite() && $network ) {
695 global $wpdb;
696
697 // get all available sites
698 $blogs_ids = $wpdb->get_col( 'SELECT blog_id FROM ' . $wpdb->blogs );
699
700 foreach ( $blogs_ids as $blog_id ) {
701 // change to another site
702 switch_to_blog( (int) $blog_id );
703
704 // run current site activation process
705 $this->activate_site();
706
707 restore_current_blog();
708 }
709 } else
710 $this->activate_site();
711 }
712
713 /**
714 * Single site activation.
715 *
716 * @global object $wpdb
717 * @global string $charset_collate
718 *
719 * @return void
720 */
721 public function activate_site() {
722 global $wpdb, $charset_collate;
723
724 // required for dbdelta
725 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
726
727 // create post views table
728 dbDelta( '
729 CREATE TABLE IF NOT EXISTS ' . $wpdb->prefix . 'post_views (
730 `id` bigint unsigned NOT NULL,
731 `type` tinyint(1) unsigned NOT NULL,
732 `period` varchar(8) NOT NULL,
733 `count` bigint unsigned NOT NULL,
734 PRIMARY KEY (type, period, id),
735 UNIQUE INDEX id_type_period_count (id, type, period, count) USING BTREE,
736 INDEX type_period_count (type, period, count) USING BTREE
737 ) ' . $charset_collate . ';'
738 );
739
740 // add default options
741 add_option( 'post_views_counter_settings_general', $this->defaults['general'], null, false );
742 add_option( 'post_views_counter_settings_display', $this->defaults['display'], null, false );
743 add_option( 'post_views_counter_settings_other', $this->defaults['other'], null, false );
744 add_option( 'post_views_counter_version', $this->defaults['version'], null, false );
745 }
746
747 /**
748 * Plugin deactivation.
749 *
750 * @global object $wpdb
751 *
752 * @param bool $network
753 *
754 * @return void
755 */
756 public function deactivation( $network ) {
757 // network deactivation?
758 if ( is_multisite() && $network ) {
759 global $wpdb;
760
761 // get all available sites
762 $blogs_ids = $wpdb->get_col( 'SELECT blog_id FROM ' . $wpdb->blogs );
763
764 foreach ( $blogs_ids as $blog_id ) {
765 // change to another site
766 switch_to_blog( (int) $blog_id );
767
768 // run current site deactivation process
769 $this->deactivate_site( true );
770
771 restore_current_blog();
772 }
773 } else
774 $this->deactivate_site();
775 }
776
777 /**
778 * Single site deactivation.
779 *
780 * @global object $wpdb
781 *
782 * @param bool $multi
783 *
784 * @return void
785 */
786 public function deactivate_site( $multi = false ) {
787 if ( $multi === true ) {
788 $options = get_option( 'post_views_counter_settings_other' );
789 $check = $options['deactivation_delete'];
790 } else
791 $check = $this->options['other']['deactivation_delete'];
792
793 // delete options if needed
794 if ( $check ) {
795 // delete options
796 delete_option( 'post_views_counter_settings_general' );
797 delete_option( 'post_views_counter_settings_display' );
798 delete_option( 'post_views_counter_settings_other' );
799 delete_option( 'post_views_counter_activation_date' );
800 delete_option( 'post_views_counter_version' );
801
802 // delete transients
803 delete_transient( 'post_views_counter_ip_cache' );
804
805 global $wpdb;
806
807 // delete table from database
808 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'post_views' );
809 }
810
811 // remove schedule
812 wp_clear_scheduled_hook( 'pvc_reset_counts' );
813
814 remove_action( 'pvc_reset_counts', [ $this->cron, 'reset_counts' ] );
815 }
816
817 /**
818 * Initialize new network site.
819 *
820 * @param object $site
821 *
822 * @return void
823 */
824 public function init_new_network_site( $site ) {
825 if ( is_multisite() ) {
826 // change to another site
827 switch_to_blog( $site->blog_id );
828
829 // run current site activation process
830 $this->activate_site();
831
832 restore_current_blog();
833 }
834 }
835
836 /**
837 * Load text domain.
838 *
839 * @return void
840 */
841 public function load_textdomain() {
842 load_plugin_textdomain( 'post-views-counter', false, POST_VIEWS_COUNTER_REL_PATH . '/languages/' );
843 }
844
845 /**
846 * Load pluggable template functions.
847 *
848 * @return void
849 */
850 public function load_pluggable_functions() {
851 include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
852 }
853
854 /**
855 * Enqueue admin scripts and styles.
856 *
857 * @global string $post_type
858 * @global string $wp_version
859 *
860 * @param string $page
861 *
862 * @return void
863 */
864 public function admin_enqueue_scripts( $page ) {
865 global $post_type;
866 global $wp_version;
867
868 // register styles
869 wp_register_style( 'pvc-admin', POST_VIEWS_COUNTER_URL . '/css/admin.css', [], $this->defaults['version'] );
870
871 // register scripts
872 wp_register_script( 'pvc-admin-settings', POST_VIEWS_COUNTER_URL . '/js/admin-settings.js', [ 'jquery' ], $this->defaults['version'] );
873 wp_register_script( 'pvc-admin-post', POST_VIEWS_COUNTER_URL . '/js/admin-post.js', [ 'jquery' ], $this->defaults['version'] );
874 wp_register_script( 'pvc-admin-quick-edit', POST_VIEWS_COUNTER_URL . '/js/admin-quick-edit.js', [ 'jquery' ], $this->defaults['version'] );
875
876 // load on pvc settings page
877 if ( in_array( $page, [ 'toplevel_page_post-views-counter', 'settings_page_post-views-counter' ], true ) ) {
878 wp_enqueue_script( 'pvc-admin-settings' );
879
880 // prepare script data
881 $script_data = [
882 'resetToDefaults' => esc_html__( 'Are you sure you want to reset these settings to defaults?', 'post-views-counter' ),
883 'resetViews' => esc_html__( 'Are you sure you want to delete all existing data?', 'post-views-counter' ),
884 'importViews' => esc_html__( 'Are you sure you want to import views now?', 'post-views-counter' )
885 ];
886
887 wp_add_inline_script( 'pvc-admin-settings', 'var pvcArgsSettings = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
888
889 wp_enqueue_style( 'pvc-admin' );
890 // load on single post page
891 } elseif ( $page === 'post.php' || $page === 'post-new.php' ) {
892 $post_types = Post_Views_Counter()->options['general']['post_types_count'];
893
894 if ( ! in_array( $post_type, (array) $post_types ) )
895 return;
896
897 wp_enqueue_style( 'pvc-admin' );
898
899 // only enqueue post script in classic editor (not block editor)
900 // block editor doesn't have the #post-views metabox that this script targets
901 if ( ! function_exists( 'use_block_editor_for_post' ) ) {
902 // WP < 5.0, always use classic editor
903 wp_enqueue_script( 'pvc-admin-post' );
904 } else {
905 // get post ID from request
906 $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : ( isset( $_POST['post_ID'] ) ? absint( $_POST['post_ID'] ) : 0 );
907
908 // check if classic editor is being used
909 if ( $post_id && ! use_block_editor_for_post( $post_id ) ) {
910 wp_enqueue_script( 'pvc-admin-post' );
911 } elseif ( ! $post_id && $page === 'post-new.php' && ! use_block_editor_for_post_type( $post_type ) ) {
912 // new post - check if block editor is default for this post type
913 wp_enqueue_script( 'pvc-admin-post' );
914 }
915 }
916 // edit post
917 } elseif ( $page === 'edit.php' ) {
918 $post_types = Post_Views_Counter()->options['general']['post_types_count'];
919
920 if ( ! in_array( $post_type, (array) $post_types ) )
921 return;
922
923 wp_enqueue_style( 'pvc-admin' );
924
925 // woocommerce
926 if ( get_post_type() !== 'product' ) {
927 wp_enqueue_script( 'pvc-admin-quick-edit' );
928
929 // prepare script data
930 $script_data = [
931 'nonce' => wp_create_nonce( 'pvc_save_bulk_post_views' ),
932 'wpVersion59' => version_compare( $wp_version, '5.9', '>=' )
933 ];
934
935 wp_add_inline_script( 'pvc-admin-quick-edit', 'var pvcArgsQuickEdit = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
936 }
937 // widgets
938 } elseif ( $page === 'widgets.php' )
939 wp_enqueue_script( 'pvc-admin-widgets', POST_VIEWS_COUNTER_URL . '/js/admin-widgets.js', [ 'jquery' ], $this->defaults['version'] );
940 // media
941 elseif ( $page === 'upload.php' )
942 wp_enqueue_style( 'pvc-admin' );
943
944 // register and enqueue styles
945 wp_register_style( 'pvc-pro-style', false );
946 wp_enqueue_style( 'pvc-pro-style' );
947
948 // add styles
949 wp_add_inline_style( 'pvc-pro-style', '
950 .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,
951 .post-views-counter-settings tr.pvc-pro-extended label[for="pvc_exclude-ai_bots"]:after {
952 content: \'PRO\';
953 display: inline;
954 background-color: #ffc107;
955 color: white;
956 padding: 2px 4px;
957 text-align: center;
958 border-radius: 4px;
959 margin-left: 4px;
960 font-weight: bold;
961 font-size: 11px;
962 }' );
963 }
964
965 /**
966 * Load admin style inline, for menu badge only.
967 *
968 * @return void
969 */
970 public function admin_print_styles() {
971 echo '
972 <style>
973 .toplevel_page_post-views-counter .pvc-admin-menu-new,
974 .settings_page_post-views-counter .pvc-admin-menu-new {
975 font-size: 10px;
976 vertical-align: super;
977 color: #ffc107;
978 }
979 </style>
980 ';
981 }
982
983 /**
984 * Get the preferred admin menu position.
985 *
986 * @return string
987 */
988 public function get_menu_position() {
989 $position = isset( $this->options['display']['menu_position'] ) ? $this->options['display']['menu_position'] : '';
990
991 if ( in_array( $position, [ 'top', 'sub' ], true ) )
992 return $position;
993
994 if ( isset( $this->options['other']['menu_position'] ) && in_array( $this->options['other']['menu_position'], [ 'top', 'sub' ], true ) )
995 return $this->options['other']['menu_position'];
996
997 return 'top';
998 }
999
1000 /**
1001 * Add link to Settings page.
1002 *
1003 * @param array $links
1004 *
1005 * @return array
1006 */
1007 public function plugin_settings_link( $links ) {
1008 if ( ! current_user_can( 'manage_options' ) )
1009 return $links;
1010
1011 // submenu?
1012 if ( $this->get_menu_position() === 'sub' )
1013 $url = admin_url( 'options-general.php?page=post-views-counter' );
1014 // topmenu?
1015 else
1016 $url = admin_url( 'admin.php?page=post-views-counter' );
1017
1018 array_unshift( $links, sprintf( '<a href="%s">%s</a>', esc_url_raw( $url ), esc_html__( 'Settings', 'post-views-counter' ) ) );
1019
1020 return $links;
1021 }
1022 }
1023 }
1024
1025 /**
1026 * Initialize Post Views Counter.
1027 *
1028 * @return object
1029 */
1030 function Post_Views_Counter() {
1031 static $instance;
1032
1033 // first call to instance() initializes the plugin
1034 if ( $instance === null || ! ( $instance instanceof Post_Views_Counter ) )
1035 $instance = Post_Views_Counter::instance();
1036
1037 return $instance;
1038 }
1039
1040 Post_Views_Counter();
1041