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

536 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 $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 is_match( $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 if ( $target === true ) {
357 $target = $this->action_type === 'pass' ? $this->match->get_data()['url'] : '';
358 }
359
360 RE_Log::create( $url, $target, Redirection_Request::get_user_agent(), Redirection_Request::get_ip(), Redirection_Request::get_referrer(), $details );
361 }
362 }
363
364 public function is_enabled() {
365 return $this->status === 'enabled';
366 }
367
368 public function reset() {
369 global $wpdb;
370
371 $this->last_count = 0;
372 $this->last_access = '0000-00-00 00:00:00';
373
374 $update = array(
375 'last_count' => 0,
376 'last_access' => $this->last_access,
377 );
378 $where = array(
379 'id' => $this->id,
380 );
381
382 $wpdb->update( $wpdb->prefix . 'redirection_items', $update, $where );
383 }
384
385 public function enable() {
386 global $wpdb;
387
388 $this->status = 'enabled';
389 $wpdb->update( $wpdb->prefix . 'redirection_items', array( 'status' => $this->status ), array( 'id' => $this->id ) );
390 }
391
392 public function disable() {
393 global $wpdb;
394
395 $this->status = 'disabled';
396 $wpdb->update( $wpdb->prefix . 'redirection_items', array( 'status' => $this->status ), array( 'id' => $this->id ) );
397 }
398
399 public function get_id() {
400 return intval( $this->id, 10 );
401 }
402
403 public function get_position() {
404 return intval( $this->position, 10 );
405 }
406
407 public function get_group_id() {
408 return intval( $this->group_id, 10 );
409 }
410
411 public function get_url() {
412 return $this->url;
413 }
414
415 public function get_match_url() {
416 return $this->match_url;
417 }
418
419 public function get_match_data() {
420 $source = $this->source_flags->get_json_with_defaults();
421
422 if ( ! empty( $source ) ) {
423 return [ 'source' => $source ];
424 }
425
426 return null;
427 }
428
429 public function get_title() {
430 return $this->title ? $this->title : '';
431 }
432
433 public function get_hits() {
434 return intval( $this->last_count, 10 );
435 }
436
437 public function get_last_hit() {
438 return intval( $this->last_access, 10 );
439 }
440
441 public function is_regex() {
442 return $this->regex ? true : false;
443 }
444
445 public function get_match_type() {
446 return $this->match_type;
447 }
448
449 public function get_action_type() {
450 return $this->action_type;
451 }
452
453 public function get_action_code() {
454 return intval( $this->action_code, 10 );
455 }
456
457 public function get_action_data() {
458 return $this->action_data ? $this->action_data : '';
459 }
460
461 public static function get_filtered( array $params ) {
462 global $wpdb;
463
464 $orderby = 'id';
465 $direction = 'DESC';
466 $limit = RED_DEFAULT_PER_PAGE;
467 $offset = 0;
468 $where = '';
469
470 if ( isset( $params['orderby'] ) && in_array( $params['orderby'], array( 'url', 'last_count', 'last_access', 'position' ), true ) ) {
471 $orderby = $params['orderby'];
472 }
473
474 if ( isset( $params['direction'] ) && in_array( $params['direction'], array( 'asc', 'desc' ), true ) ) {
475 $direction = strtoupper( $params['direction'] );
476 }
477
478 if ( isset( $params['filter'] ) && strlen( $params['filter'] ) > 0 && $params['filter'] !== '0' ) {
479 if ( isset( $params['filterBy'] ) && $params['filterBy'] === 'group' ) {
480 $where = $wpdb->prepare( 'WHERE group_id=%d', intval( $params['filter'], 10 ) );
481 } else {
482 $where = $wpdb->prepare( 'WHERE url LIKE %s', '%' . $wpdb->esc_like( trim( $params['filter'] ) ) . '%' );
483 }
484 }
485
486 if ( isset( $params['per_page'] ) ) {
487 $limit = intval( $params['per_page'], 10 );
488 $limit = min( RED_MAX_PER_PAGE, $limit );
489 $limit = max( 5, $limit );
490 }
491
492 if ( isset( $params['page'] ) ) {
493 $offset = intval( $params['page'], 10 );
494 $offset = max( 0, $offset );
495 $offset *= $limit;
496 }
497
498 // $orderby and $direction is whitelisted
499 $rows = $wpdb->get_results(
500 "SELECT * FROM {$wpdb->prefix}redirection_items $where ORDER BY $orderby $direction " . $wpdb->prepare( 'LIMIT %d,%d', $offset, $limit )
501 );
502 $total_items = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_items " . $where ) );
503 $items = array();
504
505 foreach ( $rows as $row ) {
506 $group = new Red_Item( $row );
507 $items[] = $group->to_json();
508 }
509
510 return array(
511 'items' => $items,
512 'total' => intval( $total_items, 10 ),
513 );
514 }
515
516 public function to_json() {
517 return array(
518 'id' => $this->get_id(),
519 'url' => $this->get_url(),
520 'match_url' => $this->get_match_url(),
521 'match_data' => $this->get_match_data(),
522 'action_code' => $this->get_action_code(),
523 'action_type' => $this->get_action_type(),
524 'action_data' => $this->match->get_data(),
525 'match_type' => $this->get_match_type(),
526 'title' => $this->get_title(),
527 'hits' => $this->get_hits(),
528 'regex' => $this->is_regex(),
529 'group_id' => $this->get_group_id(),
530 'position' => $this->get_position(),
531 'last_access' => $this->get_last_hit() > 0 ? date_i18n( get_option( 'date_format' ), $this->get_last_hit() ) : '-',
532 'enabled' => $this->is_enabled(),
533 );
534 }
535 }
536