PluginProbe ʕ •ᴥ•ʔ
Post Views Counter / 1.7.4
Post Views Counter v1.7.4
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-modal.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-traffic-signals.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
855 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 ) {
487 // sanitize value
488 $group = sanitize_key( $group );
489
490 // disallow disabled checkboxes
491 if ( ! empty( $field['disabled'] ) && in_array( $group, $field['disabled'], true ) )
492 continue;
493
494 if ( isset( $field['options'][$group] ) )
495 $groups[] = $group;
496 }
497 }
498
499 return array_unique( $groups );
500 }
501
502 public function validate_exclude_roles( $input, $field ) {
503 $roles = [];
504
505 if ( is_array( $input ) ) {
506 foreach ( $input as $role ) {
507 // sanitize value
508 $role = sanitize_key( $role );
509
510 if ( isset( $field['options'][$role] ) )
511 $roles[] = $role;
512 }
513 }
514
515 return array_unique( $roles );
516 }
517
518 /**
519 * Setting: exclude IP addresses.
520 *
521 * @return string
522 */
523 public function setting_exclude_ips() {
524 // get ip addresses
525 $ips = $this->pvc->options['general']['exclude_ips'];
526
527 $html = '<div class="pvc-ip-box-group">';
528
529 // any ip addresses?
530 if ( ! empty( $ips ) ) {
531 foreach ( $ips as $key => $ip ) {
532 $html .= '
533 <div class="pvc-ip-box">
534 <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>
535 </div>';
536 }
537 } else {
538 $html .= '
539 <div class="pvc-ip-box">
540 <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>
541 </div>';
542 }
543
544 $html .= '</div>';
545
546 $html .= '
547 <div class="pvc-field-group pvc-buttons-group">
548 <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'] ) . '" />
549 </div>';
550
551 $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>';
552
553 return $html;
554 }
555
556 /**
557 * Validate exclude IP addresses.
558 *
559 * @param array $input
560 * @param array $field
561 * @return array
562 */
563 public function validate_exclude_ips( $input, $field ) {
564 // any ip addresses?
565 if ( isset( $input['exclude_ips'] ) ) {
566 $ips = [];
567
568 foreach ( $input['exclude_ips'] as $ip ) {
569 if ( strpos( $ip, '*' ) !== false ) {
570 $new_ip = str_replace( '*', '0', $ip );
571
572 if ( filter_var( $new_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
573 $ips[] = $ip;
574 } elseif ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
575 $ips[] = $ip;
576 }
577
578 $input['exclude_ips'] = array_unique( $ips );
579 }
580
581 return $input;
582 }
583
584 /**
585 * Section description: tracking targets.
586 *
587 * @return void
588 */
589 public function section_tracking_targets() {
590 echo '<p class="description">' . esc_html__( 'Control which post types, archives and other content types are included in view counting.', 'post-views-counter' ) . '</p>';
591 }
592
593 /**
594 * Section description: tracking behavior.
595 *
596 * @return void
597 */
598 public function section_tracking_behavior() {
599 echo '<p class="description">' . esc_html__( 'Control how views are recorded — counting mode, intervals, time zone, and cleanup.', 'post-views-counter' ) . '</p>';
600 }
601
602 /**
603 * Section description: tracking exclusions.
604 *
605 * @return void
606 */
607 public function section_tracking_exclusions() {
608 echo '<p class="description">' . esc_html__( 'Exclude specific visitor groups or IP addresses from incrementing view counts.', 'post-views-counter' ) . '</p>';
609 }
610
611 /**
612 * Section description: performance & caching.
613 *
614 * @return void
615 */
616 public function section_tracking_performance() {
617 echo '<p class="description">' . esc_html__( 'Configure caching compatibility and object-cache handling for counting.', 'post-views-counter' ) . '</p>';
618 }
619
620 /**
621 * Get caching compatibility description.
622 *
623 * @return string
624 */
625 public function get_caching_compatibility_description() {
626 // get active caching plugins
627 $active_plugins = $this->get_active_caching_plugins();
628
629 if ( ! empty( $active_plugins ) ) {
630 $active_plugins_html = [];
631
632 $description = esc_html__( 'Currently detected active caching plugins', 'post-views-counter' ) . ': ';
633
634 foreach ( $active_plugins as $plugin ) {
635 $active_plugins_html[] = '<code>' . esc_html( $plugin ) . '</code>';
636 }
637
638 $description .= implode( ', ', $active_plugins_html ) . '.';
639 } else {
640 $description = esc_html__( 'No compatible caching plugins found.', 'post-views-counter' );
641 }
642
643 return $description;
644 }
645
646 /**
647 * Get caching compatibility label with availability indicator.
648 *
649 * @return string
650 */
651 public function get_caching_compatibility_label() {
652 $label = __( 'Enable compatibility tweaks for supported caching plugins.', 'post-views-counter' );
653
654 // add availability indicator when Pro is missing but feature is available
655 if ( ! class_exists( 'Post_Views_Counter_Pro' ) && $this->is_caching_compatibility_available() ) {
656 $label = '<span class="pvc-availability-status available">' . esc_html__( '(available)', 'post-views-counter' ) . '</span> ' . $label;
657 }
658
659 return $label;
660 }
661
662 /**
663 * Get object cache label with availability indicator.
664 *
665 * @return string
666 */
667 public function get_object_cache_label() {
668 $label = __( 'Enable Redis or Memcached object cache optimization.', 'post-views-counter' );
669
670 // add availability indicator when Pro is missing but feature is available
671 if ( ! class_exists( 'Post_Views_Counter_Pro' ) && $this->is_object_cache_available() ) {
672 $label = '<span class="pvc-availability-status available">' . esc_html__( '(available)', 'post-views-counter' ) . '</span> ' . $label;
673 }
674
675 return $label;
676 }
677
678 /**
679 * Check if caching compatibility feature is available.
680 *
681 * @return bool
682 */
683 public function is_caching_compatibility_available() {
684 $active_plugins = $this->get_active_caching_plugins();
685
686 return ! empty( $active_plugins );
687 }
688
689 /**
690 * Check if object cache feature is available.
691 *
692 * @return bool
693 */
694 public function is_object_cache_available() {
695 return wp_using_ext_object_cache();
696 }
697
698 /**
699 * Get object cache description based on availability.
700 *
701 * @return string
702 */
703 public function get_object_cache_description() {
704 if ( $this->is_object_cache_available() ) {
705 return __( 'Persistent object cache has been detected.', 'post-views-counter' );
706 }
707
708 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>' );
709 }
710
711 /**
712 * Extend active caching plugins.
713 *
714 * @param array $plugins
715 *
716 * @return array
717 */
718 public function extend_active_caching_plugins( $plugins ) {
719 // breeze
720 if ( $this->is_plugin_active( 'breeze' ) )
721 $plugins[] = 'Breeze';
722
723 return $plugins;
724 }
725
726 /**
727 * Check whether specified plugin is active.
728 *
729 * @param bool $is_plugin_active
730 * @param string $plugin
731 *
732 * @return bool
733 */
734 public function extend_is_plugin_active( $is_plugin_active, $plugin ) {
735 // breeze
736 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', '>=' ) )
737 $is_plugin_active = true;
738
739 return $is_plugin_active;
740 }
741
742 /**
743 * Get active caching plugins.
744 *
745 * @return array
746 */
747 public function get_active_caching_plugins() {
748 $active_plugins = [];
749
750 // autoptimize
751 if ( $this->is_plugin_active( 'autoptimize' ) )
752 $active_plugins[] = 'Autoptimize';
753
754 // hummingbird
755 if ( $this->is_plugin_active( 'hummingbird' ) )
756 $active_plugins[] = 'Hummingbird';
757
758 // litespeed
759 if ( $this->is_plugin_active( 'litespeed' ) )
760 $active_plugins[] = 'LiteSpeed Cache';
761
762 // speed optimizer
763 if ( $this->is_plugin_active( 'speedoptimizer' ) )
764 $active_plugins[] = 'Speed Optimizer';
765
766 // speedycache
767 if ( $this->is_plugin_active( 'speedycache' ) )
768 $active_plugins[] = 'SpeedyCache';
769
770 // wp fastest cache
771 if ( $this->is_plugin_active( 'wpfastestcache' ) )
772 $active_plugins[] = 'WP Fastest Cache';
773
774 // wp-optimize
775 if ( $this->is_plugin_active( 'wpoptimize' ) )
776 $active_plugins[] = 'WP-Optimize';
777
778 // wp rocket
779 if ( $this->is_plugin_active( 'wprocket' ) )
780 $active_plugins[] = 'WP Rocket';
781
782 return apply_filters( 'pvc_active_caching_plugins', $active_plugins );
783 }
784
785 /**
786 * Check whether specified plugin is active.
787 *
788 * @param string $plugin
789 *
790 * @return bool
791 */
792 public function is_plugin_active( $plugin = '' ) {
793 // set default flag
794 $is_plugin_active = false;
795
796 switch ( $plugin ) {
797 // autoptimize
798 case 'autoptimize':
799 if ( function_exists( 'autoptimize' ) && defined( 'AUTOPTIMIZE_PLUGIN_VERSION' ) && version_compare( AUTOPTIMIZE_PLUGIN_VERSION, '2.4', '>=' ) )
800 $is_plugin_active = true;
801 break;
802
803 // hummingbird
804 case 'hummingbird':
805 if ( class_exists( 'Hummingbird\\WP_Hummingbird' ) && defined( 'WPHB_VERSION' ) && version_compare( WPHB_VERSION, '2.1.0', '>=' ) )
806 $is_plugin_active = true;
807 break;
808
809 // litespeed
810 case 'litespeed':
811 if ( class_exists( 'LiteSpeed\Core' ) && defined( 'LSCWP_CUR_V' ) && version_compare( LSCWP_CUR_V, '3.0', '>=' ) )
812 $is_plugin_active = true;
813 break;
814
815 // speed optimizer
816 case 'speedoptimizer':
817 global $siteground_optimizer_loader;
818
819 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', '>=' ) )
820 $is_plugin_active = true;
821 break;
822
823 // speedycache
824 case 'speedycache':
825 if ( class_exists( 'SpeedyCache' ) && defined( 'SPEEDYCACHE_VERSION' ) && function_exists( 'speedycache_delete_cache' ) && version_compare( SPEEDYCACHE_VERSION, '1.0.0', '>=' ) )
826 $is_plugin_active = true;
827 break;
828
829 // wp fastest cache
830 case 'wpfastestcache':
831 if ( function_exists( 'wpfc_clear_all_cache' ) )
832 $is_plugin_active = true;
833 break;
834
835 // wp-optimize
836 case 'wpoptimize':
837 if ( function_exists( 'WP_Optimize' ) && defined( 'WPO_VERSION' ) && version_compare( WPO_VERSION, '3.0.12', '>=' ) )
838 $is_plugin_active = true;
839 break;
840
841 // wp rocket
842 case 'wprocket':
843 if ( function_exists( 'rocket_init' ) && defined( 'WP_ROCKET_VERSION' ) && version_compare( WP_ROCKET_VERSION, '3.8', '>=' ) )
844 $is_plugin_active = true;
845 break;
846
847 // other caching plugin
848 default:
849 $is_plugin_active = apply_filters( 'pvc_is_plugin_active', false, $plugin );
850 }
851
852 return $is_plugin_active;
853 }
854 }
855