PluginProbe
BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP / 2.4.13
BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP v2.4.13
3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 3.0.0 2.4.13 2.4.12 2.4.11 2.4.10 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.1.4 1.1.5 1.1.6 1.1.7 All 110 releases
betterlinks / includes / API / Clicks.php

Clicks.php in BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP 2.4.13, at includes/API/Clicks.php

560 lines 15.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace BetterLinks\API;
3 if ( ! defined( 'ABSPATH' ) ) { exit; }
4
5 use BetterLinks\Traits\ArgumentSchema;
6
7 class Clicks extends Controller {
8
9 use \BetterLinks\Traits\Clicks;
10 use ArgumentSchema;
11
12 /**
13 * Initialize hooks and option name
14 */
15 public function __construct() {
16 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
17 }
18
19 /**
20 * Register the routes for the objects of the controller.
21 */
22 public function register_routes() {
23 $endpoint = '/clicks/';
24 register_rest_route(
25 $this->namespace,
26 $endpoint,
27 array(
28 array(
29 'methods' => \WP_REST_Server::READABLE,
30 'callback' => array( $this, 'get_items' ),
31 'permission_callback' => array( $this, 'get_items_permissions_check' ),
32 'args' => $this->get_clicks_schema(),
33 ),
34 )
35 );
36 register_rest_route(
37 $this->namespace,
38 $endpoint . 'get_graphs/',
39 array(
40 array(
41 'methods' => \WP_REST_Server::READABLE,
42 'callback' => array( $this, 'get_graphs' ),
43 'permission_callback' => array( $this, 'get_items_permissions_check' ),
44 'args' => $this->get_clicks_schema(),
45 ),
46 )
47 );
48
49 register_rest_route(
50 $this->namespace,
51 $endpoint . '(?P<id>[\d]+)',
52 array(
53 'args' => array(
54 'id' => array(
55 'description' => __( 'Unique identifier for the object.' , 'betterlinks' ),
56 'type' => 'integer',
57 ),
58 ),
59 array(
60 'methods' => \WP_REST_Server::READABLE,
61 'callback' => array( $this, 'get_item' ),
62 'permission_callback' => array( $this, 'get_items_permissions_check' ),
63 'args' => $this->get_clicks_schema(),
64 ),
65 )
66 );
67 register_rest_route(
68 $this->namespace,
69 $endpoint . 'tags/' . '(?P<id>[\d]+)',
70 array(
71 'args' => array(
72 'id' => array(
73 'description' => __( 'Unique identifier for the object.' , 'betterlinks' ),
74 'type' => 'integer',
75 ),
76 ),
77 array(
78 'methods' => \WP_REST_Server::READABLE,
79 'callback' => array( $this, 'get_tags_analytics' ),
80 'permission_callback' => array( $this, 'get_items_permissions_check' ),
81 'args' => $this->get_clicks_schema(),
82 ),
83 )
84 );
85
86 register_rest_route(
87 $this->namespace,
88 $endpoint . 'tags/get_graphs/' . '(?P<id>[\d]+)',
89 array(
90 'args' => array(
91 'id' => array(
92 'description' => __( 'Unique identifier for the object.' , 'betterlinks' ),
93 'type' => 'integer',
94 ),
95 ),
96 array(
97 'methods' => \WP_REST_Server::READABLE,
98 'callback' => array( $this, 'get_tags_graph' ),
99 'permission_callback' => array( $this, 'get_items_permissions_check' ),
100 'args' => $this->get_clicks_schema(),
101 ),
102 )
103 );
104
105 register_rest_route(
106 $this->namespace,
107 $endpoint,
108 array(
109 array(
110 'methods' => \WP_REST_Server::CREATABLE,
111 'callback' => array( $this, 'create_item' ),
112 'permission_callback' => array( $this, 'permissions_check' ),
113 'args' => $this->get_clicks_schema(),
114 ),
115 )
116 );
117
118 register_rest_route(
119 $this->namespace,
120 $endpoint,
121 array(
122 array(
123 'methods' => \WP_REST_Server::EDITABLE,
124 'callback' => array( $this, 'update_item' ),
125 'permission_callback' => array( $this, 'permissions_check' ),
126 'args' => $this->get_clicks_schema(),
127 ),
128 )
129 );
130
131 register_rest_route(
132 $this->namespace,
133 $endpoint,
134 array(
135 array(
136 'methods' => \WP_REST_Server::DELETABLE,
137 'callback' => array( $this, 'delete_item' ),
138 'permission_callback' => array( $this, 'permissions_check' ),
139 'args' => $this->get_clicks_schema(),
140 ),
141 )
142 );
143
144 register_rest_route(
145 $this->namespace,
146 $endpoint . 'delete_by_links/',
147 array(
148 array(
149 'methods' => \WP_REST_Server::DELETABLE,
150 'callback' => array( $this, 'delete_links_analytics' ),
151 'permission_callback' => array( $this, 'permissions_check' ),
152 'args' => $this->get_clicks_schema(),
153 ),
154 )
155 );
156
157 do_action( 'betterlinks_register_clicks_routes', $this );
158 }
159
160 /**
161 * Get Analytics Graph Data
162 *
163 * @param WP_REST_Request $request Full data about the request.
164 * @return WP_Error|WP_REST_Response
165 */
166 public function get_graphs( $request ) {
167 $request = $request->get_params();
168 $from = $this->sanitize_date( $request['from'] ) ? $request['from'] : gmdate( 'Y-m-d', strtotime( ' - 30 days' ) );
169 $to = $this->sanitize_date( $request['to'] ) ? $request['to'] : gmdate( 'Y-m-d' );
170
171 $graph_data = $this->get_analytics_graph_data( $from, $to );
172 return new \WP_REST_Response(
173 array(
174 'success' => true,
175 'data' => array(
176 'clicks' => $graph_data,
177 ),
178 )
179 );
180 }
181
182 /**
183 * Get analytics graph data by Tag ID
184 *
185 * @param WP_REST_Request $request Full data about the request.
186 * @return WP_Error|WP_REST_Response
187 */
188 public function get_tags_graph( $request ) {
189 $request = $request->get_params();
190 $from = $this->sanitize_date( $request['from']) ? $request['from'] : '';
191 $to = $this->sanitize_date( $request['to']) ? $request['to'] : '';
192 $id = isset( $request['id'] ) ? $request['id'] : '';
193
194 if( empty( $from ) || empty( $to ) ) {
195 return new \WP_REST_Response(
196 array(
197 'success' => false,
198 'message' => __( "Invalid date range provided.", 'betterlinks' ),
199 ),
200 400
201 );
202 }
203
204 $results = $this->get_analytics_graph_data_by_tag( $from, $to, $id );
205
206 return new \WP_REST_Response(
207 array(
208 'success' => true,
209 'data' => $results,
210 ),
211 200
212 );
213 }
214
215 /**
216 * Get unique analytics list by Tag ID
217 *
218 * @param WP_REST_Request $request Full data about the request.
219 * @return WP_Error|WP_REST_Response
220 */
221 public function get_tags_analytics( $request ) {
222 $request = $request->get_params();
223
224 $from = $this->sanitize_date( $request['from'] ) ? $request['from'] : '';
225 $to = $this->sanitize_date( $request['to'] ) ? $request['to'] : '';
226 $id = isset( $request['id'] ) ? $request['id'] : '';
227
228 if( empty( $from ) || empty( $to ) ) {
229 return new \WP_REST_Response(
230 array(
231 'success' => false,
232 'message' => __( "Invalid date range provided.", 'betterlinks' ),
233 ),
234 400
235 );
236 }
237
238 $results = $this->get_analytics_unique_list_by_tag( $from, $to, $id );
239
240 $analytic = get_option( 'betterlinks_analytics_data' );
241 $analytic = $analytic ? json_decode( $analytic, true ) : array();
242
243 return new \WP_REST_Response(
244 array(
245 'success' => true,
246 'data' => array(
247 'list' => $results,
248 'analytic' => $analytic,
249 ),
250 ),
251 200
252 );
253 }
254
255 /**
256 * Get betterlinks
257 *
258 * @param WP_REST_Request $request Full data about the request.
259 * @return WP_Error|WP_REST_Response
260 */
261 public function get_items( $request ) {
262 $request = $request->get_params();
263
264 $from = isset($request['from']) && $this->sanitize_date( $request['from'] ) ? $request['from'] : '';
265 $to = isset($request['to']) && $this->sanitize_date( $request['to'] ) ? $request['to'] : '';
266
267 if( empty( $from ) || empty( $to ) ) {
268 return new \WP_REST_Response(
269 array(
270 'success' => false,
271 'data' => [],
272 'message' => __( "Invalid date range provided.", 'betterlinks' ),
273 ),
274 400
275 );
276 }
277
278 $unique_list = $this->get_analytics_unique_list( $from, $to );
279 $unique_click_count = $this->get_unique_clicks_count($from, $to);
280
281 // $analytic = get_option( 'betterlinks_analytics_data' );
282 $analytic = $this->get_analytics_data($from, $to);
283 $analytic = $analytic ? json_decode( $analytic, true ) : array();
284
285 return new \WP_REST_Response(
286 array(
287 'success' => true,
288 'data' => array(
289 'unique_list' => $unique_list,
290 'unique_count' => $unique_click_count,
291 'analytic' => $analytic,
292 ),
293 ),
294 200
295 );
296 }
297
298
299 /**
300 * Get Individual Clicks
301 *
302 * @param WP_Rest_Request $request
303 * @return WP_Error|WP_Rest_Response
304 */
305 public function get_item( $request ) {
306 $request = $request->get_params();
307
308 $id = ! empty( $request['id'] ) ? sanitize_text_field( $request['id'] ) : null;
309 $from = $this->sanitize_date( $request['from'] ) ? sanitize_text_field( $request['from'] ) : '';
310 $to = $this->sanitize_date( $request['to'] ) ? sanitize_text_field( $request['to'] ) : '';
311
312 if( empty( $from ) || empty( $to ) ) {
313 return new \WP_REST_Response(
314 array(
315 'success' => false,
316 'message' => __( "Invalid date range provided.", 'betterlinks' ),
317 ),
318 400
319 );
320 }
321
322 $results = $this->get_individual_analytics_clicks( $id, $from, $to );
323 $link_details = $this->get_individual_link_details( $id );
324 $graph_data = array(
325 'total_count' => array(),
326 'unique_count' => array(),
327 );
328 $graph_data = apply_filters( 'betterlinkspro/get_individual_graph_data', $graph_data, $id, $from, $to );
329
330 return new \WP_REST_Response(
331 array(
332 'data' => array(
333 'analytics' => $results,
334 'graph_data' => $graph_data,
335 'link_details' => $link_details,
336 ),
337 'id' => $id,
338 ),
339 200
340 );
341 }
342
343 /**
344 * Create OR Update betterlinks
345 *
346 * @param WP_REST_Request $request Full data about the request.
347 * @return WP_Error|WP_REST_Request
348 */
349 public function create_item( $request ) {
350 return new \WP_REST_Response(
351 array(
352 'success' => false,
353 'data' => array(),
354 ),
355 200
356 );
357 }
358
359 /**
360 * Create OR Update betterlinks
361 *
362 * @param WP_REST_Request $request Full data about the request.
363 * @return WP_Error|WP_REST_Request
364 */
365 public function update_item( $request ) {
366 return new \WP_REST_Response(
367 array(
368 'success' => false,
369 'data' => array(),
370 ),
371 200
372 );
373 }
374
375 /**
376 * Delete betterlinks clicks
377 *
378 * @param WP_REST_Request $request Full data about the request.
379 * @return WP_Error|WP_REST_Request
380 */
381 public function delete_item( $request ) {
382 $params = $request->get_params();
383
384 $click_ids_raw = isset( $params['click_ids'] ) ? $params['click_ids'] : '';
385 $link_id = isset( $params['link_id'] ) ? intval( $params['link_id'] ) : 0;
386
387 // Sanitize and parse click IDs
388 $click_ids = array_filter( array_map( 'intval', explode( ',', sanitize_text_field( $click_ids_raw ) ) ) );
389
390 if ( empty( $click_ids ) || empty( $link_id ) ) {
391 return new \WP_REST_Response(
392 array(
393 'success' => false,
394 'message' => __( 'Invalid click IDs or link ID provided.', 'betterlinks' ),
395 ),
396 400
397 );
398 }
399
400 // Get date range from params
401 $from = isset( $params['from'] ) ? sanitize_text_field( $params['from'] ) : gmdate( 'Y-m-d', strtotime( ' - 30 days' ) );
402 $to = isset( $params['to'] ) ? sanitize_text_field( $params['to'] ) : gmdate( 'Y-m-d' );
403
404 // Delete the clicks from the database - only within the date range
405 global $wpdb;
406 $table_name = $wpdb->prefix . 'betterlinks_clicks';
407
408 foreach ( $click_ids as $click_id ) {
409 $wpdb->delete( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
410 $table_name,
411 array(
412 'ID' => intval( $click_id ),
413 'link_id' => $link_id,
414 ),
415 array( '%d', '%d' )
416 );
417 }
418
419 // Clear all related transient caches for this link's analytics
420 $transient_key = 'btl_individual_analytics_clicks_' . md5( $from . $to . $link_id );
421 delete_transient( $transient_key );
422
423 // Also clear graph data cache
424 $graph_transient_key = 'btl_individual_graph_data_' . md5( $from . $to . $link_id );
425 delete_transient( $graph_transient_key );
426
427 // Clear all analytics caches to be safe
428 \BetterLinks\Helper::clear_analytics_cache();
429
430 // Update the betterlinks_analytics_data option with fresh data from database
431 \BetterLinks\Helper::update_links_analytics();
432
433 // Clear links cache so that page refresh shows updated analytics
434 delete_transient( BETTERLINKS_CACHE_LINKS_NAME );
435
436 $results = $this->get_individual_analytics_clicks( $link_id, $from, $to );
437 $link_details = $this->get_individual_link_details( $link_id );
438 $graph_data = array(
439 'total_count' => array(),
440 'unique_count' => array(),
441 );
442 $graph_data = apply_filters( 'betterlinkspro/get_individual_graph_data', $graph_data, $link_id, $from, $to );
443
444 return new \WP_REST_Response(
445 array(
446 'success' => true,
447 'data' => array(
448 'analytics' => $results,
449 'graph_data' => $graph_data,
450 'link_details' => $link_details,
451 ),
452 'id' => $link_id,
453 ),
454 200
455 );
456 }
457
458 /**
459 * Check if a given request has access to update a setting
460 *
461 * @param WP_REST_Request $request Full data about the request.
462 * @return WP_Error|bool
463 */
464 public function get_items_permissions_check( $request ) {
465 return apply_filters( 'betterlinks/api/analytics_items_permissions_check', current_user_can( 'manage_options' ) );
466 }
467 /**
468 * Delete analytics for multiple links
469 *
470 * @param WP_REST_Request $request Full data about the request.
471 * @return WP_Error|WP_REST_Response
472 */
473 public function delete_links_analytics( $request ) {
474 $params = $request->get_params();
475
476 $link_ids_raw = isset( $params['link_ids'] ) ? $params['link_ids'] : '';
477
478 // Sanitize and parse link IDs
479 $link_ids = array_filter( array_map( 'intval', explode( ',', sanitize_text_field( $link_ids_raw ) ) ) );
480
481 if ( empty( $link_ids ) ) {
482 return new \WP_REST_Response(
483 array(
484 'success' => false,
485 'message' => __( 'Invalid link IDs provided.', 'betterlinks' ),
486 ),
487 400
488 );
489 }
490
491 // Get date range from params
492 $from = isset( $params['from'] ) ? sanitize_text_field( $params['from'] ) : gmdate( 'Y-m-d', strtotime( ' - 30 days' ) );
493 $to = isset( $params['to'] ) ? sanitize_text_field( $params['to'] ) : gmdate( 'Y-m-d' );
494
495 // Delete clicks for the specified links ONLY within the date range
496 global $wpdb;
497 $table_name = $wpdb->prefix . 'betterlinks_clicks';
498
499 // $table_name is {$wpdb->prefix}betterlinks_clicks (wpdb-controlled); values are placeholdered.
500 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
501 foreach ( $link_ids as $link_id ) {
502 $wpdb->query(
503 $wpdb->prepare(
504 "DELETE FROM {$table_name} WHERE link_id = %d AND DATE(created_at) >= %s AND DATE(created_at) <= %s",
505 intval( $link_id ),
506 $from,
507 $to
508 )
509 );
510 }
511 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
512
513 // Clear all related transient caches for all deleted links
514 foreach ( $link_ids as $link_id ) {
515 $transient_key = 'btl_individual_analytics_clicks_' . md5( $from . $to . $link_id );
516 delete_transient( $transient_key );
517
518 $graph_transient_key = 'btl_individual_graph_data_' . md5( $from . $to . $link_id );
519 delete_transient( $graph_transient_key );
520 }
521
522 // Clear all analytics caches
523 \BetterLinks\Helper::clear_analytics_cache();
524
525 // Update the betterlinks_analytics_data option with fresh data from database
526 \BetterLinks\Helper::update_links_analytics();
527
528 // Clear links cache so that page refresh shows updated analytics
529 delete_transient( BETTERLINKS_CACHE_LINKS_NAME );
530
531 // Fetch updated analytics data for the link list
532 $unique_list = $this->get_analytics_unique_list( $from, $to );
533 $unique_click_count = $this->get_unique_clicks_count( $from, $to );
534 $analytic = $this->get_analytics_data( $from, $to );
535 $analytic = $analytic ? json_decode( $analytic, true ) : array();
536
537 return new \WP_REST_Response(
538 array(
539 'success' => true,
540 'data' => array(
541 'unique_list' => $unique_list,
542 'unique_count' => $unique_click_count,
543 'analytic' => $analytic,
544 ),
545 ),
546 200
547 );
548 }
549
550 /**
551 * Check if a given request has access to update a setting
552 *
553 * @param WP_REST_Request $request Full data about the request.
554 * @return WP_Error|bool
555 */
556 public function permissions_check( $request ) {
557 return current_user_can( 'manage_options' );
558 }
559 }
560