permalink-manager
Last commit date
includes
9 years ago
languages
9 years ago
out
9 years ago
LICENSE.txt
9 years ago
README.txt
9 years ago
permalink-manager.php
9 years ago
permalink-manager.php
344 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * Plugin Name: Permalink Manager |
| 5 | * Plugin URI: http://maciejbis.net/ |
| 6 | * Description: Most advanced Permalink utility for Wordpress. It allows to bulk edit the permalinks & permastructures and regenerate/reset all the URIs in your Wordpress instance. |
| 7 | * Version: 1.0.1 |
| 8 | * Author: Maciej Bis |
| 9 | * Author URI: http://maciejbis.net/ |
| 10 | * License: GPL-2.0+ |
| 11 | * License URI: http://www.gnu.org/licenses/gpl-2.0.txt |
| 12 | * Text Domain: permalink-manager |
| 13 | * Domain Path: /languages |
| 14 | */ |
| 15 | // If this file is called directly, abort. |
| 16 | if ( ! defined( 'WPINC' ) ) { |
| 17 | die; |
| 18 | } |
| 19 | |
| 20 | // Define the directories used to load plugin files. |
| 21 | define( 'PERMALINK_MANAGER_PLUGIN_NAME', 'Permalink Manager' ); |
| 22 | define( 'PERMALINK_MANAGER_PLUGIN_SLUG', 'permalink-manager' ); |
| 23 | define( 'PERMALINK_MANAGER_VERSION', '1.0.0' ); |
| 24 | define( 'PERMALINK_MANAGER_DIR', untrailingslashit( dirname( __FILE__ ) ) ); |
| 25 | define( 'PERMALINK_MANAGER_BASENAME', plugin_basename(__FILE__) ); |
| 26 | define( 'PERMALINK_MANAGER_URL', untrailingslashit( plugins_url( '', __FILE__ ) ) ); |
| 27 | define( 'PERMALINK_MANAGER_WEBSITE', 'http://permalinkmanager.pro' ); |
| 28 | |
| 29 | class Permalink_Manager_Class { |
| 30 | |
| 31 | public $permalink_manager, $permalink_manager_options_page, $permalink_manager_options; |
| 32 | public $sections, $functions, $permalink_manager_before_sections_html, $permalink_manager_after_sections_html; |
| 33 | |
| 34 | /** |
| 35 | * Get options from DB, load subclasses & hooks |
| 36 | */ |
| 37 | public function __construct() { |
| 38 | $this->include_subclassess(); |
| 39 | $this->register_init_hooks(); |
| 40 | } |
| 41 | |
| 42 | /** |
| 43 | * Include back-end classess and set their instances |
| 44 | */ |
| 45 | function include_subclassess() { |
| 46 | // WP_List_Table needed for post types & taxnomies editors |
| 47 | if( ! class_exists( 'WP_List_Table' ) ) { |
| 48 | require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); |
| 49 | } |
| 50 | |
| 51 | $classes = array( |
| 52 | 'core' => array( |
| 53 | 'helper-functions' => 'Permalink_Manager_Helper_Functions', |
| 54 | 'uri-functions-post' => 'Permalink_Manager_URI_Functions_Post', |
| 55 | 'uri-functions-tax' => 'Permalink_Manager_URI_Functions_Tax', |
| 56 | 'admin-functions' => 'Permalink_Manager_Admin_Functions', |
| 57 | 'actions' => 'Permalink_Manager_Actions', |
| 58 | 'pro-hooks' => 'Permalink_Manager_Pro_Hooks' |
| 59 | ), |
| 60 | 'views' => array( |
| 61 | 'uri-editor' => 'Permalink_Manager_Uri_Editor', |
| 62 | 'tools' => 'Permalink_Manager_Tools', |
| 63 | 'permastructs' => 'Permalink_Manager_Permastructs', |
| 64 | 'settings' => 'Permalink_Manager_Settings', |
| 65 | 'advanced' => 'Permalink_Manager_Advanced', |
| 66 | 'uri-editor-tax' => false, |
| 67 | 'uri-editor-post' => false |
| 68 | ) |
| 69 | ); |
| 70 | |
| 71 | // Load classes and set-up their instances |
| 72 | foreach($classes as $class_type => $classes_array) { |
| 73 | foreach($classes_array as $class => $class_name) { |
| 74 | $filename = PERMALINK_MANAGER_DIR . "/includes/{$class_type}/permalink-manager-{$class}.php"; |
| 75 | |
| 76 | if(file_exists($filename)) { |
| 77 | require_once $filename; |
| 78 | if($class_name) { $this->functions[$class] = new $class_name(); } |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Register general hooks |
| 86 | */ |
| 87 | public function register_init_hooks() { |
| 88 | // Localize plugin |
| 89 | add_action( 'plugins_loaded', array($this, 'localize_me'), 1 ); |
| 90 | |
| 91 | // Load options |
| 92 | add_action( 'init', array($this, 'get_options_and_globals'), 1 ); |
| 93 | |
| 94 | // Use the URIs set in this plugin + redirect from old URIs to new URIs + adjust canonical redirect settings |
| 95 | add_action( 'wp', array($this, 'disable_canonical_redirect'), 0, 1 ); |
| 96 | add_action( 'template_redirect', array($this, 'redirect_to_new_uri'), 999); |
| 97 | add_filter( 'request', array($this, 'detect_post'), 0, 1 ); |
| 98 | |
| 99 | // Legacy support |
| 100 | add_action( 'init', array($this, 'legacy_support'), 2 ); |
| 101 | } |
| 102 | |
| 103 | /** |
| 104 | * Localize this plugin |
| 105 | */ |
| 106 | function localize_me() { |
| 107 | load_plugin_textdomain( 'permalink-manager', false, PERMALINK_MANAGER_DIR ); |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * Get options values & set global |
| 112 | */ |
| 113 | public function get_options_and_globals() { |
| 114 | // 1. Globals with data stored in DB |
| 115 | global $permalink_manager_options, $permalink_manager_uris, $permalink_manager_permastructs; |
| 116 | |
| 117 | $this->permalink_manager_options = $permalink_manager_options = apply_filters('permalink-manager-options', get_option('permalink-manager', array())); |
| 118 | $this->permalink_manager_uris = $permalink_manager_uris = apply_filters('permalink-manager-uris', get_option('permalink-manager-uris', array())); |
| 119 | $this->permalink_manager_permastructs = $permalink_manager_permastructs = apply_filters('permalink-manager-permastructs', get_option('permalink-manager-permastructs', array())); |
| 120 | |
| 121 | // 2. Globals used to display additional content (eg. alerts) |
| 122 | global $permalink_manager_before_sections_html, $permalink_manager_after_sections_html; |
| 123 | |
| 124 | $this->permalink_manager_before_sections_html = $permalink_manager_before_sections_html = apply_filters('permalink-manager-before-sections', ''); |
| 125 | $this->permalink_manager_after_sections_html = $permalink_manager_after_sections_html = apply_filters('permalink-manager-after-sections', ''); |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * Used to optimize SQL queries amount instead of rewrite rules - the essential part of this plugin |
| 130 | */ |
| 131 | function detect_post($query) { |
| 132 | global $wpdb, $permalink_manager_uris, $sitepress_settings; |
| 133 | |
| 134 | // Check if any custom URI is used |
| 135 | if(!(is_array($permalink_manager_uris))) return $query; |
| 136 | |
| 137 | // Used in debug mode |
| 138 | $old_query = $query; |
| 139 | |
| 140 | /** |
| 141 | * 1. Prepare URL and check if it is correct |
| 142 | */ |
| 143 | $protocol = stripos($_SERVER['SERVER_PROTOCOL'], 'https') === true ? 'https://' : 'http://'; |
| 144 | $request_url = "{$protocol}{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; |
| 145 | $home_url = trim(rtrim(get_bloginfo('url'), '/')); |
| 146 | // Adjust prefix (it should be the same in both request & home_url) |
| 147 | $home_url = ($protocol == 'https://') ? str_replace("http://", "https://", $home_url) : str_replace("https://", "http://", $home_url); |
| 148 | |
| 149 | if (filter_var($request_url, FILTER_VALIDATE_URL)) { |
| 150 | /** |
| 151 | * 2. Process URL |
| 152 | */ |
| 153 | // Remove .html suffix and domain name from URL and query (URLs ended with .html will work as aliases) |
| 154 | $request_url = trim(str_replace($home_url, "", $request_url), "/"); |
| 155 | |
| 156 | // Split the current URL into subparts (check if WPML is active) |
| 157 | if(isset($sitepress_settings['language_negotiation_type']) && $sitepress_settings['language_negotiation_type'] == 1) { |
| 158 | preg_match("/^(?:(\w{2})\/)?(.+)\/?(?:(page|feed|embed|attachment|track)\/([\d+])?)?$/i", $request_url, $url_parts); |
| 159 | $lang = (!empty($url_parts[1])) ? $url_parts[1] : ""; |
| 160 | $uri = (!empty($url_parts[2])) ? $url_parts[2] : ""; |
| 161 | $endpoint = (!empty($url_parts[3])) ? $url_parts[3] : ""; |
| 162 | $endpoint_value = (!empty($url_parts[4])) ? $url_parts[4] : ""; |
| 163 | } else { |
| 164 | preg_match("/^(.+)\/?(?:(page|feed|embed|attachment|track)\/([\d+])?)?$/i", $request_url, $url_parts); |
| 165 | $lang = false; |
| 166 | $uri = (!empty($url_parts[1])) ? $url_parts[1] : ""; |
| 167 | $endpoint = (!empty($url_parts[2])) ? $url_parts[2] : ""; |
| 168 | $endpoint_value = (!empty($url_parts[3])) ? $url_parts[3] : ""; |
| 169 | } |
| 170 | |
| 171 | // Ignore URLs with no URI grabbed |
| 172 | if(empty($uri)) return $query; |
| 173 | |
| 174 | // Remove querystrings from URI |
| 175 | $uri = trim(strtok($uri, '?'), "/"); |
| 176 | |
| 177 | /** |
| 178 | * 2A. Check if found URI matches any element from custom uris array |
| 179 | */ |
| 180 | $item_id = array_search($uri, $permalink_manager_uris); |
| 181 | |
| 182 | // Check again in case someone added .html suffix to particular post (with .html suffix) |
| 183 | $item_id = (empty($item_id)) ? array_search("{$uri}.html", $permalink_manager_uris) : $item_id; |
| 184 | |
| 185 | // Clear the original query before it is filtered |
| 186 | $query = ($item_id) ? array() : $query; |
| 187 | |
| 188 | /** |
| 189 | * 2B. Custom URI assigned to taxonomy |
| 190 | */ |
| 191 | if(strpos($item_id, 'tax-') !== false) { |
| 192 | // Remove the "tax-" prefix |
| 193 | $item_id = preg_replace("/[^0-9]/", "", $item_id); |
| 194 | |
| 195 | // Get the variables to filter wp_query and double-check if tax exists |
| 196 | $term = get_term($item_id); |
| 197 | if(empty($term->taxonomy)) { return $query; } |
| 198 | |
| 199 | // Get some term data |
| 200 | $query_parameter = ($term->taxonomy == 'category') ? 'category_name' : $term->taxonomy; |
| 201 | $term_ancestors = get_ancestors($item_id, $term->taxonomy); |
| 202 | $final_uri = $term->slug; |
| 203 | |
| 204 | // Fix for hierarchical CPT & pages |
| 205 | if(empty($term_ancestors)) { |
| 206 | foreach ($term_ancestors as $parent) { |
| 207 | $parent = get_term($parent, $term->taxonomy); |
| 208 | if(!empty($parent->slug)) { |
| 209 | $final_uri = $parent->slug . '/' . $final_uri; |
| 210 | } |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | // Alter query parameters |
| 215 | $query[$query_parameter] = $final_uri; |
| 216 | } |
| 217 | /** |
| 218 | * 2C. Custom URI assigned to post/page/cpt item |
| 219 | */ |
| 220 | else if(isset($item_id) && is_numeric($item_id)) { |
| 221 | // Fix for revisions |
| 222 | $is_revision = wp_is_post_revision($item_id); |
| 223 | $item_id = ($is_revision) ? $is_revision : $item_id; |
| 224 | |
| 225 | // Fix for WPML languages mismatch |
| 226 | $post_lang_details = apply_filters('wpml_post_language_details', NULL, $item_id); |
| 227 | $language_code = (!empty($post_lang_details['language_code'])) ? $post_lang_details['language_code'] : ''; |
| 228 | if($lang && $lang != $language_code) { |
| 229 | $item_id = apply_filters('wpml_object_id', $item_id); |
| 230 | } |
| 231 | |
| 232 | $post_to_load = get_post($item_id); |
| 233 | $final_uri = $post_to_load->post_name; |
| 234 | $post_type = $post_to_load->post_type; |
| 235 | |
| 236 | // Fix for hierarchical CPT & pages |
| 237 | if(!(empty($post_to_load->ancestors))) { |
| 238 | foreach ($post_to_load->ancestors as $parent) { |
| 239 | $parent = get_post( $parent ); |
| 240 | if($parent && $parent->post_name) { |
| 241 | $final_uri = $parent->post_name . '/' . $final_uri; |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | // Alter query parameters |
| 247 | if($post_to_load->post_type == 'page') { |
| 248 | $query['pagename'] = $final_uri; |
| 249 | } else if($post_to_load->post_type == 'post') { |
| 250 | $query['name'] = $final_uri; |
| 251 | } else { |
| 252 | $query['name'] = $final_uri; |
| 253 | $query['post_type'] = $post_type; |
| 254 | $query[$post_type] = $final_uri; |
| 255 | } |
| 256 | |
| 257 | // Make the redirects more clever - see redirect_to_new_uri() method |
| 258 | $query['do_not_redirect'] = 1; |
| 259 | } |
| 260 | |
| 261 | /** |
| 262 | * 2C. Endpoints |
| 263 | */ |
| 264 | if(!(empty($endpoint_value))) { |
| 265 | $endpoint = str_replace(array('page', 'trackback'), array('paged', 'tb'), $endpoint); |
| 266 | $query[$endpoint] = $endpoint_value; |
| 267 | } |
| 268 | |
| 269 | } |
| 270 | |
| 271 | // Debug mode |
| 272 | if(isset($_REQUEST['debug_url'])) { |
| 273 | $debug_info['old_query_vars'] = $old_query; |
| 274 | $debug_info['new_query_vars'] = $query; |
| 275 | |
| 276 | $debug_txt = json_encode($debug_info); |
| 277 | $debug_txt = "<textarea style=\"width:100%;height:300px\">{$debug_txt}</textarea>"; |
| 278 | wp_die($debug_txt); |
| 279 | } |
| 280 | |
| 281 | return $query; |
| 282 | } |
| 283 | |
| 284 | function redirect_to_new_uri() { |
| 285 | global $wp, $wp_query, $permalink_manager_uris, $permalink_manager_options; |
| 286 | |
| 287 | // Affect only posts with custom URI and old URIs |
| 288 | if(is_singular() && isset($permalink_manager_uris[$wp_query->queried_object_id]) && empty($wp_query->query['do_not_redirect']) && empty($wp_query->query['preview'])) { |
| 289 | // Ignore posts with specific statuses |
| 290 | if(!(empty($wp_query->queried_object->post_status)) && in_array($wp_query->queried_object->post_status, array('draft', 'pending', 'auto-draft', 'future'))) { |
| 291 | return ''; |
| 292 | } |
| 293 | |
| 294 | // Get the real URL |
| 295 | $current_url = home_url(add_query_arg(array(),$wp->request)); |
| 296 | $native_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri($permalink_manager_uris[$wp_query->queried_object_id], true); |
| 297 | |
| 298 | $active_permalink = str_replace($native_uri, $permalink_manager_uris[$wp_query->queried_object_id], $current_url); |
| 299 | } |
| 300 | |
| 301 | // Get the redirection mode |
| 302 | $redirect_mode = $permalink_manager_options['miscellaneous']['redirect']; |
| 303 | |
| 304 | // Ignore default URIs (or do nothing if redirects are disabled) |
| 305 | if(!empty($active_permalink) && !empty($redirect_mode)) { |
| 306 | wp_redirect($active_permalink, $redirect_mode); |
| 307 | exit(); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | function disable_canonical_redirect() { |
| 312 | global $permalink_manager_options; |
| 313 | if(!($permalink_manager_options['miscellaneous']['canonical_redirect'])) { |
| 314 | remove_action('template_redirect', 'redirect_canonical'); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | /** |
| 319 | * Temporary hook |
| 320 | */ |
| 321 | function legacy_support() { |
| 322 | global $permalink_manager_permastructs, $permalink_manager_options; |
| 323 | |
| 324 | if(isset($permalink_manager_options['base-editor'])) { |
| 325 | $new_options['post_types'] = $permalink_manager_options['base-editor']; |
| 326 | update_option('permalink-manager-permastructs', $new_options); |
| 327 | } |
| 328 | else if(empty($permalink_manager_permastructs['post_types']) && count($permalink_manager_permastructs) > 0) { |
| 329 | $new_options['post_types'] = $permalink_manager_permastructs; |
| 330 | update_option('permalink-manager-permastructs', $new_options); |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | } |
| 335 | |
| 336 | /** |
| 337 | * Begins execution of the plugin. |
| 338 | */ |
| 339 | function run_permalink_manager() { |
| 340 | $Permalink_Manager_Class = new Permalink_Manager_Class(); |
| 341 | } |
| 342 | |
| 343 | run_permalink_manager(); |
| 344 |