File "permalink-manager-actions.php"

Full Path: /home/veodprin/public_html/wp-content/plugins/permalink-manager/includes/core/permalink-manager-actions.php
File size: 30.61 KB
MIME-type: text/x-php
Charset: utf-8

<?php

/**
 * Additional hooks for "Permalink Manager Pro"
 */
class Permalink_Manager_Actions {

	public function __construct() {
		add_action( 'admin_init', array( $this, 'trigger_action' ), 9 );
		add_action( 'admin_init', array( $this, 'extra_actions' ) );

		// Ajax-based functions
		if ( is_admin() ) {
			add_action( 'wp_ajax_pm_bulk_tools', array( $this, 'ajax_bulk_tools' ) );
			add_action( 'wp_ajax_pm_save_permalink', array( $this, 'ajax_save_permalink' ) );
			add_action( 'wp_ajax_pm_detect_duplicates', array( $this, 'ajax_detect_duplicates' ) );
			add_action( 'wp_ajax_pm_dismissed_notice_handler', array( $this, 'ajax_hide_global_notice' ) );
		}

		add_action( 'clean_permalinks_event', array( $this, 'clean_permalinks_hook' ) );
		add_action( 'init', array( $this, 'clean_permalinks_cronjob' ) );
	}

	/**
	 * Route the requests to functions that save datasets with associated callbacks
	 */
	public function trigger_action() {
		global $permalink_manager_after_sections_html;

		// 1. Check if the form was submitted
		if ( empty( $_POST ) ) {
			return;
		}

		// 2. Do nothing if search query is not empty
		if ( isset( $_REQUEST['search-submit'] ) || isset( $_REQUEST['months-filter-button'] ) ) {
			return;
		}

		$actions_map = array(
			'uri_editor'                     => array( 'function' => 'update_all_permalinks', 'display_uri_table' => true ),
			'permalink_manager_options'      => array( 'function' => 'save_settings' ),
			'permalink_manager_permastructs' => array( 'function' => 'save_permastructures' ),
			'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 ) && isset( $updated_slugs_array ) ) {
			$permalink_manager_after_sections_html .= Permalink_Manager_Admin_Functions::display_updated_slugs( $updated_slugs_array );
		}
	}

	/**
	 * Route the requests to the additional tools-related functions with the relevant callbacks
	 */
	public static function extra_actions() {
		global $permalink_manager_before_sections_html;

		if ( current_user_can( 'manage_options' ) && ! empty( $_GET['permalink-manager-nonce'] ) ) {
			// Check if the nonce field is correct
			$nonce = sanitize_key( $_GET['permalink-manager-nonce'] );

			if ( ! wp_verify_nonce( $nonce, 'permalink-manager' ) ) {
				$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' );

				return;
			}

			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();
		}
	}

	/**
	 * Bulk remove obsolete custom permalinks and redirects
	 */
	public static function clear_all_uris() {
		global $permalink_manager_uris, $permalink_manager_redirects, $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();

		// 3. Optional method to keep the permalinks unique
		if ( apply_filters( 'permalink_manager_fix_uri_duplicates', false ) ) {
			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 obsolete custom permalink & redirects for specific post or term
	 *
	 * @param string|int $element_id
	 * @param bool $count_removed
	 *
	 * @return array
	 */
	public static function clear_single_element_uris_and_redirects( $element_id, $count_removed = false ) {
		global $wpdb, $permalink_manager_uris, $permalink_manager_redirects, $permalink_manager_options;

		// 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_admin = is_admin();

		// Check if the advanced mode is turned on
		$advanced_mode = Permalink_Manager_Helper_Functions::is_advanced_mode_on();

		// If "Disable URI Editor to disallow Permalink changes" is set globally, the pages that follow the global settings should also be removed
		if ( $advanced_mode && ! empty( $permalink_manager_options["general"]["auto_update_uris"] ) && $permalink_manager_options["general"]["auto_update_uris"] == 2 ) {
			$strict_mode = true;
		} else {
			$strict_mode = false;
		}

		// 1. Check if element exists
		if ( strpos( $element_id, 'tax-' ) !== false ) {
			$term_id   = preg_replace( "/[^0-9]/", "", $element_id );
			$term_info = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy, meta_value FROM {$wpdb->term_taxonomy} AS t LEFT JOIN {$wpdb->termmeta} AS tm ON tm.term_id = t.term_id AND tm.meta_key = 'auto_update_uri' WHERE t.term_id = %d", $term_id ) );

			// Custom URIs for disabled taxonomies may only be deleted via the admin dashboard, although they will always be removed if the term no longer exists in the database
			$remove = ( ! empty( $term_info->taxonomy ) ) ? Permalink_Manager_Helper_Functions::is_taxonomy_disabled( $term_info->taxonomy, $check_if_admin ) : true;

			// Remove custom URIs for URIs disabled in URI Editor
			if ( $strict_mode ) {
				$remove = ( empty( $term_info->meta_value ) || $term_info->meta_value == 2 ) ? true : $remove;
			} else {
				$remove = ( ! empty( $term_info->meta_value ) && $term_info->meta_value == 2 ) ? true : $remove;
			}
		} else if ( is_numeric( $element_id ) ) {
			$post_info = $wpdb->get_row( $wpdb->prepare( "SELECT post_type, meta_value FROM {$wpdb->posts} AS p LEFT JOIN {$wpdb->postmeta} AS pm ON pm.post_ID = p.ID AND pm.meta_key = 'auto_update_uri' WHERE ID = %d AND post_status NOT IN ('auto-draft', 'trash') AND post_type != 'nav_menu_item'", $element_id ) );

			// Custom URIs for disabled post types may only be deleted via the admin dashboard, although they will always be removed if the post no longer exists in the database
			$remove = ( ! empty( $post_info->post_type ) ) ? Permalink_Manager_Helper_Functions::is_post_type_disabled( $post_info->post_type, $check_if_admin ) : true;

			// Remove custom URIs for URIs disabled in URI Editor
			if ( $strict_mode ) {
				$remove = ( empty( $post_info->meta_value ) || $post_info->meta_value == 2 ) ? true : $remove;
			} else {
				$remove = ( ! empty( $post_info->meta_value ) && $post_info->meta_value == 2 ) ? true : $remove;
			}

			// 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_info->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, false );
			$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 array();
	}

	/**
	 * Remove the duplicated custom redirect if the post/term has the same URI for both custom permalink and custom redirect
	 *
	 * @param string|int $element_id
	 * @param bool $save_redirects
	 * @param string $uri
	 *
	 * @return int
	 */
	public static function clear_single_element_duplicated_redirect( $element_id, $save_redirects = true, $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 ] );
		}

		// Update the redirects array in the database if the duplicated redirect was unset
		if ( isset( $duplicated_redirect_id ) && $save_redirects ) {
			update_option( 'permalink-manager-redirects', array_filter( $permalink_manager_redirects ) );
		}

		return ( isset( $duplicated_redirect_id ) ) ? 1 : 0;
	}

	/**
	 * Remove the duplicated if the same URI is used for multiple custom redirects and return the removed redirects count
	 *
	 * @param bool $save_redirects
	 *
	 * @return int
	 */
	public static function clear_redirects_array( $save_redirects = false ) {
		global $permalink_manager_redirects;

		$removed_redirects = 0;

		$all_redirect_duplicates = Permalink_Manager_Helper_Functions::get_all_duplicates();

		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+|\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] ] );
				}
			}
		}

		// Update the redirects array in the database if the duplicated redirect was unset
		if ( isset( $duplicated_redirect_id ) && $save_redirects ) {
			update_option( 'permalink-manager-redirects', array_filter( $permalink_manager_redirects ) );
		}

		return $removed_redirects;
	}

	/**
	 * If the custom permalink is duplicated, append the index (-2, -3, etc.)
	 */
	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 );
	}

	/**
	 * Remove custom permalinks & custom redirects for requested post or term
	 *
	 * @param $uri_key
	 *
	 * @return bool
	 */
	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;
	}

	/**
	 * Remove only custom redirects for requested post or term
	 *
	 * @param string $redirect_key
	 */
	public static function force_clear_single_redirect( $redirect_key ) {
		global $permalink_manager_redirects, $permalink_manager_before_sections_html;

		preg_match( "/redirect-(\d+)_(tax-\d+|\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' );
		}
	}

	/**
	 * 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 the plugin settings
	 *
	 * @param bool $field
	 * @param bool $value
	 * @param bool $display_alert
	 */
	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' ) : "";
	}

	/**
	 * Save the permastructures
	 */
	public static function save_permastructures() {
		global $permalink_manager_permastructs;

		$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 );
		}
	}

	/**
	 * Update all permalinks in "Bulk URI 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();
		}
	}

	/**
	 * Remove a specific section of the plugin data stored in the database
	 *
	 * @param $field_name
	 */
	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;
			default :
				$alert = '';
		}

		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' );
		}
	}

	/**
	 * Trigger bulk tools ("Regenerate & reset", "Find & replace") via AJAX
	 */
	function ajax_bulk_tools() {
		global $sitepress, $wpdb;

		// Define variables
		$return = array( 'alert' => Permalink_Manager_Admin_Functions::get_alert_message( __( '<strong>No slugs</strong> 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';
		} 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';
		}

		// Get the session ID
		$uniq_id = ( ! empty( $_POST['pm_session_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( __( '<strong>No taxonomy</strong> 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( __( '<strong>No post type</strong> 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( __( '<strong>No post status</strong> 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}" );

			// Get the iteration count and chunk size
			$iteration  = isset( $_POST['iteration'] ) ? intval( $_POST['iteration'] ) : 1;
			$chunk_size = apply_filters( 'permalink_manager_chunk_size', 50 );

			if ( empty( $items ) && ! empty ( $chunk_size ) ) {
				$items = $class_name::get_items();

				if ( ! empty( $items ) ) {
					// Count how many items need to be processed
					$total = count( $items );

					// Split items array into chunks and save them to transient
					$items = array_chunk( $items, $chunk_size );

					set_transient( "pm_{$uniq_id}", $items, 600 );

					// 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[ $iteration - 1 ] ) ) {
				$chunk = $items[ $iteration - 1 ];

				// Check how many iterations are needed
				$total_iterations = count( $items );

				// 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 ) );
					$return['updated_count'] = $output['updated_count'];
				}

				// Send total number of processed items with a first chunk
				if ( ! empty( $total ) && ! empty( $total_iterations ) && $iteration == 1 ) {
					$return['total'] = $total;
					$return['items'] = $items;
				}

				$return['iteration']        = $iteration;
				$return['total_iterations'] = $total_iterations;
				$return['progress']         = $chunk_size * $iteration;
				$return['chunk']            = $chunk;

				// After all chunks are processed remove the transient
				if ( $iteration == $total_iterations ) {
					delete_transient( "pm_{$uniq_id}" );
				}
			}

			// 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 ajax_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();
		}
	}

	/**
	 * Check if URI was used before
	 *
	 * @param string $uri
	 * @param string $element_id
	 */
	function ajax_detect_duplicates( $uri = null, $element_id = null ) {
		$duplicate_alert = __( "Permalink 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();
	}

	/**
	 * Hide global notices (AJAX)
	 */
	function ajax_hide_global_notice() {
		global $permalink_manager_alerts;

		// Get the ID of the alert
		$alert_id = ( ! empty( $_REQUEST['alert_id'] ) ) ? sanitize_title( $_REQUEST['alert_id'] ) : "";
		if ( ! empty( $permalink_manager_alerts[ $alert_id ] ) ) {
			$dismissed_transient_name = sprintf( 'permalink-manager-notice_%s', $alert_id );
			$dismissed_time           = ( ! empty( $permalink_manager_alerts[ $alert_id ]['dismissed_time'] ) ) ? (int) $permalink_manager_alerts[ $alert_id ]['dismissed_time'] : DAY_IN_SECONDS;

			set_transient( $dismissed_transient_name, 1, $dismissed_time );
		}
	}

	/**
	 * Import old URIs from "Custom Permalinks" (Pro)
	 */
	function import_custom_permalinks_uris() {
		Permalink_Manager_Third_Parties::import_custom_permalinks_uris();
	}

	/**
	 * Remove the duplicated custom permalinks & redirects automatically in the 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();
	}

	/**
	 * Schedule the function that automatically removes the custom permalinks & redirects duplicates
	 */
	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 );
		}
	}

}