PluginProbe
Redirection / 4.2.1
Redirection v4.2.1
5.10.0 5.9.0 5.8.1 5.8.0 3.7.2 3.7.3 4.0 4.0.1 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6.2 4.7.1 All 130 releases
redirection / models / redirect.php

redirect.php in Redirection 4.2.1, at models/redirect.php

532 lines 14.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 include_once dirname( __FILE__ ) . '/url.php';
4 include_once dirname( __FILE__ ) . '/regex.php';
5 include_once dirname( __FILE__ ) . '/redirect-sanitizer.php';
6
7 class Red_Item {
8 private $id = null;
9 private $url = null;
10 private $match_url = null;
11 private $match_data = null;
12 private $regex = false;
13 private $action_data = null;
14 private $action_code = 0;
15 private $action_type;
16 private $match_type;
17 private $title;
18 private $last_access = null;
19 private $last_count = 0;
20 private $status = 'enabled';
21 private $position;
22 private $group_id;
23
24 public $source_flags = false;
25
26 function __construct( $values = null ) {
27 if ( is_object( $values ) ) {
28 $this->load_from_data( $values );
29 }
30 }
31
32 private function load_from_data( stdClass $values ) {
33 foreach ( $values as $key => $value ) {
34 if ( property_exists( $this, $key ) ) {
35 $this->$key = $value;
36 }
37 }
38
39 $this->regex = (bool) $this->regex;
40 $this->last_access = $this->last_access === '0000-00-00 00:00:00' ? 0 : mysql2date( 'U', $this->last_access );
41
42 $this->load_matcher();
43 $this->load_action();
44 $this->load_source_flags();
45 }
46
47 // v4 JSON
48 private function load_source_flags() {
49 // Default regex flag to regex column. This will be removed once the regex column has been migrated
50 // todo: deprecate
51 $this->source_flags = new Red_Source_Flags( array_merge( red_get_options(), [ 'flag_regex' => $this->regex ] ) );
52
53 if ( isset( $this->match_data ) ) {
54 $json = json_decode( $this->match_data, true );
55
56 if ( $json && isset( $json['source'] ) ) {
57 // Merge redirect flags with default flags
58 $this->source_flags->set_flags( array_merge( red_get_options(), $json['source'] ) );
59 }
60 }
61 }
62
63 private function load_matcher() {
64 if ( empty( $this->match_type ) ) {
65 $this->match_type = 'url';
66 }
67
68 $this->match = Red_Match::create( $this->match_type, $this->action_data );
69 }
70
71 private function load_action() {
72 if ( empty( $this->action_type ) ) {
73 $this->action_type = 'nothing';
74 }
75
76 $this->action = Red_Action::create( $this->action_type, $this->action_code );
77 $this->match->action = $this->action;
78 }
79
80 static function get_all_for_module( $module ) {
81 global $wpdb;
82
83 $rows = $wpdb->get_results(
84 $wpdb->prepare(
85 "SELECT {$wpdb->prefix}redirection_items.* FROM {$wpdb->prefix}redirection_items
86 INNER JOIN {$wpdb->prefix}redirection_groups ON {$wpdb->prefix}redirection_groups.id={$wpdb->prefix}redirection_items.group_id
87 AND {$wpdb->prefix}redirection_groups.status='enabled' AND {$wpdb->prefix}redirection_groups.module_id=%d
88 WHERE {$wpdb->prefix}redirection_items.status='enabled'
89 ORDER BY {$wpdb->prefix}redirection_groups.position,{$wpdb->prefix}redirection_items.position",
90 $module
91 )
92 );
93 $items = array();
94
95 foreach ( (array) $rows as $row ) {
96 $items[] = new Red_Item( $row );
97 }
98
99 return $items;
100 }
101
102 static function get_for_url( $url ) {
103 $status = new Red_Database_Status();
104
105 // deprecate
106 if ( $status->does_support( '4.0' ) ) {
107 return self::get_for_matched_url( $url );
108 }
109
110 return self::get_old_url( $url );
111 }
112
113 static function get_for_matched_url( $url ) {
114 global $wpdb;
115
116 $url = new Red_Url_Match( $url );
117 $rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}redirection_items WHERE match_url=%s OR match_url='regex'", $url->get_url() ) );
118
119 $items = array();
120 if ( count( $rows ) > 0 ) {
121 foreach ( $rows as $row ) {
122 $items[] = new Red_Item( $row );
123 }
124 }
125
126 usort( $items, array( 'Red_Item', 'sort_urls' ) );
127
128 return $items;
129 }
130
131 // deprecate
132 public static function get_old_url( $url ) {
133 global $wpdb;
134
135 $rows = $wpdb->get_results(
136 $wpdb->prepare(
137 "SELECT {$wpdb->prefix}redirection_items.*,{$wpdb->prefix}redirection_groups.position AS group_pos
138 FROM {$wpdb->prefix}redirection_items INNER JOIN {$wpdb->prefix}redirection_groups ON
139 {$wpdb->prefix}redirection_groups.id={$wpdb->prefix}redirection_items.group_id AND {$wpdb->prefix}redirection_groups.status='enabled'
140 AND {$wpdb->prefix}redirection_groups.module_id=%d WHERE ({$wpdb->prefix}redirection_items.regex=1
141 OR {$wpdb->prefix}redirection_items.url=%s)",
142 WordPress_Module::MODULE_ID,
143 $url
144 )
145 );
146
147 $items = array();
148 if ( count( $rows ) > 0 ) {
149 foreach ( $rows as $row ) {
150 $items[] = array(
151 'position' => ( $row->group_pos * 1000 ) + $row->position,
152 'item' => new Red_Item( $row ),
153 );
154 }
155 }
156
157 usort( $items, array( 'Red_Item', 'sort_urls_old' ) );
158 $items = array_map( array( 'Red_Item', 'reduce_sorted_items' ), $items );
159
160 // Sort it in PHP
161 ksort( $items );
162 $items = array_values( $items );
163 return $items;
164 }
165
166 static public function reduce_sorted_items( $item ) {
167 return $item['item'];
168 }
169
170 static public function get_all() {
171 global $wpdb;
172
173 $rows = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}redirection_items" );
174 $items = array();
175
176 foreach ( (array) $rows as $row ) {
177 $items[] = new Red_Item( $row );
178 }
179
180 return $items;
181 }
182
183 public static function sort_urls( $first, $second ) {
184 if ( $first->position === $second->position ) {
185 return 0;
186 }
187
188 return ( $first->position < $second->position ) ? -1 : 1;
189 }
190
191 public static function sort_urls_old( $first, $second ) {
192 if ( $first['position'] === $second['position'] ) {
193 return 0;
194 }
195
196 return ( $first['position'] < $second['position'] ) ? -1 : 1;
197 }
198
199 static function get_by_id( $id ) {
200 global $wpdb;
201
202 $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}redirection_items WHERE id=%d", $id ) );
203 if ( $row ) {
204 return new Red_Item( $row );
205 }
206
207 return false;
208 }
209
210 public static function disable_where_matches( $url ) {
211 global $wpdb;
212
213 $wpdb->update( $wpdb->prefix . 'redirection_items', array( 'status' => 'disabled' ), array( 'url' => $url ) );
214 }
215
216 public function delete() {
217 global $wpdb;
218
219 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}redirection_items WHERE id=%d", $this->id ) );
220 do_action( 'redirection_redirect_deleted', $this );
221
222 Red_Module::flush( $this->group_id );
223 }
224
225 static function create( array $details ) {
226 global $wpdb;
227
228 $sanitizer = new Red_Item_Sanitize();
229 $data = $sanitizer->get( $details );
230 if ( is_wp_error( $data ) ) {
231 return $data;
232 }
233
234 $data['status'] = 'enabled';
235
236 // todo: fix this mess
237 if ( ( isset( $details['enabled'] ) && ( $details['enabled'] === 'disabled' || $details['enabled'] === false ) ) || ( isset( $details['status'] ) && $details['status'] === 'disabled' ) ) {
238 $data['status'] = 'disabled';
239 }
240
241 if ( ! isset( $details['position'] ) || $details['position'] === 0 ) {
242 $data['position'] = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_items WHERE group_id=%d", $data['group_id'] ) );
243 }
244
245 $data = apply_filters( 'redirection_create_redirect', $data );
246
247 if ( ! empty( $data['match_data'] ) ) {
248 $data['match_data'] = json_encode( $data['match_data'] );
249 }
250
251 // Create
252 if ( $wpdb->insert( $wpdb->prefix . 'redirection_items', $data ) !== false ) {
253 Red_Module::flush( $data['group_id'] );
254
255 $redirect = self::get_by_id( $wpdb->insert_id );
256 do_action( 'redirection_redirect_updated', $wpdb->insert_id, $redirect );
257
258 return $redirect;
259 }
260
261 return new WP_Error( 'redirect', __( 'Unable to add new redirect' ) );
262 }
263
264 public function update( $details ) {
265 global $wpdb;
266
267 $sanitizer = new Red_Item_Sanitize();
268 $data = $sanitizer->get( $details );
269
270 if ( is_wp_error( $data ) ) {
271 return $data;
272 }
273
274 $old_group = false;
275 if ( $data['group_id'] !== $this->group_id ) {
276 $old_group = $this->group_id;
277 }
278
279 // Save this
280 $data = apply_filters( 'redirection_update_redirect', $data );
281 if ( ! empty( $data['match_data'] ) ) {
282 $data['match_data'] = json_encode( $data['match_data'] );
283 }
284
285 $result = $wpdb->update( $wpdb->prefix . 'redirection_items', $data, array( 'id' => $this->id ) );
286 if ( $result !== false ) {
287 do_action( 'redirection_redirect_updated', $this, self::get_by_id( $this->id ) );
288 $this->load_from_data( (object) $data );
289
290 Red_Module::flush( $this->group_id );
291
292 if ( $old_group !== $this->group_id ) {
293 Red_Module::flush( $old_group );
294 }
295
296 return true;
297 }
298
299 return new WP_Error( 'redirect', __( 'Unable to update redirect' ) );
300 }
301
302 /**
303 * Determine if a requested URL matches this URL
304 *
305 * @param string $requested_url
306 * @return bool true if matched, false otherwise
307 */
308 public function matches( $requested_url ) {
309 if ( ! $this->is_enabled() ) {
310 return false;
311 }
312
313 $url = new Red_Url( $this->url );
314 if ( $url->is_match( $requested_url, $this->source_flags ) ) {
315 // URL is matched, now match the redirect type (i.e. login status, IP address)
316 $target = $this->match->is_match( $requested_url );
317
318 // Check if our action wants a URL
319 if ( $this->action->needs_target() ) {
320 // Our action requires a target URL - get this, using our type match result
321 $target = $this->match->get_target_url( $requested_url, $url->get_url(), $this->source_flags, $target );
322 $target = Red_Url_Query::add_to_target( $target, $requested_url, $this->source_flags );
323 $target = apply_filters( 'redirection_url_target', $target, $this->url );
324 }
325
326 // Fire any early actions
327 if ( $target ) {
328 $target = $this->action->process_before( $this->action_code, $target );
329 }
330
331 if ( $target ) {
332 // We still have a target, so log it and carry on with the action
333 do_action( 'redirection_visit', $this, $requested_url, $target );
334 return $this->action->process_after( $this->action_code, $target );
335 }
336 }
337
338 return false;
339 }
340
341 public function visit( $url, $target ) {
342 global $wpdb;
343
344 $options = red_get_options();
345
346 // Update the counters
347 $this->last_count++;
348 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}redirection_items SET last_count=last_count+1, last_access=NOW() WHERE id=%d", $this->id ) );
349
350 if ( isset( $options['expire_redirect'] ) && $options['expire_redirect'] !== -1 && $target ) {
351 $details = array(
352 'redirect_id' => $this->id,
353 'group_id' => $this->group_id,
354 );
355
356 RE_Log::create( $url, $target, Redirection_Request::get_user_agent(), Redirection_Request::get_ip(), Redirection_Request::get_referrer(), $details );
357 }
358 }
359
360 public function is_enabled() {
361 return $this->status === 'enabled';
362 }
363
364 public function reset() {
365 global $wpdb;
366
367 $this->last_count = 0;
368 $this->last_access = '0000-00-00 00:00:00';
369
370 $update = array(
371 'last_count' => 0,
372 'last_access' => $this->last_access,
373 );
374 $where = array(
375 'id' => $this->id,
376 );
377
378 $wpdb->update( $wpdb->prefix . 'redirection_items', $update, $where );
379 }
380
381 public function enable() {
382 global $wpdb;
383
384 $this->status = 'enabled';
385 $wpdb->update( $wpdb->prefix . 'redirection_items', array( 'status' => $this->status ), array( 'id' => $this->id ) );
386 }
387
388 public function disable() {
389 global $wpdb;
390
391 $this->status = 'disabled';
392 $wpdb->update( $wpdb->prefix . 'redirection_items', array( 'status' => $this->status ), array( 'id' => $this->id ) );
393 }
394
395 public function get_id() {
396 return intval( $this->id, 10 );
397 }
398
399 public function get_position() {
400 return intval( $this->position, 10 );
401 }
402
403 public function get_group_id() {
404 return intval( $this->group_id, 10 );
405 }
406
407 public function get_url() {
408 return $this->url;
409 }
410
411 public function get_match_url() {
412 return $this->match_url;
413 }
414
415 public function get_match_data() {
416 $source = $this->source_flags->get_json_with_defaults();
417
418 if ( ! empty( $source ) ) {
419 return [ 'source' => $source ];
420 }
421
422 return null;
423 }
424
425 public function get_title() {
426 return $this->title ? $this->title : '';
427 }
428
429 public function get_hits() {
430 return intval( $this->last_count, 10 );
431 }
432
433 public function get_last_hit() {
434 return intval( $this->last_access, 10 );
435 }
436
437 public function is_regex() {
438 return $this->regex ? true : false;
439 }
440
441 public function get_match_type() {
442 return $this->match_type;
443 }
444
445 public function get_action_type() {
446 return $this->action_type;
447 }
448
449 public function get_action_code() {
450 return intval( $this->action_code, 10 );
451 }
452
453 public function get_action_data() {
454 return $this->action_data ? $this->action_data : '';
455 }
456
457 public static function get_filtered( array $params ) {
458 global $wpdb;
459
460 $orderby = 'id';
461 $direction = 'DESC';
462 $limit = RED_DEFAULT_PER_PAGE;
463 $offset = 0;
464 $where = '';
465
466 if ( isset( $params['orderby'] ) && in_array( $params['orderby'], array( 'url', 'last_count', 'last_access', 'position' ), true ) ) {
467 $orderby = $params['orderby'];
468 }
469
470 if ( isset( $params['direction'] ) && in_array( $params['direction'], array( 'asc', 'desc' ), true ) ) {
471 $direction = strtoupper( $params['direction'] );
472 }
473
474 if ( isset( $params['filter'] ) && strlen( $params['filter'] ) > 0 && $params['filter'] !== '0' ) {
475 if ( isset( $params['filterBy'] ) && $params['filterBy'] === 'group' ) {
476 $where = $wpdb->prepare( 'WHERE group_id=%d', intval( $params['filter'], 10 ) );
477 } else {
478 $where = $wpdb->prepare( 'WHERE url LIKE %s', '%' . $wpdb->esc_like( trim( $params['filter'] ) ) . '%' );
479 }
480 }
481
482 if ( isset( $params['per_page'] ) ) {
483 $limit = intval( $params['per_page'], 10 );
484 $limit = min( RED_MAX_PER_PAGE, $limit );
485 $limit = max( 5, $limit );
486 }
487
488 if ( isset( $params['page'] ) ) {
489 $offset = intval( $params['page'], 10 );
490 $offset = max( 0, $offset );
491 $offset *= $limit;
492 }
493
494 // $orderby and $direction is whitelisted
495 $rows = $wpdb->get_results(
496 "SELECT * FROM {$wpdb->prefix}redirection_items $where ORDER BY $orderby $direction " . $wpdb->prepare( 'LIMIT %d,%d', $offset, $limit )
497 );
498 $total_items = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_items " . $where ) );
499 $items = array();
500
501 foreach ( $rows as $row ) {
502 $group = new Red_Item( $row );
503 $items[] = $group->to_json();
504 }
505
506 return array(
507 'items' => $items,
508 'total' => intval( $total_items, 10 ),
509 );
510 }
511
512 public function to_json() {
513 return array(
514 'id' => $this->get_id(),
515 'url' => $this->get_url(),
516 'match_url' => $this->get_match_url(),
517 'match_data' => $this->get_match_data(),
518 'action_code' => $this->get_action_code(),
519 'action_type' => $this->get_action_type(),
520 'action_data' => $this->match->get_data(),
521 'match_type' => $this->get_match_type(),
522 'title' => $this->get_title(),
523 'hits' => $this->get_hits(),
524 'regex' => $this->is_regex(),
525 'group_id' => $this->get_group_id(),
526 'position' => $this->get_position(),
527 'last_access' => $this->get_last_hit() > 0 ? date_i18n( get_option( 'date_format' ), $this->get_last_hit() ) : '-',
528 'enabled' => $this->is_enabled(),
529 );
530 }
531 }
532