array('function' => 'update_all_permalinks', 'display_uri_table' => true), 'permalink_manager_options' => array('function' => 'save_settings'), 'permalink_manager_permastructs' => array('function' => 'save_permastructures'), 'flush_sitemaps' => array('function' => 'flush_sitemaps'), 'import' => array('function' => 'import_custom_permalinks_uris'), ); // 3. Find the action foreach($actions_map as $action => $map) { if(isset($_POST[$action]) && wp_verify_nonce($_POST[$action], 'permalink-manager')) { // Execute the function $output = call_user_func(array($this, $map['function'])); // Get list of updated URIs if(!empty($map['display_uri_table'])) { $updated_slugs_count = (isset($output['updated_count']) && $output['updated_count'] > 0) ? $output['updated_count'] : false; $updated_slugs_array = ($updated_slugs_count) ? $output['updated'] : ''; } // Trigger only one function break; } } // 4. Display the slugs table (and append the globals) if(isset($updated_slugs_count)) { $permalink_manager_after_sections_html .= Permalink_Manager_Admin_Functions::display_updated_slugs($updated_slugs_array); } } /** * Save settings */ public static function save_settings($field = false, $value = false, $display_alert = true) { global $permalink_manager_options, $permalink_manager_before_sections_html; // Info: The settings array is used also by "Screen Options" $new_options = $permalink_manager_options; //$new_options = array(); // Save only selected field/sections if($field && $value) { $new_options[$field] = $value; } else { $post_fields = $_POST; foreach($post_fields as $option_name => $option_value) { $new_options[$option_name] = $option_value; } } // Allow only white-listed option groups foreach($new_options as $group => $group_options) { if(!in_array($group, array('licence', 'screen-options', 'general', 'permastructure-settings', 'stop-words'))) { unset($new_options[$group]); } } // Sanitize & override the global with new settings $new_options = Permalink_Manager_Helper_Functions::sanitize_array($new_options); $permalink_manager_options = $new_options = array_filter($new_options); // Save the settings in database update_option('permalink-manager', $new_options); // Display the message $permalink_manager_before_sections_html .= ($display_alert) ? Permalink_Manager_Admin_Functions::get_alert_message(__( 'The settings are saved!', 'permalink-manager' ), 'updated') : ""; } /** * Trigger bulk tools via AJAX */ function pm_bulk_tools() { global $sitepress, $wp_filter, $wpdb; // Define variables $return = array('alert' => Permalink_Manager_Admin_Functions::get_alert_message(__( 'No slugs were updated!', 'permalink-manager' ), 'error updated_slugs')); // Get the name of the function if(isset($_POST['regenerate']) && wp_verify_nonce($_POST['regenerate'], 'permalink-manager')) { $function_name = 'regenerate_all_permalinks'; $uniq_id = $_POST['pm_session_id']; } else if(isset($_POST['find_and_replace']) && wp_verify_nonce($_POST['find_and_replace'], 'permalink-manager') && !empty($_POST['old_string']) && !empty($_POST['new_string'])) { $function_name = 'find_and_replace'; $uniq_id = $_POST['pm_session_id']; } // Get content type & post statuses if(!empty($_POST['content_type']) && $_POST['content_type'] == 'taxonomies') { $content_type = 'taxonomies'; if(empty($_POST['taxonomies'])) { $error = true; $return = array('alert' => Permalink_Manager_Admin_Functions::get_alert_message(__( 'No taxonomy selected!', 'permalink-manager' ), 'error updated_slugs')); } } else { $content_type = 'post_types'; // Check if any post type was selected if(empty($_POST['post_types'])) { $error = true; $return = array('alert' => Permalink_Manager_Admin_Functions::get_alert_message(__( 'No post type selected!', 'permalink-manager' ), 'error updated_slugs')); } // Check post status if(empty($_POST['post_statuses'])) { $error = true; $return = array('alert' => Permalink_Manager_Admin_Functions::get_alert_message(__( 'No post status selected!', 'permalink-manager' ), 'error updated_slugs')); } } // Check if both strings are set for "Find and replace" tool if(!empty($function_name) && !empty($content_type) && empty($error)) { // Hotfix for WPML (start) if($sitepress) { remove_filter('get_terms_args', array($sitepress, 'get_terms_args_filter'), 10); remove_filter('get_term', array($sitepress, 'get_term_adjust_id'), 1); remove_filter('terms_clauses', array($sitepress, 'terms_clauses'), 10); remove_filter('get_pages', array($sitepress, 'get_pages_adjust_ids'), 1); } // Get the mode $mode = isset($_POST['mode']) ? $_POST['mode'] : 'custom_uris'; // Get the content type if($content_type == 'taxonomies') { $class_name = 'Permalink_Manager_URI_Functions_Tax'; } else { $class_name = 'Permalink_Manager_URI_Functions_Post'; } // Get items (try to get them from transient) $items = get_transient("pm_{$uniq_id}"); $progress = get_transient("pm_{$uniq_id}_progress"); $first_chunk = true; $chunk_size = apply_filters('permalink_manager_chunk_size', 50); if(empty($items)) { $items = $class_name::get_items(); if(!empty($items)) { // Set stats (to display the progress) $total = count($items); // Split items array into chunks and save them to transient $items = array_chunk($items, $chunk_size); set_transient("pm_{$uniq_id}_progress", 0, 300); set_transient("pm_{$uniq_id}", $items, 300); // Check for MySQL errors if(!empty($wpdb->last_error)) { printf('%s (%sMB)', $wpdb->last_error, strlen(serialize($items)) / 1000000); http_response_code(500); die(); } } } // Get homepage URL and ensure that it ends with slash $home_url = Permalink_Manager_Helper_Functions::get_permalink_base() . "/"; // Process the variables from $_POST object $old_string = (!empty($_POST['old_string'])) ? str_replace($home_url, '', esc_sql($_POST['old_string'])) : ''; $new_string = (!empty($_POST['old_string'])) ? str_replace($home_url, '', esc_sql($_POST['new_string'])) : ''; // Process only one subarray if(!empty($items[0])) { $chunk = array_shift($items); set_transient("pm_{$uniq_id}", $items, 300); // Check if posts or terms should be updated if($function_name == 'find_and_replace') { $output = $class_name::find_and_replace($chunk, $mode, $old_string, $new_string); } else { $output = $class_name::regenerate_all_permalinks($chunk, $mode); } if(!empty($output['updated_count'])) { $return = array_merge($return, (array) Permalink_Manager_Admin_Functions::display_updated_slugs($output['updated'], true, $first_chunk)); $return['updated_count'] = $output['updated_count']; } // Send total number of processed items with a first chunk if(!empty($total)) { $return['total'] = $total; } // Check if there are any chunks left if(count($items) > 0) { // Update progress $progress += $chunk_size; set_transient("pm_{$uniq_id}_progress", $progress, 300); $return['left_chunks'] = true; $return['progress'] = $progress; } else { delete_transient("pm_{$uniq_id}"); delete_transient("pm_{$uniq_id}_progress"); } } // Hotfix for WPML (end) if($sitepress) { add_filter('terms_clauses', array($sitepress, 'terms_clauses'), 10, 4); add_filter('get_term', array($sitepress, 'get_term_adjust_id'), 1, 1); add_filter('get_terms_args', array($sitepress, 'get_terms_args_filter'), 10, 2); add_filter('get_pages', array($sitepress, 'get_pages_adjust_ids'), 1, 2); } } wp_send_json($return); die(); } /** * Save permalink via AJAX */ public function pm_save_permalink() { $element_id = (!empty($_POST['permalink-manager-edit-uri-element-id'])) ? sanitize_text_field($_POST['permalink-manager-edit-uri-element-id']) : ''; if(!empty($element_id) && is_numeric($element_id)) { Permalink_Manager_URI_Functions_Post::update_post_uri($element_id); // Reload URI Editor & clean post cache clean_post_cache($element_id); die(); } } /** * Update all permalinks in "Permalink Editor" */ function update_all_permalinks() { // Check if posts or terms should be updated if(!empty($_POST['content_type']) && $_POST['content_type'] == 'taxonomies') { return Permalink_Manager_URI_Functions_Tax::update_all_permalinks(); } else { return Permalink_Manager_URI_Functions_Post::update_all_permalinks(); } } /** * Additional actions */ public static function extra_actions() { if(isset($_GET['flush_sitemaps'])) { self::flush_sitemaps(); } else if(isset($_GET['clear-permalink-manager-uris'])) { self::clear_all_uris(); } else if(isset($_GET['remove-permalink-manager-settings'])) { $option_name = sanitize_text_field($_GET['remove-permalink-manager-settings']); self::remove_plugin_data($option_name); } else if(!empty($_REQUEST['remove-uri'])) { $uri_key = sanitize_text_field($_REQUEST['remove-uri']); self::force_clear_single_element_uris_and_redirects($uri_key); } else if(!empty($_REQUEST['remove-redirect'])) { $redirect_key = sanitize_text_field($_REQUEST['remove-redirect']); self::force_clear_single_redirect($redirect_key); } else if(!empty($_POST['screen-options-apply'])) { self::save_screen_options(); } } /** * Save "Screen Options" */ public static function save_screen_options() { check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' ); // The values will be sanitized inside the function self::save_settings('screen-options', $_POST['screen-options']); } /** * Save permastructures */ public static function save_permastructures() { global $permalink_manager_permastructs; $post_fields = $_POST; $permastructure_options = $permastructures = array(); $permastructure_types = array('post_types', 'taxonomies'); // Split permastructures & sanitize them foreach($permastructure_types as $type) { if(empty($_POST[$type]) || !is_array($_POST[$type])) { continue; } $permastructures[$type] = $_POST[$type]; foreach($permastructures[$type] as &$single_permastructure) { $single_permastructure = Permalink_Manager_Helper_Functions::sanitize_title($single_permastructure, true, false, false); $single_permastructure = trim($single_permastructure, '\/ '); } } if(!empty($_POST['permastructure-settings'])) { $permastructure_options = $_POST['permastructure-settings']; } // A. Permastructures if(!empty($permastructures['post_types']) || !empty($permastructures['taxonomies'])) { // Override the global with settings $permalink_manager_permastructs = $permastructures; // Save the settings in database update_option('permalink-manager-permastructs', $permastructures); } // B. Permastructure settings if(!empty($permastructure_options)) { self::save_settings('permastructure-settings', $permastructure_options); } } /** * Clear URIs */ public static function clear_all_uris() { global $permalink_manager_uris, $permalink_manager_redirects, $wpdb, $permalink_manager_before_sections_html; // Check if array with custom URIs exists if(empty($permalink_manager_uris)) { return; } // Count removed URIs & redirects $removed_uris = 0; $removed_redirects = 0; // Get all element IDs $element_ids = array_merge(array_keys((array) $permalink_manager_uris), array_keys((array) $permalink_manager_redirects)); // 1. Remove unused custom URI & redirects for deleted post or term foreach($element_ids as $element_id) { $count = self::clear_single_element_uris_and_redirects($element_id, true); $removed_uris = (!empty($count[0])) ? $count[0] + $removed_uris : $removed_uris; $removed_redirects = (!empty($count[1])) ? $count[1] + $removed_redirects : $removed_redirects; } // 2. Keep only a single redirect (make it unique) $removed_redirects += self::clear_redirects_array(true); // 3. Optional method to keep the permalinks unique if(apply_filters('permalink_manager_fix_uri_duplicates', false) == true) { self::fix_uri_duplicates(); } // 4. Remove items without keys /*if(!empty($permalink_manager_uris[null])) { unset($permalink_manager_uris[null]); }*/ // Save cleared URIs & Redirects if($removed_uris > 0 || $removed_redirects > 0) { update_option('permalink-manager-uris', array_filter($permalink_manager_uris)); update_option('permalink-manager-redirects', array_filter($permalink_manager_redirects)); $permalink_manager_before_sections_html .= Permalink_Manager_Admin_Functions::get_alert_message(sprintf(__( '%d Custom URIs and %d Custom Redirects were removed!', 'permalink-manager' ), $removed_uris, $removed_redirects), 'updated updated_slugs'); } else { $permalink_manager_before_sections_html .= Permalink_Manager_Admin_Functions::get_alert_message(__( 'No Custom URIs or Custom Redirects were removed!', 'permalink-manager' ), 'error updated_slugs'); } } /** * Remove plugin data */ public static function remove_plugin_data($field_name) { global $permalink_manager, $permalink_manager_before_sections_html; // Make sure that the user is allowed to remove the plugin data if(!current_user_can('manage_options')) { $permalink_manager_before_sections_html .= Permalink_Manager_Admin_Functions::get_alert_message(__( 'You are not allowed to remove Permalink Manager data!', 'permalink-manager' ), 'error updated_slugs'); } switch($field_name) { case 'uris' : $option_name = 'permalink-manager-uris'; $alert = __('Custom permalinks', 'permalink-manager'); break; case 'redirects' : $option_name = 'permalink-manager-redirects'; $alert = __('Custom redirects', 'permalink-manager'); break; case 'external-redirects' : $option_name = 'permalink-manager-external-redirects'; $alert = __('External redirects', 'permalink-manager'); break; case 'permastructs' : $option_name = 'permalink-manager-permastructs'; $alert = __('Permastructure settings', 'permalink-manager'); break; case 'settings' : $option_name = 'permalink-manager'; $alert = __('Permastructure settings', 'permalink-manager'); break; } if(!empty($option_name)) { // Remove the option from DB delete_option($option_name); // Reload globals $permalink_manager->get_options_and_globals(); $alert_message = sprintf(__('%s were removed!', 'permalink-manager'), $alert); $permalink_manager_before_sections_html .= Permalink_Manager_Admin_Functions::get_alert_message($alert_message, 'updated updated_slugs'); } } /** * Check if the post/term uses the same URI for both permalink & custom redirects */ public static function clear_single_element_duplicated_redirect($element_id, $count_removed = false, $uri = null) { global $permalink_manager_uris, $permalink_manager_redirects; $custom_uri = (empty($uri) && !empty($permalink_manager_uris[$element_id])) ? $permalink_manager_uris[$element_id] : $uri; if($custom_uri && !empty($permalink_manager_redirects[$element_id]) && in_array($custom_uri, $permalink_manager_redirects[$element_id])) { $duplicated_redirect_id = array_search($custom_uri, $permalink_manager_redirects[$element_id]); unset($permalink_manager_redirects[$element_id][$duplicated_redirect_id]); } // Check if function should only return the counts or update if($count_removed) { return (isset($duplicated_redirect_id)) ? 1 : 0; } else if(isset($duplicated_redirect_id)) { update_option('permalink-manager-redirects', array_filter($permalink_manager_redirects)); return true; } } /** * Remove unused custom URI & redirects for deleted post or term */ public static function clear_single_element_uris_and_redirects($element_id, $count_removed = false) { global $wpdb, $permalink_manager_uris, $permalink_manager_redirects; // Count removed URIs & redirects $removed_uris = 0; $removed_redirects = 0; // Only admin users can remove the broken URIs for removed post types & taxonomies $check_if_exists = (is_admin()) ? true : false; // 1. Check if element exists if(strpos($element_id, 'tax-') !== false) { $term_id = preg_replace("/[^0-9]/", "", $element_id); $taxonomy = $wpdb->get_var($wpdb->prepare("SELECT t.taxonomy FROM $wpdb->term_taxonomy AS t WHERE t.term_id = %s LIMIT 1", $term_id)); // Remove custom URIs for removed terms or disabled taxonomies $remove = (!empty($taxonomy)) ? Permalink_Manager_Helper_Functions::is_taxonomy_disabled($taxonomy, $check_if_exists) : true; } else if(is_numeric($element_id)) { $post_type = $wpdb->get_var("SELECT post_type FROM {$wpdb->prefix}posts WHERE ID = {$element_id} AND post_status NOT IN ('auto-draft', 'trash') AND post_type != 'nav_menu_item'"); // Remove custom URIs for removed, auto-draft posts or disabled post types $remove = (!empty($post_type)) ? Permalink_Manager_Helper_Functions::is_post_type_disabled($post_type, $check_if_exists) : true; // Remove custom URIs for attachments redirected with Yoast's SEO Premium $yoast_permalink_options = (class_exists('WPSEO_Premium')) ? get_option('wpseo_permalinks') : array(); if(!empty($yoast_permalink_options['redirectattachment']) && $post_type == 'attachment') { $attachment_parent = $wpdb->get_var("SELECT post_parent FROM {$wpdb->prefix}posts WHERE ID = {$element_id} AND post_type = 'attachment'"); if(!empty($attachment_parent)) { $remove = true; } } } // 2A. Remove ALL unused custom permalinks & redirects if(!empty($remove)) { // Remove URI if(!empty($permalink_manager_uris[$element_id])) { $removed_uris = 1; unset($permalink_manager_uris[$element_id]); } // Remove all custom redirects if(!empty($permalink_manager_redirects[$element_id]) && is_array($permalink_manager_redirects[$element_id])) { $removed_redirects = count($permalink_manager_redirects[$element_id]); unset($permalink_manager_redirects[$element_id]);; } } // 2B. Check if the post/term uses the same URI for both permalink & custom redirects else { $removed_redirect = self::clear_single_element_duplicated_redirect($element_id, true); $removed_redirects = (!empty($removed_redirect)) ? 1 : 0; } // Check if function should only return the counts or update if($count_removed) { return array($removed_uris, $removed_redirects); } else if(!empty($removed_uris) || !empty($removed_redirects)) { update_option('permalink-manager-uris', array_filter($permalink_manager_uris)); update_option('permalink-manager-redirects', array_filter($permalink_manager_redirects)); return true; } } /** * Make the redirects unique */ public static function clear_redirects_array($count_removed = false) { global $permalink_manager_redirects; $removed_redirects = 0; $all_redirect_duplicates = Permalink_Manager_Helper_Functions::get_all_duplicates(true); foreach($all_redirect_duplicates as $single_redirect_duplicate) { $last_element = reset($single_redirect_duplicate); foreach($single_redirect_duplicate as $redirect_key) { // Keep a single redirect if($last_element == $redirect_key) { continue; } preg_match("/redirect-([\d]+)_((?:tax-)?(?:[\d]+))/", $redirect_key, $ids); if(!empty($ids[2]) && !empty($permalink_manager_redirects[$ids[2]][$ids[1]])) { $removed_redirects++; unset($permalink_manager_redirects[$ids[2]][$ids[1]]); } } } // Check if function should only return the counts or update if($count_removed) { return $removed_redirects; } else if(isset($duplicated_redirect_id)) { update_option('permalink-manager-redirects', array_filter($permalink_manager_redirects)); return true; } } /** * Remove custom URI & redirects for any requested post or term */ public static function force_clear_single_element_uris_and_redirects($uri_key) { global $permalink_manager_uris, $permalink_manager_redirects, $permalink_manager_before_sections_html; // Check if custom URI is set if(isset($permalink_manager_uris[$uri_key])) { $uri = $permalink_manager_uris[$uri_key]; unset($permalink_manager_uris[$uri_key]); update_option('permalink-manager-uris', $permalink_manager_uris); $updated = Permalink_Manager_Admin_Functions::get_alert_message(sprintf(__( 'URI "%s" was removed successfully!', 'permalink-manager' ), $uri), 'updated'); } // Check if custom redirects are set if(isset($permalink_manager_redirects[$uri_key])) { unset($permalink_manager_redirects[$uri_key]); update_option('permalink-manager-redirects', $permalink_manager_redirects); $updated = Permalink_Manager_Admin_Functions::get_alert_message(__( 'Broken redirects were removed successfully!', 'permalink-manager' ), 'updated'); } if(empty($updated)) { $permalink_manager_before_sections_html .= Permalink_Manager_Admin_Functions::get_alert_message(__( 'URI and/or custom redirects does not exist or were already removed!', 'permalink-manager' ), 'error'); } else { // Display the alert in admin panel if(!empty($permalink_manager_before_sections_html) && is_admin()) { $permalink_manager_before_sections_html .= $updated; } return true; } } public static function force_clear_single_redirect($redirect_key) { global $permalink_manager_redirects, $permalink_manager_before_sections_html; preg_match("/redirect-([\d]+)_((?:tax-)?(?:[\d]+))/", $redirect_key, $ids); if(!empty($permalink_manager_redirects[$ids[2]][$ids[1]])) { unset($permalink_manager_redirects[$ids[2]][$ids[1]]); update_option('permalink-manager-redirects', array_filter($permalink_manager_redirects)); $permalink_manager_before_sections_html = Permalink_Manager_Admin_Functions::get_alert_message(__( 'The redirect was removed successfully!', 'permalink-manager' ), 'updated'); } } /** * Keep the permalinks unique */ public static function fix_uri_duplicates() { global $permalink_manager_uris; $duplicates = array_count_values($permalink_manager_uris); foreach($duplicates as $uri => $count) { if($count == 1) { continue; } $ids = array_keys($permalink_manager_uris, $uri); foreach($ids as $index => $id) { if($index > 0) { $permalink_manager_uris[$id] = preg_replace('/(.+?)(\.[^\.]+$|$)/', '$1-' . $index . '$2', $uri); } } } update_option('permalink-manager-uris', $permalink_manager_uris); } /** * Check if URI was used before */ function ajax_detect_duplicates($uri = null, $element_id = null) { $duplicate_alert = __("URI is already in use, please select another one!", "permalink-manager"); if(!empty($_REQUEST['custom_uris'])) { // Sanitize the array $custom_uris = Permalink_Manager_Helper_Functions::sanitize_array($_REQUEST['custom_uris']); $duplicates_array = array(); // Check each URI foreach($custom_uris as $element_id => $uri) { $duplicates_array[$element_id] = Permalink_Manager_Helper_Functions::is_uri_duplicated($uri, $element_id) ? $duplicate_alert : 0; } // Convert the output to JSON and stop the function echo json_encode($duplicates_array); } else if(!empty($_REQUEST['custom_uri']) && !empty($_REQUEST['element_id'])) { $is_duplicated = Permalink_Manager_Helper_Functions::is_uri_duplicated($uri, $element_id); echo ($is_duplicated) ? $duplicate_alert : 0; } die(); } /** * Clear sitemaps cache */ function flush_sitemaps($types = array()) { global $permalink_manager_before_sections_html; if(class_exists('WPSEO_Sitemaps_Cache')) { $sitemaps = WPSEO_Sitemaps_Cache::clear($types); $permalink_manager_before_sections_html .= Permalink_Manager_Admin_Functions::get_alert_message(__( 'Sitemaps were updated!', 'permalink-manager' ), 'updated'); } } /** * Import old URIs from "Custom Permalinks" (Pro) */ function import_custom_permalinks_uris() { Permalink_Manager_Third_Parties::import_custom_permalinks_uris(); } /** * "Automatically remove duplicates" (if enabled) in background */ function clean_permalinks_hook() { global $permalink_manager_uris, $permalink_manager_redirects; // Backup the custom URIs if(is_array($permalink_manager_uris)) { update_option('permalink-manager-uris_backup', $permalink_manager_uris, false); } // Backup the custom redirects if(is_array($permalink_manager_redirects)) { update_option('permalink-manager-redirects_backup', $permalink_manager_redirects, false); } self::clear_all_uris(); } function clean_permalinks_cronjob() { global $permalink_manager_options; $event_name = 'clean_permalinks_event'; // Set-up the "Automatically remove duplicates" function that runs in background once a day if(!empty($permalink_manager_options['general']['auto_remove_duplicates']) && $permalink_manager_options['general']['auto_remove_duplicates'] == 2) { if(!wp_next_scheduled($event_name)) { wp_schedule_event(time(), 'daily', $event_name); } } else if(wp_next_scheduled($event_name)) { $event_timestamp = wp_next_scheduled($event_name); wp_unschedule_event($event_timestamp, $event_name); } } }