Post Views Counter - Version 1.3.7

Version Description

  • Tweak: Implemented internal settings API
Download this release

Release Info

Developer dfactory
Plugin Icon 128x128 Post Views Counter
Version 1.3.7
Comparing to
See all releases

Code changes from version 1.3.6 to 1.3.7

css/admin.css CHANGED
@@ -25,6 +25,14 @@
25
  margin-bottom: 3px;
26
  }
27
 
 
 
 
 
 
 
 
 
28
  .df-credits {
29
  float: right;
30
  width: 280px;
@@ -111,378 +119,6 @@
111
  width: auto;
112
  }
113
 
114
- /*
115
- * checkBo lightweight jQuery plugin v0.1.4 by @ElmahdiMahmoud
116
- * Licensed under the MIT license - https://github.com/elmahdim/checkbo/blob/master/LICENSE
117
- *
118
- * Custom checkbox and radio
119
- * Author URL: elmahdim.com
120
- */
121
- .cb-checkbox .cb-inner, .cb-checkbox i {
122
- width:18px;
123
- height:18px;
124
- -moz-border-radius:3px;
125
- -webkit-border-radius:3px;
126
- border-radius:3px
127
- }
128
- .cb-checkbox.cb-sm i, .cb-checkbox.cb-sm .cb-inner {
129
- width:14px;
130
- height:14px
131
- }
132
- .cb-checkbox.cb-md i, .cb-checkbox.cb-md .cb-inner {
133
- width:24px;
134
- height:24px;
135
- -moz-border-radius:4px;
136
- -webkit-border-radius:4px;
137
- border-radius:4px
138
- }
139
- .cb-checkbox.cb-lg i, .cb-checkbox.cb-lg .cb-inner {
140
- width:30px;
141
- height:30px;
142
- -moz-border-radius:6px;
143
- -webkit-border-radius:6px;
144
- border-radius:6px
145
- }
146
- .cb-radio .cb-inner {
147
- width:18px;
148
- height:18px
149
- }
150
- .cb-radio.cb-sm .cb-inner {
151
- width:14px;
152
- height:14px
153
- }
154
- .cb-radio.cb-md .cb-inner {
155
- width:24px;
156
- height:24px
157
- }
158
- .cb-radio.cb-lg .cb-inner {
159
- width:30px;
160
- height:30px
161
- }
162
- .cb-checkbox, .cb-radio {
163
- color:inherit;
164
- cursor:pointer;
165
- overflow:hidden;
166
- font-size:inherit;
167
- font-weight:normal;
168
- display:inline-block;
169
- line-height:18px;
170
- margin-right: 8px;
171
- }
172
- .form-table td fieldset label.cb-checkbox,
173
- .form-table td fieldset label.cb-radio {
174
- margin-right: 8px !important;
175
- }
176
- .cb-checkbox.disabled, .cb-checkbox.disabled *, .cb-radio.disabled, .cb-radio.disabled * {
177
- cursor:default
178
- }
179
- .cb-checkbox input[type="checkbox"], .cb-radio input[type="radio"], .cb-switcher input[type="checkbox"], .cb-switcher input[type="radio"] {
180
- display:none
181
- }
182
- .cb-checkbox.disabled, .cb-checkbox.disabled *, .cb-radio.disabled, .cb-radio.disabled *, .cb-switcher.disabled, .cb-switcher.disabled * {
183
- cursor:default
184
- }
185
- .cb-checkbox.disabled {
186
- color:#ddd
187
- }
188
- .cb-checkbox.disabled .cb-inner {
189
- color:#ddd
190
- }
191
- .cb-checkbox.disabled:hover .cb-inner {
192
- border-color:#ddd
193
- }
194
- .cb-checkbox.disabled.checked .cb-inner {
195
- background-color:#ddd;
196
- border-color:#ddd
197
- }
198
- .cb-radio.disabled {
199
- color:#ddd
200
- }
201
- .cb-radio.disabled .cb-inner {
202
- border-color:#ddd
203
- }
204
- .cb-radio.disabled i {
205
- background-color:transparent
206
- }
207
- .cb-radio.disabled.checked .cb-inner {
208
- border-color:#ddd
209
- }
210
- .cb-radio.disabled.checked .cb-inner i {
211
- background-color:#ddd
212
- }
213
- .cb-radio.disabled:hover .cb-inner {
214
- border-color:#ddd
215
- }
216
- .cb-checkbox .cb-inner {
217
- float:left;
218
- overflow:hidden;
219
- margin:0 5px 0 0;
220
- position:relative;
221
- background:#f2f2f2;
222
- display:inline-block;
223
- border:1px solid #d6d6d6;
224
- -moz-transition:all 0.5s ease;
225
- -o-transition:all 0.5s ease;
226
- -webkit-transition:all 0.5s ease;
227
- transition:all 0.5s ease
228
- }
229
- .cb-checkbox i {
230
- top:1px;
231
- left:2px;
232
- display:block;
233
- position:absolute
234
- }
235
- .cb-checkbox i:before, .cb-checkbox i:after {
236
- height:0;
237
- width:2px;
238
- content:"";
239
- display:block;
240
- position:absolute;
241
- background-color:#fff;
242
- -moz-transition:all 0.2s ease;
243
- -o-transition:all 0.2s ease;
244
- -webkit-transition:all 0.2s ease;
245
- transition:all 0.2s ease
246
- }
247
- .cb-checkbox i:before {
248
- top:0;
249
- left:0;
250
- -moz-transform:rotate(-45deg);
251
- -ms-transform:rotate(-45deg);
252
- -webkit-transform:rotate(-45deg);
253
- transform:rotate(-45deg)
254
- }
255
- .cb-checkbox i:after {
256
- left:7px;
257
- bottom:5px;
258
- -moz-transition-delay:0.3s;
259
- -o-transition-delay:0.3s;
260
- -webkit-transition-delay:0.3s;
261
- transition-delay:0.3s;
262
- -moz-transform:rotate(30deg);
263
- -ms-transform:rotate(30deg);
264
- -webkit-transform:rotate(30deg);
265
- transform:rotate(30deg)
266
- }
267
- .cb-radio .cb-inner {
268
- float:left;
269
- overflow:hidden;
270
- margin:0 5px 0 0;
271
- position:relative;
272
- display:inline-block;
273
- border:1px solid #d7d7d7;
274
- background-color:#f2f2f2;
275
- -moz-border-radius:100%;
276
- -webkit-border-radius:100%;
277
- border-radius:100%;
278
- -moz-transition:all 0.1s ease;
279
- -o-transition:all 0.1s ease;
280
- -webkit-transition:all 0.1s ease;
281
- transition:all 0.1s ease
282
- }
283
- .cb-radio i {
284
- top:50%;
285
- left:50%;
286
- width:6px;
287
- height:6px;
288
- margin-top:-3px;
289
- margin-left:-3px;
290
- position:absolute;
291
- background-color:transparent;
292
- -moz-border-radius:100%;
293
- -webkit-border-radius:100%;
294
- border-radius:100%;
295
- -moz-transform:scale(0.05, 5);
296
- -ms-transform:scale(0.05, 5);
297
- -webkit-transform:scale(0.05, 5);
298
- transform:scale(0.05, 5);
299
- -moz-transition:all 0.2s ease;
300
- -o-transition:all 0.2s ease;
301
- -webkit-transition:all 0.2s ease;
302
- transition:all 0.2s ease
303
- }
304
- .cb-checkbox.cb-sm, .cb-radio.cb-sm {
305
- line-height:14px
306
- }
307
- .cb-checkbox.cb-md, .cb-radio.cb-md {
308
- line-height:24px
309
- }
310
- .cb-checkbox.cb-lg, .cb-radio.cb-lg {
311
- line-height:30px
312
- }
313
- .cb-checkbox.cb-sm i:before {
314
- top:4px;
315
- left:1px
316
- }
317
- .cb-checkbox.cb-sm i:after {
318
- left:5px
319
- }
320
- .cb-checkbox.cb-md i:before {
321
- top:10px;
322
- left:5px
323
- }
324
- .cb-checkbox.cb-md i:after {
325
- bottom:6px;
326
- left:11px
327
- }
328
- .cb-checkbox.checked .cb-inner {
329
- border-color:#0073aa;
330
- background-color:#00a0d2;
331
- box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15);
332
- }
333
- .cb-checkbox.checked:hover .cb-inner {
334
- background-color:#0091cd
335
- }
336
- .cb-checkbox.checked.cb-sm i:before {
337
- top:4px;
338
- left:1px
339
- }
340
- .cb-checkbox.checked.cb-sm i:after {
341
- height:9px
342
- }
343
- .cb-checkbox.checked.cb-md i:before {
344
- top:10px;
345
- left:4px;
346
- height:8px
347
- }
348
- .cb-checkbox.checked.cb-md i:after {
349
- bottom:6px;
350
- left:11px;
351
- height:16px
352
- }
353
- .cb-checkbox.checked.cb-lg i:before {
354
- top:11px;
355
- left:6px;
356
- height:12px
357
- }
358
- .cb-checkbox.checked.cb-lg i:after {
359
- left:14px;
360
- bottom:7px;
361
- height:20px
362
- }
363
- .cb-checkbox.checked i:before {
364
- top:6px;
365
- left:2px;
366
- height:6px
367
- }
368
- .cb-checkbox.checked i:after {
369
- height:12px
370
- }
371
- .cb-radio.checked .cb-inner {
372
- background:#fff;
373
- box-shadow:0 0 3px #efefef
374
- }
375
- .cb-radio.checked i {
376
- -moz-transform:scale(1.1, 1.1);
377
- -ms-transform:scale(1.1, 1.1);
378
- -webkit-transform:scale(1.1, 1.1);
379
- transform:scale(1.1, 1.1);
380
- background-color:#00a0d2
381
- }
382
- .cb-checkbox:hover .cb-inner, .cb-radio:hover .cb-inner {
383
- border-color:#0073aa
384
- }
385
- .cb-switcher {
386
- display:inline-block;
387
- border:1px solid #eee;
388
- background-color:#fff;
389
- width:95px;
390
- height:35px;
391
- position:relative;
392
- -moz-border-radius:20px;
393
- -webkit-border-radius:20px;
394
- border-radius:20px;
395
- -moz-transition:background 0.4s ease;
396
- -o-transition:background 0.4s ease;
397
- -webkit-transition:background 0.4s ease;
398
- transition:background 0.4s ease
399
- }
400
- .cb-switcher, .cb-switcher * {
401
- cursor:pointer
402
- }
403
- .cb-switcher ::-moz-selection {
404
- background-color:transparent
405
- }
406
- .cb-switcher ::selection {
407
- background-color:transparent
408
- }
409
- .cb-switcher .cb-state {
410
- z-index:1;
411
- text-align:center;
412
- font-size:12px
413
- }
414
- .cb-switcher .cb-state, .cb-switcher:before {
415
- width:34px;
416
- height:34px;
417
- line-height:34px;
418
- position:absolute;
419
- left:0;
420
- top:-1px;
421
- -moz-border-radius:100%;
422
- -webkit-border-radius:100%;
423
- border-radius:100%;
424
- -moz-transition:all 0.4s ease;
425
- -o-transition:all 0.4s ease;
426
- -webkit-transition:all 0.4s ease;
427
- transition:all 0.4s ease
428
- }
429
- .cb-switcher:before {
430
- content:"";
431
- background-color:#eee;
432
- -moz-box-shadow:1px 1px 1px rgba(0, 0, 0, 0.1);
433
- -webkit-box-shadow:1px 1px 1px rgba(0, 0, 0, 0.1);
434
- box-shadow:1px 1px 1px rgba(0, 0, 0, 0.1)
435
- }
436
- .cb-switcher.checked {
437
- background-color:#00a0d2
438
- }
439
- .cb-switcher.checked .cb-state, .cb-switcher.checked:before {
440
- left:60px;
441
- color:#00a0d2
442
- }
443
- .cb-switcher.checked:before {
444
- background-color:#fff;
445
- -moz-box-shadow:-1px 1px 1px rgba(0, 0, 0, 0.1);
446
- -webkit-box-shadow:-1px 1px 1px rgba(0, 0, 0, 0.1);
447
- box-shadow:-1px 1px 1px rgba(0, 0, 0, 0.1)
448
- }
449
- .cb-switcher.checked .inner-switcher:before {
450
- border-top-color:#0073aa
451
- }
452
- .cb-switcher.checked .inner-switcher:after {
453
- border-bottom-color:#0073aa
454
- }
455
- .cb-switcher .inner-switcher:before, .cb-switcher .inner-switcher:after {
456
- content:"";
457
- position:absolute;
458
- left:50%;
459
- width:0;
460
- height:0;
461
- z-index:2;
462
- margin-left:-20px;
463
- border-left:20px solid transparent;
464
- border-right:20px solid transparent;
465
- -moz-transition:border 0.4s ease;
466
- -o-transition:border 0.4s ease;
467
- -webkit-transition:border 0.4s ease;
468
- transition:border 0.4s ease
469
- }
470
- .cb-switcher .inner-switcher:before {
471
- border-top:17px solid #fff;
472
- top:0
473
- }
474
- .cb-switcher .inner-switcher:after {
475
- border-bottom:17px solid #fff;
476
- bottom:0
477
- }
478
- .cb-state {
479
- color:#ccc;
480
- display:inline-block
481
- }
482
- .cb-switcher-group .cb-state {
483
- position:relative;
484
- top:7px
485
- }
486
  .is-hidden {
487
  display:none !important;
488
  visibility:hidden !important
25
  margin-bottom: 3px;
26
  }
27
 
28
+ .post-views-counter-settings select {
29
+ vertical-align: top;
30
+ }
31
+
32
+ .pvc_user_roles {
33
+ margin-top: 12px;
34
+ }
35
+
36
  .df-credits {
37
  float: right;
38
  width: 280px;
119
  width: auto;
120
  }
121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  .is-hidden {
123
  display:none !important;
124
  visibility:hidden !important
includes/dashboard.php CHANGED
@@ -238,8 +238,14 @@ class Post_Views_Counter_Dashboard {
238
  foreach ( $post_types as $id => $post_type ) {
239
  $post_type_obj = get_post_type_object( $post_type );
240
 
241
- $data['data']['datasets'][$id]['label'] = $post_type_obj->labels->name;
242
- $data['data']['datasets'][$id]['post_type'] = $post_type_obj->name;
 
 
 
 
 
 
243
  $data['data']['datasets'][$id]['data'] = array();
244
 
245
  // get month views
@@ -325,9 +331,15 @@ class Post_Views_Counter_Dashboard {
325
  foreach ( $post_types as $id => $post_type ) {
326
  $post_type_obj = get_post_type_object( $post_type );
327
 
328
- $data['data']['datasets'][$id]['label'] = $post_type_obj->labels->name;
329
- $data['data']['datasets'][$id]['post_type'] = $post_type_obj->name;
330
- $data['data']['datasets'][$id]['hidden'] = in_array( $post_type_obj->name, $user_options, true );
 
 
 
 
 
 
331
  $data['data']['datasets'][$id]['data'] = array();
332
 
333
  // get month views
238
  foreach ( $post_types as $id => $post_type ) {
239
  $post_type_obj = get_post_type_object( $post_type );
240
 
241
+ // unrecognized post type? (mainly from deactivated plugins)
242
+ if ( empty( $post_type_obj ) )
243
+ $label = __( 'Unknown', 'post-views-counter' ) . ' #' . $id;
244
+ else
245
+ $label = $post_type_obj->labels->name;
246
+
247
+ $data['data']['datasets'][$id]['label'] = $label;
248
+ $data['data']['datasets'][$id]['post_type'] = $post_type;
249
  $data['data']['datasets'][$id]['data'] = array();
250
 
251
  // get month views
331
  foreach ( $post_types as $id => $post_type ) {
332
  $post_type_obj = get_post_type_object( $post_type );
333
 
334
+ // unrecognized post type? (mainly from deactivated plugins)
335
+ if ( empty( $post_type_obj ) )
336
+ $label = __( 'Unknown', 'post-views-counter' ) . ' #' . $id;
337
+ else
338
+ $label = $post_type_obj->labels->name;
339
+
340
+ $data['data']['datasets'][$id]['label'] = $label;
341
+ $data['data']['datasets'][$id]['post_type'] = $post_type;
342
+ $data['data']['datasets'][$id]['hidden'] = in_array( $post_type, $user_options, true );
343
  $data['data']['datasets'][$id]['data'] = array();
344
 
345
  // get month views
includes/settings-api.php ADDED
@@ -0,0 +1,666 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Settings_API class.
8
+ *
9
+ * @class Post_Views_Counter_Settings_API
10
+ */
11
+ class Post_Views_Counter_Settings_API {
12
+
13
+ private $settings;
14
+ private $pages;
15
+ private $page_types;
16
+ private $prefix;
17
+ private $slug;
18
+ private $domain;
19
+ private $short;
20
+ private $object;
21
+ private $plugin;
22
+ private $plugin_url;
23
+
24
+ /**
25
+ * Class constructor.
26
+ *
27
+ * @return void
28
+ */
29
+ public function __construct( $args ) {
30
+ // set initial data
31
+ $this->prefix = $args['prefix'];
32
+ $this->domain = $args['domain'];
33
+
34
+ // empty slug?
35
+ if ( empty( $args['slug'] ) )
36
+ $this->slug = $args['domain'];
37
+ else
38
+ $this->slug = $args['slug'];
39
+
40
+ // empty short name?
41
+ if ( empty( $args['short'] ) ) {
42
+ $short = '';
43
+
44
+ // prepare short name based on prefix
45
+ $parts = explode( '_', $this->prefix );
46
+
47
+ foreach ( $parts as $string ) {
48
+ $short .= substr( $string, 0, 1 );
49
+ }
50
+
51
+ // set short name
52
+ $this->short = $short;
53
+ } else
54
+ $this->short = $args['short'];
55
+
56
+ $this->object = $args['object'];
57
+ $this->plugin = $args['plugin'];
58
+ $this->plugin_url = $args['plugin_url'];
59
+
60
+ // actions
61
+ add_action( 'admin_menu', [ $this, 'admin_menu_options' ], 11 );
62
+ add_action( 'admin_init', [ $this, 'register_settings' ], 11 );
63
+ }
64
+
65
+ /**
66
+ * Get prefix.
67
+ *
68
+ * @return string
69
+ */
70
+ public function get_prefix() {
71
+ return $this->prefix;
72
+ }
73
+
74
+ /**
75
+ * Get settings.
76
+ *
77
+ * @return array
78
+ */
79
+ public function get_settings() {
80
+ return $this->settings;
81
+ }
82
+
83
+ /**
84
+ * Add menu pages.
85
+ *
86
+ * @return void
87
+ */
88
+ public function admin_menu_options() {
89
+ $this->pages = apply_filters( $this->prefix . '_settings_pages', [] );
90
+ $types = [
91
+ 'page' => [],
92
+ 'subpage' => [],
93
+ 'settings_page' => []
94
+ ];
95
+
96
+ foreach ( $this->pages as $page => $data ) {
97
+ // skip invalid page types
98
+ if ( empty( $data['type'] ) || ! array_key_exists( $data['type'], $types ) )
99
+ continue;
100
+
101
+ // menu subpage?
102
+ if ( $data['type'] === 'subpage' ) {
103
+ add_submenu_page( $data['parent_slug'], $data['page_title'], $data['menu_title'], $data['capability'], $data['menu_slug'], ! empty( $data['callback'] ) ? $data['callback'] : [ $this, 'options_page' ] );
104
+
105
+ // add subpage type
106
+ $types['subpage'][$data['menu_slug']] = $page;
107
+ // menu settings page?
108
+ } elseif ( $data['type'] === 'settings_page' ) {
109
+ add_options_page( $data['page_title'], $data['menu_title'], $data['capability'], $data['menu_slug'], ! empty( $data['callback'] ) ? $data['callback'] : [ $this, 'options_page' ] );
110
+
111
+ // add settings type
112
+ $types['settings_page'][$data['menu_slug']] = $page;
113
+ }
114
+ }
115
+
116
+ // set page types
117
+ $this->page_types = $types;
118
+ }
119
+
120
+ /**
121
+ * Render settings.
122
+ *
123
+ * @return void
124
+ */
125
+ public function options_page() {
126
+ global $pagenow;
127
+
128
+ $valid_page = false;
129
+
130
+ // get current screen
131
+ $screen = get_current_screen();
132
+
133
+ // display sub level settings page?
134
+ if ( $pagenow === 'admin.php' && preg_match( '/^(?:toplevel|' . $this->prefix . ')_page_' . $this->prefix . '-(' . implode( '|', $this->page_types['subpage'] ) . ')-settings$/', $screen->base, $matches ) === 1 && ! empty( $matches[1] ) ) {
135
+ $valid_page = true;
136
+ $page_type = 'subpage';
137
+ $url_page = 'admin.php';
138
+ }
139
+
140
+ // display settings page?
141
+ if ( ! $valid_page && $pagenow === 'options-general.php' && preg_match( '/^(?:settings_page_)(' . implode( '|', array_keys( $this->page_types['settings_page'] ) ) . ')$/', $screen->base, $matches ) === 1 ) {
142
+ $valid_page = true;
143
+ $page_type = 'settings_page';
144
+ $url_page = 'options-general.php';
145
+ }
146
+
147
+ // skip invalid pages
148
+ if ( ! $valid_page )
149
+ return;
150
+
151
+ echo '
152
+ <div class="wrap">
153
+ <h2>' . esc_html( $this->settings[$matches[1]]['label'] ) . '</h2>';
154
+
155
+ // any tabs?
156
+ if ( array_key_exists( 'tabs', $this->pages[$this->page_types[$page_type][$matches[1]]] ) ) {
157
+ // get tabs
158
+ $tabs = $this->pages[$this->page_types[$page_type][$matches[1]]]['tabs'];
159
+
160
+ // reset tabs
161
+ reset( $tabs );
162
+
163
+ // get first default tab
164
+ $first_tab = key( $tabs );
165
+
166
+ // get current tab
167
+ $tab_key = ! empty( $_GET['tab'] ) && array_key_exists( $_GET['tab'], $tabs ) ? $_GET['tab'] : $first_tab;
168
+
169
+ echo '
170
+ <h2 class="nav-tab-wrapper">';
171
+
172
+ foreach ( $tabs as $key => $tab ) {
173
+ echo '
174
+ <a class="nav-tab ' . ( $tab_key === $key ? 'nav-tab-active' : '' ) . '" href="' . esc_url( admin_url( $url_page . '?page=' . $matches[1] . '&tab=' . $key ) ) . '">' . esc_html( $tab['label'] ) . '</a>';
175
+ }
176
+
177
+ echo '
178
+ </h2>';
179
+ }
180
+
181
+ // skip for internal options page
182
+ if ( $page_type !== 'settings_page' )
183
+ settings_errors();
184
+
185
+ echo '
186
+ <div class="' . $this->slug . '-settings">
187
+ <div class="df-credits">
188
+ <h3 class="hndle">' . $this->plugin . ' ' . $this->object->defaults['version'] . '</h3>
189
+ <div class="inside">
190
+ <h4 class="inner">' . __( 'Need support?', $this->domain ) . '</h4>
191
+ <p class="inner">' . sprintf( __( 'If you are having problems with this plugin, please browse it\'s <a href="%s" target="_blank">Documentation</a> or talk about them in the <a href="%s" target="_blank">Support forum</a>', $this->domain ), 'https://www.dfactory.eu/docs/' . $this->slug . '/?utm_source=' . $this->slug . '-settings&utm_medium=link&utm_campaign=docs', 'https://www.dfactory.eu/support/?utm_source=' . $this->slug . '-settings&utm_medium=link&utm_campaign=support' ) . '</p>
192
+ <hr />
193
+ <h4 class="inner">' . __( 'Do you like this plugin?', $this->domain ) . '</h4>
194
+ <p class="inner">' . sprintf( __( '<a href="%s" target="_blank">Rate it 5</a> on WordPress.org', $this->domain ), 'https://wordpress.org/support/plugin/' . $this->slug . '/reviews/?filter=5' ) . '<br />' .
195
+ sprintf( __( 'Blog about it & link to the <a href="%s" target="_blank">plugin page</a>.', $this->domain ), 'https://dfactory.eu/plugins/' . $this->slug . '/?utm_source=' . $this->slug . '-settings&utm_medium=link&utm_campaign=blog-about' ) . '<br />' .
196
+ sprintf( __( 'Check out our other <a href="%s" target="_blank">WordPress plugins</a>.', $this->domain ), 'https://dfactory.eu/plugins/?utm_source=' . $this->slug . '-settings&utm_medium=link&utm_campaign=other-plugins' ) . '
197
+ </p>
198
+ <hr />
199
+ <p class="df-link inner"><a href="http://www.dfactory.eu/?utm_source=' . $this->slug . '-settings&utm_medium=link&utm_campaign=created-by" target="_blank" title="Digital Factory"><img src="//pvc-53eb.kxcdn.com/df-black-sm.png" alt="Digital Factory"></a></p>
200
+ </div>
201
+ </div>
202
+ <form action="options.php" method="post">';
203
+
204
+ // any tabs?
205
+ if ( ! empty( $tab_key ) ) {
206
+ if ( ! empty( $tabs[$tab_key]['option_name'] ) )
207
+ $setting = $tabs[$tab_key]['option_name'];
208
+ else
209
+ $setting = $this->prefix . '_' . $tab_key . '_settings';
210
+ } else
211
+ $setting = $this->prefix . '_' . $matches[1] . '_settings';
212
+
213
+ settings_fields( $setting );
214
+ do_settings_sections( $setting );
215
+
216
+ echo '
217
+ <p class="submit">';
218
+
219
+ submit_button( '', 'primary', 'save_' . $setting, false );
220
+
221
+ echo ' ';
222
+
223
+ submit_button( __( 'Reset to defaults', $this->domain ), 'secondary reset_' . $setting . ' reset_' . $this->short . '_settings', 'reset_' . $setting, false );
224
+
225
+ echo '
226
+ </p>
227
+ </form>
228
+ </div>
229
+ <div class="clear"></div>
230
+ </div>';
231
+ }
232
+
233
+ /**
234
+ * Register settings.
235
+ *
236
+ * @return void
237
+ */
238
+ public function register_settings() {
239
+ $this->settings = apply_filters( $this->prefix . '_settings_data', [] );
240
+
241
+ // check settings
242
+ foreach ( $this->settings as $setting_id => $setting ) {
243
+ // tabs?
244
+ if ( is_array( $setting['option_name'] ) ) {
245
+ foreach ( $setting['option_name'] as $tab => $option_name ) {
246
+ $this->register_setting_fields( $tab, $setting, $option_name );
247
+ }
248
+ } else
249
+ $this->register_setting_fields( $setting_id, $setting );
250
+ }
251
+ }
252
+
253
+ public function register_setting_fields( $setting_id, $setting, $option_name = '' ) {
254
+ if ( empty( $option_name ) )
255
+ $option_name = $setting['option_name'];
256
+
257
+ // register setting
258
+ register_setting( $option_name, $option_name, ! empty( $setting['validate'] ) ? $setting['validate'] : [ $this, 'validate_settings' ] );
259
+
260
+ // register setting sections
261
+ if ( ! empty( $setting['sections'] ) ) {
262
+ foreach ( $setting['sections'] as $section_id => $section ) {
263
+ add_settings_section(
264
+ $section_id,
265
+ ! empty( $section['title'] ) ? esc_html( $section['title'] ) : '',
266
+ ! empty( $section['callback'] ) ? $section['callback'] : null,
267
+ ! empty( $section['page'] ) ? $section['page'] : $option_name
268
+ );
269
+ }
270
+ }
271
+
272
+ // register setting fields
273
+ if ( ! empty( $setting['fields'] ) ) {
274
+ foreach ( $setting['fields'] as $field_key => $field ) {
275
+ if ( ! empty( $field['tab'] ) && $field['tab'] !== $setting_id )
276
+ continue;
277
+
278
+ // set field ID
279
+ $field_id = implode( '_', [ $this->prefix, $setting_id, $field_key ] );
280
+
281
+ // skip rendering this field?
282
+ if ( ! empty( $field['skip_rendering'] ) )
283
+ continue;
284
+
285
+ add_settings_field(
286
+ $field_id,
287
+ ! empty( $field['title'] ) ? esc_html( $field['title'] ) : '',
288
+ [ $this, 'render_field' ],
289
+ $option_name,
290
+ ! empty( $field['section'] ) ? esc_attr( $field['section'] ) : '',
291
+ array_merge( $this->prepare_field_args( $field, $field_id, $field_key, $setting_id, $option_name ), $field )
292
+ );
293
+ }
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Prepare field arguments.
299
+ *
300
+ * @param array $args
301
+ * @return array
302
+ */
303
+ public function prepare_field_args( $field, $field_id, $field_key, $setting_id, $setting_name ) {
304
+ // get field type
305
+ $field_type = ! empty( $field['type'] ) ? $field['type'] : '';
306
+
307
+ return [
308
+ 'id' => $field_id,
309
+ 'name' => $setting_name . '[' . $field_key . ']',
310
+ 'class' => ! empty( $field['class'] ) ? $field['class'] : '',
311
+ 'type' => $field_type,
312
+ 'label' => ! empty( $field['label'] ) ? $field['label'] : '',
313
+ 'description' => ! empty( $field['description'] ) ? $field['description'] : '',
314
+ 'text' => ! empty( $field['text'] ) ? $field['text'] : '',
315
+ 'min' => ! empty( $field['min'] ) ? (int) $field['min'] : 0,
316
+ 'max' => ! empty( $field['max'] ) ? (int) $field['max'] : 0,
317
+ 'options' => ! empty( $field['options'] ) ? $field['options'] : [],
318
+ 'callback' => ! empty( $field['callback'] ) ? $field['callback'] : null,
319
+ 'validate' => ! empty( $field['validate'] ) ? $field['validate'] : null,
320
+ 'callback_args' => ! empty( $field['callback_args'] ) ? $field['callback_args'] : [],
321
+ 'default' => $field_type !== 'custom' ? $this->object->defaults[$setting_id][$field_key] : null,
322
+ 'value' => $field_type !== 'custom' ? $this->object->options[$setting_id][$field_key] : null
323
+ /*
324
+ after_field
325
+ before_field
326
+ */
327
+ ];
328
+ }
329
+
330
+ /**
331
+ * Render settings field.
332
+ *
333
+ * @param array $args
334
+ * @return void|string
335
+ */
336
+ public function render_field( $args ) {
337
+ if ( empty( $args ) || ! is_array( $args ) )
338
+ return;
339
+
340
+ $html = '<div id="' . $args['id'] . '_setting"' . ( ! empty( $args['class'] ) ? ' class="' . $args['class'] . '"' : '' ) . '>';
341
+
342
+ if ( ! empty ( $args['before_field'] ) )
343
+ $html .= $args['before_field'];
344
+
345
+ switch ( $args['type'] ) {
346
+ case 'boolean':
347
+ $html .= '<input type="hidden" name="' . esc_attr( $args['name'] ) . '" value="false" />';
348
+ $html .= '<label><input id="' . esc_attr( $args['id'] ) . '" type="checkbox" name="' . esc_attr( $args['name'] ) . '" value="true" ' . checked( (bool) $args['value'], true, false ) . ' ' . disabled( empty( $args['disabled'] ), false, false ) . ' />' . esc_html( $args['label'] ) . '</label>';
349
+ break;
350
+
351
+ case 'radio':
352
+ foreach ( $args['options'] as $key => $name ) {
353
+ $html .= '<label><input id="' . esc_attr( $args['id'] . '_' . $key ) . '" type="radio" name="' . esc_attr( $args['name'] ) . '" value="' . esc_attr( $key ) . '" ' . checked( $key, $args['value'], false ) . ' ' . disabled( empty( $args['disabled'] ), false, false ) . ' />' . esc_html( $name ) . '</label> ';
354
+ }
355
+ break;
356
+
357
+ case 'checkbox':
358
+ $display_type = ! empty( $args['display_type'] ) && in_array( $args['display_type'], [ 'horizontal', 'vertical' ], true ) ? $args['display_type'] : 'horizontal';
359
+
360
+ $html .= '<input type="hidden" name="' . esc_attr( $args['name'] ) . '" value="empty" />';
361
+
362
+ foreach ( $args['options'] as $key => $name ) {
363
+ $html .= '<label><input id="' . esc_attr( $args['id'] . '_' . $key ) . '" type="checkbox" name="' . esc_attr( $args['name'] ) . '[]" value="' . esc_attr( $key ) . '" ' . checked( in_array( $key, $args['value'] ), true, false ) . ' ' . disabled( empty( $args['disabled'] ), false, false ) . ' />' . esc_html( $name ) . '</label>' . ( $display_type === 'horizontal' ? ' ' : '<br />' );
364
+ }
365
+ break;
366
+
367
+ case 'select':
368
+ $html .= '<select id="' . esc_attr( $args['id'] ) . '" name="' . esc_attr( $args['name'] ) . '" />';
369
+
370
+ foreach ( $args['options'] as $key => $name ) {
371
+ $html .= '<option value="' . esc_attr( $key ) . '" ' . selected( $args['value'], $key, false ) . '>' . esc_html( $name ) . '</option>';
372
+ }
373
+
374
+ $html .= '</select>';
375
+ break;
376
+
377
+ case 'range':
378
+ $html .= '<input id="' . esc_attr( $args['id'] . '_slider' ) . '" type="range" name="' . esc_attr( $args['name'] ) . '" value="' . esc_attr( $args['value'] ) . '" min="' . esc_attr( $args['min'] ) . '" max="' . esc_attr( $args['max'] ) . '" oninput="this.form.' . esc_attr( $args['id'] ) . '_range.value = this.value" /><output class="' . esc_attr( $this->slug ) . '-range" name="' . esc_attr( $args['id'] ) . '_range">' . ( (int) $args['value'] ) . '</output>';
379
+ break;
380
+
381
+ case 'number':
382
+ $html .= ( ! empty( $args['prepend'] ) ? '<span>' . esc_html( $args['prepend'] ) . '</span> ' : '' );
383
+ $html .= '<input id="' . $args['id'] . '" type="text" value="' . esc_attr( $args['value'] ) . '" name="' . esc_attr( $args['name'] ) . '" />';
384
+ $html .= ( ! empty( $args['append'] ) ? ' <span>' . esc_html( $args['append'] ) . '</span>' : '' );
385
+ break;
386
+
387
+ case 'custom':
388
+ $html .= call_user_func( $args['callback'], $args );
389
+ break;
390
+
391
+ case 'info':
392
+ $html .= '<span' . ( ! empty( $args['subclass'] ) ? ' class="' . esc_attr( $args['subclass'] ) . '"' : '' ) . '>' . esc_html( $args['text'] ) . '</span>';
393
+ break;
394
+
395
+ case 'input':
396
+ default:
397
+ $html .= ( ! empty( $args['prepend'] ) ? '<span>' . esc_html( $args['prepend'] ) . '</span> ' : '' );
398
+ $html .= '<input id="' . $args['id'] . '"' . ( ! empty( $args['subclass'] ) ? ' class="' . esc_attr( $args['subclass'] ) . '"' : '' ) . ' type="text" value="' . esc_attr( $args['value'] ) . '" name="' . esc_attr( $args['name'] ) . '" />';
399
+ $html .= ( ! empty( $args['append'] ) ? ' <span>' . esc_html( $args['append'] ) . '</span>' : '' );
400
+ }
401
+
402
+ if ( ! empty ( $args['after_field'] ) )
403
+ $html .= $args['after_field'];
404
+
405
+ if ( ! empty ( $args['description'] ) )
406
+ $html .= '<p class="description">' . $args['description'] . '</p>';
407
+
408
+ $html .= '</div>';
409
+
410
+ if ( ! empty( $args['return'] ) )
411
+ return $html;
412
+ else
413
+ echo $html;
414
+ }
415
+
416
+ /**
417
+ * Validate settings field.
418
+ *
419
+ * @param mixed $value
420
+ * @param string $type
421
+ * @param array $args
422
+ * @return mixed
423
+ */
424
+ public function validate_field( $value = null, $type = '', $args = [] ) {
425
+ if ( is_null( $value ) )
426
+ return null;
427
+
428
+ switch ( $type ) {
429
+ case 'boolean':
430
+ // possible value: 'true' or 'false'
431
+ $value = ( $value === 'true' || $value === true );
432
+ break;
433
+
434
+ case 'radio':
435
+ $value = is_array( $value ) ? false : sanitize_text_field( $value );
436
+ break;
437
+
438
+ case 'checkbox':
439
+ // possible value: 'empty' or array
440
+ if ( $value === 'empty' )
441
+ $value = [];
442
+ else {
443
+ if ( is_array( $value ) && ! empty( $value ) ) {
444
+ $value = array_map( 'sanitize_text_field', $value );
445
+ $values = [];
446
+
447
+ foreach ( $value as $single_value ) {
448
+ if ( array_key_exists( $single_value, $args['options'] ) )
449
+ $values[] = $single_value;
450
+ }
451
+
452
+ $value = $values;
453
+ } else
454
+ $value = [];
455
+ }
456
+ break;
457
+
458
+ case 'number':
459
+ $value = (int) $value;
460
+
461
+ // is value lower than?
462
+ if ( isset( $args['min'] ) && $value < $args['min'] )
463
+ $value = $args['min'];
464
+
465
+ // is value greater than?
466
+ if ( isset( $args['max'] ) && $value > $args['max'] )
467
+ $value = $args['max'];
468
+ break;
469
+
470
+ case 'info':
471
+ $value = '';
472
+ break;
473
+
474
+ case 'custom':
475
+ // do nothing
476
+ break;
477
+
478
+ case 'input':
479
+ case 'select':
480
+ default:
481
+ $value = is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : sanitize_text_field( $value );
482
+ break;
483
+ }
484
+
485
+ return stripslashes_deep( $value );
486
+ }
487
+
488
+ /**
489
+ * Validate settings.
490
+ *
491
+ * @param array $input
492
+ * @return array
493
+ */
494
+ public function validate_settings( $input ) {
495
+ // check capability
496
+ //@TODO dodac tu capability
497
+ // if ( ! current_user_can( apply_filters( 'rl_lightbox_settings_capability', 'edit_lightbox_settings' ) ) )
498
+ // return $input;
499
+
500
+ // check capability
501
+ if ( ! current_user_can( 'manage_options' ) )
502
+ return $input;
503
+
504
+ // check option page
505
+ if ( empty( $_POST['option_page'] ) )
506
+ return $input;
507
+
508
+ // try to get setting name and ID
509
+ foreach ( $this->settings as $id => $setting ) {
510
+ // tabs?
511
+ if ( is_array( $setting['option_name'] ) ) {
512
+ foreach ( $setting['option_name'] as $tab => $option_name ) {
513
+ // found valid setting?
514
+ if ( $option_name === $_POST['option_page'] ) {
515
+ // assign setting ID
516
+ $setting_id = $tab;
517
+
518
+ // assign setting name
519
+ $setting_name = $option_name;
520
+
521
+ // assign setting key
522
+ $setting_key = $id;
523
+
524
+ // already found setting, no need to check the rest
525
+ break 2;
526
+ }
527
+ }
528
+ } else {
529
+ // found valid setting?
530
+ if ( $setting['option_name'] === $_POST['option_page'] ) {
531
+ // assign setting ID and key
532
+ $setting_key = $setting_id = $id;
533
+
534
+ // assign setting name
535
+ $setting_name = $setting['option_name'];
536
+
537
+ // already found setting, no need to check the rest
538
+ break;
539
+ }
540
+ }
541
+ }
542
+
543
+ // check setting id, no need to check $setting_name since it was at the same stage
544
+ if ( empty( $setting_id ) )
545
+ return $input;
546
+
547
+ // save settings
548
+ if ( isset( $_POST['save_' . $setting_name] ) ) {
549
+ $input = $this->validate_input_settings( $setting_id, $setting_key, $input );
550
+
551
+ add_settings_error( $setting_name, 'settings_saved', __( 'Settings saved.', $this->domain ), 'updated' );
552
+ // reset settings
553
+ } elseif ( isset( $_POST['reset_' . $setting_name] ) ) {
554
+ // get default values
555
+ $input = $this->object->defaults[$setting_id];
556
+
557
+ // check custom reset functions
558
+ if ( ! empty( $this->settings[$setting_key]['fields'] ) ) {
559
+ foreach ( $this->settings[$setting_key]['fields'] as $field_id => $field ) {
560
+ // skip invalid tab field if any
561
+ if ( ! empty( $field['tab'] ) && $field['tab'] !== $setting_id )
562
+ continue;
563
+
564
+ // custom reset function?
565
+ if ( ! empty( $field['reset'] ) ) {
566
+ // valid function? no need to check "else" since all default values are already set
567
+ if ( $this->callback_function_exists( $field['reset'] ) ) {
568
+ if ( $field['type'] === 'custom' )
569
+ $input = call_user_func( $field['reset'], $input, $field );
570
+ else
571
+ $input[$field_id] = call_user_func( $field['reset'], $input[$field_id], $field );
572
+ }
573
+ }
574
+ }
575
+ }
576
+
577
+ add_settings_error( $setting_name, 'settings_restored', __( 'Settings restored to defaults.', $this->domain ), 'updated' );
578
+ }
579
+
580
+ return $input;
581
+ }
582
+
583
+ /**
584
+ * Validate input settings.
585
+ *
586
+ * @param string $setting_id
587
+ * @param array $input
588
+ * @return array
589
+ */
590
+ public function validate_input_settings( $setting_id, $setting_key, $input ) {
591
+ if ( ! empty( $this->settings[$setting_key]['fields'] ) ) {
592
+ foreach ( $this->settings[$setting_key]['fields'] as $field_id => $field ) {
593
+ // skip saving this field?
594
+ if ( ! empty( $field['skip_saving'] ) )
595
+ continue;
596
+
597
+ // skip invalid tab field if any
598
+ if ( ! empty( $field['tab'] ) && $field['tab'] !== $setting_id )
599
+ continue;
600
+
601
+ // custom validate function?
602
+ if ( ! empty( $field['validate'] ) ) {
603
+ // valid function?
604
+ if ( $this->callback_function_exists( $field['validate'] ) ) {
605
+ if ( $field['type'] === 'custom' )
606
+ $input = call_user_func( $field['validate'], $input, $field );
607
+ else
608
+ $input[$field_id] = isset( $input[$field_id] ) ? call_user_func( $field['validate'], $input[$field_id], $field ) : $this->object->defaults[$setting_id][$field_id];
609
+ } else
610
+ $input[$field_id] = $this->object->defaults[$setting_id][$field_id];
611
+ } else {
612
+ // field data?
613
+ if ( isset( $input[$field_id] ) )
614
+ $input[$field_id] = $this->validate_field( $input[$field_id], $field['type'], $field );
615
+ else
616
+ $input[$field_id] = $this->object->defaults[$setting_id][$field_id];
617
+ }
618
+ }
619
+ }
620
+
621
+ return $input;
622
+ }
623
+
624
+ /**
625
+ * Check whether callback is a valid function.
626
+ *
627
+ * @param string|array $callback
628
+ * @return bool
629
+ */
630
+ public function callback_function_exists( $callback ) {
631
+ // function as array?
632
+ if ( is_array( $callback ) ) {
633
+ list( $object, $function ) = $callback;
634
+
635
+ // check function existence
636
+ $function_exists = method_exists( $object, $function );
637
+ // function as string?
638
+ } elseif ( is_string( $callback ) ) {
639
+ // check function existence
640
+ $function_exists = function_exists( $callback );
641
+ } else
642
+ $function_exists = false;
643
+
644
+ return $function_exists;
645
+ }
646
+
647
+ /**
648
+ * Get value based on minimum and maximum.
649
+ *
650
+ * @param array $data
651
+ * @param string $setting_name
652
+ * @param int $default
653
+ * @param int $min
654
+ * @param int $max
655
+ * @return void
656
+ */
657
+ public function get_int_value( $data, $setting_name, $default, $min, $max ) {
658
+ // check existence of value
659
+ $value = array_key_exists( $setting_name, $data ) ? (int) $data[$setting_name] : $default;
660
+
661
+ if ( $value > $max || $value < $min )
662
+ $value = $default;
663
+
664
+ return $value;
665
+ }
666
+ }
includes/settings.php CHANGED
@@ -10,112 +10,78 @@ if ( ! defined( 'ABSPATH' ) )
10
  */
11
  class Post_Views_Counter_Settings {
12
 
13
- private $tabs;
14
- private $choices;
15
- private $modes;
16
  private $time_types;
17
  private $groups;
18
  private $user_roles;
19
- private $positions;
20
- private $display_styles;
21
  public $post_types;
22
  public $page_types;
23
 
24
  public function __construct() {
25
  // actions
26
- add_action( 'admin_init', array( $this, 'register_settings' ) );
27
- add_action( 'admin_menu', array( $this, 'admin_menu_options' ) );
28
- add_action( 'after_setup_theme', array( $this, 'load_defaults' ) );
29
- add_action( 'wp_loaded', array( $this, 'load_post_types' ) );
 
 
30
  }
31
 
32
  /**
33
  * Load default settings.
 
 
34
  */
35
  public function load_defaults() {
36
  if ( ! is_admin() )
37
  return;
38
 
39
- $this->modes = array(
40
- 'php' => __( 'PHP', 'post-views-counter' ),
41
- 'js' => __( 'JavaScript', 'post-views-counter' ),
42
- 'ajax' => __( 'Fast AJAX', 'post-views-counter' )
43
- );
44
-
45
- if ( function_exists( 'register_rest_route' ) )
46
- $this->modes['rest_api'] = __( 'REST API', 'post-views-counter' );
47
-
48
- $this->time_types = array(
49
- 'minutes' => __( 'minutes', 'post-views-counter' ),
50
- 'hours' => __( 'hours', 'post-views-counter' ),
51
- 'days' => __( 'days', 'post-views-counter' ),
52
- 'weeks' => __( 'weeks', 'post-views-counter' ),
53
- 'months' => __( 'months', 'post-views-counter' ),
54
- 'years' => __( 'years', 'post-views-counter' )
55
- );
56
-
57
- $this->groups = array(
58
- 'robots' => __( 'robots', 'post-views-counter' ),
59
- 'users' => __( 'logged in users', 'post-views-counter' ),
60
- 'guests' => __( 'guests', 'post-views-counter' ),
61
- 'roles' => __( 'selected user roles', 'post-views-counter' )
62
- );
63
-
64
- $this->positions = array(
65
- 'before' => __( 'before the content', 'post-views-counter' ),
66
- 'after' => __( 'after the content', 'post-views-counter' ),
67
- 'manual' => __( 'manual', 'post-views-counter' )
68
- );
69
-
70
- $this->display_styles = array(
71
- 'icon' => __( 'icon', 'post-views-counter' ),
72
- 'text' => __( 'label', 'post-views-counter' )
73
- );
74
-
75
- $this->tabs = array(
76
- 'general' => array(
77
- 'name' => __( 'General', 'post-views-counter' ),
78
- 'key' => 'post_views_counter_settings_general',
79
- 'submit' => 'save_pvc_general',
80
- 'reset' => 'reset_pvc_general'
81
- ),
82
- 'display' => array(
83
- 'name' => __( 'Display', 'post-views-counter' ),
84
- 'key' => 'post_views_counter_settings_display',
85
- 'submit' => 'save_pvc_display',
86
- 'reset' => 'reset_pvc_display'
87
- )
88
- );
89
 
90
  $this->user_roles = $this->get_user_roles();
91
 
92
  $this->page_types = apply_filters(
93
  'pvc_page_types_display_options',
94
- array(
95
- 'home' => __( 'Home', 'post-views-counter' ),
96
- 'archive' => __( 'Archives', 'post-views-counter' ),
97
- 'singular' => __( 'Single pages', 'post-views-counter' ),
98
- 'search' => __( 'Search results', 'post-views-counter' ),
99
- )
100
  );
101
  }
102
 
103
  /**
104
  * Get post types avaiable for counting.
 
 
105
  */
106
  public function load_post_types() {
107
  if ( ! is_admin() )
108
  return;
109
 
110
- $post_types = array();
111
 
112
  // built in public post types
113
- foreach ( get_post_types( array( '_builtin' => true, 'public' => true ), 'objects', 'and' ) as $key => $post_type ) {
114
  $post_types[$key] = $post_type->labels->name;
115
  }
116
 
117
  // public custom post types
118
- foreach ( get_post_types( array( '_builtin' => false, 'public' => true ), 'objects', 'and' ) as $key => $post_type ) {
119
  $post_types[$key] = $post_type->labels->name;
120
  }
121
 
@@ -123,7 +89,7 @@ class Post_Views_Counter_Settings {
123
  if ( class_exists( 'bbPress' ) && isset( $post_types['reply'] ) )
124
  unset( $post_types['reply'] );
125
 
126
- $post_types = apply_filters( 'pvc_available_post_types', $post_types );
127
 
128
  // sort post types alphabetically with their keys
129
  asort( $post_types, SORT_STRING );
@@ -140,7 +106,7 @@ class Post_Views_Counter_Settings {
140
  public function get_user_roles() {
141
  global $wp_roles;
142
 
143
- $roles = array();
144
 
145
  foreach ( apply_filters( 'editable_roles', $wp_roles->roles ) as $role => $details ) {
146
  $roles[$role] = translate_user_role( $details['name'] );
@@ -152,687 +118,799 @@ class Post_Views_Counter_Settings {
152
  }
153
 
154
  /**
155
- * Add options page.
156
  *
157
- * @return void
 
158
  */
159
- public function admin_menu_options() {
160
- add_options_page( __( 'Post Views Counter', 'post-views-counter' ), __( 'Post Views Counter', 'post-views-counter' ), 'manage_options', 'post-views-counter', array( $this, 'options_page' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  }
162
 
163
  /**
164
- * Options page callback.
165
  *
166
- * @return void
 
167
  */
168
- public function options_page() {
169
- $tab_key = (isset( $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : 'general');
170
-
171
- echo '
172
- <div class="wrap">
173
- <h2>' . __( 'Post Views Counter', 'post-views-counter' ) . '</h2>
174
- <h2 class="nav-tab-wrapper">';
175
-
176
- foreach ( $this->tabs as $key => $name ) {
177
- echo '
178
- <a class="nav-tab ' . ($tab_key == $key ? 'nav-tab-active' : '') . '" href="' . esc_url( admin_url( 'options-general.php?page=post-views-counter&tab=' . $key ) ) . '">' . $name['name'] . '</a>';
179
- }
180
-
181
- echo '
182
- </h2>
183
- <div class="post-views-counter-settings">
184
- <div class="df-credits">
185
- <h3 class="hndle">' . __( 'Post Views Counter', 'post-views-counter' ) . ' ' . Post_Views_Counter()->defaults['version'] . '</h3>
186
- <div class="inside">
187
- <h4 class="inner">' . __( 'Need support?', 'post-views-counter' ) . '</h4>
188
- <p class="inner">' . sprintf( __( 'If you are having problems with this plugin, please browse it\'s <a href="%s" target="_blank">Documentation</a> or talk about them in the <a href="%s" target="_blank">Support forum</a>', 'post-views-counter' ), 'https://www.dfactory.eu/docs/post-views-counter/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=docs', 'https://www.dfactory.eu/support/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=support' ) . '</p>
189
- <hr />
190
- <h4 class="inner">' . __( 'Do you like this plugin?', 'post-views-counter' ) . '</h4>
191
- <p class="inner">' . sprintf( __( '<a href="%s" target="_blank">Rate it 5</a> on WordPress.org', 'post-views-counter' ), 'https://wordpress.org/support/plugin/post-views-counter/reviews/?filter=5' ) . '<br />' .
192
- sprintf( __( 'Blog about it & link to the <a href="%s" target="_blank">plugin page</a>.', 'post-views-counter' ), 'https://dfactory.eu/plugins/post-views-counter/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=blog-about' ) . '<br />' .
193
- sprintf( __( 'Check out our other <a href="%s" target="_blank">WordPress plugins</a>.', 'post-views-counter' ), 'https://dfactory.eu/plugins/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=other-plugins' ) . '
194
- </p>
195
- <hr />
196
- <p class="df-link inner"><a href="http://www.dfactory.eu/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=created-by" target="_blank" title="Digital Factory"><img src="//pvc-53eb.kxcdn.com/df-black-sm.png' . '" alt="Digital Factory" /></a></p>
197
- </div>
198
- </div>
199
- <form action="options.php" method="post">';
200
-
201
- wp_nonce_field( 'update-options' );
202
- settings_fields( $this->tabs[$tab_key]['key'] );
203
- do_settings_sections( $this->tabs[$tab_key]['key'] );
204
-
205
- echo '
206
- <p class="submit">';
207
-
208
- submit_button( '', 'primary', $this->tabs[$tab_key]['submit'], false );
209
-
210
- echo ' ';
211
-
212
- submit_button( __( 'Reset to defaults', 'post-views-counter' ), 'secondary reset_pvc_settings', $this->tabs[$tab_key]['reset'], false );
213
-
214
- echo '
215
- </p>
216
- </form>
217
- </div>
218
- <div class="clear"></div>
219
- </div>';
220
  }
221
 
222
  /**
223
- * Register settings callback.
 
 
 
224
  */
225
- public function register_settings() {
226
- // general options
227
- register_setting( 'post_views_counter_settings_general', 'post_views_counter_settings_general', array( $this, 'validate_settings' ) );
228
- add_settings_section( 'post_views_counter_settings_general', __( 'General settings', 'post-views-counter' ), '', 'post_views_counter_settings_general' );
229
- add_settings_field( 'pvc_post_types_count', __( 'Post Types Count', 'post-views-counter' ), array( $this, 'post_types_count' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
230
- add_settings_field( 'pvc_counter_mode', __( 'Counter Mode', 'post-views-counter' ), array( $this, 'counter_mode' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
231
- add_settings_field( 'pvc_post_views_column', __( 'Post Views Column', 'post-views-counter' ), array( $this, 'post_views_column' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
232
- add_settings_field( 'pvc_restrict_edit_views', __( 'Restrict Edit', 'post-views-counter' ), array( $this, 'restrict_edit_views' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
233
- add_settings_field( 'pvc_time_between_counts', __( 'Count Interval', 'post-views-counter' ), array( $this, 'time_between_counts' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
234
- add_settings_field( 'pvc_reset_counts', __( 'Reset Data Interval', 'post-views-counter' ), array( $this, 'reset_counts' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
235
- add_settings_field( 'pvc_flush_interval', __( 'Flush Object Cache Interval', 'post-views-counter' ), array( $this, 'flush_interval' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
236
- add_settings_field( 'pvc_exclude', __( 'Exclude Visitors', 'post-views-counter' ), array( $this, 'exclude' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
237
- add_settings_field( 'pvc_exclude_ips', __( 'Exclude IPs', 'post-views-counter' ), array( $this, 'exclude_ips' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
238
- add_settings_field( 'pvc_strict_counts', __( 'Strict counts', 'post-views-counter' ), array( $this, 'strict_counts' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
239
- add_settings_field( 'pvc_wp_postviews', __( 'Tools', 'post-views-counter' ), array( $this, 'wp_postviews' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
240
- add_settings_field( 'pvc_deactivation_delete', __( 'Deactivation', 'post-views-counter' ), array( $this, 'deactivation_delete' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
241
-
242
- // display options
243
- register_setting( 'post_views_counter_settings_display', 'post_views_counter_settings_display', array( $this, 'validate_settings' ) );
244
- add_settings_section( 'post_views_counter_settings_display', __( 'Display settings', 'post-views-counter' ), '', 'post_views_counter_settings_display' );
245
- add_settings_field( 'pvc_post_views_label', __( 'Post Views Label', 'post-views-counter' ), array( $this, 'post_views_label' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
246
- add_settings_field( 'pvc_post_types_display', __( 'Post Type', 'post-views-counter' ), array( $this, 'post_types_display' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
247
- add_settings_field( 'pvc_page_types_display', __( 'Page Type', 'post-views-counter' ), array( $this, 'page_types_display' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
248
- add_settings_field( 'pvc_restrict_display', __( 'User Type', 'post-views-counter' ), array( $this, 'restrict_display' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
249
- add_settings_field( 'pvc_position', __( 'Position', 'post-views-counter' ), array( $this, 'position' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
250
- add_settings_field( 'pvc_display_style', __( 'Display Style', 'post-views-counter' ), array( $this, 'display_style' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
251
- add_settings_field( 'pvc_icon_class', __( 'Icon Class', 'post-views-counter' ), array( $this, 'icon_class' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
252
- add_settings_field( 'pvc_toolbar_statistics', __( 'Toolbar Chart', 'post-views-counter' ), array( $this, 'toolbar_statistics' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
253
- }
254
 
255
- /**
256
- * Post views label option.
257
- */
258
- public function post_views_label() {
259
- echo '
260
- <div id="pvc_post_views_label">
261
- <fieldset>
262
- <input type="text" class="large-text" name="post_views_counter_settings_display[label]" value="' . esc_attr( Post_Views_Counter()->options['display']['label'] ) . '" />
263
- <p class="description">' . __( 'Enter the label for the post views counter field.', 'post-views-counter' ) . '</p>
264
- </fieldset>
265
- </div>';
266
- }
267
 
268
- /**
269
- * Post types to count option.
270
- */
271
- public function post_types_count() {
272
- echo '
273
- <div id="pvc_post_types_count">';
274
 
275
- foreach ( $this->post_types as $post_type => $post_type_name ) {
276
- echo '
277
- <label><input id="pvc_post_types_count-' . esc_attr( $post_type ) . '" type="checkbox" name="post_views_counter_settings_general[post_types_count][' . esc_attr( $post_type ) . ']" value="1" ' . checked( in_array( $post_type, Post_Views_Counter()->options['general']['post_types_count'], true ), true, false ) . ' />' . esc_html( $post_type_name ) . ' </label>';
278
- }
279
 
280
- echo '
281
- <p class="description">' . __( 'Select post types for which post views will be counted.', 'post-views-counter' ) . '</p>
282
- </div>';
283
- }
284
 
285
- /**
286
- * Post types to display option.
287
- */
288
- public function post_types_display() {
289
- echo '
290
- <div id="pvc_post_types_display">';
 
 
 
 
 
 
 
 
 
291
 
292
- foreach ( $this->post_types as $post_type => $post_type_name ) {
293
- echo '
294
- <label><input id="pvc_post_types_display-' . esc_attr( $post_type ) . '" type="checkbox" name="post_views_counter_settings_display[post_types_display][' . esc_attr( $post_type ) . ']" value="1" ' . checked( in_array( $post_type, Post_Views_Counter()->options['display']['post_types_display'], true ), true, false ) . ' />' . esc_html( $post_type_name ) . '</label>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  }
296
 
297
- echo '
298
- <p class="description">' . __( 'Select post types for which the views count will be displayed.', 'post-views-counter' ) . '</p>
299
- </div>';
300
  }
301
 
302
  /**
303
- * Counter mode option.
 
 
 
 
304
  */
305
- public function counter_mode() {
306
- echo '
307
- <div id="pvc_counter_mode">';
308
 
309
- foreach ( $this->modes as $key => $value ) {
310
- $key = esc_attr( $key );
311
 
312
- echo '
313
- <label><input type="radio" name="post_views_counter_settings_general[counter_mode]" value="' . $key . '" ' . checked( $key, Post_Views_Counter()->options['general']['counter_mode'], false ) . ' />' . esc_html( $value ) . '</label>';
314
- }
315
 
316
- echo '
317
- <p class="description">' . __( 'Select the method of collecting post views data. If you are using any of the caching plugins select Javascript or REST API (if available).', 'post-views-counter' ) . '<br />' . __( 'Optionally try the Fast AJAX experimental method, usually 10+ times faster than Javascript or REST API.', 'post-views-counter' ) . '</p>
318
- </div>';
 
319
  }
320
 
321
  /**
322
- * Post views column option.
 
 
 
 
323
  */
324
- public function post_views_column() {
325
- echo '
326
- <div id="pvc_post_views_column">
327
- <label><input id="pvc-post-views-column-enable" type="checkbox" name="post_views_counter_settings_general[post_views_column]" value="1" ' . checked( true, Post_Views_Counter()->options['general']['post_views_column'], false ) . ' />' . __( 'Enable to display post views count column for each of the selected post types.', 'post-views-counter' ) . '</label>
328
- </div>';
329
  }
330
 
331
  /**
332
- * Time between counts option.
 
 
 
333
  */
334
- public function time_between_counts() {
335
- echo '
336
- <div id="pvc_time_between_counts">
337
- <input size="4" type="text" name="post_views_counter_settings_general[time_between_counts][number]" value="' . esc_attr( Post_Views_Counter()->options['general']['time_between_counts']['number'] ) . '" />
338
- <select class="pvc-chosen-short" name="post_views_counter_settings_general[time_between_counts][type]">';
339
 
340
- foreach ( $this->time_types as $type => $type_name ) {
341
- echo '
342
- <option value="' . esc_attr( $type ) . '" ' . selected( $type, Post_Views_Counter()->options['general']['time_between_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
 
 
 
343
  }
344
 
345
- echo '
346
- </select>
347
- <p class="description">' . __( 'Enter the time between single user visit count.', 'post-views-counter' ) . '</p>
348
- </div>';
349
  }
350
 
351
  /**
352
- * Reset counts option.
 
 
 
 
353
  */
354
- public function reset_counts() {
355
- echo '
356
- <div id="pvc_reset_counts">
357
- <input size="4" type="text" name="post_views_counter_settings_general[reset_counts][number]" value="' . esc_attr( Post_Views_Counter()->options['general']['reset_counts']['number'] ) . '" />
358
- <select class="pvc-chosen-short" name="post_views_counter_settings_general[reset_counts][type]">';
359
 
360
- foreach ( array_slice( $this->time_types, 2, null, true ) as $type => $type_name ) {
361
- echo '
362
- <option value="' . esc_attr( $type ) . '" ' . selected( $type, Post_Views_Counter()->options['general']['reset_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
 
363
  }
364
 
365
- echo '
366
- </select>
367
- <p class="description">' . __( 'Delete single day post views data older than specified above. Enter 0 (number zero) if you want to preserve your data regardless of its age.', 'post-views-counter' ) . '</p>
368
- </div>';
 
 
 
 
 
 
 
369
  }
370
 
371
  /**
372
- * Flush interval option.
 
 
 
373
  */
374
- public function flush_interval() {
375
- echo '
376
- <div id="pvc_flush_interval">
377
- <input size="4" type="text" name="post_views_counter_settings_general[flush_interval][number]" value="' . esc_attr( Post_Views_Counter()->options['general']['flush_interval']['number'] ) . '" />
378
- <select class="pvc-chosen-short" name="post_views_counter_settings_general[flush_interval][type]">';
 
 
379
 
380
  foreach ( $this->time_types as $type => $type_name ) {
381
- echo '
382
- <option value="' . esc_attr( $type ) . '" ' . selected( $type, Post_Views_Counter()->options['general']['flush_interval']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
383
  }
384
 
385
- echo '
386
- </select>
387
- <p class="description">' . __( 'How often to flush cached view counts from the object cache into the database. This feature is used only if a persistent object cache is detected and the interval is greater than 0 (number zero). 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.<br /><strong>Notice:</strong> Potential data loss may occur if the object cache is cleared/unavailable for the duration of the interval.', 'post-views-counter' ) . '</p>
388
- </div>';
 
389
  }
390
 
391
  /**
392
- * Exlude user groups option.
 
 
 
 
393
  */
394
- public function exclude() {
395
- echo '
396
- <div id="pvc_exclude">
397
- <fieldset>';
398
 
399
- foreach ( $this->groups as $type => $type_name ) {
400
- echo '
401
- <label><input id="pvc_exclude-' . $type . '" type="checkbox" name="post_views_counter_settings_general[exclude][groups][' . $type . ']" value="1" ' . checked( in_array( $type, Post_Views_Counter()->options['general']['exclude']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
402
- }
403
 
404
- echo '
405
- <p class="description">' . __( 'Use it exclude specific user groups from post views count.', 'post-views-counter' ) . '</p>
406
- <div class="pvc_user_roles"' . (in_array( 'roles', Post_Views_Counter()->options['general']['exclude']['groups'], true ) ? '' : ' style="display: none;"') . '>';
407
 
408
- foreach ( $this->user_roles as $role => $role_name ) {
409
- echo '
410
- <label><input type="checkbox" name="post_views_counter_settings_general[exclude][roles][' . $role . ']" value="1" ' . checked( in_array( $role, Post_Views_Counter()->options['general']['exclude']['roles'], true ), true, false ) . '>' . esc_html( $role_name ) . '</label>';
411
- }
412
 
413
- echo '
414
- <p class="description">' . __( 'Use it exclude specific user roles from post views count.', 'post-views-counter' ) . '</p>
415
- </div>
416
- </fieldset>
417
- </div>';
418
  }
419
 
420
  /**
421
- * Exclude IPs option.
 
 
 
422
  */
423
- public function exclude_ips() {
424
- // lovely php 5.2 limitations
425
- $ips = Post_Views_Counter()->options['general']['exclude_ips'];
426
 
427
- echo '
428
- <div id="pvc_exclude_ips">';
 
429
 
430
- if ( ! empty( $ips ) ) {
431
- foreach ( $ips as $key => $ip ) {
432
- echo '
433
- <div class="ip-box">
434
- <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_attr__( 'Remove', 'post-views-counter' ) . '</a>
435
- </div>';
436
- }
437
- } else {
438
- echo '
439
- <div class="ip-box">
440
- <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' ) . '">' . esc_attr__( 'Remove', 'post-views-counter' ) . '</a>
441
- </div>';
442
  }
443
 
444
- echo '
445
- <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>
446
- <p class="description">' . __( 'Enter the IP addresses to be excluded from post views count.', 'post-views-counter' ) . '</p>
447
- </div>';
448
- }
449
 
450
- /**
451
- * Strict counts option.
452
- */
453
- public function strict_counts() {
454
- echo '
455
- <div id="pvc_strict_counts">
456
- <label><input id="pvc-strict-counts" type="checkbox" name="post_views_counter_settings_general[strict_counts]" value="1" ' . checked( true, Post_Views_Counter()->options['general']['strict_counts'], false ) . ' />' . __( 'Enable to prevent bypassing the counts interval (for e.g. using incognito browser window or by clearing cookies).', 'post-views-counter' ) . '</label>
457
- </div>';
458
  }
459
 
460
  /**
461
- * WP-PostViews import option.
 
 
 
 
462
  */
463
- public function wp_postviews() {
464
- echo '
465
- <div id="pvc_wp_postviews">
466
- <fieldset>
467
- <input type="submit" class="button button-secondary" name="post_views_counter_import_wp_postviews" value="' . __( 'Import views', 'post-views-counter' ) . '"/> <label><input id="pvc-wp-postviews" type="checkbox" name="post_views_counter_import_wp_postviews_override" value="1" />' . __( 'Override existing views data.', 'post-views-counter' ) . '</label>
468
- <p class="description">' . __( 'Import post views data from WP-PostViews plugin.', 'post-views-counter' ) . '</p>
469
- <br /><input type="submit" class="button button-secondary" name="post_views_counter_reset_views" value="' . __( 'Delete views', 'post-views-counter' ) . '"/>
470
- <p class="description">' . __( 'Delete ALL the existing post views data.', 'post-views-counter' ) . '</p>
471
- </fieldset>
472
- </div>';
473
- }
474
 
475
- /**
476
- * Limit views edit to admins.
477
- */
478
- public function restrict_edit_views() {
479
- echo '
480
- <div id="pvc_restrict_edit_views">
481
- <label><input type="checkbox" name="post_views_counter_settings_general[restrict_edit_views]" value="1" ' . checked( true, Post_Views_Counter()->options['general']['restrict_edit_views'], false ) . ' />' . __( 'Enable to restrict post views editing to admins only.', 'post-views-counter' ) . '</label>
482
- </div>';
483
- }
484
 
485
- /**
486
- * Plugin deactivation option.
487
- */
488
- public function deactivation_delete() {
489
- echo '
490
- <div id="pvc_deactivation_delete">
491
- <label><input type="checkbox" name="post_views_counter_settings_general[deactivation_delete]" value="1" ' . checked( true, Post_Views_Counter()->options['general']['deactivation_delete'], false ) . ' />' . __( 'Enable to delete all plugin data on deactivation.', 'post-views-counter' ) . '</label>
492
- </div>';
493
- }
494
 
495
- /**
496
- * Visibility option.
497
- */
498
- public function page_types_display() {
499
- echo '
500
- <div id="pvc_post_types_display">';
501
 
502
- foreach ( $this->page_types as $key => $label ) {
503
- echo '
504
- <label><input id="pvc_page_types_display-' . esc_attr( $key ) . '" type="checkbox" name="post_views_counter_settings_display[page_types_display][' . esc_attr( $key ) . ']" value="1" ' . checked( in_array( $key, Post_Views_Counter()->options['display']['page_types_display'], true ), true, false ) . ' />' . esc_html( $label ) . '</label>';
505
- }
 
506
 
507
- echo '
508
- <p class="description">' . __( 'Select page types where the views count will be displayed.', 'post-views-counter' ) . '</p>
509
- </div>';
510
  }
511
 
512
  /**
513
- * Counter position option.
 
 
 
514
  */
515
- public function position() {
516
- echo '
517
- <div id="pvc_position">
518
- <select class="pvc-chosen-short" name="post_views_counter_settings_display[position]">';
519
-
520
- foreach ( $this->positions as $position => $position_name ) {
521
- echo '
522
- <option value="' . esc_attr( $position ) . '" ' . selected( $position, Post_Views_Counter()->options['display']['position'], false ) . '>' . esc_html( $position_name ) . '</option>';
 
 
 
523
  }
524
 
525
- echo '
526
- </select>
527
- <p class="description">' . __( 'Select where would you like to display the post views counter. Use [post-views] shortcode for manual display.', 'post-views-counter' ) . '</p>
528
- </div>';
 
529
  }
530
 
531
  /**
532
- * Counter style option.
 
 
 
 
533
  */
534
- public function display_style() {
535
- echo '
536
- <div id="pvc_display_style">';
537
 
538
- foreach ( $this->display_styles as $display => $style ) {
539
- $display = esc_attr( $display );
540
 
541
- echo '
542
- <label><input type="checkbox" name="post_views_counter_settings_display[display_style][' . $display . ']" value="1" ' . checked( true, Post_Views_Counter()->options['display']['display_style'][$display], false ) . ' />' . esc_html( $style ) . '</label>';
543
- }
544
 
545
- echo '
546
- <p class="description">' . __( 'Choose how to display the post views counter.', 'post-views-counter' ) . '</p>
547
- </div>';
548
- }
549
 
550
- /**
551
- * Counter icon class option.
552
- */
553
- public function icon_class() {
554
- echo '
555
- <div id="pvc_icon_class">
556
- <input type="text" name="post_views_counter_settings_display[icon_class]" class="large-text" value="' . esc_attr( Post_Views_Counter()->options['display']['icon_class'] ) . '" />
557
- <p class="description">' . sprintf( __( 'Enter the post views icon class. Any of the <a href="%s" target="_blank">Dashicons</a> classes are available.', 'post-views-counter' ), 'https://developer.wordpress.org/resource/dashicons/' ) . '</p>
558
- </div>';
559
- }
560
 
561
- /**
562
- * Toolbar statistics.
563
- */
564
- public function toolbar_statistics() {
565
- echo '
566
- <div id="pvc_toolbar_statistics">
567
- <label><input type="checkbox" name="post_views_counter_settings_display[toolbar_statistics]" value="1" ' . checked( true, Post_Views_Counter()->options['display']['toolbar_statistics'], false ) . ' />' . __( 'Enable to display the post views chart at the toolbar.', 'post-views-counter' ) . '</label>
568
- <p class="description">' . __( 'The post views chart will be displayed for the post types that are being counted.', 'post-views-counter' ) . '</p>
569
- </div>';
570
  }
571
 
572
  /**
573
- * Restrict display option.
 
 
574
  */
575
- public function restrict_display() {
576
- echo '
577
- <div id="pvc_restrict_display">
578
- <fieldset>';
579
-
580
- foreach ( $this->groups as $type => $type_name ) {
581
 
582
- if ( $type === 'robots' )
583
- continue;
584
 
585
- echo '
586
- <label><input id="pvc_restrict_display-' . $type . '" type="checkbox" name="post_views_counter_settings_display[restrict_display][groups][' . esc_html( $type ) . ']" value="1" ' . checked( in_array( $type, Post_Views_Counter()->options['display']['restrict_display']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
 
587
  }
588
 
589
- echo '
590
- <p class="description">' . __( 'Use it to hide the post views counter from selected type of visitors.', 'post-views-counter' ) . '</p>
591
- <div class="pvc_user_roles"' . (in_array( 'roles', Post_Views_Counter()->options['display']['restrict_display']['groups'], true ) ? '' : ' style="display: none;"') . '>';
592
 
593
  foreach ( $this->user_roles as $role => $role_name ) {
594
- echo '
595
- <label><input type="checkbox" name="post_views_counter_settings_display[restrict_display][roles][' . $role . ']" value="1" ' . checked( in_array( $role, Post_Views_Counter()->options['display']['restrict_display']['roles'], true ), true, false ) . ' />' . esc_html( $role_name ) . '</label>';
596
  }
597
 
598
- echo '
599
- <p class="description">' . __( 'Use it to hide the post views counter from selected user roles.', 'post-views-counter' ) . '</p>
600
- </div>
601
- </fieldset>
602
- </div>';
603
  }
604
 
605
  /**
606
- * Validate general settings.
 
 
 
 
607
  */
608
- public function validate_settings( $input ) {
609
  // get main instance
610
  $pvc = Post_Views_Counter();
611
 
612
- // import post views data from another plugin
613
- if ( isset( $_POST['post_views_counter_import_wp_postviews'] ) ) {
614
- // make sure we do not change anything in the settings
615
- $input = $pvc->options['general'];
616
-
617
- global $wpdb;
618
-
619
- $meta_key = esc_attr( apply_filters( 'pvc_import_meta_key', 'views' ) );
620
 
621
- $views = $wpdb->get_results( "SELECT post_id, meta_value FROM " . $wpdb->postmeta . " WHERE meta_key = '" . $meta_key . "'", ARRAY_A, 0 );
622
-
623
- if ( ! empty( $views ) ) {
624
- $sql = array();
625
-
626
- foreach ( $views as $view ) {
627
- $sql[] = "(" . $view['post_id'] . ", 4, 'total', " . $view['meta_value'] . ")";
628
- }
629
-
630
- $wpdb->query( "INSERT INTO " . $wpdb->prefix . "post_views(id, type, period, count) VALUES " . implode( ',', $sql ) . " ON DUPLICATE KEY UPDATE count = " . ( isset( $_POST['post_views_counter_import_wp_postviews_override'] ) ? '' : 'count + ' ) . "VALUES(count)" );
631
-
632
- add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'Post views data imported succesfully.', 'post-views-counter' ), 'updated' );
633
- } else {
634
- add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'There was no post views data to import.', 'post-views-counter' ), 'updated' );
635
  }
636
- // delete all post views data
637
- } elseif ( isset( $_POST['post_views_counter_reset_views'] ) ) {
638
- // make sure we do not change anything in the settings
639
- $input = $pvc->options['general'];
640
-
641
- global $wpdb;
642
-
643
- if ( $wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . 'post_views' ) )
644
- add_settings_error( 'reset_post_views', 'reset_post_views', __( 'All existing data deleted succesfully.', 'post-views-counter' ), 'updated' );
645
- else
646
- add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Error occurred. All existing data were not deleted.', 'post-views-counter' ), 'error' );
647
- // save general settings
648
- } elseif ( isset( $_POST['save_pvc_general'] ) ) {
649
- // post types count
650
- if ( isset( $input['post_types_count'] ) ) {
651
- $post_types = array();
652
-
653
- foreach ( $input['post_types_count'] as $post_type => $set ) {
654
- if ( isset( $this->post_types[$post_type] ) )
655
- $post_types[] = $post_type;
656
- }
657
-
658
- $input['post_types_count'] = array_unique( $post_types );
659
- } else
660
- $input['post_types_count'] = array();
661
 
662
- // counter mode
663
- $input['counter_mode'] = isset( $input['counter_mode'], $this->modes[$input['counter_mode']] ) ? $input['counter_mode'] : $pvc->defaults['general']['counter_mode'];
 
664
 
665
- // post views column
666
- $input['post_views_column'] = isset( $input['post_views_column'] );
 
667
 
668
- // time between counts
669
- $input['time_between_counts']['number'] = (int) ( isset( $input['time_between_counts']['number'] ) ? $input['time_between_counts']['number'] : $pvc->defaults['general']['time_between_counts']['number'] );
670
- $input['time_between_counts']['type'] = isset( $input['time_between_counts']['type'], $this->time_types[$input['time_between_counts']['type']] ) ? $input['time_between_counts']['type'] : $pvc->defaults['general']['time_between_counts']['type'];
671
-
672
- // flush interval
673
- $input['flush_interval']['number'] = (int) ( isset( $input['flush_interval']['number'] ) ? $input['flush_interval']['number'] : $pvc->defaults['general']['flush_interval']['number'] );
674
- $input['flush_interval']['type'] = isset( $input['flush_interval']['type'], $this->time_types[$input['flush_interval']['type']] ) ? $input['flush_interval']['type'] : $pvc->defaults['general']['flush_interval']['type'];
675
-
676
- // Since the settings are about to be saved and cache flush interval could've changed,
677
- // we want to make sure that any changes done on the settings page are in effect immediately
678
- // (instead of having to wait for the previous schedule to occur).
679
- // We achieve that by making sure to clear any previous cache flush schedules and
680
- // schedule the new one if the specified interval is > 0
681
- $pvc->remove_cache_flush();
682
-
683
- if ( $input['flush_interval']['number'] > 0 )
684
- $pvc->schedule_cache_flush();
685
 
686
- // reset counts
687
- $input['reset_counts']['number'] = (int) ( isset( $input['reset_counts']['number'] ) ? $input['reset_counts']['number'] : $pvc->defaults['general']['reset_counts']['number'] );
688
- $input['reset_counts']['type'] = isset( $input['reset_counts']['type'], $this->time_types[$input['reset_counts']['type']] ) ? $input['reset_counts']['type'] : $pvc->defaults['general']['reset_counts']['type'];
689
 
690
- // run cron on next visit?
691
- $input['cron_run'] = ( $input['reset_counts']['number'] > 0 );
692
- $input['cron_update'] = ( $input['cron_run'] && ( $pvc->options['general']['reset_counts']['number'] !== $input['reset_counts']['number'] || $pvc->options['general']['reset_counts']['type'] !== $input['reset_counts']['type'] ) );
693
 
694
- // exclude
695
- if ( isset( $input['exclude']['groups'] ) ) {
696
- $groups = array();
 
 
 
 
697
 
698
- foreach ( $input['exclude']['groups'] as $group => $set ) {
699
- if ( isset( $this->groups[$group] ) )
700
- $groups[] = $group;
701
- }
702
 
703
- $input['exclude']['groups'] = array_unique( $groups );
704
- } else {
705
- $input['exclude']['groups'] = array();
 
 
 
706
  }
 
 
 
 
 
 
707
 
708
- if ( in_array( 'roles', $input['exclude']['groups'], true ) && isset( $input['exclude']['roles'] ) ) {
709
- $roles = array();
 
710
 
711
- foreach ( $input['exclude']['roles'] as $role => $set ) {
712
- if ( isset( $this->user_roles[$role] ) )
713
- $roles[] = $role;
714
- }
715
 
716
- $input['exclude']['roles'] = array_unique( $roles );
717
- } else
718
- $input['exclude']['roles'] = array();
 
 
 
 
 
 
 
719
 
720
- // exclude ips
721
- if ( isset( $input['exclude_ips'] ) ) {
722
- $ips = array();
723
 
724
- foreach ( $input['exclude_ips'] as $ip ) {
725
- if ( strpos( $ip, '*' ) !== false ) {
726
- $new_ip = str_replace( '*', '0', $ip );
727
 
728
- if ( filter_var( $new_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
729
- $ips[] = $ip;
730
- } elseif ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
731
  $ips[] = $ip;
732
- }
733
-
734
- $input['exclude_ips'] = array_unique( $ips );
735
  }
736
 
737
- // restrict edit views
738
- $input['restrict_edit_views'] = isset( $input['restrict_edit_views'] );
739
-
740
- // strict counts
741
- $input['strict_counts'] = isset( $input['strict_counts'] );
742
-
743
- // deactivation delete
744
- $input['deactivation_delete'] = isset( $input['deactivation_delete'] );
745
-
746
- $input['update_version'] = $pvc->options['general']['update_version'];
747
- $input['update_notice'] = $pvc->options['general']['update_notice'];
748
- // save display settings
749
- } elseif ( isset( $_POST['save_pvc_display'] ) ) {
750
- // post views label
751
- $input['label'] = isset( $input['label'] ) ? $input['label'] : $pvc->defaults['display']['label'];
752
-
753
- if ( function_exists( 'icl_register_string' ) )
754
- icl_register_string( 'Post Views Counter', 'Post Views Label', $input['label'] );
755
-
756
- // position
757
- $input['position'] = isset( $input['position'], $this->positions[$input['position']] ) ? $input['position'] : $pvc->defaults['display']['position'];
758
-
759
- // display style
760
- $input['display_style']['icon'] = isset( $input['display_style']['icon'] );
761
- $input['display_style']['text'] = isset( $input['display_style']['text'] );
762
 
763
- // link to post
764
- $input['link_to_post'] = isset( $input['link_to_post'] ) ? $input['link_to_post'] : $pvc->defaults['display']['link_to_post'];
765
 
766
- // icon class
767
- $input['icon_class'] = isset( $input['icon_class'] ) ? trim( $input['icon_class'] ) : $pvc->defaults['display']['icon_class'];
 
 
 
 
 
 
 
 
 
 
 
 
768
 
769
- // toolbar statistics
770
- $input['toolbar_statistics'] = isset( $input['toolbar_statistics'] );
 
 
 
 
 
 
771
 
772
- // post types display
773
- if ( isset( $input['post_types_display'] ) ) {
774
- $post_types = array();
775
 
776
- foreach ( $input['post_types_display'] as $post_type => $set ) {
777
- if ( isset( $this->post_types[$post_type] ) )
778
- $post_types[] = $post_type;
779
- }
780
 
781
- $input['post_types_display'] = array_unique( $post_types );
782
- } else
783
- $input['post_types_display'] = array();
784
 
785
- // page types display
786
- if ( isset( $input['page_types_display'] ) ) {
787
- $page_types = array();
788
 
789
- foreach ( $input['page_types_display'] as $page_type => $set ) {
790
- if ( isset( $this->page_types[$page_type] ) )
791
- $page_types[] = $page_type;
792
- }
793
 
794
- $input['page_types_display'] = array_unique( $page_types );
795
- } else
796
- $input['page_types_display'] = array();
797
 
798
- // restrict display
799
- if ( isset( $input['restrict_display']['groups'] ) ) {
800
- $groups = array();
801
 
802
- foreach ( $input['restrict_display']['groups'] as $group => $set ) {
803
- if ( $group === 'robots' )
804
- continue;
 
 
 
 
 
 
 
805
 
806
- if ( isset( $this->groups[$group] ) )
807
- $groups[] = $group;
808
- }
809
 
810
- $input['restrict_display']['groups'] = array_unique( $groups );
811
- } else
812
- $input['restrict_display']['groups'] = array();
813
 
814
- if ( in_array( 'roles', $input['restrict_display']['groups'], true ) && isset( $input['restrict_display']['roles'] ) ) {
815
- $roles = array();
 
816
 
817
- foreach ( $input['restrict_display']['roles'] as $role => $set ) {
818
- if ( isset( $this->user_roles[$role] ) )
819
- $roles[] = $role;
820
- }
821
 
822
- $input['restrict_display']['roles'] = array_unique( $roles );
823
- } else
824
- $input['restrict_display']['roles'] = array();
825
- // reset general settings
826
- } elseif ( isset( $_POST['reset_pvc_general'] ) ) {
827
- $input = $pvc->defaults['general'];
828
 
829
- add_settings_error( 'reset_general_settings', 'settings_reset', __( 'General settings restored to defaults.', 'post-views-counter' ), 'updated' );
830
- // reset display settings
831
- } elseif ( isset( $_POST['reset_pvc_display'] ) ) {
832
- $input = $pvc->defaults['display'];
833
 
834
- add_settings_error( 'reset_general_settings', 'settings_reset', __( 'Display settings restored to defaults.', 'post-views-counter' ), 'updated' );
835
- }
 
836
 
837
  return $input;
838
  }
10
  */
11
  class Post_Views_Counter_Settings {
12
 
 
 
 
13
  private $time_types;
14
  private $groups;
15
  private $user_roles;
 
 
16
  public $post_types;
17
  public $page_types;
18
 
19
  public function __construct() {
20
  // actions
21
+ add_action( 'after_setup_theme', [ $this, 'load_defaults' ] );
22
+ add_action( 'wp_loaded', [ $this, 'load_post_types' ] );
23
+
24
+ // filters
25
+ add_filter( 'post_views_counter_settings_data', [ $this, 'settings_data' ] );
26
+ add_filter( 'post_views_counter_settings_pages', [ $this, 'settings_page' ] );
27
  }
28
 
29
  /**
30
  * Load default settings.
31
+ *
32
+ * @return void
33
  */
34
  public function load_defaults() {
35
  if ( ! is_admin() )
36
  return;
37
 
38
+ $this->time_types = [
39
+ 'minutes' => __( 'minutes', 'post-views-counter' ),
40
+ 'hours' => __( 'hours', 'post-views-counter' ),
41
+ 'days' => __( 'days', 'post-views-counter' ),
42
+ 'weeks' => __( 'weeks', 'post-views-counter' ),
43
+ 'months' => __( 'months', 'post-views-counter' ),
44
+ 'years' => __( 'years', 'post-views-counter' )
45
+ ];
46
+
47
+ $this->groups = [
48
+ 'robots' => __( 'robots', 'post-views-counter' ),
49
+ 'users' => __( 'logged in users', 'post-views-counter' ),
50
+ 'guests' => __( 'guests', 'post-views-counter' ),
51
+ 'roles' => __( 'selected user roles', 'post-views-counter' )
52
+ ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  $this->user_roles = $this->get_user_roles();
55
 
56
  $this->page_types = apply_filters(
57
  'pvc_page_types_display_options',
58
+ [
59
+ 'home' => __( 'Home', 'post-views-counter' ),
60
+ 'archive' => __( 'Archives', 'post-views-counter' ),
61
+ 'singular' => __( 'Single pages', 'post-views-counter' ),
62
+ 'search' => __( 'Search results', 'post-views-counter' ),
63
+ ]
64
  );
65
  }
66
 
67
  /**
68
  * Get post types avaiable for counting.
69
+ *
70
+ * @return void
71
  */
72
  public function load_post_types() {
73
  if ( ! is_admin() )
74
  return;
75
 
76
+ $post_types = [];
77
 
78
  // built in public post types
79
+ foreach ( get_post_types( [ '_builtin' => true, 'public' => true ], 'objects', 'and' ) as $key => $post_type ) {
80
  $post_types[$key] = $post_type->labels->name;
81
  }
82
 
83
  // public custom post types
84
+ foreach ( get_post_types( [ '_builtin' => false, 'public' => true ], 'objects', 'and' ) as $key => $post_type ) {
85
  $post_types[$key] = $post_type->labels->name;
86
  }
87
 
89
  if ( class_exists( 'bbPress' ) && isset( $post_types['reply'] ) )
90
  unset( $post_types['reply'] );
91
 
92
+ $post_types = apply_filters( 'pvc_available_post_types', $post_types );
93
 
94
  // sort post types alphabetically with their keys
95
  asort( $post_types, SORT_STRING );
106
  public function get_user_roles() {
107
  global $wp_roles;
108
 
109
+ $roles = [];
110
 
111
  foreach ( apply_filters( 'editable_roles', $wp_roles->roles ) as $role => $details ) {
112
  $roles[$role] = translate_user_role( $details['name'] );
118
  }
119
 
120
  /**
121
+ * Add settings data.
122
  *
123
+ * @param array $settings
124
+ * @return array
125
  */
126
+ public function settings_data( $settings ) {
127
+ $modes = [
128
+ 'php' => __( 'PHP', 'post-views-counter' ),
129
+ 'js' => __( 'JavaScript', 'post-views-counter' ),
130
+ 'ajax' => __( 'Fast AJAX', 'post-views-counter' )
131
+ ];
132
+
133
+ // WordPress 4.4+?
134
+ if ( function_exists( 'register_rest_route' ) )
135
+ $modes['rest_api'] = __( 'REST API', 'post-views-counter' );
136
+
137
+ $settings['post-views-counter'] = [
138
+ 'label' => __( 'Post Views Counter Settings', 'post-views-counter' ),
139
+ 'option_name' => [
140
+ 'general' => 'post_views_counter_settings_general',
141
+ 'display' => 'post_views_counter_settings_display'
142
+ ],
143
+ 'validate' => [ $this, 'validate_settings' ],
144
+ 'sections' => [
145
+ 'post_views_counter_general_settings' => [],
146
+ 'post_views_counter_display_settings' => []
147
+ ],
148
+ 'fields' => [
149
+ 'post_types_count' => [
150
+ 'tab' => 'general',
151
+ 'title' => __( 'Post Types Count', 'post-views-counter' ),
152
+ 'section' => 'post_views_counter_general_settings',
153
+ 'type' => 'checkbox',
154
+ 'display_type' => 'horizontal',
155
+ 'description' => __( 'Select post types for which post views will be counted.', 'post-views-counter' ),
156
+ 'options' => $this->post_types
157
+ ],
158
+ 'counter_mode' => [
159
+ 'tab' => 'general',
160
+ 'title' => __( 'Counter Mode', 'post-views-counter' ),
161
+ 'section' => 'post_views_counter_general_settings',
162
+ 'type' => 'radio',
163
+ 'description' => __( 'Select the method of collecting post views data. If you are using any of the caching plugins select Javascript or REST API (if available).', 'post-views-counter' ) . '<br />' . __( 'Optionally try the Fast AJAX experimental method, usually 10+ times faster than Javascript or REST API.', 'post-views-counter' ),
164
+ 'options' => $modes
165
+ ],
166
+ 'post_views_column' => [
167
+ 'tab' => 'general',
168
+ 'title' => __( 'Post Views Column', 'post-views-counter' ),
169
+ 'section' => 'post_views_counter_general_settings',
170
+ 'type' => 'boolean',
171
+ 'description' => '',
172
+ 'label' => __( 'Enable to display post views count column for each of the selected post types.', 'post-views-counter' )
173
+ ],
174
+ 'restrict_edit_views' => [
175
+ 'tab' => 'general',
176
+ 'title' => __( 'Restrict Edit', 'post-views-counter' ),
177
+ 'section' => 'post_views_counter_general_settings',
178
+ 'type' => 'boolean',
179
+ 'description' => '',
180
+ 'label' => __( 'Enable to restrict post views editing to admins only.', 'post-views-counter' )
181
+ ],
182
+ 'time_between_counts' => [
183
+ 'tab' => 'general',
184
+ 'title' => __( 'Count Interval', 'post-views-counter' ),
185
+ 'section' => 'post_views_counter_general_settings',
186
+ 'type' => 'custom',
187
+ 'description' => '',
188
+ 'min' => 0,
189
+ 'max' => 999999,
190
+ 'callback' => [ $this, 'setting_time_between_counts' ],
191
+ 'validate' => [ $this, 'validate_time_between_counts' ]
192
+ ],
193
+ 'reset_counts' => [
194
+ 'tab' => 'general',
195
+ 'title' => __( 'Reset Data Interval', 'post-views-counter' ),
196
+ 'section' => 'post_views_counter_general_settings',
197
+ 'type' => 'custom',
198
+ 'description' => '',
199
+ 'min' => 0,
200
+ 'max' => 999999,
201
+ 'callback' => [ $this, 'setting_reset_counts' ],
202
+ 'validate' => [ $this, 'validate_reset_counts' ]
203
+ ],
204
+ 'flush_interval' => [
205
+ 'tab' => 'general',
206
+ 'title' => __( 'Flush Object Cache Interval', 'post-views-counter' ),
207
+ 'section' => 'post_views_counter_general_settings',
208
+ 'type' => 'custom',
209
+ 'description' => '',
210
+ 'min' => 0,
211
+ 'max' => 999999,
212
+ 'callback' => [ $this, 'setting_flush_interval' ],
213
+ 'validate' => [ $this, 'validate_flush_interval' ]
214
+ ],
215
+ 'exclude' => [
216
+ 'tab' => 'general',
217
+ 'title' => __( 'Exclude Visitors', 'post-views-counter' ),
218
+ 'section' => 'post_views_counter_general_settings',
219
+ 'type' => 'custom',
220
+ 'description' => '',
221
+ 'callback' => [ $this, 'setting_exclude' ],
222
+ 'validate' => [ $this, 'validate_exclude' ]
223
+ ],
224
+ 'exclude_ips' => [
225
+ 'tab' => 'general',
226
+ 'title' => __( 'Exclude IPs', 'post-views-counter' ),
227
+ 'section' => 'post_views_counter_general_settings',
228
+ 'type' => 'custom',
229
+ 'description' => '',
230
+ 'callback' => [ $this, 'setting_exclude_ips' ],
231
+ 'validate' => [ $this, 'validate_exclude_ips' ]
232
+ ],
233
+ 'strict_counts' => [
234
+ 'tab' => 'general',
235
+ 'title' => __( 'Strict counts', 'post-views-counter' ),
236
+ 'section' => 'post_views_counter_general_settings',
237
+ 'type' => 'boolean',
238
+ 'description' => '',
239
+ 'label' => __( 'Enable to prevent bypassing the counts interval (for e.g. using incognito browser window or by clearing cookies).', 'post-views-counter' )
240
+ ],
241
+ 'wp_postviews' => [
242
+ 'tab' => 'general',
243
+ 'title' => __( 'Tools', 'post-views-counter' ),
244
+ 'section' => 'post_views_counter_general_settings',
245
+ 'type' => 'custom',
246
+ 'description' => '',
247
+ 'skip_saving' => true,
248
+ 'callback' => [ $this, 'setting_wp_postviews' ]
249
+ ],
250
+ 'deactivation_delete' => [
251
+ 'tab' => 'general',
252
+ 'title' => __( 'Deactivation', 'post-views-counter' ),
253
+ 'section' => 'post_views_counter_general_settings',
254
+ 'type' => 'boolean',
255
+ 'description' => '',
256
+ 'label' => __( 'Enable to delete all plugin data on deactivation.', 'post-views-counter' )
257
+ ],
258
+ 'label' => [
259
+ 'tab' => 'display',
260
+ 'title' => __( 'Post Views Label', 'post-views-counter' ),
261
+ 'section' => 'post_views_counter_display_settings',
262
+ 'type' => 'input',
263
+ 'description' => __( 'Enter the label for the post views counter field.', 'post-views-counter' ),
264
+ 'subclass' => 'regular-text',
265
+ 'validate' => [ $this, 'validate_label' ],
266
+ 'reset' => [ $this, 'reset_label' ]
267
+ ],
268
+ 'post_types_display' => [
269
+ 'tab' => 'display',
270
+ 'title' => __( 'Post Type', 'post-views-counter' ),
271
+ 'section' => 'post_views_counter_display_settings',
272
+ 'type' => 'checkbox',
273
+ 'display_type' => 'horizontal',
274
+ 'description' => __( 'Select post types for which the views count will be displayed.', 'post-views-counter' ),
275
+ 'options' => $this->post_types
276
+ ],
277
+ 'page_types_display' => [
278
+ 'tab' => 'display',
279
+ 'title' => __( 'Page Type', 'post-views-counter' ),
280
+ 'section' => 'post_views_counter_display_settings',
281
+ 'type' => 'checkbox',
282
+ 'display_type' => 'horizontal',
283
+ 'description' => __( 'Select page types where the views count will be displayed.', 'post-views-counter' ),
284
+ 'options' => $this->page_types
285
+ ],
286
+ 'restrict_display' => [
287
+ 'tab' => 'display',
288
+ 'title' => __( 'User Type', 'post-views-counter' ),
289
+ 'section' => 'post_views_counter_display_settings',
290
+ 'type' => 'custom',
291
+ 'description' => '',
292
+ 'callback' => [ $this, 'setting_restrict_display' ],
293
+ 'validate' => [ $this, 'validate_restrict_display' ]
294
+ ],
295
+ 'position' => [
296
+ 'tab' => 'display',
297
+ 'title' => __( 'Position', 'post-views-counter' ),
298
+ 'section' => 'post_views_counter_display_settings',
299
+ 'type' => 'select',
300
+ 'description' => __( 'Select where would you like to display the post views counter. Use [post-views] shortcode for manual display.', 'post-views-counter' ),
301
+ 'options' => [
302
+ 'before' => __( 'before the content', 'post-views-counter' ),
303
+ 'after' => __( 'after the content', 'post-views-counter' ),
304
+ 'manual' => __( 'manual', 'post-views-counter' )
305
+ ]
306
+ ],
307
+ 'display_style' => [
308
+ 'tab' => 'display',
309
+ 'title' => __( 'Display Style', 'post-views-counter' ),
310
+ 'section' => 'post_views_counter_display_settings',
311
+ 'type' => 'custom',
312
+ 'description' => __( 'Choose how to display the post views counter.', 'post-views-counter' ),
313
+ 'callback' => [ $this, 'setting_display_style' ],
314
+ 'validate' => [ $this, 'validate_display_style' ],
315
+ 'options' => [
316
+ 'icon' => __( 'icon', 'post-views-counter' ),
317
+ 'text' => __( 'label', 'post-views-counter' )
318
+ ]
319
+ ],
320
+ 'icon_class' => [
321
+ 'tab' => 'display',
322
+ 'title' => __( 'Icon Class', 'post-views-counter' ),
323
+ 'section' => 'post_views_counter_display_settings',
324
+ 'type' => 'input',
325
+ 'description' => sprintf( __( 'Enter the post views icon class. Any of the <a href="%s" target="_blank">Dashicons</a> classes are available.', 'post-views-counter' ), 'https://developer.wordpress.org/resource/dashicons/' ),
326
+ 'subclass' => 'regular-text'
327
+ ],
328
+ 'toolbar_statistics' => [
329
+ 'tab' => 'display',
330
+ 'title' => __( 'Toolbar Chart', 'post-views-counter' ),
331
+ 'section' => 'post_views_counter_display_settings',
332
+ 'type' => 'boolean',
333
+ 'description' => __( 'The post views chart will be displayed for the post types that are being counted.', 'post-views-counter' ),
334
+ 'label' => __( 'Enable to display the post views chart at the toolbar.', 'post-views-counter' )
335
+ ]
336
+ ]
337
+ ];
338
+
339
+ return $settings;
340
  }
341
 
342
  /**
343
+ * Add settings page.
344
  *
345
+ * @param array $pages
346
+ * @return array
347
  */
348
+ public function settings_page( $pages ) {
349
+ $pages['post-views-counter'] = [
350
+ 'type' => 'settings_page',
351
+ 'menu_slug' => 'post-views-counter',
352
+ 'page_title' => __( 'Post Views Counter Settings', 'post-views-counter' ),
353
+ 'menu_title' => __( 'Post Views Counter', 'post-views-counter' ),
354
+ 'capability' => apply_filters( 'pvc_settings_capability', 'manage_options' ),
355
+ 'callback' => null,
356
+ 'tabs' => [
357
+ 'general' => [
358
+ 'label' => __( 'General', 'post-views-counter' ),
359
+ 'option_name' => 'post_views_counter_settings_general'
360
+ ],
361
+ 'display' => [
362
+ 'label' => __( 'Display', 'post-views-counter' ),
363
+ 'option_name' => 'post_views_counter_settings_display'
364
+ ]
365
+ ]
366
+ ];
367
+
368
+ return $pages;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  }
370
 
371
  /**
372
+ * Validate options.
373
+ *
374
+ * @param array $input Settings data
375
+ * @return array
376
  */
377
+ public function validate_settings( $input ) {
378
+ // check capability
379
+ if ( ! current_user_can( 'manage_options' ) )
380
+ return $input;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
+ // get main instance
383
+ $pvc = Post_Views_Counter();
 
 
 
 
 
 
 
 
 
 
384
 
385
+ // use internal settings API to validate settings first
386
+ $input = $pvc->settings_api->validate_settings( $input );
 
 
 
 
387
 
388
+ // import post views data from another plugin
389
+ if ( isset( $_POST['post_views_counter_import_wp_postviews'] ) ) {
390
+ // make sure we do not change anything in the settings
391
+ $input = $pvc->options['general'];
392
 
393
+ global $wpdb;
 
 
 
394
 
395
+ // get views key
396
+ $meta_key = esc_attr( apply_filters( 'pvc_import_meta_key', 'views' ) );
397
+
398
+ // get views
399
+ $views = $wpdb->get_results( "SELECT post_id, meta_value FROM " . $wpdb->postmeta . " WHERE meta_key = '" . $meta_key . "'", ARRAY_A, 0 );
400
+
401
+ // any views?
402
+ if ( ! empty( $views ) ) {
403
+ $sql = [];
404
+
405
+ foreach ( $views as $view ) {
406
+ $sql[] = "(" . $view['post_id'] . ", 4, 'total', " . ( (int) $view['meta_value'] ) . ")";
407
+ }
408
+
409
+ $wpdb->query( "INSERT INTO " . $wpdb->prefix . "post_views(id, type, period, count) VALUES " . implode( ',', $sql ) . " ON DUPLICATE KEY UPDATE count = " . ( isset( $_POST['post_views_counter_import_wp_postviews_override'] ) ? '' : 'count + ' ) . "VALUES(count)" );
410
 
411
+ add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'Post views data imported succesfully.', 'post-views-counter' ), 'updated' );
412
+ } else
413
+ add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'There was no post views data to import.', 'post-views-counter' ), 'updated' );
414
+ // delete all post views data
415
+ } elseif ( isset( $_POST['post_views_counter_reset_views'] ) ) {
416
+ // make sure we do not change anything in the settings
417
+ $input = $pvc->options['general'];
418
+
419
+ global $wpdb;
420
+
421
+ if ( $wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . 'post_views' ) )
422
+ add_settings_error( 'reset_post_views', 'reset_post_views', __( 'All existing data deleted succesfully.', 'post-views-counter' ), 'updated' );
423
+ else
424
+ add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Error occurred. All existing data were not deleted.', 'post-views-counter' ), 'error' );
425
+ // save general settings
426
+ } elseif ( isset( $_POST['save_post_views_counter_settings_general'] ) ) {
427
+ $input['update_version'] = $pvc->options['general']['update_version'];
428
+ $input['update_notice'] = $pvc->options['general']['update_notice'];
429
+ $input['update_delay_date'] = $pvc->options['general']['update_delay_date'];
430
+ // reset general settings
431
+ } elseif ( isset( $_POST['reset_post_views_counter_settings_general'] ) ) {
432
+ $input['update_version'] = $pvc->options['general']['update_version'];
433
+ $input['update_notice'] = $pvc->options['general']['update_notice'];
434
+ $input['update_delay_date'] = $pvc->options['general']['update_delay_date'];
435
  }
436
 
437
+ return $input;
 
 
438
  }
439
 
440
  /**
441
+ * Validate label.
442
+ *
443
+ * @param array $input Input POST data
444
+ * @param array $field Field options
445
+ * @return array
446
  */
447
+ public function validate_label( $input, $field ) {
448
+ // get main instance
449
+ $pvc = Post_Views_Counter();
450
 
451
+ if ( ! isset( $input ) )
452
+ $input = $pvc->defaults['display']['label'];
453
 
454
+ // use internal settings API to validate settings first
455
+ $input = $pvc->settings_api->validate_field( $input, 'input', $field );
 
456
 
457
+ if ( function_exists( 'icl_register_string' ) )
458
+ icl_register_string( 'Post Views Counter', 'Post Views Label', $input );
459
+
460
+ return $input;
461
  }
462
 
463
  /**
464
+ * Restore post views label to default value.
465
+ *
466
+ * @param array $default Default value
467
+ * @param array $field Field options
468
+ * @return array
469
  */
470
+ public function reset_label( $default, $field ) {
471
+ if ( function_exists( 'icl_register_string' ) )
472
+ icl_register_string( 'Post Views Counter', 'Post Views Label', $default );
473
+
474
+ return $default;
475
  }
476
 
477
  /**
478
+ * Setting: display style.
479
+ *
480
+ * @param array $field Field options
481
+ * @return string
482
  */
483
+ public function setting_display_style( $field ) {
484
+ // get main instance
485
+ $pvc = Post_Views_Counter();
 
 
486
 
487
+ $html = '
488
+ <input type="hidden" name="post_views_counter_settings_display[display_style]" value="empty" />';
489
+
490
+ foreach ( $field['options'] as $key => $label ) {
491
+ $html .= '
492
+ <label><input id="post_views_counter_display_display_style_' . esc_attr( $key ) . '" type="checkbox" name="post_views_counter_settings_display[display_style][]" value="' . esc_attr( $key ) . '" ' . checked( ! empty( $pvc->options['display']['display_style'][$key] ), true, false ) . ' />' . esc_html( $label ) . '</label> ';
493
  }
494
 
495
+ return $html;
 
 
 
496
  }
497
 
498
  /**
499
+ * Validate display style.
500
+ *
501
+ * @param array $input Input POST data
502
+ * @param array $field Field options
503
+ * @return array
504
  */
505
+ public function validate_display_style( $input, $field ) {
506
+ // get main instance
507
+ $pvc = Post_Views_Counter();
 
 
508
 
509
+ $data = [];
510
+
511
+ foreach ( $field['options'] as $value => $label ) {
512
+ $data[$value] = false;
513
  }
514
 
515
+ // any data?
516
+ if ( ! empty( $input['display_style'] && $input['display_style'] !== 'empty' && is_array( $input['display_style'] ) ) ) {
517
+ foreach ( $input['display_style'] as $value ) {
518
+ if ( array_key_exists( $value, $field['options'] ) )
519
+ $data[$value] = true;
520
+ }
521
+ }
522
+
523
+ $input['display_style'] = $data;
524
+
525
+ return $input;
526
  }
527
 
528
  /**
529
+ * Setting: count interval.
530
+ *
531
+ * @param array $field Field options
532
+ * @return string
533
  */
534
+ public function setting_time_between_counts( $field ) {
535
+ // get main instance
536
+ $pvc = Post_Views_Counter();
537
+
538
+ $html = '
539
+ <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( $pvc->options['general']['time_between_counts']['number'] ) . '" />
540
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[time_between_counts][type]">';
541
 
542
  foreach ( $this->time_types as $type => $type_name ) {
543
+ $html .= '
544
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['time_between_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
545
  }
546
 
547
+ $html .= '
548
+ </select>
549
+ <p class="description">' . __( 'Enter the time between single user visit count.', 'post-views-counter' ) . '</p>';
550
+
551
+ return $html;
552
  }
553
 
554
  /**
555
+ * Validate count interval.
556
+ *
557
+ * @param array $input Input POST data
558
+ * @param array $field Field options
559
+ * @return array
560
  */
561
+ public function validate_time_between_counts( $input, $field ) {
562
+ // get main instance
563
+ $pvc = Post_Views_Counter();
 
564
 
565
+ // number
566
+ $input['time_between_counts']['number'] = isset( $input['time_between_counts']['number'] ) ? (int) $input['time_between_counts']['number'] : $pvc->defaults['general']['time_between_counts']['number'];
 
 
567
 
568
+ if ( $input['time_between_counts']['number'] < $field['min'] || $input['time_between_counts']['number'] > $field['max'] )
569
+ $input['time_between_counts']['number'] = $pvc->defaults['general']['time_between_counts']['number'];
 
570
 
571
+ // type
572
+ $input['time_between_counts']['type'] = isset( $input['time_between_counts']['type'], $this->time_types[$input['time_between_counts']['type']] ) ? $input['time_between_counts']['type'] : $pvc->defaults['general']['time_between_counts']['type'];
 
 
573
 
574
+ return $input;
 
 
 
 
575
  }
576
 
577
  /**
578
+ * Setting: reset data interval.
579
+ *
580
+ * @param array $field Field options
581
+ * @return string
582
  */
583
+ public function setting_reset_counts( $field ) {
584
+ // get main instance
585
+ $pvc = Post_Views_Counter();
586
 
587
+ $html = '
588
+ <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( $pvc->options['general']['reset_counts']['number'] ) . '" />
589
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[reset_counts][type]">';
590
 
591
+ foreach ( array_slice( $this->time_types, 2, null, true ) as $type => $type_name ) {
592
+ $html .= '
593
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['reset_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
 
 
 
 
 
 
 
 
 
594
  }
595
 
596
+ $html .= '
597
+ </select>
598
+ <p class="description">' . __( 'Delete single day post views data older than specified above. Enter 0 (number zero) if you want to preserve your data regardless of its age.', 'post-views-counter' ) . '</p>';
 
 
599
 
600
+ return $html;
 
 
 
 
 
 
 
601
  }
602
 
603
  /**
604
+ * Validate reset data interval.
605
+ *
606
+ * @param array $input Input POST data
607
+ * @param array $field Field options
608
+ * @return array
609
  */
610
+ public function validate_reset_counts( $input, $field ) {
611
+ // get main instance
612
+ $pvc = Post_Views_Counter();
 
 
 
 
 
 
 
 
613
 
614
+ // number
615
+ $input['reset_counts']['number'] = isset( $input['reset_counts']['number'] ) ? (int) $input['reset_counts']['number'] : $pvc->defaults['general']['reset_counts']['number'];
 
 
 
 
 
 
 
616
 
617
+ if ( $input['reset_counts']['number'] < $field['min'] || $input['reset_counts']['number'] > $field['max'] )
618
+ $input['reset_counts']['number'] = $pvc->defaults['general']['reset_counts']['number'];
 
 
 
 
 
 
 
619
 
620
+ // type
621
+ $input['reset_counts']['type'] = isset( $input['reset_counts']['type'], $this->time_types[$input['reset_counts']['type']] ) ? $input['reset_counts']['type'] : $pvc->defaults['general']['reset_counts']['type'];
 
 
 
 
622
 
623
+ // run cron on next visit?
624
+ $input['cron_run'] = ( $input['reset_counts']['number'] > 0 );
625
+
626
+ // cron update?
627
+ $input['cron_update'] = ( $input['cron_run'] && ( $pvc->options['general']['reset_counts']['number'] !== $input['reset_counts']['number'] || $pvc->options['general']['reset_counts']['type'] !== $input['reset_counts']['type'] ) );
628
 
629
+ return $input;
 
 
630
  }
631
 
632
  /**
633
+ * Setting: flush object cache interval.
634
+ *
635
+ * @param array $field Field options
636
+ * @return string
637
  */
638
+ public function setting_flush_interval( $field ) {
639
+ // get main instance
640
+ $pvc = Post_Views_Counter();
641
+
642
+ $html = '
643
+ <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[flush_interval][number]" value="' . esc_attr( $pvc->options['general']['flush_interval']['number'] ) . '" />
644
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[flush_interval][type]">';
645
+
646
+ foreach ( $this->time_types as $type => $type_name ) {
647
+ $html .= '
648
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['flush_interval']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
649
  }
650
 
651
+ $html .= '
652
+ </select>
653
+ <p class="description">' . __( 'How often to flush cached view counts from the object cache into the database. This feature is used only if a persistent object cache is detected and the interval is greater than 0 (number zero). 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.<br /><strong>Notice:</strong> Potential data loss may occur if the object cache is cleared/unavailable for the duration of the interval.', 'post-views-counter' ) . '</p>';
654
+
655
+ return $html;
656
  }
657
 
658
  /**
659
+ * Validate flush object cache interval.
660
+ *
661
+ * @param array $input Input POST data
662
+ * @param array $field Field options
663
+ * @return array
664
  */
665
+ public function validate_flush_interval( $input, $field ) {
666
+ // get main instance
667
+ $pvc = Post_Views_Counter();
668
 
669
+ // number
670
+ $input['flush_interval']['number'] = isset( $input['flush_interval']['number'] ) ? (int) $input['flush_interval']['number'] : $pvc->defaults['general']['flush_interval']['number'];
671
 
672
+ if ( $input['flush_interval']['number'] < $field['min'] || $input['flush_interval']['number'] > $field['max'] )
673
+ $input['flush_interval']['number'] = $pvc->defaults['general']['flush_interval']['number'];
 
674
 
675
+ // type
676
+ $input['flush_interval']['type'] = isset( $input['flush_interval']['type'], $this->time_types[$input['flush_interval']['type']] ) ? $input['flush_interval']['type'] : $pvc->defaults['general']['flush_interval']['type'];
 
 
677
 
678
+ // Since the settings are about to be saved and cache flush interval could've changed,
679
+ // we want to make sure that any changes done on the settings page are in effect immediately
680
+ // (instead of having to wait for the previous schedule to occur).
681
+ // We achieve that by making sure to clear any previous cache flush schedules and
682
+ // schedule the new one if the specified interval is > 0
683
+ $pvc->remove_cache_flush();
 
 
 
 
684
 
685
+ if ( $input['flush_interval']['number'] > 0 )
686
+ $pvc->schedule_cache_flush();
687
+
688
+ return $input;
 
 
 
 
 
689
  }
690
 
691
  /**
692
+ * Setting: exclude visitors.
693
+ *
694
+ * @return string
695
  */
696
+ public function setting_exclude() {
697
+ // get main instance
698
+ $pvc = Post_Views_Counter();
 
 
 
699
 
700
+ $html = '';
 
701
 
702
+ foreach ( $this->groups as $type => $type_name ) {
703
+ $html .= '
704
+ <label><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, $pvc->options['general']['exclude']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
705
  }
706
 
707
+ $html .= '
708
+ <p class="description">' . __( 'Use it exclude specific user groups from post views count.', 'post-views-counter' ) . '</p>
709
+ <div class="pvc_user_roles"' . ( in_array( 'roles', $pvc->options['general']['exclude']['groups'], true ) ? '' : ' style="display: none;"' ) . '>';
710
 
711
  foreach ( $this->user_roles as $role => $role_name ) {
712
+ $html .= '
713
+ <label><input type="checkbox" name="post_views_counter_settings_general[exclude][roles][' . $role . ']" value="1" ' . checked( in_array( $role, $pvc->options['general']['exclude']['roles'], true ), true, false ) . '>' . esc_html( $role_name ) . '</label>';
714
  }
715
 
716
+ $html .= '
717
+ <p class="description">' . __( 'Use it exclude specific user roles from post views count.', 'post-views-counter' ) . '</p>
718
+ </div>';
719
+
720
+ return $html;
721
  }
722
 
723
  /**
724
+ * Validate exclude visitors.
725
+ *
726
+ * @param array $input Input POST data
727
+ * @param array $field Field options
728
+ * @return array
729
  */
730
+ public function validate_exclude( $input, $field ) {
731
  // get main instance
732
  $pvc = Post_Views_Counter();
733
 
734
+ // any groups?
735
+ if ( isset( $input['exclude']['groups'] ) ) {
736
+ $groups = [];
 
 
 
 
 
737
 
738
+ foreach ( $input['exclude']['groups'] as $group => $set ) {
739
+ if ( isset( $this->groups[$group] ) )
740
+ $groups[] = $group;
 
 
 
 
 
 
 
 
 
 
 
741
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742
 
743
+ $input['exclude']['groups'] = array_unique( $groups );
744
+ } else
745
+ $input['exclude']['groups'] = [];
746
 
747
+ // any roles?
748
+ if ( in_array( 'roles', $input['exclude']['groups'], true ) && isset( $input['exclude']['roles'] ) ) {
749
+ $roles = [];
750
 
751
+ foreach ( $input['exclude']['roles'] as $role => $set ) {
752
+ if ( isset( $this->user_roles[$role] ) )
753
+ $roles[] = $role;
754
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
755
 
756
+ $input['exclude']['roles'] = array_unique( $roles );
757
+ } else
758
+ $input['exclude']['roles'] = [];
759
 
760
+ return $input;
761
+ }
 
762
 
763
+ /**
764
+ * Setting: exclude IP addresses.
765
+ *
766
+ * @return string
767
+ */
768
+ public function setting_exclude_ips() {
769
+ $ips = Post_Views_Counter()->options['general']['exclude_ips'];
770
 
771
+ $html = '';
 
 
 
772
 
773
+ if ( ! empty( $ips ) ) {
774
+ foreach ( $ips as $key => $ip ) {
775
+ $html .= '
776
+ <div class="ip-box">
777
+ <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>
778
+ </div>';
779
  }
780
+ } else {
781
+ $html .= '
782
+ <div class="ip-box">
783
+ <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' ) . '">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
784
+ </div>';
785
+ }
786
 
787
+ $html .= '
788
+ <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>
789
+ <p class="description">' . esc_html__( 'Enter the IP addresses to be excluded from post views count.', 'post-views-counter' ) . '</p>';
790
 
791
+ return $html;
792
+ }
 
 
793
 
794
+ /**
795
+ * Validate exclude IP addresses.
796
+ *
797
+ * @param array $input Input POST data
798
+ * @param array $field Field options
799
+ * @return array
800
+ */
801
+ public function validate_exclude_ips( $input, $field ) {
802
+ // get main instance
803
+ $pvc = Post_Views_Counter();
804
 
805
+ // any ip addresses?
806
+ if ( isset( $input['exclude_ips'] ) ) {
807
+ $ips = [];
808
 
809
+ foreach ( $input['exclude_ips'] as $ip ) {
810
+ if ( strpos( $ip, '*' ) !== false ) {
811
+ $new_ip = str_replace( '*', '0', $ip );
812
 
813
+ if ( filter_var( $new_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
 
 
814
  $ips[] = $ip;
815
+ } elseif ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
816
+ $ips[] = $ip;
 
817
  }
818
 
819
+ $input['exclude_ips'] = array_unique( $ips );
820
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
821
 
822
+ return $input;
823
+ }
824
 
825
+ /**
826
+ * Setting: tools.
827
+ *
828
+ * @return string
829
+ */
830
+ public function setting_wp_postviews() {
831
+ $html = '
832
+ <input type="submit" class="button button-secondary" name="post_views_counter_import_wp_postviews" value="' . __( 'Import views', 'post-views-counter' ) . '"/> <label><input id="pvc-wp-postviews" type="checkbox" name="post_views_counter_import_wp_postviews_override" value="1" />' . __( 'Override existing views data.', 'post-views-counter' ) . '</label>
833
+ <p class="description">' . __( 'Import post views data from WP-PostViews plugin.', 'post-views-counter' ) . '</p>
834
+ <br /><input type="submit" class="button button-secondary" name="post_views_counter_reset_views" value="' . __( 'Delete views', 'post-views-counter' ) . '"/>
835
+ <p class="description">' . __( 'Delete all the existing post views data.', 'post-views-counter' ) . '</p>';
836
+
837
+ return $html;
838
+ }
839
 
840
+ /**
841
+ * Setting: user type.
842
+ *
843
+ * @return string
844
+ */
845
+ public function setting_restrict_display() {
846
+ // get main instance
847
+ $pvc = Post_Views_Counter();
848
 
849
+ $html = '';
 
 
850
 
851
+ foreach ( $this->groups as $type => $type_name ) {
852
+ if ( $type === 'robots' )
853
+ continue;
 
854
 
855
+ $html .= '
856
+ <label><input id="pvc_restrict_display-' . esc_attr( $type ) . '" type="checkbox" name="post_views_counter_settings_display[restrict_display][groups][' . esc_html( $type ) . ']" value="1" ' . checked( in_array( $type, $pvc->options['display']['restrict_display']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
857
+ }
858
 
859
+ $html .= '
860
+ <p class="description">' . __( 'Use it to hide the post views counter from selected type of visitors.', 'post-views-counter' ) . '</p>
861
+ <div class="pvc_user_roles"' . ( in_array( 'roles', $pvc->options['display']['restrict_display']['groups'], true ) ? '' : ' style="display: none;"' ) . '>';
862
 
863
+ foreach ( $this->user_roles as $role => $role_name ) {
864
+ $html .= '
865
+ <label><input type="checkbox" name="post_views_counter_settings_display[restrict_display][roles][' . esc_attr( $role ) . ']" value="1" ' . checked( in_array( $role, $pvc->options['display']['restrict_display']['roles'], true ), true, false ) . ' />' . esc_html( $role_name ) . '</label>';
866
+ }
867
 
868
+ $html .= '
869
+ <p class="description">' . __( 'Use it to hide the post views counter from selected user roles.', 'post-views-counter' ) . '</p>
870
+ </div>';
871
 
872
+ return $html;
873
+ }
 
874
 
875
+ /**
876
+ * Validate user type.
877
+ *
878
+ * @param array $input Input POST data
879
+ * @param array $field Field options
880
+ * @return array
881
+ */
882
+ public function validate_restrict_display( $input, $field ) {
883
+ // get main instance
884
+ $pvc = Post_Views_Counter();
885
 
886
+ // any groups?
887
+ if ( isset( $input['restrict_display']['groups'] ) ) {
888
+ $groups = [];
889
 
890
+ foreach ( $input['restrict_display']['groups'] as $group => $set ) {
891
+ if ( $group === 'robots' )
892
+ continue;
893
 
894
+ if ( isset( $this->groups[$group] ) )
895
+ $groups[] = $group;
896
+ }
897
 
898
+ $input['restrict_display']['groups'] = array_unique( $groups );
899
+ } else
900
+ $input['restrict_display']['groups'] = [];
 
901
 
902
+ // any roles?
903
+ if ( in_array( 'roles', $input['restrict_display']['groups'], true ) && isset( $input['restrict_display']['roles'] ) ) {
904
+ $roles = [];
 
 
 
905
 
906
+ foreach ( $input['restrict_display']['roles'] as $role => $set ) {
907
+ if ( isset( $this->user_roles[$role] ) )
908
+ $roles[] = $role;
909
+ }
910
 
911
+ $input['restrict_display']['roles'] = array_unique( $roles );
912
+ } else
913
+ $input['restrict_display']['roles'] = [];
914
 
915
  return $input;
916
  }
js/admin-dashboard.js CHANGED
@@ -125,6 +125,11 @@
125
  scaleLabel: {
126
  display: false,
127
  labelString: response.text.yAxes
 
 
 
 
 
128
  }
129
  } ]
130
  },
125
  scaleLabel: {
126
  display: false,
127
  labelString: response.text.yAxes
128
+ },
129
+ ticks: {
130
+ precision: 0,
131
+ beginAtZero: true,
132
+ maxTicksLimit: 12
133
  }
134
  } ]
135
  },
js/admin-settings.js CHANGED
@@ -1,66 +1,65 @@
1
- ( function( $ ) {
2
-
3
- // ready event
4
- $( function() {
5
- var ip_boxes = $( '#pvc_exclude_ips' ).find( '.ip-box' ).length;
6
-
7
- $( '#pvc_exclude_ips .ip-box:first' ).find( '.remove-exclude-ip' ).hide();
8
-
9
- // ask whether to reset options to defaults
10
- $( document ).on( 'click', '.reset_pvc_settings', function() {
11
- return confirm( pvcArgsSettings.resetToDefaults );
12
- } );
13
-
14
- // ask whether to reset views
15
- $( document ).on( 'click', 'input[name="post_views_counter_reset_views"]', function() {
16
- return confirm( pvcArgsSettings.resetViews );
17
- } );
18
-
19
- // remove ip box
20
- $( document ).on( 'click', '.remove-exclude-ip', function( e ) {
21
- e.preventDefault();
22
-
23
- ip_boxes--;
24
-
25
- var parent = $( this ).parent();
26
-
27
- // remove ip box
28
- parent.slideUp( 'fast', function() {
29
- $( this ).remove();
30
- } );
31
- } );
32
-
33
- // add ip box
34
- $( document ).on( 'click', '.add-exclude-ip', function() {
35
- ip_boxes++;
36
-
37
- var parent = $( this ).parents( '#pvc_exclude_ips' ),
38
- new_ip_box = parent.find( '.ip-box:last' ).clone().hide();
39
-
40
- // clear value
41
- new_ip_box.find( 'input' ).val( '' );
42
-
43
- if ( ip_boxes > 1 ) {
44
- new_ip_box.find( '.remove-exclude-ip' ).show();
45
- }
46
-
47
- // add and display new ip box
48
- parent.find( '.ip-box:last' ).after( new_ip_box ).next().slideDown( 'fast' );
49
- } );
50
-
51
- // add current ip
52
- $( document ).on( 'click', '.add-current-ip', function() {
53
- // fill input with user's current ip
54
- $( this ).parents( '#pvc_exclude_ips' ).find( '.ip-box' ).last().find( 'input' ).val( $( this ).attr( 'data-rel' ) );
55
- } );
56
-
57
- // toggle user roles
58
- $( '#pvc_exclude-roles, #pvc_restrict_display-roles' ).on( 'change', function() {
59
- if ( $( this ).is( ':checked' ) )
60
- $( '.pvc_user_roles' ).slideDown( 'fast' );
61
- else
62
- $( '.pvc_user_roles' ).slideUp( 'fast' );
63
- } );
64
- } );
65
-
66
  } )( jQuery );
1
+ ( function( $ ) {
2
+
3
+ // ready event
4
+ $( function() {
5
+ var ip_boxes = $( '#post_views_counter_general_exclude_ips_setting' ).find( '.ip-box' ).length;
6
+
7
+ $( '#post_views_counter_general_exclude_ips_setting .ip-box:first' ).find( '.remove-exclude-ip' ).hide();
8
+
9
+ // ask whether to reset options to defaults
10
+ $( document ).on( 'click', '.reset_pvc_settings', function() {
11
+ return confirm( pvcArgsSettings.resetToDefaults );
12
+ } );
13
+
14
+ // ask whether to reset views
15
+ $( document ).on( 'click', 'input[name="post_views_counter_reset_views"]', function() {
16
+ return confirm( pvcArgsSettings.resetViews );
17
+ } );
18
+
19
+ // remove ip box
20
+ $( document ).on( 'click', '.remove-exclude-ip', function( e ) {
21
+ e.preventDefault();
22
+
23
+ ip_boxes--;
24
+
25
+ var parent = $( this ).parent();
26
+
27
+ // remove ip box
28
+ parent.slideUp( 'fast', function() {
29
+ $( this ).remove();
30
+ } );
31
+ } );
32
+
33
+ // add ip box
34
+ $( document ).on( 'click', '.add-exclude-ip', function() {
35
+ ip_boxes++;
36
+
37
+ var parent = $( this ).parents( '#post_views_counter_general_exclude_ips_setting' ),
38
+ new_ip_box = parent.find( '.ip-box:last' ).clone().hide();
39
+
40
+ // clear value
41
+ new_ip_box.find( 'input' ).val( '' );
42
+
43
+ if ( ip_boxes > 1 )
44
+ new_ip_box.find( '.remove-exclude-ip' ).show();
45
+
46
+ // add and display new ip box
47
+ parent.find( '.ip-box:last' ).after( new_ip_box ).next().slideDown( 'fast' );
48
+ } );
49
+
50
+ // add current ip
51
+ $( document ).on( 'click', '.add-current-ip', function() {
52
+ // fill input with user's current ip
53
+ $( this ).parents( '#post_views_counter_general_exclude_ips_setting' ).find( '.ip-box' ).last().find( 'input' ).val( $( this ).attr( 'data-rel' ) );
54
+ } );
55
+
56
+ // toggle user roles
57
+ $( '#pvc_exclude-roles, #pvc_restrict_display-roles' ).on( 'change', function() {
58
+ if ( $( this ).is( ':checked' ) )
59
+ $( '.pvc_user_roles' ).slideDown( 'fast' );
60
+ else
61
+ $( '.pvc_user_roles' ).slideUp( 'fast' );
62
+ } );
63
+ } );
64
+
 
65
  } )( jQuery );
post-views-counter.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: Post Views Counter
4
  Description: Post Views Counter allows you to display how many times a post, page or custom post type had been viewed in a simple, fast and reliable way.
5
- Version: 1.3.6
6
  Author: Digital Factory
7
  Author URI: http://www.dfactory.eu/
8
  Plugin URI: http://www.dfactory.eu/plugins/post-views-counter/
@@ -31,77 +31,80 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
31
  * Post Views Counter final class.
32
  *
33
  * @class Post_Views_Counter
34
- * @version 1.3.6
35
  */
36
  final class Post_Views_Counter {
37
 
38
  private static $instance;
39
  public $options;
40
- public $defaults = array(
41
- 'general' => array(
42
- 'post_types_count' => array( 'post' ),
43
  'counter_mode' => 'php',
44
  'post_views_column' => true,
45
- 'time_between_counts' => array(
46
- 'number' => 24,
47
- 'type' => 'hours'
48
- ),
49
- 'reset_counts' => array(
50
- 'number' => 30,
51
- 'type' => 'days'
52
- ),
53
- 'flush_interval' => array(
54
- 'number' => 0,
55
- 'type' => 'minutes'
56
- ),
57
- 'exclude' => array(
58
- 'groups' => array(),
59
- 'roles' => array()
60
- ),
61
- 'exclude_ips' => array(),
62
- 'strict_counts' => false,
63
  'restrict_edit_views' => false,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  'deactivation_delete' => false,
65
  'cron_run' => true,
66
  'cron_update' => true,
67
  'update_version' => 1,
68
  'update_notice' => true,
69
  'update_delay_date' => 0
70
- ),
71
- 'display' => array(
72
  'label' => 'Post Views:',
73
- 'post_types_display' => array( 'post' ),
74
- 'page_types_display' => array( 'singular' ),
75
- 'restrict_display' => array(
76
- 'groups' => array(),
77
- 'roles' => array()
78
- ),
79
  'position' => 'after',
80
- 'display_style' => array(
81
  'icon' => true,
82
  'text' => true
83
- ),
84
- 'link_to_post' => true,
85
  'icon_class' => 'dashicons-chart-bar',
86
  'toolbar_statistics' => true
87
- ),
88
- 'version' => '1.3.6'
89
- );
90
 
91
  /**
92
  * Disable object cloning.
 
 
93
  */
94
  public function __clone() {}
95
 
96
  /**
97
  * Disable unserializing of the class.
 
 
98
  */
99
  public function __wakeup() {}
100
 
101
  /**
102
  * Main plugin instance,
103
  * Insures that only one instance of the plugin exists in memory at one time.
104
- *
105
  * @return object
106
  */
107
  public static function instance() {
@@ -119,9 +122,22 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
119
  self::$instance->crawler_detect = new Post_Views_Counter_Crawler_Detect();
120
  // regular setup
121
  } else {
122
- add_action( 'plugins_loaded', array( self::$instance, 'load_textdomain' ) );
123
 
124
  self::$instance->includes();
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  self::$instance->update = new Post_Views_Counter_Update();
126
  self::$instance->settings = new Post_Views_Counter_Settings();
127
  self::$instance->query = new Post_Views_Counter_Query();
@@ -160,6 +176,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
160
  */
161
  private function includes() {
162
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/update.php' );
 
163
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/settings.php' );
164
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/columns.php' );
165
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/query.php' );
@@ -178,30 +195,30 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
178
  */
179
  public function __construct() {
180
  if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
181
- $this->options = array(
182
  'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
183
  'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) )
184
- );
185
  } else {
186
- register_activation_hook( __FILE__, array( $this, 'multisite_activation' ) );
187
- register_deactivation_hook( __FILE__, array( $this, 'multisite_deactivation' ) );
188
 
189
  // settings
190
- $this->options = array(
191
  'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
192
  'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) )
193
- );
194
 
195
  // actions
196
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
197
- add_action( 'wp_loaded', array( $this, 'load_pluggable_functions' ), 10 );
198
- // add_action( 'init', array( $this, 'gutenberg_blocks' ) );
199
- add_action( 'admin_init', array( $this, 'update_notice' ) );
200
- add_action( 'wp_ajax_pvc_dismiss_notice', array( $this, 'dismiss_notice' ) );
201
 
202
  // filters
203
- add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
204
- add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
205
  }
206
  }
207
 
@@ -221,7 +238,13 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
221
 
222
  if ( $this->options['general']['update_version'] < $current_update ) {
223
  // check version, if update version is lower than plugin version, set update notice to true
224
- $this->options['general'] = array_merge( $this->options['general'], array( 'update_version' => $current_update, 'update_notice' => true ) );
 
 
 
 
 
 
225
 
226
  update_option( 'post_views_counter_settings_general', $this->options['general'] );
227
 
@@ -235,7 +258,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
235
  // display current version notice
236
  if ( $this->options['general']['update_notice'] === true ) {
237
  // include notice js, only if needed
238
- add_action( 'admin_print_scripts', array( $this, 'admin_inline_js' ), 999 );
239
 
240
  // get activation date
241
  $activation_date = get_option( 'post_views_counter_activation_date' );
@@ -260,21 +283,22 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
260
  * @param string $html
261
  * @param string $status
262
  * @param bool $paragraph
 
263
  */
264
  public function add_notice( $html = '', $status = 'error', $paragraph = true ) {
265
- $this->notices[] = array(
266
  'html' => $html,
267
  'status' => $status,
268
  'paragraph' => $paragraph
269
- );
270
 
271
- add_action( 'admin_notices', array( $this, 'display_notice' ) );
272
  }
273
 
274
  /**
275
  * Print admin notices.
276
- *
277
- * @return mixed
278
  */
279
  public function display_notice() {
280
  foreach( $this->notices as $notice ) {
@@ -289,8 +313,8 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
289
 
290
  /**
291
  * Print admin scripts.
292
- *
293
- * @return mixed
294
  */
295
  public function admin_inline_js() {
296
  if ( ! current_user_can( 'install_plugins' ) )
@@ -324,6 +348,8 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
324
 
325
  /**
326
  * Dismiss notice.
 
 
327
  */
328
  public function dismiss_notice() {
329
  if ( ! current_user_can( 'install_plugins' ) )
@@ -336,14 +362,29 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
336
  // delay notice
337
  case 'delay':
338
  // set delay period to 1 week from now
339
- $this->options['general'] = array_merge( $this->options['general'], array( 'update_delay_date' => time() + 1209600 ) );
 
 
 
 
 
340
  update_option( 'post_views_counter_settings_general', $this->options['general'] );
341
  break;
342
 
343
  // hide notice
344
  default:
345
- $this->options['general'] = array_merge( $this->options['general'], array( 'update_notice' => false ) );
346
- $this->options['general'] = array_merge( $this->options['general'], array( 'update_delay_date' => 0 ) );
 
 
 
 
 
 
 
 
 
 
347
 
348
  update_option( 'post_views_counter_settings_general', $this->options['general'] );
349
  }
@@ -357,12 +398,13 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
357
  *
358
  * @global object $wpdb
359
  * @param bool $networkwide
 
360
  */
361
  public function multisite_activation( $networkwide ) {
362
  if ( is_multisite() && $networkwide ) {
363
  global $wpdb;
364
 
365
- $activated_blogs = array();
366
  $current_blog_id = $wpdb->blogid;
367
  $blogs_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT blog_id FROM ' . $wpdb->blogs, '' ) );
368
 
@@ -373,7 +415,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
373
  }
374
 
375
  switch_to_blog( $current_blog_id );
376
- update_site_option( 'post_views_counter_activated_blogs', $activated_blogs, array() );
377
  } else
378
  $this->activate_single();
379
  }
@@ -382,6 +424,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
382
  * Single site activation.
383
  *
384
  * @global array $wp_roles
 
385
  */
386
  public function activate_single() {
387
  global $wpdb, $charset_collate;
@@ -416,6 +459,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
416
  *
417
  * @global array $wpdb
418
  * @param bool $networkwide
 
419
  */
420
  public function multisite_deactivation( $networkwide ) {
421
  if ( is_multisite() && $networkwide ) {
@@ -425,7 +469,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
425
  $blogs_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT blog_id FROM ' . $wpdb->blogs, '' ) );
426
 
427
  if ( ! ( $activated_blogs = get_site_option( 'post_views_counter_activated_blogs', false, false ) ) )
428
- $activated_blogs = array();
429
 
430
  foreach ( $blogs_ids as $blog_id ) {
431
  switch_to_blog( $blog_id );
@@ -446,6 +490,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
446
  *
447
  * @global array $wp_roles
448
  * @param bool $multi
 
449
  */
450
  public function deactivate_single( $multi = false ) {
451
  if ( $multi ) {
@@ -467,7 +512,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
467
 
468
  // remove schedule
469
  wp_clear_scheduled_hook( 'pvc_reset_counts' );
470
- remove_action( 'pvc_reset_counts', array( Post_Views_Counter()->cron, 'reset_counts' ) );
471
 
472
  $this->remove_cache_flush();
473
  }
@@ -476,6 +521,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
476
  * Schedule cache flushing if it's not already scheduled.
477
  *
478
  * @param bool $forced
 
479
  */
480
  public function schedule_cache_flush( $forced = true ) {
481
  if ( $forced || ! wp_next_scheduled( 'pvc_flush_cached_counts' ) )
@@ -484,57 +530,66 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
484
 
485
  /**
486
  * Remove scheduled cache flush and the corresponding action.
 
 
487
  */
488
  public function remove_cache_flush() {
489
  wp_clear_scheduled_hook( 'pvc_flush_cached_counts' );
490
- remove_action( 'pvc_flush_cached_counts', array( Post_Views_Counter()->cron, 'flush_cached_counts' ) );
491
  }
492
 
493
  /**
494
- * Load text domain.
 
 
495
  */
496
  public function load_textdomain() {
497
  load_plugin_textdomain( 'post-views-counter', false, POST_VIEWS_COUNTER_REL_PATH . 'languages/' );
498
  }
499
 
500
  /**
501
- * Load pluggable template functions.
 
 
502
  */
503
  public function load_pluggable_functions() {
504
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
505
  }
506
 
507
  /**
508
- * Add Gutenberg blocks.
 
 
509
  */
510
  public function gutenberg_blocks() {
511
- wp_register_script( 'pvc-admin-block-views', POST_VIEWS_COUNTER_URL . '/js/admin-block.js', array( 'wp-blocks', 'wp-element', 'wp-i18n' ) );
512
 
513
- register_block_type( 'post-views-counter/views', array( 'editor_script' => 'pvc-admin-block-views' ) );
514
  }
515
 
516
  /**
517
  * Enqueue admin scripts and styles.
518
  *
519
  * @global string $post_type
520
- * @param string $page
 
521
  */
522
  public function admin_enqueue_scripts( $page ) {
523
  wp_register_style( 'pvc-admin', POST_VIEWS_COUNTER_URL . '/css/admin.css' );
524
 
525
- wp_register_script( 'pvc-admin-settings', POST_VIEWS_COUNTER_URL . '/js/admin-settings.js', array( 'jquery' ), $this->defaults['version'] );
526
- wp_register_script( 'pvc-admin-post', POST_VIEWS_COUNTER_URL . '/js/admin-post.js', array( 'jquery' ), $this->defaults['version'] );
527
- wp_register_script( 'pvc-admin-quick-edit', POST_VIEWS_COUNTER_URL . '/js/admin-quick-edit.js', array( 'jquery', 'inline-edit-post' ), $this->defaults['version'] );
528
 
529
  // load on PVC settings page
530
  if ( $page === 'settings_page_post-views-counter' ) {
531
  wp_enqueue_script( 'pvc-admin-settings' );
532
 
533
  wp_localize_script(
534
- 'pvc-admin-settings', 'pvcArgsSettings', array(
535
  'resetToDefaults' => __( 'Are you sure you want to reset these settings to defaults?', 'post-views-counter' ),
536
  'resetViews' => __( 'Are you sure you want to delete all existing data?', 'post-views-counter' )
537
- )
538
  );
539
 
540
  wp_enqueue_style( 'pvc-admin' );
@@ -566,7 +621,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
566
  wp_enqueue_script( 'pvc-admin-quick-edit' );
567
  // widgets
568
  } elseif ( $page === 'widgets.php' )
569
- wp_enqueue_script( 'pvc-admin-widgets', POST_VIEWS_COUNTER_URL . '/js/admin-widgets.js', array( 'jquery' ), $this->defaults['version'] );
570
  // media
571
  elseif ( $page === 'upload.php' )
572
  wp_enqueue_style( 'pvc-admin' );
@@ -587,7 +642,10 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
587
 
588
  if ( $file == $plugin ) {
589
  return array_merge(
590
- $links, array( sprintf( '<a href="http://www.dfactory.eu/support/forum/post-views-counter/" target="_blank">%s</a>', __( 'Support', 'post-views-counter' ) ) )
 
 
 
591
  );
592
  }
593
 
2
  /*
3
  Plugin Name: Post Views Counter
4
  Description: Post Views Counter allows you to display how many times a post, page or custom post type had been viewed in a simple, fast and reliable way.
5
+ Version: 1.3.7
6
  Author: Digital Factory
7
  Author URI: http://www.dfactory.eu/
8
  Plugin URI: http://www.dfactory.eu/plugins/post-views-counter/
31
  * Post Views Counter final class.
32
  *
33
  * @class Post_Views_Counter
34
+ * @version 1.3.7
35
  */
36
  final class Post_Views_Counter {
37
 
38
  private static $instance;
39
  public $options;
40
+ public $defaults = [
41
+ 'general' => [
42
+ 'post_types_count' => [ 'post' ],
43
  'counter_mode' => 'php',
44
  'post_views_column' => true,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  'restrict_edit_views' => false,
46
+ 'time_between_counts' => [
47
+ 'number' => 24,
48
+ 'type' => 'hours'
49
+ ],
50
+ 'reset_counts' => [
51
+ 'number' => 30,
52
+ 'type' => 'days'
53
+ ],
54
+ 'flush_interval' => [
55
+ 'number' => 0,
56
+ 'type' => 'minutes'
57
+ ],
58
+ 'exclude' => [
59
+ 'groups' => [],
60
+ 'roles' => []
61
+ ],
62
+ 'exclude_ips' => [],
63
+ 'strict_counts' => false,
64
  'deactivation_delete' => false,
65
  'cron_run' => true,
66
  'cron_update' => true,
67
  'update_version' => 1,
68
  'update_notice' => true,
69
  'update_delay_date' => 0
70
+ ],
71
+ 'display' => [
72
  'label' => 'Post Views:',
73
+ 'post_types_display' => [ 'post' ],
74
+ 'page_types_display' => [ 'singular' ],
75
+ 'restrict_display' => [
76
+ 'groups' => [],
77
+ 'roles' => []
78
+ ],
79
  'position' => 'after',
80
+ 'display_style' => [
81
  'icon' => true,
82
  'text' => true
83
+ ],
 
84
  'icon_class' => 'dashicons-chart-bar',
85
  'toolbar_statistics' => true
86
+ ],
87
+ 'version' => '1.3.7'
88
+ ];
89
 
90
  /**
91
  * Disable object cloning.
92
+ *
93
+ * @return void
94
  */
95
  public function __clone() {}
96
 
97
  /**
98
  * Disable unserializing of the class.
99
+ *
100
+ * @return void
101
  */
102
  public function __wakeup() {}
103
 
104
  /**
105
  * Main plugin instance,
106
  * Insures that only one instance of the plugin exists in memory at one time.
107
+ *
108
  * @return object
109
  */
110
  public static function instance() {
122
  self::$instance->crawler_detect = new Post_Views_Counter_Crawler_Detect();
123
  // regular setup
124
  } else {
125
+ add_action( 'plugins_loaded', [ self::$instance, 'load_textdomain' ] );
126
 
127
  self::$instance->includes();
128
+
129
+ // create settings API
130
+ self::$instance->settings_api = new Post_Views_Counter_Settings_API(
131
+ [
132
+ 'object' => self::$instance,
133
+ 'prefix' => 'post_views_counter',
134
+ 'slug' => 'post-views-counter',
135
+ 'domain' => 'post-views-counter',
136
+ 'plugin' => 'Post Views Counter',
137
+ 'plugin_url' => 'POST_VIEWS_COUNTER_URL'
138
+ ]
139
+ );
140
+
141
  self::$instance->update = new Post_Views_Counter_Update();
142
  self::$instance->settings = new Post_Views_Counter_Settings();
143
  self::$instance->query = new Post_Views_Counter_Query();
176
  */
177
  private function includes() {
178
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/update.php' );
179
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/settings-api.php' );
180
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/settings.php' );
181
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/columns.php' );
182
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/query.php' );
195
  */
196
  public function __construct() {
197
  if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
198
+ $this->options = [
199
  'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
200
  'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) )
201
+ ];
202
  } else {
203
+ register_activation_hook( __FILE__, [ $this, 'multisite_activation' ] );
204
+ register_deactivation_hook( __FILE__, [ $this, 'multisite_deactivation' ] );
205
 
206
  // settings
207
+ $this->options = [
208
  'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
209
  'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) )
210
+ ];
211
 
212
  // actions
213
+ add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
214
+ add_action( 'wp_loaded', [ $this, 'load_pluggable_functions' ], 10 );
215
+ // add_action( 'init', [ $this, 'gutenberg_blocks' ] );
216
+ add_action( 'admin_init', [ $this, 'update_notice' ] );
217
+ add_action( 'wp_ajax_pvc_dismiss_notice', [ $this, 'dismiss_notice' ] );
218
 
219
  // filters
220
+ add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 10, 2 );
221
+ add_filter( 'plugin_action_links', [ $this, 'plugin_action_links' ], 10, 2 );
222
  }
223
  }
224
 
238
 
239
  if ( $this->options['general']['update_version'] < $current_update ) {
240
  // check version, if update version is lower than plugin version, set update notice to true
241
+ $this->options['general'] = array_merge(
242
+ $this->options['general'],
243
+ [
244
+ 'update_version' => $current_update,
245
+ 'update_notice' => true
246
+ ]
247
+ );
248
 
249
  update_option( 'post_views_counter_settings_general', $this->options['general'] );
250
 
258
  // display current version notice
259
  if ( $this->options['general']['update_notice'] === true ) {
260
  // include notice js, only if needed
261
+ add_action( 'admin_print_scripts', [ $this, 'admin_inline_js' ], 999 );
262
 
263
  // get activation date
264
  $activation_date = get_option( 'post_views_counter_activation_date' );
283
  * @param string $html
284
  * @param string $status
285
  * @param bool $paragraph
286
+ * @return void
287
  */
288
  public function add_notice( $html = '', $status = 'error', $paragraph = true ) {
289
+ $this->notices[] = [
290
  'html' => $html,
291
  'status' => $status,
292
  'paragraph' => $paragraph
293
+ ];
294
 
295
+ add_action( 'admin_notices', [ $this, 'display_notice' ] );
296
  }
297
 
298
  /**
299
  * Print admin notices.
300
+ *
301
+ * @return void
302
  */
303
  public function display_notice() {
304
  foreach( $this->notices as $notice ) {
313
 
314
  /**
315
  * Print admin scripts.
316
+ *
317
+ * @return void
318
  */
319
  public function admin_inline_js() {
320
  if ( ! current_user_can( 'install_plugins' ) )
348
 
349
  /**
350
  * Dismiss notice.
351
+ *
352
+ * @return void
353
  */
354
  public function dismiss_notice() {
355
  if ( ! current_user_can( 'install_plugins' ) )
362
  // delay notice
363
  case 'delay':
364
  // set delay period to 1 week from now
365
+ $this->options['general'] = array_merge(
366
+ $this->options['general'],
367
+ [
368
+ 'update_delay_date' => time() + 1209600
369
+ ]
370
+ );
371
  update_option( 'post_views_counter_settings_general', $this->options['general'] );
372
  break;
373
 
374
  // hide notice
375
  default:
376
+ $this->options['general'] = array_merge(
377
+ $this->options['general'],
378
+ [
379
+ 'update_notice' => false
380
+ ]
381
+ );
382
+ $this->options['general'] = array_merge(
383
+ $this->options['general'],
384
+ [
385
+ 'update_delay_date' => 0
386
+ ]
387
+ );
388
 
389
  update_option( 'post_views_counter_settings_general', $this->options['general'] );
390
  }
398
  *
399
  * @global object $wpdb
400
  * @param bool $networkwide
401
+ * @return void
402
  */
403
  public function multisite_activation( $networkwide ) {
404
  if ( is_multisite() && $networkwide ) {
405
  global $wpdb;
406
 
407
+ $activated_blogs = [];
408
  $current_blog_id = $wpdb->blogid;
409
  $blogs_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT blog_id FROM ' . $wpdb->blogs, '' ) );
410
 
415
  }
416
 
417
  switch_to_blog( $current_blog_id );
418
+ update_site_option( 'post_views_counter_activated_blogs', $activated_blogs, [] );
419
  } else
420
  $this->activate_single();
421
  }
424
  * Single site activation.
425
  *
426
  * @global array $wp_roles
427
+ * @return void
428
  */
429
  public function activate_single() {
430
  global $wpdb, $charset_collate;
459
  *
460
  * @global array $wpdb
461
  * @param bool $networkwide
462
+ * @return void
463
  */
464
  public function multisite_deactivation( $networkwide ) {
465
  if ( is_multisite() && $networkwide ) {
469
  $blogs_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT blog_id FROM ' . $wpdb->blogs, '' ) );
470
 
471
  if ( ! ( $activated_blogs = get_site_option( 'post_views_counter_activated_blogs', false, false ) ) )
472
+ $activated_blogs = [];
473
 
474
  foreach ( $blogs_ids as $blog_id ) {
475
  switch_to_blog( $blog_id );
490
  *
491
  * @global array $wp_roles
492
  * @param bool $multi
493
+ * @return void
494
  */
495
  public function deactivate_single( $multi = false ) {
496
  if ( $multi ) {
512
 
513
  // remove schedule
514
  wp_clear_scheduled_hook( 'pvc_reset_counts' );
515
+ remove_action( 'pvc_reset_counts', [ Post_Views_Counter()->cron, 'reset_counts' ] );
516
 
517
  $this->remove_cache_flush();
518
  }
521
  * Schedule cache flushing if it's not already scheduled.
522
  *
523
  * @param bool $forced
524
+ * @return void
525
  */
526
  public function schedule_cache_flush( $forced = true ) {
527
  if ( $forced || ! wp_next_scheduled( 'pvc_flush_cached_counts' ) )
530
 
531
  /**
532
  * Remove scheduled cache flush and the corresponding action.
533
+ *
534
+ * @return void
535
  */
536
  public function remove_cache_flush() {
537
  wp_clear_scheduled_hook( 'pvc_flush_cached_counts' );
538
+ remove_action( 'pvc_flush_cached_counts', [ Post_Views_Counter()->cron, 'flush_cached_counts' ] );
539
  }
540
 
541
  /**
542
+ * Load text domain..
543
+ *
544
+ * @return void
545
  */
546
  public function load_textdomain() {
547
  load_plugin_textdomain( 'post-views-counter', false, POST_VIEWS_COUNTER_REL_PATH . 'languages/' );
548
  }
549
 
550
  /**
551
+ * Load pluggable template functions..
552
+ *
553
+ * @return void
554
  */
555
  public function load_pluggable_functions() {
556
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
557
  }
558
 
559
  /**
560
+ * Add Gutenberg blocks..
561
+ *
562
+ * @return void
563
  */
564
  public function gutenberg_blocks() {
565
+ wp_register_script( 'pvc-admin-block-views', POST_VIEWS_COUNTER_URL . '/js/admin-block.js', [ 'wp-blocks', 'wp-element', 'wp-i18n' ] );
566
 
567
+ register_block_type( 'post-views-counter/views', [ 'editor_script' => 'pvc-admin-block-views' ] );
568
  }
569
 
570
  /**
571
  * Enqueue admin scripts and styles.
572
  *
573
  * @global string $post_type
574
+ * @param string $page.
575
+ * @return void
576
  */
577
  public function admin_enqueue_scripts( $page ) {
578
  wp_register_style( 'pvc-admin', POST_VIEWS_COUNTER_URL . '/css/admin.css' );
579
 
580
+ wp_register_script( 'pvc-admin-settings', POST_VIEWS_COUNTER_URL . '/js/admin-settings.js', [ 'jquery' ], $this->defaults['version'] );
581
+ wp_register_script( 'pvc-admin-post', POST_VIEWS_COUNTER_URL . '/js/admin-post.js', [ 'jquery' ], $this->defaults['version'] );
582
+ wp_register_script( 'pvc-admin-quick-edit', POST_VIEWS_COUNTER_URL . '/js/admin-quick-edit.js', [ 'jquery', 'inline-edit-post' ], $this->defaults['version'] );
583
 
584
  // load on PVC settings page
585
  if ( $page === 'settings_page_post-views-counter' ) {
586
  wp_enqueue_script( 'pvc-admin-settings' );
587
 
588
  wp_localize_script(
589
+ 'pvc-admin-settings', 'pvcArgsSettings', [
590
  'resetToDefaults' => __( 'Are you sure you want to reset these settings to defaults?', 'post-views-counter' ),
591
  'resetViews' => __( 'Are you sure you want to delete all existing data?', 'post-views-counter' )
592
+ ]
593
  );
594
 
595
  wp_enqueue_style( 'pvc-admin' );
621
  wp_enqueue_script( 'pvc-admin-quick-edit' );
622
  // widgets
623
  } elseif ( $page === 'widgets.php' )
624
+ wp_enqueue_script( 'pvc-admin-widgets', POST_VIEWS_COUNTER_URL . '/js/admin-widgets.js', [ 'jquery' ], $this->defaults['version'] );
625
  // media
626
  elseif ( $page === 'upload.php' )
627
  wp_enqueue_style( 'pvc-admin' );
642
 
643
  if ( $file == $plugin ) {
644
  return array_merge(
645
+ $links,
646
+ [
647
+ sprintf( '<a href="http://www.dfactory.eu/support/forum/post-views-counter/" target="_blank">%s</a>', __( 'Support', 'post-views-counter' ) )
648
+ ]
649
  );
650
  }
651
 
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: counter, hits, posts, postviews, post views, views, count, statistics, sta
5
  Requires at least: 5.0
6
  Requires PHP: 5.2.4
7
  Tested up to: 5.8.1
8
- Stable tag: 1.3.6
9
  License: MIT License
10
  License URI: http://opensource.org/licenses/MIT
11
 
@@ -62,6 +62,9 @@ No questions yet.
62
 
63
  == Changelog ==
64
 
 
 
 
65
  = 1.3.6 =
66
  * Fix: Option to hide admin bar chart
67
 
@@ -219,5 +222,5 @@ Initial release
219
 
220
  == Upgrade Notice ==
221
 
222
- = 1.3.6 =
223
- * Fix: Option to hide admin bar chart
5
  Requires at least: 5.0
6
  Requires PHP: 5.2.4
7
  Tested up to: 5.8.1
8
+ Stable tag: 1.3.7
9
  License: MIT License
10
  License URI: http://opensource.org/licenses/MIT
11
 
62
 
63
  == Changelog ==
64
 
65
+ = 1.3.7 =
66
+ * Tweak: Implemented internal settings API
67
+
68
  = 1.3.6 =
69
  * Fix: Option to hide admin bar chart
70
 
222
 
223
  == Upgrade Notice ==
224
 
225
+ = 1.3.7 =
226
+ * Tweak: Implemented internal settings API