PluginProbe
Redirection / 2.8.1
Redirection v2.8.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 2.8.1, at models/redirect.php

522 lines 14.7 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 return false;
136 }
137
138 public static function disable_where_matches( $url ) {
139 global $wpdb;
140
141 $wpdb->update( $wpdb->prefix.'redirection_items', array( 'status' => 'disabled' ), array( 'url' => $url ) );
142 }
143
144 public function delete() {
145 global $wpdb;
146
147 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}redirection_items WHERE id=%d", $this->id ) );
148
149 Red_Module::flush( $this->group_id );
150 }
151
152 static function create( array $details ) {
153 global $wpdb;
154
155 $sanitizer = new Red_Item_Sanitize();
156 $data = $sanitizer->get( $details );
157 if ( is_wp_error( $data ) ) {
158 return $data;
159 }
160
161 $data['status'] = isset( $details['status'] ) && $details['status'] === 'disabled' ? 'disabled' : 'enabled';
162 $data['position'] = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_items WHERE group_id=%d", $data['group_id'] ) );
163 $data = apply_filters( 'redirection_create_redirect', $data );
164
165 // Create
166 if ( $wpdb->insert( $wpdb->prefix.'redirection_items', $data ) !== false ) {
167 Red_Module::flush( $data['group_id'] );
168 return self::get_by_id( $wpdb->insert_id );
169 }
170
171 return new WP_Error( 'redirect', __( 'Unable to add new redirect' ) );
172 }
173
174 public function update( $details ) {
175 global $wpdb;
176
177 $sanitizer = new Red_Item_Sanitize();
178 $data = $sanitizer->get( $details );
179
180 if ( is_wp_error( $data ) ) {
181 return $data;
182 }
183
184 $old_group = false;
185 if ( $data['group_id'] !== $this->group_id ) {
186 $old_group = $this->group_id;
187 }
188
189 // Save this
190 $wpdb->update( $wpdb->prefix.'redirection_items', $data, array( 'id' => $this->id ) );
191
192 $this->load_from_data( (object) $data );
193
194 Red_Module::flush( $this->group_id );
195
196 if ( $old_group !== $this->group_id ) {
197 Red_Module::flush( $old_group );
198 }
199
200 return true;
201 }
202
203 public function matches( $url ) {
204 if ( ! $this->is_enabled() ) {
205 return false;
206 }
207
208 $this->url = str_replace( ' ', '%20', $this->url );
209 $matches = false;
210
211 // Check if we match the URL
212 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) ) {
213 // Check if our match wants this URL
214 $target = $this->match->get_target( $url, $this->url, $this->regex );
215 $target = apply_filters( 'redirection_url_target', $target, $this->url );
216 $target = $this->action->process_before( $this->action_code, $target );
217
218 if ( $target ) {
219 do_action( 'redirection_visit', $this, $url, $target );
220 return $this->action->process_after( $this->action_code, $target );
221 }
222
223 return true;
224 }
225
226 return false;
227 }
228
229 public function visit( $url, $target ) {
230 global $wpdb;
231
232 $options = red_get_options();
233
234 // Update the counters
235 $this->last_count++;
236 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}redirection_items SET last_count=last_count+1, last_access=NOW() WHERE id=%d", $this->id ) );
237
238 if ( isset( $options['expire_redirect'] ) && $options['expire_redirect'] !== -1 && $target ) {
239 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 ) );
240 }
241 }
242
243 public function is_enabled() {
244 return $this->status === 'enabled';
245 }
246
247 public function reset() {
248 global $wpdb;
249
250 $this->last_count = 0;
251 $this->last_access = '0000-00-00 00:00:00';
252
253 $wpdb->update( $wpdb->prefix.'redirection_items', array( 'last_count' => 0, 'last_access' => $this->last_access ), array( 'id' => $this->id ) );
254 }
255
256 public function enable() {
257 global $wpdb;
258
259 $this->status = 'enabled';
260 $wpdb->update( $wpdb->prefix.'redirection_items', array( 'status' => $this->status ), array( 'id' => $this->id ) );
261 }
262
263 public function disable() {
264 global $wpdb;
265
266 $this->status = 'disabled';
267 $wpdb->update( $wpdb->prefix.'redirection_items', array( 'status' => $this->status ), array( 'id' => $this->id ) );
268 }
269
270 public function get_id() {
271 return intval( $this->id, 10 );
272 }
273
274 public function get_position() {
275 return intval( $this->position, 10 );
276 }
277
278 public function get_group_id() {
279 return intval( $this->group_id, 10 );
280 }
281
282 public function get_url() {
283 return $this->url;
284 }
285
286 public function get_title() {
287 return $this->title ? $this->title : '';
288 }
289
290 public function get_hits() {
291 return intval( $this->last_count, 10 );
292 }
293
294 public function get_last_hit() {
295 return intval( $this->last_access, 10 );
296 }
297
298 public function is_regex() {
299 return $this->regex ? true : false;
300 }
301
302 public function get_match_type() {
303 return $this->match_type;
304 }
305
306 public function get_action_type() {
307 return $this->action_type;
308 }
309
310 public function get_action_code() {
311 return intval( $this->action_code, 10 );
312 }
313
314 public function get_action_data() {
315 return $this->action_data ? $this->action_data : '';
316 }
317
318 public static function get_filtered( array $params ) {
319 global $wpdb;
320
321 $orderby = 'id';
322 $direction = 'DESC';
323 $limit = RED_DEFAULT_PER_PAGE;
324 $offset = 0;
325 $where = '';
326
327 if ( isset( $params['orderBy'] ) && in_array( $params['orderBy'], array( 'url', 'last_count', 'last_access', 'position' ), true ) ) {
328 $orderby = $params['orderBy'];
329 }
330
331 if ( isset( $params['direction'] ) && in_array( $params['direction'], array( 'asc', 'desc' ), true ) ) {
332 $direction = strtoupper( $params['direction'] );
333 }
334
335 if ( isset( $params['filter'] ) && strlen( $params['filter'] ) > 0 && $params['filter'] !== '0' ) {
336 if ( isset( $params['filterBy'] ) && $params['filterBy'] === 'group' ) {
337 $where = $wpdb->prepare( "WHERE group_id=%d", intval( $params['filter'], 10 ) );
338 } else {
339 $where = $wpdb->prepare( 'WHERE url LIKE %s', '%'.$wpdb->esc_like( trim( $params['filter'] ) ).'%' );
340 }
341 }
342
343 if ( isset( $params['perPage'] ) ) {
344 $limit = intval( $params['perPage'], 10 );
345 $limit = min( RED_MAX_PER_PAGE, $limit );
346 $limit = max( 5, $limit );
347 }
348
349 if ( isset( $params['page'] ) ) {
350 $offset = intval( $params['page'], 10 );
351 $offset = max( 0, $offset );
352 $offset *= $limit;
353 }
354
355 $table = $wpdb->prefix.'redirection_items';
356 $sql = trim( "SELECT * FROM {$table} $where " ).$wpdb->prepare( " ORDER BY $orderby $direction LIMIT %d,%d", $offset, $limit );
357
358 $rows = $wpdb->get_results( $sql );
359 $total_items = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$table} ".$where ) );
360 $items = array();
361
362 foreach ( $rows as $row ) {
363 $group = new Red_Item( $row );
364 $items[] = $group->to_json();
365 }
366
367 return array(
368 'items' => $items,
369 'total' => intval( $total_items, 10 ),
370 );
371 }
372
373 public function to_json() {
374 return array(
375 'id' => $this->get_id(),
376 'url' => $this->get_url(),
377 'action_code' => $this->get_action_code(),
378 'action_type' => $this->get_action_type(),
379 'action_data' => $this->match->get_data(),
380 'match_type' => $this->get_match_type(),
381 'title' => $this->get_title(),
382 'hits' => $this->get_hits(),
383 'regex' => $this->is_regex(),
384 'group_id' => $this->get_group_id(),
385 'position' => $this->get_position(),
386 'last_access' => $this->get_last_hit() > 0 ? date_i18n( get_option( 'date_format' ), $this->get_last_hit() ) : '-',
387 'enabled' => $this->is_enabled(),
388 );
389 }
390 }
391
392 class Red_Item_Sanitize {
393 public function get( array $details ) {
394 $data = array();
395
396 $details = array_map( 'trim', $details );
397 $details = array_map( 'stripslashes', $details );
398
399 $data['regex'] = isset( $details['regex'] ) && ( $details['regex'] === 'true' || $details['regex'] === '1' ) ? 1 : 0;
400 $data['title'] = isset( $details['title'] ) ? $details['title'] : null;
401 $data['url'] = $this->get_url( empty( $details['url'] ) ? $this->auto_generate() : $details['url'], $data['regex'] );
402 $data['group_id'] = $this->get_group( isset( $details['group_id'] ) ? $details['group_id'] : 0 );
403 $data['position'] = $this->get_position( $details );
404
405 if ( $data['title'] ) {
406 $data['title'] = substr( $data['title'], 0, 50 );
407 }
408
409 $matcher = Red_Match::create( isset( $details['match_type'] ) ? $details['match_type'] : false );
410 if ( ! $matcher ) {
411 return new WP_Error( 'redirect', __( 'Invalid redirect matcher', 'redirection' ) );
412 }
413
414 $action_code = isset( $details['action_code'] ) ? intval( $details['action_code'], 10 ) : 0;
415 $action = Red_Action::create( isset( $details['action_type'] ) ? $details['action_type'] : false, $action_code );
416 if ( ! $action ) {
417 return new WP_Error( 'redirect', __( 'Invalid redirect action', 'redirection' ) );
418 }
419
420 $data['action_type'] = $details['action_type'];
421 $data['action_code'] = $this->get_code( $details['action_type'], $action_code );
422 $data['match_type'] = $details['match_type'];
423
424 $match_data = $matcher->save( $details, ! $this->is_url_type( $data['action_type'] ) );
425
426 $data['action_data'] = is_array( $match_data ) ? serialize( $match_data ) : $match_data;
427
428 // Any errors?
429 foreach ( $data as $value ) {
430 if ( is_wp_error( $value ) ) {
431 return $value;
432 }
433 }
434
435 return apply_filters( 'redirection_validate_redirect', $data );
436 }
437
438 protected function get_position( $details ) {
439 if ( isset( $details['position'] ) ) {
440 return max( 0, intval( $details['position'], 10 ) );
441 }
442
443 return 0;
444 }
445
446 protected function is_url_type( $type ) {
447 if ( $type === 'url' || $type === 'pass' ) {
448 return true;
449 }
450
451 return false;
452 }
453
454 protected function get_code( $action_type, $code ) {
455 if ( $action_type === 'url' || $action_type === 'random' ) {
456 if ( in_array( $code, array( 301, 302, 307, 308 ), true ) ) {
457 return $code;
458 }
459 }
460
461 if ( $action_type === 'error' ) {
462 if ( in_array( $code, array( 401, 404, 410 ), true ) ) {
463 return $code;
464 }
465 }
466
467 return 0;
468 }
469
470 protected function get_group( $group_id ) {
471 $group_id = intval( $group_id, 10 );
472
473 if ( ! Red_Group::get( $group_id ) ) {
474 return new WP_Error( 'redirect', __( 'Invalid group when creating redirect', 'redirection' ) );
475 }
476
477 return $group_id;
478 }
479
480 protected function get_url( $url, $regex ) {
481 $url = self::sanitize_url( $url, $regex );
482
483 if ( $url === '' ) {
484 return new WP_Error( 'redirect', __( 'Invalid source URL', 'redirection' ) );
485 }
486
487 return $url;
488 }
489
490 protected function auto_generate() {
491 $options = red_get_options();
492 $url = '';
493
494 if ( isset( $options['auto_target'] ) && $options['auto_target'] ) {
495 $id = time();
496 $url = str_replace( '$dec$', $id, $options['auto_target'] );
497 $url = str_replace( '$hex$', sprintf( '%x', $id ), $url );
498 }
499
500 return $url;
501 }
502
503 public function sanitize_url( $url, $regex = false ) {
504 // Make sure that the old URL is relative
505 $url = preg_replace( '@^https?://(.*?)/@', '/', $url );
506 $url = preg_replace( '@^https?://(.*?)$@', '/', $url );
507
508 // No new lines
509 $url = preg_replace( "/[\r\n\t].*?$/s", '', $url );
510
511 // Clean control codes
512 $url = preg_replace( '/[^\PC\s]/u', '', $url );
513
514 // Ensure a slash at start
515 if ( substr( $url, 0, 1 ) !== '/' && $regex === false ) {
516 $url = '/'.$url;
517 }
518
519 return $url;
520 }
521 }
522