PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 7.3.5
WP Popular Posts v7.3.5
4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 5.0.0 5.0.1 5.0.2 5.1.0 5.2.0 5.2.1 5.2.2 5.2.3 5.2.4 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.1.0 6.1.1 6.1.2 6.1.3 6.1.4 6.2.0 6.2.1 6.3.0 6.3.1 6.3.2 6.3.3 6.3.4 6.4.0 6.4.1 6.4.2 7.0.0 7.0.1 7.1.0 7.2.0 7.3.0 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.3.7 7.3.8 7.4.0 trunk 2.3.7 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.2 4.0.3 4.0.5 4.0.6
wordpress-popular-posts / src / Rest / ViewLoggerEndpoint.php
wordpress-popular-posts / src / Rest Last commit date
Controller.php 8 months ago Endpoint.php 8 months ago PostsEndpoint.php 8 months ago TaxonomiesEndpoint.php 8 months ago ThemesEndpoint.php 8 months ago ThumbnailsEndpoint.php 8 months ago ViewLoggerEndpoint.php 8 months ago WidgetEndpoint.php 8 months ago
ViewLoggerEndpoint.php
379 lines
1 <?php
2 namespace WordPressPopularPosts\Rest;
3
4 use WordPressPopularPosts\Helper;
5
6 class ViewLoggerEndpoint extends Endpoint {
7
8 /**
9 * Registers the endpoint(s).
10 *
11 * @since 5.3.0
12 */
13 public function register()
14 {
15 $version = '2';
16 $namespace = 'wordpress-popular-posts/v' . $version;
17
18 /** @TODO: This endpoint has been superseeded by /views, please remove */
19 register_rest_route('wordpress-popular-posts/v1', '/popular-posts', [
20 [
21 'methods' => \WP_REST_Server::CREATABLE,
22 'callback' => [$this, 'update_views_count'],
23 'permission_callback' => '__return_true',
24 'args' => $this->get_tracking_params(),
25 ]
26 ]);
27
28 register_rest_route($namespace, '/views/(?P<id>[\d]+)', [
29 [
30 'methods' => \WP_REST_Server::READABLE,
31 'callback' => [$this, 'get_views_count'],
32 'permission_callback' => '__return_true',
33 'args' => $this->get_views_params(),
34 ]
35 ]);
36
37 register_rest_route($namespace, '/views/(?P<id>[\d]+)', [
38 [
39 'methods' => \WP_REST_Server::CREATABLE,
40 'callback' => [$this, 'update_views_count'],
41 'permission_callback' => '__return_true',
42 'args' => $this->get_tracking_params(),
43 ]
44 ]);
45 }
46
47 /**
48 * Returs the views count of a post/page.
49 *
50 * @since 7.0.0
51 *
52 * @param \WP_REST_Request $request Full details about the request.
53 * @return string Views count string.
54 */
55 public function get_views_count($request) {
56 $post_id = $request->get_param('id');
57 $range = in_array( $request->get_param('range'), ['last24hours', 'last7days', 'last30days', 'all', 'custom'] ) ? $request->get_param('range') : 'all';
58 $time_unit = in_array( $request->get_param('time_unit'), ['minute', 'hour', 'day', 'week', 'month'] ) ? $request->get_param('time_unit') : 'hour';
59 $time_quantity = $request->get_param('time_quantity');
60 $include_views_text = 1 == $request->get_param('include_views_text') ? 1 : 0;
61
62 $views_count_shortcode = '[wpp_views_count post_id=' . $post_id . ' include_views_text=' . $include_views_text . ' range="' . $range . '"';
63
64 if ( 'custom' == $range ) {
65 $views_count_shortcode .= ' time_unit="' . $time_unit . '" time_quantity=' . $time_quantity;
66 }
67
68 $views_count_shortcode .= ']';
69
70 $response['text'] = do_shortcode($views_count_shortcode);
71
72 return new \WP_REST_Response( $response, 200 );
73 }
74
75 /**
76 * Updates the views count of a post / page.
77 *
78 * @since 4.1.0
79 *
80 * @param \WP_REST_Request $request Full details about the request.
81 * @return string
82 */
83 public function update_views_count($request) {
84 global $wpdb;
85
86 /** @TODO: Remove this check once the /v1/popular-posts is removed */
87 if ( false !== strpos($request->get_route(), '/v1/popular-posts') ) {
88 $post_ID = $request->get_param('wpp_id');
89 // Throw warning to let developers know that
90 // the /v1/popular-posts endpoint is going away
91 trigger_error('The /wordpress-popular-posts/v1/popular-posts POST endpoint has been deprecated, please POST to /wordpress-popular-posts/v2/views/[ID] instead.', E_USER_WARNING);
92 }
93 else {
94 $post_ID = $request->get_param('id');
95 }
96
97 $sampling = $request->get_param('sampling');
98 $sampling_rate = $request->get_param('sampling_rate');
99
100 // Sampling settings from database
101 $_sampling = $this->config['tools']['sampling']['active'];
102 $_sampling_rate = $this->config['tools']['sampling']['rate'];
103
104 // Let's make sure that sampling settings we got
105 // on this request are what we expect
106 $sampling = $sampling != $_sampling ? $_sampling : $sampling;
107 $sampling_rate = $sampling_rate != $_sampling_rate ? $_sampling_rate : $sampling_rate;
108
109 $table = $wpdb->prefix . 'popularposts';
110 $data_table = "{$table}data";
111 $summary_table = "{$table}summary";
112 $wpdb->show_errors();
113
114 // Get translated object ID
115 $post_ID = $this->translate->get_object_id(
116 $post_ID,
117 get_post_type($post_ID),
118 true,
119 $this->translate->get_default_language()
120 );
121
122 $now = Helper::now();
123 $curdate = Helper::curdate();
124 $views = ($sampling)
125 ? $sampling_rate
126 : 1;
127
128 $original_views_count = $views;
129 $views = apply_filters('wpp_update_views_count_value', $views, $post_ID, $sampling, $sampling_rate);
130
131 if ( ! Helper::is_number($views) || $views <= 0 ) {
132 $views = $original_views_count;
133 }
134
135 // Allow WP themers / coders perform an action
136 // before updating views count
137 if ( has_action('wpp_pre_update_views') ) {
138 do_action('wpp_pre_update_views', $post_ID, $views);
139 }
140
141 $result1 = false;
142 $result2 = false;
143
144 $exec_time = 0;
145 $start = Helper::microtime_float();
146
147 // Store views data in persistent object cache
148 if (
149 wp_using_ext_object_cache()
150 && defined('WPP_CACHE_VIEWS')
151 && WPP_CACHE_VIEWS
152 ) {
153
154 $now_datetime = new \DateTime($now, wp_timezone());
155 $timestamp = $now_datetime->getTimestamp();
156 $date_time = $now_datetime->format('Y-m-d H:i');
157 $date_time_with_seconds = $now_datetime->format('Y-m-d H:i:s');
158 $high_accuracy = false;
159
160 $key = $high_accuracy ? $timestamp : $date_time;
161
162 $wpp_cache = wp_cache_get('_wpp_cache', 'transient');
163
164 if ( ! $wpp_cache ) {
165 $wpp_cache = [
166 'last_updated' => $date_time_with_seconds,
167 'data' => [
168 $post_ID => [
169 $key => 1
170 ]
171 ]
172 ];
173 } else {
174 if ( ! isset($wpp_cache['data'][$post_ID][$key]) ) {
175 $wpp_cache['data'][$post_ID][$key] = 1;
176 } else {
177 $wpp_cache['data'][$post_ID][$key] += 1;
178 }
179 }
180
181 // Update cache
182 wp_cache_set('_wpp_cache', $wpp_cache, 'transient', 0);
183
184 // How long has it been since the last time we saved to the database?
185 $last_update = $now_datetime->diff(new \DateTime($wpp_cache['last_updated'], wp_timezone()));
186 $diff_in_minutes = $last_update->days * 24 * 60;
187 $diff_in_minutes += $last_update->h * 60;
188 $diff_in_minutes += $last_update->i;
189
190 // It's been more than 2 minutes, save everything to DB
191 if ( $diff_in_minutes > 2 ) {
192
193 $query_data = $wpdb->prepare("INSERT INTO %i (`postid`,`day`,`last_viewed`,`pageviews`) VALUES ", $data_table);
194 $query_summary = $wpdb->prepare("INSERT INTO %i (`postid`,`pageviews`,`view_date`,`view_datetime`) VALUES ", $summary_table);
195
196 foreach( $wpp_cache['data'] as $pid => $data ) {
197 $views_count = 0;
198
199 foreach( $data as $ts => $cached_views ){
200 $views_count += $cached_views;
201 $ts = Helper::is_timestamp($ts) ? $ts : strtotime($ts);
202
203 $query_summary .= $wpdb->prepare('(%d,%d,%s,%s),', [
204 $pid,
205 $cached_views,
206 date('Y-m-d', $ts),
207 date('Y-m-d H:i:s', $ts)
208 ]);
209 }
210
211 $query_data .= $wpdb->prepare( '(%d,%s,%s,%s),', [
212 $pid,
213 $date_time_with_seconds,
214 $date_time_with_seconds,
215 $views_count
216 ]);
217 }
218
219 $query_data = rtrim($query_data, ',') . ' ON DUPLICATE KEY UPDATE pageviews=pageviews+VALUES(pageviews),last_viewed=VALUES(last_viewed);';
220 $query_summary = rtrim($query_summary, ',') . ';';
221
222 // Clear cache
223 $wpp_cache['last_updated'] = $date_time_with_seconds;
224 $wpp_cache['data'] = [];
225 wp_cache_set('_wpp_cache', $wpp_cache, 'transient', 0);
226
227 // Save
228 //phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.NotPrepared -- We already prepared $query_data and $query_summary above
229 $result1 = $wpdb->query($query_data);
230 $result2 = $wpdb->query($query_summary);
231 //phpcs:enable
232 }
233 else {
234 $result1 = true;
235 $result2 = true;
236 }
237 } // Live update to the DB
238 else {
239 //phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
240 // Update all-time table
241 $result1 = $wpdb->query($wpdb->prepare(
242 "INSERT INTO %i
243 (postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
244 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, last_viewed = %s;",
245 $data_table,
246 $post_ID,
247 $now,
248 $now,
249 $views,
250 $views,
251 $now
252 ));
253
254 // Update range (summary) table
255 $result2 = $wpdb->query($wpdb->prepare(
256 "INSERT INTO %i
257 (postid, pageviews, view_date, view_datetime) VALUES (%d, %d, %s, %s)
258 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, view_datetime = %s;",
259 $summary_table,
260 $post_ID,
261 $views,
262 $curdate,
263 $now,
264 $views,
265 $now
266 ));
267 //phpcs:enable
268 }
269
270 $end = Helper::microtime_float();
271 $exec_time += round($end - $start, 6);
272
273 $response = ['results' => ''];
274
275 if ( ! $result1 || ! $result2 ) {
276 $response['results'] = 'WPP: failed to update views count!';
277 return new \WP_REST_Response($response, 500);
278 }
279
280 // Allow WP themers / coders perform an action
281 // after updating views count
282 if ( has_action('wpp_post_update_views') ) {
283 do_action('wpp_post_update_views', $post_ID);
284 }
285
286 $response['results'] = 'WPP: OK. Execution time: ' . $exec_time . ' seconds';
287 return new \WP_REST_Response($response, 201);
288 }
289
290 /**
291 * Retrieves the query params for tracking views count.
292 *
293 * @since 4.1.0
294 *
295 * @return array Query parameters for tracking views count.
296 */
297 public function get_tracking_params()
298 {
299 /** @TODO: Remove wpp_id key once the /v1/popular-posts is removed */
300 return [
301 'wpp_id' => [
302 'description' => __('The post / page ID.'),
303 'type' => 'integer',
304 'default' => 0,
305 'sanitize_callback' => 'absint',
306 'validate_callback' => 'rest_validate_request_arg',
307 ],
308 'id' => [
309 'type' => 'integer',
310 'minimum' => 1,
311 'sanitize_callback' => 'absint',
312 'validate_callback' => 'rest_validate_request_arg',
313 ],
314 'sampling' => [
315 'description' => __('Enables Data Sampling.'),
316 'type' => 'integer',
317 'default' => 0,
318 'sanitize_callback' => 'absint',
319 'validate_callback' => 'rest_validate_request_arg',
320 ],
321 'sampling_rate' => [
322 'description' => __('Sets the Sampling Rate.'),
323 'type' => 'integer',
324 'default' => 100,
325 'sanitize_callback' => 'absint',
326 'validate_callback' => 'rest_validate_request_arg',
327 ]
328 ];
329 }
330
331 /**
332 * Retrieves the query params for getting post/page/cpt views count.
333 *
334 * @since 7.0.0
335 *
336 * @return array Query parameters for getting post/page/cpt views count.
337 */
338 public function get_views_params()
339 {
340 return [
341 'id' => [
342 'type' => 'integer',
343 'minimum' => 1,
344 'sanitize_callback' => 'absint',
345 'validate_callback' => 'rest_validate_request_arg',
346 ],
347 'range' => [
348 'type' => 'string',
349 'enum' => ['last24hours', 'last7days', 'last30days', 'all', 'custom'],
350 'default' => 'all',
351 'sanitize_callback' => 'sanitize_text_field',
352 'validate_callback' => '__return_true'
353 ],
354 'time_unit' => [
355 'type' => 'string',
356 'enum' => ['minute', 'hour', 'day', 'week', 'month'],
357 'default' => 'hour',
358 'sanitize_callback' => 'sanitize_text_field',
359 'validate_callback' => 'rest_validate_request_arg',
360 ],
361 'time_quantity' => [
362 'type' => 'integer',
363 'default' => 24,
364 'minimum' => 1,
365 'sanitize_callback' => 'absint',
366 'validate_callback' => 'rest_validate_request_arg',
367 ],
368 'include_views_text' => [
369 'type' => 'integer',
370 'default' => 1,
371 'sanitize_callback' => 'absint',
372 'validate_callback' => function($param, $request, $key) {
373 return is_numeric($param);
374 }
375 ],
376 ];
377 }
378 }
379