Post Views Counter - Version 1.2.8

Version Description

  • New: Multisite compatibility
  • Fix: Undefined index post_views_column on post_views_counter/includes/settings.php
  • Tweak: Improved user IP handling
Download this release

Release Info

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

Code changes from version 1.2.7 to 1.2.8

includes/counter.php CHANGED
@@ -42,22 +42,27 @@ class Post_Views_Counter_Counter {
42
  // get user id, from current user or static var in rest api request
43
  $user_id = get_current_user_id();
44
 
 
 
 
45
  // empty id?
46
  if ( empty( $id ) )
47
  return;
 
 
48
 
49
  // get ips
50
  $ips = Post_Views_Counter()->options['general']['exclude_ips'];
51
 
52
  // whether to count this ip
53
- if ( ! empty( $ips ) && filter_var( preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) ) {
54
  // check ips
55
  foreach ( $ips as $ip ) {
56
  if ( strpos( $ip, '*' ) !== false ) {
57
- if ( $this->ipv4_in_range( $_SERVER['REMOTE_ADDR'], $ip ) )
58
  return;
59
  } else {
60
- if ( $_SERVER['REMOTE_ADDR'] === $ip )
61
  return;
62
  }
63
  }
@@ -192,11 +197,14 @@ class Post_Views_Counter_Counter {
192
  if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) )
193
  return;
194
 
 
 
 
195
  // is cookie set?
196
- if ( isset( $_COOKIE['pvc_visits'] ) && ! empty( $_COOKIE['pvc_visits'] ) ) {
197
  $visited_posts = $expirations = array();
198
 
199
- foreach ( $_COOKIE['pvc_visits'] as $content ) {
200
  // is cookie valid?
201
  if ( preg_match( '/^(([0-9]+b[0-9]+a?)+)$/', $content ) === 1 ) {
202
  // get single id with expiration
@@ -229,10 +237,13 @@ class Post_Views_Counter_Counter {
229
  private function save_cookie( $id, $cookie = array(), $expired = true ) {
230
  $expiration = $this->get_timestamp( Post_Views_Counter()->options['general']['time_between_counts']['type'], Post_Views_Counter()->options['general']['time_between_counts']['number'] );
231
 
 
 
 
232
  // is this a new cookie?
233
  if ( empty( $cookie ) ) {
234
  // set cookie
235
- setcookie( 'pvc_visits[0]', $expiration . 'b' . $id, $expiration, COOKIEPATH, COOKIE_DOMAIN, (isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? true : false ), true );
236
  } else {
237
  if ( $expired ) {
238
  // add new id or chang expiration date if id already exists
@@ -294,7 +305,7 @@ class Post_Views_Counter_Counter {
294
 
295
  foreach ( $cookies as $key => $value ) {
296
  // set cookie
297
- setcookie( 'pvc_visits[' . $key . ']', $value, $cookie['expiration'], COOKIEPATH, COOKIE_DOMAIN, (isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? true : false ), true );
298
  }
299
  }
300
  }
@@ -539,7 +550,7 @@ class Post_Views_Counter_Counter {
539
  * @param string $range IP range
540
  * @return boolean Whether IP is in range
541
  */
542
- function ipv4_in_range( $ip, $range ) {
543
  $start = str_replace( '*', '0', $range );
544
  $end = str_replace( '*', '255', $range );
545
  $ip = (float) sprintf( "%u", ip2long( $ip ) );
@@ -547,6 +558,41 @@ class Post_Views_Counter_Counter {
547
  return ( $ip >= (float) sprintf( "%u", ip2long( $start ) ) && $ip <= (float) sprintf( "%u", ip2long( $end ) ) );
548
  }
549
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
  /**
551
  * Register REST API endpoints.
552
  *
42
  // get user id, from current user or static var in rest api request
43
  $user_id = get_current_user_id();
44
 
45
+ // get user IP address
46
+ $user_ip = $this->get_user_ip();
47
+
48
  // empty id?
49
  if ( empty( $id ) )
50
  return;
51
+
52
+ do_action( 'pvc_before_check_visit', $id, $user_id, $user_ip );
53
 
54
  // get ips
55
  $ips = Post_Views_Counter()->options['general']['exclude_ips'];
56
 
57
  // whether to count this ip
58
+ if ( ! empty( $ips ) && filter_var( preg_replace( '/[^0-9a-fA-F:., ]/', '', $user_ip ), FILTER_VALIDATE_IP ) ) {
59
  // check ips
60
  foreach ( $ips as $ip ) {
61
  if ( strpos( $ip, '*' ) !== false ) {
62
+ if ( $this->ipv4_in_range( $user_ip, $ip ) )
63
  return;
64
  } else {
65
+ if ( $user_ip === $ip )
66
  return;
67
  }
68
  }
197
  if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) )
198
  return;
199
 
200
+ // assign cookie name
201
+ $cookie_name = 'pvc_visits' . ( is_multisite() ? '_' . get_current_blog_id() : '' );
202
+
203
  // is cookie set?
204
+ if ( isset( $_COOKIE[$cookie_name] ) && ! empty( $_COOKIE[$cookie_name] ) ) {
205
  $visited_posts = $expirations = array();
206
 
207
+ foreach ( $_COOKIE[$cookie_name] as $content ) {
208
  // is cookie valid?
209
  if ( preg_match( '/^(([0-9]+b[0-9]+a?)+)$/', $content ) === 1 ) {
210
  // get single id with expiration
237
  private function save_cookie( $id, $cookie = array(), $expired = true ) {
238
  $expiration = $this->get_timestamp( Post_Views_Counter()->options['general']['time_between_counts']['type'], Post_Views_Counter()->options['general']['time_between_counts']['number'] );
239
 
240
+ // assign cookie name
241
+ $cookie_name = 'pvc_visits' . ( is_multisite() ? '_' . get_current_blog_id() : '' );
242
+
243
  // is this a new cookie?
244
  if ( empty( $cookie ) ) {
245
  // set cookie
246
+ setcookie( $cookie_name . '[0]', $expiration . 'b' . $id, $expiration, COOKIEPATH, COOKIE_DOMAIN, (isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? true : false ), true );
247
  } else {
248
  if ( $expired ) {
249
  // add new id or chang expiration date if id already exists
305
 
306
  foreach ( $cookies as $key => $value ) {
307
  // set cookie
308
+ setcookie( $cookie_name . '[' . $key . ']', $value, $cookie['expiration'], COOKIEPATH, COOKIE_DOMAIN, (isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? true : false ), true );
309
  }
310
  }
311
  }
550
  * @param string $range IP range
551
  * @return boolean Whether IP is in range
552
  */
553
+ public function ipv4_in_range( $ip, $range ) {
554
  $start = str_replace( '*', '0', $range );
555
  $end = str_replace( '*', '255', $range );
556
  $ip = (float) sprintf( "%u", ip2long( $ip ) );
558
  return ( $ip >= (float) sprintf( "%u", ip2long( $start ) ) && $ip <= (float) sprintf( "%u", ip2long( $end ) ) );
559
  }
560
 
561
+ /**
562
+ * Get user real IP address.
563
+ *
564
+ * @return string
565
+ */
566
+ public function get_user_ip() {
567
+ $ip_keys = array( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' );
568
+ foreach ( $ip_keys as $key ) {
569
+ if ( array_key_exists( $key, $_SERVER ) === true ) {
570
+ foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
571
+ // trim for safety measures
572
+ $ip = trim( $ip );
573
+ // attempt to validate IP
574
+ if ( $this->validate_user_ip( $ip ) ) {
575
+ return $ip;
576
+ }
577
+ }
578
+ }
579
+ }
580
+ return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
581
+ }
582
+
583
+ /**
584
+ * Ensure an ip address is both a valid IP and does not fall within a private network range.
585
+ *
586
+ * @param $ip
587
+ * @return bool
588
+ */
589
+ public function validate_user_ip( $ip ) {
590
+ if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) === false ) {
591
+ return false;
592
+ }
593
+ return true;
594
+ }
595
+
596
  /**
597
  * Register REST API endpoints.
598
  *
includes/functions.php CHANGED
@@ -67,7 +67,8 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
67
  $range = array();
68
  $defaults = array(
69
  'fields' => 'views',
70
- 'post_type' => 'post',
 
71
  'views_query' => array(
72
  'year' => '',
73
  'month' => '',
@@ -78,9 +79,25 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
78
 
79
  $args = apply_filters( 'pvc_get_views_args', array_merge( $defaults, $args ) );
80
 
81
- // check post type
82
- if ( empty( $args['post_type'] ) )
 
 
 
 
 
 
 
 
83
  $args['post_type'] = $defaults['post_type'];
 
 
 
 
 
 
 
 
84
 
85
  // check fields
86
  if ( ! in_array( $args['fields'], array( 'views', 'date=>views' ), true ) )
@@ -106,6 +123,8 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
106
  if ( ! empty( $args['views_query']['day'] ) )
107
  $day = str_pad( (int) $args['views_query']['day'], 2, 0, STR_PAD_LEFT );
108
 
 
 
109
  // year
110
  if ( isset( $year ) ) {
111
  // year, week
@@ -132,9 +151,9 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
132
  // get month of sunday
133
  $sunday_month = $date->format( 'm' );
134
 
135
- $query = " AND pvc.type = 0 AND pvc.period >= '" . $year . $monday_month . $monday . "' AND pvc.period <= " . $date->format( 'Y' ) . $sunday_month . $date->format( 'd' );
136
  } else
137
- $query = " AND pvc.type = 1 AND pvc.period = '" . $year . $week . "'";
138
  // year, month
139
  } elseif ( isset( $month ) ) {
140
  // year, month, day
@@ -143,7 +162,7 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
143
  // prepare range
144
  $range[(string) ( $year . $month . $day )] = 0;
145
 
146
- $query = " AND pvc.type = 0 AND pvc.period = '" . $year . $month . $day . "'";
147
  // year, month
148
  } else {
149
  if ( $args['fields'] === 'date=>views' ) {
@@ -158,9 +177,9 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
158
  $range[(string) ( $year . $month . str_pad( $i, 2, 0, STR_PAD_LEFT ) )] = 0;
159
  }
160
 
161
- $query = " AND pvc.type = 0 AND pvc.period >= '" . $year . $month . "01' AND pvc.period <= " . $year . $month . $last;
162
  } else
163
- $query = " AND pvc.type = 2 AND pvc.period = '" . $year . $month . "'";
164
  }
165
  // year
166
  } else {
@@ -173,34 +192,34 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
173
  // create date
174
  $date = new DateTime( $year . '-12-01' );
175
 
176
- $query = " AND pvc.type = 2 AND pvc.period >= '" . $year . "01' AND pvc.period <= " . $year . "12";
177
  } else
178
- $query = " AND pvc.type = 3 AND pvc.period = '" . $year . "'";
179
  }
180
  // month
181
  } elseif ( isset( $month ) ) {
182
  // month, day
183
  if ( isset( $day ) ) {
184
- $query = " AND pvc.type = 0 AND RIGHT( pvc.period, 4 ) = '" . $month . $day . "'";
185
  // month
186
  } else {
187
- $query = " AND pvc.type = 2 AND RIGHT( pvc.period, 2 ) = '" . $month . "'";
188
  }
189
  // week
190
  } elseif ( isset( $week ) ) {
191
- $query = " AND pvc.type = 1 AND RIGHT( pvc.period, 2 ) = '" . $week . "'";
192
  // day
193
  } elseif ( isset( $day ) ) {
194
- $query = " AND pvc.type = 0 AND RIGHT( pvc.period, 2 ) = '" . $day . "'";
195
  }
196
 
197
  global $wpdb;
198
 
199
  $query = "SELECT " . ( $args['fields'] === 'date=>views' ? 'pvc.period, ' : '' ) . "SUM( IFNULL( pvc.count, 0 ) ) AS post_views
200
  FROM " . $wpdb->prefix . "posts wpp
201
- LEFT JOIN " . $wpdb->prefix . "post_views pvc ON pvc.id = wpp.ID " . $query . "
202
- WHERE wpp.post_type = '" . $args['post_type'] . "'
203
- GROUP BY pvc.period
204
  HAVING post_views > 0";
205
 
206
  // get cached data
@@ -218,13 +237,12 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
218
  }
219
 
220
  $post_views = $range;
221
- } else {
222
  $post_views = (int) $wpdb->get_var( $query );
223
- }
224
 
225
  // set the cache expiration, 5 minutes by default
226
  $expire = absint( apply_filters( 'pvc_object_cache_expire', 5 * 60 ) );
227
-
228
  wp_cache_add( md5( $query ), $post_views, 'pvc-get_views', $expire );
229
  }
230
 
@@ -325,7 +343,9 @@ if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
325
  'show_post_views' => true,
326
  'show_post_thumbnail' => false,
327
  'show_post_excerpt' => false,
328
- 'no_posts_message' => __( 'No Posts', 'post-views-counter' )
 
 
329
  );
330
 
331
  $args = apply_filters( 'pvc_most_viewed_posts_args', wp_parse_args( $args, $defaults ) );
@@ -351,16 +371,18 @@ if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
351
 
352
  $html .= '
353
  <li>';
 
 
354
 
355
  if ( $args['show_post_thumbnail'] && has_post_thumbnail( $post->ID ) ) {
356
  $html .= '
357
- <span class="post-thumbnail">
358
- ' . get_the_post_thumbnail( $post->ID, $args['thumbnail_size'] ) . '
359
- </span>';
360
  }
361
 
362
  $html .= '
363
- <a class="post-title" href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a>' . ($args['show_post_views'] ? ' <span class="count">(' . number_format_i18n( pvc_get_post_views( $post->ID ) ) . ')</span>' : '');
364
 
365
  $excerpt = '';
366
 
@@ -376,7 +398,10 @@ if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
376
 
377
  if ( ! empty( $excerpt ) )
378
  $html .= '
379
- <div class="post-excerpt">' . esc_html( $excerpt ) . '</div>';
 
 
 
380
 
381
  $html .= '
382
  </li>';
67
  $range = array();
68
  $defaults = array(
69
  'fields' => 'views',
70
+ 'post_id' => '',
71
+ 'post_type' => '',
72
  'views_query' => array(
73
  'year' => '',
74
  'month' => '',
79
 
80
  $args = apply_filters( 'pvc_get_views_args', array_merge( $defaults, $args ) );
81
 
82
+ // check post types
83
+ if ( is_array( $args['post_type'] ) && ! empty( $args['post_type'] ) ) {
84
+ $post_types = array();
85
+
86
+ foreach( $args['post_type'] as $post_type ) {
87
+ $post_types[] = "'" . $post_type . "'";
88
+ }
89
+
90
+ $args['post_type'] = implode( ', ', $post_types );
91
+ } elseif ( ! is_string( $args['post_type'] ) )
92
  $args['post_type'] = $defaults['post_type'];
93
+ else
94
+ $args['post_type'] = "'" . $args['post_type'] . "'";
95
+
96
+ // check post ids
97
+ if ( is_array( $args['post_id'] ) && ! empty( $args['post_id'] ) )
98
+ $args['post_id'] = implode( ', ', array_unique( array_map( 'intval', $args['post_id'] ) ) );
99
+ else
100
+ $args['post_id'] = (int) $args['post_id'];
101
 
102
  // check fields
103
  if ( ! in_array( $args['fields'], array( 'views', 'date=>views' ), true ) )
123
  if ( ! empty( $args['views_query']['day'] ) )
124
  $day = str_pad( (int) $args['views_query']['day'], 2, 0, STR_PAD_LEFT );
125
 
126
+ $views_query = '';
127
+
128
  // year
129
  if ( isset( $year ) ) {
130
  // year, week
151
  // get month of sunday
152
  $sunday_month = $date->format( 'm' );
153
 
154
+ $views_query = " AND pvc.type = 0 AND pvc.period >= '" . $year . $monday_month . $monday . "' AND pvc.period <= '" . $date->format( 'Y' ) . $sunday_month . $date->format( 'd' ) . "'";
155
  } else
156
+ $views_query = " AND pvc.type = 1 AND pvc.period = '" . $year . $week . "'";
157
  // year, month
158
  } elseif ( isset( $month ) ) {
159
  // year, month, day
162
  // prepare range
163
  $range[(string) ( $year . $month . $day )] = 0;
164
 
165
+ $views_query = " AND pvc.type = 0 AND pvc.period = '" . $year . $month . $day . "'";
166
  // year, month
167
  } else {
168
  if ( $args['fields'] === 'date=>views' ) {
177
  $range[(string) ( $year . $month . str_pad( $i, 2, 0, STR_PAD_LEFT ) )] = 0;
178
  }
179
 
180
+ $views_query = " AND pvc.type = 0 AND pvc.period >= '" . $year . $month . "01' AND pvc.period <= '" . $year . $month . $last . "'";
181
  } else
182
+ $views_query = " AND pvc.type = 2 AND pvc.period = '" . $year . $month . "'";
183
  }
184
  // year
185
  } else {
192
  // create date
193
  $date = new DateTime( $year . '-12-01' );
194
 
195
+ $views_query = " AND pvc.type = 2 AND pvc.period >= '" . $year . "01' AND pvc.period <= '" . $year . "12'";
196
  } else
197
+ $views_query = " AND pvc.type = 3 AND pvc.period = '" . $year . "'";
198
  }
199
  // month
200
  } elseif ( isset( $month ) ) {
201
  // month, day
202
  if ( isset( $day ) ) {
203
+ $views_query = " AND pvc.type = 0 AND RIGHT( pvc.period, 4 ) = '" . $month . $day . "'";
204
  // month
205
  } else {
206
+ $views_query = " AND pvc.type = 2 AND RIGHT( pvc.period, 2 ) = '" . $month . "'";
207
  }
208
  // week
209
  } elseif ( isset( $week ) ) {
210
+ $views_query = " AND pvc.type = 1 AND RIGHT( pvc.period, 2 ) = '" . $week . "'";
211
  // day
212
  } elseif ( isset( $day ) ) {
213
+ $views_query = " AND pvc.type = 0 AND RIGHT( pvc.period, 2 ) = '" . $day . "'";
214
  }
215
 
216
  global $wpdb;
217
 
218
  $query = "SELECT " . ( $args['fields'] === 'date=>views' ? 'pvc.period, ' : '' ) . "SUM( IFNULL( pvc.count, 0 ) ) AS post_views
219
  FROM " . $wpdb->prefix . "posts wpp
220
+ LEFT JOIN " . $wpdb->prefix . "post_views pvc ON pvc.id = wpp.ID" . ( $views_query !== '' ? ' ' . $views_query : ' AND pvc.type = 4' ) . ( ! empty( $args['post_id'] ) ? ' AND pvc.id IN (' . $args['post_id'] . ')' : '' ) . "
221
+ " . ( $args['post_type'] !== '' ? "WHERE wpp.post_type IN (" . $args['post_type'] . ")" : '' ) . "
222
+ " . ( $views_query !== '' ? 'GROUP BY pvc.period' : '' ) . "
223
  HAVING post_views > 0";
224
 
225
  // get cached data
237
  }
238
 
239
  $post_views = $range;
240
+ } else
241
  $post_views = (int) $wpdb->get_var( $query );
 
242
 
243
  // set the cache expiration, 5 minutes by default
244
  $expire = absint( apply_filters( 'pvc_object_cache_expire', 5 * 60 ) );
245
+
246
  wp_cache_add( md5( $query ), $post_views, 'pvc-get_views', $expire );
247
  }
248
 
343
  'show_post_views' => true,
344
  'show_post_thumbnail' => false,
345
  'show_post_excerpt' => false,
346
+ 'no_posts_message' => __( 'No Posts', 'post-views-counter' ),
347
+ 'item_before' => '',
348
+ 'item_after' => ''
349
  );
350
 
351
  $args = apply_filters( 'pvc_most_viewed_posts_args', wp_parse_args( $args, $defaults ) );
371
 
372
  $html .= '
373
  <li>';
374
+
375
+ $html .= apply_filters( 'pvc_most_viewed_posts_item_before', $args['item_before'], $post );
376
 
377
  if ( $args['show_post_thumbnail'] && has_post_thumbnail( $post->ID ) ) {
378
  $html .= '
379
+ <span class="post-thumbnail">
380
+ ' . get_the_post_thumbnail( $post->ID, $args['thumbnail_size'] ) . '
381
+ </span>';
382
  }
383
 
384
  $html .= '
385
+ <a class="post-title" href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a>' . ($args['show_post_views'] ? ' <span class="count">(' . number_format_i18n( pvc_get_post_views( $post->ID ) ) . ')</span>' : '');
386
 
387
  $excerpt = '';
388
 
398
 
399
  if ( ! empty( $excerpt ) )
400
  $html .= '
401
+
402
+ <div class="post-excerpt">' . esc_html( $excerpt ) . '</div>';
403
+
404
+ $html .= apply_filters( 'pvc_most_viewed_posts_item_after', $args['item_after'], $post );
405
 
406
  $html .= '
407
  </li>';
includes/settings.php CHANGED
@@ -159,7 +159,7 @@ class Post_Views_Counter_Settings {
159
  * @return mixed
160
  */
161
  public function options_page() {
162
- $tab_key = (isset( $_GET['tab'] ) ? $_GET['tab'] : 'general');
163
 
164
  echo '
165
  <div class="wrap">' . screen_icon() . '
@@ -586,7 +586,7 @@ class Post_Views_Counter_Settings {
586
  $input = Post_Views_Counter()->defaults['general'];
587
  $input['wp_postviews_import'] = true;
588
 
589
- $sql = '';
590
 
591
  foreach ( $views as $view ) {
592
  $sql[] = "(" . $view['post_id'] . ", 4, 'total', " . $view['meta_value'] . ")";
@@ -623,7 +623,7 @@ class Post_Views_Counter_Settings {
623
  $input['counter_mode'] = isset( $input['counter_mode'], $this->modes[$input['counter_mode']] ) ? $input['counter_mode'] : Post_Views_Counter()->defaults['general']['counter_mode'];
624
 
625
  // post views column
626
- $input['post_views_column'] = $input['post_views_column'];
627
 
628
  // time between counts
629
  $input['time_between_counts']['number'] = (int) ( isset( $input['time_between_counts']['number'] ) ? $input['time_between_counts']['number'] : Post_Views_Counter()->defaults['general']['time_between_counts']['number'] );
159
  * @return mixed
160
  */
161
  public function options_page() {
162
+ $tab_key = (isset( $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : 'general');
163
 
164
  echo '
165
  <div class="wrap">' . screen_icon() . '
586
  $input = Post_Views_Counter()->defaults['general'];
587
  $input['wp_postviews_import'] = true;
588
 
589
+ $sql = array();
590
 
591
  foreach ( $views as $view ) {
592
  $sql[] = "(" . $view['post_id'] . ", 4, 'total', " . $view['meta_value'] . ")";
623
  $input['counter_mode'] = isset( $input['counter_mode'], $this->modes[$input['counter_mode']] ) ? $input['counter_mode'] : Post_Views_Counter()->defaults['general']['counter_mode'];
624
 
625
  // post views column
626
+ $input['post_views_column'] = isset( $input['post_views_column'] );
627
 
628
  // time between counts
629
  $input['time_between_counts']['number'] = (int) ( isset( $input['time_between_counts']['number'] ) ? $input['time_between_counts']['number'] : Post_Views_Counter()->defaults['general']['time_between_counts']['number'] );
js/admin-dashboard.js CHANGED
@@ -1,99 +1,99 @@
1
  ( function ( $ ) {
2
 
3
- // set global options
4
- // Chart.defaults.global.tooltips.titleMarginBottom = 0;
5
- // Chart.defaults.global.tooltips.footerMarginTop = 4;
6
 
7
- window.onload = function () {
8
- updateChart( 'this_month' );
9
- };
10
 
11
- function updateChart( period ) {
12
 
13
- var container = document.getElementById( 'pvc_dashboard_container' );
14
 
15
- if ( $( container ).length > 0 ) {
16
 
17
- $( container ).addClass( 'loading' ).append( '<span class="spinner is-active"></span>' );
18
 
19
- $.ajax( {
20
- url: pvcArgs.ajaxURL,
21
- type: 'POST',
22
- dataType: 'json',
23
- data: ( {
24
- action: 'pvc_dashboard_chart',
25
- nonce: pvcArgs.nonce,
26
- period: period
27
- } ),
28
- success: function ( args ) {
29
- $( container ).removeClass( 'loading' );
30
- $( container ).find( '.spinner' ).removeClass( 'is-active' );
31
 
32
- var config = {
33
- type: 'line',
34
- data: args.data,
35
- options: {
36
- responsive: true,
37
- legend: {
38
- display: false,
39
- position: 'bottom',
40
- },
41
- scales: {
42
- xAxes: [ {
43
- display: true,
44
- scaleLabel: {
45
- display: true,
46
- labelString: args.text.xAxes,
47
- fontSize: 14,
48
- fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'
49
- }
50
- } ],
51
- yAxes: [ {
52
- display: true,
53
- scaleLabel: {
54
- display: false,
55
- labelString: args.text.yAxes
56
- }
57
- } ]
58
- },
59
- hover: {
60
- mode: 'label'
61
- },
62
- tooltips: {
63
- custom: function ( tooltip ) {
64
- // console.log( tooltip );
65
- },
66
- callbacks: {
67
- title: function ( tooltip ) {
68
- return args.data.dates[tooltip[0].index];
69
- }
70
- }
71
- }
72
- }
73
- };
74
 
75
- $.each( config.data.datasets, function ( i, dataset ) {
76
- dataset.fill = args.design.fill;
77
- dataset.borderColor = args.design.borderColor;
78
- dataset.backgroundColor = args.design.backgroundColor;
79
- dataset.borderWidth = args.design.borderWidth;
80
- dataset.borderDash = args.design.borderDash;
81
- dataset.pointBorderColor = args.design.pointBorderColor;
82
- dataset.pointBackgroundColor = args.design.pointBackgroundColor;
83
- dataset.pointBorderWidth = args.design.pointBorderWidth;
84
- } );
85
 
86
- window.chartPVC = new Chart( document.getElementById( 'pvc_chart' ).getContext( '2d' ), config );
87
- }
88
- } );
89
 
 
90
  }
91
- }
92
 
93
- function updateLegend() {
94
- $legendContainer = $( '#legendContainer' );
95
- $legendContainer.empty();
96
- $legendContainer.append( window.chartPVC.generateLegend() );
97
- }
98
 
99
  } )( jQuery );
1
  ( function ( $ ) {
2
 
3
+ // set global options
4
+ // Chart.defaults.global.tooltips.titleMarginBottom = 0;
5
+ // Chart.defaults.global.tooltips.footerMarginTop = 4;
6
 
7
+ window.onload = function () {
8
+ updateChart( 'this_month' );
9
+ };
10
 
11
+ function updateChart( period ) {
12
 
13
+ var container = document.getElementById( 'pvc_dashboard_container' );
14
 
15
+ if ( $( container ).length > 0 ) {
16
 
17
+ $( container ).addClass( 'loading' ).append( '<span class="spinner is-active"></span>' );
18
 
19
+ $.ajax( {
20
+ url: pvcArgs.ajaxURL,
21
+ type: 'POST',
22
+ dataType: 'json',
23
+ data: ( {
24
+ action: 'pvc_dashboard_chart',
25
+ nonce: pvcArgs.nonce,
26
+ period: period
27
+ } ),
28
+ success: function ( args ) {
29
+ $( container ).removeClass( 'loading' );
30
+ $( container ).find( '.spinner' ).removeClass( 'is-active' );
31
 
32
+ var config = {
33
+ type: 'line',
34
+ data: args.data,
35
+ options: {
36
+ responsive: true,
37
+ legend: {
38
+ display: false,
39
+ position: 'bottom',
40
+ },
41
+ scales: {
42
+ xAxes: [ {
43
+ display: true,
44
+ scaleLabel: {
45
+ display: true,
46
+ labelString: args.text.xAxes,
47
+ fontSize: 14,
48
+ fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'
49
+ }
50
+ } ],
51
+ yAxes: [ {
52
+ display: true,
53
+ scaleLabel: {
54
+ display: false,
55
+ labelString: args.text.yAxes
56
+ }
57
+ } ]
58
+ },
59
+ hover: {
60
+ mode: 'label'
61
+ },
62
+ tooltips: {
63
+ custom: function ( tooltip ) {
64
+ // console.log( tooltip );
65
+ },
66
+ callbacks: {
67
+ title: function ( tooltip ) {
68
+ return args.data.dates[tooltip[0].index];
69
+ }
70
+ }
71
+ }
72
+ }
73
+ };
74
 
75
+ $.each( config.data.datasets, function ( i, dataset ) {
76
+ dataset.fill = args.design.fill;
77
+ dataset.borderColor = args.design.borderColor;
78
+ dataset.backgroundColor = args.design.backgroundColor;
79
+ dataset.borderWidth = args.design.borderWidth;
80
+ dataset.borderDash = args.design.borderDash;
81
+ dataset.pointBorderColor = args.design.pointBorderColor;
82
+ dataset.pointBackgroundColor = args.design.pointBackgroundColor;
83
+ dataset.pointBorderWidth = args.design.pointBorderWidth;
84
+ } );
85
 
86
+ window.chartPVC = new Chart( document.getElementById( 'pvc_chart' ).getContext( '2d' ), config );
87
+ }
88
+ } );
89
 
90
+ }
91
  }
 
92
 
93
+ function updateLegend() {
94
+ $legendContainer = $( '#legendContainer' );
95
+ $legendContainer.empty();
96
+ $legendContainer.append( window.chartPVC.generateLegend() );
97
+ }
98
 
99
  } )( jQuery );
js/admin-post.js CHANGED
@@ -1,49 +1,49 @@
1
  ( function ( $ ) {
2
 
3
- $( document ).ready( function () {
4
-
5
- // post views input
6
- $( '#post-views .edit-post-views' ).click( function () {
7
- if ( $( '#post-views-input-container' ).is( ":hidden" ) ) {
8
- $( '#post-views-input-container' ).slideDown( 'fast' );
9
- $( this ).hide();
10
- }
11
- return false;
12
- } );
13
 
14
- // save post views
15
- $( '#post-views .save-post-views' ).click( function () {
 
 
 
 
 
 
16
 
17
- var views = $.trim( $( '#post-views-display b' ).text() );
 
18
 
19
- $( '#post-views-input-container' ).slideUp( 'fast' );
20
- $( '#post-views .edit-post-views' ).show();
21
 
22
- views = parseInt( $( '#post-views-input' ).val() );
23
- // reassign value as integer
24
- $( '#post-views-input' ).val( views );
25
 
26
- $( '#post-views-display b' ).text( views );
 
 
27
 
28
- return false;
29
- } );
30
 
31
- // cancel post views
32
- $( '#post-views .cancel-post-views' ).click( function () {
33
 
34
- var views = $.trim( $( '#post-views-display b' ).text() );
 
35
 
36
- $( '#post-views-input-container' ).slideUp( 'fast' );
37
- $( '#post-views .edit-post-views' ).show();
38
 
39
- views = parseInt( $( '#post-views-current' ).val() );
 
40
 
41
- $( '#post-views-display b' ).text( views );
42
- $( '#post-views-input' ).val( views );
43
 
44
- return false;
45
- } );
46
 
47
- } );
 
 
 
48
 
49
  } )( jQuery );
1
  ( function ( $ ) {
2
 
3
+ $( document ).ready( function () {
 
 
 
 
 
 
 
 
 
4
 
5
+ // post views input
6
+ $( '#post-views .edit-post-views' ).click( function () {
7
+ if ( $( '#post-views-input-container' ).is( ":hidden" ) ) {
8
+ $( '#post-views-input-container' ).slideDown( 'fast' );
9
+ $( this ).hide();
10
+ }
11
+ return false;
12
+ } );
13
 
14
+ // save post views
15
+ $( '#post-views .save-post-views' ).click( function () {
16
 
17
+ var views = $.trim( $( '#post-views-display b' ).text() );
 
18
 
19
+ $( '#post-views-input-container' ).slideUp( 'fast' );
20
+ $( '#post-views .edit-post-views' ).show();
 
21
 
22
+ views = parseInt( $( '#post-views-input' ).val() );
23
+ // reassign value as integer
24
+ $( '#post-views-input' ).val( views );
25
 
26
+ $( '#post-views-display b' ).text( views );
 
27
 
28
+ return false;
29
+ } );
30
 
31
+ // cancel post views
32
+ $( '#post-views .cancel-post-views' ).click( function () {
33
 
34
+ var views = $.trim( $( '#post-views-display b' ).text() );
 
35
 
36
+ $( '#post-views-input-container' ).slideUp( 'fast' );
37
+ $( '#post-views .edit-post-views' ).show();
38
 
39
+ views = parseInt( $( '#post-views-current' ).val() );
 
40
 
41
+ $( '#post-views-display b' ).text( views );
42
+ $( '#post-views-input' ).val( views );
43
 
44
+ return false;
45
+ } );
46
+
47
+ } );
48
 
49
  } )( jQuery );
js/admin-quick-edit.js CHANGED
@@ -1,57 +1,57 @@
1
  ( function ( $ ) {
2
- // we create a copy of the WP inline edit post function
3
- var $wp_inline_edit = inlineEditPost.edit;
4
- // and then we overwrite the function with our own code
5
- inlineEditPost.edit = function ( id ) {
6
- // call the original WP edit function
7
- // we don't want to leave WordPress hanging
8
- $wp_inline_edit.apply( this, arguments );
9
-
10
- // get the post ID
11
- var $post_id = 0;
12
-
13
- if ( typeof ( id ) == 'object' )
14
- $post_id = parseInt( this.getId( id ) );
15
-
16
- if ( $post_id > 0 ) {
17
- // define the edit row
18
- var $edit_row = $( '#edit-' + $post_id );
19
- var $post_row = $( '#post-' + $post_id );
20
-
21
- // get the data
22
- var $post_views = $( '.column-post_views', $post_row ).text();
23
-
24
- // populate the data
25
- $( ':input[name="post_views"]', $edit_row ).val( $post_views );
26
- }
27
-
28
- return false;
29
- };
30
-
31
- $( document ).on( 'click', '#bulk_edit', function () {
32
- // define the bulk edit row
33
- var $bulk_row = $( '#bulk-edit' );
34
-
35
- // get the selected post ids that are being edited
36
- var $post_ids = new Array();
37
- $bulk_row.find( '#bulk-titles' ).children().each( function () {
38
- $post_ids.push( $( this ).attr( 'id' ).replace( /^(ttle)/i, '' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  } );
40
-
41
- // get the data
42
- var $post_views = $bulk_row.find( 'input[name="post_views"]' ).val();
43
-
44
- // save the data
45
- $.ajax( {
46
- url: ajaxurl, // this is a variable that WordPress has already defined for us
47
- type: 'post',
48
- async: false,
49
- cache: false,
50
- data: {
51
- action: 'save_bulk_post_views', // this is the name of our WP AJAX function that we'll set up next
52
- post_ids: $post_ids, // and these are the 2 parameters we're passing to our function
53
- post_views: $post_views,
54
- }
55
- } );
56
- } );
57
  } )( jQuery );
1
  ( function ( $ ) {
2
+ // we create a copy of the WP inline edit post function
3
+ var $wp_inline_edit = inlineEditPost.edit;
4
+ // and then we overwrite the function with our own code
5
+ inlineEditPost.edit = function ( id ) {
6
+ // call the original WP edit function
7
+ // we don't want to leave WordPress hanging
8
+ $wp_inline_edit.apply( this, arguments );
9
+
10
+ // get the post ID
11
+ var $post_id = 0;
12
+
13
+ if ( typeof ( id ) == 'object' )
14
+ $post_id = parseInt( this.getId( id ) );
15
+
16
+ if ( $post_id > 0 ) {
17
+ // define the edit row
18
+ var $edit_row = $( '#edit-' + $post_id );
19
+ var $post_row = $( '#post-' + $post_id );
20
+
21
+ // get the data
22
+ var $post_views = $( '.column-post_views', $post_row ).text();
23
+
24
+ // populate the data
25
+ $( ':input[name="post_views"]', $edit_row ).val( $post_views );
26
+ }
27
+
28
+ return false;
29
+ };
30
+
31
+ $( document ).on( 'click', '#bulk_edit', function () {
32
+ // define the bulk edit row
33
+ var $bulk_row = $( '#bulk-edit' );
34
+
35
+ // get the selected post ids that are being edited
36
+ var $post_ids = new Array();
37
+ $bulk_row.find( '#bulk-titles' ).children().each( function () {
38
+ $post_ids.push( $( this ).attr( 'id' ).replace( /^(ttle)/i, '' ) );
39
+ } );
40
+
41
+ // get the data
42
+ var $post_views = $bulk_row.find( 'input[name="post_views"]' ).val();
43
+
44
+ // save the data
45
+ $.ajax( {
46
+ url: ajaxurl, // this is a variable that WordPress has already defined for us
47
+ type: 'post',
48
+ async: false,
49
+ cache: false,
50
+ data: {
51
+ action: 'save_bulk_post_views', // this is the name of our WP AJAX function that we'll set up next
52
+ post_ids: $post_ids, // and these are the 2 parameters we're passing to our function
53
+ post_views: $post_views,
54
+ }
55
+ } );
56
  } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  } )( jQuery );
js/admin-settings.js CHANGED
@@ -1,71 +1,71 @@
1
  ( function ( $ ) {
2
 
3
- $( document ).ready( function () {
4
 
5
- $( '.post-views-counter-settings' ).checkBo();
6
 
7
- var ip_boxes = $( '#pvc_exclude_ips' ).find( '.ip-box' ).length;
8
 
9
- $( '#pvc_exclude_ips .ip-box:first' ).find( '.remove-exclude-ip' ).hide();
10
 
11
- // ask whether to reset options to defaults
12
- $( document ).on( 'click', '.reset_pvc_settings', function () {
13
- return confirm( pvcArgsSettings.resetToDefaults );
14
- } );
15
 
16
- // ask whether to reset views
17
- $( document ).on( 'click', 'input[name="post_views_counter_reset_views"]', function () {
18
- return confirm( pvcArgsSettings.resetViews );
19
- } );
20
 
21
- // remove ip box
22
- $( document ).on( 'click', '.remove-exclude-ip', function ( e ) {
23
- e.preventDefault();
24
 
25
- ip_boxes--;
26
 
27
- var parent = $( this ).parent();
28
 
29
- // remove ip box
30
- parent.slideUp( 'fast', function () {
31
- $( this ).remove();
32
- } );
33
- } );
34
 
35
- // add ip box
36
- $( document ).on( 'click', '.add-exclude-ip', function () {
37
- ip_boxes++;
38
 
39
- var parent = $( this ).parents( '#pvc_exclude_ips' ),
40
- new_ip_box = parent.find( '.ip-box:last' ).clone().hide();
41
 
42
- // clear value
43
- new_ip_box.find( 'input' ).val( '' );
44
 
45
- if ( ip_boxes > 1 ) {
46
- new_ip_box.find( '.remove-exclude-ip' ).show();
47
- }
48
 
49
- // add and display new ip box
50
- parent.find( '.ip-box:last' ).after( new_ip_box ).next().slideDown( 'fast' );
51
- } );
52
 
53
- // add current ip
54
- $( document ).on( 'click', '.add-current-ip', function () {
55
- // fill input with user's current ip
56
- $( this ).parents( '#pvc_exclude_ips' ).find( '.ip-box' ).last().find( 'input' ).val( $( this ).attr( 'data-rel' ) );
57
- } );
58
 
59
- // toggle user roles
60
- $( '#pvc_exclude-roles, #pvc_restrict_display-roles' ).change( function () {
61
- if ( $( this ).is( ':checked' ) ) {
62
- $( '.pvc_user_roles' ).slideDown( 'fast' );
63
- } else {
64
- $( '.pvc_user_roles' ).slideUp( 'fast' );
65
- }
66
- } );
67
 
68
- } );
69
 
70
  } )( jQuery );
71
 
@@ -77,57 +77,57 @@
77
  * Author URL: elmahdim.com
78
  */
79
  !function ( e ) {
80
- e.fn.checkBo = function ( c ) {
81
- return c = e.extend( { }, { checkAllButton: null, checkAllTarget: null, checkAllTextDefault: null, checkAllTextToggle: null }, c ), this.each( function () {
82
- function t( e ) {
83
- this.input = e
84
- }
85
- function n() {
86
- var c = e( this ).is( ":checked" );
87
- e( this ).closest( "label" ).toggleClass( "checked", c )
88
- }
89
- function i( e, c, t ) {
90
- e.text( e.parent( a ).hasClass( "checked" ) ? t : c )
91
- }
92
- function h( c ) {
93
- var t = c.attr( "data-show" );
94
- c = c.attr( "data-hide" ), e( t ).removeClass( "is-hidden" ), e( c ).addClass( "is-hidden" )
95
- }
96
- var l = e( this ), a = l.find( ".cb-checkbox" ), d = l.find( ".cb-radio" ), o = l.find( ".cb-switcher" ), s = a.find( "input:checkbox" ), f = d.find( "input:radio" );
97
- s.wrap( '<span class="cb-inner"><i></i></span>' ), f.wrap( '<span class="cb-inner"><i></i></span>' );
98
- var k = new t( "input:checkbox" ), r = new t( "input:radio" );
99
- if ( t.prototype.checkbox = function ( e ) {
100
- var c = e.find( this.input ).is( ":checked" );
101
- e.find( this.input ).prop( "checked", !c ).trigger( "change" )
102
- }, t.prototype.radiobtn = function ( c, t ) {
103
- var n = e( 'input:radio[name="' + t + '"]' );
104
- n.prop( "checked", !1 ), n.closest( n.closest( d ) ).removeClass( "checked" ), c.addClass( "checked" ), c.find( this.input ).get( 0 ).checked = c.hasClass( "checked" ), c.find( this.input ).change()
105
- }, s.on( "change", n ), f.on( "change", n ), a.find( "a" ).on( "click", function ( e ) {
106
- e.stopPropagation()
107
- } ), a.on( "click", function ( c ) {
108
- c.preventDefault(), k.checkbox( e( this ) ), c = e( this ).attr( "data-toggle" ), e( c ).toggleClass( "is-hidden" ), h( e( this ) )
109
- } ), d.on( "click", function ( c ) {
110
- c.preventDefault(), r.radiobtn( e( this ), e( this ).find( "input:radio" ).attr( "name" ) ), h( e( this ) )
111
- } ), e.fn.toggleCheckbox = function () {
112
- this.prop( "checked", !this.is( ":checked" ) )
113
- }, e.fn.switcherChecker = function () {
114
- var c = e( this ), t = c.find( "input" ), n = c.find( ".cb-state" );
115
- t.is( ":checked" ) ? ( c.addClass( "checked" ), n.html( t.attr( "data-state-on" ) ) ) : ( c.removeClass( "checked" ), n.html( t.attr( "data-state-off" ) ) )
116
- }, o.on( "click", function ( c ) {
117
- c.preventDefault(), c = e( this ), c.find( "input" ).toggleCheckbox(), c.switcherChecker(), e( c.attr( "data-toggle" ) ).toggleClass( "is-hidden" )
118
- } ), o.each( function () {
119
- e( this ).switcherChecker()
120
- } ), c.checkAllButton && c.checkAllTarget ) {
121
- var u = e( this );
122
- u.find( e( c.checkAllButton ) ).on( "click", function () {
123
- u.find( c.checkAllTarget ).find( "input:checkbox" ).each( function () {
124
- u.find( e( c.checkAllButton ) ).hasClass( "checked" ) ? u.find( c.checkAllTarget ).find( "input:checkbox" ).prop( "checked", !0 ).change() : u.find( c.checkAllTarget ).find( "input:checkbox" ).prop( "checked", !1 ).change()
125
- } ), i( u.find( e( c.checkAllButton ) ).find( ".toggle-text" ), c.checkAllTextDefault, c.checkAllTextToggle )
126
- } ), u.find( c.checkAllTarget ).find( a ).on( "click", function () {
127
- u.find( c.checkAllButton ).find( "input:checkbox" ).prop( "checked", !1 ).change().removeClass( "checked" ), i( u.find( e( c.checkAllButton ) ).find( ".toggle-text" ), c.checkAllTextDefault, c.checkAllTextToggle )
 
 
 
128
  } )
129
- }
130
- l.find( '[checked="checked"]' ).closest( "label" ).addClass( "checked" ), l.find( "input" ).is( "input:disabled" ) && l.find( "input:disabled" ).closest( "label" ).off().addClass( "disabled" )
131
- } )
132
- }
133
  }( jQuery, window, document );
1
  ( function ( $ ) {
2
 
3
+ $( document ).ready( function () {
4
 
5
+ $( '.post-views-counter-settings' ).checkBo();
6
 
7
+ var ip_boxes = $( '#pvc_exclude_ips' ).find( '.ip-box' ).length;
8
 
9
+ $( '#pvc_exclude_ips .ip-box:first' ).find( '.remove-exclude-ip' ).hide();
10
 
11
+ // ask whether to reset options to defaults
12
+ $( document ).on( 'click', '.reset_pvc_settings', function () {
13
+ return confirm( pvcArgsSettings.resetToDefaults );
14
+ } );
15
 
16
+ // ask whether to reset views
17
+ $( document ).on( 'click', 'input[name="post_views_counter_reset_views"]', function () {
18
+ return confirm( pvcArgsSettings.resetViews );
19
+ } );
20
 
21
+ // remove ip box
22
+ $( document ).on( 'click', '.remove-exclude-ip', function ( e ) {
23
+ e.preventDefault();
24
 
25
+ ip_boxes--;
26
 
27
+ var parent = $( this ).parent();
28
 
29
+ // remove ip box
30
+ parent.slideUp( 'fast', function () {
31
+ $( this ).remove();
32
+ } );
33
+ } );
34
 
35
+ // add ip box
36
+ $( document ).on( 'click', '.add-exclude-ip', function () {
37
+ ip_boxes++;
38
 
39
+ var parent = $( this ).parents( '#pvc_exclude_ips' ),
40
+ new_ip_box = parent.find( '.ip-box:last' ).clone().hide();
41
 
42
+ // clear value
43
+ new_ip_box.find( 'input' ).val( '' );
44
 
45
+ if ( ip_boxes > 1 ) {
46
+ new_ip_box.find( '.remove-exclude-ip' ).show();
47
+ }
48
 
49
+ // add and display new ip box
50
+ parent.find( '.ip-box:last' ).after( new_ip_box ).next().slideDown( 'fast' );
51
+ } );
52
 
53
+ // add current ip
54
+ $( document ).on( 'click', '.add-current-ip', function () {
55
+ // fill input with user's current ip
56
+ $( this ).parents( '#pvc_exclude_ips' ).find( '.ip-box' ).last().find( 'input' ).val( $( this ).attr( 'data-rel' ) );
57
+ } );
58
 
59
+ // toggle user roles
60
+ $( '#pvc_exclude-roles, #pvc_restrict_display-roles' ).change( function () {
61
+ if ( $( this ).is( ':checked' ) ) {
62
+ $( '.pvc_user_roles' ).slideDown( 'fast' );
63
+ } else {
64
+ $( '.pvc_user_roles' ).slideUp( 'fast' );
65
+ }
66
+ } );
67
 
68
+ } );
69
 
70
  } )( jQuery );
71
 
77
  * Author URL: elmahdim.com
78
  */
79
  !function ( e ) {
80
+ e.fn.checkBo = function ( c ) {
81
+ return c = e.extend( { }, { checkAllButton: null, checkAllTarget: null, checkAllTextDefault: null, checkAllTextToggle: null }, c ), this.each( function () {
82
+ function t( e ) {
83
+ this.input = e
84
+ }
85
+ function n() {
86
+ var c = e( this ).is( ":checked" );
87
+ e( this ).closest( "label" ).toggleClass( "checked", c )
88
+ }
89
+ function i( e, c, t ) {
90
+ e.text( e.parent( a ).hasClass( "checked" ) ? t : c )
91
+ }
92
+ function h( c ) {
93
+ var t = c.attr( "data-show" );
94
+ c = c.attr( "data-hide" ), e( t ).removeClass( "is-hidden" ), e( c ).addClass( "is-hidden" )
95
+ }
96
+ var l = e( this ), a = l.find( ".cb-checkbox" ), d = l.find( ".cb-radio" ), o = l.find( ".cb-switcher" ), s = a.find( "input:checkbox" ), f = d.find( "input:radio" );
97
+ s.wrap( '<span class="cb-inner"><i></i></span>' ), f.wrap( '<span class="cb-inner"><i></i></span>' );
98
+ var k = new t( "input:checkbox" ), r = new t( "input:radio" );
99
+ if ( t.prototype.checkbox = function ( e ) {
100
+ var c = e.find( this.input ).is( ":checked" );
101
+ e.find( this.input ).prop( "checked", !c ).trigger( "change" )
102
+ }, t.prototype.radiobtn = function ( c, t ) {
103
+ var n = e( 'input:radio[name="' + t + '"]' );
104
+ n.prop( "checked", !1 ), n.closest( n.closest( d ) ).removeClass( "checked" ), c.addClass( "checked" ), c.find( this.input ).get( 0 ).checked = c.hasClass( "checked" ), c.find( this.input ).change()
105
+ }, s.on( "change", n ), f.on( "change", n ), a.find( "a" ).on( "click", function ( e ) {
106
+ e.stopPropagation()
107
+ } ), a.on( "click", function ( c ) {
108
+ c.preventDefault(), k.checkbox( e( this ) ), c = e( this ).attr( "data-toggle" ), e( c ).toggleClass( "is-hidden" ), h( e( this ) )
109
+ } ), d.on( "click", function ( c ) {
110
+ c.preventDefault(), r.radiobtn( e( this ), e( this ).find( "input:radio" ).attr( "name" ) ), h( e( this ) )
111
+ } ), e.fn.toggleCheckbox = function () {
112
+ this.prop( "checked", !this.is( ":checked" ) )
113
+ }, e.fn.switcherChecker = function () {
114
+ var c = e( this ), t = c.find( "input" ), n = c.find( ".cb-state" );
115
+ t.is( ":checked" ) ? ( c.addClass( "checked" ), n.html( t.attr( "data-state-on" ) ) ) : ( c.removeClass( "checked" ), n.html( t.attr( "data-state-off" ) ) )
116
+ }, o.on( "click", function ( c ) {
117
+ c.preventDefault(), c = e( this ), c.find( "input" ).toggleCheckbox(), c.switcherChecker(), e( c.attr( "data-toggle" ) ).toggleClass( "is-hidden" )
118
+ } ), o.each( function () {
119
+ e( this ).switcherChecker()
120
+ } ), c.checkAllButton && c.checkAllTarget ) {
121
+ var u = e( this );
122
+ u.find( e( c.checkAllButton ) ).on( "click", function () {
123
+ u.find( c.checkAllTarget ).find( "input:checkbox" ).each( function () {
124
+ u.find( e( c.checkAllButton ) ).hasClass( "checked" ) ? u.find( c.checkAllTarget ).find( "input:checkbox" ).prop( "checked", !0 ).change() : u.find( c.checkAllTarget ).find( "input:checkbox" ).prop( "checked", !1 ).change()
125
+ } ), i( u.find( e( c.checkAllButton ) ).find( ".toggle-text" ), c.checkAllTextDefault, c.checkAllTextToggle )
126
+ } ), u.find( c.checkAllTarget ).find( a ).on( "click", function () {
127
+ u.find( c.checkAllButton ).find( "input:checkbox" ).prop( "checked", !1 ).change().removeClass( "checked" ), i( u.find( e( c.checkAllButton ) ).find( ".toggle-text" ), c.checkAllTextDefault, c.checkAllTextToggle )
128
+ } )
129
+ }
130
+ l.find( '[checked="checked"]' ).closest( "label" ).addClass( "checked" ), l.find( "input" ).is( "input:disabled" ) && l.find( "input:disabled" ).closest( "label" ).off().addClass( "disabled" )
131
  } )
132
+ }
 
 
 
133
  }( jQuery, window, document );
js/frontend.js CHANGED
@@ -1,44 +1,44 @@
1
  ( function ( $ ) {
2
 
3
- $( document ).ready( function () {
4
-
5
- // rest api request
6
- if ( pvcArgsFrontend.mode == 'rest_api' ) {
7
-
8
- var request = {
9
- id: pvcArgsFrontend.postID
10
- };
11
-
12
- $.ajax( {
13
- url: pvcArgsFrontend.requestURL + '?id=' + pvcArgsFrontend.postID,
14
- type: 'post',
15
- async: true,
16
- cache: false,
17
- data: request,
18
- beforeSend: function ( xhr ) {
19
- xhr.setRequestHeader( 'X-WP-Nonce', pvcArgsFrontend.nonce );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
- } );
22
-
23
- // admin ajax request
24
- } else {
25
-
26
- var request = {
27
- action: 'pvc-check-post',
28
- pvc_nonce: pvcArgsFrontend.nonce,
29
- id: pvcArgsFrontend.postID
30
- };
31
-
32
- $.ajax( {
33
- url: pvcArgsFrontend.requestURL,
34
- type: 'post',
35
- async: true,
36
- cache: false,
37
- data: request
38
- } );
39
-
40
- }
41
-
42
- } );
43
 
44
  } )( jQuery );
1
  ( function ( $ ) {
2
 
3
+ $( document ).ready( function () {
4
+
5
+ // rest api request
6
+ if ( pvcArgsFrontend.mode == 'rest_api' ) {
7
+
8
+ var request = {
9
+ id: pvcArgsFrontend.postID
10
+ };
11
+
12
+ $.ajax( {
13
+ url: pvcArgsFrontend.requestURL + '?id=' + pvcArgsFrontend.postID,
14
+ type: 'post',
15
+ async: true,
16
+ cache: false,
17
+ data: request,
18
+ beforeSend: function ( xhr ) {
19
+ xhr.setRequestHeader( 'X-WP-Nonce', pvcArgsFrontend.nonce );
20
+ }
21
+ } );
22
+
23
+ // admin ajax request
24
+ } else {
25
+
26
+ var request = {
27
+ action: 'pvc-check-post',
28
+ pvc_nonce: pvcArgsFrontend.nonce,
29
+ id: pvcArgsFrontend.postID
30
+ };
31
+
32
+ $.ajax( {
33
+ url: pvcArgsFrontend.requestURL,
34
+ type: 'post',
35
+ async: true,
36
+ cache: false,
37
+ data: request
38
+ } );
39
+
40
  }
41
+
42
+ } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  } )( 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.2.7
6
  Author: dFactory
7
  Author URI: http://www.dfactory.eu/
8
  Plugin URI: http://www.dfactory.eu/plugins/post-views-counter/
@@ -31,7 +31,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
31
  * Post Views Counter final class.
32
  *
33
  * @class Post_Views_Counter
34
- * @version 1.2.7
35
  */
36
  final class Post_Views_Counter {
37
 
@@ -80,7 +80,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
80
  'link_to_post' => true,
81
  'icon_class' => 'dashicons-chart-bar'
82
  ),
83
- 'version' => '1.2.7'
84
  );
85
 
86
  /**
@@ -162,8 +162,8 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
162
  * @return void
163
  */
164
  public function __construct() {
165
- register_activation_hook( __FILE__, array( $this, 'activation' ) );
166
- register_deactivation_hook( __FILE__, array( $this, 'deactivation' ) );
167
 
168
  // settings
169
  $this->options = array(
@@ -181,9 +181,37 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
181
  }
182
 
183
  /**
184
- * Plugin activation function.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  */
186
- public function activation() {
187
  global $wpdb, $charset_collate;
188
 
189
  // required for dbdelta
@@ -212,11 +240,50 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
212
  }
213
 
214
  /**
215
- * Plugin deactivation function.
 
 
 
216
  */
217
- public function deactivation() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  // delete default options
219
- if ( $this->options['general']['deactivation_delete'] ) {
220
  delete_option( 'post_views_counter_settings_general' );
221
  delete_option( 'post_views_counter_settings_display' );
222
 
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.2.8
6
  Author: dFactory
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.2.8
35
  */
36
  final class Post_Views_Counter {
37
 
80
  'link_to_post' => true,
81
  'icon_class' => 'dashicons-chart-bar'
82
  ),
83
+ 'version' => '1.2.8'
84
  );
85
 
86
  /**
162
  * @return void
163
  */
164
  public function __construct() {
165
+ register_activation_hook( __FILE__, array( $this, 'multisite_activation' ) );
166
+ register_deactivation_hook( __FILE__, array( $this, 'multisite_deactivation' ) );
167
 
168
  // settings
169
  $this->options = array(
181
  }
182
 
183
  /**
184
+ * Multisite activation.
185
+ *
186
+ * @global object $wpdb
187
+ * @param bool $networkwide
188
+ */
189
+ public function multisite_activation( $networkwide ) {
190
+ if ( is_multisite() && $networkwide ) {
191
+ global $wpdb;
192
+
193
+ $activated_blogs = array();
194
+ $current_blog_id = $wpdb->blogid;
195
+ $blogs_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT blog_id FROM ' . $wpdb->blogs, '' ) );
196
+
197
+ foreach ( $blogs_ids as $blog_id ) {
198
+ switch_to_blog( $blog_id );
199
+ $this->activate_single();
200
+ $activated_blogs[] = (int) $blog_id;
201
+ }
202
+
203
+ switch_to_blog( $current_blog_id );
204
+ update_site_option( 'post_views_counter_activated_blogs', $activated_blogs, array() );
205
+ } else
206
+ $this->activate_single();
207
+ }
208
+
209
+ /**
210
+ * Single site activation.
211
+ *
212
+ * @global array $wp_roles
213
  */
214
+ public function activate_single() {
215
  global $wpdb, $charset_collate;
216
 
217
  // required for dbdelta
240
  }
241
 
242
  /**
243
+ * Multisite deactivation.
244
+ *
245
+ * @global array $wpdb
246
+ * @param bool $networkwide
247
  */
248
+ public function multisite_deactivation( $networkwide ) {
249
+ if ( is_multisite() && $networkwide ) {
250
+ global $wpdb;
251
+
252
+ $current_blog_id = $wpdb->blogid;
253
+ $blogs_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT blog_id FROM ' . $wpdb->blogs, '' ) );
254
+
255
+ if ( ! ($activated_blogs = get_site_option( 'post_views_counter_activated_blogs', false, false )) )
256
+ $activated_blogs = array();
257
+
258
+ foreach ( $blogs_ids as $blog_id ) {
259
+ switch_to_blog( $blog_id );
260
+ $this->deactivate_single( true );
261
+
262
+ if ( in_array( (int) $blog_id, $activated_blogs, true ) )
263
+ unset( $activated_blogs[array_search( $blog_id, $activated_blogs )] );
264
+ }
265
+
266
+ switch_to_blog( $current_blog_id );
267
+ update_site_option( 'post_views_counter_activated_blogs', $activated_blogs );
268
+ } else
269
+ $this->deactivate_single();
270
+ }
271
+
272
+ /**
273
+ * Single site deactivation.
274
+ *
275
+ * @global array $wp_roles
276
+ * @param bool $multi
277
+ */
278
+ public function deactivate_single( $multi = false ) {
279
+ if ( $multi ) {
280
+ $options = get_option( 'post_views_counter_settings_general' );
281
+ $check = $options['deactivation_delete'];
282
+ } else
283
+ $check = $this->options['general']['deactivation_delete'];
284
+
285
  // delete default options
286
+ if ( $check ) {
287
  delete_option( 'post_views_counter_settings_general' );
288
  delete_option( 'post_views_counter_settings_display' );
289
 
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: dfactory
3
  Donate link: http://www.dfactory.eu/
4
  Tags: counter, hits, posts, postviews, post views, views, count, statistics, stats, analytics, pageviews, tracking
5
  Requires at least: 4.0
6
- Tested up to: 4.7.2
7
- Stable tag: 1.2.7
8
  License: MIT License
9
  License URI: http://opensource.org/licenses/MIT
10
 
@@ -32,6 +32,7 @@ For more information, check out plugin page at [dFactory](http://www.dfactory.eu
32
  * One-click data import from WP-PostViews
33
  * Sortable admin column
34
  * Post views display position, automatic or manual via shortcode
 
35
  * W3 Cache/WP SuperCache compatible
36
  * Optional object cache support
37
  * WPML and Polylang compatible
@@ -59,6 +60,11 @@ No questions yet.
59
 
60
  == Changelog ==
61
 
 
 
 
 
 
62
  = 1.2.7 =
63
  * Fix: Chart data not updating for object cached installs due to missing expire parameter
64
  * Fix: Bug preventing hiding the counter based on user role.
@@ -156,7 +162,7 @@ Initial release
156
 
157
  == Upgrade Notice ==
158
 
159
- = 1.2.7 =
160
- * Fix: Chart data not updating for object cached installs due to missing expire parameter
161
- * Fix: Bug preventing hiding the counter based on user role.
162
- * Fix: Undefined notice in admin dashboard request
3
  Donate link: http://www.dfactory.eu/
4
  Tags: counter, hits, posts, postviews, post views, views, count, statistics, stats, analytics, pageviews, tracking
5
  Requires at least: 4.0
6
+ Tested up to: 4.8.1
7
+ Stable tag: 1.2.8
8
  License: MIT License
9
  License URI: http://opensource.org/licenses/MIT
10
 
32
  * One-click data import from WP-PostViews
33
  * Sortable admin column
34
  * Post views display position, automatic or manual via shortcode
35
+ * Multisite compatibile
36
  * W3 Cache/WP SuperCache compatible
37
  * Optional object cache support
38
  * WPML and Polylang compatible
60
 
61
  == Changelog ==
62
 
63
+ = 1.2.8 =
64
+ * New: Multisite compatibility
65
+ * Fix: Undefined index post_views_column on post_views_counter/includes/settings.php
66
+ * Tweak: Improved user IP handling
67
+
68
  = 1.2.7 =
69
  * Fix: Chart data not updating for object cached installs due to missing expire parameter
70
  * Fix: Bug preventing hiding the counter based on user role.
162
 
163
  == Upgrade Notice ==
164
 
165
+ = 1.2.8 =
166
+ * New: Multisite compatibility
167
+ * Fix: Undefined index post_views_column on post_views_counter/includes/settings.php
168
+ * Tweak: Improved user IP handling