<?php
/**
* Setup SRM post type
*
* @package safe-redirect-manager
*/
/**
* Post type class
*/
class SRM_Post_Type {
/**
* Status code labels for reuse
*
* @var array
*/
public $status_code_labels = array(); // Defined later to allow i18n
/**
* We have to store the redirect search so we can grab it later
*
* @var string
*/
private $redirect_search_term = false;
/**
* Sets up redirect manager
*
* @since 1.8
*/
public function setup() {
add_action( 'init', array( $this, 'init_properties' ) );
add_action( 'init', array( $this, 'action_register_post_types' ) );
add_action( 'admin_init', array( $this, 'init_search_filters' ) );
add_action( 'save_post', array( $this, 'action_save_post' ) );
add_filter( 'manage_redirect_rule_posts_columns', array( $this, 'filter_redirect_columns' ) );
add_filter( 'manage_edit-redirect_rule_sortable_columns', array( $this, 'filter_redirect_sortable_columns' ) );
add_action( 'manage_redirect_rule_posts_custom_column', array( $this, 'action_custom_redirect_columns' ), 10, 2 );
add_action( 'quick_edit_custom_box', array( $this, 'action_quick_edit_custom_redirect_columns' ), 10, 2 );
add_action( 'bulk_edit_custom_box', array( $this, 'action_quick_edit_custom_redirect_columns' ), 10, 2 );
add_action( 'transition_post_status', array( $this, 'action_transition_post_status' ), 10, 3 );
add_filter( 'post_updated_messages', array( $this, 'filter_redirect_updated_messages' ) );
add_action( 'admin_notices', array( $this, 'action_redirect_chain_alert' ) );
add_filter( 'the_title', array( $this, 'filter_admin_title' ), 100, 2 );
add_action( 'admin_print_styles-edit.php', array( $this, 'action_print_logo_css' ), 10, 1 );
add_action( 'admin_print_styles-post.php', array( $this, 'action_print_logo_css' ), 10, 1 );
add_action( 'admin_print_styles-post-new.php', array( $this, 'action_print_logo_css' ), 10, 1 );
add_filter( 'post_type_link', array( $this, 'filter_post_type_link' ), 10, 2 );
add_filter( 'default_hidden_columns', array( $this, 'filter_hidden_columns' ), 10, 1 );
add_action( 'admin_enqueue_scripts', array( $this, 'load_resources' ), 10, 0 );
add_action( 'wp_ajax_srm_validate_from_url', array( $this, 'srm_validate_from_url' ), 10, 0 );
add_action( 'wp_ajax_srm_autocomplete', array( $this, 'srm_autocomplete' ), 10, 0 );
}
/**
* Initialises class properties.
*
* @since 2.0.0
*/
public function init_properties() {
$this->status_code_labels = srm_get_valid_status_codes_data();
}
/**
* Setup search filters
*/
public function init_search_filters() {
$redirect_capability = $this->get_redirect_capability();
if ( ! is_admin() ) {
return;
}
if ( ! current_user_can( $redirect_capability ) ) {
return;
}
add_action( 'pre_get_posts', array( $this, 'disable_core_search' ) );
add_filter( 'posts_clauses', array( $this, 'filter_search_clauses' ), 10, 2 );
add_filter( 'post_row_actions', array( $this, 'filter_disable_quick_edit' ), 10, 2 );
}
/**
* Hide order column by default
*
* @param array $hidden Array of hidden post types
* @since 1.9
* @return array
*/
public function filter_hidden_columns( $hidden ) {
if ( empty( $_GET['post_type'] ) || 'redirect_rule' !== $_GET['post_type'] ) {
return $hidden;
}
$hidden[] = 'menu_order';
return $hidden;
}
/**
* Remove quick edit
*
* @param array $actions Array of actions
* @param int|WP_Post|null $post Post object
* @since 1.8
* @return array
*/
public function filter_disable_quick_edit( $actions, $post ) {
if ( 'redirect_rule' === get_post_type( $post ) && isset( $actions['inline hide-if-no-js'] ) ) {
unset( $actions['view'] );
}
return $actions;
}
/**
* We don't need core's fancy search functionality since we provide our own.
*
* @param \WP_Query $query WP Query object
*/
public function disable_core_search( $query ) {
if ( $query->is_search() && 'redirect_rule' === $query->get( 'post_type' ) ) {
// Store a reference to the search term for later use.
$this->redirect_search_term = $query->get( 's' );
// Don't let core build it's search clauses since we override them.
$query->set( 's', '' );
}
}
/**
* Build custom JOIN + WHERE clauses to do a more direct search through meta.
*
* @param array $clauses Array of SQL clauses
* @param WP_Query $query WP_Query object
* @return array
*/
public function filter_search_clauses( $clauses, $query ) {
global $wpdb;
if ( $this->redirect_search_term ) {
$search_term = $this->redirect_search_term;
$search_term_like = '%' . $wpdb->esc_like( $search_term ) . '%';
$query->set( 's', $this->redirect_search_term );
// Restore search term to a false value to prevent modifying the query again.
$this->redirect_search_term = false;
$clauses['distinct'] = 'DISTINCT';
$clauses['join'] .= " LEFT JOIN $wpdb->postmeta AS pm ON ($wpdb->posts.ID = pm.post_id) ";
$clauses['where'] = $wpdb->prepare(
"AND (
(
pm.meta_value LIKE %s
AND pm.meta_key = '_redirect_rule_from'
) OR (
pm.meta_value LIKE %s
AND pm.meta_key = '_redirect_rule_to'
)
)
AND $wpdb->posts.post_type = 'redirect_rule'
AND $wpdb->posts.post_status IN ( 'publish', 'future', 'draft', 'pending' )
",
$search_term_like,
$search_term_like
);
}
return $clauses;
}
/**
* Swap tools logo for plugin logo
*
* @since 1.1
* @uses plugins_url
* @return void
*/
public function action_print_logo_css() {
if ( $this->is_plugin_page() ) {
?>
<style type="text/css">
#visibility, .view-switch, .posts .inline-edit-col-left .inline-edit-group, #preview-action {
display: none;
}
#srm_redirect_rule_from {
width: 60%;
}
</style>
<?php
}
}
/**
* Whether or not this is an admin page specific to the plugin
*
* @since 1.1
* @uses get_post_type
* @return bool
*/
private function is_plugin_page() {
return (bool) ( get_post_type() === 'redirect_rule' || ( isset( $_GET['post_type'] ) && 'redirect_rule' === $_GET['post_type'] ) );
}
/**
* Echoes admin message if redirect chains exist
*
* @since 1.0
* @uses apply_filters
* @return void
*/
public function action_redirect_chain_alert() {
global $hook_suffix;
if ( $this->is_plugin_page() ) {
/**
* Filter whether possible redirect loop checking is enabled or not.
*
* @hook srm_check_for_possible_redirect_loops
* @param {bool} $check_possible_loop Whether to check for redirect loops. Default true.
* @returns {bool} Bool to check for redirect loops.
*/
$possible_loop = apply_filters( 'srm_check_for_possible_redirect_loops', true );
if ( $possible_loop ) {
$cycle_source = SRM_Loop_Detection::detect_redirect_loops();
$paths = SRM_Loop_Detection::get_cycle_source( $cycle_source );
if ( ! empty( $cycle_source ) ) {
?>
<div class="notice notice-error">
<p><?php esc_html_e( 'Safe Redirect Manager Error: The following redirects with the "Redirect To" value have created redirect loops.', 'safe-redirect-manager' ); ?></p>
<ul style="list-style: inside;">
<?php foreach ( $paths as $path ) : ?>
<li>
<?php
printf(
'<a href="%s">%s</a>',
esc_url( get_edit_post_link( esc_html( $path['id'] ) ) ),
esc_html( $path['path'] )
);
?>
</li>
<?php endforeach; ?>
</ul style>
</div>
<?php
}
}
if ( srm_max_redirects_reached() ) {
if ( 'post-new.php' === $hook_suffix ) {
?>
<style type="text/css">#post { display: none; }</style>
<?php
}
?>
<div class="error">
<p><?php esc_html_e( 'Safe Redirect Manager Error: You have reached the maximum allowable number of redirects', 'safe-redirect-manager' ); ?></p>
</div>
<?php
}
}
}
/**
* Filters title out for redirect from in post manager
*
* @since 1.0
* @param string $title Admin title
* @param int $post_id Post ID
* @uses is_admin, get_post_meta
* @return string
*/
public function filter_admin_title( $title, $post_id = 0 ) {
if ( ! is_admin() ) {
return $title;
}
$redirect = get_post( $post_id );
if ( empty( $redirect ) ) {
return $title;
}
if ( 'redirect_rule' !== $redirect->post_type ) {
return $title;
}
$redirect_from = get_post_meta( $post_id, '_redirect_rule_from', true );
if ( ! empty( $redirect_from ) ) {
return $redirect_from;
}
return $title;
}
/**
* Customizes updated messages for redirects
*
* @since 1.0
* @param array $messages Array of messages
* @uses esc_url, get_permalink, add_query_var, wp_post_revision_title
* @return array
*/
public function filter_redirect_updated_messages( $messages ) {
global $post, $post_ID;
$message_tpl = function( $message ) {
return sprintf(
/* translators: %1%s: message status, %2%s: URL to the list of redirect rules */
__( 'Redirect rule %1$s. <a href="%2$s">← Back to rules</a>', 'safe-redirect-manager' ),
$message,
esc_url( admin_url( 'edit.php?post_type=redirect_rule' ) )
);
};
$messages['redirect_rule'] = array(
0 => '', // Unused. Messages start at index 1.
1 => $message_tpl( esc_html__( 'updated', 'safe-redirect-manager' ) ),
2 => esc_html__( 'Custom field updated.', 'safe-redirect-manager' ),
3 => esc_html__( 'Custom field deleted.', 'safe-redirect-manager' ),
4 => $message_tpl( __( 'updated', 'safe-redirect-manager' ) ),
5 => isset( $_GET['revision'] )
? $message_tpl(
sprintf(
/* translators: %s: the revision title */
esc_html__( 'restored to revision from %s', 'safe-redirect-manager' ),
wp_post_revision_title( (int) $_GET['revision'], false )
)
)
: false,
6 => $message_tpl( esc_html__( 'published', 'safe-redirect-manager' ) ),
7 => $message_tpl( esc_html__( 'saved', 'safe-redirect-manager' ) ),
8 => $message_tpl( esc_html__( 'submitted', 'safe-redirect-manager' ) ),
9 => $message_tpl(
sprintf(
/* translators: %s: publish box date format, see http://php.net/date */
esc_html__( 'scheduled for %s', 'safe-redirect-manager' ),
date_i18n( esc_html__( 'M j, Y @ G:i' ), strtotime( $post->post_date ) )
)
),
10 => $message_tpl( esc_html__( 'draft updated', 'safe-redirect-manager' ) ),
);
return $messages;
}
/**
* Clear redirect cache if appropriate post type is transitioned
*
* @since 1.0
* @param string $new_status New post status
* @param string $old_status Old post status
* @param WP_Post $post Post object
* @return void
*/
public function action_transition_post_status( $new_status, $old_status, $post ) {
if ( ! is_object( $post ) ) {
return;
}
// recreate redirect cache
if ( 'redirect_rule' === $post->post_type ) {
srm_flush_cache();
}
}
/**
* Displays custom columns on redirect manager screen
*
* @since 1.0
* @param string $column Column name
* @param int $post_id Post Id
* @uses get_post_meta, esc_html, admin_url
* @return void
*/
public function action_custom_redirect_columns( $column, $post_id ) {
if ( 'srm_redirect_rule_to' === $column ) {
echo esc_html( get_post_meta( $post_id, '_redirect_rule_to', true ) );
} elseif ( 'srm_redirect_rule_status_code' === $column ) {
echo absint( get_post_meta( $post_id, '_redirect_rule_status_code', true ) );
} elseif ( 'srm_redirect_rule_force_https' === $column ) {
echo absint( get_post_meta( $post_id, '_force_https', true ) ) ? '✓' : 'x';
} elseif ( 'menu_order' === $column ) {
global $post;
echo esc_html( $post->menu_order );
}
}
/**
* Add new columns to manage redirect screen
*
* @since 1.0
* @param array $columns Array columns
* @return array
*/
public function filter_redirect_columns( $columns ) {
$columns['srm_redirect_rule_to'] = esc_html__( 'Redirect To', 'safe-redirect-manager' );
$columns['srm_redirect_rule_status_code'] = esc_html__( 'HTTP Status Code', 'safe-redirect-manager' );
$columns['srm_redirect_rule_force_https'] = esc_html__( 'Force https', 'safe-redirect-manager' );
$columns['menu_order'] = esc_html__( 'Order', 'safe-redirect-manager' );
// Change the title column
$columns['title'] = esc_html__( 'Redirect From', 'safe-redirect-manager' );
// Move date column to the back
unset( $columns['date'] );
$columns['date'] = esc_html__( 'Date', 'safe-redirect-manager' );
return $columns;
}
/**
* Allow menu_order column to be sortable.
*
* @param array $columns Array of columns
* @since 1.9
* @return array
*/
public function filter_redirect_sortable_columns( $columns ) {
$columns['menu_order'] = 'menu_order';
return $columns;
}
/**
* Add custom fields to the quick edit screen.
*
* @param string $column_name Name of the column to edit.
* @param string $post_type The post type slug, or current screen name if this is a taxonomy list table.
*
* @return void
*/
public function action_quick_edit_custom_redirect_columns( $column_name, $post_type ) {
if ( 'redirect_rule' !== $post_type ) {
return;
}
if ( 'srm_redirect_rule_status_code' === $column_name ) :
wp_nonce_field( 'srm-save-redirect-ajax-meta', 'srm_redirect_ajax_nonce' );
?>
<fieldset class="inline-edit-col-right margin-top-0">
<div class="inline-edit-col">
<div class="inline-edit-group wp-clearfix">
<label class="inline-edit-status alignleft">
<span class="title"><?php esc_html_e( 'HTTP Status Code', 'safe-redirect-manager' ); ?></span>
<select name="srm_redirect_rule_status_code">
<option value="-1"><?php esc_html_e( '— No Change —', 'safe-redirect-manager' ); ?></option>
<?php foreach ( srm_get_valid_status_codes() as $code ) : ?>
<option value="<?php echo esc_attr( $code ); ?>"><?php echo esc_html( $code . ' ' . $this->status_code_labels[ $code ] ); ?></option>
<?php endforeach; ?>
</select>
</label>
</div>
</div>
</fieldset>
<?php
endif;
if ( 'srm_redirect_rule_force_https' === $column_name ) :
?>
<fieldset class="inline-edit-col-right margin-top-0">
<div class="inline-edit-col">
<div class="inline-edit-group wp-clearfix">
<label class="inline-edit-status alignleft">
<span class="title"><?php esc_html_e( 'Force https', 'safe-redirect-manager' ); ?></span>
<input type="checkbox" name="srm_redirect_rule_force_https" value="1"/>
</label>
</div>
</div>
</fieldset>
<?php
endif;
}
/**
* Saves meta info for redirect rules
*
* @since 1.0
* @param int $post_id Post ID
* @uses current_user_can, get_post_type, wp_verify_nonce, update_post_meta, delete_post_meta
* @return void
*/
public function action_save_post( $post_id ) {
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || 'revision' === get_post_type( $post_id ) ) {
return;
}
// Update post meta for redirect rules
if ( ! empty( $_POST['srm_redirect_nonce'] ) && wp_verify_nonce( $_POST['srm_redirect_nonce'], 'srm-save-redirect-meta' ) && current_user_can( 'edit_post', $post_id ) ) {
if ( ! empty( $_POST['srm_redirect_rule_from_regex'] ) ) {
$allow_regex = (bool) $_POST['srm_redirect_rule_from_regex'];
update_post_meta( $post_id, '_redirect_rule_from_regex', $allow_regex );
} else {
$allow_regex = false;
delete_post_meta( $post_id, '_redirect_rule_from_regex' );
}
if ( ! empty( $_POST['srm_redirect_rule_from'] ) ) {
update_post_meta( $post_id, '_redirect_rule_from', srm_sanitize_redirect_from( $_POST['srm_redirect_rule_from'], $allow_regex ) );
} else {
delete_post_meta( $post_id, '_redirect_rule_from' );
}
if ( ! empty( $_POST['srm_redirect_rule_to'] ) ) {
update_post_meta( $post_id, '_redirect_rule_to', srm_sanitize_redirect_to( $_POST['srm_redirect_rule_to'] ) );
} else {
delete_post_meta( $post_id, '_redirect_rule_to' );
}
if ( ! empty( $_POST['srm_redirect_rule_status_code'] ) ) {
update_post_meta( $post_id, '_redirect_rule_status_code', absint( $_POST['srm_redirect_rule_status_code'] ) );
} else {
delete_post_meta( $post_id, '_redirect_rule_status_code' );
}
if ( ! empty( $_POST['srm_force_https'] ) ) {
update_post_meta( $post_id, '_force_https', true );
} else {
delete_post_meta( $post_id, '_force_https' );
}
if ( ! empty( $_POST['srm_redirect_rule_message'] ) ) {
update_post_meta( $post_id, '_redirect_rule_message', sanitize_text_field( $_POST['srm_redirect_rule_message'] ) );
} else {
delete_post_meta( $post_id, '_redirect_rule_message' );
}
if ( ! empty( $_POST['srm_redirect_rule_notes'] ) ) {
update_post_meta( $post_id, '_redirect_rule_notes', sanitize_text_field( $_POST['srm_redirect_rule_notes'] ) );
} else {
delete_post_meta( $post_id, '_redirect_rule_notes' );
}
/**
* This fixes an important bug where the redirect cache was not up-to-date. Previously the cache was only being
* updated on transition_post_status which gets called BEFORE save post. But since save_post is where all the important
* redirect info is saved, updating the cache before it is not sufficient.
*/
srm_flush_cache();
}
if ( ! empty( $_REQUEST['srm_redirect_ajax_nonce'] )
&& wp_verify_nonce( $_REQUEST['srm_redirect_ajax_nonce'], 'srm-save-redirect-ajax-meta' )
&& current_user_can( 'edit_post', $post_id )
) {
if ( ! empty( $_REQUEST['srm_redirect_rule_status_code'] ) && '-1' !== $_REQUEST['srm_redirect_rule_status_code'] ) {
update_post_meta( $post_id, '_redirect_rule_status_code', absint( $_REQUEST['srm_redirect_rule_status_code'] ) );
} elseif ( '-1' !== $_REQUEST['srm_redirect_rule_status_code'] ) {
delete_post_meta( $post_id, '_redirect_rule_status_code' );
}
if ( ! empty( $_REQUEST['srm_redirect_rule_force_https'] ) ) {
update_post_meta( $post_id, '_force_https', true );
} else {
delete_post_meta( $post_id, '_force_https' );
}
}
}
/**
* Get required capability for managing redirects
*
* @return string
*/
protected function get_redirect_capability() {
$redirect_capability = 'srm_manage_redirects';
$roles = array( 'administrator' );
foreach ( $roles as $role ) {
$role = get_role( $role );
if ( empty( $role ) || $role->has_cap( $redirect_capability ) ) {
continue;
}
$role->add_cap( $redirect_capability );
}
/**
* Filter the capability required to manage redirects.
*
* @hook srm_restrict_to_capability
* @param {string} $redirect_capability The required capability. Default `srm_manage_redirects`.
* @returns {string} The required capability.
*/
return apply_filters( 'srm_restrict_to_capability', $redirect_capability );
}
/**
* Registers post types for plugin
*
* @since 1.0
* @uses register_post_type, _x, plugins_url, apply_filters
* @return void
*/
public function action_register_post_types() {
$redirect_labels = array(
'name' => esc_html_x( 'Safe Redirect Manager', 'post type general name', 'safe-redirect-manager' ),
'singular_name' => esc_html_x( 'Redirect', 'post type singular name', 'safe-redirect-manager' ),
'add_new' => esc_html_x( 'Create Redirect Rule', 'redirect rule', 'safe-redirect-manager' ),
'add_new_item' => esc_html__( 'Create Redirect Rule', 'safe-redirect-manager' ),
'edit_item' => esc_html__( 'Edit Redirect Rule', 'safe-redirect-manager' ),
'new_item' => esc_html__( 'New Redirect Rule', 'safe-redirect-manager' ),
'all_items' => esc_html__( 'Safe Redirect Manager', 'safe-redirect-manager' ),
'view_item' => esc_html__( 'View Redirect Rule', 'safe-redirect-manager' ),
'search_items' => esc_html__( 'Search Redirects', 'safe-redirect-manager' ),
'not_found' => esc_html__( 'No redirect rules found.', 'safe-redirect-manager' ),
'not_found_in_trash' => esc_html__( 'No redirect rules found in trash.', 'safe-redirect-manager' ),
'parent_item_colon' => '',
'menu_name' => esc_html__( 'Safe Redirect Manager', 'safe-redirect-manager' ),
);
$redirect_capability = $this->get_redirect_capability();
$capabilities = array(
'edit_post' => $redirect_capability,
'read_post' => $redirect_capability,
'delete_post' => $redirect_capability,
'delete_posts' => $redirect_capability,
'edit_posts' => $redirect_capability,
'edit_others_posts' => $redirect_capability,
'publish_posts' => $redirect_capability,
'read_private_posts' => $redirect_capability,
);
$redirect_args = array(
'labels' => $redirect_labels,
'public' => false,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => 'tools.php',
'query_var' => false,
'rewrite' => false,
'capability_type' => 'post',
'capabilities' => $capabilities,
'has_archive' => false,
'hierarchical' => false,
'register_meta_box_cb' => array( $this, 'action_redirect_rule_metabox' ),
'menu_position' => 80,
'supports' => array( 'page-attributes' ),
);
register_post_type( 'redirect_rule', $redirect_args );
}
/**
* Registers meta boxes for redirect rule post type
*
* @since 1.0
* @uses add_meta_box
* @return void
*/
public function action_redirect_rule_metabox() {
add_meta_box( 'redirect_settings', esc_html__( 'Redirect Settings', 'safe-redirect-manager' ), array( $this, 'redirect_rule_metabox' ), 'redirect_rule', 'normal', 'core' );
}
/**
* Echoes HTML for redirect rule meta box
*
* @since 1.0
* @param WP_Post $post Post object
* @uses wp_nonce_field, get_post_meta, esc_attr, selected
* @return void
*/
public function redirect_rule_metabox( $post ) {
wp_nonce_field( 'srm-save-redirect-meta', 'srm_redirect_nonce' );
$redirect_from = get_post_meta( $post->ID, '_redirect_rule_from', true );
$redirect_to = get_post_meta( $post->ID, '_redirect_rule_to', true );
$redirect_notes = get_post_meta( $post->ID, '_redirect_rule_notes', true );
$status_code = get_post_meta( $post->ID, '_redirect_rule_status_code', true );
$enable_regex = get_post_meta( $post->ID, '_redirect_rule_from_regex', true );
$force_https = get_post_meta( $post->ID, '_force_https', true );
$redirect_message = get_post_meta( $post->ID, '_redirect_rule_message', true );
if ( empty( $status_code ) ) {
/**
* Filter the default HTTP status code to redirect with.
*
* Which HTTP redirect code safe redirect manager should default to. This can
* be overridden in the dashboard for each redirect.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
*
* @hook srm_default_direct_status
* @param {int} $default_status_code Default redirect status. Default value `302`.
* @returns {int} Redirect status.
*/
$status_code = apply_filters( 'srm_default_direct_status', 302 );
}
?>
<div class="notice notice-error" id="message" style="display: none;"></div>
<p>
<label for="srm_redirect_rule_from"><strong><?php esc_html_e( '* Redirect From:', 'safe-redirect-manager' ); ?></strong></label><br />
<input type="text" name="srm_redirect_rule_from" id="srm_redirect_rule_from" value="<?php echo esc_attr( $redirect_from ); ?>" />
<input type="checkbox" name="srm_redirect_rule_from_regex" id="srm_redirect_rule_from_regex" <?php checked( true, (bool) $enable_regex ); ?> value="1" />
<label for="srm_redirect_rule_from_regex"><?php esc_html_e( 'Enable Regular Expressions (advanced)', 'safe-redirect-manager' ); ?></label>
</p>
<p class="description"><?php esc_html_e( 'This path should be relative to the root of this WordPress installation (or the sub-site, if you are running a multi-site). Appending a (*) wildcard character will match all requests with the base. Warning: Enabling regular expressions will disable wildcards and completely change the way the * symbol is interpreted.', 'safe-redirect-manager' ); ?></p>
<p>
<label for="srm_redirect_rule_to"><strong><?php esc_html_e( '* Redirect To:', 'safe-redirect-manager' ); ?></strong></label><br />
<input class="widefat" type="text" name="srm_redirect_rule_to" id="srm_redirect_rule_to" value="<?php echo esc_attr( urldecode( $redirect_to ) ); ?>" />
</p>
<p class="description" id="srm_to_disabled_message" style="display:none;"><em><?php esc_html_e( 'The "Redirect to" value doesn\'t apply for 4xx error codes.', 'safe-redirect-manager' ); ?></em></p>
<p class="description"><?php esc_html_e( 'This can be a URL or a path relative to the root of your website (not your WordPress installation). Ending with a (*) wildcard character will append the request match to the redirect.', 'safe-redirect-manager' ); ?></p>
<p>
<label for="srm_redirect_rule_status_code"><strong><?php esc_html_e( '* HTTP Status Code:', 'safe-redirect-manager' ); ?></strong></label><br/>
<select name="srm_redirect_rule_status_code" id="srm_redirect_rule_status_code">
<?php foreach ( srm_get_valid_status_codes() as $code ) : ?>
<option value="<?php echo esc_attr( $code ); ?>" <?php selected( $status_code, $code ); ?>><?php echo esc_html( $code . ' ' . $this->status_code_labels[ $code ] ); ?></option>
<?php endforeach; ?>
</select>
<em><?php esc_html_e( "If you don't know what this is, leave it as is.", 'safe-redirect-manager' ); ?></em>
</p>
<p id="srm_redirect_rule_message_container">
<label for="srm_redirect_rule_message"><strong><?php esc_html_e( 'Message:', 'safe-redirect-manager' ); ?></strong></label>
<textarea name="srm_redirect_rule_message" id="srm_redirect_rule_message" class="widefat"><?php echo esc_textarea( $redirect_message ); ?></textarea>
<em><?php esc_html_e( 'Optionally display a message to users when they navigate to a 403 or 410 endpoint.', 'safe-redirect-manager' ); ?></em>
</p>
<p>
<label><strong><?php esc_html_e( 'Redirect Protocol:', 'safe-redirect-manager' ); ?></strong></label><br/>
<label>
<input type="checkbox" name="srm_force_https" value="1" <?php checked( $force_https, true ); ?>/> <?php esc_html_e( 'Force https', 'safe-redirect-manager' ); ?>
</label>
</p>
<p>
<label for="srm_redirect_rule_notes"><strong><?php esc_html_e( 'Notes:', 'safe-redirect-manager' ); ?></strong></label>
<textarea name="srm_redirect_rule_notes" id="srm_redirect_rule_notes" class="widefat"><?php echo esc_textarea( $redirect_notes ); ?></textarea>
<em><?php esc_html_e( 'Optionally leave notes on this redirect e.g. why was it created.', 'safe-redirect-manager' ); ?></em>
</p>
<?php
}
/**
* Return a permalink for a redirect post, which is the "redirect from"
* URL for that redirect.
*
* @since 1.7
* @param string $permalink The permalink
* @param object $post A Post object
* @uses home_url, get_post_meta
* @return string The permalink
*/
public function filter_post_type_link( $permalink, $post ) {
if ( 'redirect_rule' !== $post->post_type ) {
return $permalink;
}
// We can't do anything to provide a permalink
// for regex enabled redirects.
if ( get_post_meta( $post->ID, '_redirect_rule_from_regex', true ) ) {
return $permalink;
}
// We can't do anything if there is a wildcard in the redirect from
$redirect_from = get_post_meta( $post->ID, '_redirect_rule_from', true );
if ( false !== strpos( $redirect_from, '*' ) ) {
return $permalink;
}
// Use default permalink if no $redirect_from exists - this prevents duplicate GUIDs
if ( empty( $redirect_from ) ) {
return $permalink;
}
return home_url( $redirect_from );
}
/**
* Return singleton instance of class
*
* @return object
* @since 1.8
*/
public static function factory() {
static $instance = false;
if ( ! $instance ) {
$instance = new self();
$instance->setup();
}
return $instance;
}
/**
* Load scripts.
*
* @return void
*/
public function load_resources() {
if ( 'redirect_rule' === get_post_type() ) {
wp_enqueue_style( 'redirectjs', SRM_PLUGIN_URL . 'assets/css/redirect.css', array(), SRM_VERSION );
wp_enqueue_script( 'bulk-select', SRM_PLUGIN_URL . 'assets/js/bulk-select.js', array( 'jquery' ), SRM_VERSION, true );
wp_enqueue_script( 'redirectjs', SRM_PLUGIN_URL . 'assets/js/redirect.js', array( 'jquery' ), SRM_VERSION );
wp_enqueue_script( 'quick-bulk-editjs', SRM_PLUGIN_URL . 'assets/js/quick-bulk-edit.js', array( 'jquery', 'inline-edit-post' ), SRM_VERSION );
wp_localize_script(
'redirectjs',
'redirectValidation',
array(
'urlError' => __( 'There are some issues validating the URL. Please try again.', 'safe-redirect-manager' ),
'fail' => __( 'There is an existing redirect with the same Redirect From URL. You may <a href="%s">Edit</a> the redirect or try other `from` URL.', 'safe-redirect-manager' ),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'ajax_nonce' => wp_create_nonce( 'srm_autocomplete_nonce' ),
)
);
}
}
/**
* Fetches posts and pages based on the 'Redirect to:' field value.
* This function handles an AJAX request, returning a JSON array of posts
* and pages that match the search term.
*
* @return void
*/
public function srm_autocomplete() {
check_ajax_referer( 'srm_autocomplete_nonce', 'security' );
if ( ! current_user_can( 'srm_manage_redirects' ) ) {
echo wp_json_encode( array() );
wp_die();
}
$search_term = isset( $_REQUEST['term'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['term'] ) ) : false;
if ( ! $search_term ) {
echo wp_json_encode( array() );
wp_die();
}
// Remove the beginning / to prevent 0 results.
$search_term = ltrim( $search_term, '/' );
$query = get_posts(
array(
// Get publicly viewable post types, except for redirect_rule.
'post_type' => array_diff_key(
array_filter(
get_post_types(),
'is_post_type_viewable'
),
array(
'redirect_rule' => '',
)
),
's' => $search_term,
'posts_per_page' => 5,
)
);
if ( ! $query ) {
echo wp_json_encode( array() );
wp_die();
}
$suggestions = array();
foreach ( $query as $key => $post ) {
$suggestions[] = array(
'relative_url' => wp_make_link_relative( get_the_permalink( $post->ID ) ),
'post_title' => $post->post_title,
'post_type' => $post->post_type,
);
}
echo wp_json_encode( $suggestions );
wp_die();
}
/**
* Validate whether the from URL already exists or not.
*
* @return void
*/
public function srm_validate_from_url() {
if ( ! isset( $_GET['_wpnonce'] ) || ! isset( $_GET['from'] ) ) {
echo 0;
die();
}
$_wpnonce = sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) );
if ( ! wp_verify_nonce( $_wpnonce, 'srm-save-redirect-meta' ) ) {
echo 0;
die();
}
$from = srm_sanitize_redirect_from( wp_unslash( $_GET['from'] ) );
/**
* SRM treats '/sample-page' and 'sample-page' equally.
* If the $from value does not start with a forward slash,
* then we normalize it by adding one.
*/
$from = '/' === substr( $from, 0, 1 ) ? $from : '/' . $from;
$existing_post_ids = new WP_Query(
[
'meta_key' => '_redirect_rule_from',
'meta_value' => $from,
'fields' => 'ids',
'posts_per_page' => 1,
'no_found_rows' => true,
'post_type' => 'redirect_rule',
'post_status' => 'publish',
'orderby' => 'ID',
'order' => 'ASC',
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
]
);
// If no posts found, then bail out.
if ( empty( $existing_post_ids->posts ) ) {
echo 1;
die();
}
$existing_post_id = $existing_post_ids->posts[0];
echo esc_url( get_edit_post_link( $existing_post_id ) );
die();
}
}