PluginProbe
Redirection / 3.3.1
Redirection v3.3.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 3.3.1, at models/redirect.php

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