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 / includes / class-settings-general.php
post-views-counter / includes Last commit date
class-admin.php 6 months ago class-columns.php 6 months ago class-counter.php 6 months ago class-crawler-detect.php 6 months ago class-cron.php 6 months ago class-dashboard.php 6 months ago class-frontend.php 6 months ago class-functions.php 6 months ago class-import.php 6 months ago class-integration-gutenberg.php 6 months ago class-integrations.php 6 months ago class-query.php 6 months ago class-settings-api.php 6 months ago class-settings-display.php 6 months ago class-settings-general.php 6 months ago class-settings-integrations.php 6 months ago class-settings-other.php 6 months ago class-settings-reports.php 6 months ago class-settings.php 6 months ago class-toolbar.php 6 months ago class-update.php 6 months ago class-widgets.php 6 months ago functions.php 6 months ago
class-settings-general.php
789 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 'display_type' => 'horizontal',
131 'description' => __( 'Select post types whose views should be counted.', 'post-views-counter' ),
132 'options' => $post_types
133 ],
134 'taxonomies_count' => [
135 'tab' => 'general',
136 'title' => __( 'Taxonomies', 'post-views-counter' ),
137 'section' => 'post_views_counter_general_tracking_targets',
138 'type' => 'custom',
139 'label' => __( 'Enable counting views on taxonomy term archive pages.', 'post-views-counter' ),
140 'class' => 'pvc-pro',
141 'skip_saving' => true,
142 'callback' => [ $this, 'setting_taxonomies_count' ]
143 ],
144 'users_count' => [
145 'tab' => 'general',
146 'title' => __( 'Author Archives', 'post-views-counter' ),
147 'section' => 'post_views_counter_general_tracking_targets',
148 'type' => 'custom',
149 'label' => __( 'Enable counting views on author archive pages.', 'post-views-counter' ),
150 'class' => 'pvc-pro',
151 'skip_saving' => true,
152 'callback' => [ $this, 'setting_users_count' ]
153 ],
154 'other_count' => [
155 'tab' => 'general',
156 'title' => __( 'Other Pages', 'post-views-counter' ),
157 'section' => 'post_views_counter_general_tracking_targets',
158 'type' => 'boolean',
159 'label' => __( 'Track views on the front page, post type archives, date archives, search results, and 404 pages.', 'post-views-counter' ),
160 'class' => 'pvc-pro',
161 'disabled' => true,
162 'skip_saving' => true,
163 'value' => false
164 ],
165 'technology_count' => [
166 'tab' => 'general',
167 'title' => __( 'Traffic Sources', 'post-views-counter' ),
168 'section' => 'post_views_counter_general_tracking_targets',
169 'type' => 'boolean',
170 'label' => __( 'Collect aggregate stats about visitors\' browsers, devices, operating systems and referrers.', 'post-views-counter' ),
171 'class' => 'pvc-pro',
172 'disabled' => true,
173 'skip_saving' => true,
174 'value' => false
175 ],
176 'counter_mode' => [
177 'tab' => 'general',
178 'title' => __( 'Counter Mode', 'post-views-counter' ),
179 'section' => 'post_views_counter_general_tracking_behavior',
180 'type' => 'radio',
181 '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' ),
182 'class' => 'pvc-pro-extended',
183 'options' => $this->get_counter_modes(),
184 'disabled' => [ 'ajax' ]
185 ],
186 'data_storage' => [
187 'tab' => 'general',
188 'title' => __( 'Data Storage', 'post-views-counter' ),
189 'section' => 'post_views_counter_general_tracking_behavior',
190 'type' => 'radio',
191 'class' => 'pvc-pro',
192 'skip_saving' => true,
193 'description' => __( "Choose how to store the content views data in the user's browser - with or without cookies.", 'post-views-counter' ),
194 'options' => [
195 'cookies' => __( 'Cookies', 'post-views-counter' ),
196 'cookieless' => __( 'Cookieless', 'post-views-counter' )
197 ],
198 'disabled' => [ 'cookies', 'cookieless' ],
199 'value' => 'cookies'
200 ],
201 'time_between_counts' => [
202 'tab' => 'general',
203 'title' => __( 'Count Interval', 'post-views-counter' ),
204 'section' => 'post_views_counter_general_tracking_behavior',
205 'type' => 'custom',
206 'description' => '',
207 'min' => 0,
208 'max' => 999999,
209 'options' => $time_types,
210 'callback' => [ $this, 'setting_time_between_counts' ],
211 'validate' => [ $this, 'validate_time_between_counts' ]
212 ],
213 'count_time' => [
214 'tab' => 'general',
215 'title' => __( 'Count Time', 'post-views-counter' ),
216 'section' => 'post_views_counter_general_tracking_behavior',
217 'type' => 'radio',
218 'class' => 'pvc-pro',
219 'skip_saving' => true,
220 'description' => __( 'Whether to store the views using GMT timezone or adjust it to the GMT offset of the site.', 'post-views-counter' ),
221 'options' => [
222 'gmt' => __( 'GMT Time', 'post-views-counter' ),
223 'local' => __( 'Local Time', 'post-views-counter' )
224 ],
225 'disabled' => [ 'gmt', 'local' ],
226 'value' => 'gmt'
227 ],
228 'strict_counts' => [
229 'tab' => 'general',
230 'title' => __( 'Strict Counts', 'post-views-counter' ),
231 'section' => 'post_views_counter_general_tracking_behavior',
232 'type' => 'boolean',
233 'class' => 'pvc-pro',
234 'disabled' => true,
235 'skip_saving' => true,
236 'value' => false,
237 'description' => '',
238 'label' => __( 'Prevent bypassing the count interval (for example by using incognito mode or clearing cookies).', 'post-views-counter' )
239 ],
240 'reset_counts' => [
241 'tab' => 'general',
242 'title' => __( 'Cleanup Interval', 'post-views-counter' ),
243 'section' => 'post_views_counter_general_tracking_behavior',
244 'type' => 'custom',
245 '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>' ),
246 'min' => 0,
247 'max' => 999999,
248 'options' => $time_types,
249 'callback' => [ $this, 'setting_reset_counts' ],
250 'validate' => [ $this, 'validate_reset_counts' ]
251 ],
252 'caching_compatibility' => [
253 'tab' => 'general',
254 'title' => __( 'Caching Compatibility', 'post-views-counter' ),
255 'section' => 'post_views_counter_general_performance',
256 'type' => 'boolean',
257 'class' => 'pvc-pro',
258 'disabled' => true,
259 'value' => false,
260 'skip_saving' => true,
261 'label' => __( 'Enable compatibility tweaks for supported caching plugins.', 'post-views-counter' ),
262 'description' => $this->get_caching_compatibility_description()
263 ],
264 'object_cache' => [
265 'tab' => 'general',
266 'title' => __( 'Object Cache Support', 'post-views-counter' ),
267 'section' => 'post_views_counter_general_performance',
268 'type' => 'boolean',
269 'class' => 'pvc-pro',
270 'disabled' => true,
271 'value' => false,
272 'skip_saving' => true,
273 'label' => __( 'Enable Redis or Memcached object cache optimization.', 'post-views-counter' ),
274 'description' => 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>' ) . '<br />' . __( 'Current status', '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>.'
275 ],
276 'exclude' => [
277 'tab' => 'general',
278 'title' => __( 'Exclude Visitors', 'post-views-counter' ),
279 'section' => 'post_views_counter_general_exclusions',
280 'type' => 'custom',
281 'description' => '',
282 'class' => 'pvc-pro-extended',
283 'options' => [
284 'groups' => $groups,
285 'roles' => $user_roles
286 ],
287 'disabled' => [
288 'groups' => [ 'ai_bots' ]
289 ],
290 'callback' => [ $this, 'setting_exclude' ],
291 'validate' => [ $this, 'validate_exclude' ]
292 ],
293 'exclude_ips' => [
294 'tab' => 'general',
295 'title' => __( 'Exclude IPs', 'post-views-counter' ),
296 'section' => 'post_views_counter_general_exclusions',
297 'type' => 'custom',
298 'description' => '',
299 'callback' => [ $this, 'setting_exclude_ips' ],
300 'validate' => [ $this, 'validate_exclude_ips' ]
301 ]
302 ];
303 }
304
305 /**
306 * Setting: taxonomies count.
307 *
308 * @param array $field
309 * @return string
310 */
311 public function setting_taxonomies_count( $field ) {
312 $html = '
313 <label><input id="post_views_counter_general_taxonomies_count" type="checkbox" name="" value="" disabled />' . esc_html( $field['label'] ) . '</label>';
314
315 return $html;
316 }
317
318 /**
319 * Setting: users count.
320 *
321 * @param array $field
322 * @return string
323 */
324 public function setting_users_count( $field ) {
325 $html = '
326 <label><input id="post_views_counter_general_users_count" type="checkbox" name="" value="" disabled />' . esc_html( $field['label'] ) . '</label>';
327
328 return $html;
329 }
330
331 /**
332 * Setting: count interval.
333 *
334 * @param array $field
335 * @return string
336 */
337 public function setting_time_between_counts( $field ) {
338 $html = '
339 <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'] ) . '" />
340 <select name="post_views_counter_settings_general[time_between_counts][type]">';
341
342 foreach ( $field['options'] as $type => $type_name ) {
343 $html .= '
344 <option value="' . esc_attr( $type ) . '" ' . selected( $type, $this->pvc->options['general']['time_between_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
345 }
346
347 $html .= '
348 </select>
349 <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>';
350
351 return $html;
352 }
353
354 /**
355 * Validate count interval.
356 *
357 * @param array $input
358 * @param array $field
359 * @return array
360 */
361 public function validate_time_between_counts( $input, $field ) {
362 // number
363 $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'];
364
365 if ( $input['time_between_counts']['number'] < $field['min'] || $input['time_between_counts']['number'] > $field['max'] )
366 $input['time_between_counts']['number'] = $this->pvc->defaults['general']['time_between_counts']['number'];
367
368 // type
369 $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'];
370
371 return $input;
372 }
373
374 /**
375 * Setting: reset data interval.
376 *
377 * @param array $field
378 * @return string
379 */
380 public function setting_reset_counts( $field ) {
381 $html = '
382 <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'] ) . '" />
383 <select name="post_views_counter_settings_general[reset_counts][type]">';
384
385 foreach ( array_slice( $field['options'], 2, null, true ) as $type => $type_name ) {
386 $html .= '
387 <option value="' . esc_attr( $type ) . '" ' . selected( $type, $this->pvc->options['general']['reset_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
388 }
389
390 $html .= '
391 </select>';
392
393 return $html;
394 }
395
396 /**
397 * Validate reset data interval.
398 *
399 * @param array $input
400 * @param array $field
401 * @return array
402 */
403 public function validate_reset_counts( $input, $field ) {
404 // number
405 $input['reset_counts']['number'] = isset( $input['reset_counts']['number'] ) ? (int) $input['reset_counts']['number'] : $this->pvc->defaults['general']['reset_counts']['number'];
406
407 if ( $input['reset_counts']['number'] < $field['min'] || $input['reset_counts']['number'] > $field['max'] )
408 $input['reset_counts']['number'] = $this->pvc->defaults['general']['reset_counts']['number'];
409
410 // type
411 $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'];
412
413 // run cron on next visit?
414 $input['cron_run'] = ( $input['reset_counts']['number'] > 0 );
415
416 // cron update?
417 $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'] ) );
418
419 return $input;
420 }
421
422 /**
423 * Setting: object cache.
424 *
425 * @param array $field
426 * @return string
427 */
428 public function setting_object_cache( $field ) {
429 $html = '
430 <input size="4" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="" value="0" disabled /> <span>' . __( 'minutes', 'post-views-counter' ) . '</span>
431 <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>
432 <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>';
433
434 return $html;
435 }
436
437 /**
438 * Setting: exclude visitors.
439 *
440 * @param array $field
441 * @return string
442 */
443 public function setting_exclude( $field ) {
444 $html = '';
445
446 $html .= '<div class="pvc-field-group pvc-checkbox-group">';
447
448 foreach ( $field['options']['groups'] as $type => $type_name ) {
449 $is_disabled = ! empty( $field['disabled']['groups'] ) && in_array( $type, $field['disabled']['groups'], true );
450
451 $html .= '
452 <label for="' . esc_attr( 'pvc_exclude-' . $type ) . '"><input id="' . esc_attr( 'pvc_exclude-' . $type ) . '" type="checkbox" name="post_views_counter_settings_general[exclude][groups][' . esc_attr( $type ) . ']" value="1" ' . checked( in_array( $type, $this->pvc->options['general']['exclude']['groups'], true ) && ! $is_disabled, true, false ) . ' ' . disabled( $is_disabled, true, false ) . ' />' . esc_html( $type_name ) . '</label>';
453 }
454
455 $html .= '</div>';
456
457 $html .= '
458 <p class="description">' . __( 'Use this to exclude specific visitor groups from counting views.', 'post-views-counter' ) . '</p>';
459
460 // user roles subfield
461 $html .= '
462 <div class="pvc_user_roles pvc_subfield pvc-field-group pvc-checkbox-group"' . ( in_array( 'roles', $this->pvc->options['general']['exclude']['groups'], true ) ? '' : ' style="display: none;"' ) . '>';
463
464 foreach ( $field['options']['roles'] as $role => $role_name ) {
465 $html .= '
466 <label><input type="checkbox" name="post_views_counter_settings_general[exclude][roles][' . $role . ']" value="1" ' . checked( in_array( $role, $this->pvc->options['general']['exclude']['roles'], true ), true, false ) . ' />' . esc_html( $role_name ) . '</label>';
467 }
468
469 $html .= '
470 <p class="description">' . __( 'Use this to exclude specific user roles from counting views.', 'post-views-counter' ) . '</p>
471 </div>';
472
473 return $html;
474 }
475
476 /**
477 * Validate exclude visitors.
478 *
479 * @param array $input
480 * @param array $field
481 * @return array
482 */
483 public function validate_exclude( $input, $field ) {
484 // any groups?
485 if ( isset( $input['exclude']['groups'] ) ) {
486 $groups = [];
487
488 foreach ( $input['exclude']['groups'] as $group => $set ) {
489 // disallow disabled checkboxes
490 if ( ! empty( $field['disabled']['groups'] ) && in_array( $group, $field['disabled']['groups'], true ) )
491 continue;
492
493 if ( isset( $field['options']['groups'][$group] ) )
494 $groups[] = $group;
495 }
496
497 $input['exclude']['groups'] = array_unique( $groups );
498 } else
499 $input['exclude']['groups'] = [];
500
501 // any roles?
502 if ( in_array( 'roles', $input['exclude']['groups'], true ) && isset( $input['exclude']['roles'] ) ) {
503 $roles = [];
504
505 foreach ( $input['exclude']['roles'] as $role => $set ) {
506 if ( isset( $field['options']['roles'][$role] ) )
507 $roles[] = $role;
508 }
509
510 $input['exclude']['roles'] = array_unique( $roles );
511 } else
512 $input['exclude']['roles'] = [];
513
514 return $input;
515 }
516
517 /**
518 * Setting: exclude IP addresses.
519 *
520 * @return string
521 */
522 public function setting_exclude_ips() {
523 // get ip addresses
524 $ips = $this->pvc->options['general']['exclude_ips'];
525
526 $html = '';
527
528 // any ip addresses?
529 if ( ! empty( $ips ) ) {
530 foreach ( $ips as $key => $ip ) {
531 $html .= '
532 <div class="ip-box">
533 <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="' . esc_attr( $ip ) . '" /> <a href="#" class="remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
534 </div>';
535 }
536 } else {
537 $html .= '
538 <div class="ip-box">
539 <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="" /> <a href="#" class="remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '" style="display: none">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
540 </div>';
541 }
542
543 $html .= '
544 <p><input type="button" class="button button-secondary add-exclude-ip" value="' . esc_attr__( 'Add new', 'post-views-counter' ) . '" /> <input type="button" class="button button-secondary add-current-ip" value="' . esc_attr__( 'Add my current IP', 'post-views-counter' ) . '" data-rel="' . esc_attr( $_SERVER['REMOTE_ADDR'] ) . '" /></p>
545 <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 // caching compatibility description
621 $caching_compatibility_desc = '';
622
623 // get active caching plugins
624 $active_plugins = $this->get_active_caching_plugins();
625
626 if ( ! empty( $active_plugins ) ) {
627 $empty_active_caching_plugins = false;
628 $active_plugins_html = [];
629
630 $caching_compatibility_desc .= esc_html__( 'Currently detected active caching plugins', 'post-views-counter' ) . ': ';
631
632 foreach ( $active_plugins as $plugin ) {
633 $active_plugins_html[] = '<code>' . esc_html( $plugin ) . '</code>';
634 }
635
636 $caching_compatibility_desc .= implode( ', ', $active_plugins_html ) . '.';
637 } else {
638 $empty_active_caching_plugins = true;
639
640 $caching_compatibility_desc .= esc_html__( 'No compatible caching plugins found.', 'post-views-counter' );
641 }
642
643 return $caching_compatibility_desc . '<br />' . __( 'Current status', 'post-views-counter' ) . ': <span class="' . ( ! $empty_active_caching_plugins ? '' : 'un' ) . 'available">' . ( ! $empty_active_caching_plugins ? __( 'available', 'post-views-counter' ) : __( 'unavailable', 'post-views-counter' ) ) . '</span>.';
644 }
645
646 /**
647 * Extend active caching plugins.
648 *
649 * @param array $plugins
650 *
651 * @return array
652 */
653 public function extend_active_caching_plugins( $plugins ) {
654 // breeze
655 if ( $this->is_plugin_active( 'breeze' ) )
656 $plugins[] = 'Breeze';
657
658 return $plugins;
659 }
660
661 /**
662 * Check whether specified plugin is active.
663 *
664 * @param bool $is_plugin_active
665 * @param string $plugin
666 *
667 * @return bool
668 */
669 public function extend_is_plugin_active( $is_plugin_active, $plugin ) {
670 // breeze
671 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', '>=' ) )
672 $is_plugin_active = true;
673
674 return $is_plugin_active;
675 }
676
677 /**
678 * Get active caching plugins.
679 *
680 * @return array
681 */
682 public function get_active_caching_plugins() {
683 $active_plugins = [];
684
685 // autoptimize
686 if ( $this->is_plugin_active( 'autoptimize' ) )
687 $active_plugins[] = 'Autoptimize';
688
689 // hummingbird
690 if ( $this->is_plugin_active( 'hummingbird' ) )
691 $active_plugins[] = 'Hummingbird';
692
693 // litespeed
694 if ( $this->is_plugin_active( 'litespeed' ) )
695 $active_plugins[] = 'LiteSpeed Cache';
696
697 // speed optimizer
698 if ( $this->is_plugin_active( 'speedoptimizer' ) )
699 $active_plugins[] = 'Speed Optimizer';
700
701 // speedycache
702 if ( $this->is_plugin_active( 'speedycache' ) )
703 $active_plugins[] = 'SpeedyCache';
704
705 // wp fastest cache
706 if ( $this->is_plugin_active( 'wpfastestcache' ) )
707 $active_plugins[] = 'WP Fastest Cache';
708
709 // wp-optimize
710 if ( $this->is_plugin_active( 'wpoptimize' ) )
711 $active_plugins[] = 'WP-Optimize';
712
713 // wp rocket
714 if ( $this->is_plugin_active( 'wprocket' ) )
715 $active_plugins[] = 'WP Rocket';
716
717 return apply_filters( 'pvc_active_caching_plugins', $active_plugins );
718 }
719
720 /**
721 * Check whether specified plugin is active.
722 *
723 * @param string $plugin
724 *
725 * @return bool
726 */
727 public function is_plugin_active( $plugin = '' ) {
728 // set default flag
729 $is_plugin_active = false;
730
731 switch ( $plugin ) {
732 // autoptimize
733 case 'autoptimize':
734 if ( function_exists( 'autoptimize' ) && defined( 'AUTOPTIMIZE_PLUGIN_VERSION' ) && version_compare( AUTOPTIMIZE_PLUGIN_VERSION, '2.4', '>=' ) )
735 $is_plugin_active = true;
736 break;
737
738 // hummingbird
739 case 'hummingbird':
740 if ( class_exists( 'Hummingbird\\WP_Hummingbird' ) && defined( 'WPHB_VERSION' ) && version_compare( WPHB_VERSION, '2.1.0', '>=' ) )
741 $is_plugin_active = true;
742 break;
743
744 // litespeed
745 case 'litespeed':
746 if ( class_exists( 'LiteSpeed\Core' ) && defined( 'LSCWP_CUR_V' ) && version_compare( LSCWP_CUR_V, '3.0', '>=' ) )
747 $is_plugin_active = true;
748 break;
749
750 // speed optimizer
751 case 'speedoptimizer':
752 global $siteground_optimizer_loader;
753
754 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', '>=' ) )
755 $is_plugin_active = true;
756 break;
757
758 // speedycache
759 case 'speedycache':
760 if ( class_exists( 'SpeedyCache' ) && defined( 'SPEEDYCACHE_VERSION' ) && function_exists( 'speedycache_delete_cache' ) && version_compare( SPEEDYCACHE_VERSION, '1.0.0', '>=' ) )
761 $is_plugin_active = true;
762 break;
763
764 // wp fastest cache
765 case 'wpfastestcache':
766 if ( function_exists( 'wpfc_clear_all_cache' ) )
767 $is_plugin_active = true;
768 break;
769
770 // wp-optimize
771 case 'wpoptimize':
772 if ( function_exists( 'WP_Optimize' ) && defined( 'WPO_VERSION' ) && version_compare( WPO_VERSION, '3.0.12', '>=' ) )
773 $is_plugin_active = true;
774 break;
775
776 // wp rocket
777 case 'wprocket':
778 if ( function_exists( 'rocket_init' ) && defined( 'WP_ROCKET_VERSION' ) && version_compare( WP_ROCKET_VERSION, '3.8', '>=' ) )
779 $is_plugin_active = true;
780 break;
781
782 // other caching plugin
783 default:
784 $is_plugin_active = apply_filters( 'pvc_is_plugin_active', false, $plugin );
785 }
786
787 return $is_plugin_active;
788 }
789 }