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

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