Skip to content

Instantly share code, notes, and snippets.

@mkdizajn
Forked from mikeschinkel/redirect-old-urls.php
Created June 6, 2016 20:27
Show Gist options
  • Save mkdizajn/7084ad7dd4989341cfd8109ce4be0c4b to your computer and use it in GitHub Desktop.
Save mkdizajn/7084ad7dd4989341cfd8109ce4be0c4b to your computer and use it in GitHub Desktop.
WordPress plugin to redirect old URLs when a site is refreshed. Uses a JSON file on activate to import into the DB. If Activation fails because of an error in the JSON file just deactivate, edit the file and then reactivate.
<?php
/**
* Plugin Name: Redirect Old URLs
* Description: Supports redirecting from an old site's URL to a new site on same domain.
* Version: 0.1.0
* Author: The WPLib Team
* Author URI: http://github.com/wplib
* Author Email: team@wplib.org
* License: GPL2
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*
* Looks for a redirects.json file in the WP_CONTENT_DIR directory (typically /wp-content) on plugin activation
* that contains an array of two dimensional arrays where:
*
* [0] The old URL path from the root but w/o a leading slash.
* [1] The new URL path to 301 redirect to.
*
* It then loads this into the wp_terms table where the name is prefixed with 'redirect:'.
*
* This will support URLs up to 200 characters in length.
*
* @example:
*
* [
* [ 'old-foo/', 'new-foo/' ],
* [ 'old-bar/', 'new-bar/' ],
* ...
* ]
*
* @note Yes, this uses the wp_terms in a non-standard way. But it is better than creating a
* custom table as it should not affect anything because it will be an orphan term from
* the perspective of WordPress as it will have no associated taxonomy and thus these
* terms will be ignored.
*
* Just DO NOT RUN any plugin that DELETES ORPHAN TERMS!
*
*/
class Redirect_Old_Urls {
const REDIRECTS_FILE = '/redirects.json';
/**
*
*/
static function on_load() {
add_action( 'template_redirect', array( __CLASS__, '_template_redirect') );
add_action( 'pre_current_active_plugins', array( __CLASS__, '_pre_current_active_plugins') );
register_activation_hook( __FILE__, array( __CLASS__, '_activate') );
}
/**
*
*/
static function _template_redirect() {
if ( is_404() || is_feed() ) {
/**
* Only attempt to redirect if on a 404
*
* First, get the incoming URL path, stripping it of leading and trailing slashes,
*
* @example http://example.com/foo/bar/baz/ --> 'foo/bar/baz'
*/
$url_path = trim( $_SERVER['REQUEST_URI'], '/\\' );
if ( $new_path = self::lookup_url_redirect( $url_path ) ) {
/**
* If we found a URL to redirect, do so.
*/
wp_safe_redirect( "/{$new_path}", 301 );
die;
}
/**
* If no redirect found then continue on and let WordPress display a 404 page.
*/
}
}
/**
* Retrieves a new url path given an old url path from the wp_terms table.
*
* @param string|array $old_path
*
* @return string
*/
static function lookup_url_redirect( $old_path ) {
/**
* @var wpdb $wpdb
*/
global $wpdb;
$sql = $wpdb->prepare(
"SELECT slug FROM {$wpdb->terms} WHERE name = '%s' LIMIT 1",
"redirect:{$old_path}"
);
return $wpdb->get_var( $sql );
}
/**
*
*/
static function _activate() {
do {
$errors = array();
if ( ! is_file( $redirects_filepath = self::redirects_filepath() ) ) {
$err_msg = "Import Terminated; no redirect file found: %s.";
$errors[] = sprintf( $err_msg, $redirects_filepath );
break;
}
if ( ! ( $redirects_json = json_decode( file_get_contents( $redirects_filepath ) ) ) ) {
$err_msg = "Import Terminated; redirects file appears to be invalid JSON: %s.";
$errors[] = sprintf( $err_msg, $redirects_filepath );
break;
}
if ( ! is_array( $redirects_json ) ) {
$err_msg = "Import Terminated; redirects file does not return an array of redirects: %s.";
$errors[] = sprintf( $err_msg, $redirects_filepath );
break;
}
global $wpdb;
/**
* Delete all the old URL redirects, if there are any...
*/
$sql = "DELETE FROM {$wpdb->terms} WHERE name LIKE 'redirect:%'";
$wpdb->query( $sql );
foreach( $redirects_json as $index => $redirect_pair ) {
if ( ! isset( $redirect_pair[0] ) || ! isset( $redirect_pair[1] ) ) {
$err_msg = "Redirect #%d Failed; not a valid two element array in %s.";
$errors[] = sprintf( $err_msg, $index, $redirects_filepath );
continue;
}
if ( 191 < strlen( $redirect_pair[0] ) ) {
$err_msg = "Redirect #%d Failed; URL path '%s' is too long in %s (max 191 chars allowed.)";
$errors[] = sprintf( $err_msg, $index, $redirect_pair[0], $redirects_filepath );
continue;
}
if ( 200 < strlen( $redirect_pair[1] ) ) {
$err_msg = "Redirect #%d Failed; URL path '%s' is too long in %s (max 200 chars allowed.)";
$errors[] = sprintf( $err_msg, $index, $redirect_pair[1], $redirects_filepath );
continue;
}
$redirects_json[ $index ] = $wpdb->prepare( "('%s','%s')", "redirect:{$redirect_pair[0]}", $redirect_pair[1] );
}
if ( count( $errors ) ) {
break;
}
$sql = "INSERT INTO {$wpdb->terms} (name,slug) VALUES \n\t" . implode( ",\n\t", $redirects_json );
$wpdb->query( $sql );
} while ( false );
if ( count( $errors ) ) {
$errors = implode( '</li><li>', $errors );
$err_msg = sprintf( '<p>Errors while attempting to activate plugin:</p><ul><li>%s</li></ul>', $errors );
set_transient( 'rou_activate_errors', $err_msg, 30 );
}
}
/**
*
*/
static function _pre_current_active_plugins() {
do {
$err_msg = get_transient( 'rou_activate_errors' );
/**
* If no errors, exit
*/
if ( ! $err_msg ) {
break;
}
/**
* Output the error messages
*/
echo '<div id="rou-message" class="error below-h2">';
echo wp_kses_post( $err_msg );
echo '</div>';
/**
* Clear the error message
*/
delete_transient( 'rou_activate_errors' );
/**
* Unhook notices to avoid dups
*/
remove_action( 'pre_current_active_plugins', array( __CLASS__, __FUNCTION__ ) );
} while ( false );
}
/**
*
*/
static function redirects_filepath() {
return WP_CONTENT_DIR . self::REDIRECTS_FILE;
}
}
Redirect_Old_Urls::on_load();
[
[ "page/13", "about-us/"],
[ "page/42", "public art page (tbd)/"],
[ "etc", "new-etc/"],
[
"Store this file as /wp-content/redirects.json, or wherever you changed content to.",
"File is only needed during activation."
]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment