PluginProbe ʕ •ᴥ•ʔ
Post Views Counter / 1.7.4
Post Views Counter v1.7.4
1.7.13 1.7.12 1.7.11 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.2 1.3.2.1 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.7.10 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9
post-views-counter / includes / class-columns-modal.php
post-views-counter / includes Last commit date
class-admin.php 5 months ago class-columns-modal.php 5 months ago class-columns.php 5 months ago class-counter.php 5 months ago class-crawler-detect.php 5 months ago class-cron.php 5 months ago class-dashboard.php 5 months ago class-frontend.php 5 months ago class-functions.php 5 months ago class-import.php 5 months ago class-integration-gutenberg.php 5 months ago class-integrations.php 5 months ago class-query.php 5 months ago class-settings-api.php 5 months ago class-settings-display.php 5 months ago class-settings-general.php 5 months ago class-settings-integrations.php 5 months ago class-settings-other.php 5 months ago class-settings-reports.php 5 months ago class-settings.php 5 months ago class-toolbar.php 5 months ago class-traffic-signals.php 5 months ago class-update.php 5 months ago class-widgets.php 5 months ago functions.php 5 months ago
class-columns-modal.php
298 lines
1 <?php
2 // exit if accessed directly
3 if ( ! defined( 'ABSPATH' ) )
4 exit;
5
6 /**
7 * Post_Views_Counter_Columns_Modal class.
8 *
9 * Handles modal functionality for post view charts in admin columns,
10 * including AJAX handlers, asset enqueuing, and HTML rendering.
11 *
12 * @class Post_Views_Counter_Columns_Modal
13 */
14 class Post_Views_Counter_Columns_Modal {
15
16 /**
17 * Class constructor.
18 *
19 * @return void
20 */
21 public function __construct() {
22 // actions
23 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_chart_modal_assets' ] );
24 add_action( 'wp_ajax_pvc_column_chart', [ $this, 'ajax_column_chart' ] );
25 }
26
27 /**
28 * Enqueue chart modal assets on post list screens.
29 *
30 * @param string $page
31 * @return void
32 */
33 public function enqueue_chart_modal_assets( $page ) {
34 // only on edit.php and upload.php
35 if ( ! in_array( $page, [ 'edit.php', 'upload.php' ], true ) )
36 return;
37
38 $screen = get_current_screen();
39 $pvc = Post_Views_Counter();
40
41 // break if display is not allowed
42 if ( ! $pvc->options['display']['post_views_column'] || ! in_array( $screen->post_type, $pvc->options['general']['post_types_count'], true ) )
43 return;
44
45 // check if user can see stats
46 if ( apply_filters( 'pvc_admin_display_post_views', true ) === false )
47 return;
48
49 // enqueue Micromodal
50 wp_enqueue_script( 'pvc-micromodal', POST_VIEWS_COUNTER_URL . '/assets/micromodal/micromodal.min.js', [], '0.4.10', true );
51
52 // enqueue Chart.js (already registered)
53 wp_enqueue_script( 'pvc-chartjs' );
54
55 // enqueue modal assets
56 wp_enqueue_style( 'pvc-admin-columns', POST_VIEWS_COUNTER_URL . '/css/admin-columns.css', [], $pvc->defaults['version'] );
57 wp_enqueue_script( 'pvc-admin-columns', POST_VIEWS_COUNTER_URL . '/js/admin-columns.js', [ 'jquery', 'pvc-chartjs', 'pvc-micromodal' ], $pvc->defaults['version'], true );
58
59 // BACKWARD COMPAT: Register legacy handles for Pro 1.7.3 and earlier
60 // Pro checks for 'pvc-column-modal' handle; keep both registered
61 wp_register_style( 'pvc-column-modal', POST_VIEWS_COUNTER_URL . '/css/admin-columns.css', [], $pvc->defaults['version'] );
62 wp_register_script( 'pvc-column-modal', POST_VIEWS_COUNTER_URL . '/js/admin-columns.js', [ 'jquery', 'pvc-chartjs', 'pvc-micromodal' ], $pvc->defaults['version'], true );
63
64 // localize script
65 wp_add_inline_script( 'pvc-admin-columns', 'var pvcColumnModal = ' . wp_json_encode( [
66 'ajaxURL' => admin_url( 'admin-ajax.php' ),
67 'nonce' => wp_create_nonce( 'pvc-column-modal' ),
68 'i18n' => [
69 'loading' => __( 'Loading...', 'post-views-counter' ),
70 'close' => __( 'Close', 'post-views-counter' ),
71 'error' => __( 'An error occurred while loading data.', 'post-views-counter' ),
72 'summary' => __( 'Total views in this period:', 'post-views-counter' ),
73 'view' => __( 'view', 'post-views-counter' ),
74 'views' => __( 'views', 'post-views-counter' )
75 ]
76 ] ) . "\n", 'before' );
77
78 // add modal HTML to footer
79 add_action( 'admin_footer', [ $this, 'render_modal_html' ] );
80 }
81
82 /**
83 * AJAX handler for column chart data.
84 *
85 * @return void
86 */
87 public function ajax_column_chart() {
88 // permission & nonce check
89 if ( ! check_ajax_referer( 'pvc-column-modal', 'nonce', false ) )
90 wp_send_json_error( [ 'message' => __( 'Permission denied.', 'post-views-counter' ) ] );
91
92 // get PVC instance
93 $pvc = Post_Views_Counter();
94
95 // get post ID
96 $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
97
98 if ( ! $post_id )
99 wp_send_json_error( [ 'message' => __( 'Invalid post ID.', 'post-views-counter' ) ] );
100
101 // check post exists
102 $post = get_post( $post_id );
103
104 if ( ! $post )
105 wp_send_json_error( [ 'message' => __( 'Post not found.', 'post-views-counter' ) ] );
106
107 // break if display is not allowed
108 if ( ! $pvc->options['display']['post_views_column'] )
109 wp_send_json_error( [ 'message' => __( 'Admin column disabled.', 'post-views-counter' ) ] );
110
111 // ensure post type is tracked
112 if ( ! in_array( $post->post_type, $pvc->options['general']['post_types_count'], true ) )
113 wp_send_json_error( [ 'message' => __( 'Post type is not tracked.', 'post-views-counter' ) ] );
114
115 // check display permission for this specific post
116 if ( apply_filters( 'pvc_admin_display_post_views', true, $post_id ) === false )
117 wp_send_json_error( [ 'message' => __( 'Access denied for this post.', 'post-views-counter' ) ] );
118
119 // get period (format: YYYYMM or empty for current month)
120 $period_str = isset( $_POST['period'] ) && ! empty( $_POST['period'] ) ? preg_replace( '/[^0-9]/', '', $_POST['period'] ) : '';
121
122 // parse period or use current
123 if ( $period_str && strlen( $period_str ) === 6 ) {
124 $year = substr( $period_str, 0, 4 );
125 $month = substr( $period_str, 4, 2 );
126 $date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
127
128 if ( ! $date )
129 $date = new DateTime( 'now', wp_timezone() );
130 } else {
131 $date = new DateTime( 'now', wp_timezone() );
132 }
133
134 $year = $date->format( 'Y' );
135 $month = $date->format( 'm' );
136 $last_day = $date->format( 't' );
137
138 // fetch views data
139 $views = pvc_get_views( [
140 'post_id' => $post_id,
141 'post_type' => $post->post_type,
142 'fields' => 'date=>views',
143 'views_query' => [
144 'year' => (int) $year,
145 'month' => (int) $month
146 ]
147 ] );
148
149 // get colors
150 $colors = $pvc->functions->get_colors();
151
152 // prepare response data
153 $data = [
154 'post_id' => $post_id,
155 'post_title'=> get_the_title( $post_id ),
156 'period' => $year . $month,
157 'design' => [
158 'fill' => true,
159 'backgroundColor' => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 0.2)',
160 'borderColor' => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 1)',
161 'borderWidth' => 1.2,
162 'borderDash' => [],
163 'pointBorderColor' => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 1)',
164 'pointBackgroundColor' => 'rgba(255, 255, 255, 1)',
165 'pointBorderWidth' => 1.2
166 ],
167 'data' => [
168 'labels' => [],
169 'dates' => [],
170 'datasets' => [
171 [
172 'label' => get_the_title( $post_id ),
173 'data' => []
174 ]
175 ]
176 ]
177 ];
178
179 // generate dates and data
180 for ( $i = 1; $i <= $last_day; $i++ ) {
181 $date_key = $year . $month . str_pad( $i, 2, '0', STR_PAD_LEFT );
182
183 // labels: show only odd days
184 $data['data']['labels'][] = ( $i % 2 === 0 ? '' : $i );
185
186 // formatted dates for tooltips
187 $data['data']['dates'][] = date_i18n( get_option( 'date_format' ), strtotime( $year . '-' . $month . '-' . str_pad( $i, 2, '0', STR_PAD_LEFT ) ) );
188
189 // view count
190 $data['data']['datasets'][0]['data'][] = isset( $views[$date_key] ) ? (int) $views[$date_key] : 0;
191 }
192
193 // calculate total views for the period
194 $data['total_views'] = array_sum( $data['data']['datasets'][0]['data'] );
195
196 // check if there is any period-specific data
197 $period_has_data = false;
198 foreach ( $data['data']['datasets'][0]['data'] as $val ) {
199 if ( (int) $val > 0 ) {
200 $period_has_data = true;
201 break;
202 }
203 }
204
205 $data['period_has_data'] = $period_has_data;
206
207 // generate date navigation HTML
208 $data['dates_html'] = $this->generate_modal_dates( (int) $year, (int) $month );
209
210 wp_send_json_success( $data );
211 }
212
213 /**
214 * Generate month navigation for modal.
215 *
216 * @param int $year
217 * @param int $month
218 * @return string
219 */
220 private function generate_modal_dates( $year, $month ) {
221 // previous month
222 $prev_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
223 $prev_date->modify( '-1 month' );
224
225 // next month
226 $next_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
227 $next_date->modify( '+1 month' );
228
229 // current
230 $current_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
231
232 // check if next is in the future
233 $now = new DateTime( 'now', wp_timezone() );
234 $can_go_next = $next_date <= $now;
235
236 $html = '<div class="pvc-modal-nav">';
237 $html .= '<a href="#" class="pvc-modal-nav-prev" data-period="' . $prev_date->format( 'Ym' ) . '">‹ ' . date_i18n( 'F Y', $prev_date->getTimestamp() ) . '</a>';
238 $html .= '<span class="pvc-modal-nav-current">' . date_i18n( 'F Y', $current_date->getTimestamp() ) . '</span>';
239
240 if ( $can_go_next )
241 $html .= '<a href="#" class="pvc-modal-nav-next" data-period="' . $next_date->format( 'Ym' ) . '">' . date_i18n( 'F Y', $next_date->getTimestamp() ) . ' ›</a>';
242 else
243 $html .= '<span class="pvc-modal-nav-next pvc-disabled">' . date_i18n( 'F Y', $next_date->getTimestamp() ) . ' ›</span>';
244
245 $html .= '</div>';
246
247 return $html;
248 }
249
250 /**
251 * Render modal HTML in admin footer.
252 *
253 * @return void
254 */
255 public function render_modal_html() {
256 ?>
257 <div id="pvc-chart-modal" class="pvc-modal micromodal-slide" aria-hidden="true">
258 <div class="pvc-modal__overlay" tabindex="-1" data-micromodal-close>
259 <div class="pvc-modal__container" role="dialog" aria-modal="true" aria-labelledby="pvc-modal-title">
260 <header class="pvc-modal__header">
261 <h2 class="pvc-modal__title" id="pvc-modal-title"></h2>
262 <button class="pvc-modal__close" aria-label="<?php esc_attr_e( 'Close', 'post-views-counter' ); ?>" data-micromodal-close></button>
263 </header>
264 <div class="pvc-modal__content">
265 <div class="pvc-modal-content-top">
266 <div class="pvc-modal-summary">
267 <span class="pvc-modal-views-label"></span>
268 <span class="pvc-modal-views-data">
269 <span class="pvc-modal-count"></span>
270 </span>
271 </div>
272 <div class="pvc-modal-tabs" role="tablist">
273 <button type="button" class="pvc-modal-tab pvc-pro" disabled><span><?php _e( 'Year', 'post-views-counter' ); ?></span></button>
274 <button type="button" class="pvc-modal-tab active"><span><?php _e( 'Month', 'post-views-counter' ); ?></span></button>
275 <button type="button" class="pvc-modal-tab pvc-pro" disabled><span><?php _e( 'Week', 'post-views-counter' ); ?></span></button>
276 </div>
277 </div>
278 <div class="pvc-modal-chart-container">
279 <canvas id="pvc-modal-chart" height="200"></canvas>
280 <span class="spinner"></span>
281 </div>
282 <div class="pvc-modal-content-middle" style="display: none;">
283 <div class="pvc-modal-insights">
284 <div class="pvc-insight pvc-insight-lock pvc-modal-insights-empty">
285 <span class="pvc-insight-text"><?php _e( 'More insights available', 'post-views-counter' ); ?></span>
286 <a href="https://postviewscounter.com/upgrade/?utm_source=post-views-counter-lite&utm_medium=link&utm_campaign=unlock-insights" target="_blank"><?php echo esc_html__( 'Upgrade to Pro to unlock it', 'post-views-counter' ); ?></a>
287 </div>
288 </div>
289 </div>
290 <div class="pvc-modal-content-bottom pvc-modal-dates"></div>
291 </div>
292 </div>
293 </div>
294 </div>
295 <?php
296 }
297 }
298