PluginProbe ʕ •ᴥ•ʔ
Post Views Counter / 1.7.2
Post Views Counter v1.7.2
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 / includes / class-settings-general.php
post-views-counter / includes Last commit date
class-admin.php 5 months ago class-columns.php 5 months ago class-counter.php 5 months ago class-crawler-detect.php 5 months ago class-cron.php 5 months ago class-dashboard.php 5 months ago class-frontend.php 5 months ago class-functions.php 5 months ago class-import.php 5 months ago class-integration-gutenberg.php 5 months ago class-integrations.php 5 months ago class-query.php 5 months ago class-settings-api.php 5 months ago class-settings-display.php 5 months ago class-settings-general.php 5 months ago class-settings-integrations.php 5 months ago class-settings-other.php 5 months ago class-settings-reports.php 5 months ago class-settings.php 5 months ago class-toolbar.php 5 months ago class-update.php 5 months ago class-widgets.php 5 months ago functions.php 5 months ago
class-settings-general.php
849 lines
1 <?php
2 // exit if accessed directly
3 if ( ! defined( 'ABSPATH' ) )
4 exit;
5
6 /**
7 * Post_Views_Counter_Settings_General class.
8 *
9 * @class Post_Views_Counter_Settings_General
10 */
11 class Post_Views_Counter_Settings_General {
12
13 private $pvc;
14
15 /**
16 * Class constructor.
17 *
18 * @return void
19 */
20 public function __construct() {
21 $this->pvc = Post_Views_Counter();
22
23 // actions
24 add_action( 'admin_init', [ $this, 'update_counter_mode' ], 12 );
25 }
26
27 /**
28 * Update counter mode.
29 *
30 * @return void
31 */
32 public function update_counter_mode() {
33 // get settings
34 $settings = $this->pvc->settings_api->get_settings();
35
36 // fast ajax as active but not available counter mode?
37 if ( $this->pvc->options['general']['counter_mode'] === 'ajax' && in_array( 'ajax', $settings['post-views-counter']['fields']['counter_mode']['disabled'], true ) ) {
38 // set standard javascript ajax calls
39 $this->pvc->options['general']['counter_mode'] = 'js';
40
41 // update database options
42 update_option( 'post_views_counter_settings_general', $this->pvc->options['general'] );
43 }
44 }
45
46 /**
47 * Get available counter modes.
48 *
49 * @return array
50 */
51 public function get_counter_modes() {
52 // counter modes
53 $modes = [
54 'php' => __( 'PHP', 'post-views-counter' ),
55 'js' => __( 'JavaScript', 'post-views-counter' ),
56 'rest_api' => __( 'REST API', 'post-views-counter' ),
57 'ajax' => __( 'Fast AJAX', 'post-views-counter' )
58 ];
59
60 return apply_filters( 'pvc_get_counter_modes', $modes );
61 }
62
63 /**
64 * Get sections for general tab.
65 *
66 * @return array
67 */
68 public function get_sections() {
69 return [
70 'post_views_counter_general_tracking_targets' => [
71 'tab' => 'general',
72 'title' => __( 'Tracking Targets', 'post-views-counter' ),
73 'callback' => [ $this, 'section_tracking_targets' ],
74 ],
75 'post_views_counter_general_tracking_behavior' => [
76 'tab' => 'general',
77 'title' => __( 'Tracking Behavior', 'post-views-counter' ),
78 'callback' => [ $this, 'section_tracking_behavior' ],
79 ],
80 'post_views_counter_general_exclusions' => [
81 'tab' => 'general',
82 'title' => __( 'Visitor Exclusions', 'post-views-counter' ),
83 'callback' => [ $this, 'section_tracking_exclusions' ],
84 ],
85 'post_views_counter_general_performance' => [
86 'tab' => 'general',
87 'title' => __( 'Performance & Caching', 'post-views-counter' ),
88 'callback' => [ $this, 'section_tracking_performance' ],
89 ]
90 ];
91 }
92
93 /**
94 * Get fields for general tab.
95 *
96 * @return array
97 */
98 public function get_fields() {
99 // time types
100 $time_types = [
101 'minutes' => __( 'minutes', 'post-views-counter' ),
102 'hours' => __( 'hours', 'post-views-counter' ),
103 'days' => __( 'days', 'post-views-counter' ),
104 'weeks' => __( 'weeks', 'post-views-counter' ),
105 'months' => __( 'months', 'post-views-counter' ),
106 'years' => __( 'years', 'post-views-counter' )
107 ];
108
109 // user groups
110 $groups = [
111 'robots' => __( 'crawlers', 'post-views-counter' ),
112 'ai_bots' => __( 'AI bots', 'post-views-counter' ),
113 'users' => __( 'logged in users', 'post-views-counter' ),
114 'guests' => __( 'guests', 'post-views-counter' ),
115 'roles' => __( 'selected user roles', 'post-views-counter' )
116 ];
117
118 // get user roles
119 $user_roles = $this->pvc->functions->get_user_roles();
120
121 // get post types
122 $post_types = $this->pvc->functions->get_post_types();
123
124 return [
125 'post_types_count' => [
126 'tab' => 'general',
127 'title' => __( 'Post Types', 'post-views-counter' ),
128 'section' => 'post_views_counter_general_tracking_targets',
129 'type' => 'checkbox',
130 'description' => __( 'Select post types whose views should be counted.', 'post-views-counter' ),
131 'options' => $post_types
132 ],
133 'taxonomies_count' => [
134 'tab' => 'general',
135 'title' => __( 'Taxonomies', 'post-views-counter' ),
136 'section' => 'post_views_counter_general_tracking_targets',
137 'type' => 'boolean',
138 'label' => __( 'Enable counting views on taxonomy term archive pages.', 'post-views-counter' ),
139 'class' => 'pvc-pro',
140 'disabled' => true,
141 'skip_saving' => true,
142 'value' => false,
143 'pro_only' => true
144 ],
145 'users_count' => [
146 'tab' => 'general',
147 'title' => __( 'Author Archives', 'post-views-counter' ),
148 'section' => 'post_views_counter_general_tracking_targets',
149 'type' => 'boolean',
150 'label' => __( 'Enable counting views on author archive pages.', 'post-views-counter' ),
151 'class' => 'pvc-pro',
152 'disabled' => true,
153 'skip_saving' => true,
154 'value' => false,
155 'pro_only' => true
156 ],
157 'other_count' => [
158 'tab' => 'general',
159 'title' => __( 'Other Pages', 'post-views-counter' ),
160 'section' => 'post_views_counter_general_tracking_targets',
161 'type' => 'boolean',
162 'label' => __( 'Track views on the front page, post type archives, date archives, search results, and 404 pages.', 'post-views-counter' ),
163 'class' => 'pvc-pro',
164 'disabled' => true,
165 'skip_saving' => true,
166 'value' => false,
167 'pro_only' => true
168 ],
169 'technology_count' => [
170 'tab' => 'general',
171 'title' => __( 'Traffic Sources', 'post-views-counter' ),
172 'section' => 'post_views_counter_general_tracking_targets',
173 'type' => 'boolean',
174 'label' => __( 'Collect aggregate stats about visitors\' browsers, devices, operating systems and referrers.', 'post-views-counter' ),
175 'class' => 'pvc-pro',
176 'disabled' => true,
177 'skip_saving' => true,
178 'value' => false,
179 'pro_only' => true
180 ],
181 'counter_mode' => [
182 'tab' => 'general',
183 'title' => __( 'Counter Mode', 'post-views-counter' ),
184 'section' => 'post_views_counter_general_tracking_behavior',
185 'type' => 'radio',
186 'description' => __( 'Choose how views are recorded. If you use caching, select JavaScript, REST API or Fast AJAX (up to <code>10+</code> times faster).', 'post-views-counter' ),
187 'class' => 'pvc-pro-extended',
188 'options' => $this->get_counter_modes(),
189 'disabled' => [ 'ajax' ],
190 'pro_only' => [ 'ajax' ]
191 ],
192 'data_storage' => [
193 'tab' => 'general',
194 'title' => __( 'Data Storage', 'post-views-counter' ),
195 'section' => 'post_views_counter_general_tracking_behavior',
196 'type' => 'radio',
197 'class' => 'pvc-pro',
198 'skip_saving' => true,
199 'description' => __( "Choose how to store the content views data in the user's browser - with or without cookies.", 'post-views-counter' ),
200 'pro_only' => true,
201 'options' => [
202 'cookies' => __( 'Cookies', 'post-views-counter' ),
203 'cookieless' => __( 'Cookieless', 'post-views-counter' )
204 ],
205 'disabled' => [ 'cookies', 'cookieless' ],
206 'value' => 'cookies'
207 ],
208 'time_between_counts' => [
209 'tab' => 'general',
210 'title' => __( 'Count Interval', 'post-views-counter' ),
211 'section' => 'post_views_counter_general_tracking_behavior',
212 'type' => 'custom',
213 'description' => '',
214 'min' => 0,
215 'max' => 999999,
216 'options' => $time_types,
217 'callback' => [ $this, 'setting_time_between_counts' ],
218 'validate' => [ $this, 'validate_time_between_counts' ]
219 ],
220 'count_time' => [
221 'tab' => 'general',
222 'title' => __( 'Count Time', 'post-views-counter' ),
223 'section' => 'post_views_counter_general_tracking_behavior',
224 'type' => 'radio',
225 'class' => 'pvc-pro',
226 'disabled' => true,
227 'skip_saving' => true,
228 'description' => __( 'Whether to store the views using GMT timezone or adjust it to the GMT offset of the site.', 'post-views-counter' ),
229 'options' => [
230 'gmt' => __( 'GMT Time', 'post-views-counter' ),
231 'local' => __( 'Local Time', 'post-views-counter' )
232 ],
233 'pro_only' => true
234 ],
235 'strict_counts' => [
236 'tab' => 'general',
237 'title' => __( 'Strict Counts', 'post-views-counter' ),
238 'section' => 'post_views_counter_general_tracking_behavior',
239 'type' => 'boolean',
240 'class' => 'pvc-pro',
241 'disabled' => true,
242 'skip_saving' => true,
243 'value' => false,
244 'description' => '',
245 'label' => __( 'Prevent bypassing the count interval (for example by using incognito mode or clearing cookies).', 'post-views-counter' ),
246 'pro_only' => true
247 ],
248 'reset_counts' => [
249 'tab' => 'general',
250 'title' => __( 'Cleanup Interval', 'post-views-counter' ),
251 'section' => 'post_views_counter_general_tracking_behavior',
252 'type' => 'custom',
253 'description' => sprintf( __( 'Delete daily content view data older than the period specified above. Enter %s to keep data regardless of age. Cleanup runs once per day.', 'post-views-counter' ), '<code>0</code>' ),
254 'min' => 0,
255 'max' => 999999,
256 'options' => $time_types,
257 'callback' => [ $this, 'setting_reset_counts' ],
258 'validate' => [ $this, 'validate_reset_counts' ]
259 ],
260 'caching_compatibility' => [
261 'tab' => 'general',
262 'title' => __( 'Caching Compatibility', 'post-views-counter' ),
263 'section' => 'post_views_counter_general_performance',
264 'type' => 'boolean',
265 'class' => 'pvc-pro',
266 'disabled' => true,
267 'value' => false,
268 'skip_saving' => true,
269 'animation' => 'slide',
270 'available' => $this->is_caching_compatibility_available(),
271 'label' => $this->get_caching_compatibility_label(),
272 'description' => $this->get_caching_compatibility_description(),
273 'pro_only' => true
274 ],
275 'object_cache' => [
276 'tab' => 'general',
277 'title' => __( 'Object Cache Support', 'post-views-counter' ),
278 'section' => 'post_views_counter_general_performance',
279 'type' => 'boolean',
280 'class' => 'pvc-pro',
281 'disabled' => true,
282 'value' => false,
283 'skip_saving' => true,
284 'animation' => 'slide',
285 'available' => $this->is_object_cache_available(),
286 'label' => $this->get_object_cache_label(),
287 'description' => $this->get_object_cache_description(),
288 'pro_only' => true
289 ],
290 'exclude_groups' => [
291 'tab' => 'general',
292 'title' => __( 'Exclude Visitors', 'post-views-counter' ),
293 'section' => 'post_views_counter_general_exclusions',
294 'type' => 'checkbox',
295 'description' => __( 'Use this to exclude specific visitor groups from counting views.', 'post-views-counter' ),
296 'class' => 'pvc-pro-extended',
297 'options' => $groups,
298 'disabled' => [ 'ai_bots' ],
299 'pro_only' => [ 'ai_bots' ],
300 'name' => 'post_views_counter_settings_general[exclude][groups]',
301 'value' => $this->pvc->options['general']['exclude']['groups'],
302 'validate' => [ $this, 'validate_exclude_groups' ]
303 ],
304 'exclude_roles' => [
305 'tab' => 'general',
306 'title' => __( 'Exclude User Roles', 'post-views-counter' ),
307 'section' => 'post_views_counter_general_exclusions',
308 'type' => 'checkbox',
309 'description' => __( 'Use this to exclude specific user roles from counting views.', 'post-views-counter' ),
310 'options' => $user_roles,
311 'logic' => [
312 [
313 'field' => 'exclude_groups',
314 'operator' => 'contains',
315 'value' => 'roles'
316 ]
317 ],
318 'animation' => 'slide',
319 'name' => 'post_views_counter_settings_general[exclude][roles]',
320 'value' => $this->pvc->options['general']['exclude']['roles'],
321 'validate' => [ $this, 'validate_exclude_roles' ]
322 ],
323 'exclude_ips' => [
324 'tab' => 'general',
325 'title' => __( 'Exclude IPs', 'post-views-counter' ),
326 'section' => 'post_views_counter_general_exclusions',
327 'type' => 'custom',
328 'description' => '',
329 'callback' => [ $this, 'setting_exclude_ips' ],
330 'validate' => [ $this, 'validate_exclude_ips' ]
331 ]
332 ];
333 }
334
335 /**
336 * Setting: taxonomies count.
337 *
338 * @param array $field
339 * @return string
340 */
341 public function setting_taxonomies_count( $field ) {
342 $html = '
343 <label class="pvc-disabled"><input id="post_views_counter_general_taxonomies_count" type="checkbox" name="" value="" disabled role="switch" />' . esc_html( $field['label'] ) . '</label>';
344
345 return $html;
346 }
347
348 /**
349 * Setting: users count.
350 *
351 * @param array $field
352 * @return string
353 */
354 public function setting_users_count( $field ) {
355 $html = '
356 <label class="pvc-disabled"><input id="post_views_counter_general_users_count" type="checkbox" name="" value="" disabled role="switch" />' . esc_html( $field['label'] ) . '</label>';
357
358 return $html;
359 }
360
361 /**
362 * Setting: count interval.
363 *
364 * @param array $field
365 * @return string
366 */
367 public function setting_time_between_counts( $field ) {
368 $html = '
369 <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[time_between_counts][number]" value="' . esc_attr( $this->pvc->options['general']['time_between_counts']['number'] ) . '" />
370 <select name="post_views_counter_settings_general[time_between_counts][type]">';
371
372 foreach ( $field['options'] as $type => $type_name ) {
373 $html .= '
374 <option value="' . esc_attr( $type ) . '" ' . selected( $type, $this->pvc->options['general']['time_between_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
375 }
376
377 $html .= '
378 </select>
379 <p class="description">' . __( 'Minimum time between counting new views from the same visitor. Enter <code>0</code> to count every page view.', 'post-views-counter' ) . '</p>';
380
381 return $html;
382 }
383
384 /**
385 * Validate count interval.
386 *
387 * @param array $input
388 * @param array $field
389 * @return array
390 */
391 public function validate_time_between_counts( $input, $field ) {
392 // number
393 $input['time_between_counts']['number'] = isset( $input['time_between_counts']['number'] ) ? (int) $input['time_between_counts']['number'] : $this->pvc->defaults['general']['time_between_counts']['number'];
394
395 if ( $input['time_between_counts']['number'] < $field['min'] || $input['time_between_counts']['number'] > $field['max'] )
396 $input['time_between_counts']['number'] = $this->pvc->defaults['general']['time_between_counts']['number'];
397
398 // type
399 $input['time_between_counts']['type'] = isset( $input['time_between_counts']['type'], $field['options'][$input['time_between_counts']['type']] ) ? $input['time_between_counts']['type'] : $this->pvc->defaults['general']['time_between_counts']['type'];
400
401 return $input;
402 }
403
404 /**
405 * Setting: reset data interval.
406 *
407 * @param array $field
408 * @return string
409 */
410 public function setting_reset_counts( $field ) {
411 $html = '
412 <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[reset_counts][number]" value="' . esc_attr( $this->pvc->options['general']['reset_counts']['number'] ) . '" />
413 <select name="post_views_counter_settings_general[reset_counts][type]">';
414
415 foreach ( array_slice( $field['options'], 2, null, true ) as $type => $type_name ) {
416 $html .= '
417 <option value="' . esc_attr( $type ) . '" ' . selected( $type, $this->pvc->options['general']['reset_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
418 }
419
420 $html .= '
421 </select>';
422
423 return $html;
424 }
425
426 /**
427 * Validate reset data interval.
428 *
429 * @param array $input
430 * @param array $field
431 * @return array
432 */
433 public function validate_reset_counts( $input, $field ) {
434 // number
435 $input['reset_counts']['number'] = isset( $input['reset_counts']['number'] ) ? (int) $input['reset_counts']['number'] : $this->pvc->defaults['general']['reset_counts']['number'];
436
437 if ( $input['reset_counts']['number'] < $field['min'] || $input['reset_counts']['number'] > $field['max'] )
438 $input['reset_counts']['number'] = $this->pvc->defaults['general']['reset_counts']['number'];
439
440 // type
441 $input['reset_counts']['type'] = isset( $input['reset_counts']['type'], $field['options'][$input['reset_counts']['type']] ) ? $input['reset_counts']['type'] : $this->pvc->defaults['general']['reset_counts']['type'];
442
443 // run cron on next visit?
444 $input['cron_run'] = ( $input['reset_counts']['number'] > 0 );
445
446 // cron update?
447 $input['cron_update'] = ( $input['cron_run'] && ( $this->pvc->options['general']['reset_counts']['number'] !== $input['reset_counts']['number'] || $this->pvc->options['general']['reset_counts']['type'] !== $input['reset_counts']['type'] ) );
448
449 return $input;
450 }
451
452 /**
453 * Setting: object cache.
454 *
455 * @param array $field
456 * @return string
457 */
458 public function setting_object_cache( $field ) {
459 $html = '
460 <input size="4" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="" value="0" disabled /> <span>' . __( 'minutes', 'post-views-counter' ) . '</span>
461 <p class="">' . __( 'Persistent Object Cache', 'post-views-counter' ) . ': <span class="' . ( wp_using_ext_object_cache() ? '' : 'un' ) . 'available">' . ( wp_using_ext_object_cache() ? __( 'available', 'post-views-counter' ) : __( 'unavailable', 'post-views-counter' ) ) . '</span></p>
462 <p class="description">' . sprintf( __( 'How often to flush cached view counts from the object cache into the database. This feature is used only if a persistent object cache like %s or %s is detected and the interval is greater than %s. When used, view counts will be collected and stored in the object cache instead of the database and will then be asynchronously flushed to the database according to the specified interval. The maximum value is %s which means 24 hours.%sNotice:%s Potential data loss may occur if the object cache is cleared/unavailable for the duration of the interval.', 'post-views-counter' ), '<code>Redis</code>', '<code>Memcached</code>', '<code>0</code>', '<code>1440</code>', '<br /><strong> ', '</strong>' ) . '</p>';
463
464 return $html;
465 }
466
467 /**
468 * Setting: exclude visitors.
469 *
470 * @param array $field
471 * @return string
472 */
473
474
475 /**
476 * Validate exclude visitors.
477 *
478 * @param array $input
479 * @param array $field
480 * @return array
481 */
482 public function validate_exclude_groups( $input, $field ) {
483 $groups = [];
484
485 if ( is_array( $input ) ) {
486 foreach ( $input as $group => $set ) {
487 // disallow disabled checkboxes
488 if ( ! empty( $field['disabled'] ) && in_array( $group, $field['disabled'], true ) )
489 continue;
490
491 if ( isset( $field['options'][$group] ) )
492 $groups[] = $group;
493 }
494 }
495
496 return array_unique( $groups );
497 }
498
499 public function validate_exclude_roles( $input, $field ) {
500 $roles = [];
501
502 if ( is_array( $input ) ) {
503 foreach ( $input as $role => $set ) {
504 if ( isset( $field['options'][$role] ) )
505 $roles[] = $role;
506 }
507 }
508
509 return array_unique( $roles );
510 }
511
512 /**
513 * Setting: exclude IP addresses.
514 *
515 * @return string
516 */
517 public function setting_exclude_ips() {
518 // get ip addresses
519 $ips = $this->pvc->options['general']['exclude_ips'];
520
521 $html = '<div class="pvc-ip-box-group">';
522
523 // any ip addresses?
524 if ( ! empty( $ips ) ) {
525 foreach ( $ips as $key => $ip ) {
526 $html .= '
527 <div class="pvc-ip-box">
528 <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="' . esc_attr( $ip ) . '" /> <a href="#" class="pvc-remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
529 </div>';
530 }
531 } else {
532 $html .= '
533 <div class="pvc-ip-box">
534 <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="" /> <a href="#" class="pvc-remove-exclude-ip pvc-hidden" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
535 </div>';
536 }
537
538 $html .= '</div>';
539
540 $html .= '
541 <div class="pvc-field-group pvc-buttons-group">
542 <input type="button" class="button outline pvc-add-exclude-ip" value="' . esc_attr__( 'Add new', 'post-views-counter' ) . '" /> <input type="button" class="button outline pvc-add-current-ip" value="' . esc_attr__( 'Add my current IP', 'post-views-counter' ) . '" data-rel="' . esc_attr( $_SERVER['REMOTE_ADDR'] ) . '" />
543 </div>';
544
545 $html .= '<p class="description">' . esc_html__( 'Add IP addresses or wildcards (e.g. 192.168.0.*) to exclude them from counting views.', 'post-views-counter' ) . '</p>';
546
547 return $html;
548 }
549
550 /**
551 * Validate exclude IP addresses.
552 *
553 * @param array $input
554 * @param array $field
555 * @return array
556 */
557 public function validate_exclude_ips( $input, $field ) {
558 // any ip addresses?
559 if ( isset( $input['exclude_ips'] ) ) {
560 $ips = [];
561
562 foreach ( $input['exclude_ips'] as $ip ) {
563 if ( strpos( $ip, '*' ) !== false ) {
564 $new_ip = str_replace( '*', '0', $ip );
565
566 if ( filter_var( $new_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
567 $ips[] = $ip;
568 } elseif ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
569 $ips[] = $ip;
570 }
571
572 $input['exclude_ips'] = array_unique( $ips );
573 }
574
575 return $input;
576 }
577
578 /**
579 * Section description: tracking targets.
580 *
581 * @return void
582 */
583 public function section_tracking_targets() {
584 echo '<p class="description">' . esc_html__( 'Control which post types, archives and other content types are included in view counting.', 'post-views-counter' ) . '</p>';
585 }
586
587 /**
588 * Section description: tracking behavior.
589 *
590 * @return void
591 */
592 public function section_tracking_behavior() {
593 echo '<p class="description">' . esc_html__( 'Control how views are recorded — counting mode, intervals, time zone, and cleanup.', 'post-views-counter' ) . '</p>';
594 }
595
596 /**
597 * Section description: tracking exclusions.
598 *
599 * @return void
600 */
601 public function section_tracking_exclusions() {
602 echo '<p class="description">' . esc_html__( 'Exclude specific visitor groups or IP addresses from incrementing view counts.', 'post-views-counter' ) . '</p>';
603 }
604
605 /**
606 * Section description: performance & caching.
607 *
608 * @return void
609 */
610 public function section_tracking_performance() {
611 echo '<p class="description">' . esc_html__( 'Configure caching compatibility and object-cache handling for counting.', 'post-views-counter' ) . '</p>';
612 }
613
614 /**
615 * Get caching compatibility description.
616 *
617 * @return string
618 */
619 public function get_caching_compatibility_description() {
620 // get active caching plugins
621 $active_plugins = $this->get_active_caching_plugins();
622
623 if ( ! empty( $active_plugins ) ) {
624 $active_plugins_html = [];
625
626 $description = esc_html__( 'Currently detected active caching plugins', 'post-views-counter' ) . ': ';
627
628 foreach ( $active_plugins as $plugin ) {
629 $active_plugins_html[] = '<code>' . esc_html( $plugin ) . '</code>';
630 }
631
632 $description .= implode( ', ', $active_plugins_html ) . '.';
633 } else {
634 $description = esc_html__( 'No compatible caching plugins found.', 'post-views-counter' );
635 }
636
637 return $description;
638 }
639
640 /**
641 * Get caching compatibility label with availability indicator.
642 *
643 * @return string
644 */
645 public function get_caching_compatibility_label() {
646 $label = __( 'Enable compatibility tweaks for supported caching plugins.', 'post-views-counter' );
647
648 // add availability indicator when Pro is missing but feature is available
649 if ( ! class_exists( 'Post_Views_Counter_Pro' ) && $this->is_caching_compatibility_available() ) {
650 $label = '<span class="pvc-availability-status available">' . esc_html__( '(available)', 'post-views-counter' ) . '</span> ' . $label;
651 }
652
653 return $label;
654 }
655
656 /**
657 * Get object cache label with availability indicator.
658 *
659 * @return string
660 */
661 public function get_object_cache_label() {
662 $label = __( 'Enable Redis or Memcached object cache optimization.', 'post-views-counter' );
663
664 // add availability indicator when Pro is missing but feature is available
665 if ( ! class_exists( 'Post_Views_Counter_Pro' ) && $this->is_object_cache_available() ) {
666 $label = '<span class="pvc-availability-status available">' . esc_html__( '(available)', 'post-views-counter' ) . '</span> ' . $label;
667 }
668
669 return $label;
670 }
671
672 /**
673 * Check if caching compatibility feature is available.
674 *
675 * @return bool
676 */
677 public function is_caching_compatibility_available() {
678 $active_plugins = $this->get_active_caching_plugins();
679
680 return ! empty( $active_plugins );
681 }
682
683 /**
684 * Check if object cache feature is available.
685 *
686 * @return bool
687 */
688 public function is_object_cache_available() {
689 return wp_using_ext_object_cache();
690 }
691
692 /**
693 * Get object cache description based on availability.
694 *
695 * @return string
696 */
697 public function get_object_cache_description() {
698 if ( $this->is_object_cache_available() ) {
699 return __( 'Persistent object cache has been detected.', 'post-views-counter' );
700 }
701
702 return sprintf( __( 'This feature requires a persistent object cache like %s or %s to be installed and activated.', 'post-views-counter' ), '<code>Redis</code>', '<code>Memcached</code>' );
703 }
704
705 /**
706 * Extend active caching plugins.
707 *
708 * @param array $plugins
709 *
710 * @return array
711 */
712 public function extend_active_caching_plugins( $plugins ) {
713 // breeze
714 if ( $this->is_plugin_active( 'breeze' ) )
715 $plugins[] = 'Breeze';
716
717 return $plugins;
718 }
719
720 /**
721 * Check whether specified plugin is active.
722 *
723 * @param bool $is_plugin_active
724 * @param string $plugin
725 *
726 * @return bool
727 */
728 public function extend_is_plugin_active( $is_plugin_active, $plugin ) {
729 // breeze
730 if ( $plugin === 'breeze' && class_exists( 'Breeze_PurgeCache' ) && class_exists( 'Breeze_Options_Reader' ) && function_exists( 'breeze_get_option' ) && function_exists( 'breeze_update_option' ) && defined( 'BREEZE_VERSION' ) && version_compare( BREEZE_VERSION, '2.0.30', '>=' ) )
731 $is_plugin_active = true;
732
733 return $is_plugin_active;
734 }
735
736 /**
737 * Get active caching plugins.
738 *
739 * @return array
740 */
741 public function get_active_caching_plugins() {
742 $active_plugins = [];
743
744 // autoptimize
745 if ( $this->is_plugin_active( 'autoptimize' ) )
746 $active_plugins[] = 'Autoptimize';
747
748 // hummingbird
749 if ( $this->is_plugin_active( 'hummingbird' ) )
750 $active_plugins[] = 'Hummingbird';
751
752 // litespeed
753 if ( $this->is_plugin_active( 'litespeed' ) )
754 $active_plugins[] = 'LiteSpeed Cache';
755
756 // speed optimizer
757 if ( $this->is_plugin_active( 'speedoptimizer' ) )
758 $active_plugins[] = 'Speed Optimizer';
759
760 // speedycache
761 if ( $this->is_plugin_active( 'speedycache' ) )
762 $active_plugins[] = 'SpeedyCache';
763
764 // wp fastest cache
765 if ( $this->is_plugin_active( 'wpfastestcache' ) )
766 $active_plugins[] = 'WP Fastest Cache';
767
768 // wp-optimize
769 if ( $this->is_plugin_active( 'wpoptimize' ) )
770 $active_plugins[] = 'WP-Optimize';
771
772 // wp rocket
773 if ( $this->is_plugin_active( 'wprocket' ) )
774 $active_plugins[] = 'WP Rocket';
775
776 return apply_filters( 'pvc_active_caching_plugins', $active_plugins );
777 }
778
779 /**
780 * Check whether specified plugin is active.
781 *
782 * @param string $plugin
783 *
784 * @return bool
785 */
786 public function is_plugin_active( $plugin = '' ) {
787 // set default flag
788 $is_plugin_active = false;
789
790 switch ( $plugin ) {
791 // autoptimize
792 case 'autoptimize':
793 if ( function_exists( 'autoptimize' ) && defined( 'AUTOPTIMIZE_PLUGIN_VERSION' ) && version_compare( AUTOPTIMIZE_PLUGIN_VERSION, '2.4', '>=' ) )
794 $is_plugin_active = true;
795 break;
796
797 // hummingbird
798 case 'hummingbird':
799 if ( class_exists( 'Hummingbird\\WP_Hummingbird' ) && defined( 'WPHB_VERSION' ) && version_compare( WPHB_VERSION, '2.1.0', '>=' ) )
800 $is_plugin_active = true;
801 break;
802
803 // litespeed
804 case 'litespeed':
805 if ( class_exists( 'LiteSpeed\Core' ) && defined( 'LSCWP_CUR_V' ) && version_compare( LSCWP_CUR_V, '3.0', '>=' ) )
806 $is_plugin_active = true;
807 break;
808
809 // speed optimizer
810 case 'speedoptimizer':
811 global $siteground_optimizer_loader;
812
813 if ( ! empty( $siteground_optimizer_loader ) && is_object( $siteground_optimizer_loader ) && is_a( $siteground_optimizer_loader, 'SiteGround_Optimizer\Loader\Loader' ) && defined( '\SiteGround_Optimizer\VERSION' ) && version_compare( \SiteGround_Optimizer\VERSION, '5.5', '>=' ) )
814 $is_plugin_active = true;
815 break;
816
817 // speedycache
818 case 'speedycache':
819 if ( class_exists( 'SpeedyCache' ) && defined( 'SPEEDYCACHE_VERSION' ) && function_exists( 'speedycache_delete_cache' ) && version_compare( SPEEDYCACHE_VERSION, '1.0.0', '>=' ) )
820 $is_plugin_active = true;
821 break;
822
823 // wp fastest cache
824 case 'wpfastestcache':
825 if ( function_exists( 'wpfc_clear_all_cache' ) )
826 $is_plugin_active = true;
827 break;
828
829 // wp-optimize
830 case 'wpoptimize':
831 if ( function_exists( 'WP_Optimize' ) && defined( 'WPO_VERSION' ) && version_compare( WPO_VERSION, '3.0.12', '>=' ) )
832 $is_plugin_active = true;
833 break;
834
835 // wp rocket
836 case 'wprocket':
837 if ( function_exists( 'rocket_init' ) && defined( 'WP_ROCKET_VERSION' ) && version_compare( WP_ROCKET_VERSION, '3.8', '>=' ) )
838 $is_plugin_active = true;
839 break;
840
841 // other caching plugin
842 default:
843 $is_plugin_active = apply_filters( 'pvc_is_plugin_active', false, $plugin );
844 }
845
846 return $is_plugin_active;
847 }
848 }
849