PluginProbe ʕ •ᴥ•ʔ
Post Views Counter / trunk
Post Views Counter vtrunk
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 3 months ago class-columns-modal.php 1 month ago class-columns.php 2 weeks ago class-counter.php 2 months ago class-crawler-detect.php 7 months ago class-cron.php 1 month ago class-dashboard.php 1 month ago class-emails-mailer.php 1 month ago class-emails-period.php 1 month ago class-emails-query.php 1 month ago class-emails-scheduler.php 1 month ago class-emails-template.php 1 month ago class-emails.php 1 month ago class-frontend.php 2 weeks ago class-functions.php 1 year ago class-import.php 2 months ago class-integration-gutenberg.php 6 months ago class-integrations.php 2 months ago class-query.php 2 months ago class-settings-api.php 1 month ago class-settings-display.php 4 months ago class-settings-emails.php 1 month ago class-settings-general.php 1 month ago class-settings-integrations.php 5 months ago class-settings-other.php 1 month ago class-settings-reports.php 1 month ago class-settings.php 1 month ago class-toolbar.php 3 months ago class-traffic-signals.php 2 months ago class-update.php 1 month ago class-widgets.php 1 month ago functions.php 1 month ago
class-columns-modal.php
300 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 $post_types = (array) $pvc->options['general']['post_types_count'];
41
42 // break if display is not allowed
43 if ( ! $pvc->options['display']['post_views_column'] || ! in_array( $screen->post_type, $post_types, true ) )
44 return;
45
46 // check if user can see stats
47 if ( apply_filters( 'pvc_admin_display_post_views', true ) === false )
48 return;
49
50 // enqueue Micromodal
51 wp_enqueue_script( 'pvc-micromodal', POST_VIEWS_COUNTER_URL . '/assets/micromodal/micromodal.min.js', [], '0.4.10', true );
52
53 // enqueue Chart.js (already registered)
54 wp_enqueue_script( 'pvc-chartjs' );
55
56 // enqueue modal assets
57 wp_enqueue_style( 'pvc-admin-columns', POST_VIEWS_COUNTER_URL . '/css/admin-columns.css', [], $pvc->defaults['version'] );
58 wp_enqueue_script( 'pvc-admin-columns', POST_VIEWS_COUNTER_URL . '/js/admin-columns.js', [ 'jquery', 'pvc-chartjs', 'pvc-micromodal' ], $pvc->defaults['version'], true );
59
60 // BACKWARD COMPAT: Register legacy handles for version 1.7.3 and earlier
61 // Legacy checks for 'pvc-column-modal' handle; keep both registered
62 wp_register_style( 'pvc-column-modal', POST_VIEWS_COUNTER_URL . '/css/admin-columns.css', [], $pvc->defaults['version'] );
63 wp_register_script( 'pvc-column-modal', POST_VIEWS_COUNTER_URL . '/js/admin-columns.js', [ 'jquery', 'pvc-chartjs', 'pvc-micromodal' ], $pvc->defaults['version'], true );
64
65 // localize script
66 wp_add_inline_script( 'pvc-admin-columns', 'var pvcColumnModal = ' . wp_json_encode( [
67 'ajaxURL' => admin_url( 'admin-ajax.php' ),
68 'nonce' => wp_create_nonce( 'pvc-column-modal' ),
69 'i18n' => [
70 'loading' => __( 'Loading...', 'post-views-counter' ),
71 'close' => __( 'Close', 'post-views-counter' ),
72 'error' => __( 'An error occurred while loading data.', 'post-views-counter' ),
73 'summary' => __( 'Total views in this period:', 'post-views-counter' ),
74 'view' => __( 'view', 'post-views-counter' ),
75 'views' => __( 'views', 'post-views-counter' )
76 ]
77 ] ) . "\n", 'before' );
78
79 // add modal HTML to footer
80 add_action( 'admin_footer', [ $this, 'render_modal_html' ] );
81 }
82
83 /**
84 * AJAX handler for column chart data.
85 *
86 * @return void
87 */
88 public function ajax_column_chart() {
89 // permission & nonce check
90 if ( ! check_ajax_referer( 'pvc-column-modal', 'nonce', false ) )
91 wp_send_json_error( [ 'message' => __( 'Permission denied.', 'post-views-counter' ) ] );
92
93 // get PVC instance
94 $pvc = Post_Views_Counter();
95 $post_types = (array) $pvc->options['general']['post_types_count'];
96
97 // get post ID
98 $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
99
100 if ( ! $post_id )
101 wp_send_json_error( [ 'message' => __( 'Invalid post ID.', 'post-views-counter' ) ] );
102
103 // check post exists
104 $post = get_post( $post_id );
105
106 if ( ! $post )
107 wp_send_json_error( [ 'message' => __( 'Post not found.', 'post-views-counter' ) ] );
108
109 // break if display is not allowed
110 if ( ! $pvc->options['display']['post_views_column'] )
111 wp_send_json_error( [ 'message' => __( 'Admin column disabled.', 'post-views-counter' ) ] );
112
113 // ensure post type is tracked
114 if ( ! in_array( $post->post_type, $post_types, true ) )
115 wp_send_json_error( [ 'message' => __( 'Post type is not tracked.', 'post-views-counter' ) ] );
116
117 // check display permission for this specific post
118 if ( apply_filters( 'pvc_admin_display_post_views', true, $post_id ) === false )
119 wp_send_json_error( [ 'message' => __( 'Access denied for this post.', 'post-views-counter' ) ] );
120
121 // get period (format: YYYYMM or empty for current month)
122 $period_str = isset( $_POST['period'] ) && ! empty( $_POST['period'] ) ? preg_replace( '/[^0-9]/', '', $_POST['period'] ) : '';
123
124 // parse period or use current
125 if ( $period_str && strlen( $period_str ) === 6 ) {
126 $year = substr( $period_str, 0, 4 );
127 $month = substr( $period_str, 4, 2 );
128 $date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
129
130 if ( ! $date )
131 $date = new DateTime( 'now', wp_timezone() );
132 } else {
133 $date = new DateTime( 'now', wp_timezone() );
134 }
135
136 $year = $date->format( 'Y' );
137 $month = $date->format( 'm' );
138 $last_day = $date->format( 't' );
139
140 // fetch views data
141 $views = pvc_get_views( [
142 'post_id' => $post_id,
143 'post_type' => $post->post_type,
144 'fields' => 'date=>views',
145 'views_query' => [
146 'year' => (int) $year,
147 'month' => (int) $month
148 ]
149 ] );
150
151 // get colors
152 $colors = $pvc->functions->get_colors();
153
154 // prepare response data
155 $data = [
156 'post_id' => $post_id,
157 'post_title'=> get_the_title( $post_id ),
158 'period' => $year . $month,
159 'design' => [
160 'fill' => true,
161 'backgroundColor' => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 0.2)',
162 'borderColor' => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 1)',
163 'borderWidth' => 1.2,
164 'borderDash' => [],
165 'pointBorderColor' => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 1)',
166 'pointBackgroundColor' => 'rgba(255, 255, 255, 1)',
167 'pointBorderWidth' => 1.2
168 ],
169 'data' => [
170 'labels' => [],
171 'dates' => [],
172 'datasets' => [
173 [
174 'label' => get_the_title( $post_id ),
175 'data' => []
176 ]
177 ]
178 ]
179 ];
180
181 // generate dates and data
182 for ( $i = 1; $i <= $last_day; $i++ ) {
183 $date_key = $year . $month . str_pad( $i, 2, '0', STR_PAD_LEFT );
184
185 // labels: show only odd days
186 $data['data']['labels'][] = ( $i % 2 === 0 ? '' : $i );
187
188 // formatted dates for tooltips
189 $data['data']['dates'][] = date_i18n( get_option( 'date_format' ), strtotime( $year . '-' . $month . '-' . str_pad( $i, 2, '0', STR_PAD_LEFT ) ) );
190
191 // view count
192 $data['data']['datasets'][0]['data'][] = isset( $views[$date_key] ) ? (int) $views[$date_key] : 0;
193 }
194
195 // calculate total views for the period
196 $data['total_views'] = array_sum( $data['data']['datasets'][0]['data'] );
197
198 // check if there is any period-specific data
199 $period_has_data = false;
200 foreach ( $data['data']['datasets'][0]['data'] as $val ) {
201 if ( (int) $val > 0 ) {
202 $period_has_data = true;
203 break;
204 }
205 }
206
207 $data['period_has_data'] = $period_has_data;
208
209 // generate date navigation HTML
210 $data['dates_html'] = $this->generate_modal_dates( (int) $year, (int) $month );
211
212 wp_send_json_success( $data );
213 }
214
215 /**
216 * Generate month navigation for modal.
217 *
218 * @param int $year
219 * @param int $month
220 * @return string
221 */
222 private function generate_modal_dates( $year, $month ) {
223 // previous month
224 $prev_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
225 $prev_date->modify( '-1 month' );
226
227 // next month
228 $next_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
229 $next_date->modify( '+1 month' );
230
231 // current
232 $current_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
233
234 // check if next is in the future
235 $now = new DateTime( 'now', wp_timezone() );
236 $can_go_next = $next_date <= $now;
237
238 $html = '<div class="pvc-modal-nav">';
239 $html .= '<a href="#" class="pvc-modal-nav-prev" data-period="' . $prev_date->format( 'Ym' ) . '">‹ ' . date_i18n( 'F Y', $prev_date->getTimestamp() ) . '</a>';
240 $html .= '<span class="pvc-modal-nav-current">' . date_i18n( 'F Y', $current_date->getTimestamp() ) . '</span>';
241
242 if ( $can_go_next )
243 $html .= '<a href="#" class="pvc-modal-nav-next" data-period="' . $next_date->format( 'Ym' ) . '">' . date_i18n( 'F Y', $next_date->getTimestamp() ) . ' ›</a>';
244 else
245 $html .= '<span class="pvc-modal-nav-next pvc-disabled">' . date_i18n( 'F Y', $next_date->getTimestamp() ) . ' ›</span>';
246
247 $html .= '</div>';
248
249 return $html;
250 }
251
252 /**
253 * Render modal HTML in admin footer.
254 *
255 * @return void
256 */
257 public function render_modal_html() {
258 ?>
259 <div id="pvc-chart-modal" class="pvc-modal micromodal-slide" aria-hidden="true">
260 <div class="pvc-modal__overlay" tabindex="-1" data-micromodal-close>
261 <div class="pvc-modal__container" role="dialog" aria-modal="true" aria-labelledby="pvc-modal-title">
262 <header class="pvc-modal__header">
263 <h2 class="pvc-modal__title" id="pvc-modal-title"></h2>
264 <button class="pvc-modal__close" aria-label="<?php esc_attr_e( 'Close', 'post-views-counter' ); ?>" data-micromodal-close></button>
265 </header>
266 <div class="pvc-modal__content">
267 <div class="pvc-modal-content-top">
268 <div class="pvc-modal-summary">
269 <span class="pvc-modal-views-label"></span>
270 <span class="pvc-modal-views-data">
271 <span class="pvc-modal-count"></span>
272 </span>
273 </div>
274 <div class="pvc-modal-tabs" role="tablist">
275 <button type="button" class="pvc-modal-tab pvc-pro" disabled><span><?php _e( 'Year', 'post-views-counter' ); ?></span></button>
276 <button type="button" class="pvc-modal-tab active"><span><?php _e( 'Month', 'post-views-counter' ); ?></span></button>
277 <button type="button" class="pvc-modal-tab pvc-pro" disabled><span><?php _e( 'Week', 'post-views-counter' ); ?></span></button>
278 </div>
279 </div>
280 <div class="pvc-modal-chart-container">
281 <canvas id="pvc-modal-chart" height="200"></canvas>
282 <span class="spinner"></span>
283 </div>
284 <div class="pvc-modal-content-middle" style="display: none;">
285 <div class="pvc-modal-insights">
286 <div class="pvc-insight pvc-insight-lock pvc-modal-insights-empty">
287 <span class="pvc-insight-text"><?php _e( 'More insights available', 'post-views-counter' ); ?></span>
288 <a href="<?php echo esc_url( Post_Views_Counter()->get_postviewscounter_url( '/upgrade/', 'link', 'upgrade-to-pro', 'admin-column-modal-locked-insight-link', 'free' ) ); ?>" target="_blank"><?php echo esc_html__( 'Upgrade to Pro to unlock it', 'post-views-counter' ); ?></a>
289 </div>
290 </div>
291 </div>
292 <div class="pvc-modal-content-bottom pvc-modal-dates"></div>
293 </div>
294 </div>
295 </div>
296 </div>
297 <?php
298 }
299 }
300