PluginProbe
Redirection / 4.3.2
Redirection v4.3.2
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.3.2, at models/redirect.php

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