redirects did. $activityTimestamp = get_site_option(self::SETTINGS_INIT_TIME_KEY, null); if ( empty($activityTimestamp) ) { add_site_option(self::SETTINGS_INIT_TIME_KEY, time()); } $params = ['updated' => 1]; if ( !empty($post['selectedTrigger']) ) { $params['selectedTrigger'] = strval($post['selectedTrigger']); } wp_redirect($this->getTabUrl($params)); exit; } /** * @param $settings * @return bool|WP_Error */ protected function validateSubmittedSettings($settings) { if ( !is_array($settings) ) { return new WP_Error( 'ame_invalid_json', sprintf('Invalid JSON data. Expected an associative array, got %s.', gettype($settings)) ); } if ( !array_key_exists('redirects', $settings) ) { return new WP_Error('rui_missing_redirects_key', 'The required "redirects" field is missing.'); } $allowedProperties = [ //Actor IDs always follow the "prefix:value" format. 'actorId' => /** @lang RegExp */ '@^[a-z]{1,15}+:[^\s].{0,300}+$@i', //The URL can be basically anything, so we don't try to validate it. 'urlTemplate' => null, //Menu template IDs are based on menu URLs, so they're pretty unpredictable. If one is given, it must be non-empty. 'menuTemplateId' => /** @lang RegExp */ '@^.@', //A trigger is always a lowercase string. We could just list the supported values once dev. is done. 'trigger' => /** @lang RegExp */ '@^[a-z\-]{2,20}+$@i', //The shortcode flag is a boolean value. No regex for that. 'shortcodesEnabled' => null, ]; $requiredProperties = [ 'actorId' => true, 'urlTemplate' => true, 'shortcodesEnabled' => true, 'trigger' => true, ]; foreach ($settings['redirects'] as $key => $redirect) { if ( !is_array($redirect) ) { return new WP_Error( 'rui_bad_redirect_data_type', sprintf('Redirect %s should be an array but it is actually %s', $key, gettype($redirect)) ); } //Verify that it has all the required properties. $missingProperties = array_diff_key($requiredProperties, $redirect); if ( !empty($missingProperties) ) { $firstMissingProp = reset($missingProperties); return new WP_Error( 'rui_missing_key', sprintf('Redirect %s is missing the required property "%s"', $key, $firstMissingProp) ); } //Verify that the redirect has only allowed properties. $badProperties = array_diff_key($redirect, $allowedProperties); if ( !empty($badProperties) ) { $firstBadProp = reset($badProperties); return new WP_Error( 'rui_bad_key', sprintf('Redirect %s has an unsupported property "%s"', $key, $firstBadProp) ); } //String properties must match their validation regex (if any). foreach ($allowedProperties as $property => $regex) { if ( is_string($regex) && isset($redirect[$property]) && (!is_string($redirect[$property]) || !preg_match($regex, $redirect[$property])) ) { return new WP_Error( 'rui_invalid_property_value', sprintf('Redirect %s: Property "%s" has an invalid value.', $key, $property) ); } } //shortcodesEnabled must be a boolean. if ( array_key_exists('shortcodesEnabled', $redirect) && !is_bool($redirect['shortcodesEnabled']) ) { return new WP_Error( 'rui_invalid_property_value', sprintf( 'Redirect %s: The "shortcodesEnabled" property is invalid.' . ' Expected a boolean, but actual type is "%s".', $key, gettype($redirect['shortcodesEnabled']) ) ); } //URL template must be a string. if ( !is_string($redirect['urlTemplate']) ) { return new WP_Error( 'rui_invalid_property_value', sprintf( 'Redirect %s: The "urlTemplate" property is invalid.' . ' Expected a string, but actual type is "%s".', $key, gettype($redirect['urlTemplate']) ) ); } //URL template must be non-empty. if ( trim($redirect['urlTemplate']) === '' ) { return new WP_Error( 'rui_empty_url', sprintf('Redirect %s: The "urlTemplate" property is empty.', $key) ); } } return true; } /** * Load user data for display in the redirect management UI. * * Will load some or all users depending on how many users there are in total. * Users that have custom redirects are always loaded. * * @param array $flattenedRedirects * @return array{array,boolean} An array of users and a boolean indicating if the total number exceeds the limit. */ protected function preloadUsers(array $flattenedRedirects) { $loadedUsers = get_users([ //In Multisite, include all sites and not just the current site. Note that this might not work if used //together with some other arguments (judging by WP_User_Query::prepare_query source code). 'blog_id' => 0, 'number' => self::PRELOADED_USER_LIMIT + 1, 'count_total' => false, //Allegedly, this can improve performance. 'fields' => self::$desiredUserFields, ]); $hasMoreUsers = count($loadedUsers) > self::PRELOADED_USER_LIMIT; $isUserLoaded = []; foreach ($loadedUsers as $user) { $isUserLoaded[$user->user_login] = true; } //Always load users that already have custom redirects. $userPrefix = 'user:'; $userPrefixLength = strlen($userPrefix); $usersToLoad = []; foreach ($flattenedRedirects as $details) { if ( substr($details['actorId'], 0, $userPrefixLength) === $userPrefix ) { $userLogin = substr($details['actorId'], $userPrefixLength); if ( is_string($userLogin) && ($userLogin !== '') && empty($isUserLoaded[$userLogin]) && empty($usersToLoad[$userLogin]) ) { $usersToLoad[$userLogin] = true; } } } if ( !empty($usersToLoad) ) { $additionalUsers = get_users([ 'blog_id' => 0, 'count_total' => false, 'fields' => self::$desiredUserFields, 'login__in' => array_values($usersToLoad), ]); $loadedUsers = array_merge($loadedUsers, $additionalUsers); } return [$loadedUsers, $hasMoreUsers]; } public function userCanSearchUsers() { return $this->menuEditor->current_user_can_edit_menu(); } public function ajaxSearchUsers($params) { $foundUsers = get_users([ 'search' => '*' . $params['term'] . '*', 'search_columns' => ['user_login', 'display_name'], 'blog_id' => 0, 'number' => self::SEARCH_USER_LIMIT, 'count_total' => false, 'fields' => self::$desiredUserFields, ]); $results = []; foreach ($foundUsers as $user) { $user = (array)$user; $results[] = array_merge($user, ['label' => $user['user_login']]); } return $results; } public function addContextualHelp() { if ( !is_callable('get_current_screen') ) { return; } $screen = get_current_screen(); if ( $screen ) { $screen->add_help_tab([ 'title' => 'Shortcodes', 'id' => 'ame-rui-help-shortcodes', 'content' => $this->getShortcodeHelp(), ]); $screen->add_help_tab([ 'title' => 'Priority', 'id' => 'ame-rui-help-priority', 'content' => $this->getPriorityHelp(), ]); $screen->add_help_tab([ 'title' => 'First Login', 'id' => 'ame-rui-help-first-login', 'content' => $this->getFirstLoginHelp(), ]); $screen->add_help_tab([ 'title' => 'Disabling Redirects', 'id' => 'ame-rui-emergency-shutdown', 'content' => $this->getEmergencyShutdownHelp(), ]); } } private function getShortcodeHelp() { $message = '
You can use shortcodes in redirect URLs. This plugin comes with a few shortcodes that could be useful for redirects:
'; $message .= 'field
parameter to specify which field to output. Examples:'
) . '%s
= %sSome shortcodes from other plugins may also work, but it depends on the shortcode.
'; return $message; } private function formatShortcodeInfo($tag, $description, $exampleCode = null) { $result = sprintf('[%s]
- %s', esc_html($tag), $description);
if ( $exampleCode !== null ) {
$result .= ' Example output:%s
', esc_html($output));
}
private function getPriorityHelp() {
$tips = [
'Redirects are processed from top to bottom and the first matching setting is used.',
'You can drag and drop redirects to change their priority.',
'When you create redirects for specific users their order doesn\'t matter, but you can still move them around to organize them.',
];
return 'A "first login" redirect happens when a new user logs in for the first time.
' . '
WordPress does not record logins, so sometimes it\'s not possible to reliably determine if a user has already logged in before or not. To help avoid unnecessary redirects, the plugin will only perform a "first login" redirect when all of the following conditions are met:
' . 'If something goes wrong, you can disable all custom redirects by adding this code to wp-config.php:
' . 'define(\'AME_DISABLE_REDIRECTS\', true);
Note that this only applies to redirects created using this plugin. It will not prevent other plugins or themes from redirecting users.
'; } } class Redirect { /** * @var string */ private $actorId; /** * @var string */ private $urlTemplate; /** * @var boolean */ private $shortcodesEnabled; protected function __construct( $actorId, $urlTemplate, $shortcodesEnabled = false ) { $this->actorId = $actorId; $this->urlTemplate = $urlTemplate; $this->shortcodesEnabled = $shortcodesEnabled; } public static function fromArray(array $properties) { return new static( $properties['actorId'], $properties['urlTemplate'], !empty($properties['shortcodesEnabled']) ); } /** * @return string */ public function getActorId() { return $this->actorId; } /** * @return string */ public function getUrl() { $url = $this->urlTemplate; if ( $this->shortcodesEnabled && function_exists('do_shortcode') ) { $url = do_shortcode($url); } return $url; } } class RedirectCollection { /** * @var array