PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 6.4.1
WP Popular Posts v6.4.1
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 2 years ago Endpoint.php 2 years ago PostsEndpoint.php 2 years ago TaxonomiesEndpoint.php 2 years ago ThemesEndpoint.php 2 years ago ThumbnailsEndpoint.php 2 years ago ViewLoggerEndpoint.php 2 years ago WidgetEndpoint.php 2 years ago
ViewLoggerEndpoint.php
363 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 $wpdb->show_errors();
111
112 // Get translated object ID
113 $post_ID = $this->translate->get_object_id(
114 $post_ID,
115 get_post_type($post_ID),
116 true,
117 $this->translate->get_default_language()
118 );
119
120 $now = Helper::now();
121 $curdate = Helper::curdate();
122 $views = ($sampling)
123 ? $sampling_rate
124 : 1;
125
126 $original_views_count = $views;
127 $views = apply_filters('wpp_update_views_count_value', $views, $post_ID, $sampling, $sampling_rate);
128
129 if ( ! Helper::is_number($views) || $views <= 0 ) {
130 $views = $original_views_count;
131 }
132
133 // Allow WP themers / coders perform an action
134 // before updating views count
135 if ( has_action('wpp_pre_update_views') ) {
136 do_action('wpp_pre_update_views', $post_ID, $views);
137 }
138
139 $result1 = false;
140 $result2 = false;
141
142 $exec_time = 0;
143 $start = Helper::microtime_float();
144
145 // Store views data in persistent object cache
146 if (
147 wp_using_ext_object_cache()
148 && defined('WPP_CACHE_VIEWS')
149 && WPP_CACHE_VIEWS
150 ) {
151
152 $now_datetime = new \DateTime($now, wp_timezone());
153 $timestamp = $now_datetime->getTimestamp();
154 $date_time = $now_datetime->format('Y-m-d H:i');
155 $date_time_with_seconds = $now_datetime->format('Y-m-d H:i:s');
156 $high_accuracy = false;
157
158 $key = $high_accuracy ? $timestamp : $date_time;
159
160 $wpp_cache = wp_cache_get('_wpp_cache', 'transient');
161
162 if ( ! $wpp_cache ) {
163 $wpp_cache = [
164 'last_updated' => $date_time_with_seconds,
165 'data' => [
166 $post_ID => [
167 $key => 1
168 ]
169 ]
170 ];
171 } else {
172 if ( ! isset($wpp_cache['data'][$post_ID][$key]) ) {
173 $wpp_cache['data'][$post_ID][$key] = 1;
174 } else {
175 $wpp_cache['data'][$post_ID][$key] += 1;
176 }
177 }
178
179 // Update cache
180 wp_cache_set('_wpp_cache', $wpp_cache, 'transient', 0);
181
182 // How long has it been since the last time we saved to the database?
183 $last_update = $now_datetime->diff(new \DateTime($wpp_cache['last_updated'], wp_timezone()));
184 $diff_in_minutes = $last_update->days * 24 * 60;
185 $diff_in_minutes += $last_update->h * 60;
186 $diff_in_minutes += $last_update->i;
187
188 // It's been more than 2 minutes, save everything to DB
189 if ( $diff_in_minutes > 2 ) {
190
191 $query_data = "INSERT INTO {$table}data (`postid`,`day`,`last_viewed`,`pageviews`) VALUES ";
192 $query_summary = "INSERT INTO {$table}summary (`postid`,`pageviews`,`view_date`,`view_datetime`) VALUES ";
193
194 foreach( $wpp_cache['data'] as $pid => $data ) {
195 $views_count = 0;
196
197 foreach( $data as $ts => $cached_views ){
198 $views_count += $cached_views;
199 $ts = Helper::is_timestamp($ts) ? $ts : strtotime($ts);
200
201 $query_summary .= $wpdb->prepare('(%d,%d,%s,%s),', [
202 $pid,
203 $cached_views,
204 date('Y-m-d', $ts),
205 date('Y-m-d H:i:s', $ts)
206 ]);
207 }
208
209 $query_data .= $wpdb->prepare( '(%d,%s,%s,%s),', [
210 $pid,
211 $date_time_with_seconds,
212 $date_time_with_seconds,
213 $views_count
214 ]);
215 }
216
217 $query_data = rtrim($query_data, ',') . ' ON DUPLICATE KEY UPDATE pageviews=pageviews+VALUES(pageviews),last_viewed=VALUES(last_viewed);';
218 $query_summary = rtrim($query_summary, ',') . ';';
219
220 // Clear cache
221 $wpp_cache['last_updated'] = $date_time_with_seconds;
222 $wpp_cache['data'] = [];
223 wp_cache_set('_wpp_cache', $wpp_cache, 'transient', 0);
224
225 // Save
226 $result1 = $wpdb->query($query_data); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- We already prepared $query_data above
227 $result2 = $wpdb->query($query_summary); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- We already prepared $query_summary above
228 }
229 else {
230 $result1 = true;
231 $result2 = true;
232 }
233 } // Live update to the DB
234 else {
235 // Update all-time table
236 //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table is safe to use
237 $result1 = $wpdb->query($wpdb->prepare(
238 "INSERT INTO {$table}data
239 (postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
240 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, last_viewed = %s;",
241 $post_ID,
242 $now,
243 $now,
244 $views,
245 $views,
246 $now
247 ));
248 //phpcs:enable
249
250 // Update range (summary) table
251 //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table is safe to use
252 $result2 = $wpdb->query($wpdb->prepare(
253 "INSERT INTO {$table}summary
254 (postid, pageviews, view_date, view_datetime) VALUES (%d, %d, %s, %s)
255 ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, view_datetime = %s;",
256 $post_ID,
257 $views,
258 $curdate,
259 $now,
260 $views,
261 $now
262 ));
263 //phpcs:enable
264 }
265
266 $end = Helper::microtime_float();
267 $exec_time += round($end - $start, 6);
268
269 $response = ['results' => ''];
270
271 if ( ! $result1 || ! $result2 ) {
272 $response['results'] = 'WPP: failed to update views count!';
273 return new \WP_REST_Response($response, 500);
274 }
275
276 // Allow WP themers / coders perform an action
277 // after updating views count
278 if ( has_action('wpp_post_update_views') ) {
279 do_action('wpp_post_update_views', $post_ID);
280 }
281
282 $response['results'] = 'WPP: OK. Execution time: ' . $exec_time . ' seconds';
283 return new \WP_REST_Response($response, 201);
284 }
285
286 /**
287 * Retrieves the query params for tracking views count.
288 *
289 * @since 4.1.0
290 *
291 * @return array Query parameters for tracking views count.
292 */
293 public function get_tracking_params()
294 {
295 /** @TODO: Remove wpp_id key once the /v1/popular-posts is removed */
296 return [
297 'wpp_id' => [
298 'description' => __('The post / page ID.'),
299 'type' => 'integer',
300 'default' => 0,
301 'sanitize_callback' => 'absint',
302 'validate_callback' => 'rest_validate_request_arg',
303 ],
304 'sampling' => [
305 'description' => __('Enables Data Sampling.'),
306 'type' => 'integer',
307 'default' => 0,
308 'sanitize_callback' => 'absint',
309 'validate_callback' => 'rest_validate_request_arg',
310 ],
311 'sampling_rate' => [
312 'description' => __('Sets the Sampling Rate.'),
313 'type' => 'integer',
314 'default' => 100,
315 'sanitize_callback' => 'absint',
316 'validate_callback' => 'rest_validate_request_arg',
317 ]
318 ];
319 }
320
321 /**
322 * Retrieves the query params for getting post/page/cpt views count.
323 *
324 * @since 7.0.0
325 *
326 * @return array Query parameters for getting post/page/cpt views count.
327 */
328 public function get_views_params()
329 {
330 return [
331 'range' => [
332 'type' => 'string',
333 'enum' => ['last24hours', 'last7days', 'last30days', 'all', 'custom'],
334 'default' => 'all',
335 'sanitize_callback' => 'sanitize_text_field',
336 'validate_callback' => '__return_true'
337 ],
338 'time_unit' => [
339 'type' => 'string',
340 'enum' => ['minute', 'hour', 'day', 'week', 'month'],
341 'default' => 'hour',
342 'sanitize_callback' => 'sanitize_text_field',
343 'validate_callback' => 'rest_validate_request_arg',
344 ],
345 'time_quantity' => [
346 'type' => 'integer',
347 'default' => 24,
348 'minimum' => 1,
349 'sanitize_callback' => 'absint',
350 'validate_callback' => 'rest_validate_request_arg',
351 ],
352 'include_views_text' => [
353 'type' => 'integer',
354 'default' => 1,
355 'sanitize_callback' => 'absint',
356 'validate_callback' => function($param, $request, $key) {
357 return is_numeric($param);
358 }
359 ],
360 ];
361 }
362 }
363