courses
Signed-off-by: Jeff <jeff@gotenzing.com>
Showing
192 changed files
with
2925 additions
and
1 deletions
| 1 | |||
| 2 | RewriteEngine on | ||
| 3 | RewriteRule ^product/(.*)$ /courses/$1 [R=302,NC,L] | ||
| 4 | |||
| 1 | # BEGIN WordPress | 5 | # BEGIN WordPress |
| 2 | # The directives (lines) between "BEGIN WordPress" and "END WordPress" are | 6 | # The directives (lines) between "BEGIN WordPress" and "END WordPress" are |
| 3 | # dynamically generated, and should only be modified via WordPress filters. | 7 | # dynamically generated, and should only be modified via WordPress filters. |
| ... | @@ -12,4 +16,5 @@ RewriteCond %{REQUEST_FILENAME} !-d | ... | @@ -12,4 +16,5 @@ RewriteCond %{REQUEST_FILENAME} !-d |
| 12 | RewriteRule . /index.php [L] | 16 | RewriteRule . /index.php [L] |
| 13 | </IfModule> | 17 | </IfModule> |
| 14 | 18 | ||
| 15 | # END WordPress | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 19 | # END WordPress | ||
| 20 | ... | ... |
This diff is collapsed.
Click to expand it.
wp-content/plugins/redirection/README.md
0 → 100644
| 1 | # Redirection | ||
| 2 | |||
| 3 | [](https://travis-ci.org/johngodley/redirection) | ||
| 4 | |||
| 5 | Redirection is a WordPress plugin to manage 301 redirections, keep track of 404 errors, and generally tidy up any loose ends your site may have. This is particularly useful if you are migrating pages from an old website, or are changing the directory of your WordPress installation. | ||
| 6 | |||
| 7 | Note: this is the current 'trunk' version of Redirection. It may be newer than what is in the WordPress.org plugin repository, and should be considered experimental. | ||
| 8 | |||
| 9 | ## Installation | ||
| 10 | Redirection can be installed by visiting the WordPress.org plugin page: | ||
| 11 | |||
| 12 | https://wordpress.org/plugins/redirection/ | ||
| 13 | |||
| 14 | ## Customisation | ||
| 15 | |||
| 16 | ### Request Information | ||
| 17 | |||
| 18 | The following WordPress filters are available for customisation of a server requests: | ||
| 19 | |||
| 20 | - `redirection_request_url` - The request URL | ||
| 21 | - `redirection_request_agent` - The request user agent | ||
| 22 | - `redirection_request_referrer` - The request referrer | ||
| 23 | - `redirection_request_ip` - The request IP address | ||
| 24 | |||
| 25 | ### Logging | ||
| 26 | |||
| 27 | The following WordPress filters are available for customisation of logged data: | ||
| 28 | |||
| 29 | - `redirection_404_data` - Data to be inserted into the 404 table | ||
| 30 | - `redirection_log_data` - Data to be inserted into the redirect log table | ||
| 31 | |||
| 32 | ### Redirect source and target | ||
| 33 | |||
| 34 | - `redirection_url_source` - The original URL used before matching a request. Return false to stop any redirection | ||
| 35 | - `redirection_url_target` - The target URL after a request has been matched (and after any regular expression captures have been replaced). Return false to stop any redirection | ||
| 36 | |||
| 37 | ### Dynamic URL data | ||
| 38 | |||
| 39 | The following special words can be inserted into a target URL: | ||
| 40 | |||
| 41 | - `%userid%` - Insert user's ID | ||
| 42 | - `%userlogin%` - Insert user's login name | ||
| 43 | - `%userurl%` - Insert user's custom URL | ||
| 44 | |||
| 45 | ### Management | ||
| 46 | |||
| 47 | - `redirection_permalink_changed` - return boolean if a post's permalink has changed | ||
| 48 | - `redirection_remove_existing` - fired when a post changes permalink and we need to clear existing redirects that might affect it | ||
| 49 | |||
| 50 | Additionally, if the target URL is a number without any slashes then Redirection will treat it as a post ID and redirect to the full URL for that post. | ||
| 51 | |||
| 52 | ## Support | ||
| 53 | |||
| 54 | Please raise any bug reports or enhancement requests here. Pull requests are always welcome. | ||
| 55 | |||
| 56 | You can find a more detailed description of the plugin on the [Redirection home page](http://urbangiraffe.com/plugins/redirection/) | ||
| 57 | |||
| 58 | Translations can be added here: | ||
| 59 | |||
| 60 | https://translate.wordpress.org/projects/wp-plugins/redirection |
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * Return an error to the client, and trigger the WordPress error page | ||
| 5 | */ | ||
| 6 | class Error_Action extends Red_Action { | ||
| 7 | /** | ||
| 8 | * Set WordPress to show the error page | ||
| 9 | * | ||
| 10 | * @return void | ||
| 11 | */ | ||
| 12 | public function run() { | ||
| 13 | wp_reset_query(); | ||
| 14 | |||
| 15 | // Set the query to be a 404 | ||
| 16 | set_query_var( 'is_404', true ); | ||
| 17 | |||
| 18 | // Return the 404 page | ||
| 19 | add_filter( 'template_include', [ $this, 'template_include' ] ); | ||
| 20 | |||
| 21 | // Clear any posts if this is actually a valid URL | ||
| 22 | add_filter( 'pre_handle_404', [ $this, 'pre_handle_404' ] ); | ||
| 23 | |||
| 24 | // Ensure the appropriate http code is returned | ||
| 25 | add_action( 'wp', [ $this, 'wp' ] ); | ||
| 26 | } | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Output selected HTTP code, as well as redirection header | ||
| 30 | * | ||
| 31 | * @return void | ||
| 32 | */ | ||
| 33 | public function wp() { | ||
| 34 | status_header( $this->code ); | ||
| 35 | nocache_headers(); | ||
| 36 | |||
| 37 | global $wp_version; | ||
| 38 | |||
| 39 | if ( version_compare( $wp_version, '5.1', '<' ) ) { | ||
| 40 | header( 'X-Redirect-Agent: redirection' ); | ||
| 41 | } else { | ||
| 42 | header( 'X-Redirect-By: redirection' ); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | public function pre_handle_404() { | ||
| 47 | global $wp_query; | ||
| 48 | |||
| 49 | // Page comments plugin interferes with this | ||
| 50 | $wp_query->posts = []; | ||
| 51 | return false; | ||
| 52 | } | ||
| 53 | |||
| 54 | public function template_include() { | ||
| 55 | return get_404_template(); | ||
| 56 | } | ||
| 57 | |||
| 58 | public function name() { | ||
| 59 | return __( 'Error (404)', 'redirection' ); | ||
| 60 | } | ||
| 61 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * The 'do nothing' action. This really does nothing, and is used to short-circuit Redirection so that it doesn't trigger other redirects. | ||
| 5 | */ | ||
| 6 | class Nothing_Action extends Red_Action { | ||
| 7 | /** | ||
| 8 | * Issue an action when nothing happens. This stops further processing. | ||
| 9 | * | ||
| 10 | * @return void | ||
| 11 | */ | ||
| 12 | public function run() { | ||
| 13 | do_action( 'redirection_do_nothing', $this->get_target() ); | ||
| 14 | } | ||
| 15 | |||
| 16 | public function name() { | ||
| 17 | return __( 'Do nothing (ignore)', 'redirection' ); | ||
| 18 | } | ||
| 19 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | require_once dirname( __FILE__ ) . '/url.php'; | ||
| 4 | |||
| 5 | /** | ||
| 6 | * A 'pass through' action. Matches a rewrite rather than a redirect, and uses PHP to fetch data from a remote URL. | ||
| 7 | */ | ||
| 8 | class Pass_Action extends Url_Action { | ||
| 9 | /** | ||
| 10 | * Process an external passthrough - a URL that lives external to this server. | ||
| 11 | * | ||
| 12 | * @param String $url Target URL. | ||
| 13 | * @return void | ||
| 14 | */ | ||
| 15 | public function process_external( $url ) { | ||
| 16 | // This is entirely at the user's risk. The $url is set by the user | ||
| 17 | // phpcs:ignore | ||
| 18 | echo wp_remote_fopen( $url ); | ||
| 19 | } | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Process an internal passthrough - a URL that lives on the same server. Here we change the request URI and continue without making a remote request. | ||
| 23 | * | ||
| 24 | * @param String $target Target URL. | ||
| 25 | * @return void | ||
| 26 | */ | ||
| 27 | public function process_internal( $target ) { | ||
| 28 | // Another URL on the server | ||
| 29 | $pos = strpos( $target, '?' ); | ||
| 30 | $_SERVER['REQUEST_URI'] = $target; | ||
| 31 | $_SERVER['PATH_INFO'] = $target; | ||
| 32 | |||
| 33 | if ( $pos ) { | ||
| 34 | $_SERVER['QUERY_STRING'] = substr( $target, $pos + 1 ); | ||
| 35 | $_SERVER['PATH_INFO'] = $target; | ||
| 36 | |||
| 37 | // Take the query params in the target and make them the params for this request | ||
| 38 | parse_str( $_SERVER['QUERY_STRING'], $_GET ); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Is a URL external? | ||
| 44 | * | ||
| 45 | * @param String $target URL to test. | ||
| 46 | * @return boolean | ||
| 47 | */ | ||
| 48 | public function is_external( $target ) { | ||
| 49 | return substr( $target, 0, 7 ) === 'http://' || substr( $target, 0, 8 ) === 'https://'; | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Pass the data from the target | ||
| 54 | * | ||
| 55 | * @return void | ||
| 56 | */ | ||
| 57 | public function run() { | ||
| 58 | // External target | ||
| 59 | $target = $this->get_target(); | ||
| 60 | if ( $target === null ) { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | if ( $this->is_external( $target ) ) { | ||
| 65 | // Pass on to an external request, echo the results, and then stop | ||
| 66 | $this->process_external( $target ); | ||
| 67 | exit(); | ||
| 68 | } | ||
| 69 | |||
| 70 | // Change the request and carry on | ||
| 71 | $this->process_internal( $target ); | ||
| 72 | } | ||
| 73 | |||
| 74 | public function name() { | ||
| 75 | return __( 'Pass-through', 'redirection' ); | ||
| 76 | } | ||
| 77 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | require_once dirname( __FILE__ ) . '/url.php'; | ||
| 4 | |||
| 5 | /** | ||
| 6 | * URL action - redirect to a URL | ||
| 7 | */ | ||
| 8 | class Random_Action extends Url_Action { | ||
| 9 | /** | ||
| 10 | * Get a random URL | ||
| 11 | * | ||
| 12 | * @return string|null | ||
| 13 | */ | ||
| 14 | private function get_random_url() { | ||
| 15 | // Pick a random WordPress page | ||
| 16 | global $wpdb; | ||
| 17 | |||
| 18 | $id = $wpdb->get_var( "SELECT ID FROM {$wpdb->prefix}posts WHERE post_status='publish' AND post_password='' AND post_type='post' ORDER BY RAND() LIMIT 0,1" ); | ||
| 19 | if ( $id ) { | ||
| 20 | $url = get_permalink( $id ); | ||
| 21 | |||
| 22 | if ( $url ) { | ||
| 23 | return $url; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | return null; | ||
| 28 | } | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Run this action. May not return from this function. | ||
| 32 | * | ||
| 33 | * @return void | ||
| 34 | */ | ||
| 35 | public function run() { | ||
| 36 | $target = $this->get_random_url(); | ||
| 37 | |||
| 38 | if ( $target ) { | ||
| 39 | $this->redirect_to( $target ); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | public function needs_target() { | ||
| 44 | return false; | ||
| 45 | } | ||
| 46 | |||
| 47 | public function name() { | ||
| 48 | return __( 'Redirect to random post', 'redirection' ); | ||
| 49 | } | ||
| 50 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * URL action - redirect to a URL | ||
| 5 | */ | ||
| 6 | class Url_Action extends Red_Action { | ||
| 7 | /** | ||
| 8 | * Redirect to a URL | ||
| 9 | * | ||
| 10 | * @param string $target Target URL. | ||
| 11 | * @return void | ||
| 12 | */ | ||
| 13 | protected function redirect_to( $target ) { | ||
| 14 | // This is a known redirect, possibly extenal | ||
| 15 | // phpcs:ignore | ||
| 16 | $redirect = wp_redirect( $target, $this->get_code(), 'redirection' ); | ||
| 17 | |||
| 18 | if ( $redirect ) { | ||
| 19 | /** @psalm-suppress InvalidGlobal */ | ||
| 20 | global $wp_version; | ||
| 21 | |||
| 22 | if ( version_compare( $wp_version, '5.1', '<' ) ) { | ||
| 23 | header( 'X-Redirect-Agent: redirection' ); | ||
| 24 | } | ||
| 25 | |||
| 26 | die(); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Run this action. May not return from this function. | ||
| 32 | * | ||
| 33 | * @return void | ||
| 34 | */ | ||
| 35 | public function run() { | ||
| 36 | $target = $this->get_target(); | ||
| 37 | |||
| 38 | if ( $target !== null ) { | ||
| 39 | $this->redirect_to( $target ); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Does this action need a target? | ||
| 45 | * | ||
| 46 | * @return boolean | ||
| 47 | */ | ||
| 48 | public function needs_target() { | ||
| 49 | return true; | ||
| 50 | } | ||
| 51 | |||
| 52 | public function name() { | ||
| 53 | return __( 'Redirect to URL', 'redirection' ); | ||
| 54 | } | ||
| 55 | } |
| 1 | <?php | ||
| 2 | /** | ||
| 3 | * @api {get} /redirection/v1/404 Get 404 logs | ||
| 4 | * @apiName GetLogs | ||
| 5 | * @apiDescription Get a paged list of 404 logs after applying a set of filters and result ordering. | ||
| 6 | * @apiGroup 404 | ||
| 7 | * | ||
| 8 | * @apiUse 404QueryParams | ||
| 9 | * | ||
| 10 | * @apiUse 404List | ||
| 11 | * @apiUse 401Error | ||
| 12 | * @apiUse 404Error | ||
| 13 | */ | ||
| 14 | |||
| 15 | /** | ||
| 16 | * @api {post} /redirection/v1/bulk/404/:type Bulk action | ||
| 17 | * @apiName BulkAction | ||
| 18 | * @apiDescription Delete 404 logs by ID | ||
| 19 | * @apiGroup 404 | ||
| 20 | * | ||
| 21 | * @apiParam (URL) {String="delete"} :type Type of bulk action that is applied to every log ID. | ||
| 22 | * | ||
| 23 | * @apiParam (Query Parameter) {String[]} [items] Array of group IDs to perform the action on | ||
| 24 | * @apiParam (Query Parameter) {Boolean=false} [global] Perform action globally using the filter parameters | ||
| 25 | * @apiUse 404QueryParams | ||
| 26 | * | ||
| 27 | * @apiUse 404List | ||
| 28 | * @apiUse 401Error | ||
| 29 | * @apiUse 404Error | ||
| 30 | * @apiUse 400MissingError | ||
| 31 | */ | ||
| 32 | |||
| 33 | /** | ||
| 34 | * @apiDefine 404QueryParams 404 log query parameters | ||
| 35 | * | ||
| 36 | * @apiParam (Query Parameter) {String} [filterBy[ip]] Filter the results by the supplied IP | ||
| 37 | * @apiParam (Query Parameter) {String} [filterBy[url]] Filter the results by the supplied URL | ||
| 38 | * @apiParam (Query Parameter) {String} [filterBy[url-]exact] Filter the results by the exact URL (not a substring match, as per `url`) | ||
| 39 | * @apiParam (Query Parameter) {String} [filterBy[referrer]] Filter the results by the supplied referrer | ||
| 40 | * @apiParam (Query Parameter) {String} [filterBy[agent]] Filter the results by the supplied user agent | ||
| 41 | * @apiParam (Query Parameter) {String} [filterBy[target]] Filter the results by the supplied redirect target | ||
| 42 | * @apiParam (Query Parameter) {String} [filterBy[domain]] Filter the results by the supplied domain name | ||
| 43 | * @apiParam (Query Parameter) {String="head","get","post"} [filterBy[method]] Filter the results by the supplied HTTP request method | ||
| 44 | * @apiParam (Query Parameter) {Integer} [filterBy[http]] Filter the results by the supplied redirect HTTP code | ||
| 45 | * @apiParam (Query Parameter) {string="ip","url"} [orderby] Order by IP or URL | ||
| 46 | * @apiParam (Query Parameter) {String="asc","desc"} [direction] Direction to order the results by (ascending or descending) | ||
| 47 | * @apiParam (Query Parameter) {Integer{1...200}} [per_page=25] Number of results per request | ||
| 48 | * @apiParam (Query Parameter) {Integer} [page=0] Current page of results | ||
| 49 | * @apiParam (Query Parameter) {String="ip","url"} [groupBy] Group by IP or URL | ||
| 50 | */ | ||
| 51 | |||
| 52 | /** | ||
| 53 | * @apiDefine 404List | ||
| 54 | * | ||
| 55 | * @apiSuccess {Object[]} items Array of 404 log objects | ||
| 56 | * @apiSuccess {Integer} items.id ID of 404 log entry | ||
| 57 | * @apiSuccess {String} items.created Date the 404 log entry was recorded | ||
| 58 | * @apiSuccess {Integer} items.created_time Unix time value for `created` | ||
| 59 | * @apiSuccess {Integer} items.url The requested URL that caused the 404 log entry | ||
| 60 | * @apiSuccess {String} items.agent User agent of the client initiating the request | ||
| 61 | * @apiSuccess {Integer} items.referrer Referrer of the client initiating the request | ||
| 62 | * @apiSuccess {Integer} total Number of items | ||
| 63 | * | ||
| 64 | * @apiSuccessExample {json} Success 200: | ||
| 65 | * HTTP/1.1 200 OK | ||
| 66 | * { | ||
| 67 | * "items": [ | ||
| 68 | * { | ||
| 69 | * "id": 3, | ||
| 70 | * "created": "2019-01-01 12:12:00, | ||
| 71 | * "created_time": "12345678", | ||
| 72 | * "url": "/the-url", | ||
| 73 | * "agent": "FancyBrowser", | ||
| 74 | * "referrer": "http://site.com/previous/, | ||
| 75 | * } | ||
| 76 | * ], | ||
| 77 | * "total": 1 | ||
| 78 | * } | ||
| 79 | */ | ||
| 80 | |||
| 81 | /** | ||
| 82 | * 404 API endpoint | ||
| 83 | */ | ||
| 84 | class Redirection_Api_404 extends Redirection_Api_Filter_Route { | ||
| 85 | /** | ||
| 86 | * 404 API endpoint constructor | ||
| 87 | * | ||
| 88 | * @param String $namespace Namespace. | ||
| 89 | */ | ||
| 90 | public function __construct( $namespace ) { | ||
| 91 | $orders = [ 'url', 'ip', 'total', 'count', '' ]; | ||
| 92 | $filters = [ 'ip', 'url-exact', 'referrer', 'agent', 'url', 'domain', 'method', 'http' ]; | ||
| 93 | |||
| 94 | register_rest_route( $namespace, '/404', array( | ||
| 95 | 'args' => $this->get_filter_args( $orders, $filters ), | ||
| 96 | $this->get_route( WP_REST_Server::READABLE, 'route_404', [ $this, 'permission_callback_manage' ] ), | ||
| 97 | ) ); | ||
| 98 | |||
| 99 | register_rest_route( $namespace, '/bulk/404/(?P<bulk>delete)', array( | ||
| 100 | $this->get_route( WP_REST_Server::EDITABLE, 'route_bulk', [ $this, 'permission_callback_delete' ] ), | ||
| 101 | 'args' => array_merge( $this->get_filter_args( $orders, $filters ), [ | ||
| 102 | 'items' => [ | ||
| 103 | 'description' => 'Comma separated list of item IDs to perform action on', | ||
| 104 | 'type' => 'array', | ||
| 105 | 'items' => [ | ||
| 106 | 'description' => 'Item ID', | ||
| 107 | 'type' => [ 'string', 'number' ], | ||
| 108 | ], | ||
| 109 | ], | ||
| 110 | ] ), | ||
| 111 | ) ); | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Checks a manage capability | ||
| 116 | * | ||
| 117 | * @param WP_REST_Request $request Request. | ||
| 118 | * @return Bool | ||
| 119 | */ | ||
| 120 | public function permission_callback_manage( WP_REST_Request $request ) { | ||
| 121 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_404_MANAGE ); | ||
| 122 | } | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Checks a delete capability | ||
| 126 | * | ||
| 127 | * @param WP_REST_Request $request Request. | ||
| 128 | * @return Bool | ||
| 129 | */ | ||
| 130 | public function permission_callback_delete( WP_REST_Request $request ) { | ||
| 131 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_404_DELETE ); | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Get 404 log | ||
| 136 | * | ||
| 137 | * @param WP_REST_Request $request The request. | ||
| 138 | * @return WP_Error|array Return an array of results, or a WP_Error | ||
| 139 | */ | ||
| 140 | public function route_404( WP_REST_Request $request ) { | ||
| 141 | return $this->get_404( $request->get_params() ); | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Perform action on 404s | ||
| 146 | * | ||
| 147 | * @param WP_REST_Request $request The request. | ||
| 148 | * @return WP_Error|array Return an array of results, or a WP_Error | ||
| 149 | */ | ||
| 150 | public function route_bulk( WP_REST_Request $request ) { | ||
| 151 | $params = $request->get_params(); | ||
| 152 | |||
| 153 | if ( isset( $params['items'] ) && is_array( $params['items'] ) ) { | ||
| 154 | $items = $params['items']; | ||
| 155 | |||
| 156 | foreach ( $items as $item ) { | ||
| 157 | if ( is_numeric( $item ) ) { | ||
| 158 | Red_404_Log::delete( intval( $item, 10 ) ); | ||
| 159 | } elseif ( isset( $params['groupBy'] ) ) { | ||
| 160 | $group_by = sanitize_text_field( $params['groupBy'] ); | ||
| 161 | $delete_by = 'url-exact'; | ||
| 162 | |||
| 163 | if ( in_array( $group_by, [ 'ip', 'agent' ], true ) ) { | ||
| 164 | $delete_by = $group_by; | ||
| 165 | } | ||
| 166 | |||
| 167 | Red_404_Log::delete_all( [ 'filterBy' => [ $delete_by => $item ] ] ); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | if ( isset( $params['groupBy'] ) && $params['groupBy'] === 'url-exact' ) { | ||
| 172 | unset( $params['groupBy'] ); | ||
| 173 | } | ||
| 174 | } elseif ( isset( $params['global'] ) && $params['global'] ) { | ||
| 175 | Red_404_Log::delete_all( $params ); | ||
| 176 | } | ||
| 177 | |||
| 178 | return $this->get_404( $params ); | ||
| 179 | } | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Get 404 log | ||
| 183 | * | ||
| 184 | * @param array $params The request. | ||
| 185 | * @return WP_Error|array Return an array of results, or a WP_Error | ||
| 186 | */ | ||
| 187 | private function get_404( array $params ) { | ||
| 188 | if ( isset( $params['groupBy'] ) && in_array( $params['groupBy'], [ 'ip', 'url', 'agent', 'url-exact' ], true ) ) { | ||
| 189 | $group_by = sanitize_text_field( $params['groupBy'] ); | ||
| 190 | if ( $group_by === 'url-exact' ) { | ||
| 191 | $group_by = 'url'; | ||
| 192 | } | ||
| 193 | |||
| 194 | return Red_404_Log::get_grouped( $group_by, $params ); | ||
| 195 | } | ||
| 196 | |||
| 197 | return Red_404_Log::get_filtered( $params ); | ||
| 198 | } | ||
| 199 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * @api {get} /redirection/v1/export/:module/:format Export redirects | ||
| 5 | * @apiName Export | ||
| 6 | * @apiDescription Export redirects for a module to Apache, CSV, Nginx, or JSON format | ||
| 7 | * @apiGroup Import/Export | ||
| 8 | * | ||
| 9 | * @apiParam (URL) {String="1","2","3","all"} :module The module to export, with 1 being WordPress, 2 is Apache, and 3 is Nginx | ||
| 10 | * @apiParam (URL) {String="csv","apache","nginx","json"} :format The format of the export | ||
| 11 | * | ||
| 12 | * @apiSuccess {String} data Exported data | ||
| 13 | * @apiSuccess {Integer} total Number of items exported | ||
| 14 | * | ||
| 15 | * @apiUse 401Error | ||
| 16 | * @apiUse 404Error | ||
| 17 | * @apiError (Error 400) redirect_export_invalid_module Invalid module | ||
| 18 | * @apiErrorExample {json} 404 Error Response: | ||
| 19 | * HTTP/1.1 400 Bad Request | ||
| 20 | * { | ||
| 21 | * "code": "redirect_export_invalid_module", | ||
| 22 | * "message": "Invalid module" | ||
| 23 | * } | ||
| 24 | */ | ||
| 25 | class Redirection_Api_Export extends Redirection_Api_Route { | ||
| 26 | public function __construct( $namespace ) { | ||
| 27 | register_rest_route( $namespace, '/export/(?P<module>1|2|3|all)/(?P<format>csv|apache|nginx|json)', array( | ||
| 28 | $this->get_route( WP_REST_Server::READABLE, 'route_export', [ $this, 'permission_callback_manage' ] ), | ||
| 29 | ) ); | ||
| 30 | } | ||
| 31 | |||
| 32 | public function permission_callback_manage( WP_REST_Request $request ) { | ||
| 33 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_IO_MANAGE ); | ||
| 34 | } | ||
| 35 | |||
| 36 | public function route_export( WP_REST_Request $request ) { | ||
| 37 | $module = sanitize_text_field( $request['module'] ); | ||
| 38 | $format = 'json'; | ||
| 39 | |||
| 40 | if ( in_array( $request['format'], [ 'csv', 'apache', 'nginx', 'json' ], true ) ) { | ||
| 41 | $format = sanitize_text_field( $request['format'] ); | ||
| 42 | } | ||
| 43 | |||
| 44 | $export = Red_FileIO::export( $module, $format ); | ||
| 45 | if ( $export === false ) { | ||
| 46 | return $this->add_error_details( new WP_Error( 'redirect_export_invalid_module', 'Invalid module' ), __LINE__ ); | ||
| 47 | } | ||
| 48 | |||
| 49 | return array( | ||
| 50 | 'data' => $export['data'], | ||
| 51 | 'total' => $export['total'], | ||
| 52 | ); | ||
| 53 | } | ||
| 54 | } |
This diff is collapsed.
Click to expand it.
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * @api {get} /redirection/v1/import/file/:group_id Import redirects | ||
| 5 | * @apiName Import | ||
| 6 | * @apiDescription Import redirects from CSV, JSON, or Apache .htaccess | ||
| 7 | * @apiGroup Import/Export | ||
| 8 | * | ||
| 9 | * @apiParam (URL) {Integer} :group_id The group ID to import into | ||
| 10 | * @apiParam (File) {File} file The multipart form upload containing the file to import | ||
| 11 | * | ||
| 12 | * @apiSuccess {Integer} imported Number of items imported | ||
| 13 | * | ||
| 14 | * @apiUse 401Error | ||
| 15 | * @apiUse 404Error | ||
| 16 | * @apiError (Error 400) redirect_import_invalid_group Invalid group | ||
| 17 | * @apiErrorExample {json} 404 Error Response: | ||
| 18 | * HTTP/1.1 400 Bad Request | ||
| 19 | * { | ||
| 20 | * "code": "redirect_import_invalid_group", | ||
| 21 | * "message": "Invalid group" | ||
| 22 | * } | ||
| 23 | * @apiError (Error 400) redirect_import_invalid_file Invalid file upload | ||
| 24 | * @apiErrorExample {json} 404 Error Response: | ||
| 25 | * HTTP/1.1 400 Bad Request | ||
| 26 | * { | ||
| 27 | * "code": "redirect_import_invalid_file", | ||
| 28 | * "message": "Invalid file upload" | ||
| 29 | * } | ||
| 30 | */ | ||
| 31 | class Redirection_Api_Import extends Redirection_Api_Route { | ||
| 32 | public function __construct( $namespace ) { | ||
| 33 | register_rest_route( $namespace, '/import/file/(?P<group_id>\d+)', array( | ||
| 34 | $this->get_route( WP_REST_Server::EDITABLE, 'route_import_file', [ $this, 'permission_callback_manage' ] ), | ||
| 35 | ) ); | ||
| 36 | |||
| 37 | register_rest_route( $namespace, '/import/plugin', array( | ||
| 38 | $this->get_route( WP_REST_Server::READABLE, 'route_plugin_import_list', [ $this, 'permission_callback_manage' ] ), | ||
| 39 | ) ); | ||
| 40 | |||
| 41 | register_rest_route( $namespace, '/import/plugin', array( | ||
| 42 | $this->get_route( WP_REST_Server::EDITABLE, 'route_plugin_import', [ $this, 'permission_callback_manage' ] ), | ||
| 43 | ) ); | ||
| 44 | } | ||
| 45 | |||
| 46 | public function permission_callback_manage( WP_REST_Request $request ) { | ||
| 47 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_IO_MANAGE ); | ||
| 48 | } | ||
| 49 | |||
| 50 | public function route_plugin_import_list( WP_REST_Request $request ) { | ||
| 51 | include_once dirname( __DIR__ ) . '/models/importer.php'; | ||
| 52 | |||
| 53 | return array( 'importers' => Red_Plugin_Importer::get_plugins() ); | ||
| 54 | } | ||
| 55 | |||
| 56 | public function route_plugin_import( WP_REST_Request $request ) { | ||
| 57 | include_once dirname( __DIR__ ) . '/models/importer.php'; | ||
| 58 | |||
| 59 | $params = $request->get_params( $request ); | ||
| 60 | $groups = Red_Group::get_all(); | ||
| 61 | $plugins = is_array( $request['plugin'] ) ? $request['plugin'] : [ $request['plugin'] ]; | ||
| 62 | $plugins = array_map( 'sanitize_text_field', $plugins ); | ||
| 63 | $total = 0; | ||
| 64 | |||
| 65 | foreach ( $plugins as $plugin ) { | ||
| 66 | $total += Red_Plugin_Importer::import( $plugin, $groups[0]['id'] ); | ||
| 67 | } | ||
| 68 | |||
| 69 | return [ 'imported' => $total ]; | ||
| 70 | } | ||
| 71 | |||
| 72 | public function route_import_file( WP_REST_Request $request ) { | ||
| 73 | $upload = $request->get_file_params(); | ||
| 74 | $upload = isset( $upload['file'] ) ? $upload['file'] : false; | ||
| 75 | $group_id = intval( $request['group_id'], 10 ); | ||
| 76 | |||
| 77 | if ( $upload && is_uploaded_file( $upload['tmp_name'] ) ) { | ||
| 78 | $count = Red_FileIO::import( $group_id, $upload ); | ||
| 79 | |||
| 80 | if ( $count !== false ) { | ||
| 81 | return array( | ||
| 82 | 'imported' => $count, | ||
| 83 | ); | ||
| 84 | } | ||
| 85 | |||
| 86 | return $this->add_error_details( new WP_Error( 'redirect_import_invalid_group', 'Invalid group' ), __LINE__ ); | ||
| 87 | } | ||
| 88 | |||
| 89 | return $this->add_error_details( new WP_Error( 'redirect_import_invalid_file', 'Invalid file' ), __LINE__ ); | ||
| 90 | } | ||
| 91 | |||
| 92 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * @api {get} /redirection/v1/log Get logs | ||
| 5 | * @apiName GetLogs | ||
| 6 | * @apiDescription Get a paged list of redirect logs after applying a set of filters and result ordering. | ||
| 7 | * @apiGroup Log | ||
| 8 | * | ||
| 9 | * @apiUse LogQueryParams | ||
| 10 | * | ||
| 11 | * @apiUse LogList | ||
| 12 | * @apiUse 401Error | ||
| 13 | * @apiUse 404Error | ||
| 14 | */ | ||
| 15 | |||
| 16 | /** | ||
| 17 | * @api {post} /redirection/v1/log Delete logs | ||
| 18 | * @apiName DeleteLogs | ||
| 19 | * @apiDescription Delete logs by filter. If no filter is supplied then all entries will be deleted. The endpoint will return the next page of results after. | ||
| 20 | * performing the action, based on the supplied query parameters. This information can be used to refresh a list displayed to the client. | ||
| 21 | * @apiGroup Log | ||
| 22 | * | ||
| 23 | * @apiParam (Query Parameter) {String} filterBy[ip] Filter the results by the supplied IP | ||
| 24 | * @apiParam (Query Parameter) {String} filterBy[url] Filter the results by the supplied URL | ||
| 25 | * @apiParam (Query Parameter) {String} filterBy[url-exact] Filter the results by the exact URL (not a substring match, as per `url`) | ||
| 26 | * @apiParam (Query Parameter) {String} filterBy[referrer] Filter the results by the supplied referrer | ||
| 27 | * @apiParam (Query Parameter) {String} filterBy[agent] Filter the results by the supplied user agent | ||
| 28 | * @apiParam (Query Parameter) {String} filterBy[target] Filter the results by the supplied redirect target | ||
| 29 | * | ||
| 30 | * @apiUse LogList | ||
| 31 | * @apiUse 401Error | ||
| 32 | * @apiUse 404Error | ||
| 33 | */ | ||
| 34 | |||
| 35 | /** | ||
| 36 | * @api {post} /redirection/v1/bulk/log/:type Bulk action | ||
| 37 | * @apiName BulkAction | ||
| 38 | * @apiDescription Delete logs by ID | ||
| 39 | * @apiGroup Log | ||
| 40 | * | ||
| 41 | * @apiParam (URL) {String="delete"} :type Type of bulk action that is applied to every log ID. | ||
| 42 | * @apiParam (Query Parameter) {String[]} [items] Array of group IDs to perform the action on | ||
| 43 | * @apiParam (Query Parameter) {Boolean=false} [global] Perform action globally using the filter parameters | ||
| 44 | * @apiUse LogQueryParams | ||
| 45 | * | ||
| 46 | * @apiUse LogList | ||
| 47 | * @apiUse 401Error | ||
| 48 | * @apiUse 404Error | ||
| 49 | * @apiUse 400MissingError | ||
| 50 | */ | ||
| 51 | |||
| 52 | /** | ||
| 53 | * @apiDefine LogQueryParams Log query parameters | ||
| 54 | * | ||
| 55 | * @apiParam (Query Parameter) {String} [filterBy[ip]] Filter the results by the supplied IP | ||
| 56 | * @apiParam (Query Parameter) {String} [filterBy[url]] Filter the results by the supplied URL | ||
| 57 | * @apiParam (Query Parameter) {String} [filterBy[url-]exact] Filter the results by the exact URL (not a substring match, as per `url`) | ||
| 58 | * @apiParam (Query Parameter) {String} [filterBy[referrer]] Filter the results by the supplied referrer | ||
| 59 | * @apiParam (Query Parameter) {String} [filterBy[agent]] Filter the results by the supplied user agent | ||
| 60 | * @apiParam (Query Parameter) {String} [filterBy[target]] Filter the results by the supplied redirect target | ||
| 61 | * @apiParam (Query Parameter) {String} [filterBy[domain]] Filter the results by the supplied domain name | ||
| 62 | * @apiParam (Query Parameter) {String} [filterBy[redirect_by]] Filter the results by the redirect agent | ||
| 63 | * @apiParam (Query Parameter) {String="head","get","post"} [filterBy[method]] Filter the results by the supplied HTTP request method | ||
| 64 | * @apiParam (Query Parameter) {String="ip","url"} [orderby] Order by IP or URL | ||
| 65 | * @apiParam (Query Parameter) {String="asc","desc"} [direction=desc] Direction to order the results by (ascending or descending) | ||
| 66 | * @apiParam (Query Parameter) {Integer{1...200}} [per_page=25] Number of results per request | ||
| 67 | * @apiParam (Query Parameter) {Integer} [page=0] Current page of results | ||
| 68 | * @apiParam (Query Parameter) {String="ip","url"} [groupBy] Group by IP or URL | ||
| 69 | */ | ||
| 70 | |||
| 71 | /** | ||
| 72 | * @apiDefine LogList | ||
| 73 | * | ||
| 74 | * @apiSuccess {Object[]} items Array of log objects | ||
| 75 | * @apiSuccess {Integer} items.id ID of log entry | ||
| 76 | * @apiSuccess {String} items.created Date the log entry was recorded | ||
| 77 | * @apiSuccess {Integer} items.created_time Unix time value for `created` | ||
| 78 | * @apiSuccess {Integer} items.url The requested URL that caused the log entry | ||
| 79 | * @apiSuccess {String} items.agent User agent of the client initiating the request | ||
| 80 | * @apiSuccess {Integer} items.referrer Referrer of the client initiating the request | ||
| 81 | * @apiSuccess {Integer} total Number of items | ||
| 82 | * | ||
| 83 | * @apiSuccessExample {json} Success 200: | ||
| 84 | * HTTP/1.1 200 OK | ||
| 85 | * { | ||
| 86 | * "items": [ | ||
| 87 | * { | ||
| 88 | * "id": 3, | ||
| 89 | * "created": "2019-01-01 12:12:00, | ||
| 90 | * "created_time": "12345678", | ||
| 91 | * "url": "/the-url", | ||
| 92 | * "agent": "FancyBrowser", | ||
| 93 | * "referrer": "http://site.com/previous/, | ||
| 94 | * } | ||
| 95 | * ], | ||
| 96 | * "total": 1 | ||
| 97 | * } | ||
| 98 | */ | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Log API endpoint | ||
| 102 | */ | ||
| 103 | class Redirection_Api_Log extends Redirection_Api_Filter_Route { | ||
| 104 | /** | ||
| 105 | * Log API endpoint constructor | ||
| 106 | * | ||
| 107 | * @param String $namespace Namespace. | ||
| 108 | */ | ||
| 109 | public function __construct( $namespace ) { | ||
| 110 | $orders = [ 'url', 'ip', 'total', 'count', '' ]; | ||
| 111 | $filters = [ 'ip', 'url-exact', 'referrer', 'agent', 'url', 'target', 'domain', 'method', 'http', 'redirect_by' ]; | ||
| 112 | |||
| 113 | register_rest_route( $namespace, '/log', array( | ||
| 114 | 'args' => $this->get_filter_args( $orders, $filters ), | ||
| 115 | $this->get_route( WP_REST_Server::READABLE, 'route_log', [ $this, 'permission_callback_manage' ] ), | ||
| 116 | ) ); | ||
| 117 | |||
| 118 | register_rest_route( $namespace, '/bulk/log/(?P<bulk>delete)', [ | ||
| 119 | $this->get_route( WP_REST_Server::EDITABLE, 'route_bulk', [ $this, 'permission_callback_delete' ] ), | ||
| 120 | 'args' => array_merge( $this->get_filter_args( $orders, $filters ), [ | ||
| 121 | 'items' => [ | ||
| 122 | 'description' => 'Comma separated list of item IDs to perform action on', | ||
| 123 | 'type' => 'array', | ||
| 124 | 'items' => [ | ||
| 125 | 'description' => 'Item ID', | ||
| 126 | 'type' => [ 'string', 'number' ], | ||
| 127 | ], | ||
| 128 | ], | ||
| 129 | ] ), | ||
| 130 | ] ); | ||
| 131 | } | ||
| 132 | |||
| 133 | /** | ||
| 134 | * Checks a manage capability | ||
| 135 | * | ||
| 136 | * @param WP_REST_Request $request Request. | ||
| 137 | * @return Bool | ||
| 138 | */ | ||
| 139 | public function permission_callback_manage( WP_REST_Request $request ) { | ||
| 140 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_LOG_MANAGE ); | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Checks a delete capability | ||
| 145 | * | ||
| 146 | * @param WP_REST_Request $request Request. | ||
| 147 | * @return Bool | ||
| 148 | */ | ||
| 149 | public function permission_callback_delete( WP_REST_Request $request ) { | ||
| 150 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_LOG_DELETE ); | ||
| 151 | } | ||
| 152 | |||
| 153 | /** | ||
| 154 | * Get log list | ||
| 155 | * | ||
| 156 | * @param WP_REST_Request $request The request. | ||
| 157 | * @return WP_Error|array Return an array of results, or a WP_Error | ||
| 158 | */ | ||
| 159 | public function route_log( WP_REST_Request $request ) { | ||
| 160 | return $this->get_logs( $request->get_params() ); | ||
| 161 | } | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Perform bulk action on logs | ||
| 165 | * | ||
| 166 | * @param WP_REST_Request $request The request. | ||
| 167 | * @return WP_Error|array Return an array of results, or a WP_Error | ||
| 168 | */ | ||
| 169 | public function route_bulk( WP_REST_Request $request ) { | ||
| 170 | $params = $request->get_params(); | ||
| 171 | |||
| 172 | if ( isset( $params['items'] ) && is_array( $params['items'] ) ) { | ||
| 173 | $items = $params['items']; | ||
| 174 | |||
| 175 | foreach ( $items as $item ) { | ||
| 176 | if ( is_numeric( $item ) ) { | ||
| 177 | Red_Redirect_Log::delete( intval( $item, 10 ) ); | ||
| 178 | } elseif ( isset( $params['groupBy'] ) ) { | ||
| 179 | $delete_by = 'url-exact'; | ||
| 180 | |||
| 181 | if ( in_array( $params['groupBy'], [ 'ip', 'agent' ], true ) ) { | ||
| 182 | $delete_by = sanitize_text_field( $params['groupBy'] ); | ||
| 183 | } | ||
| 184 | |||
| 185 | Red_Redirect_Log::delete_all( [ 'filterBy' => [ $delete_by => $item ] ] ); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | } elseif ( isset( $params['global'] ) && $params['global'] ) { | ||
| 189 | Red_Redirect_Log::delete_all( $params ); | ||
| 190 | } | ||
| 191 | |||
| 192 | return $this->route_log( $request ); | ||
| 193 | } | ||
| 194 | |||
| 195 | private function get_logs( array $params ) { | ||
| 196 | if ( isset( $params['groupBy'] ) && in_array( $params['groupBy'], [ 'ip', 'url', 'agent' ], true ) ) { | ||
| 197 | return Red_Redirect_Log::get_grouped( sanitize_text_field( $params['groupBy'] ), $params ); | ||
| 198 | } | ||
| 199 | |||
| 200 | return Red_Redirect_Log::get_filtered( $params ); | ||
| 201 | } | ||
| 202 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * 'Plugin' functions for Redirection | ||
| 5 | */ | ||
| 6 | class Redirection_Api_Plugin extends Redirection_Api_Route { | ||
| 7 | public function __construct( $namespace ) { | ||
| 8 | register_rest_route( $namespace, '/plugin', array( | ||
| 9 | $this->get_route( WP_REST_Server::READABLE, 'route_status', [ $this, 'permission_callback_manage' ] ), | ||
| 10 | ) ); | ||
| 11 | |||
| 12 | register_rest_route( $namespace, '/plugin', array( | ||
| 13 | $this->get_route( WP_REST_Server::EDITABLE, 'route_fixit', [ $this, 'permission_callback_manage' ] ), | ||
| 14 | 'args' => [ | ||
| 15 | 'name' => array( | ||
| 16 | 'description' => 'Name', | ||
| 17 | 'type' => 'string', | ||
| 18 | ), | ||
| 19 | 'value' => array( | ||
| 20 | 'description' => 'Value', | ||
| 21 | 'type' => 'string', | ||
| 22 | ), | ||
| 23 | ], | ||
| 24 | ) ); | ||
| 25 | |||
| 26 | register_rest_route( $namespace, '/plugin/delete', array( | ||
| 27 | $this->get_route( WP_REST_Server::EDITABLE, 'route_delete', [ $this, 'permission_callback_manage' ] ), | ||
| 28 | ) ); | ||
| 29 | |||
| 30 | register_rest_route( $namespace, '/plugin/test', array( | ||
| 31 | $this->get_route( WP_REST_Server::ALLMETHODS, 'route_test', [ $this, 'permission_callback_manage' ] ), | ||
| 32 | ) ); | ||
| 33 | |||
| 34 | register_rest_route( $namespace, '/plugin/data', array( | ||
| 35 | $this->get_route( WP_REST_Server::EDITABLE, 'route_database', [ $this, 'permission_callback_manage' ] ), | ||
| 36 | 'args' => [ | ||
| 37 | 'upgrade' => [ | ||
| 38 | 'description' => 'Upgrade parameter', | ||
| 39 | 'type' => 'string', | ||
| 40 | 'enum' => array( | ||
| 41 | 'stop', | ||
| 42 | 'skip', | ||
| 43 | 'retry', | ||
| 44 | ), | ||
| 45 | ], | ||
| 46 | ], | ||
| 47 | ) ); | ||
| 48 | } | ||
| 49 | |||
| 50 | public function permission_callback_manage( WP_REST_Request $request ) { | ||
| 51 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_SUPPORT_MANAGE ); | ||
| 52 | } | ||
| 53 | |||
| 54 | public function route_status( WP_REST_Request $request ) { | ||
| 55 | include_once dirname( REDIRECTION_FILE ) . '/models/fixer.php'; | ||
| 56 | |||
| 57 | $fixer = new Red_Fixer(); | ||
| 58 | return $fixer->get_json(); | ||
| 59 | } | ||
| 60 | |||
| 61 | public function route_fixit( WP_REST_Request $request ) { | ||
| 62 | include_once dirname( REDIRECTION_FILE ) . '/models/fixer.php'; | ||
| 63 | |||
| 64 | $params = $request->get_params(); | ||
| 65 | $fixer = new Red_Fixer(); | ||
| 66 | |||
| 67 | if ( isset( $params['name'] ) && isset( $params['value'] ) ) { | ||
| 68 | global $wpdb; | ||
| 69 | |||
| 70 | $fixer->save_debug( sanitize_text_field( $params['name'] ), sanitize_text_field( $params['value'] ) ); | ||
| 71 | |||
| 72 | $groups = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_groups" ), 10 ); | ||
| 73 | if ( $groups === 0 ) { | ||
| 74 | Red_Group::create( 'new group', 1 ); | ||
| 75 | } | ||
| 76 | } else { | ||
| 77 | $fixer->fix( $fixer->get_status() ); | ||
| 78 | } | ||
| 79 | |||
| 80 | return $fixer->get_json(); | ||
| 81 | } | ||
| 82 | |||
| 83 | public function route_delete() { | ||
| 84 | if ( is_multisite() ) { | ||
| 85 | return new WP_Error( 'redirect_delete_multi', 'Multisite installations must delete the plugin from the network admin' ); | ||
| 86 | } | ||
| 87 | |||
| 88 | $plugin = Redirection_Admin::init(); | ||
| 89 | $plugin->plugin_uninstall(); | ||
| 90 | |||
| 91 | $current = get_option( 'active_plugins' ); | ||
| 92 | $plugin_position = array_search( basename( dirname( REDIRECTION_FILE ) ) . '/' . basename( REDIRECTION_FILE ), $current ); | ||
| 93 | if ( $plugin_position !== false ) { | ||
| 94 | array_splice( $current, $plugin_position, 1 ); | ||
| 95 | update_option( 'active_plugins', $current ); | ||
| 96 | } | ||
| 97 | |||
| 98 | return array( 'location' => admin_url() . 'plugins.php' ); | ||
| 99 | } | ||
| 100 | |||
| 101 | public function route_test( WP_REST_Request $request ) { | ||
| 102 | return array( | ||
| 103 | 'success' => true, | ||
| 104 | ); | ||
| 105 | } | ||
| 106 | |||
| 107 | public function route_database( WP_REST_Request $request ) { | ||
| 108 | $params = $request->get_params(); | ||
| 109 | $status = new Red_Database_Status(); | ||
| 110 | $upgrade = false; | ||
| 111 | |||
| 112 | if ( isset( $params['upgrade'] ) && in_array( $params['upgrade'], [ 'stop', 'skip' ], true ) ) { | ||
| 113 | $upgrade = sanitize_text_field( $params['upgrade'] ); | ||
| 114 | } | ||
| 115 | |||
| 116 | // Check upgrade | ||
| 117 | if ( ! $status->needs_updating() && ! $status->needs_installing() ) { | ||
| 118 | /* translators: version number */ | ||
| 119 | $status->set_error( sprintf( __( 'Your database does not need updating to %s.', 'redirection' ), REDIRECTION_DB_VERSION ) ); | ||
| 120 | |||
| 121 | return $status->get_json(); | ||
| 122 | } | ||
| 123 | |||
| 124 | if ( $upgrade === 'stop' ) { | ||
| 125 | $status->stop_update(); | ||
| 126 | } elseif ( $upgrade === 'skip' ) { | ||
| 127 | $status->set_next_stage(); | ||
| 128 | } | ||
| 129 | |||
| 130 | if ( $upgrade === false || $status->get_current_stage() ) { | ||
| 131 | $database = new Red_Database(); | ||
| 132 | $database->apply_upgrade( $status ); | ||
| 133 | } | ||
| 134 | |||
| 135 | return $status->get_json(); | ||
| 136 | } | ||
| 137 | } |
This diff is collapsed.
Click to expand it.
| 1 | <?php | ||
| 2 | /** | ||
| 3 | * @api {get} /redirection/v1/setting Get settings | ||
| 4 | * @apiName GetSettings | ||
| 5 | * @apiDescription Get all settings for Redirection. This includes user-configurable settings, as well as necessary WordPress settings. | ||
| 6 | * @apiGroup Settings | ||
| 7 | * | ||
| 8 | * @apiUse SettingItem | ||
| 9 | * @apiUse 401Error | ||
| 10 | * @apiUse 404Error | ||
| 11 | */ | ||
| 12 | |||
| 13 | /** | ||
| 14 | * @api {post} /redirection/v1/setting Update settings | ||
| 15 | * @apiName UpdateSettings | ||
| 16 | * @apiDescription Update Redirection settings. Note you can do partial updates, and only the values specified will be changed. | ||
| 17 | * @apiGroup Settings | ||
| 18 | * | ||
| 19 | * @apiParam {Object} settings An object containing all the settings to update | ||
| 20 | * @apiParamExample {json} settings: | ||
| 21 | * { | ||
| 22 | * "expire_redirect": 14, | ||
| 23 | * "https": false | ||
| 24 | * } | ||
| 25 | * | ||
| 26 | * @apiUse SettingItem | ||
| 27 | * @apiUse 401Error | ||
| 28 | * @apiUse 404Error | ||
| 29 | */ | ||
| 30 | |||
| 31 | /** | ||
| 32 | * @apiDefine SettingItem Settings | ||
| 33 | * Redirection settings | ||
| 34 | * | ||
| 35 | * @apiSuccess {Object[]} settings An object containing all settings | ||
| 36 | * @apiSuccess {String} settings.expire_redirect | ||
| 37 | * @apiSuccess {String} settings.token | ||
| 38 | * @apiSuccess {String} settings.monitor_post | ||
| 39 | * @apiSuccess {String[]} settings.monitor_types | ||
| 40 | * @apiSuccess {String} settings.associated_redirect | ||
| 41 | * @apiSuccess {String} settings.auto_target | ||
| 42 | * @apiSuccess {String} settings.expire_redirect | ||
| 43 | * @apiSuccess {String} settings.expire_404 | ||
| 44 | * @apiSuccess {String} settings.modules | ||
| 45 | * @apiSuccess {String} settings.newsletter | ||
| 46 | * @apiSuccess {String} settings.redirect_cache | ||
| 47 | * @apiSuccess {String} settings.ip_logging | ||
| 48 | * @apiSuccess {String} settings.last_group_id | ||
| 49 | * @apiSuccess {String} settings.rest_api | ||
| 50 | * @apiSuccess {String} settings.https | ||
| 51 | * @apiSuccess {String} settings.headers | ||
| 52 | * @apiSuccess {String} settings.database | ||
| 53 | * @apiSuccess {String} settings.relocate Relocate this site to the specified domain (and path) | ||
| 54 | * @apiSuccess {String="www","nowww",""} settings.preferred_domain Preferred canonical domain | ||
| 55 | * @apiSuccess {String[]} settings.aliases Array of domains that will be redirected to the current WordPress site | ||
| 56 | * @apiSuccess {Object[]} groups An array of groups | ||
| 57 | * @apiSuccess {String} groups.label Name of the group | ||
| 58 | * @apiSuccess {Integer} groups.value Group ID | ||
| 59 | * @apiSuccess {String} installed The path that WordPress is installed in | ||
| 60 | * @apiSuccess {Boolean} canDelete True if Redirection can be deleted, false otherwise (on multisite, for example) | ||
| 61 | * @apiSuccess {String[]} post_types Array of WordPress post types | ||
| 62 | * | ||
| 63 | * @apiSuccessExample {json} Success-Response: | ||
| 64 | * HTTP/1.1 200 OK | ||
| 65 | * { | ||
| 66 | * "settings": { | ||
| 67 | * "expire_redirect": 7, | ||
| 68 | * "https": true | ||
| 69 | * }, | ||
| 70 | * "groups": [ | ||
| 71 | * { label: 'My group', value: 5 } | ||
| 72 | * ], | ||
| 73 | * "installed": "/var/html/wordpress", | ||
| 74 | * "canDelete": true, | ||
| 75 | * "post_types": [ | ||
| 76 | * "post", | ||
| 77 | * "page" | ||
| 78 | * ] | ||
| 79 | * } | ||
| 80 | */ | ||
| 81 | |||
| 82 | class Redirection_Api_Settings extends Redirection_Api_Route { | ||
| 83 | public function __construct( $namespace ) { | ||
| 84 | register_rest_route( $namespace, '/setting', array( | ||
| 85 | $this->get_route( WP_REST_Server::READABLE, 'route_settings', [ $this, 'permission_callback_manage' ] ), | ||
| 86 | $this->get_route( WP_REST_Server::EDITABLE, 'route_save_settings', [ $this, 'permission_callback_manage' ] ), | ||
| 87 | ) ); | ||
| 88 | } | ||
| 89 | |||
| 90 | public function route_settings( WP_REST_Request $request ) { | ||
| 91 | if ( ! function_exists( 'get_home_path' ) ) { | ||
| 92 | include_once ABSPATH . '/wp-admin/includes/file.php'; | ||
| 93 | } | ||
| 94 | |||
| 95 | return [ | ||
| 96 | 'settings' => red_get_options(), | ||
| 97 | 'groups' => $this->groups_to_json( Red_Group::get_for_select() ), | ||
| 98 | 'installed' => get_home_path(), | ||
| 99 | 'canDelete' => ! is_multisite(), | ||
| 100 | 'post_types' => red_get_post_types(), | ||
| 101 | ]; | ||
| 102 | } | ||
| 103 | |||
| 104 | public function permission_callback_manage( WP_REST_Request $request ) { | ||
| 105 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_OPTION_MANAGE ) || Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_SITE_MANAGE ); | ||
| 106 | } | ||
| 107 | |||
| 108 | public function route_save_settings( WP_REST_Request $request ) { | ||
| 109 | $params = $request->get_params(); | ||
| 110 | $result = true; | ||
| 111 | |||
| 112 | if ( isset( $params['location'] ) && strlen( $params['location'] ) > 0 ) { | ||
| 113 | $module = Red_Module::get( 2 ); | ||
| 114 | $result = $module->can_save( sanitize_text_field( $params['location'] ) ); | ||
| 115 | } | ||
| 116 | |||
| 117 | red_set_options( $params ); | ||
| 118 | |||
| 119 | $settings = $this->route_settings( $request ); | ||
| 120 | if ( is_wp_error( $result ) ) { | ||
| 121 | $settings['warning'] = $result->get_error_message(); | ||
| 122 | } | ||
| 123 | |||
| 124 | return $settings; | ||
| 125 | } | ||
| 126 | |||
| 127 | private function groups_to_json( $groups, $depth = 0 ) { | ||
| 128 | $items = array(); | ||
| 129 | |||
| 130 | foreach ( $groups as $text => $value ) { | ||
| 131 | if ( is_array( $value ) && $depth === 0 ) { | ||
| 132 | $items[] = (object) array( | ||
| 133 | 'label' => $text, | ||
| 134 | 'value' => $this->groups_to_json( $value, 1 ), | ||
| 135 | ); | ||
| 136 | } else { | ||
| 137 | $items[] = (object) array( | ||
| 138 | 'label' => $value, | ||
| 139 | 'value' => $text, | ||
| 140 | ); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | return $items; | ||
| 145 | } | ||
| 146 | } |
wp-content/plugins/redirection/api/api.php
0 → 100644
| 1 | <?php | ||
| 2 | |||
| 3 | require_once __DIR__ . '/api-group.php'; | ||
| 4 | require_once __DIR__ . '/api-redirect.php'; | ||
| 5 | require_once __DIR__ . '/api-log.php'; | ||
| 6 | require_once __DIR__ . '/api-404.php'; | ||
| 7 | require_once __DIR__ . '/api-settings.php'; | ||
| 8 | require_once __DIR__ . '/api-plugin.php'; | ||
| 9 | require_once __DIR__ . '/api-import.php'; | ||
| 10 | require_once __DIR__ . '/api-export.php'; | ||
| 11 | |||
| 12 | define( 'REDIRECTION_API_NAMESPACE', 'redirection/v1' ); | ||
| 13 | |||
| 14 | /** | ||
| 15 | * @apiDefine 401Error | ||
| 16 | * | ||
| 17 | * @apiError (Error 401) rest_forbidden You are not authorized to access this API endpoint | ||
| 18 | * @apiErrorExample {json} 401 Error Response: | ||
| 19 | * HTTP/1.1 401 Bad Request | ||
| 20 | * { | ||
| 21 | * "code": "rest_forbidden", | ||
| 22 | * "message": "Sorry, you are not allowed to do that." | ||
| 23 | * } | ||
| 24 | */ | ||
| 25 | |||
| 26 | /** | ||
| 27 | * @apiDefine 404Error | ||
| 28 | * | ||
| 29 | * @apiError (Error 404) rest_no_route Endpoint not found | ||
| 30 | * @apiErrorExample {json} 404 Error Response: | ||
| 31 | * HTTP/1.1 404 Not Found | ||
| 32 | * { | ||
| 33 | * "code": "rest_no_route", | ||
| 34 | * "message": "No route was found matching the URL and request method" | ||
| 35 | * } | ||
| 36 | */ | ||
| 37 | |||
| 38 | /** | ||
| 39 | * @apiDefine 400Error | ||
| 40 | * | ||
| 41 | * @apiError rest_forbidden You are not authorized to access this API endpoint | ||
| 42 | * @apiErrorExample {json} 400 Error Response: | ||
| 43 | * HTTP/1.1 400 Bad Request | ||
| 44 | * { | ||
| 45 | * "error": "invalid", | ||
| 46 | * "message": "Invalid request" | ||
| 47 | * } | ||
| 48 | */ | ||
| 49 | |||
| 50 | /** | ||
| 51 | * @apiDefine 400MissingError | ||
| 52 | * @apiError (Error 400) rest_missing_callback_param Some required parameters are not present or not in the correct format | ||
| 53 | * @apiErrorExample {json} 400 Error Response: | ||
| 54 | * HTTP/1.1 400 Bad Request | ||
| 55 | * { | ||
| 56 | * "code": "rest_missing_callback_param", | ||
| 57 | * "message": "Missing parameter(s): PARAM" | ||
| 58 | * } | ||
| 59 | */ | ||
| 60 | class Redirection_Api_Route { | ||
| 61 | protected function add_error_details( WP_Error $error, $line, $code = 400 ) { | ||
| 62 | global $wpdb; | ||
| 63 | |||
| 64 | $data = array( | ||
| 65 | 'status' => $code, | ||
| 66 | 'error_code' => $line, | ||
| 67 | ); | ||
| 68 | |||
| 69 | if ( isset( $wpdb->last_error ) && $wpdb->last_error ) { | ||
| 70 | $data['wpdb'] = $wpdb->last_error; | ||
| 71 | } | ||
| 72 | |||
| 73 | $error->add_data( $data ); | ||
| 74 | return $error; | ||
| 75 | } | ||
| 76 | |||
| 77 | public function permission_callback( WP_REST_Request $request ) { | ||
| 78 | return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_PLUGIN ); | ||
| 79 | } | ||
| 80 | |||
| 81 | public function get_route( $method, $callback, $permissions = false ) { | ||
| 82 | return [ | ||
| 83 | 'methods' => $method, | ||
| 84 | 'callback' => [ $this, $callback ], | ||
| 85 | 'permission_callback' => $permissions ? $permissions : [ $this, 'permission_callback' ], | ||
| 86 | ]; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | class Redirection_Api_Filter_Route extends Redirection_Api_Route { | ||
| 91 | public function validate_filter( $value, $request, $param ) { | ||
| 92 | $fields = $request->get_attributes()['args']['filterBy']['filter_fields']; | ||
| 93 | |||
| 94 | if ( ! is_array( $value ) ) { | ||
| 95 | return new WP_Error( 'rest_invalid_param', 'Filter is not an array', array( 'status' => 400 ) ); | ||
| 96 | } | ||
| 97 | |||
| 98 | if ( ! empty( $fields ) ) { | ||
| 99 | foreach ( array_keys( $value ) as $key ) { | ||
| 100 | if ( ! in_array( $key, $fields, true ) ) { | ||
| 101 | return new WP_Error( 'rest_invalid_param', 'Filter type is not supported: ' . $key, array( 'status' => 400 ) ); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | return true; | ||
| 107 | } | ||
| 108 | |||
| 109 | protected function get_filter_args( $order_fields, $filters = [] ) { | ||
| 110 | return [ | ||
| 111 | 'filterBy' => [ | ||
| 112 | 'description' => 'Field to filter by', | ||
| 113 | 'validate_callback' => [ $this, 'validate_filter' ], | ||
| 114 | 'filter_fields' => $filters, | ||
| 115 | ], | ||
| 116 | 'orderby' => [ | ||
| 117 | 'description' => 'Field to order results by', | ||
| 118 | 'type' => 'string', | ||
| 119 | 'enum' => $order_fields, | ||
| 120 | ], | ||
| 121 | 'direction' => [ | ||
| 122 | 'description' => 'Direction of ordered results', | ||
| 123 | 'type' => 'string', | ||
| 124 | 'default' => 'desc', | ||
| 125 | 'enum' => [ 'asc', 'desc' ], | ||
| 126 | ], | ||
| 127 | 'per_page' => [ | ||
| 128 | 'description' => 'Number of results per page', | ||
| 129 | 'type' => 'integer', | ||
| 130 | 'default' => 25, | ||
| 131 | 'minimum' => 5, | ||
| 132 | 'maximum' => RED_MAX_PER_PAGE, | ||
| 133 | ], | ||
| 134 | 'page' => [ | ||
| 135 | 'description' => 'Page offset', | ||
| 136 | 'type' => 'integer', | ||
| 137 | 'minimum' => 0, | ||
| 138 | 'default' => 0, | ||
| 139 | ], | ||
| 140 | ]; | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Register a bulk action route | ||
| 145 | * | ||
| 146 | * @param String $namespace Namespace. | ||
| 147 | * @param String $route Route. | ||
| 148 | * @param Array $orders | ||
| 149 | * @param Array $filters | ||
| 150 | * @param Object $callback | ||
| 151 | * @param boolean $permissions | ||
| 152 | * @return void | ||
| 153 | */ | ||
| 154 | public function register_bulk( $namespace, $route, $orders, $filters, $callback, $permissions = false ) { | ||
| 155 | register_rest_route( $namespace, $route, array( | ||
| 156 | $this->get_route( WP_REST_Server::EDITABLE, $callback, $permissions ), | ||
| 157 | 'args' => array_merge( $this->get_filter_args( $orders, $filters ), [ | ||
| 158 | 'items' => [ | ||
| 159 | 'description' => 'Comma separated list of item IDs to perform action on', | ||
| 160 | 'type' => 'array', | ||
| 161 | 'items' => [ | ||
| 162 | 'type' => 'string', | ||
| 163 | ], | ||
| 164 | ], | ||
| 165 | ] ), | ||
| 166 | ) ); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | class Redirection_Api { | ||
| 171 | private static $instance = null; | ||
| 172 | private $routes = array(); | ||
| 173 | |||
| 174 | public static function init() { | ||
| 175 | if ( is_null( self::$instance ) ) { | ||
| 176 | self::$instance = new Redirection_Api(); | ||
| 177 | } | ||
| 178 | |||
| 179 | return self::$instance; | ||
| 180 | } | ||
| 181 | |||
| 182 | public function __construct() { | ||
| 183 | global $wpdb; | ||
| 184 | |||
| 185 | $wpdb->hide_errors(); | ||
| 186 | |||
| 187 | $this->routes[] = new Redirection_Api_Redirect( REDIRECTION_API_NAMESPACE ); | ||
| 188 | $this->routes[] = new Redirection_Api_Group( REDIRECTION_API_NAMESPACE ); | ||
| 189 | $this->routes[] = new Redirection_Api_Log( REDIRECTION_API_NAMESPACE ); | ||
| 190 | $this->routes[] = new Redirection_Api_404( REDIRECTION_API_NAMESPACE ); | ||
| 191 | $this->routes[] = new Redirection_Api_Settings( REDIRECTION_API_NAMESPACE ); | ||
| 192 | $this->routes[] = new Redirection_Api_Plugin( REDIRECTION_API_NAMESPACE ); | ||
| 193 | $this->routes[] = new Redirection_Api_Import( REDIRECTION_API_NAMESPACE ); | ||
| 194 | $this->routes[] = new Redirection_Api_Export( REDIRECTION_API_NAMESPACE ); | ||
| 195 | } | ||
| 196 | } |
This diff is collapsed.
Click to expand it.
| 1 | <?php | ||
| 2 | |||
| 3 | abstract class Red_Database_Upgrader { | ||
| 4 | private $queries = []; | ||
| 5 | private $live = true; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * Return an array of all the stages for an upgrade | ||
| 9 | * | ||
| 10 | * @return array stage name => reason | ||
| 11 | */ | ||
| 12 | abstract public function get_stages(); | ||
| 13 | |||
| 14 | public function get_reason( $stage ) { | ||
| 15 | $stages = $this->get_stages(); | ||
| 16 | |||
| 17 | if ( isset( $stages[ $stage ] ) ) { | ||
| 18 | return $stages[ $stage ]; | ||
| 19 | } | ||
| 20 | |||
| 21 | return 'Unknown'; | ||
| 22 | } | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Run a particular stage on the current upgrader | ||
| 26 | * | ||
| 27 | * @return Red_Database_Status | ||
| 28 | */ | ||
| 29 | public function perform_stage( Red_Database_Status $status ) { | ||
| 30 | global $wpdb; | ||
| 31 | |||
| 32 | $stage = $status->get_current_stage(); | ||
| 33 | if ( $this->has_stage( $stage ) && method_exists( $this, $stage ) ) { | ||
| 34 | try { | ||
| 35 | $this->$stage( $wpdb ); | ||
| 36 | $status->set_ok( $this->get_reason( $stage ) ); | ||
| 37 | } catch ( Exception $e ) { | ||
| 38 | $status->set_error( $e->getMessage() ); | ||
| 39 | } | ||
| 40 | } else { | ||
| 41 | $status->set_error( 'No stage found for upgrade ' . $stage ); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | public function get_queries_for_stage( $stage ) { | ||
| 46 | global $wpdb; | ||
| 47 | |||
| 48 | $this->queries = []; | ||
| 49 | $this->live = false; | ||
| 50 | $this->$stage( $wpdb, false ); | ||
| 51 | $this->live = true; | ||
| 52 | |||
| 53 | return $this->queries; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Returns the current database charset | ||
| 58 | * | ||
| 59 | * @return string Database charset | ||
| 60 | */ | ||
| 61 | public function get_charset() { | ||
| 62 | global $wpdb; | ||
| 63 | |||
| 64 | $charset_collate = ''; | ||
| 65 | if ( ! empty( $wpdb->charset ) ) { | ||
| 66 | // Fix some common invalid charset values | ||
| 67 | $fixes = [ | ||
| 68 | 'utf-8', | ||
| 69 | 'utf', | ||
| 70 | ]; | ||
| 71 | |||
| 72 | $charset = $wpdb->charset; | ||
| 73 | if ( in_array( strtolower( $charset ), $fixes, true ) ) { | ||
| 74 | $charset = 'utf8'; | ||
| 75 | } | ||
| 76 | |||
| 77 | $charset_collate = "DEFAULT CHARACTER SET $charset"; | ||
| 78 | } | ||
| 79 | |||
| 80 | if ( ! empty( $wpdb->collate ) ) { | ||
| 81 | $charset_collate .= " COLLATE=$wpdb->collate"; | ||
| 82 | } | ||
| 83 | |||
| 84 | return $charset_collate; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Performs a $wpdb->query, and throws an exception if an error occurs | ||
| 89 | * | ||
| 90 | * @return bool true if query is performed ok, otherwise an exception is thrown | ||
| 91 | */ | ||
| 92 | protected function do_query( $wpdb, $sql ) { | ||
| 93 | if ( ! $this->live ) { | ||
| 94 | $this->queries[] = $sql; | ||
| 95 | return true; | ||
| 96 | } | ||
| 97 | |||
| 98 | // These are known queries without user input | ||
| 99 | // phpcs:ignore | ||
| 100 | $result = $wpdb->query( $sql ); | ||
| 101 | |||
| 102 | if ( $result === false ) { | ||
| 103 | /* translators: 1: SQL string */ | ||
| 104 | throw new Exception( sprintf( 'Failed to perform query "%s"', $sql ) ); | ||
| 105 | } | ||
| 106 | |||
| 107 | return true; | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Load a database upgrader class | ||
| 112 | * | ||
| 113 | * @return object Database upgrader | ||
| 114 | */ | ||
| 115 | public static function get( $version ) { | ||
| 116 | include_once dirname( __FILE__ ) . '/schema/' . str_replace( [ '..', '/' ], '', $version['file'] ); | ||
| 117 | |||
| 118 | return new $version['class']; | ||
| 119 | } | ||
| 120 | |||
| 121 | private function has_stage( $stage ) { | ||
| 122 | return in_array( $stage, array_keys( $this->get_stages() ), true ); | ||
| 123 | } | ||
| 124 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | require_once __DIR__ . '/database-status.php'; | ||
| 4 | require_once __DIR__ . '/database-upgrader.php'; | ||
| 5 | |||
| 6 | class Red_Database { | ||
| 7 | /** | ||
| 8 | * Get all upgrades for a database version | ||
| 9 | * | ||
| 10 | * @return array Array of versions from self::get_upgrades() | ||
| 11 | */ | ||
| 12 | public function get_upgrades_for_version( $current_version, $current_stage ) { | ||
| 13 | if ( empty( $current_version ) ) { | ||
| 14 | return [ | ||
| 15 | [ | ||
| 16 | 'version' => REDIRECTION_DB_VERSION, | ||
| 17 | 'file' => 'latest.php', | ||
| 18 | 'class' => 'Red_Latest_Database', | ||
| 19 | ], | ||
| 20 | ]; | ||
| 21 | } | ||
| 22 | |||
| 23 | $upgraders = []; | ||
| 24 | $found = false; | ||
| 25 | |||
| 26 | foreach ( $this->get_upgrades() as $upgrade ) { | ||
| 27 | if ( ! $found ) { | ||
| 28 | $upgrader = Red_Database_Upgrader::get( $upgrade ); | ||
| 29 | |||
| 30 | $stage_present = in_array( $current_stage, array_keys( $upgrader->get_stages() ), true ); | ||
| 31 | $same_version = $current_stage === false && version_compare( $upgrade['version'], $current_version, 'gt' ); | ||
| 32 | |||
| 33 | if ( $stage_present || $same_version ) { | ||
| 34 | $found = true; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | if ( $found ) { | ||
| 39 | $upgraders[] = $upgrade; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | return $upgraders; | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Apply a particular upgrade stage | ||
| 48 | * | ||
| 49 | * @return mixed Result for upgrade | ||
| 50 | */ | ||
| 51 | public function apply_upgrade( Red_Database_Status $status ) { | ||
| 52 | $upgraders = $this->get_upgrades_for_version( $status->get_current_version(), $status->get_current_stage() ); | ||
| 53 | |||
| 54 | if ( count( $upgraders ) === 0 ) { | ||
| 55 | $status->set_error( 'No upgrades found for version ' . $status->get_current_version() ); | ||
| 56 | return; | ||
| 57 | } | ||
| 58 | |||
| 59 | if ( $status->get_current_stage() === false ) { | ||
| 60 | if ( $status->needs_installing() ) { | ||
| 61 | $status->start_install( $upgraders ); | ||
| 62 | } else { | ||
| 63 | $status->start_upgrade( $upgraders ); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | // Look at first upgrade | ||
| 68 | $upgrader = Red_Database_Upgrader::get( $upgraders[0] ); | ||
| 69 | |||
| 70 | // Perform the upgrade | ||
| 71 | $upgrader->perform_stage( $status ); | ||
| 72 | |||
| 73 | if ( ! $status->is_error() ) { | ||
| 74 | $status->set_next_stage(); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | public static function apply_to_sites( $callback ) { | ||
| 79 | if ( is_multisite() && ( is_network_admin() || defined( 'WP_CLI' ) && WP_CLI ) ) { | ||
| 80 | $total = get_sites( [ 'count' => true ] ); | ||
| 81 | $per_page = 100; | ||
| 82 | |||
| 83 | // Paginate through all sites and apply the callback | ||
| 84 | for ( $offset = 0; $offset < $total; $offset += $per_page ) { | ||
| 85 | array_map( function( $site ) use ( $callback ) { | ||
| 86 | switch_to_blog( $site->blog_id ); | ||
| 87 | |||
| 88 | $callback(); | ||
| 89 | |||
| 90 | restore_current_blog(); | ||
| 91 | }, get_sites( [ 'number' => $per_page, 'offset' => $offset ] ) ); | ||
| 92 | } | ||
| 93 | |||
| 94 | return; | ||
| 95 | } | ||
| 96 | |||
| 97 | $callback(); | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Get latest database installer | ||
| 102 | * | ||
| 103 | * @return object Red_Latest_Database | ||
| 104 | */ | ||
| 105 | public static function get_latest_database() { | ||
| 106 | include_once dirname( __FILE__ ) . '/schema/latest.php'; | ||
| 107 | |||
| 108 | return new Red_Latest_Database(); | ||
| 109 | } | ||
| 110 | |||
| 111 | /** | ||
| 112 | * List of all upgrades and their associated file | ||
| 113 | * | ||
| 114 | * @return array Database upgrade array | ||
| 115 | */ | ||
| 116 | public function get_upgrades() { | ||
| 117 | return [ | ||
| 118 | [ | ||
| 119 | 'version' => '2.0.1', | ||
| 120 | 'file' => '201.php', | ||
| 121 | 'class' => 'Red_Database_201', | ||
| 122 | ], | ||
| 123 | [ | ||
| 124 | 'version' => '2.1.16', | ||
| 125 | 'file' => '216.php', | ||
| 126 | 'class' => 'Red_Database_216', | ||
| 127 | ], | ||
| 128 | [ | ||
| 129 | 'version' => '2.2', | ||
| 130 | 'file' => '220.php', | ||
| 131 | 'class' => 'Red_Database_220', | ||
| 132 | ], | ||
| 133 | [ | ||
| 134 | 'version' => '2.3.1', | ||
| 135 | 'file' => '231.php', | ||
| 136 | 'class' => 'Red_Database_231', | ||
| 137 | ], | ||
| 138 | [ | ||
| 139 | 'version' => '2.3.2', | ||
| 140 | 'file' => '232.php', | ||
| 141 | 'class' => 'Red_Database_232', | ||
| 142 | ], | ||
| 143 | [ | ||
| 144 | 'version' => '2.3.3', | ||
| 145 | 'file' => '233.php', | ||
| 146 | 'class' => 'Red_Database_233', | ||
| 147 | ], | ||
| 148 | [ | ||
| 149 | 'version' => '2.4', | ||
| 150 | 'file' => '240.php', | ||
| 151 | 'class' => 'Red_Database_240', | ||
| 152 | ], | ||
| 153 | [ | ||
| 154 | 'version' => '4.0', | ||
| 155 | 'file' => '400.php', | ||
| 156 | 'class' => 'Red_Database_400', | ||
| 157 | ], | ||
| 158 | [ | ||
| 159 | 'version' => '4.1', | ||
| 160 | 'file' => '410.php', | ||
| 161 | 'class' => 'Red_Database_410', | ||
| 162 | ], | ||
| 163 | [ | ||
| 164 | 'version' => '4.2', | ||
| 165 | 'file' => '420.php', | ||
| 166 | 'class' => 'Red_Database_420', | ||
| 167 | ], | ||
| 168 | ]; | ||
| 169 | } | ||
| 170 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | // Note: not localised as the messages aren't important enough | ||
| 4 | class Red_Database_201 extends Red_Database_Upgrader { | ||
| 5 | public function get_stages() { | ||
| 6 | return [ | ||
| 7 | 'add_title_201' => 'Add titles to redirects', | ||
| 8 | ]; | ||
| 9 | } | ||
| 10 | |||
| 11 | protected function add_title_201( $wpdb ) { | ||
| 12 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD `title` varchar(50) NULL" ); | ||
| 13 | } | ||
| 14 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | // Note: not localised as the messages aren't important enough | ||
| 4 | class Red_Database_216 extends Red_Database_Upgrader { | ||
| 5 | public function get_stages() { | ||
| 6 | return [ | ||
| 7 | 'add_group_indices_216' => 'Add indices to groups', | ||
| 8 | 'add_redirect_indices_216' => 'Add indices to redirects', | ||
| 9 | ]; | ||
| 10 | } | ||
| 11 | |||
| 12 | protected function add_group_indices_216( $wpdb ) { | ||
| 13 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_groups` ADD INDEX(module_id)" ); | ||
| 14 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_groups` ADD INDEX(status)" ); | ||
| 15 | |||
| 16 | return true; | ||
| 17 | } | ||
| 18 | |||
| 19 | protected function add_redirect_indices_216( $wpdb ) { | ||
| 20 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX(url(191))" ); | ||
| 21 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX(status)" ); | ||
| 22 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX(regex)" ); | ||
| 23 | |||
| 24 | return true; | ||
| 25 | } | ||
| 26 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | // Note: not localised as the messages aren't important enough | ||
| 4 | class Red_Database_220 extends Red_Database_Upgrader { | ||
| 5 | public function get_stages() { | ||
| 6 | return [ | ||
| 7 | 'add_group_indices_220' => 'Add group indices to redirects', | ||
| 8 | 'add_log_indices_220' => 'Add indices to logs', | ||
| 9 | ]; | ||
| 10 | } | ||
| 11 | |||
| 12 | protected function add_group_indices_220( $wpdb ) { | ||
| 13 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX `group_idpos` (`group_id`,`position`)" ); | ||
| 14 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX `group` (`group_id`)" ); | ||
| 15 | return true; | ||
| 16 | } | ||
| 17 | |||
| 18 | protected function add_log_indices_220( $wpdb ) { | ||
| 19 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `created` (`created`)" ); | ||
| 20 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `redirection_id` (`redirection_id`)" ); | ||
| 21 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `ip` (`ip`)" ); | ||
| 22 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `group_id` (`group_id`)" ); | ||
| 23 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `module_id` (`module_id`)" ); | ||
| 24 | return true; | ||
| 25 | } | ||
| 26 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | // Note: not localised as the messages aren't important enough | ||
| 4 | class Red_Database_231 extends Red_Database_Upgrader { | ||
| 5 | public function get_stages() { | ||
| 6 | return [ | ||
| 7 | 'remove_404_module_231' => 'Remove 404 module', | ||
| 8 | 'create_404_table_231' => 'Create 404 table', | ||
| 9 | ]; | ||
| 10 | } | ||
| 11 | |||
| 12 | protected function remove_404_module_231( $wpdb ) { | ||
| 13 | return $this->do_query( $wpdb, "UPDATE {$wpdb->prefix}redirection_groups SET module_id=1 WHERE module_id=3" ); | ||
| 14 | } | ||
| 15 | |||
| 16 | protected function create_404_table_231( $wpdb ) { | ||
| 17 | $this->do_query( $wpdb, $this->get_404_table( $wpdb ) ); | ||
| 18 | } | ||
| 19 | |||
| 20 | private function get_404_table( $wpdb ) { | ||
| 21 | $charset_collate = $this->get_charset(); | ||
| 22 | |||
| 23 | return "CREATE TABLE `{$wpdb->prefix}redirection_404` ( | ||
| 24 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, | ||
| 25 | `created` datetime NOT NULL, | ||
| 26 | `url` varchar(255) NOT NULL DEFAULT '', | ||
| 27 | `agent` varchar(255) DEFAULT NULL, | ||
| 28 | `referrer` varchar(255) DEFAULT NULL, | ||
| 29 | `ip` int(10) unsigned NOT NULL, | ||
| 30 | PRIMARY KEY (`id`), | ||
| 31 | KEY `created` (`created`), | ||
| 32 | KEY `url` (`url`(191)), | ||
| 33 | KEY `ip` (`ip`), | ||
| 34 | KEY `referrer` (`referrer`(191)) | ||
| 35 | ) $charset_collate"; | ||
| 36 | } | ||
| 37 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | // Note: not localised as the messages aren't important enough | ||
| 4 | class Red_Database_232 extends Red_Database_Upgrader { | ||
| 5 | public function get_stages() { | ||
| 6 | return [ | ||
| 7 | 'remove_modules_232' => 'Remove module table', | ||
| 8 | ]; | ||
| 9 | } | ||
| 10 | |||
| 11 | protected function remove_modules_232( $wpdb ) { | ||
| 12 | $this->do_query( $wpdb, "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_modules" ); | ||
| 13 | return true; | ||
| 14 | } | ||
| 15 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | // Note: not localised as the messages aren't important enough | ||
| 4 | class Red_Database_233 extends Red_Database_Upgrader { | ||
| 5 | public function get_stages() { | ||
| 6 | return [ | ||
| 7 | 'fix_invalid_groups_233' => 'Migrate any groups with invalid module ID', | ||
| 8 | ]; | ||
| 9 | } | ||
| 10 | |||
| 11 | protected function fix_invalid_groups_233( $wpdb ) { | ||
| 12 | $this->do_query( $wpdb, "UPDATE {$wpdb->prefix}redirection_groups SET module_id=1 WHERE module_id > 2" ); | ||
| 13 | |||
| 14 | $latest = Red_Database::get_latest_database(); | ||
| 15 | return $latest->create_groups( $wpdb ); | ||
| 16 | } | ||
| 17 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * There are several problems with 2.3.3 => 2.4 that this attempts to cope with: | ||
| 5 | * - some sites have a misconfigured IP column | ||
| 6 | * - some sites don't have any IP column | ||
| 7 | */ | ||
| 8 | class Red_Database_240 extends Red_Database_Upgrader { | ||
| 9 | public function get_stages() { | ||
| 10 | return [ | ||
| 11 | 'convert_int_ip_to_varchar_240' => 'Convert integer IP values to support IPv6', | ||
| 12 | 'expand_log_ip_column_240' => 'Expand IP size in logs to support IPv6', | ||
| 13 | 'convert_title_to_text_240' => 'Expand size of redirect titles', | ||
| 14 | 'add_missing_index_240' => 'Add missing IP index to 404 logs', | ||
| 15 | ]; | ||
| 16 | } | ||
| 17 | |||
| 18 | private function has_ip_index( $wpdb ) { | ||
| 19 | $wpdb->hide_errors(); | ||
| 20 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_404`", ARRAY_N ); | ||
| 21 | $wpdb->show_errors(); | ||
| 22 | |||
| 23 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'key `ip` (' ) !== false ) { | ||
| 24 | return true; | ||
| 25 | } | ||
| 26 | |||
| 27 | return false; | ||
| 28 | } | ||
| 29 | |||
| 30 | protected function has_varchar_ip( $wpdb ) { | ||
| 31 | $wpdb->hide_errors(); | ||
| 32 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_404`", ARRAY_N ); | ||
| 33 | $wpdb->show_errors(); | ||
| 34 | |||
| 35 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), '`ip` varchar(45)' ) !== false ) { | ||
| 36 | return true; | ||
| 37 | } | ||
| 38 | |||
| 39 | return false; | ||
| 40 | } | ||
| 41 | |||
| 42 | protected function has_int_ip( $wpdb ) { | ||
| 43 | $wpdb->hide_errors(); | ||
| 44 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_404`", ARRAY_N ); | ||
| 45 | $wpdb->show_errors(); | ||
| 46 | |||
| 47 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), '`ip` int' ) !== false ) { | ||
| 48 | return true; | ||
| 49 | } | ||
| 50 | |||
| 51 | return false; | ||
| 52 | } | ||
| 53 | |||
| 54 | protected function convert_int_ip_to_varchar_240( $wpdb ) { | ||
| 55 | if ( $this->has_int_ip( $wpdb ) ) { | ||
| 56 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `ipaddress` VARCHAR(45) DEFAULT NULL AFTER `ip`" ); | ||
| 57 | $this->do_query( $wpdb, "UPDATE {$wpdb->prefix}redirection_404 SET ipaddress=INET_NTOA(ip)" ); | ||
| 58 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` DROP `ip`" ); | ||
| 59 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` CHANGE `ipaddress` `ip` VARCHAR(45) DEFAULT NULL" ); | ||
| 60 | } | ||
| 61 | |||
| 62 | return true; | ||
| 63 | } | ||
| 64 | |||
| 65 | protected function expand_log_ip_column_240( $wpdb ) { | ||
| 66 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` CHANGE `ip` `ip` VARCHAR(45) DEFAULT NULL" ); | ||
| 67 | } | ||
| 68 | |||
| 69 | protected function add_missing_index_240( $wpdb ) { | ||
| 70 | if ( $this->has_ip_index( $wpdb ) ) { | ||
| 71 | // Remove index | ||
| 72 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` DROP INDEX ip" ); | ||
| 73 | } | ||
| 74 | |||
| 75 | // Ensure we have an IP column | ||
| 76 | $this->convert_int_ip_to_varchar_240( $wpdb ); | ||
| 77 | if ( ! $this->has_varchar_ip( $wpdb ) ) { | ||
| 78 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `ip` VARCHAR(45) DEFAULT NULL" ); | ||
| 79 | } | ||
| 80 | |||
| 81 | // Finally add the index | ||
| 82 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD INDEX `ip` (`ip`)" ); | ||
| 83 | } | ||
| 84 | |||
| 85 | protected function convert_title_to_text_240( $wpdb ) { | ||
| 86 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` CHANGE `title` `title` text" ); | ||
| 87 | } | ||
| 88 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | class Red_Database_400 extends Red_Database_Upgrader { | ||
| 4 | public function get_stages() { | ||
| 5 | return [ | ||
| 6 | 'add_match_url_400' => 'Add a matched URL column', | ||
| 7 | 'add_match_url_index' => 'Add match URL index', | ||
| 8 | 'add_redirect_data_400' => 'Add column to store new flags', | ||
| 9 | 'convert_existing_urls_400' => 'Convert existing URLs to new format', | ||
| 10 | ]; | ||
| 11 | } | ||
| 12 | |||
| 13 | private function has_column( $wpdb, $column ) { | ||
| 14 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_items`", ARRAY_N ); | ||
| 15 | |||
| 16 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), strtolower( $column ) ) !== false ) { | ||
| 17 | return true; | ||
| 18 | } | ||
| 19 | |||
| 20 | return false; | ||
| 21 | } | ||
| 22 | |||
| 23 | private function has_match_index( $wpdb ) { | ||
| 24 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_items`", ARRAY_N ); | ||
| 25 | |||
| 26 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'key `match_url' ) !== false ) { | ||
| 27 | return true; | ||
| 28 | } | ||
| 29 | |||
| 30 | return false; | ||
| 31 | } | ||
| 32 | |||
| 33 | protected function add_match_url_400( $wpdb ) { | ||
| 34 | if ( ! $this->has_column( $wpdb, '`match_url` varchar(2000)' ) ) { | ||
| 35 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD `match_url` VARCHAR(2000) NULL DEFAULT NULL AFTER `url`" ); | ||
| 36 | } | ||
| 37 | |||
| 38 | return true; | ||
| 39 | } | ||
| 40 | |||
| 41 | protected function add_match_url_index( $wpdb ) { | ||
| 42 | if ( ! $this->has_match_index( $wpdb ) ) { | ||
| 43 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX `match_url` (`match_url`(191))" ); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | protected function add_redirect_data_400( $wpdb ) { | ||
| 48 | if ( ! $this->has_column( $wpdb, '`match_data` TEXT' ) ) { | ||
| 49 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD `match_data` TEXT NULL DEFAULT NULL AFTER `match_url`" ); | ||
| 50 | } | ||
| 51 | |||
| 52 | return true; | ||
| 53 | } | ||
| 54 | |||
| 55 | protected function convert_existing_urls_400( $wpdb ) { | ||
| 56 | // All regex get match_url=regex | ||
| 57 | $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url='regex' WHERE regex=1" ); | ||
| 58 | |||
| 59 | // Remove query part from all URLs and lowercase | ||
| 60 | $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url=LOWER(url) WHERE regex=0" ); | ||
| 61 | |||
| 62 | // Set exact match if query param present | ||
| 63 | $this->do_query( $wpdb, $wpdb->prepare( "UPDATE `{$wpdb->prefix}redirection_items` SET match_data=%s WHERE regex=0 AND match_url LIKE '%?%'", '{"source":{"flag_query":"exactorder"}}' ) ); | ||
| 64 | |||
| 65 | // Trim the last / from a URL | ||
| 66 | $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url=LEFT(match_url,LENGTH(match_url)-1) WHERE regex=0 AND match_url != '/' AND RIGHT(match_url, 1) = '/'" ); | ||
| 67 | $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url=REPLACE(match_url, '/?', '?') WHERE regex=0" ); | ||
| 68 | |||
| 69 | // Any URL that is now empty becomes / | ||
| 70 | return $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url='/' WHERE match_url=''" ); | ||
| 71 | } | ||
| 72 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | class Red_Database_410 extends Red_Database_Upgrader { | ||
| 4 | public function get_stages() { | ||
| 5 | return [ | ||
| 6 | 'handle_double_slash' => 'Support double-slash URLs', | ||
| 7 | ]; | ||
| 8 | } | ||
| 9 | |||
| 10 | protected function handle_double_slash( $wpdb ) { | ||
| 11 | // Update any URL with a double slash at the end | ||
| 12 | $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url=LOWER(LEFT(SUBSTRING_INDEX(url, '?', 1),LENGTH(SUBSTRING_INDEX(url, '?', 1)) - 1)) WHERE RIGHT(SUBSTRING_INDEX(url, '?', 1), 2) = '//' AND regex=0" ); | ||
| 13 | |||
| 14 | // Any URL that is now empty becomes / | ||
| 15 | return $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url='/' WHERE match_url=''" ); | ||
| 16 | } | ||
| 17 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | class Red_Database_420 extends Red_Database_Upgrader { | ||
| 4 | public function get_stages() { | ||
| 5 | return [ | ||
| 6 | 'add_extra_logging' => 'Add extra logging support', | ||
| 7 | 'remove_module_id' => 'Remove module ID from logs', | ||
| 8 | 'remove_group_id' => 'Remove group ID from logs', | ||
| 9 | 'add_extra_404' => 'Add extra 404 logging support', | ||
| 10 | ]; | ||
| 11 | } | ||
| 12 | |||
| 13 | protected function remove_module_id( $wpdb ) { | ||
| 14 | if ( ! $this->has_module_id( $wpdb ) ) { | ||
| 15 | return true; | ||
| 16 | } | ||
| 17 | |||
| 18 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` DROP `module_id`" ); | ||
| 19 | } | ||
| 20 | |||
| 21 | protected function remove_group_id( $wpdb ) { | ||
| 22 | if ( ! $this->has_group_id( $wpdb ) ) { | ||
| 23 | return true; | ||
| 24 | } | ||
| 25 | |||
| 26 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` DROP `group_id`" ); | ||
| 27 | } | ||
| 28 | |||
| 29 | private function has_module_id( $wpdb ) { | ||
| 30 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_logs`", ARRAY_N ); | ||
| 31 | |||
| 32 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'module_id' ) !== false ) { | ||
| 33 | return true; | ||
| 34 | } | ||
| 35 | |||
| 36 | return false; | ||
| 37 | } | ||
| 38 | |||
| 39 | private function has_group_id( $wpdb ) { | ||
| 40 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_logs`", ARRAY_N ); | ||
| 41 | |||
| 42 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'group_id' ) !== false ) { | ||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | return false; | ||
| 47 | } | ||
| 48 | |||
| 49 | private function has_log_domain( $wpdb ) { | ||
| 50 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_logs`", ARRAY_N ); | ||
| 51 | |||
| 52 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'domain` varchar' ) !== false ) { | ||
| 53 | return true; | ||
| 54 | } | ||
| 55 | |||
| 56 | return false; | ||
| 57 | } | ||
| 58 | |||
| 59 | private function has_404_domain( $wpdb ) { | ||
| 60 | $existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_404`", ARRAY_N ); | ||
| 61 | |||
| 62 | if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'domain` varchar' ) !== false ) { | ||
| 63 | return true; | ||
| 64 | } | ||
| 65 | |||
| 66 | return false; | ||
| 67 | } | ||
| 68 | |||
| 69 | protected function add_extra_logging( $wpdb ) { | ||
| 70 | if ( $this->has_log_domain( $wpdb ) ) { | ||
| 71 | return true; | ||
| 72 | } | ||
| 73 | |||
| 74 | // Update any URL with a double slash at the end | ||
| 75 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `domain` VARCHAR(255) NULL DEFAULT NULL AFTER `url`" ); | ||
| 76 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `http_code` INT(11) unsigned NOT NULL DEFAULT 0 AFTER `referrer`" ); | ||
| 77 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `request_method` VARCHAR(10) NULL DEFAULT NULL AFTER `http_code`" ); | ||
| 78 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `redirect_by` VARCHAR(50) NULL DEFAULT NULL AFTER `request_method`" ); | ||
| 79 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `request_data` MEDIUMTEXT NULL DEFAULT NULL AFTER `request_method`" ); | ||
| 80 | |||
| 81 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` CHANGE COLUMN `agent` `agent` MEDIUMTEXT NULL" ); | ||
| 82 | } | ||
| 83 | |||
| 84 | protected function add_extra_404( $wpdb ) { | ||
| 85 | if ( $this->has_404_domain( $wpdb ) ) { | ||
| 86 | return true; | ||
| 87 | } | ||
| 88 | |||
| 89 | // Update any URL with a double slash at the end | ||
| 90 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `domain` VARCHAR(255) NULL DEFAULT NULL AFTER `url`" ); | ||
| 91 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `http_code` INT(11) unsigned NOT NULL DEFAULT 0 AFTER `referrer`" ); | ||
| 92 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `request_method` VARCHAR(10) NULL DEFAULT NULL AFTER `http_code`" ); | ||
| 93 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `request_data` MEDIUMTEXT NULL DEFAULT NULL AFTER `request_method`" ); | ||
| 94 | |||
| 95 | // Same as log table | ||
| 96 | $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` DROP INDEX `url`" ); | ||
| 97 | return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` CHANGE COLUMN `url` `url` MEDIUMTEXT NOT NULL" ); | ||
| 98 | } | ||
| 99 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | /** | ||
| 4 | * Latest database schema | ||
| 5 | */ | ||
| 6 | class Red_Latest_Database extends Red_Database_Upgrader { | ||
| 7 | public function get_stages() { | ||
| 8 | return [ | ||
| 9 | /* translators: displayed when installing the plugin */ | ||
| 10 | 'create_tables' => __( 'Install Redirection tables', 'redirection' ), | ||
| 11 | /* translators: displayed when installing the plugin */ | ||
| 12 | 'create_groups' => __( 'Create basic data', 'redirection' ), | ||
| 13 | ]; | ||
| 14 | } | ||
| 15 | |||
| 16 | /** | ||
| 17 | * Install the latest database | ||
| 18 | * | ||
| 19 | * @return bool|WP_Error true if installed, WP_Error otherwise | ||
| 20 | */ | ||
| 21 | public function install() { | ||
| 22 | global $wpdb; | ||
| 23 | |||
| 24 | foreach ( $this->get_stages() as $stage => $info ) { | ||
| 25 | $result = $this->$stage( $wpdb ); | ||
| 26 | |||
| 27 | if ( is_wp_error( $result ) ) { | ||
| 28 | if ( $wpdb->last_error ) { | ||
| 29 | $result->add_data( $wpdb->last_error ); | ||
| 30 | } | ||
| 31 | |||
| 32 | return $result; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | red_set_options( array( 'database' => REDIRECTION_DB_VERSION ) ); | ||
| 37 | return true; | ||
| 38 | } | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Remove the database and any options (including unused ones) | ||
| 42 | */ | ||
| 43 | public function remove() { | ||
| 44 | global $wpdb; | ||
| 45 | |||
| 46 | $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_items" ); | ||
| 47 | $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_logs" ); | ||
| 48 | $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_groups" ); | ||
| 49 | $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_modules" ); | ||
| 50 | $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_404" ); | ||
| 51 | |||
| 52 | delete_option( 'redirection_lookup' ); | ||
| 53 | delete_option( 'redirection_post' ); | ||
| 54 | delete_option( 'redirection_root' ); | ||
| 55 | delete_option( 'redirection_index' ); | ||
| 56 | delete_option( 'redirection_options' ); | ||
| 57 | delete_option( Red_Database_Status::OLD_DB_VERSION ); | ||
| 58 | } | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Return any tables that are missing from the database | ||
| 62 | * | ||
| 63 | * @return array Array of missing table names | ||
| 64 | */ | ||
| 65 | public function get_missing_tables() { | ||
| 66 | global $wpdb; | ||
| 67 | |||
| 68 | $tables = array_keys( $this->get_all_tables() ); | ||
| 69 | $missing = []; | ||
| 70 | |||
| 71 | foreach ( $tables as $table ) { | ||
| 72 | $result = $wpdb->query( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ); | ||
| 73 | |||
| 74 | if ( intval( $result, 10 ) !== 1 ) { | ||
| 75 | $missing[] = $table; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | return $missing; | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Get table schema for latest database tables | ||
| 84 | * | ||
| 85 | * @return array Database schema array | ||
| 86 | */ | ||
| 87 | public function get_table_schema() { | ||
| 88 | global $wpdb; | ||
| 89 | |||
| 90 | $tables = array_keys( $this->get_all_tables() ); | ||
| 91 | $show = array(); | ||
| 92 | |||
| 93 | foreach ( $tables as $table ) { | ||
| 94 | // These are known queries without user input | ||
| 95 | // phpcs:ignore | ||
| 96 | $row = $wpdb->get_row( 'SHOW CREATE TABLE ' . $table, ARRAY_N ); | ||
| 97 | |||
| 98 | if ( $row ) { | ||
| 99 | $show = array_merge( $show, explode( "\n", $row[1] ) ); | ||
| 100 | $show[] = ''; | ||
| 101 | } else { | ||
| 102 | /* translators: 1: table name */ | ||
| 103 | $show[] = sprintf( __( 'Table "%s" is missing', 'redirection' ), $table ); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | return $show; | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Return array of table names and table schema | ||
| 112 | * | ||
| 113 | * @return array | ||
| 114 | */ | ||
| 115 | public function get_all_tables() { | ||
| 116 | global $wpdb; | ||
| 117 | |||
| 118 | $charset_collate = $this->get_charset(); | ||
| 119 | |||
| 120 | return array( | ||
| 121 | "{$wpdb->prefix}redirection_items" => $this->create_items_sql( $wpdb->prefix, $charset_collate ), | ||
| 122 | "{$wpdb->prefix}redirection_groups" => $this->create_groups_sql( $wpdb->prefix, $charset_collate ), | ||
| 123 | "{$wpdb->prefix}redirection_logs" => $this->create_log_sql( $wpdb->prefix, $charset_collate ), | ||
| 124 | "{$wpdb->prefix}redirection_404" => $this->create_404_sql( $wpdb->prefix, $charset_collate ), | ||
| 125 | ); | ||
| 126 | } | ||
| 127 | |||
| 128 | /** | ||
| 129 | * Creates default group information | ||
| 130 | */ | ||
| 131 | public function create_groups( $wpdb, $is_live = true ) { | ||
| 132 | if ( ! $is_live ) { | ||
| 133 | return true; | ||
| 134 | } | ||
| 135 | |||
| 136 | $defaults = [ | ||
| 137 | [ | ||
| 138 | 'name' => __( 'Redirections', 'redirection' ), | ||
| 139 | 'module_id' => 1, | ||
| 140 | 'position' => 0, | ||
| 141 | ], | ||
| 142 | [ | ||
| 143 | 'name' => __( 'Modified Posts', 'redirection' ), | ||
| 144 | 'module_id' => 1, | ||
| 145 | 'position' => 1, | ||
| 146 | ], | ||
| 147 | ]; | ||
| 148 | |||
| 149 | $existing_groups = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_groups" ); | ||
| 150 | |||
| 151 | // Default groups | ||
| 152 | if ( intval( $existing_groups, 10 ) === 0 ) { | ||
| 153 | $wpdb->insert( $wpdb->prefix . 'redirection_groups', $defaults[0] ); | ||
| 154 | $wpdb->insert( $wpdb->prefix . 'redirection_groups', $defaults[1] ); | ||
| 155 | } | ||
| 156 | |||
| 157 | $group = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}redirection_groups LIMIT 1" ); | ||
| 158 | if ( $group ) { | ||
| 159 | red_set_options( array( 'last_group_id' => $group->id ) ); | ||
| 160 | } | ||
| 161 | |||
| 162 | return true; | ||
| 163 | } | ||
| 164 | |||
| 165 | /** | ||
| 166 | * Creates all the tables | ||
| 167 | */ | ||
| 168 | public function create_tables( $wpdb ) { | ||
| 169 | global $wpdb; | ||
| 170 | |||
| 171 | foreach ( $this->get_all_tables() as $table => $sql ) { | ||
| 172 | $sql = preg_replace( '/[ \t]{2,}/', '', $sql ); | ||
| 173 | $this->do_query( $wpdb, $sql ); | ||
| 174 | } | ||
| 175 | |||
| 176 | return true; | ||
| 177 | } | ||
| 178 | |||
| 179 | private function create_items_sql( $prefix, $charset_collate ) { | ||
| 180 | return "CREATE TABLE IF NOT EXISTS `{$prefix}redirection_items` ( | ||
| 181 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, | ||
| 182 | `url` mediumtext NOT NULL, | ||
| 183 | `match_url` VARCHAR(2000) DEFAULT NULL, | ||
| 184 | `match_data` TEXT, | ||
| 185 | `regex` INT(11) unsigned NOT NULL DEFAULT '0', | ||
| 186 | `position` INT(11) unsigned NOT NULL DEFAULT '0', | ||
| 187 | `last_count` INT(10) unsigned NOT NULL DEFAULT '0', | ||
| 188 | `last_access` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', | ||
| 189 | `group_id` INT(11) NOT NULL DEFAULT '0', | ||
| 190 | `status` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', | ||
| 191 | `action_type` VARCHAR(20) NOT NULL, | ||
| 192 | `action_code` INT(11) unsigned NOT NULL, | ||
| 193 | `action_data` MEDIUMTEXT, | ||
| 194 | `match_type` VARCHAR(20) NOT NULL, | ||
| 195 | `title` TEXT, | ||
| 196 | PRIMARY KEY (`id`), | ||
| 197 | KEY `url` (`url`(191)), | ||
| 198 | KEY `status` (`status`), | ||
| 199 | KEY `regex` (`regex`), | ||
| 200 | KEY `group_idpos` (`group_id`,`position`), | ||
| 201 | KEY `group` (`group_id`), | ||
| 202 | KEY `match_url` (`match_url`(191)) | ||
| 203 | ) $charset_collate"; | ||
| 204 | } | ||
| 205 | |||
| 206 | private function create_groups_sql( $prefix, $charset_collate ) { | ||
| 207 | return "CREATE TABLE IF NOT EXISTS `{$prefix}redirection_groups` ( | ||
| 208 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, | ||
| 209 | `name` VARCHAR(50) NOT NULL, | ||
| 210 | `tracking` INT(11) NOT NULL DEFAULT '1', | ||
| 211 | `module_id` INT(11) unsigned NOT NULL DEFAULT '0', | ||
| 212 | `status` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', | ||
| 213 | `position` INT(11) unsigned NOT NULL DEFAULT '0', | ||
| 214 | PRIMARY KEY (`id`), | ||
| 215 | KEY `module_id` (`module_id`), | ||
| 216 | KEY `status` (`status`) | ||
| 217 | ) $charset_collate"; | ||
| 218 | } | ||
| 219 | |||
| 220 | private function create_log_sql( $prefix, $charset_collate ) { | ||
| 221 | return "CREATE TABLE IF NOT EXISTS `{$prefix}redirection_logs` ( | ||
| 222 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, | ||
| 223 | `created` datetime NOT NULL, | ||
| 224 | `url` MEDIUMTEXT NOT NULL, | ||
| 225 | `domain` VARCHAR(255) DEFAULT NULL, | ||
| 226 | `sent_to` MEDIUMTEXT, | ||
| 227 | `agent` MEDIUMTEXT, | ||
| 228 | `referrer` MEDIUMTEXT, | ||
| 229 | `http_code` INT(11) unsigned NOT NULL DEFAULT '0', | ||
| 230 | `request_method` VARCHAR(10) DEFAULT NULL, | ||
| 231 | `request_data` MEDIUMTEXT, | ||
| 232 | `redirect_by` VARCHAR(50) DEFAULT NULL, | ||
| 233 | `redirection_id` INT(11) unsigned DEFAULT NULL, | ||
| 234 | `ip` VARCHAR(45) DEFAULT NULL, | ||
| 235 | PRIMARY KEY (`id`), | ||
| 236 | KEY `created` (`created`), | ||
| 237 | KEY `redirection_id` (`redirection_id`), | ||
| 238 | KEY `ip` (`ip`) | ||
| 239 | ) $charset_collate"; | ||
| 240 | } | ||
| 241 | |||
| 242 | private function create_404_sql( $prefix, $charset_collate ) { | ||
| 243 | return "CREATE TABLE IF NOT EXISTS `{$prefix}redirection_404` ( | ||
| 244 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, | ||
| 245 | `created` datetime NOT NULL, | ||
| 246 | `url` MEDIUMTEXT NOT NULL, | ||
| 247 | `domain` VARCHAR(255) DEFAULT NULL, | ||
| 248 | `agent` VARCHAR(255) DEFAULT NULL, | ||
| 249 | `referrer` VARCHAR(255) DEFAULT NULL, | ||
| 250 | `http_code` INT(11) unsigned NOT NULL DEFAULT '0', | ||
| 251 | `request_method` VARCHAR(10) DEFAULT NULL, | ||
| 252 | `request_data` MEDIUMTEXT, | ||
| 253 | `ip` VARCHAR(45) DEFAULT NULL, | ||
| 254 | PRIMARY KEY (`id`), | ||
| 255 | KEY `created` (`created`), | ||
| 256 | KEY `referrer` (`referrer`(191)), | ||
| 257 | KEY `ip` (`ip`) | ||
| 258 | ) $charset_collate"; | ||
| 259 | } | ||
| 260 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | class Red_Apache_File extends Red_FileIO { | ||
| 4 | public function force_download() { | ||
| 5 | parent::force_download(); | ||
| 6 | |||
| 7 | header( 'Content-Type: application/octet-stream' ); | ||
| 8 | header( 'Content-Disposition: attachment; filename="' . $this->export_filename( 'htaccess' ) . '"' ); | ||
| 9 | } | ||
| 10 | |||
| 11 | public function get_data( array $items, array $groups ) { | ||
| 12 | include_once dirname( dirname( __FILE__ ) ) . '/models/htaccess.php'; | ||
| 13 | |||
| 14 | $htaccess = new Red_Htaccess(); | ||
| 15 | |||
| 16 | foreach ( $items as $item ) { | ||
| 17 | $htaccess->add( $item ); | ||
| 18 | } | ||
| 19 | |||
| 20 | return $htaccess->get() . PHP_EOL; | ||
| 21 | } | ||
| 22 | |||
| 23 | public function load( $group, $filename, $data ) { | ||
| 24 | // Remove any comments | ||
| 25 | $data = str_replace( "\n", "\r", $data ); | ||
| 26 | |||
| 27 | // Split it into lines | ||
| 28 | $lines = array_filter( explode( "\r", $data ) ); | ||
| 29 | $count = 0; | ||
| 30 | |||
| 31 | foreach ( (array) $lines as $line ) { | ||
| 32 | $item = $this->get_as_item( $line ); | ||
| 33 | |||
| 34 | if ( $item ) { | ||
| 35 | $item['group_id'] = $group; | ||
| 36 | $redirect = Red_Item::create( $item ); | ||
| 37 | |||
| 38 | if ( ! is_wp_error( $redirect ) ) { | ||
| 39 | $count++; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | return $count; | ||
| 45 | } | ||
| 46 | |||
| 47 | public function get_as_item( $line ) { | ||
| 48 | $item = false; | ||
| 49 | |||
| 50 | if ( preg_match( '@rewriterule\s+(.*?)\s+(.*?)\s+(\[.*\])*@i', $line, $matches ) > 0 ) { | ||
| 51 | $item = array( | ||
| 52 | 'url' => $this->regex_url( $matches[1] ), | ||
| 53 | 'match_type' => 'url', | ||
| 54 | 'action_type' => 'url', | ||
| 55 | 'action_data' => array( 'url' => $this->decode_url( $matches[2] ) ), | ||
| 56 | 'action_code' => $this->get_code( $matches[3] ), | ||
| 57 | 'regex' => $this->is_regex( $matches[1] ), | ||
| 58 | ); | ||
| 59 | } elseif ( preg_match( '@Redirect\s+(.*?)\s+"(.*?)"\s+(.*)@i', $line, $matches ) > 0 || preg_match( '@Redirect\s+(.*?)\s+(.*?)\s+(.*)@i', $line, $matches ) > 0 ) { | ||
| 60 | $item = array( | ||
| 61 | 'url' => $this->decode_url( $matches[2] ), | ||
| 62 | 'match_type' => 'url', | ||
| 63 | 'action_type' => 'url', | ||
| 64 | 'action_data' => array( 'url' => $this->decode_url( $matches[3] ) ), | ||
| 65 | 'action_code' => $this->get_code( $matches[1] ), | ||
| 66 | ); | ||
| 67 | } elseif ( preg_match( '@Redirect\s+"(.*?)"\s+(.*)@i', $line, $matches ) > 0 || preg_match( '@Redirect\s+(.*?)\s+(.*)@i', $line, $matches ) > 0 ) { | ||
| 68 | $item = array( | ||
| 69 | 'url' => $this->decode_url( $matches[1] ), | ||
| 70 | 'match_type' => 'url', | ||
| 71 | 'action_type' => 'url', | ||
| 72 | 'action_data' => array( 'url' => $this->decode_url( $matches[2] ) ), | ||
| 73 | 'action_code' => 302, | ||
| 74 | ); | ||
| 75 | } elseif ( preg_match( '@Redirectmatch\s+(.*?)\s+(.*?)\s+(.*)@i', $line, $matches ) > 0 ) { | ||
| 76 | $item = array( | ||
| 77 | 'url' => $this->decode_url( $matches[2] ), | ||
| 78 | 'match_type' => 'url', | ||
| 79 | 'action_type' => 'url', | ||
| 80 | 'action_data' => array( 'url' => $this->decode_url( $matches[3] ) ), | ||
| 81 | 'action_code' => $this->get_code( $matches[1] ), | ||
| 82 | 'regex' => true, | ||
| 83 | ); | ||
| 84 | } elseif ( preg_match( '@Redirectmatch\s+(.*?)\s+(.*)@i', $line, $matches ) > 0 ) { | ||
| 85 | $item = array( | ||
| 86 | 'url' => $this->decode_url( $matches[1] ), | ||
| 87 | 'match_type' => 'url', | ||
| 88 | 'action_type' => 'url', | ||
| 89 | 'action_data' => array( 'url' => $this->decode_url( $matches[2] ) ), | ||
| 90 | 'action_code' => 302, | ||
| 91 | 'regex' => true, | ||
| 92 | ); | ||
| 93 | } | ||
| 94 | |||
| 95 | if ( $item ) { | ||
| 96 | $item['action_type'] = 'url'; | ||
| 97 | $item['match_type'] = 'url'; | ||
| 98 | |||
| 99 | if ( $item['action_code'] === 0 ) { | ||
| 100 | $item['action_type'] = 'pass'; | ||
| 101 | } | ||
| 102 | |||
| 103 | return $item; | ||
| 104 | } | ||
| 105 | |||
| 106 | return false; | ||
| 107 | } | ||
| 108 | |||
| 109 | private function decode_url( $url ) { | ||
| 110 | $url = rawurldecode( $url ); | ||
| 111 | |||
| 112 | // Replace quoted slashes | ||
| 113 | $url = preg_replace( '@\\\/@', '/', $url ); | ||
| 114 | |||
| 115 | // Ensure escaped '.' is still escaped | ||
| 116 | $url = preg_replace( '@\\\\.@', '\\\\.', $url ); | ||
| 117 | return $url; | ||
| 118 | } | ||
| 119 | |||
| 120 | private function is_str_regex( $url ) { | ||
| 121 | $regex = '()[]$^?+.'; | ||
| 122 | $escape = false; | ||
| 123 | $len = strlen( $url ); | ||
| 124 | |||
| 125 | for ( $x = 0; $x < $len; $x++ ) { | ||
| 126 | $escape = false; | ||
| 127 | $char = substr( $url, $x, 1 ); | ||
| 128 | |||
| 129 | if ( $char === '\\' ) { | ||
| 130 | $escape = true; | ||
| 131 | } elseif ( strpos( $regex, $char ) !== false && ! $escape ) { | ||
| 132 | return true; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | return false; | ||
| 137 | } | ||
| 138 | |||
| 139 | private function is_regex( $url ) { | ||
| 140 | if ( $this->is_str_regex( $url ) ) { | ||
| 141 | $tmp = ltrim( $url, '^' ); | ||
| 142 | $tmp = rtrim( $tmp, '$' ); | ||
| 143 | |||
| 144 | if ( $this->is_str_regex( $tmp ) ) { | ||
| 145 | return true; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | return false; | ||
| 150 | } | ||
| 151 | |||
| 152 | private function regex_url( $url ) { | ||
| 153 | $url = $this->decode_url( $url ); | ||
| 154 | |||
| 155 | if ( $this->is_str_regex( $url ) ) { | ||
| 156 | $tmp = ltrim( $url, '^' ); | ||
| 157 | $tmp = rtrim( $tmp, '$' ); | ||
| 158 | |||
| 159 | if ( $this->is_str_regex( $tmp ) ) { | ||
| 160 | return '^/' . ltrim( $tmp, '/' ); | ||
| 161 | } | ||
| 162 | |||
| 163 | return '/' . ltrim( $tmp, '/' ); | ||
| 164 | } | ||
| 165 | |||
| 166 | return $this->decode_url( $url ); | ||
| 167 | } | ||
| 168 | |||
| 169 | private function get_code( $code ) { | ||
| 170 | if ( strpos( $code, '301' ) !== false || stripos( $code, 'permanent' ) !== false ) { | ||
| 171 | return 301; | ||
| 172 | } | ||
| 173 | |||
| 174 | if ( strpos( $code, '302' ) !== false ) { | ||
| 175 | return 302; | ||
| 176 | } | ||
| 177 | |||
| 178 | if ( strpos( $code, '307' ) !== false || stripos( $code, 'seeother' ) !== false ) { | ||
| 179 | return 307; | ||
| 180 | } | ||
| 181 | |||
| 182 | if ( strpos( $code, '404' ) !== false || stripos( $code, 'forbidden' ) !== false || strpos( $code, 'F' ) !== false ) { | ||
| 183 | return 404; | ||
| 184 | } | ||
| 185 | |||
| 186 | if ( strpos( $code, '410' ) !== false || stripos( $code, 'gone' ) !== false || strpos( $code, 'G' ) !== false ) { | ||
| 187 | return 410; | ||
| 188 | } | ||
| 189 | |||
| 190 | return 302; | ||
| 191 | } | ||
| 192 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | class Red_Csv_File extends Red_FileIO { | ||
| 4 | const CSV_SOURCE = 0; | ||
| 5 | const CSV_TARGET = 1; | ||
| 6 | const CSV_REGEX = 2; | ||
| 7 | const CSV_CODE = 3; | ||
| 8 | |||
| 9 | public function force_download() { | ||
| 10 | parent::force_download(); | ||
| 11 | |||
| 12 | header( 'Content-Type: text/csv' ); | ||
| 13 | header( 'Content-Disposition: attachment; filename="' . $this->export_filename( 'csv' ) . '"' ); | ||
| 14 | } | ||
| 15 | |||
| 16 | public function get_data( array $items, array $groups ) { | ||
| 17 | $lines = [ implode( ',', array( 'source', 'target', 'regex', 'code', 'type', 'match', 'hits', 'title', 'status' ) ) ]; | ||
| 18 | |||
| 19 | foreach ( $items as $line ) { | ||
| 20 | $lines[] = $this->item_as_csv( $line ); | ||
| 21 | } | ||
| 22 | |||
| 23 | return implode( PHP_EOL, $lines ) . PHP_EOL; | ||
| 24 | } | ||
| 25 | |||
| 26 | public function item_as_csv( $item ) { | ||
| 27 | $data = $item->match->get_data(); | ||
| 28 | |||
| 29 | if ( isset( $data['url'] ) ) { | ||
| 30 | $data = $data['url']; | ||
| 31 | } else { | ||
| 32 | $data = '/unknown'; | ||
| 33 | } | ||
| 34 | |||
| 35 | if ( $item->get_action_code() > 400 && $item->get_action_code() < 500 ) { | ||
| 36 | $data = ''; | ||
| 37 | } | ||
| 38 | |||
| 39 | $csv = array( | ||
| 40 | $item->get_url(), | ||
| 41 | $data, | ||
| 42 | $item->is_regex() ? 1 : 0, | ||
| 43 | $item->get_action_code(), | ||
| 44 | $item->get_action_type(), | ||
| 45 | $item->get_hits(), | ||
| 46 | $item->get_title(), | ||
| 47 | $item->is_enabled() ? 'active' : 'disabled', | ||
| 48 | ); | ||
| 49 | |||
| 50 | $csv = array_map( array( $this, 'escape_csv' ), $csv ); | ||
| 51 | return implode( ',', $csv ); | ||
| 52 | } | ||
| 53 | |||
| 54 | public function escape_csv( $item ) { | ||
| 55 | if ( is_numeric( $item ) ) { | ||
| 56 | return $item; | ||
| 57 | } | ||
| 58 | |||
| 59 | return '"' . str_replace( '"', '""', $item ) . '"'; | ||
| 60 | } | ||
| 61 | |||
| 62 | public function load( $group, $filename, $data ) { | ||
| 63 | ini_set( 'auto_detect_line_endings', true ); | ||
| 64 | |||
| 65 | $file = fopen( $filename, 'r' ); | ||
| 66 | |||
| 67 | ini_set( 'auto_detect_line_endings', false ); | ||
| 68 | |||
| 69 | if ( $file ) { | ||
| 70 | $separators = [ | ||
| 71 | ',', | ||
| 72 | ';', | ||
| 73 | '|', | ||
| 74 | ]; | ||
| 75 | |||
| 76 | foreach ( $separators as $separator ) { | ||
| 77 | fseek( $file, 0 ); | ||
| 78 | $count = $this->load_from_file( $group, $file, $separator ); | ||
| 79 | |||
| 80 | if ( $count > 0 ) { | ||
| 81 | return $count; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | public function load_from_file( $group_id, $file, $separator ) { | ||
| 90 | global $wpdb; | ||
| 91 | |||
| 92 | $count = 0; | ||
| 93 | |||
| 94 | while ( ( $csv = fgetcsv( $file, 5000, $separator ) ) ) { | ||
| 95 | $item = $this->csv_as_item( $csv, $group_id ); | ||
| 96 | |||
| 97 | if ( $item && $this->item_is_valid( $item ) ) { | ||
| 98 | $created = Red_Item::create( $item ); | ||
| 99 | |||
| 100 | // The query log can use up all the memory | ||
| 101 | $wpdb->queries = []; | ||
| 102 | |||
| 103 | if ( ! is_wp_error( $created ) ) { | ||
| 104 | $count++; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | return $count; | ||
| 110 | } | ||
| 111 | |||
| 112 | private function item_is_valid( array $csv ) { | ||
| 113 | if ( strlen( $csv['url'] ) === 0 ) { | ||
| 114 | return false; | ||
| 115 | } | ||
| 116 | |||
| 117 | if ( $csv['action_data']['url'] === $csv['url'] ) { | ||
| 118 | return false; | ||
| 119 | } | ||
| 120 | |||
| 121 | return true; | ||
| 122 | } | ||
| 123 | |||
| 124 | private function get_valid_code( $code ) { | ||
| 125 | if ( get_status_header_desc( $code ) !== '' ) { | ||
| 126 | return intval( $code, 10 ); | ||
| 127 | } | ||
| 128 | |||
| 129 | return 301; | ||
| 130 | } | ||
| 131 | |||
| 132 | private function get_action_type( $code ) { | ||
| 133 | if ( $code > 400 && $code < 500 ) { | ||
| 134 | return 'error'; | ||
| 135 | } | ||
| 136 | |||
| 137 | return 'url'; | ||
| 138 | } | ||
| 139 | |||
| 140 | public function csv_as_item( $csv, $group ) { | ||
| 141 | if ( count( $csv ) > 1 && $csv[ self::CSV_SOURCE ] !== 'source' && $csv[ self::CSV_TARGET ] !== 'target' ) { | ||
| 142 | $code = isset( $csv[ self::CSV_CODE ] ) ? $this->get_valid_code( $csv[ self::CSV_CODE ] ) : 301; | ||
| 143 | |||
| 144 | return array( | ||
| 145 | 'url' => trim( $csv[ self::CSV_SOURCE ] ), | ||
| 146 | 'action_data' => array( 'url' => trim( $csv[ self::CSV_TARGET ] ) ), | ||
| 147 | 'regex' => isset( $csv[ self::CSV_REGEX ] ) ? $this->parse_regex( $csv[ self::CSV_REGEX ] ) : $this->is_regex( $csv[ self::CSV_SOURCE ] ), | ||
| 148 | 'group_id' => $group, | ||
| 149 | 'match_type' => 'url', | ||
| 150 | 'action_type' => $this->get_action_type( $code ), | ||
| 151 | 'action_code' => $code, | ||
| 152 | ); | ||
| 153 | } | ||
| 154 | |||
| 155 | return false; | ||
| 156 | } | ||
| 157 | |||
| 158 | private function parse_regex( $value ) { | ||
| 159 | return intval( $value, 10 ) === 1 ? true : false; | ||
| 160 | } | ||
| 161 | |||
| 162 | private function is_regex( $url ) { | ||
| 163 | $regex = '()[]$^*'; | ||
| 164 | |||
| 165 | if ( strpbrk( $url, $regex ) === false ) { | ||
| 166 | return false; | ||
| 167 | } | ||
| 168 | |||
| 169 | return true; | ||
| 170 | } | ||
| 171 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | class Red_Json_File extends Red_FileIO { | ||
| 4 | public function force_download() { | ||
| 5 | parent::force_download(); | ||
| 6 | |||
| 7 | header( 'Content-Type: application/json' ); | ||
| 8 | header( 'Content-Disposition: attachment; filename="' . $this->export_filename( 'json' ) . '"' ); | ||
| 9 | } | ||
| 10 | |||
| 11 | public function get_data( array $items, array $groups ) { | ||
| 12 | $version = red_get_plugin_data( dirname( dirname( __FILE__ ) ) . '/redirection.php' ); | ||
| 13 | |||
| 14 | $items = array( | ||
| 15 | 'plugin' => array( | ||
| 16 | 'version' => trim( $version['Version'] ), | ||
| 17 | 'date' => date( 'r' ), | ||
| 18 | ), | ||
| 19 | 'groups' => $groups, | ||
| 20 | 'redirects' => array_map( function( $item ) { | ||
| 21 | return $item->to_json(); | ||
| 22 | }, $items ), | ||
| 23 | ); | ||
| 24 | |||
| 25 | return wp_json_encode( $items, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) . PHP_EOL; | ||
| 26 | } | ||
| 27 | |||
| 28 | public function load( $group, $filename, $data ) { | ||
| 29 | global $wpdb; | ||
| 30 | |||
| 31 | $count = 0; | ||
| 32 | $json = @json_decode( $data, true ); | ||
| 33 | if ( $json === false ) { | ||
| 34 | return 0; | ||
| 35 | } | ||
| 36 | |||
| 37 | // Import groups | ||
| 38 | $groups = array(); | ||
| 39 | $group_map = array(); | ||
| 40 | |||
| 41 | if ( isset( $json['groups'] ) ) { | ||
| 42 | foreach ( $json['groups'] as $group ) { | ||
| 43 | $old_group_id = $group['id']; | ||
| 44 | unset( $group['id'] ); | ||
| 45 | |||
| 46 | $group = Red_Group::create( $group['name'], $group['module_id'], $group['enabled'] ? true : false ); | ||
| 47 | if ( $group ) { | ||
| 48 | $group_map[ $old_group_id ] = $group->get_id(); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | unset( $json['groups'] ); | ||
| 54 | |||
| 55 | // Import redirects | ||
| 56 | if ( isset( $json['redirects'] ) ) { | ||
| 57 | foreach ( $json['redirects'] as $pos => $redirect ) { | ||
| 58 | unset( $redirect['id'] ); | ||
| 59 | |||
| 60 | if ( ! isset( $group_map[ $redirect['group_id'] ] ) ) { | ||
| 61 | $new_group = Red_Group::create( 'Group', 1 ); | ||
| 62 | $group_map[ $redirect['group_id'] ] = $new_group->get_id(); | ||
| 63 | } | ||
| 64 | |||
| 65 | if ( $redirect['match_type'] === 'url' && isset( $redirect['action_data'] ) && ! is_array( $redirect['action_data'] ) ) { | ||
| 66 | $redirect['action_data'] = array( 'url' => $redirect['action_data'] ); | ||
| 67 | } | ||
| 68 | |||
| 69 | $redirect['group_id'] = $group_map[ $redirect['group_id'] ]; | ||
| 70 | Red_Item::create( $redirect ); | ||
| 71 | $count++; | ||
| 72 | |||
| 73 | // Helps reduce memory usage | ||
| 74 | unset( $json['redirects'][ $pos ] ); | ||
| 75 | $wpdb->queries = array(); | ||
| 76 | $wpdb->num_queries = 0; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | return $count; | ||
| 81 | } | ||
| 82 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | class Red_Nginx_File extends Red_FileIO { | ||
| 4 | public function force_download() { | ||
| 5 | parent::force_download(); | ||
| 6 | |||
| 7 | header( 'Content-Type: application/octet-stream' ); | ||
| 8 | header( 'Content-Disposition: attachment; filename="' . $this->export_filename( 'nginx' ) . '"' ); | ||
| 9 | } | ||
| 10 | |||
| 11 | public function get_data( array $items, array $groups ) { | ||
| 12 | $lines = array(); | ||
| 13 | $version = red_get_plugin_data( dirname( dirname( __FILE__ ) ) . '/redirection.php' ); | ||
| 14 | |||
| 15 | $lines[] = '# Created by Redirection'; | ||
| 16 | $lines[] = '# ' . date( 'r' ); | ||
| 17 | $lines[] = '# Redirection ' . trim( $version['Version'] ) . ' - https://redirection.me'; | ||
| 18 | $lines[] = ''; | ||
| 19 | $lines[] = 'server {'; | ||
| 20 | |||
| 21 | $parts = array(); | ||
| 22 | foreach ( $items as $item ) { | ||
| 23 | if ( $item->is_enabled() ) { | ||
| 24 | $parts[] = $this->get_nginx_item( $item ); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | $lines = array_merge( $lines, array_filter( $parts ) ); | ||
| 29 | |||
| 30 | $lines[] = '}'; | ||
| 31 | $lines[] = ''; | ||
| 32 | $lines[] = '# End of Redirection'; | ||
| 33 | |||
| 34 | return implode( PHP_EOL, $lines ) . PHP_EOL; | ||
| 35 | } | ||
| 36 | |||
| 37 | private function get_redirect_code( Red_Item $item ) { | ||
| 38 | if ( $item->get_action_code() === 301 ) { | ||
| 39 | return 'permanent'; | ||
| 40 | } | ||
| 41 | return 'redirect'; | ||
| 42 | } | ||
| 43 | |||
| 44 | public function load( $group, $data, $filename = '' ) { | ||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | private function get_nginx_item( Red_Item $item ) { | ||
| 49 | $target = 'add_' . $item->get_match_type(); | ||
| 50 | |||
| 51 | if ( method_exists( $this, $target ) ) { | ||
| 52 | return ' ' . $this->$target( $item, $item->get_match_data() ); | ||
| 53 | } | ||
| 54 | |||
| 55 | return false; | ||
| 56 | } | ||
| 57 | |||
| 58 | private function add_url( Red_Item $item, array $match_data ) { | ||
| 59 | return $this->get_redirect( $item->get_url(), $item->get_action_data(), $this->get_redirect_code( $item ), $match_data['source'], $item->source_flags->is_regex() ); | ||
| 60 | } | ||
| 61 | |||
| 62 | private function add_agent( Red_Item $item, array $match_data ) { | ||
| 63 | if ( $item->match->url_from ) { | ||
| 64 | $lines[] = 'if ( $http_user_agent ~* ^' . $item->match->user_agent . '$ ) {'; | ||
| 65 | $lines[] = ' ' . $this->get_redirect( $item->get_url(), $item->match->url_from, $this->get_redirect_code( $item ), $match_data['source'] ); | ||
| 66 | $lines[] = ' }'; | ||
| 67 | } | ||
| 68 | |||
| 69 | if ( $item->match->url_notfrom ) { | ||
| 70 | $lines[] = 'if ( $http_user_agent !~* ^' . $item->match->user_agent . '$ ) {'; | ||
| 71 | $lines[] = ' ' . $this->get_redirect( $item->get_url(), $item->match->url_notfrom, $this->get_redirect_code( $item ), $match_data['source'] ); | ||
| 72 | $lines[] = ' }'; | ||
| 73 | } | ||
| 74 | |||
| 75 | return implode( "\n", $lines ); | ||
| 76 | } | ||
| 77 | |||
| 78 | private function add_referrer( Red_Item $item, array $match_data ) { | ||
| 79 | if ( $item->match->url_from ) { | ||
| 80 | $lines[] = 'if ( $http_referer ~* ^' . $item->match->referrer . '$ ) {'; | ||
| 81 | $lines[] = ' ' . $this->get_redirect( $item->get_url(), $item->match->url_from, $this->get_redirect_code( $item ), $match_data['source'] ); | ||
| 82 | $lines[] = ' }'; | ||
| 83 | } | ||
| 84 | |||
| 85 | if ( $item->match->url_notfrom ) { | ||
| 86 | $lines[] = 'if ( $http_referer !~* ^' . $item->match->referrer . '$ ) {'; | ||
| 87 | $lines[] = ' ' . $this->get_redirect( $item->get_url(), $item->match->url_notfrom, $this->get_redirect_code( $item ), $match_data['source'] ); | ||
| 88 | $lines[] = ' }'; | ||
| 89 | } | ||
| 90 | |||
| 91 | return implode( "\n", $lines ); | ||
| 92 | } | ||
| 93 | |||
| 94 | private function get_redirect( $line, $target, $code, $source, $regex = false ) { | ||
| 95 | $line = ltrim( $line, '^' ); | ||
| 96 | $line = rtrim( $line, '$' ); | ||
| 97 | |||
| 98 | $source_url = new Red_Url_Encode( $line, $regex ); | ||
| 99 | $target_url = new Red_Url_Encode( $target ); | ||
| 100 | |||
| 101 | // Remove any existing start/end from a regex | ||
| 102 | $from = $source_url->get_as_source(); | ||
| 103 | $from = ltrim( $from, '^' ); | ||
| 104 | $from = rtrim( $from, '$' ); | ||
| 105 | |||
| 106 | if ( isset( $source['flag_case'] ) && $source['flag_case'] ) { | ||
| 107 | $from = '(?i)^' . $from; | ||
| 108 | } else { | ||
| 109 | $from = '^' . $from; | ||
| 110 | } | ||
| 111 | |||
| 112 | return 'rewrite ' . $from . '$ ' . $target_url->get_as_target() . ' ' . $code . ';'; | ||
| 113 | } | ||
| 114 | } |
| 1 | <?php | ||
| 2 | |||
| 3 | class Red_Rss_File extends Red_FileIO { | ||
| 4 | public function force_download() { | ||
| 5 | header( 'Content-type: text/xml; charset=' . get_option( 'blog_charset' ), true ); | ||
| 6 | } | ||
| 7 | |||
| 8 | public function get_data( array $items, array $groups ) { | ||
| 9 | $xml = '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?' . ">\r\n"; | ||
| 10 | ob_start(); | ||
| 11 | ?> | ||
| 12 | <rss version="2.0" | ||
| 13 | xmlns:content="http://purl.org/rss/1.0/modules/content/" | ||
| 14 | xmlns:wfw="http://wellformedweb.org/CommentAPI/" | ||
| 15 | xmlns:dc="http://purl.org/dc/elements/1.1/"> | ||
| 16 | <channel> | ||
| 17 | <title>Redirection - <?php bloginfo_rss( 'name' ); ?></title> | ||
| 18 | <link><?php esc_url( bloginfo_rss( 'url' ) ); ?></link> | ||
| 19 | <description><?php esc_html( bloginfo_rss( 'description' ) ); ?></description> | ||
| 20 | <pubDate><?php echo esc_html( mysql2date( 'D, d M Y H:i:s +0000', get_lastpostmodified( 'GMT' ), false ) ); ?></pubDate> | ||
| 21 | <generator> | ||
| 22 | <?php echo esc_html( 'http://wordpress.org/?v=' ); ?> | ||
| 23 | <?php bloginfo_rss( 'version' ); ?> | ||
| 24 | </generator> | ||
| 25 | <language><?php echo esc_html( get_option( 'rss_language' ) ); ?></language> | ||
| 26 | |||
| 27 | <?php foreach ( $items as $log ) : ?> | ||
| 28 | <item> | ||
| 29 | <title><?php echo esc_html( $log->get_url() ); ?></title> | ||
| 30 | <link><![CDATA[<?php echo esc_url( home_url() ) . esc_url( $log->get_url() ); ?>]]></link> | ||
| 31 | <pubDate><?php echo esc_html( date( 'D, d M Y H:i:s +0000', intval( $log->get_last_hit(), 10 ) ) ); ?></pubDate> | ||
| 32 | <guid isPermaLink="false"><?php echo esc_html( $log->get_id() ); ?></guid> | ||
| 33 | <description><?php echo esc_html( $log->get_url() ); ?></description> | ||
| 34 | </item> | ||
| 35 | <?php endforeach; ?> | ||
| 36 | </channel> | ||
| 37 | </rss> | ||
| 38 | <?php | ||
| 39 | $xml .= ob_get_contents(); | ||
| 40 | ob_end_clean(); | ||
| 41 | |||
| 42 | return $xml; | ||
| 43 | } | ||
| 44 | |||
| 45 | function load( $group, $data, $filename = '' ) { | ||
| 46 | } | ||
| 47 | } |
2.56 KB
2.85 KB
wp-content/plugins/redirection/license.txt
0 → 100644
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff is collapsed.
Click to expand it.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
wp-content/plugins/redirection/readme.txt
0 → 100644
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
-
Please register or sign in to post a comment