PluginProbe ʕ •ᴥ•ʔ
WP Job Manager / 2.4.7
WP Job Manager v2.4.7
2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 1.1.2 1.1.3 1.10.0 1.11.0 1.11.1 1.12.0 1.12.1 1.13.0 1.14.0 1.15.0 1.16.0 1.16.1 1.17.0 1.18.0 1.19.0 1.2.0 1.20.0 1.20.1 1.21.0 1.21.1 1.21.2 1.21.3 1.21.4 1.22.0 1.22.1 1.22.2 1.22.3 1.23.0 1.23.1 1.23.10 1.23.11 1.23.12 1.23.13 1.23.2 1.23.3 1.23.4 1.23.5 1.23.6 1.23.7 1.23.8 1.23.9 1.24.0 1.24.0.1 1.25.0 1.25.0.1 1.25.1 1.25.1.1 1.25.2 1.25.2.1 1.25.3 1.25.3.1 1.26.0 1.26.0.1 1.26.1 1.26.1.1 1.26.2 1.26.2.1 1.27.0 1.27.0.1 1.28.0 1.28.0.1 1.29.0 1.29.0.1 1.29.1 1.29.1.1 1.29.2 1.29.2.1 1.29.3 1.29.3.1 1.3.0 1.3.1 1.30.0 1.30.0.1 1.30.1 1.30.1.1 1.30.2 1.30.2.1 1.31.0 1.31.0.1 1.31.1 1.31.1.1 1.31.2 1.31.3 1.32.0 1.32.1 1.32.2 1.32.3 1.33.0 1.33.1 1.33.2 1.33.3 1.33.4 1.33.5 1.34.0 1.34.1 1.34.2 1.34.3 1.34.4 1.34.5 1.35.0 1.35.1 1.35.2 1.35.3 1.36.0 1.36.1 1.36.2 1.37.0 1.38.0 1.38.1 1.39.0 1.4.0 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.5.0 1.5.1 1.5.2 1.6.0 1.7.0 1.7.1 1.7.3 1.8.0 1.8.1 1.8.2 1.9.0 1.9.1 1.9.2 1.9.3 2.0.0 2.1.0 2.1.1 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.4.1
wp-job-manager / includes / class-stats-dashboard.php
wp-job-manager / includes Last commit date
3rd-party 3 days ago abstracts 3 months ago admin 2 weeks ago emails 2 weeks ago forms 3 days ago helper 3 months ago promoted-jobs 3 days ago ui 2 weeks ago widgets 2 weeks ago class-access-token.php 2 years ago class-dev-tools.php 2 years ago class-guest-session.php 2 years ago class-guest-user.php 2 years ago class-job-dashboard-shortcode.php 6 months ago class-job-listing-stats.php 2 years ago class-job-overlay.php 2 years ago class-stats-dashboard.php 2 years ago class-stats-script.php 3 months ago class-stats.php 3 months ago class-wp-job-manager-ajax.php 2 months ago class-wp-job-manager-api.php 6 years ago class-wp-job-manager-blocks.php 2 weeks ago class-wp-job-manager-cache-helper.php 3 months ago class-wp-job-manager-category-walker.php 6 years ago class-wp-job-manager-com-api.php 2 years ago class-wp-job-manager-data-cleaner.php 2 years ago class-wp-job-manager-data-exporter.php 3 months ago class-wp-job-manager-dependency-checker.php 2 years ago class-wp-job-manager-email-notifications.php 2 years ago class-wp-job-manager-forms.php 5 years ago class-wp-job-manager-geocode.php 2 weeks ago class-wp-job-manager-install.php 2 years ago class-wp-job-manager-post-types.php 3 days ago class-wp-job-manager-recaptcha.php 6 months ago class-wp-job-manager-rest-api.php 2 months ago class-wp-job-manager-shortcodes.php 2 weeks ago class-wp-job-manager-usage-tracking-data.php 2 years ago class-wp-job-manager-usage-tracking.php 2 years ago class-wp-job-manager-widget.php 2 weeks ago class-wp-job-manager.php 3 months ago trait-singleton.php 2 years ago
class-stats-dashboard.php
298 lines
1 <?php
2 /**
3 * File containing the class Stats_Dashboard.
4 *
5 * @package wp-job-manager
6 */
7
8 namespace WP_Job_Manager;
9
10 /**
11 * Job listing stats for the jobs dashboard.
12 *
13 * @since 2.3.0
14 */
15 class Stats_Dashboard {
16
17 use Singleton;
18
19 private const COLUMN_NAME = 'stats';
20
21 /**
22 * Max number of days to display in the daily stats chart.
23 */
24 private const DAYS_PER_PAGE = 180;
25
26 /**
27 * Constructor.
28 */
29 private function __construct() {
30
31 if ( ! Stats::is_enabled() ) {
32 return;
33 }
34
35 add_filter( 'job_manager_job_dashboard_columns', [ $this, 'add_stats_column' ] );
36 add_action( 'job_manager_job_dashboard_column_' . self::COLUMN_NAME, [ $this, 'render_stats_column' ] );
37
38 add_filter( 'manage_edit-job_listing_columns', [ $this, 'add_stats_column' ] );
39 add_action( 'manage_job_listing_posts_custom_column', [ $this, 'maybe_render_admin_stats_column' ], 2 );
40
41 add_action( 'job_manager_job_overlay_content', [ $this, 'output_job_stats' ], 12 );
42 }
43
44 /**
45 * Add a new column to the job dashboards.
46 *
47 * @param array $columns
48 *
49 * @return array
50 */
51 public function add_stats_column( $columns ) {
52 $columns[ self::COLUMN_NAME ] = __( 'Views', 'wp-job-manager' );
53
54 return $columns;
55 }
56
57 /**
58 * Output the stats column content.
59 *
60 * @param \WP_Post $job
61 */
62 public function render_stats_column( $job ) {
63 $stats = new Job_Listing_Stats( $job->ID );
64 $views = $stats->get_event_total( Job_Listing_Stats::VIEW );
65 $impressions = $stats->get_event_total( Job_Listing_Stats::SEARCH_IMPRESSION );
66
67 // translators: %1d is the number of page views.
68 $views_str = '<div>' . sprintf( _n( '%1d view', '%1d views', $views, 'wp-job-manager' ), $views ) . '</div>';
69 // translators: %1d is the number of impressions.
70 $views_str .= '<small>' . sprintf( _n( '%1d impression', '%1d impressions', $impressions, 'wp-job-manager' ), $impressions ) . '</small>';
71
72 echo wp_kses_post( $views_str );
73 }
74
75 /**
76 * Output stats column for the job listing post type admin screen.
77 *
78 * @param string $column
79 */
80 public function maybe_render_admin_stats_column( $column ) {
81 global $post;
82
83 if ( self::COLUMN_NAME === $column && ! empty( $post->ID ) ) {
84 echo '<a href="#' . esc_attr( $post->ID ) . ' ">';
85 $this->render_stats_column( $post );
86 echo '</a>';
87 }
88 }
89
90 /**
91 * Output job analytics section.
92 *
93 * @param \WP_Post $job
94 */
95 public function output_job_stats( $job ) {
96
97 $stat_summaries = $this->get_stat_summaries( $job );
98
99 $chart = $this->get_daily_stats_chart( $job );
100
101 get_job_manager_template(
102 'job-stats.php',
103 [
104 'stats' => $stat_summaries,
105 'chart' => $chart,
106 ]
107 );
108 }
109
110 /**
111 * Get data for the daily stats chart for a job.
112 *
113 * @param \WP_Post $job
114 *
115 * @return array
116 */
117 private function get_daily_stats_chart( \WP_Post $job ): array {
118
119 $start_date = get_post_datetime( $job );
120
121 if ( ! $start_date ) {
122 return [];
123 }
124
125 $past_days = $start_date->diff( new \DateTime() )->days + 1;
126
127 if ( $past_days > self::DAYS_PER_PAGE ) {
128 $start_date = $start_date->modify( '+' . ( $past_days - self::DAYS_PER_PAGE ) . ' day' );
129 }
130
131 $job_stats = new Job_Listing_Stats( $job->ID, $start_date ? [ $start_date ] : [] );
132
133 $daily_views = $job_stats->get_event_daily( Job_Listing_Stats::VIEW );
134 $daily_uniques = $job_stats->get_event_daily( Job_Listing_Stats::VIEW_UNIQUE );
135 $daily_impressions = $job_stats->get_event_daily( Job_Listing_Stats::SEARCH_IMPRESSION );
136
137 $max_views = ! empty( $daily_views ) ? max( $daily_views ) : 100;
138 $resolution = $max_views < 1000 ? 100 : 1000;
139 $max = max( ceil( $max_views / $resolution ) * $resolution, 100 );
140 $by_day = [];
141
142 foreach ( $daily_views as $date => $views ) {
143 $by_day[ $date ] = [
144 'date' => $date,
145 'views' => $views,
146 'uniques' => $daily_uniques[ $date ] ?? 0,
147 'impressions' => $daily_impressions[ $date ] ?? 0,
148 'class' => '',
149 ];
150 }
151
152 $end_date = \WP_Job_Manager_Post_Types::instance()->get_job_expiration( $job );
153
154 if ( ! $end_date ) {
155 $end_date = $start_date->modify( '+1 month' );
156 }
157
158 $all_days = $start_date->diff( $end_date )->days + 1;
159
160 $all_days = min( $all_days, self::DAYS_PER_PAGE );
161
162 $today = ( new \DateTime() )->format( 'Y-m-d' );
163
164 for ( $i = 0; $i < $all_days; $i++ ) {
165 $date = $start_date->modify( '+' . $i . ' day' )->format( 'Y-m-d' );
166 if ( empty( $by_day[ $date ] ) ) {
167 $by_day[ $date ] = [
168 'date' => $date,
169 'views' => 0,
170 'uniques' => 0,
171 'impressions' => 0,
172 'class' => 'future-day',
173 ];
174 }
175 }
176
177 if ( ! empty( $by_day[ $today ] ) ) {
178 $by_day[ $today ]['class'] = 'today';
179 }
180
181 ksort( $by_day );
182
183 $date_format = apply_filters( 'job_manager_get_dashboard_date_format', 'M d, Y' );
184 $timezone = new \DateTimeZone( wp_timezone_string() );
185
186 $by_day_formatted = [];
187 foreach ( $by_day as $date => $data ) {
188 $date_obj = new \DateTime( $date, $timezone );
189 $date_fmt = wp_date( $date_format, $date_obj->getTimestamp(), $timezone );
190 $by_day_formatted[ $date_fmt ] = $by_day[ $date ];
191 $by_day_formatted[ $date_fmt ]['date'] = $date_fmt;
192 }
193
194 $chart = [
195 'values' => $by_day_formatted,
196 'max' => $max,
197 'y-labels' => [ $max / 2, $max ],
198 ];
199 /**
200 * Filter the job daily stat data, displayed as a chart in the job overlay.
201 *
202 * @param array $stats Stat definition.
203 * @param \WP_Post $job Job post object.
204 */
205 return apply_filters( 'job_manager_job_stats_chart', $chart, $job );
206 }
207
208 /**
209 * Get summaries grouped into sections for various stats.
210 *
211 * @param \WP_Post $job
212 *
213 * @return mixed
214 */
215 private function get_stat_summaries( \WP_Post $job ) {
216 $job_stats = new Job_Listing_Stats( $job->ID );
217
218 $views = $job_stats->get_event_total( Job_Listing_Stats::VIEW );
219 $views_unique = $job_stats->get_event_total( Job_Listing_Stats::VIEW_UNIQUE );
220 $views_repeat = $views - $views_unique;
221 $search_impressions = $job_stats->get_event_total( Job_Listing_Stats::SEARCH_IMPRESSION );
222 $search_clicks = $search_impressions ? $views_unique / $search_impressions * 100 : 0;
223
224 /**
225 * Filter the job stat summaries, displayed in the job overlay.
226 *
227 * @param array $stats Stat definition.
228 * @param \WP_Post $job Job post object.
229 */
230 $stats = apply_filters(
231 'job_manager_job_stats_summary',
232 [
233 'views' => [
234 'title' => __( 'Total Views', 'wp-job-manager' ),
235 'stats' => [
236 [
237 'icon' => 'color-page-view',
238 'label' => __( 'Page views', 'wp-job-manager' ),
239 'value' => $views,
240 ],
241 [
242 'icon' => 'color-unique-view',
243 'label' => __( 'Unique visitors', 'wp-job-manager' ),
244 'value' => $views_unique,
245 ],
246 ],
247 'column' => 1,
248 ],
249 'impressions' => [
250 'title' => __( 'Impressions', 'wp-job-manager' ),
251 'help' => __( 'How many times the listing was seen in search results.', 'wp-job-manager' ),
252 'stats' => [
253 [
254 'icon' => 'search',
255 'label' => __( 'Search impressions', 'wp-job-manager' ),
256 'value' => $search_impressions,
257 ],
258 ],
259 'column' => 1,
260 ],
261 'interest' => [
262 'title' => __( 'Interest', 'wp-job-manager' ),
263 'stats' => [
264 [
265 'icon' => 'search',
266 'label' => __( 'Search click-through rate', 'wp-job-manager' ),
267 'percent' => $search_clicks,
268 ],
269 [
270 'icon' => 'cursor',
271 'label' => __( 'Apply clicks', 'wp-job-manager' ),
272 'value' => $job_stats->get_event_total( Job_Listing_Stats::APPLY_CLICK ),
273 ],
274 [
275 'icon' => 'url("data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'24\' height=\'24\' fill=\'none\' viewBox=\'0 0 24 24\'%3e%3cg fill=\'black\'%3e%3cpath d=\'M16.6 7.4a6.5 6.5 0 0 0-9.17-.02L8.7 8.66l-3.9.36.36-3.9 1.2 1.2a8 8 0 1 1-2.3 6.72l1.49-.2A6.5 6.5 0 1 0 16.6 7.4Z\'/%3e%3cpath d=\'m12 7-1 5c0 .37.2.7.51.87l4.13 2.76-2.74-4.11L12 7Z\'/%3e%3c/g%3e%3c/svg%3e")',
276 'label' => __( 'Repeat Views', 'wp-job-manager' ),
277 'value' => $views_repeat,
278 ],
279 ],
280 'column' => 2,
281 ],
282 ],
283 $job
284 );
285
286 $stat_columns = array_reduce(
287 $stats,
288 fn( $columns, $section ) => array_merge_recursive(
289 $columns,
290 [ 'column-' . $section['column'] => [ $section ] ]
291 ),
292 []
293 );
294
295 return $stat_columns;
296 }
297 }
298