$label ) { // Check if taxonomy is allowed if ( Permalink_Manager_Helper_Functions::is_taxonomy_disabled( $tax ) ) { continue; } add_action( "edited_{$tax}", array( $this, 'update_term_uri' ), 10, 2 ); add_action( "create_{$tax}", array( $this, 'update_term_uri' ), 10, 2 ); add_action( "delete_{$tax}", array( $this, 'remove_term_uri' ), 10, 2 ); // Check the user capabilities if ( is_admin() ) { $edit_uris_cap = ( ! empty( $permalink_manager_options['general']['edit_uris_cap'] ) ) ? $permalink_manager_options['general']['edit_uris_cap'] : 'publish_posts'; if ( current_user_can( $edit_uris_cap ) ) { add_action( "{$tax}_add_form_fields", array( $this, 'edit_uri_box' ), 10, 1 ); add_action( "{$tax}_edit_form_fields", array( $this, 'edit_uri_box' ), 10, 1 ); add_filter( "manage_edit-{$tax}_columns", array( $this, 'quick_edit_column' ) ); add_filter( "manage_{$tax}_custom_column", array( $this, 'quick_edit_column_content' ), 20, 3 ); } } } } /** * Apply the custom permalinks to the terms * * @param string $permalink * @param WP_Term|int $term * * @return string */ function custom_tax_permalinks( $permalink, $term ) { global $permalink_manager_uris, $permalink_manager_options, $permalink_manager_ignore_permalink_filters; // Do not filter permalinks in Customizer if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) { return $permalink; } // Do not filter in WPML String Editor if ( ! empty( $_REQUEST['icl_ajx_action'] ) && $_REQUEST['icl_ajx_action'] == 'icl_st_save_translation' ) { return $permalink; } // Do not filter if $permalink_manager_ignore_permalink_filters global is set if ( ! empty( $permalink_manager_ignore_permalink_filters ) ) { return $permalink; } $term = ( is_numeric( $term ) ) ? get_term( $term ) : $term; // Check if the term is allowed if ( empty( $term->term_id ) || Permalink_Manager_Helper_Functions::is_term_excluded( $term ) || ! is_string( $permalink ) ) { return $permalink; } // Get term id $term_id = $term->term_id; // Save the old permalink to separate variable $old_permalink = $permalink; if ( isset( $permalink_manager_uris["tax-{$term_id}"] ) ) { // Start with homepage URL $permalink = Permalink_Manager_Permastructure_Functions::get_permalink_base( $term ); // Encode URI? if ( ! empty( $permalink_manager_options['general']['decode_uris'] ) ) { $permalink .= rawurldecode( "/{$permalink_manager_uris["tax-{$term_id}"]}" ); } else { $permalink .= Permalink_Manager_Helper_Functions::encode_uri( "/{$permalink_manager_uris["tax-{$term_id}"]}" ); } } else if ( ! empty( $permalink_manager_options['general']['decode_uris'] ) ) { $permalink = rawurldecode( $permalink ); } return apply_filters( 'permalink_manager_filter_final_term_permalink', $permalink, $term, $old_permalink ); } /** * Check if the provided slug is unique and then update it with SQL query. * * @param string $slug * @param int $id * @param bool $preview_mode * * @return string */ static function update_slug_by_id( $slug, $id, $preview_mode = false ) { global $wpdb; // Update slug and make it unique $term = get_term( intval( $id ) ); $slug = ( empty( $slug ) ) ? get_the_title( $term->name ) : $slug; $slug = sanitize_title( $slug ); $new_slug = wp_unique_term_slug( $slug, $term ); if ( ! $preview_mode ) { $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->terms} SET slug = %s WHERE term_id = %d", $new_slug, $id ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } return $new_slug; } /** * Get the currently used custom permalink (or default/empty URI) * * @param int $term_id * @param bool $native_uri * @param bool $no_fallback * * @return string */ public static function get_term_uri( $term_id, $native_uri = false, $no_fallback = false ) { global $permalink_manager_uris; // Check if input is term object $term = ( isset( $term_id->term_id ) ) ? $term_id->term_id : get_term( $term_id ); if ( ! empty( $permalink_manager_uris["tax-{$term_id}"] ) ) { $final_uri = $permalink_manager_uris["tax-{$term_id}"]; } else if ( ! $no_fallback ) { $final_uri = self::get_default_term_uri( $term->term_id, $native_uri ); } else { $final_uri = ''; } return $final_uri; } /** * Get the default custom permalink (not overwritten by the user) or native permalink (unfiltered) * * @param WP_Term|int $term * @param bool $native_uri * @param bool $check_if_disabled * * @return string */ public static function get_default_term_uri( $term, $native_uri = false, $check_if_disabled = false ) { global $permalink_manager_permastructs, $wp_taxonomies; // Disable WPML adjust ID filter if ( class_exists( 'SitePress' ) ) { add_filter( 'wpml_disable_term_adjust_id', '__return_true', 999 ); } // 1. Load all bases & term $term = is_object( $term ) ? $term : get_term( $term ); // $term_id = $term->term_id; $taxonomy_name = $term->taxonomy; $taxonomy = get_taxonomy( $taxonomy_name ); $term_slug = $term->slug; $top_parent_slug = ''; // 1A. Check if taxonomy is allowed if ( $check_if_disabled && Permalink_Manager_Helper_Functions::is_taxonomy_disabled( $taxonomy ) ) { return ''; } // 2A. Get the native permastructure $native_permastructure = Permalink_Manager_Permastructure_Functions::get_default_permastruct( $taxonomy_name ); // 2B. Get the permastructure if ( $native_uri || empty( $permalink_manager_permastructs['taxonomies'][ $taxonomy_name ] ) ) { $permastructure = $native_permastructure; } else { $permastructure = apply_filters( 'permalink_manager_filter_permastructure', $permalink_manager_permastructs['taxonomies'][ $taxonomy_name ], $term ); } // 2C. Set the permastructure $default_base = ( ! empty( $permastructure ) ) ? trim( $permastructure, '/' ) : ""; // 3A. Check if the taxonomy has custom permastructure set if ( empty( $default_base ) && ! isset( $permalink_manager_permastructs['taxonomies'][ $taxonomy_name ] ) ) { if ( 'category' == $taxonomy_name ) { $default_uri = "?cat={$term->term_id}"; } elseif ( $taxonomy->query_var ) { $default_uri = "?{$taxonomy->query_var}={$term_slug}"; } else if ( ! empty( $term_slug ) ) { $default_uri = "?taxonomy={$taxonomy_name}&term={$term_slug}"; } else { $default_uri = ''; } } // 3B. Use custom permastructure else { $default_uri = $default_base; // 3B. Get the full slug $term_slug = Permalink_Manager_Helper_Functions::remove_slashes( $term_slug ); $custom_slug = $full_custom_slug = Permalink_Manager_Helper_Functions::force_custom_slugs( $term_slug, $term, true, null, false ); $term_title_slug = Permalink_Manager_Helper_Functions::force_custom_slugs( $term_slug, $term, true, 1 ); $full_native_slug = $term_slug; // Add ancestors to hierarchical taxonomy if ( is_taxonomy_hierarchical( $taxonomy_name ) ) { $ancestors = get_ancestors( $term->term_id, $taxonomy_name, 'taxonomy' ); foreach ( $ancestors as $ancestor ) { $ancestor_term = get_term( $ancestor, $taxonomy_name ); $full_native_slug = $ancestor_term->slug . '/' . $full_native_slug; $full_custom_slug = Permalink_Manager_Helper_Functions::force_custom_slugs( $ancestor_term->slug, $ancestor_term ) . '/' . $full_custom_slug; } // Get top parent term if ( strpos( $default_uri, "%{$taxonomy_name}_top%" ) === false || strpos( $default_uri, "%term_top%" ) === false ) { $top_parent_slug = Permalink_Manager_Helper_Functions::get_term_full_slug( $term, $ancestors, 3, $native_uri ); } } // Allow filter the default slug (only custom permalinks) if ( ! $native_uri ) { $full_slug = apply_filters( 'permalink_manager_filter_default_term_slug', $full_custom_slug, $term, $term->name ); } else { $full_slug = $full_native_slug; } // Get the taxonomy slug if ( ! empty( $wp_taxonomies[ $taxonomy_name ]->rewrite['slug'] ) ) { $taxonomy_name_slug = $wp_taxonomies[ $taxonomy_name ]->rewrite['slug']; } else if ( is_string( $wp_taxonomies[ $taxonomy_name ]->rewrite ) ) { $taxonomy_name_slug = $wp_taxonomies[ $taxonomy_name ]->rewrite; } else { $taxonomy_name_slug = $taxonomy_name; } $taxonomy_name_slug = apply_filters( 'permalink_manager_filter_taxonomy_slug', $taxonomy_name_slug, $term, $taxonomy_name ); $slug_tags = array( "%term_name%", "%term_flat%", "%{$taxonomy_name}%", "%{$taxonomy_name}_flat%", "%term_top%", "%{$taxonomy_name}_top%", "%native_slug%", "%native_title%", "%taxonomy%", "%term_id%" ); $slug_tags_replacement = array( $full_slug, $custom_slug, $full_slug, $custom_slug, $top_parent_slug, $top_parent_slug, $full_native_slug, $term_title_slug, $taxonomy_name_slug, $term->term_id ); // Check if any term tag is present in custom permastructure $do_not_append_slug = Permalink_Manager_Permastructure_Functions::is_slug_tag_present( $default_uri, $slug_tags, $term ); // Replace the term tags with slugs or append the slug if no term tag is defined if ( ! empty( $do_not_append_slug ) ) { $default_uri = str_replace( $slug_tags, $slug_tags_replacement, $default_uri ); } else { $default_uri .= "/{$full_slug}"; } } // Restore WPML adjust ID filter if ( class_exists( 'SitePress' ) ) { remove_filter( 'wpml_disable_term_adjust_id', '__return_true', 999 ); } return apply_filters( 'permalink_manager_filter_default_term_uri', $default_uri, $term->slug, $term, $term_slug, $native_uri ); } /** * Get array with all term items based on the user-selected settings in the "Bulk tools" form * * @return array|false */ public static function get_items() { global $wpdb, $permalink_manager_options; // Check if taxonomies are not empty if ( empty( $_POST['taxonomies'] ) || ! is_array( $_POST['taxonomies'] ) ) { return false; } $taxonomy_names_array = array_map( 'sanitize_key', $_POST['taxonomies'] ); $taxonomy_names_in = Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $taxonomy_names_array, false ); // Filter the terms by IDs $where = ''; if ( ! empty( $_POST['ids'] ) ) { // Remove whitespaces and prepare array with IDs and/or ranges $ids = esc_sql( preg_replace( '/\s*/m', '', $_POST['ids'] ) ); preg_match_all( "/([\d]+(?:-?[\d]+)?)/x", $ids, $groups ); // Prepare the extra ID filters $where .= "AND ("; foreach ( $groups[0] as $group ) { $where .= ( $group == reset( $groups[0] ) ) ? "" : " OR "; // A. Single number if ( is_numeric( $group ) ) { $where .= "(t.term_id = {$group})"; } // B. Range else if ( substr_count( $group, '-' ) ) { $range_edges = explode( "-", $group ); $where .= "(t.term_id BETWEEN {$range_edges[0]} AND {$range_edges[1]})"; } } $where .= ")"; } // Get excluded items $excluded_terms = (array) apply_filters( 'permalink_manager_excluded_term_ids', array() ); if ( ! empty( $excluded_terms ) ) { $where .= sprintf( " AND t.term_id NOT IN (%s) ", Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $excluded_terms ) ); } // Check the auto-update mode // A. Allow only user-approved posts if ( ! empty( $permalink_manager_options["general"]["auto_update_uris"] ) && $permalink_manager_options["general"]["auto_update_uris"] == 2 ) { $where .= " AND tm.meta_value IN (1, -1) "; } // B. Allow all posts not disabled by the user else { $where .= " AND (tm.meta_value IS NULL OR tm.meta_value IN (1, -1)) "; } // Get the rows before they are altered $query = "SELECT t.slug, t.name, t.term_id, tt.taxonomy FROM {$wpdb->terms} AS t INNER JOIN {$wpdb->term_taxonomy} AS tt ON tt.term_id = t.term_id LEFT JOIN {$wpdb->termmeta} AS tm ON (tm.term_id = t.term_id AND tm.meta_key = 'auto_update_uri') WHERE tt.taxonomy IN ({$taxonomy_names_in}) {$where}"; $query = apply_filters( 'permalink_manager_get_items_query', $query, $where, 'taxonomies' ); return $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter } /** * Process the permalinks/slugs "Find & replace" & "Regenerate/rest" tool * * @param array $chunk * @param string $mode * @param string $operation * @param string $old_string * @param string $new_string * @param bool $preview_mode * * @return array|false */ public static function bulk_process_items( $chunk = null, $mode = '', $operation = '', $old_string = '', $new_string = '', $preview_mode = false ) { // Reset variables $updated_slugs_count = 0; $updated_array = array(); // Get the rows before they are altered $terms_to_update = ( ! empty( $chunk ) ) ? $chunk : self::get_items(); if ( empty( $operation ) ) { return false; } // Now if the array is not empty use IDs from each subarray as a key if ( $terms_to_update ) { foreach ( $terms_to_update as $row ) { $this_term = get_term( $row['term_id'] ); // Get default & native URL $native_uri = self::get_default_term_uri( $this_term, true ); $default_uri = self::get_default_term_uri( $this_term ); $old_custom_uri = Permalink_Manager_URI_Functions::get_single_uri( $row['term_id'], true, true, true ); $old_uri = ( ! empty( $old_custom_uri ) ) ? $old_custom_uri : $native_uri; $old_term_name = $row['slug']; if ( $operation == 'regenerate' ) { if ( $mode == 'slugs' ) { $new_uri = $old_uri; $new_term_name = Permalink_Manager_Helper_Functions::sanitize_title( $row['name'] ); } else if ( $mode == 'native' ) { $new_uri = $native_uri; $new_term_name = $old_term_name; } else { $new_uri = $default_uri; $new_term_name = $old_term_name; } } else { list( $new_term_name, $new_uri ) = Permalink_Manager_Helper_Functions::replace_uri_slug( $old_string, $new_string, $old_term_name, $old_uri, $mode ); } // Check if native slug should be changed if ( $mode == 'slugs' && $old_term_name !== $new_term_name ) { $new_term_name = self::update_slug_by_id( $new_term_name, $row['term_id'], $preview_mode ); } $new_uri = apply_filters( 'permalink_manager_pre_update_term_uri', $new_uri, $row['term_id'], $old_uri, $native_uri, $default_uri ); if ( ! ( empty( $new_uri ) ) && ( $old_custom_uri !== $new_uri ) || ( $old_term_name !== $new_term_name ) ) { if ( ! $preview_mode && ( $old_custom_uri !== $new_uri ) ) { Permalink_Manager_URI_Functions::save_single_uri( $row['term_id'], $new_uri, true, false ); do_action( 'permalink_manager_updated_term_uri', $row['term_id'], $new_uri, $old_uri, $native_uri, $default_uri ); } $updated_array[] = array( 'item_title' => $row['name'], 'ID' => $row['term_id'], 'old_uri' => $old_uri, 'new_uri' => $new_uri, 'old_slug' => $old_term_name, 'new_slug' => $new_term_name, 'tax' => $this_term->taxonomy ); $updated_slugs_count ++; } } // Save all custom permalinks if ( ! $preview_mode ) { Permalink_Manager_URI_Functions::save_all_uris(); } $output = array( 'updated' => $updated_array, 'updated_count' => $updated_slugs_count ); wp_reset_postdata(); } return ( ! empty( $output ) ) ? $output : false; } /** * Save the custom permalinks in "Bulk URI Editor" tool * * @return array|false */ static public function update_all_permalinks() { // Setup needed variables $updated_slugs_count = 0; $updated_array = array(); $new_uris = isset( $_POST['uri'] ) ? $_POST['uri'] : array(); // Double check if the slugs and ids are stored in arrays if ( ! is_array( $new_uris ) ) { $new_uris = explode( ',', $new_uris ); } if ( ! empty( $new_uris ) ) { foreach ( $new_uris as $id => $new_uri ) { // Remove prefix from field name to obtain term id $term_id = filter_var( str_replace( 'tax-', '', $id ), FILTER_SANITIZE_NUMBER_INT ); $this_term = get_term( $term_id ); $old_uri = Permalink_Manager_URI_Functions::get_single_uri( $term_id, false, true, true ); $new_uri = ( empty( $new_uri ) ) ? null : $new_uri; // If the string is empty, convert it to null to force the default permalink $final_uri = self::save_uri( $this_term, $new_uri, false, false, false ); if ( $final_uri ) { $updated_array[] = array( 'item_title' => $this_term->name, 'ID' => $term_id, 'old_uri' => $old_uri, 'new_uri' => $final_uri, 'tax' => $this_term->taxonomy ); $updated_slugs_count ++; } } // Save all custom permalinks Permalink_Manager_URI_Functions::save_all_uris(); $output = array( 'updated' => $updated_array, 'updated_count' => $updated_slugs_count ); } return ( ! empty( $output ) ) ? $output : false; } /** * Allow to edit URIs from "New Term" & "Edit Term" admin pages * * @param WP_Term $term */ public function edit_uri_box( $term = '' ) { // Check if the term is excluded if ( empty( $term ) || Permalink_Manager_Helper_Functions::is_term_excluded( $term ) ) { return; } // Stop the hook (if needed) if ( ! empty( $term->taxonomy ) ) { $show_uri_editor = apply_filters( "permalink_manager_show_uri_editor_term", true, $term, $term->taxonomy ); if ( ! $show_uri_editor ) { return; } } $label = __( "Custom URI", "permalink-manager" ); $description = __( "Clear/leave the field empty to use the default permalink.", "permalink-manager" ); // A. New term if ( empty( $term->term_id ) ) { $html = "
%s
", $description ); $html .= "