Plugin Directory

Changeset 3332124


Ignore:
Timestamp:
07/22/2025 09:35:24 AM (8 months ago)
Author:
sasiddiqui
Message:

Bump to v3.0.0

Location:
custom-permalinks
Files:
12 added
4 deleted
20 edited
1 copied

Legend:

Unmodified
Added
Removed
  • custom-permalinks/tags/3.0.0/admin/class-custom-permalinks-admin.php

    r3138206 r3332124  
    2222
    2323    /**
     24     * JS file suffix extension.
     25     *
     26     * @var string
     27     */
     28    private $js_file_suffix = '.min.js';
     29
     30    /**
    2431     * Initializes WordPress hooks.
    2532     */
    2633    public function __construct() {
    2734        /*
    28          * Css file suffix (version number with extension).
     35         * CSS file suffix (version number with extension).
    2936         */
    3037        $this->css_file_suffix = '-' . CUSTOM_PERMALINKS_VERSION . '.min.css';
     38
     39        /*
     40         * JS file suffix (version number with extension).
     41         */
     42        $this->js_file_suffix = '-' . CUSTOM_PERMALINKS_VERSION . '.min.js';
    3143
    3244        add_action( 'admin_init', array( $this, 'privacy_policy' ) );
     
    7385            array( $this, 'taxonomy_permalinks_page' )
    7486        );
     87        $post_settings_page       = add_submenu_page(
     88            'cp-post-permalinks',
     89            __( 'Post Types Settings', 'custom-permalinks' ),
     90            __( 'Post Types Settings', 'custom-permalinks' ),
     91            'custom_permalinks_post_settings',
     92            'custom-permalinks-post-settings',
     93            array( $this, 'post_settings_page' )
     94        );
    7595        $about_page               = add_submenu_page(
    7696            'cp-post-permalinks',
     
    91111        );
    92112        add_action(
     113            'admin_print_styles-' . $post_settings_page,
     114            array( $this, 'add_post_settings_style' )
     115        );
     116        add_action(
    93117            'admin_print_styles-' . $about_page . '',
    94118            array( $this, 'add_about_style' )
     119        );
     120    }
     121
     122    /**
     123     * Add post settings page style.
     124     *
     125     * @since 3.0.0
     126     */
     127    public function add_post_settings_style() {
     128        wp_enqueue_script(
     129            'custom-permalinks-post-settings',
     130            plugins_url(
     131                '/assets/js/post-settings' . $this->js_file_suffix,
     132                CUSTOM_PERMALINKS_FILE
     133            ),
     134            array(),
     135            CUSTOM_PERMALINKS_VERSION,
     136            array(
     137                'strategy'  => 'async',
     138                'in_footer' => true,
     139            )
     140        );
     141
     142        wp_enqueue_style(
     143            'custom-permalinks-post-settings',
     144            plugins_url(
     145                '/assets/css/post-settings' . $this->css_file_suffix,
     146                CUSTOM_PERMALINKS_FILE
     147            ),
     148            array(),
     149            CUSTOM_PERMALINKS_VERSION
    95150        );
    96151    }
     
    140195    public function taxonomy_permalinks_page() {
    141196        Custom_Permalinks_Taxonomies_Table::output();
     197
     198        add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );
     199    }
     200
     201    /**
     202     * Calls another Function which shows the Post Types Settings Page.
     203     *
     204     * @since 3.0.0
     205     */
     206    public function post_settings_page() {
     207        new Custom_Permalinks_Post_Types_Settings();
    142208
    143209        add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );
  • custom-permalinks/tags/3.0.0/admin/class-custom-permalinks-post-types.php

    r3136157 r3332124  
    2626
    2727        $total_posts = wp_cache_get( 'total_posts_result', 'custom_permalinks' );
    28         if ( ! $total_posts ) {
     28        if ( false === $total_posts ) {
    2929            $sql_query = "
    3030                SELECT COUNT(p.ID) FROM $wpdb->posts AS p
     
    6363            }
    6464
    65             wp_cache_set( 'total_posts_result', $total_posts, 'custom_permalinks' );
     65            wp_cache_set( 'total_posts_result', $total_posts, 'custom_permalinks', 60 );
    6666        }
    6767
     
    8484
    8585        $posts = wp_cache_get( 'post_type_results', 'custom_permalinks' );
    86         if ( ! $posts ) {
     86        if ( false === $posts ) {
    8787            $page_offset = ( $page_number - 1 ) * $per_page;
    8888            $order_by    = 'p.ID';
     
    156156            // phpcs:enable WordPress.Security.NonceVerification.Recommended
    157157
    158             wp_cache_set( 'post_type_results', $posts, 'custom_permalinks' );
     158            wp_cache_set( 'post_type_results', $posts, 'custom_permalinks', 60 );
    159159        }
    160160
  • custom-permalinks/tags/3.0.0/admin/class-custom-permalinks-taxonomies.php

    r2844616 r3332124  
    3939    public static function total_permalinks() {
    4040        $total_taxonomies = wp_cache_get( 'total_taxonomies_result', 'custom_permalinks' );
    41         if ( ! $total_taxonomies ) {
     41        if ( false === $total_taxonomies ) {
    4242            $search_taxonomy  = array();
    4343            $taxonomy_table   = get_option( 'custom_permalink_table' );
     
    6666            }
    6767
    68             wp_cache_set( 'total_taxonomies_result', $total_taxonomies, 'custom_permalinks' );
     68            wp_cache_set( 'total_taxonomies_result', $total_taxonomies, 'custom_permalinks', 60 );
    6969        }
    7070
     
    8585    public static function get_permalinks( $per_page = 20, $page_number = 1 ) {
    8686        $taxonomies = wp_cache_get( 'taxonomies_results', 'custom_permalinks' );
    87         if ( ! $taxonomies ) {
     87        if ( false === $taxonomies ) {
    8888            $page_offset     = ( $page_number - 1 ) * $per_page;
    8989            $taxonomy_table  = get_option( 'custom_permalink_table' );
     
    130130            }
    131131
    132             wp_cache_set( 'taxonomies_results', $taxonomies, 'custom_permalinks' );
     132            wp_cache_set( 'taxonomies_results', $taxonomies, 'custom_permalinks', 60 );
    133133        }
    134134
  • custom-permalinks/tags/3.0.0/changelog.txt

    r2842482 r3332124  
    22
    33This file contains only old changelog. See readme.txt for newer versions.
     4
     5= 2.8.0 - Apr 29, 2025 =
     6
     7* Bug:
     8  * Resolved pagination issue with custom permalinks (now supports /page/{number} format correctly).
     9* Enhancements:
     10    * Added compatibility with Polylang 3.7.
     11    * Metabox is now hidden for post types that are not publicly queryable.
     12
     13= 2.7.0 - Aug 20, 2024 =
     14
     15* Bug
     16  * [Passing null to parameter string is deprecated](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/86)
     17  * [Fix PHP warning with empty permalink on new page/post](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/87)
     18    * [Authenticated(Editor+) Stored Cross-Site Scripting](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/96)
     19* Enhancement:
     20    * [Improve I18N](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/72)
     21
     22= 2.6.0 - Aug 15, 2024 =
     23
     24* Feature Additions:
     25  * Compatibility with PolyLang Plugin
     26
     27= 2.5.2 - Feb 14, 2023 =
     28
     29* Bug
     30  * [Error in new update](https://wordpress.org/support/topic/error-in-new-update-3/)
     31
     32= 2.5.1 - Feb 14, 2023 =
     33
     34* Bug
     35  * [“http//” is added in front of permalinks](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/71)
     36
     37= 2.5.0 - Jan 02, 2023 =
     38
     39* Bugs
     40  * [Retreiving info from installed plugin (GDPR)](https://wordpress.org/support/topic/retreiving-info-from-installed-plugin-gdpr/)
     41* Enhancement
     42    * Same permalink with WPML different domain
     43
     44= 2.4.0 - Nov 26, 2021 =
     45
     46* Bugs
     47  * [filter for leading special characters](https://wordpress.org/support/topic/filter-for-leading-special-characters/)
     48  * [“search Permalinks” button doesn’t work. (part2)](https://wordpress.org/support/topic/search-permalinks-button-doesnt-work-part2/)
     49  * [PHP 8 errors on first visit of Taxonomy Permalinks tab](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/59)
     50  * [Notice: Undefined variable: site_url in custom-permalinks/admin/class-custom-permalinks-post-types-table.php on line 306](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/56)
     51* Enhancements
     52  * [Pending Post Preview Link](https://wordpress.org/support/topic/pending-post-preview-link/)
    453
    554= 2.3.0 - Sep 21, 2021 =
  • custom-permalinks/tags/3.0.0/custom-permalinks.php

    r3284095 r3332124  
    44 * Plugin URI: https://www.custompermalinks.com/
    55 * Description: Set custom permalinks on a per-post basis.
    6  * Version: 2.8.0
     6 * Version: 3.0.0
    77 * Requires at least: 2.6
    88 * Requires PHP: 7.0
  • custom-permalinks/tags/3.0.0/includes/class-custom-permalinks-form.php

    r3284095 r3332124  
    4444        add_action( 'add_meta_boxes', array( $this, 'permalink_edit_box' ) );
    4545        add_action( 'save_post', array( $this, 'save_post' ), 10, 3 );
     46        add_action( 'pmxi_saved_post', array( $this, 'pmxi_post_permalink' ), 10, 3 );
     47        add_action(
     48            'custom_permalinks_generate_post_permalink',
     49            array( $this, 'generate_post_permalink' ),
     50            10,
     51            1
     52        );
    4653        add_action( 'delete_post', array( $this, 'delete_permalink' ), 10 );
    4754        add_action( 'category_add_form', array( $this, 'term_options' ) );
     
    7077
    7178    /**
    72      * Initialize WordPress Hooks.
     79     * Check whether the permalink can be customized/generates on this post or not.
    7380     *
    7481     * @since 1.6.0
     
    7986     * return bool false Whether to show Custom Permalink form or not.
    8087     */
    81     private function exclude_custom_permalinks( $post ) {
     88    private function is_permalink_customizable( $post ) {
    8289        $exclude_post_types = apply_filters(
    8390            'custom_permalinks_exclude_post_type',
     
    347354
    348355        $permalink = preg_replace( '/\s+/', '-', $permalink );
    349         $permalink = preg_replace( '|-+|', '-', $permalink );
     356        $permalink = preg_replace( '/(\-+)/', '-', $permalink );
    350357        $permalink = str_replace( '-/', '/', $permalink );
    351358        $permalink = str_replace( '/-', '/', $permalink );
     
    365372
    366373    /**
     374     * Check whether the permalink exists or not. If exists append unique number
     375     * at the end of it to prevent making duplicate permalinks.
     376     *
     377     * @since 3.0.0
     378     * @access private
     379     *
     380     * @param int    $post_id       Post ID.
     381     * @param string $permalink     Permalink which is going to be set.
     382     * @param string $language_code Page Language if multi-langauge is enabled.
     383     *
     384     * @return string
     385     */
     386    private function check_permalink_exists( $post_id, $permalink, $language_code ) {
     387        global $wpdb;
     388
     389        $trailing_slash = substr( $permalink, -1 );
     390        if ( '/' === $trailing_slash ) {
     391            $permalink = rtrim( $permalink, '/' );
     392        }
     393
     394        $append_number  = 1;
     395        $init_permalink = $permalink;
     396        while ( 1 ) {
     397            // First, check permalink before appending number.
     398            if ( 1 < $append_number ) {
     399                $permalink = $init_permalink . '-' . $append_number;
     400            }
     401
     402            if ( null !== $language_code ) {
     403                // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
     404                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
     405                $existing_url_ids = $wpdb->get_col(
     406                    $wpdb->prepare(
     407                        "SELECT pm.`post_id` FROM $wpdb->postmeta AS pm
     408                        INNER JOIN $wpdb->posts AS p ON (p.`ID` = pm.`post_id`)
     409                        WHERE pm.`post_id` != %d
     410                            AND pm.`meta_key` = 'custom_permalink'
     411                            AND p.`post_status` != 'inherit'
     412                            AND (pm.`meta_value` = %s OR pm.`meta_value` = %s)",
     413                        $post_id,
     414                        $permalink,
     415                        $permalink . '/'
     416                    )
     417                );
     418                // phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
     419
     420                $check_exist_url = null;
     421                foreach ( $existing_url_ids as $existing_url_id ) {
     422                    $existing_url_lang = get_post_meta( $existing_url_id, 'custom_permalink_language', true );
     423                    if ( $existing_url_lang === $language_code ) {
     424                        $check_exist_url = 1;
     425                        break;
     426                    }
     427                }
     428            } else {
     429                // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
     430                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
     431                $check_exist_url = $wpdb->get_var(
     432                    $wpdb->prepare(
     433                        "SELECT COUNT(pm.`post_id`) FROM $wpdb->postmeta AS pm
     434                        INNER JOIN $wpdb->posts AS p ON (p.`ID` = pm.`post_id`)
     435                        WHERE pm.`post_id` != %d
     436                            AND pm.`meta_key` = 'custom_permalink'
     437                            AND p.`post_status` != 'inherit'
     438                            AND (pm.`meta_value` = %s OR pm.`meta_value` = %s)",
     439                        $post_id,
     440                        $permalink,
     441                        $permalink . '/'
     442                    )
     443                );
     444                // phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
     445            }
     446
     447            // Check URL should not be duplicated in any post Permalink.
     448            if ( empty( $check_exist_url ) ) {
     449                $existing_post_id = url_to_postid( $permalink );
     450                if ( 0 === $existing_post_id || $post_id === $existing_post_id ) {
     451                    break;
     452                }
     453            }
     454
     455            ++$append_number;
     456        }
     457
     458        if ( '/' === $trailing_slash ) {
     459            $permalink = $permalink . '/';
     460        }
     461
     462        if ( 0 === strpos( $permalink, '/' ) ) {
     463            $permalink = substr( $permalink, 1 );
     464        }
     465
     466        return $permalink;
     467    }
     468
     469    /**
     470     * Clear post permalink cache if exists.
     471     *
     472     * @since 3.0.0
     473     * @access private
     474     *
     475     * @param string $cached_permalink Permalink for which cache needs to be cleared.
     476     */
     477    private function clear_post_permalink_cache( $cached_permalink ) {
     478        if ( ! empty( $cached_permalink ) ) {
     479            $cache_name   = 'cp$_' . str_replace( '/', '-', $cached_permalink ) . '_#cp';
     480            $cache_exists = wp_cache_get( $cache_name, 'custom_permalinks' );
     481            if ( false !== $cache_exists ) {
     482                wp_cache_delete( $cache_name, 'custom_permalinks' );
     483            }
     484        }
     485    }
     486
     487    /**
    367488     * Save per-post options.
    368489     *
     
    371492     * @param int     $post_id Post ID.
    372493     * @param WP_Post $post    Post object.
    373      *
    374      * @return void
    375      */
    376     public function save_post( $post_id, $post ) {
    377         if ( ! isset( $_REQUEST['_custom_permalinks_post_nonce'] )
    378             && ! isset( $_REQUEST['custom_permalink'] )
     494     * @param bool    $update  Whether this is an existing post being updated or not.
     495     */
     496    public function save_post( $post_id, $post, $update ) {
     497        /*
     498         * Enable permalink regeneration on creating new post if permalink structure
     499         * is defined for the post type.
     500         */
     501        if ( ! $update ) {
     502            $permalink_structure = '';
     503            $post_types_settings = get_option( 'custom_permalinks_post_types_settings', array() );
     504
     505            if ( isset( $post_types_settings[ $post->post_type ] ) ) {
     506                $permalink_structure = $post_types_settings[ $post->post_type ];
     507            }
     508
     509            /*
     510             * Permalink structure is not defined in the Plugin Settings.
     511             */
     512            if ( ! empty( $permalink_structure ) ) {
     513                // Make it 1 to keep generating permalink on updating the post.
     514                update_post_meta( $post_id, 'custom_permalink_regenerate_status', 1 );
     515            }
     516        }
     517
     518        if ( 'auto-draft' === sanitize_title( $post->post_title ) ) {
     519            return;
     520        }
     521
     522        if ( isset( $_REQUEST['_custom_permalinks_post_nonce'] )
     523            && isset( $_REQUEST['custom_permalink'] )
    379524        ) {
    380             return;
    381         }
    382 
    383         $action = 'custom-permalinks_' . $post_id;
    384         // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
    385         // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
    386         if ( ! wp_verify_nonce( $_REQUEST['_custom_permalinks_post_nonce'], $action ) ) {
    387             return;
    388         }
    389         // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
     525            $action = 'custom-permalinks_' . $post_id;
     526            // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
     527            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     528            if ( ! wp_verify_nonce( $_REQUEST['_custom_permalinks_post_nonce'], $action ) ) {
     529                return;
     530            }
     531            // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
     532        }
     533
     534        if ( 'inherit' === $post->post_status ) {
     535            $post_parent_id = $post->post_parent;
     536            if ( ! empty( $post_parent_id ) ) {
     537                $post_id = $post_parent_id;
     538                $post    = get_post( $post_parent_id );
     539            }
     540        }
     541
     542        $current_permalink = get_post_meta( $post_id, 'custom_permalink', true );
     543        $is_refresh        = get_post_meta( $post_id, 'custom_permalink_regenerate_status', true );
     544
     545        // Make it 0 if not exist.
     546        if ( empty( $is_refresh ) ) {
     547            $is_refresh = 0;
     548        }
     549
     550        /*
     551         * Make sure that the post saved from quick edit form so, just make the
     552         * $_REQUEST['custom_permalink'] same as $current_permalink to regenerate permalink
     553         * if applicable.
     554         */
     555        if ( ! isset( $_REQUEST['custom_permalink'] ) ) {
     556            $_REQUEST['custom_permalink'] = $current_permalink;
     557        }
     558
     559        $is_regenerated = false;
     560        if ( 'trash' !== $post->post_status
     561            && $current_permalink === $_REQUEST['custom_permalink']
     562            && 1 === (int) $is_refresh
     563        ) {
     564            $cp_post_permalinks = new Custom_Permalinks_Generate_Post_Permalinks();
     565            $is_regenerated     = $cp_post_permalinks->generate( $post_id, $post );
     566        }
    390567
    391568        $cp_frontend   = new Custom_Permalinks_Frontend();
    392569        $original_link = $cp_frontend->original_post_link( $post_id );
    393 
    394570        if ( ! empty( $_REQUEST['custom_permalink'] )
    395571            && $_REQUEST['custom_permalink'] !== $original_link
     572            && $_REQUEST['custom_permalink'] !== $current_permalink
    396573        ) {
    397574            $language_code = apply_filters(
     
    403580                )
    404581            );
     582            if ( null !== $language_code ) {
     583                update_post_meta( $post_id, 'custom_permalink_language', $language_code );
     584            } else {
     585                delete_metadata( 'post', $post_id, 'custom_permalink_language' );
     586            }
    405587
    406588            $permalink = $this->sanitize_permalink(
     
    409591                $language_code
    410592            );
     593            $permalink = $this->check_permalink_exists( $post_id, $permalink, $language_code );
    411594            $permalink = apply_filters(
    412595                'custom_permalink_before_saving',
    413596                $permalink,
    414                 $post_id
     597                $post_id,
     598                $language_code
    415599            );
    416600
    417601            update_post_meta( $post_id, 'custom_permalink', $permalink );
    418             if ( null !== $language_code ) {
    419                 update_post_meta( $post_id, 'custom_permalink_language', $language_code );
    420             } else {
    421                 delete_metadata( 'post', $post_id, 'custom_permalink_language' );
    422             }
     602
     603            // Clear cache for the previous and updated permalink.
     604            $this->clear_post_permalink_cache( $current_permalink );
     605            $this->clear_post_permalink_cache( $permalink );
     606
     607            // If true means it triggers from the regeneration code so don't override it.
     608            if ( ! $is_regenerated ) {
     609                // Delete to prevent generating permalink on updating the post.
     610                delete_post_meta( $post_id, 'custom_permalink_regenerate_status' );
     611            }
     612        }
     613    }
     614
     615    /**
     616     * This action fires when WP All Import saves a post of any type.
     617     *
     618     * @since 3.0.0
     619     * @access public
     620     *
     621     * @param int              $post_id   The ID of the item (post/user/taxonomy) saved or updated.
     622     * @param SimpleXMLElement $xml_node  The libxml resource of the current XML element.
     623     * @param bool             $is_update Returns 0 for new item 1 for updated item.
     624     *
     625     * @return void
     626     */
     627    public function pmxi_post_permalink( $post_id, $xml_node, $is_update ) {
     628        $post = get_post( $post_id );
     629        if ( is_object( $post ) && isset( $post->post_type ) ) {
     630            $updated = false;
     631            if ( 1 === $is_update ) {
     632                $updated = true;
     633            }
     634
     635            $this->save_post( $post_id, $post, $updated );
     636        }
     637    }
     638
     639    /**
     640     * Generates post permalink for the provided post id.
     641     *
     642     * @since 3.0.0
     643     * @access public
     644     *
     645     * @param int $post_id Post ID.
     646     *
     647     * @return void
     648     */
     649    public function generate_post_permalink( $post_id ) {
     650        $post = get_post( $post_id );
     651        if ( is_object( $post ) && isset( $post->post_type ) ) {
     652            $this->save_post( $post_id, $post, false );
    423653        }
    424654    }
     
    434664     */
    435665    public function delete_permalink( $post_id ) {
     666        // Clear cache for the deleting post.
     667        $permalink = get_post_meta( $post_id, 'custom_permalink', true );
     668        $this->clear_post_permalink_cache( $permalink );
     669
    436670        delete_metadata( 'post', $post_id, 'custom_permalink' );
    437671    }
     
    615849        $post = get_post( $post_id );
    616850
    617         $disable_cp              = $this->exclude_custom_permalinks( $post );
     851        $is_customizable         = $this->is_permalink_customizable( $post );
    618852        $this->permalink_metabox = 1;
    619         if ( $disable_cp ) {
     853        if ( $is_customizable ) {
    620854            return $html;
    621855        }
     
    635869     */
    636870    public function meta_edit_form( $post ) {
    637         $disable_cp = $this->exclude_custom_permalinks( $post );
    638         if ( $disable_cp ) {
     871        $is_customizable = $this->is_permalink_customizable( $post );
     872        if ( $is_customizable ) {
    639873            wp_enqueue_script(
    640874                'custom-permalinks-form',
     
    7871021            true
    7881022        );
    789 
    790         echo esc_url( $home_url ) .
    791         '<span id="editable-post-name" title="Click to edit this part of the permalink">';
    792 
    793         if ( isset( $postname ) && '' !== $postname ) :
    794             ?>
     1023        ?>
     1024
     1025        <span id="uneditable-permalink"><?php echo esc_url( $home_url ); ?></span>
     1026        <span id="editable-post-name" title="Click to edit this part of the permalink">
     1027
     1028        <?php if ( isset( $postname ) && '' !== $postname ) : ?>
    7951029
    7961030            <input type="hidden" id="new-post-slug" class="text" value="<?php echo esc_attr( $postname ); ?>" />
  • custom-permalinks/tags/3.0.0/includes/class-custom-permalinks-frontend.php

    r3284095 r3332124  
    7777        add_filter( 'post_type_link', array( $this, 'custom_post_link' ), 10, 2 );
    7878        add_filter( 'page_link', array( $this, 'custom_page_link' ), 10, 2 );
     79        add_filter( 'url_to_postid', array( $this, 'postid_to_customized_permalink' ), 10, 1 );
    7980        add_filter( 'term_link', array( $this, 'custom_term_link' ), 10, 2 );
    8081        add_filter( 'user_trailingslashit', array( $this, 'custom_trailingslash' ) );
     
    202203        $posts      = wp_cache_get( $cache_name, 'custom_permalinks' );
    203204
    204         if ( ! $posts ) {
     205        if ( false === $posts ) {
    205206            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
    206207            $posts = $wpdb->get_results(
     
    238239            }
    239240
    240             wp_cache_set( $cache_name, $posts, 'custom_permalinks' );
     241            // Cache permalink for 24 hours.
     242            wp_cache_set( $cache_name, $posts, 'custom_permalinks', 86400 );
    241243        }
    242244
     
    267269        }
    268270
    269         if ( ! $matched_post ) {
     271        if ( false === $matched_post ) {
    270272            $matched_post = array();
    271273
     
    306308        }
    307309
    308         wp_cache_set( $cache_name, $matched_post, 'custom_permalinks' );
     310        // Cache permalink for 24 hours.
     311        wp_cache_set( $cache_name, $matched_post, 'custom_permalinks', 86400 );
    309312
    310313        return $matched_post;
     
    713716        }
    714717
    715         $custom_permalink   = '';
    716         $original_permalink = '';
    717 
    718718        // Get request URI, strip parameters.
    719719        $url     = wp_parse_url( get_bloginfo( 'url' ) );
     
    735735         * @since 1.7.0
    736736         */
    737         $avoid_redirect = apply_filters(
    738             'custom_permalinks_avoid_redirect',
    739             $request
    740         );
    741 
     737        $avoid_redirect = apply_filters( 'custom_permalinks_avoid_redirect', $request );
    742738        if ( is_bool( $avoid_redirect ) && $avoid_redirect ) {
    743739            return;
    744740        }
    745741
    746         if ( defined( 'POLYLANG_VERSION' ) ) {
    747             $cp_form = new Custom_Permalinks_Form();
    748             $request = $cp_form->check_conflicts( $request );
    749         }
    750 
    751         $request_no_slash = preg_replace( '@/+@', '/', trim( $request, '/' ) );
    752         $posts            = $this->query_post( $request_no_slash );
    753 
    754         if ( ! isset( $posts[0]->ID ) || ! isset( $posts[0]->meta_value )
    755             || empty( $posts[0]->meta_value )
    756         ) {
    757             global $wp_query;
    758 
    759             /*
    760              * If the post/tag/category we're on has a custom permalink, get it
    761              * and check against the request.
    762              */
    763             if ( ( is_single() || is_page() ) && ! empty( $wp_query->post ) ) {
    764                 $post             = $wp_query->post;
    765                 $custom_permalink = get_post_meta(
    766                     $post->ID,
    767                     'custom_permalink',
    768                     true
    769                 );
    770                 if ( 'page' === $post->post_type ) {
    771                     $original_permalink = $this->original_page_link( $post->ID );
    772                 } else {
    773                     $original_permalink = $this->original_post_link( $post->ID );
    774                 }
    775             } elseif ( is_tag() || is_category() ) {
    776                 $the_term           = $wp_query->get_queried_object();
    777                 $custom_permalink   = $this->term_permalink( $the_term->term_id );
    778                 $original_permalink = $this->original_term_link( $the_term->term_id );
     742        $custom_permalink   = '';
     743        $original_permalink = '';
     744        $get_post_id        = url_to_postid( $this->request_uri );
     745
     746        // Redirect original post permalink.
     747        if ( ! empty( $get_post_id ) ) {
     748            $custom_permalink = get_post_meta( $get_post_id, 'custom_permalink', true );
     749            if ( ! empty( $custom_permalink ) ) {
     750                // Append any query component.
     751                $custom_permalink .= strstr( $this->request_uri, '?' );
     752
     753                wp_safe_redirect( home_url() . '/' . $custom_permalink, 301 );
     754                exit( 0 );
    779755            }
    780756        } else {
    781             $custom_permalink = $posts[0]->meta_value;
    782             if ( 'page' === $posts[0]->post_type ) {
    783                 $original_permalink = $this->original_page_link( $posts[0]->ID );
    784             } else {
    785                 $original_permalink = $this->original_post_link( $posts[0]->ID );
    786             }
    787         }
    788 
    789         $custom_length = strlen( $custom_permalink );
    790         if ( $custom_permalink
    791             && (
    792                 substr( $request, 0, $custom_length ) !== $custom_permalink
    793                 || $request === $custom_permalink . '/'
    794             )
    795         ) {
    796             // Request doesn't match permalink - redirect.
    797             $url             = $custom_permalink;
    798             $original_length = strlen( $original_permalink );
    799 
    800             if ( substr( $request, 0, $original_length ) === $original_permalink
    801                 && trim( $request, '/' ) !== trim( $original_permalink, '/' )
     757            if ( defined( 'POLYLANG_VERSION' ) ) {
     758                $cp_form = new Custom_Permalinks_Form();
     759                $request = $cp_form->check_conflicts( $request );
     760            }
     761
     762            $request_no_slash = preg_replace( '@/+@', '/', trim( $request, '/' ) );
     763            $posts            = $this->query_post( $request_no_slash );
     764
     765            if ( ! isset( $posts[0]->ID ) || ! isset( $posts[0]->meta_value )
     766                || empty( $posts[0]->meta_value )
    802767            ) {
    803                 // This is the original link; we can use this URL to derive the new one.
    804                 $url = preg_replace(
    805                     '@//*@',
    806                     '/',
    807                     str_replace(
    808                         trim( $original_permalink, '/' ),
    809                         trim( $custom_permalink, '/' ),
    810                         $request
    811                     )
    812                 );
    813                 $url = preg_replace( '@([^?]*)&@', '\1?', $url );
    814             }
    815 
    816             // Append any query component.
    817             $url .= strstr( $this->request_uri, '?' );
    818 
    819             wp_safe_redirect( home_url() . '/' . $url, 301 );
    820             exit( 0 );
     768                global $wp_query;
     769
     770                /*
     771                * If the post/tag/category we're on has a custom permalink, get it
     772                * and check against the request.
     773                */
     774                if ( ( is_single() || is_page() ) && ! empty( $wp_query->post ) ) {
     775                    $post             = $wp_query->post;
     776                    $custom_permalink = get_post_meta(
     777                        $post->ID,
     778                        'custom_permalink',
     779                        true
     780                    );
     781                    if ( 'page' === $post->post_type ) {
     782                        $original_permalink = $this->original_page_link( $post->ID );
     783                    } else {
     784                        $original_permalink = $this->original_post_link( $post->ID );
     785                    }
     786                } elseif ( is_tag() || is_category() ) {
     787                    $the_term           = $wp_query->get_queried_object();
     788                    $custom_permalink   = $this->term_permalink( $the_term->term_id );
     789                    $original_permalink = $this->original_term_link( $the_term->term_id );
     790                }
     791            }
     792
     793            $custom_length = strlen( $custom_permalink );
     794            if ( $custom_permalink
     795                && (
     796                    substr( $request, 0, $custom_length ) !== $custom_permalink
     797                    || $request === $custom_permalink . '/'
     798                )
     799            ) {
     800                // Request doesn't match permalink - redirect.
     801                $url             = $custom_permalink;
     802                $original_length = strlen( $original_permalink );
     803
     804                if ( substr( $request, 0, $original_length ) === $original_permalink
     805                    && trim( $request, '/' ) !== trim( $original_permalink, '/' )
     806                ) {
     807                    // This is the original link; we can use this URL to derive the new one.
     808                    $url = preg_replace(
     809                        '@//*@',
     810                        '/',
     811                        str_replace(
     812                            trim( $original_permalink, '/' ),
     813                            trim( $custom_permalink, '/' ),
     814                            $request
     815                        )
     816                    );
     817                    $url = preg_replace( '@([^?]*)&@', '\1?', $url );
     818                }
     819
     820                // Append any query component.
     821                $url .= strstr( $this->request_uri, '?' );
     822
     823                wp_safe_redirect( home_url() . '/' . $url, 301 );
     824                exit( 0 );
     825            }
    821826        }
    822827    }
     
    924929
    925930    /**
     931     * Fetch default permalink against the customized permalink.
     932     *
     933     * @since 3.0.0
     934     * @access public
     935     *
     936     * @param string $permalink URL Permalink to check.
     937     *
     938     * @return string Default Permalink or the same permalink if not found.
     939     */
     940    public function postid_to_customized_permalink( $permalink ) {
     941        $customized_permalink = ltrim( substr( $permalink, strlen( $url ) ), '/' );
     942        if ( defined( 'POLYLANG_VERSION' ) ) {
     943            $cp_form              = new Custom_Permalinks_Form();
     944            $customized_permalink = $cp_form->check_conflicts( $customized_permalink );
     945        }
     946
     947        $customized_permalink = preg_replace( '@/+@', '/', trim( $customized_permalink, '/' ) );
     948        $posts                = $this->query_post( $customized_permalink );
     949        if ( is_array( $posts ) && ! empty( $posts ) ) {
     950            if ( 'draft' === $posts[0]->post_status
     951                || 'pending' === $posts[0]->post_status
     952            ) {
     953                if ( 'page' === $posts[0]->post_type ) {
     954                    $original_url = '?page_id=' . $posts[0]->ID;
     955                } else {
     956                    $original_url = '?post_type=' . $posts[0]->post_type . '&p=' . $posts[0]->ID;
     957                }
     958            } else {
     959                $post_meta = trim( strtolower( $posts[0]->meta_value ), '/' );
     960                if ( 'page' === $posts[0]->post_type ) {
     961                    $get_original_url = $this->original_page_link( $posts[0]->ID );
     962                    $original_url     = preg_replace(
     963                        '@/+@',
     964                        '/',
     965                        str_replace(
     966                            $post_meta,
     967                            $get_original_url,
     968                            strtolower( $customized_permalink )
     969                        )
     970                    );
     971                } else {
     972                    $get_original_url = $this->original_post_link( $posts[0]->ID );
     973                    $original_url     = preg_replace(
     974                        '@/+@',
     975                        '/',
     976                        str_replace(
     977                            $post_meta,
     978                            $get_original_url,
     979                            strtolower( $customized_permalink )
     980                        )
     981                    );
     982                }
     983            }
     984
     985            $permalink = $original_url;
     986        }
     987
     988        return $permalink;
     989    }
     990
     991    /**
    926992     * Filter to replace the term permalink with the custom one.
    927993     *
  • custom-permalinks/tags/3.0.0/includes/class-custom-permalinks.php

    r3284095 r3332124  
    1919     * @var string
    2020     */
    21     public $version = '2.8.0';
     21    public $version = '3.0.0';
    2222
    2323    /**
     
    6565    private function includes() {
    6666        include_once CUSTOM_PERMALINKS_PATH . 'includes/class-custom-permalinks-form.php';
     67        include_once CUSTOM_PERMALINKS_PATH . 'includes/class-custom-permalinks-generate-post-permalinks.php';
    6768        include_once CUSTOM_PERMALINKS_PATH . 'includes/class-custom-permalinks-frontend.php';
    6869        include_once CUSTOM_PERMALINKS_PATH . 'admin/class-custom-permalinks-admin.php';
     70        include_once CUSTOM_PERMALINKS_PATH . 'admin/class-custom-permalinks-post-types-settings.php';
    6971        include_once CUSTOM_PERMALINKS_PATH . 'admin/class-custom-permalinks-post-types.php';
    7072        include_once CUSTOM_PERMALINKS_PATH . 'admin/class-custom-permalinks-post-types-table.php';
     
    107109     */
    108110    public static function add_roles() {
    109         $admin_role      = get_role( 'administrator' );
    110         $cp_role         = get_role( 'custom_permalinks_manager' );
    111         $current_version = get_option( 'custom_permalinks_plugin_version', -1 );
     111        $admin_role = get_role( 'administrator' );
     112        $cp_role    = get_role( 'custom_permalinks_manager' );
    112113
    113114        if ( ! empty( $admin_role ) ) {
     115            $admin_role->add_cap( 'custom_permalinks_post_settings' );
    114116            $admin_role->add_cap( 'cp_view_post_permalinks' );
    115117            $admin_role->add_cap( 'cp_view_category_permalinks' );
     
    121123                __( 'Custom Permalinks Manager' ),
    122124                array(
    123                     'cp_view_post_permalinks'     => true,
    124                     'cp_view_category_permalinks' => true,
     125                    'custom_permalinks_post_settings' => true,
     126                    'cp_view_post_permalinks'         => true,
     127                    'cp_view_category_permalinks'     => true,
    125128                )
    126129            );
  • custom-permalinks/tags/3.0.0/readme.txt

    r3284095 r3332124  
    11=== Custom Permalinks ===
    22Contributors: sasiddiqui
    3 Tags: permalink, url, link, address, redirect, custom post type
     3Tags: permalink, url, link, address, redirect
     4Requires at least: 5.0
     5Requires PHP: 5.6
    46Tested up to: 6.8
    5 Stable tag: 2.8.0
     7Stable tag: 3.0.0
    68License: GPLv3
    79License URI: https://www.gnu.org/licenses/gpl-3.0.html
    810
    9 Set custom permalinks on a per-post, per-tag or per-category basis.
     11Custom Permalinks is a powerful WordPress plugin that grants you complete control over your site's URLs. Easily set unique, custom permalinks for any post, page, tag, or category, allowing you to design an ideal site structure. It includes features for defining post type-specific structures with dynamic tags and automatically redirects old URLs to your new custom ones. Developers can also leverage a wide array of filters for advanced customization.
    1012
    1113== Description ==
    1214
    13 > In case of found any site breaking issue after upgrading to the latest version then please report the issue on [WordPress Forum](https://wordpress.org/support/plugin/custom-permalinks/) OR [GitHub](https://github.com/samiahmedsiddiqui/custom-permalinks) with complete information to reproduce the issue and move back to the old version. You can download any of the old version from here: https://wordpress.org/plugins/custom-permalinks/advanced/
     15You want to take control of your WordPress site's URLs? The **Custom Permalinks** plugin gives you the power to set unique, custom URLs for any post, page, tag, or category. This means you can design your site's structure exactly how you envision it, rather than being limited by WordPress's default settings. When you set a custom permalink, the original post URL will be automatically redirected to your new, customized URL.
    1416
    15 Lay out your site the way *you* want it. Set the URL of any post, page, tag or category to anything you want. Old permalinks will redirect properly to the new address. Custom Permalinks give you ultimate control over your site structure.
     17=== Key Features ===
    1618
    17 > Be warned: *This plugin is not a replacement for WordPress's built-in permalink system*. Check your WordPress administration's "Permalinks" settings page first, to make sure that this doesn't already meet your needs.
     19* **Individual Permalink Control**: Assign unique URLs to any post, page, tag, or category.
     20* **Site Structure Control**: Gain ultimate control over how your site's URLs are organized.
     21* **Post Type Permalink Structures (v3.0.0+)**: Define custom permalink structures for each public Post Type using predefined tags, automatically generating URLs upon content creation. You can still manually edit any permalink. If left empty, default settings will apply.
    1822
    19 This plugin is only useful for assigning custom permalinks for *individual* posts, pages, tags or categories. It will not apply whole permalink structures or automatically apply a category's custom permalink to the posts within that category.
     23=== Getting Started: Plugin Settings ===
    2024
    21 == Filters ==
     25You can configure Custom Permalinks by navigating to **Settings \> Custom Permalinks** in your WordPress Dashboard.
    2226
    23 === Add `PATH_INFO` in `$_SERVER` Variable ===
     27=== Available Tags for Permalink Structures ===
    2428
    25 `
    26 add_filter( 'custom_permalinks_path_info', '__return_true' );
    27 `
     29When setting up your custom permalink structures, you can use a variety of tags that will dynamically populate the URL. Here's a breakdown of what's available:
    2830
    29 === Disable Redirects ===
     31* **%year%**: The year of the post in four digits, eg: 2025
     32* **%monthnum%**: Month the post was published, in two digits, eg: 01
     33* **%day%**: Day the post was published in two digits, eg: 02
     34* **%hour%**: Hour of the day, the post was published, eg: 15
     35* **%minute%**: Minute of the hour, the post was published, eg: 43
     36* **%second%**: Second of the minute, the post was published, eg: 33
     37* **%post_id%**: The unique ID of the post, eg: 123
     38* **%category%**: A clean version of the category name (its slug). Nested sub-categories will appear as nested directories in the URL..
     39* **%author%**: A sanitized version of the post author’s name.
     40* **%postname%**: A clean version of the post or page title (its slug). For example, "This Is A Great Post\!" becomes `this-is-a-great-post` in the URL.
     41* **%parent_postname%**: Similar to `%postname%`, but uses the immediate parent page's slug if a parent is selected.
     42* **%parents_postnames%**: Similar to `%postname%`, but includes all parent page slugs if parents are selected.
     43* **%title%**: The title of the post, converted to a slug. For example, "This Is A Great Post\!" becomes `this-is-a-great-post`. Unlike `%postname%` which is set once, `%title%` automatically updates in the permalink if the post title changes (unless the post is published or the permalink is manually edited).
     44* **%ctax_TAXONOMY_NAME%**: A clean version of a custom taxonomy's name. Replace `TAXONOMY_NAME` with the actual taxonomy name. You can also provide a default slug for when no category/taxonomy is selected by using `??` (e.g., `%ctax_type??sales%` will use "sales" as a default).
     45* **%ctax_parent_TAXONOMY_NAME%**: Similar to `%ctax_TAXONOMY_NAME%`, but includes the immediate parent category/tag slug in the URL if a parent is selected.
     46* **%ctax_parents_TAXONOMY_NAME%**: Similar to `%ctax_TAXONOMY_NAME%`, but includes all parent category/tag slugs in the URL if parents are selected.
     47* **%custom_permalinks_TAG_NAME%**: Developers have the flexibility to define their own custom tags(replace `_TAG_NAME` with your desired name). To ensure these tags resolve to the correct permalinks, simply apply the `custom_permalinks_post_permalink_tag` filter.
    3048
    31 To disable complete redirects functionality provided by this plugin, add the filter that looks like this:
     49**Important Note:** For new posts, Custom Permalinks will keep updating the permalink while the post is in draft mode, assuming a structure is defined in the plugin settings. Once the post is published or its permalink is manually updated, the plugin will stop automatic updates for that specific post.
    3250
    33 `
    34 function yasglobal_avoid_redirect( $permalink ) {
    35   return true;
    36 }
    37 add_filter( 'custom_permalinks_avoid_redirect', 'yasglobal_avoid_redirect' );
    38 `
     51=== Custom Permalinks: Fine-Tuning with Filters ===
    3952
    40 === Disable Particular Redirects ===
     53Custom Permalinks offers a range of **filters** that empower developers to precisely control its behavior. You can explore all available filters, complete with example code snippets, in our [GitHub repository](https://github.com/samiahmedsiddiqui/custom-permalinks).
    4154
    42 To disable any specific redirect to be processed by this plugin, add the filter that looks like this:
     55**For Assistance:**
    4356
    44 `
    45 function yasglobal_avoid_redirect( $permalink ) {
    46   // Replace 'testing-hello-world/' with the permalink you want to avoid
    47   if ( 'testing-hello-world/' === $permalink ) {
    48     return true;
    49   }
     57* **Premium Users:** If you need assistance implementing these filters, please don't hesitate to reach out to us via our [Premium contact support](https://www.custompermalinks.com/contact-us/).
     58* **Other Users:** You can also directly reach out to the plugin author via [LinkedIn](https://www.linkedin.com/in/sami-ahmed-siddiqui/).
    5059
    51   return false;
    52 }
    53 add_filter( 'custom_permalinks_avoid_redirect', 'yasglobal_avoid_redirect' );
    54 `
     60=== Need Help or Found a Bug? ===
    5561
    56 === Exclude Permalink to be processed ===
     62* **Support:** For one-on-one email support, consider purchasing [Custom Permalinks Premium](https://www.custompermalinks.com/#pricing-section). While some basic support may be provided on the WordPress.org forums, email support is prioritized for premium users.
     63* **Bug Reports:** If you encounter a bug, please report it on [GitHub](https://github.com/samiahmedsiddiqui/custom-permalinks). Make sure to provide complete information to reproduce the issue. GitHub is for bug reports, not general support questions.
    5764
    58 To exclude any Permalink to be processed by the plugin, add the filter that looks like this:
    59 
    60 `
    61 function yasglobal_xml_sitemap_url( $permalink ) {
    62   if ( false !== strpos( $permalink, 'sitemap.xml' ) ) {
    63     return '__true';
    64   }
    65 
    66   return;
    67 }
    68 add_filter( 'custom_permalinks_request_ignore', 'yasglobal_xml_sitemap_url' );
    69 `
    70 
    71 === Exclude Post Type ===
    72 
    73 To remove custom permalink **form** from any post type, add the filter that looks like this:
    74 
    75 `
    76 function yasglobal_exclude_post_types( $post_type ) {
    77   // Replace 'custompost' with your post type name
    78   if ( 'custompost' === $post_type ) {
    79     return '__true';
    80   }
    81 
    82   return '__false';
    83 }
    84 add_filter( 'custom_permalinks_exclude_post_type', 'yasglobal_exclude_post_types' );
    85 `
    86 
    87 === Exclude Posts ===
    88 
    89 To exclude custom permalink **form** from any posts (based on ID, Template, etc), add the filter that looks like this:
    90 
    91 `
    92 function yasglobal_exclude_posts( $post ) {
    93   if ( 1557 === $post->ID ) {
    94     return true;
    95   }
    96 
    97   return false;
    98 }
    99 add_filter( 'custom_permalinks_exclude_posts', 'yasglobal_exclude_posts' );
    100 `
    101 
    102 === Allow Accents Letters ===
    103 
    104 To allow accents letters, please add below-mentioned line in your theme `functions.php`:
    105 
    106 `
    107 function yasglobal_permalink_allow_accents() {
    108   return true;
    109 }
    110 add_filter( 'custom_permalinks_allow_accents', 'yasglobal_permalink_allow_accents' );
    111 `
    112 
    113 === Allow Uppercase Letters ===
    114 
    115 To allow uppercase letters/words, please add below-mentioned line in your theme `functions.php`:
    116 
    117 `
    118 function yasglobal_allow_uppercaps() {
    119   return true;
    120 }
    121 add_filter( 'custom_permalinks_allow_caps', 'yasglobal_allow_uppercaps' );
    122 `
    123 
    124 === Allow Redundant Hyphens ===
    125 
    126 To allow redundant hyphens, please add below-mentioned line in your theme `functions.php`:
    127 
    128 `
    129 function yasglobal_redundant_hyphens() {
    130   return true;
    131 }
    132 add_filter( 'custom_permalinks_redundant_hyphens', 'yasglobal_redundant_hyphens' );
    133 `
    134 
    135 === Manipulate Permalink Before Saving ===
    136 
    137 To make changes in permalink before saving, please use `custom_permalink_before_saving` filter. Here is an example to see how it works.
    138 
    139 `
    140 function yasglobal_permalink_before_saving( $permalink, $post_id ) {
    141   // Check trialing slash in the permalink.
    142   if ( substr( $permalink, -1 ) !== '/' ) {
    143     // If permalink doesn't contain trialing slash then add one.
    144     $permalink .= '/';
    145   }
    146 
    147   return $permalink;
    148 }
    149 add_filter( 'custom_permalink_before_saving', 'yasglobal_permalink_before_saving', 10, 2 );
    150 `
    151 
    152 === Remove `like` Query ===
    153 
    154 To remove `like` query to being work, add below-mentioned line in your theme `functions.php`:
    155 `
    156 add_filter( 'cp_remove_like_query', '__return_false' );
    157 `
    158 
    159 Note: Use `custom_permalinks_like_query` filter if the URLs doesn't works for you after upgrading to `v1.2.9`.
    160 
    161 === Thanks for the Support ===
    162 
    163 I do not always provide active support for the Custom Permalinks plugin on the WordPress.org forums, as I have prioritized the email support. One-on-one email support is available to people who bought [Custom Permalinks Premium](https://www.custompermalinks.com/#pricing-section) only.
    164 
    165 === Bug reports ===
    166 
    167 Bug reports for Custom Permalinks are [welcomed on GitHub](https://github.com/samiahmedsiddiqui/custom-permalinks). Please note GitHub is not a support forum, and issues that aren't properly qualified as bugs will be closed.
     65If you experience any site-breaking issues after upgrading, please report them on the [WordPress Forum](https://wordpress.org/support/plugin/custom-permalinks/) or [GitHub](https://github.com/samiahmedsiddiqui/custom-permalinks) with detailed information. You can always revert to an older version by downloading it from [https://wordpress.org/plugins/custom-permalinks/advanced/](https://wordpress.org/plugins/custom-permalinks/advanced/).
    16866
    16967== Installation ==
     68You have two ways to install Custom Permalinks:
    17069
    171 This process defines you the steps to follow either you are installing through WordPress or Manually from FTP.
     70#### From within WordPress
    17271
    173 **From within WordPress**
     721.  Go to **Plugins \> Add New** in your WordPress dashboard.
     732.  Search for "Custom Permalinks".
     743.  Click "Install Now" and then "Activate" the plugin from your Plugins page.
    17475
    175 1. Visit 'Plugins > Add New'
    176 2. Search for Custom Permalinks
    177 3. Activate Custom Permalinks from your Plugins page.
     76#### Manually via FTP
    17877
    179 **Manually**
    180 
    181 1. Upload the `custom-permalinks` folder to the `/wp-content/plugins/` directory
    182 2. Activate Custom Permalinks through the 'Plugins' menu in WordPress
     781.  Download the `custom-permalinks` folder.
     792.  Upload the `custom-permalinks` folder to your `/wp-content/plugins/` directory.
     803.  Activate Custom Permalinks through the "Plugins" menu in your WordPress dashboard.
    18381
    18482== Changelog ==
    18583
    186 = 2.8.0 - Apr 29, 2025 =
     84= 3.0.0 - Jul 22, 2025 =
    18785
    188 * Bug:
    189   * Resolved pagination issue with custom permalinks (now supports /page/{number} format correctly).
    190 * Enhancements:
    191     * Added compatibility with Polylang 3.7.
    192     * Metabox is now hidden for post types that are not publicly queryable.
     86This release of Custom Permalinks brings significant enhancements to post type permalink management, introduces new customization options, and refines the overall user and developer experience.
    19387
    194 = 2.7.0 - Aug 20, 2024 =
     88**Added**
    19589
    196 * Bug
    197   * [Passing null to parameter string is deprecated](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/86)
    198   * [Fix PHP warning with empty permalink on new page/post](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/87)
    199     * [Authenticated(Editor+) Stored Cross-Site Scripting](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/96)
    200 * Enhancement:
    201     * [Improve I18N](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/72)
     90  * **Post Type Permalink Structures:** Introduced robust functionality to define custom permalink structures for each public Post Type directly within the plugin settings. This allows for automatic URL generation based on predefined tags upon content creation, offering greater flexibility while still allowing manual edits.
     91  * **New Available Permalink Tags:** Expanded the list of dynamic tags that can be used in permalink structures, including:
     92    * `%parent_postname%`: For immediate parent page slugs.
     93    * `%parents_postnames%`: For all parent page slugs.
     94    * `%title%`: A dynamic slug that updates with post title changes (until published or manually edited).
     95    * `%ctax_parent_TAXONOMY_NAME%`: For immediate parent custom taxonomy slugs.
     96    * `%ctax_parents_TAXONOMY_NAME%`: For all parent custom taxonomy slugs.
     97    * `%custom_permalinks_TAG_NAME%`: Allows developers to define and resolve their own custom tags.
     98  * **WP All Import Compatibility:** Added support to generate/update permalinks when importing posts using the WP All Import plugin.
     99  * **New Filter Examples:** Included clear code examples for `custom_permalinks_post_permalink_tag` to set custom values from ACF fields, and for programmatically generating permalinks for single posts and entire post types.
    202100
    203 = 2.6.0 - Aug 15, 2024 =
     101**Improved**
    204102
    205 * Feature Additions:
    206   * Compatibility with PolyLang Plugin
    207 
    208 = 2.5.2 - Feb 14, 2023 =
    209 
    210 * Bug
    211   * [Error in new update](https://wordpress.org/support/topic/error-in-new-update-3/)
    212 
    213 = 2.5.1 - Feb 14, 2023 =
    214 
    215 * Bug
    216   * [“http//” is added in front of permalinks](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/71)
    217 
    218 = 2.5.0 - Jan 02, 2023 =
    219 
    220 * Bugs
    221   * [Retreiving info from installed plugin (GDPR)](https://wordpress.org/support/topic/retreiving-info-from-installed-plugin-gdpr/)
    222 * Enhancement
    223     * Same permalink with WPML different domain
    224 
    225 = 2.4.0 - Nov 26, 2021 =
    226 
    227 * Bugs
    228   * [filter for leading special characters](https://wordpress.org/support/topic/filter-for-leading-special-characters/)
    229   * [“search Permalinks” button doesn’t work. (part2)](https://wordpress.org/support/topic/search-permalinks-button-doesnt-work-part2/)
    230   * [PHP 8 errors on first visit of Taxonomy Permalinks tab](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/59)
    231   * [Notice: Undefined variable: site_url in custom-permalinks/admin/class-custom-permalinks-post-types-table.php on line 306](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/56)
    232 * Enhancements
    233   * [Pending Post Preview Link](https://wordpress.org/support/topic/pending-post-preview-link/)
     103  * **Post Caching:** Enhanced post caching mechanisms and optimized cache deletion upon updates for better performance.
     104  * **Permalink Retrieval:** Improved logic to allow fetching posts against customized permalinks.
     105  * **Filter Documentation:** Refined existing filter descriptions and improved code formatting for clarity.
     106  * **Plugin Purpose Clarity:** Updated documentation to explicitly state that original post URLs will automatically redirect to the customized URLs, ensuring seamless transitions.
    234107
    235108= Earlier versions =
  • custom-permalinks/tags/3.0.0/uninstall.php

    r2575556 r3332124  
    1515delete_post_meta_by_key( 'custom_permalink' );
    1616delete_option( 'custom_permalink_table' );
     17delete_option( 'custom_permalinks_post_types_settings' );
    1718
    1819$wp_role = get_role( 'administrator' );
     
    2021    $wp_role->remove_cap( 'cp_view_post_permalinks' );
    2122    $wp_role->remove_cap( 'cp_view_category_permalinks' );
     23    $wp_role->remove_cap( 'custom_permalinks_post_settings' );
    2224}
    2325
     
    2628    $wp_role->remove_cap( 'cp_view_post_permalinks' );
    2729    $wp_role->remove_cap( 'cp_view_category_permalinks' );
     30    $wp_role->remove_cap( 'custom_permalinks_post_settings' );
    2831
    2932    remove_role( 'custom_permalinks_manager' );
  • custom-permalinks/trunk/admin/class-custom-permalinks-admin.php

    r3138206 r3332124  
    2222
    2323    /**
     24     * JS file suffix extension.
     25     *
     26     * @var string
     27     */
     28    private $js_file_suffix = '.min.js';
     29
     30    /**
    2431     * Initializes WordPress hooks.
    2532     */
    2633    public function __construct() {
    2734        /*
    28          * Css file suffix (version number with extension).
     35         * CSS file suffix (version number with extension).
    2936         */
    3037        $this->css_file_suffix = '-' . CUSTOM_PERMALINKS_VERSION . '.min.css';
     38
     39        /*
     40         * JS file suffix (version number with extension).
     41         */
     42        $this->js_file_suffix = '-' . CUSTOM_PERMALINKS_VERSION . '.min.js';
    3143
    3244        add_action( 'admin_init', array( $this, 'privacy_policy' ) );
     
    7385            array( $this, 'taxonomy_permalinks_page' )
    7486        );
     87        $post_settings_page       = add_submenu_page(
     88            'cp-post-permalinks',
     89            __( 'Post Types Settings', 'custom-permalinks' ),
     90            __( 'Post Types Settings', 'custom-permalinks' ),
     91            'custom_permalinks_post_settings',
     92            'custom-permalinks-post-settings',
     93            array( $this, 'post_settings_page' )
     94        );
    7595        $about_page               = add_submenu_page(
    7696            'cp-post-permalinks',
     
    91111        );
    92112        add_action(
     113            'admin_print_styles-' . $post_settings_page,
     114            array( $this, 'add_post_settings_style' )
     115        );
     116        add_action(
    93117            'admin_print_styles-' . $about_page . '',
    94118            array( $this, 'add_about_style' )
     119        );
     120    }
     121
     122    /**
     123     * Add post settings page style.
     124     *
     125     * @since 3.0.0
     126     */
     127    public function add_post_settings_style() {
     128        wp_enqueue_script(
     129            'custom-permalinks-post-settings',
     130            plugins_url(
     131                '/assets/js/post-settings' . $this->js_file_suffix,
     132                CUSTOM_PERMALINKS_FILE
     133            ),
     134            array(),
     135            CUSTOM_PERMALINKS_VERSION,
     136            array(
     137                'strategy'  => 'async',
     138                'in_footer' => true,
     139            )
     140        );
     141
     142        wp_enqueue_style(
     143            'custom-permalinks-post-settings',
     144            plugins_url(
     145                '/assets/css/post-settings' . $this->css_file_suffix,
     146                CUSTOM_PERMALINKS_FILE
     147            ),
     148            array(),
     149            CUSTOM_PERMALINKS_VERSION
    95150        );
    96151    }
     
    140195    public function taxonomy_permalinks_page() {
    141196        Custom_Permalinks_Taxonomies_Table::output();
     197
     198        add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );
     199    }
     200
     201    /**
     202     * Calls another Function which shows the Post Types Settings Page.
     203     *
     204     * @since 3.0.0
     205     */
     206    public function post_settings_page() {
     207        new Custom_Permalinks_Post_Types_Settings();
    142208
    143209        add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );
  • custom-permalinks/trunk/admin/class-custom-permalinks-post-types.php

    r3136157 r3332124  
    2626
    2727        $total_posts = wp_cache_get( 'total_posts_result', 'custom_permalinks' );
    28         if ( ! $total_posts ) {
     28        if ( false === $total_posts ) {
    2929            $sql_query = "
    3030                SELECT COUNT(p.ID) FROM $wpdb->posts AS p
     
    6363            }
    6464
    65             wp_cache_set( 'total_posts_result', $total_posts, 'custom_permalinks' );
     65            wp_cache_set( 'total_posts_result', $total_posts, 'custom_permalinks', 60 );
    6666        }
    6767
     
    8484
    8585        $posts = wp_cache_get( 'post_type_results', 'custom_permalinks' );
    86         if ( ! $posts ) {
     86        if ( false === $posts ) {
    8787            $page_offset = ( $page_number - 1 ) * $per_page;
    8888            $order_by    = 'p.ID';
     
    156156            // phpcs:enable WordPress.Security.NonceVerification.Recommended
    157157
    158             wp_cache_set( 'post_type_results', $posts, 'custom_permalinks' );
     158            wp_cache_set( 'post_type_results', $posts, 'custom_permalinks', 60 );
    159159        }
    160160
  • custom-permalinks/trunk/admin/class-custom-permalinks-taxonomies.php

    r2844616 r3332124  
    3939    public static function total_permalinks() {
    4040        $total_taxonomies = wp_cache_get( 'total_taxonomies_result', 'custom_permalinks' );
    41         if ( ! $total_taxonomies ) {
     41        if ( false === $total_taxonomies ) {
    4242            $search_taxonomy  = array();
    4343            $taxonomy_table   = get_option( 'custom_permalink_table' );
     
    6666            }
    6767
    68             wp_cache_set( 'total_taxonomies_result', $total_taxonomies, 'custom_permalinks' );
     68            wp_cache_set( 'total_taxonomies_result', $total_taxonomies, 'custom_permalinks', 60 );
    6969        }
    7070
     
    8585    public static function get_permalinks( $per_page = 20, $page_number = 1 ) {
    8686        $taxonomies = wp_cache_get( 'taxonomies_results', 'custom_permalinks' );
    87         if ( ! $taxonomies ) {
     87        if ( false === $taxonomies ) {
    8888            $page_offset     = ( $page_number - 1 ) * $per_page;
    8989            $taxonomy_table  = get_option( 'custom_permalink_table' );
     
    130130            }
    131131
    132             wp_cache_set( 'taxonomies_results', $taxonomies, 'custom_permalinks' );
     132            wp_cache_set( 'taxonomies_results', $taxonomies, 'custom_permalinks', 60 );
    133133        }
    134134
  • custom-permalinks/trunk/changelog.txt

    r2842482 r3332124  
    22
    33This file contains only old changelog. See readme.txt for newer versions.
     4
     5= 2.8.0 - Apr 29, 2025 =
     6
     7* Bug:
     8  * Resolved pagination issue with custom permalinks (now supports /page/{number} format correctly).
     9* Enhancements:
     10    * Added compatibility with Polylang 3.7.
     11    * Metabox is now hidden for post types that are not publicly queryable.
     12
     13= 2.7.0 - Aug 20, 2024 =
     14
     15* Bug
     16  * [Passing null to parameter string is deprecated](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/86)
     17  * [Fix PHP warning with empty permalink on new page/post](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/87)
     18    * [Authenticated(Editor+) Stored Cross-Site Scripting](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/96)
     19* Enhancement:
     20    * [Improve I18N](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/72)
     21
     22= 2.6.0 - Aug 15, 2024 =
     23
     24* Feature Additions:
     25  * Compatibility with PolyLang Plugin
     26
     27= 2.5.2 - Feb 14, 2023 =
     28
     29* Bug
     30  * [Error in new update](https://wordpress.org/support/topic/error-in-new-update-3/)
     31
     32= 2.5.1 - Feb 14, 2023 =
     33
     34* Bug
     35  * [“http//” is added in front of permalinks](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/71)
     36
     37= 2.5.0 - Jan 02, 2023 =
     38
     39* Bugs
     40  * [Retreiving info from installed plugin (GDPR)](https://wordpress.org/support/topic/retreiving-info-from-installed-plugin-gdpr/)
     41* Enhancement
     42    * Same permalink with WPML different domain
     43
     44= 2.4.0 - Nov 26, 2021 =
     45
     46* Bugs
     47  * [filter for leading special characters](https://wordpress.org/support/topic/filter-for-leading-special-characters/)
     48  * [“search Permalinks” button doesn’t work. (part2)](https://wordpress.org/support/topic/search-permalinks-button-doesnt-work-part2/)
     49  * [PHP 8 errors on first visit of Taxonomy Permalinks tab](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/59)
     50  * [Notice: Undefined variable: site_url in custom-permalinks/admin/class-custom-permalinks-post-types-table.php on line 306](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/56)
     51* Enhancements
     52  * [Pending Post Preview Link](https://wordpress.org/support/topic/pending-post-preview-link/)
    453
    554= 2.3.0 - Sep 21, 2021 =
  • custom-permalinks/trunk/custom-permalinks.php

    r3284095 r3332124  
    44 * Plugin URI: https://www.custompermalinks.com/
    55 * Description: Set custom permalinks on a per-post basis.
    6  * Version: 2.8.0
     6 * Version: 3.0.0
    77 * Requires at least: 2.6
    88 * Requires PHP: 7.0
  • custom-permalinks/trunk/includes/class-custom-permalinks-form.php

    r3284095 r3332124  
    4444        add_action( 'add_meta_boxes', array( $this, 'permalink_edit_box' ) );
    4545        add_action( 'save_post', array( $this, 'save_post' ), 10, 3 );
     46        add_action( 'pmxi_saved_post', array( $this, 'pmxi_post_permalink' ), 10, 3 );
     47        add_action(
     48            'custom_permalinks_generate_post_permalink',
     49            array( $this, 'generate_post_permalink' ),
     50            10,
     51            1
     52        );
    4653        add_action( 'delete_post', array( $this, 'delete_permalink' ), 10 );
    4754        add_action( 'category_add_form', array( $this, 'term_options' ) );
     
    7077
    7178    /**
    72      * Initialize WordPress Hooks.
     79     * Check whether the permalink can be customized/generates on this post or not.
    7380     *
    7481     * @since 1.6.0
     
    7986     * return bool false Whether to show Custom Permalink form or not.
    8087     */
    81     private function exclude_custom_permalinks( $post ) {
     88    private function is_permalink_customizable( $post ) {
    8289        $exclude_post_types = apply_filters(
    8390            'custom_permalinks_exclude_post_type',
     
    347354
    348355        $permalink = preg_replace( '/\s+/', '-', $permalink );
    349         $permalink = preg_replace( '|-+|', '-', $permalink );
     356        $permalink = preg_replace( '/(\-+)/', '-', $permalink );
    350357        $permalink = str_replace( '-/', '/', $permalink );
    351358        $permalink = str_replace( '/-', '/', $permalink );
     
    365372
    366373    /**
     374     * Check whether the permalink exists or not. If exists append unique number
     375     * at the end of it to prevent making duplicate permalinks.
     376     *
     377     * @since 3.0.0
     378     * @access private
     379     *
     380     * @param int    $post_id       Post ID.
     381     * @param string $permalink     Permalink which is going to be set.
     382     * @param string $language_code Page Language if multi-langauge is enabled.
     383     *
     384     * @return string
     385     */
     386    private function check_permalink_exists( $post_id, $permalink, $language_code ) {
     387        global $wpdb;
     388
     389        $trailing_slash = substr( $permalink, -1 );
     390        if ( '/' === $trailing_slash ) {
     391            $permalink = rtrim( $permalink, '/' );
     392        }
     393
     394        $append_number  = 1;
     395        $init_permalink = $permalink;
     396        while ( 1 ) {
     397            // First, check permalink before appending number.
     398            if ( 1 < $append_number ) {
     399                $permalink = $init_permalink . '-' . $append_number;
     400            }
     401
     402            if ( null !== $language_code ) {
     403                // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
     404                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
     405                $existing_url_ids = $wpdb->get_col(
     406                    $wpdb->prepare(
     407                        "SELECT pm.`post_id` FROM $wpdb->postmeta AS pm
     408                        INNER JOIN $wpdb->posts AS p ON (p.`ID` = pm.`post_id`)
     409                        WHERE pm.`post_id` != %d
     410                            AND pm.`meta_key` = 'custom_permalink'
     411                            AND p.`post_status` != 'inherit'
     412                            AND (pm.`meta_value` = %s OR pm.`meta_value` = %s)",
     413                        $post_id,
     414                        $permalink,
     415                        $permalink . '/'
     416                    )
     417                );
     418                // phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
     419
     420                $check_exist_url = null;
     421                foreach ( $existing_url_ids as $existing_url_id ) {
     422                    $existing_url_lang = get_post_meta( $existing_url_id, 'custom_permalink_language', true );
     423                    if ( $existing_url_lang === $language_code ) {
     424                        $check_exist_url = 1;
     425                        break;
     426                    }
     427                }
     428            } else {
     429                // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
     430                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
     431                $check_exist_url = $wpdb->get_var(
     432                    $wpdb->prepare(
     433                        "SELECT COUNT(pm.`post_id`) FROM $wpdb->postmeta AS pm
     434                        INNER JOIN $wpdb->posts AS p ON (p.`ID` = pm.`post_id`)
     435                        WHERE pm.`post_id` != %d
     436                            AND pm.`meta_key` = 'custom_permalink'
     437                            AND p.`post_status` != 'inherit'
     438                            AND (pm.`meta_value` = %s OR pm.`meta_value` = %s)",
     439                        $post_id,
     440                        $permalink,
     441                        $permalink . '/'
     442                    )
     443                );
     444                // phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
     445            }
     446
     447            // Check URL should not be duplicated in any post Permalink.
     448            if ( empty( $check_exist_url ) ) {
     449                $existing_post_id = url_to_postid( $permalink );
     450                if ( 0 === $existing_post_id || $post_id === $existing_post_id ) {
     451                    break;
     452                }
     453            }
     454
     455            ++$append_number;
     456        }
     457
     458        if ( '/' === $trailing_slash ) {
     459            $permalink = $permalink . '/';
     460        }
     461
     462        if ( 0 === strpos( $permalink, '/' ) ) {
     463            $permalink = substr( $permalink, 1 );
     464        }
     465
     466        return $permalink;
     467    }
     468
     469    /**
     470     * Clear post permalink cache if exists.
     471     *
     472     * @since 3.0.0
     473     * @access private
     474     *
     475     * @param string $cached_permalink Permalink for which cache needs to be cleared.
     476     */
     477    private function clear_post_permalink_cache( $cached_permalink ) {
     478        if ( ! empty( $cached_permalink ) ) {
     479            $cache_name   = 'cp$_' . str_replace( '/', '-', $cached_permalink ) . '_#cp';
     480            $cache_exists = wp_cache_get( $cache_name, 'custom_permalinks' );
     481            if ( false !== $cache_exists ) {
     482                wp_cache_delete( $cache_name, 'custom_permalinks' );
     483            }
     484        }
     485    }
     486
     487    /**
    367488     * Save per-post options.
    368489     *
     
    371492     * @param int     $post_id Post ID.
    372493     * @param WP_Post $post    Post object.
    373      *
    374      * @return void
    375      */
    376     public function save_post( $post_id, $post ) {
    377         if ( ! isset( $_REQUEST['_custom_permalinks_post_nonce'] )
    378             && ! isset( $_REQUEST['custom_permalink'] )
     494     * @param bool    $update  Whether this is an existing post being updated or not.
     495     */
     496    public function save_post( $post_id, $post, $update ) {
     497        /*
     498         * Enable permalink regeneration on creating new post if permalink structure
     499         * is defined for the post type.
     500         */
     501        if ( ! $update ) {
     502            $permalink_structure = '';
     503            $post_types_settings = get_option( 'custom_permalinks_post_types_settings', array() );
     504
     505            if ( isset( $post_types_settings[ $post->post_type ] ) ) {
     506                $permalink_structure = $post_types_settings[ $post->post_type ];
     507            }
     508
     509            /*
     510             * Permalink structure is not defined in the Plugin Settings.
     511             */
     512            if ( ! empty( $permalink_structure ) ) {
     513                // Make it 1 to keep generating permalink on updating the post.
     514                update_post_meta( $post_id, 'custom_permalink_regenerate_status', 1 );
     515            }
     516        }
     517
     518        if ( 'auto-draft' === sanitize_title( $post->post_title ) ) {
     519            return;
     520        }
     521
     522        if ( isset( $_REQUEST['_custom_permalinks_post_nonce'] )
     523            && isset( $_REQUEST['custom_permalink'] )
    379524        ) {
    380             return;
    381         }
    382 
    383         $action = 'custom-permalinks_' . $post_id;
    384         // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
    385         // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
    386         if ( ! wp_verify_nonce( $_REQUEST['_custom_permalinks_post_nonce'], $action ) ) {
    387             return;
    388         }
    389         // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
     525            $action = 'custom-permalinks_' . $post_id;
     526            // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
     527            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     528            if ( ! wp_verify_nonce( $_REQUEST['_custom_permalinks_post_nonce'], $action ) ) {
     529                return;
     530            }
     531            // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
     532        }
     533
     534        if ( 'inherit' === $post->post_status ) {
     535            $post_parent_id = $post->post_parent;
     536            if ( ! empty( $post_parent_id ) ) {
     537                $post_id = $post_parent_id;
     538                $post    = get_post( $post_parent_id );
     539            }
     540        }
     541
     542        $current_permalink = get_post_meta( $post_id, 'custom_permalink', true );
     543        $is_refresh        = get_post_meta( $post_id, 'custom_permalink_regenerate_status', true );
     544
     545        // Make it 0 if not exist.
     546        if ( empty( $is_refresh ) ) {
     547            $is_refresh = 0;
     548        }
     549
     550        /*
     551         * Make sure that the post saved from quick edit form so, just make the
     552         * $_REQUEST['custom_permalink'] same as $current_permalink to regenerate permalink
     553         * if applicable.
     554         */
     555        if ( ! isset( $_REQUEST['custom_permalink'] ) ) {
     556            $_REQUEST['custom_permalink'] = $current_permalink;
     557        }
     558
     559        $is_regenerated = false;
     560        if ( 'trash' !== $post->post_status
     561            && $current_permalink === $_REQUEST['custom_permalink']
     562            && 1 === (int) $is_refresh
     563        ) {
     564            $cp_post_permalinks = new Custom_Permalinks_Generate_Post_Permalinks();
     565            $is_regenerated     = $cp_post_permalinks->generate( $post_id, $post );
     566        }
    390567
    391568        $cp_frontend   = new Custom_Permalinks_Frontend();
    392569        $original_link = $cp_frontend->original_post_link( $post_id );
    393 
    394570        if ( ! empty( $_REQUEST['custom_permalink'] )
    395571            && $_REQUEST['custom_permalink'] !== $original_link
     572            && $_REQUEST['custom_permalink'] !== $current_permalink
    396573        ) {
    397574            $language_code = apply_filters(
     
    403580                )
    404581            );
     582            if ( null !== $language_code ) {
     583                update_post_meta( $post_id, 'custom_permalink_language', $language_code );
     584            } else {
     585                delete_metadata( 'post', $post_id, 'custom_permalink_language' );
     586            }
    405587
    406588            $permalink = $this->sanitize_permalink(
     
    409591                $language_code
    410592            );
     593            $permalink = $this->check_permalink_exists( $post_id, $permalink, $language_code );
    411594            $permalink = apply_filters(
    412595                'custom_permalink_before_saving',
    413596                $permalink,
    414                 $post_id
     597                $post_id,
     598                $language_code
    415599            );
    416600
    417601            update_post_meta( $post_id, 'custom_permalink', $permalink );
    418             if ( null !== $language_code ) {
    419                 update_post_meta( $post_id, 'custom_permalink_language', $language_code );
    420             } else {
    421                 delete_metadata( 'post', $post_id, 'custom_permalink_language' );
    422             }
     602
     603            // Clear cache for the previous and updated permalink.
     604            $this->clear_post_permalink_cache( $current_permalink );
     605            $this->clear_post_permalink_cache( $permalink );
     606
     607            // If true means it triggers from the regeneration code so don't override it.
     608            if ( ! $is_regenerated ) {
     609                // Delete to prevent generating permalink on updating the post.
     610                delete_post_meta( $post_id, 'custom_permalink_regenerate_status' );
     611            }
     612        }
     613    }
     614
     615    /**
     616     * This action fires when WP All Import saves a post of any type.
     617     *
     618     * @since 3.0.0
     619     * @access public
     620     *
     621     * @param int              $post_id   The ID of the item (post/user/taxonomy) saved or updated.
     622     * @param SimpleXMLElement $xml_node  The libxml resource of the current XML element.
     623     * @param bool             $is_update Returns 0 for new item 1 for updated item.
     624     *
     625     * @return void
     626     */
     627    public function pmxi_post_permalink( $post_id, $xml_node, $is_update ) {
     628        $post = get_post( $post_id );
     629        if ( is_object( $post ) && isset( $post->post_type ) ) {
     630            $updated = false;
     631            if ( 1 === $is_update ) {
     632                $updated = true;
     633            }
     634
     635            $this->save_post( $post_id, $post, $updated );
     636        }
     637    }
     638
     639    /**
     640     * Generates post permalink for the provided post id.
     641     *
     642     * @since 3.0.0
     643     * @access public
     644     *
     645     * @param int $post_id Post ID.
     646     *
     647     * @return void
     648     */
     649    public function generate_post_permalink( $post_id ) {
     650        $post = get_post( $post_id );
     651        if ( is_object( $post ) && isset( $post->post_type ) ) {
     652            $this->save_post( $post_id, $post, false );
    423653        }
    424654    }
     
    434664     */
    435665    public function delete_permalink( $post_id ) {
     666        // Clear cache for the deleting post.
     667        $permalink = get_post_meta( $post_id, 'custom_permalink', true );
     668        $this->clear_post_permalink_cache( $permalink );
     669
    436670        delete_metadata( 'post', $post_id, 'custom_permalink' );
    437671    }
     
    615849        $post = get_post( $post_id );
    616850
    617         $disable_cp              = $this->exclude_custom_permalinks( $post );
     851        $is_customizable         = $this->is_permalink_customizable( $post );
    618852        $this->permalink_metabox = 1;
    619         if ( $disable_cp ) {
     853        if ( $is_customizable ) {
    620854            return $html;
    621855        }
     
    635869     */
    636870    public function meta_edit_form( $post ) {
    637         $disable_cp = $this->exclude_custom_permalinks( $post );
    638         if ( $disable_cp ) {
     871        $is_customizable = $this->is_permalink_customizable( $post );
     872        if ( $is_customizable ) {
    639873            wp_enqueue_script(
    640874                'custom-permalinks-form',
     
    7871021            true
    7881022        );
    789 
    790         echo esc_url( $home_url ) .
    791         '<span id="editable-post-name" title="Click to edit this part of the permalink">';
    792 
    793         if ( isset( $postname ) && '' !== $postname ) :
    794             ?>
     1023        ?>
     1024
     1025        <span id="uneditable-permalink"><?php echo esc_url( $home_url ); ?></span>
     1026        <span id="editable-post-name" title="Click to edit this part of the permalink">
     1027
     1028        <?php if ( isset( $postname ) && '' !== $postname ) : ?>
    7951029
    7961030            <input type="hidden" id="new-post-slug" class="text" value="<?php echo esc_attr( $postname ); ?>" />
  • custom-permalinks/trunk/includes/class-custom-permalinks-frontend.php

    r3284095 r3332124  
    7777        add_filter( 'post_type_link', array( $this, 'custom_post_link' ), 10, 2 );
    7878        add_filter( 'page_link', array( $this, 'custom_page_link' ), 10, 2 );
     79        add_filter( 'url_to_postid', array( $this, 'postid_to_customized_permalink' ), 10, 1 );
    7980        add_filter( 'term_link', array( $this, 'custom_term_link' ), 10, 2 );
    8081        add_filter( 'user_trailingslashit', array( $this, 'custom_trailingslash' ) );
     
    202203        $posts      = wp_cache_get( $cache_name, 'custom_permalinks' );
    203204
    204         if ( ! $posts ) {
     205        if ( false === $posts ) {
    205206            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
    206207            $posts = $wpdb->get_results(
     
    238239            }
    239240
    240             wp_cache_set( $cache_name, $posts, 'custom_permalinks' );
     241            // Cache permalink for 24 hours.
     242            wp_cache_set( $cache_name, $posts, 'custom_permalinks', 86400 );
    241243        }
    242244
     
    267269        }
    268270
    269         if ( ! $matched_post ) {
     271        if ( false === $matched_post ) {
    270272            $matched_post = array();
    271273
     
    306308        }
    307309
    308         wp_cache_set( $cache_name, $matched_post, 'custom_permalinks' );
     310        // Cache permalink for 24 hours.
     311        wp_cache_set( $cache_name, $matched_post, 'custom_permalinks', 86400 );
    309312
    310313        return $matched_post;
     
    713716        }
    714717
    715         $custom_permalink   = '';
    716         $original_permalink = '';
    717 
    718718        // Get request URI, strip parameters.
    719719        $url     = wp_parse_url( get_bloginfo( 'url' ) );
     
    735735         * @since 1.7.0
    736736         */
    737         $avoid_redirect = apply_filters(
    738             'custom_permalinks_avoid_redirect',
    739             $request
    740         );
    741 
     737        $avoid_redirect = apply_filters( 'custom_permalinks_avoid_redirect', $request );
    742738        if ( is_bool( $avoid_redirect ) && $avoid_redirect ) {
    743739            return;
    744740        }
    745741
    746         if ( defined( 'POLYLANG_VERSION' ) ) {
    747             $cp_form = new Custom_Permalinks_Form();
    748             $request = $cp_form->check_conflicts( $request );
    749         }
    750 
    751         $request_no_slash = preg_replace( '@/+@', '/', trim( $request, '/' ) );
    752         $posts            = $this->query_post( $request_no_slash );
    753 
    754         if ( ! isset( $posts[0]->ID ) || ! isset( $posts[0]->meta_value )
    755             || empty( $posts[0]->meta_value )
    756         ) {
    757             global $wp_query;
    758 
    759             /*
    760              * If the post/tag/category we're on has a custom permalink, get it
    761              * and check against the request.
    762              */
    763             if ( ( is_single() || is_page() ) && ! empty( $wp_query->post ) ) {
    764                 $post             = $wp_query->post;
    765                 $custom_permalink = get_post_meta(
    766                     $post->ID,
    767                     'custom_permalink',
    768                     true
    769                 );
    770                 if ( 'page' === $post->post_type ) {
    771                     $original_permalink = $this->original_page_link( $post->ID );
    772                 } else {
    773                     $original_permalink = $this->original_post_link( $post->ID );
    774                 }
    775             } elseif ( is_tag() || is_category() ) {
    776                 $the_term           = $wp_query->get_queried_object();
    777                 $custom_permalink   = $this->term_permalink( $the_term->term_id );
    778                 $original_permalink = $this->original_term_link( $the_term->term_id );
     742        $custom_permalink   = '';
     743        $original_permalink = '';
     744        $get_post_id        = url_to_postid( $this->request_uri );
     745
     746        // Redirect original post permalink.
     747        if ( ! empty( $get_post_id ) ) {
     748            $custom_permalink = get_post_meta( $get_post_id, 'custom_permalink', true );
     749            if ( ! empty( $custom_permalink ) ) {
     750                // Append any query component.
     751                $custom_permalink .= strstr( $this->request_uri, '?' );
     752
     753                wp_safe_redirect( home_url() . '/' . $custom_permalink, 301 );
     754                exit( 0 );
    779755            }
    780756        } else {
    781             $custom_permalink = $posts[0]->meta_value;
    782             if ( 'page' === $posts[0]->post_type ) {
    783                 $original_permalink = $this->original_page_link( $posts[0]->ID );
    784             } else {
    785                 $original_permalink = $this->original_post_link( $posts[0]->ID );
    786             }
    787         }
    788 
    789         $custom_length = strlen( $custom_permalink );
    790         if ( $custom_permalink
    791             && (
    792                 substr( $request, 0, $custom_length ) !== $custom_permalink
    793                 || $request === $custom_permalink . '/'
    794             )
    795         ) {
    796             // Request doesn't match permalink - redirect.
    797             $url             = $custom_permalink;
    798             $original_length = strlen( $original_permalink );
    799 
    800             if ( substr( $request, 0, $original_length ) === $original_permalink
    801                 && trim( $request, '/' ) !== trim( $original_permalink, '/' )
     757            if ( defined( 'POLYLANG_VERSION' ) ) {
     758                $cp_form = new Custom_Permalinks_Form();
     759                $request = $cp_form->check_conflicts( $request );
     760            }
     761
     762            $request_no_slash = preg_replace( '@/+@', '/', trim( $request, '/' ) );
     763            $posts            = $this->query_post( $request_no_slash );
     764
     765            if ( ! isset( $posts[0]->ID ) || ! isset( $posts[0]->meta_value )
     766                || empty( $posts[0]->meta_value )
    802767            ) {
    803                 // This is the original link; we can use this URL to derive the new one.
    804                 $url = preg_replace(
    805                     '@//*@',
    806                     '/',
    807                     str_replace(
    808                         trim( $original_permalink, '/' ),
    809                         trim( $custom_permalink, '/' ),
    810                         $request
    811                     )
    812                 );
    813                 $url = preg_replace( '@([^?]*)&@', '\1?', $url );
    814             }
    815 
    816             // Append any query component.
    817             $url .= strstr( $this->request_uri, '?' );
    818 
    819             wp_safe_redirect( home_url() . '/' . $url, 301 );
    820             exit( 0 );
     768                global $wp_query;
     769
     770                /*
     771                * If the post/tag/category we're on has a custom permalink, get it
     772                * and check against the request.
     773                */
     774                if ( ( is_single() || is_page() ) && ! empty( $wp_query->post ) ) {
     775                    $post             = $wp_query->post;
     776                    $custom_permalink = get_post_meta(
     777                        $post->ID,
     778                        'custom_permalink',
     779                        true
     780                    );
     781                    if ( 'page' === $post->post_type ) {
     782                        $original_permalink = $this->original_page_link( $post->ID );
     783                    } else {
     784                        $original_permalink = $this->original_post_link( $post->ID );
     785                    }
     786                } elseif ( is_tag() || is_category() ) {
     787                    $the_term           = $wp_query->get_queried_object();
     788                    $custom_permalink   = $this->term_permalink( $the_term->term_id );
     789                    $original_permalink = $this->original_term_link( $the_term->term_id );
     790                }
     791            }
     792
     793            $custom_length = strlen( $custom_permalink );
     794            if ( $custom_permalink
     795                && (
     796                    substr( $request, 0, $custom_length ) !== $custom_permalink
     797                    || $request === $custom_permalink . '/'
     798                )
     799            ) {
     800                // Request doesn't match permalink - redirect.
     801                $url             = $custom_permalink;
     802                $original_length = strlen( $original_permalink );
     803
     804                if ( substr( $request, 0, $original_length ) === $original_permalink
     805                    && trim( $request, '/' ) !== trim( $original_permalink, '/' )
     806                ) {
     807                    // This is the original link; we can use this URL to derive the new one.
     808                    $url = preg_replace(
     809                        '@//*@',
     810                        '/',
     811                        str_replace(
     812                            trim( $original_permalink, '/' ),
     813                            trim( $custom_permalink, '/' ),
     814                            $request
     815                        )
     816                    );
     817                    $url = preg_replace( '@([^?]*)&@', '\1?', $url );
     818                }
     819
     820                // Append any query component.
     821                $url .= strstr( $this->request_uri, '?' );
     822
     823                wp_safe_redirect( home_url() . '/' . $url, 301 );
     824                exit( 0 );
     825            }
    821826        }
    822827    }
     
    924929
    925930    /**
     931     * Fetch default permalink against the customized permalink.
     932     *
     933     * @since 3.0.0
     934     * @access public
     935     *
     936     * @param string $permalink URL Permalink to check.
     937     *
     938     * @return string Default Permalink or the same permalink if not found.
     939     */
     940    public function postid_to_customized_permalink( $permalink ) {
     941        $customized_permalink = ltrim( substr( $permalink, strlen( $url ) ), '/' );
     942        if ( defined( 'POLYLANG_VERSION' ) ) {
     943            $cp_form              = new Custom_Permalinks_Form();
     944            $customized_permalink = $cp_form->check_conflicts( $customized_permalink );
     945        }
     946
     947        $customized_permalink = preg_replace( '@/+@', '/', trim( $customized_permalink, '/' ) );
     948        $posts                = $this->query_post( $customized_permalink );
     949        if ( is_array( $posts ) && ! empty( $posts ) ) {
     950            if ( 'draft' === $posts[0]->post_status
     951                || 'pending' === $posts[0]->post_status
     952            ) {
     953                if ( 'page' === $posts[0]->post_type ) {
     954                    $original_url = '?page_id=' . $posts[0]->ID;
     955                } else {
     956                    $original_url = '?post_type=' . $posts[0]->post_type . '&p=' . $posts[0]->ID;
     957                }
     958            } else {
     959                $post_meta = trim( strtolower( $posts[0]->meta_value ), '/' );
     960                if ( 'page' === $posts[0]->post_type ) {
     961                    $get_original_url = $this->original_page_link( $posts[0]->ID );
     962                    $original_url     = preg_replace(
     963                        '@/+@',
     964                        '/',
     965                        str_replace(
     966                            $post_meta,
     967                            $get_original_url,
     968                            strtolower( $customized_permalink )
     969                        )
     970                    );
     971                } else {
     972                    $get_original_url = $this->original_post_link( $posts[0]->ID );
     973                    $original_url     = preg_replace(
     974                        '@/+@',
     975                        '/',
     976                        str_replace(
     977                            $post_meta,
     978                            $get_original_url,
     979                            strtolower( $customized_permalink )
     980                        )
     981                    );
     982                }
     983            }
     984
     985            $permalink = $original_url;
     986        }
     987
     988        return $permalink;
     989    }
     990
     991    /**
    926992     * Filter to replace the term permalink with the custom one.
    927993     *
  • custom-permalinks/trunk/includes/class-custom-permalinks.php

    r3284095 r3332124  
    1919     * @var string
    2020     */
    21     public $version = '2.8.0';
     21    public $version = '3.0.0';
    2222
    2323    /**
     
    6565    private function includes() {
    6666        include_once CUSTOM_PERMALINKS_PATH . 'includes/class-custom-permalinks-form.php';
     67        include_once CUSTOM_PERMALINKS_PATH . 'includes/class-custom-permalinks-generate-post-permalinks.php';
    6768        include_once CUSTOM_PERMALINKS_PATH . 'includes/class-custom-permalinks-frontend.php';
    6869        include_once CUSTOM_PERMALINKS_PATH . 'admin/class-custom-permalinks-admin.php';
     70        include_once CUSTOM_PERMALINKS_PATH . 'admin/class-custom-permalinks-post-types-settings.php';
    6971        include_once CUSTOM_PERMALINKS_PATH . 'admin/class-custom-permalinks-post-types.php';
    7072        include_once CUSTOM_PERMALINKS_PATH . 'admin/class-custom-permalinks-post-types-table.php';
     
    107109     */
    108110    public static function add_roles() {
    109         $admin_role      = get_role( 'administrator' );
    110         $cp_role         = get_role( 'custom_permalinks_manager' );
    111         $current_version = get_option( 'custom_permalinks_plugin_version', -1 );
     111        $admin_role = get_role( 'administrator' );
     112        $cp_role    = get_role( 'custom_permalinks_manager' );
    112113
    113114        if ( ! empty( $admin_role ) ) {
     115            $admin_role->add_cap( 'custom_permalinks_post_settings' );
    114116            $admin_role->add_cap( 'cp_view_post_permalinks' );
    115117            $admin_role->add_cap( 'cp_view_category_permalinks' );
     
    121123                __( 'Custom Permalinks Manager' ),
    122124                array(
    123                     'cp_view_post_permalinks'     => true,
    124                     'cp_view_category_permalinks' => true,
     125                    'custom_permalinks_post_settings' => true,
     126                    'cp_view_post_permalinks'         => true,
     127                    'cp_view_category_permalinks'     => true,
    125128                )
    126129            );
  • custom-permalinks/trunk/readme.txt

    r3284095 r3332124  
    11=== Custom Permalinks ===
    22Contributors: sasiddiqui
    3 Tags: permalink, url, link, address, redirect, custom post type
     3Tags: permalink, url, link, address, redirect
     4Requires at least: 5.0
     5Requires PHP: 5.6
    46Tested up to: 6.8
    5 Stable tag: 2.8.0
     7Stable tag: 3.0.0
    68License: GPLv3
    79License URI: https://www.gnu.org/licenses/gpl-3.0.html
    810
    9 Set custom permalinks on a per-post, per-tag or per-category basis.
     11Custom Permalinks is a powerful WordPress plugin that grants you complete control over your site's URLs. Easily set unique, custom permalinks for any post, page, tag, or category, allowing you to design an ideal site structure. It includes features for defining post type-specific structures with dynamic tags and automatically redirects old URLs to your new custom ones. Developers can also leverage a wide array of filters for advanced customization.
    1012
    1113== Description ==
    1214
    13 > In case of found any site breaking issue after upgrading to the latest version then please report the issue on [WordPress Forum](https://wordpress.org/support/plugin/custom-permalinks/) OR [GitHub](https://github.com/samiahmedsiddiqui/custom-permalinks) with complete information to reproduce the issue and move back to the old version. You can download any of the old version from here: https://wordpress.org/plugins/custom-permalinks/advanced/
     15You want to take control of your WordPress site's URLs? The **Custom Permalinks** plugin gives you the power to set unique, custom URLs for any post, page, tag, or category. This means you can design your site's structure exactly how you envision it, rather than being limited by WordPress's default settings. When you set a custom permalink, the original post URL will be automatically redirected to your new, customized URL.
    1416
    15 Lay out your site the way *you* want it. Set the URL of any post, page, tag or category to anything you want. Old permalinks will redirect properly to the new address. Custom Permalinks give you ultimate control over your site structure.
     17=== Key Features ===
    1618
    17 > Be warned: *This plugin is not a replacement for WordPress's built-in permalink system*. Check your WordPress administration's "Permalinks" settings page first, to make sure that this doesn't already meet your needs.
     19* **Individual Permalink Control**: Assign unique URLs to any post, page, tag, or category.
     20* **Site Structure Control**: Gain ultimate control over how your site's URLs are organized.
     21* **Post Type Permalink Structures (v3.0.0+)**: Define custom permalink structures for each public Post Type using predefined tags, automatically generating URLs upon content creation. You can still manually edit any permalink. If left empty, default settings will apply.
    1822
    19 This plugin is only useful for assigning custom permalinks for *individual* posts, pages, tags or categories. It will not apply whole permalink structures or automatically apply a category's custom permalink to the posts within that category.
     23=== Getting Started: Plugin Settings ===
    2024
    21 == Filters ==
     25You can configure Custom Permalinks by navigating to **Settings \> Custom Permalinks** in your WordPress Dashboard.
    2226
    23 === Add `PATH_INFO` in `$_SERVER` Variable ===
     27=== Available Tags for Permalink Structures ===
    2428
    25 `
    26 add_filter( 'custom_permalinks_path_info', '__return_true' );
    27 `
     29When setting up your custom permalink structures, you can use a variety of tags that will dynamically populate the URL. Here's a breakdown of what's available:
    2830
    29 === Disable Redirects ===
     31* **%year%**: The year of the post in four digits, eg: 2025
     32* **%monthnum%**: Month the post was published, in two digits, eg: 01
     33* **%day%**: Day the post was published in two digits, eg: 02
     34* **%hour%**: Hour of the day, the post was published, eg: 15
     35* **%minute%**: Minute of the hour, the post was published, eg: 43
     36* **%second%**: Second of the minute, the post was published, eg: 33
     37* **%post_id%**: The unique ID of the post, eg: 123
     38* **%category%**: A clean version of the category name (its slug). Nested sub-categories will appear as nested directories in the URL..
     39* **%author%**: A sanitized version of the post author’s name.
     40* **%postname%**: A clean version of the post or page title (its slug). For example, "This Is A Great Post\!" becomes `this-is-a-great-post` in the URL.
     41* **%parent_postname%**: Similar to `%postname%`, but uses the immediate parent page's slug if a parent is selected.
     42* **%parents_postnames%**: Similar to `%postname%`, but includes all parent page slugs if parents are selected.
     43* **%title%**: The title of the post, converted to a slug. For example, "This Is A Great Post\!" becomes `this-is-a-great-post`. Unlike `%postname%` which is set once, `%title%` automatically updates in the permalink if the post title changes (unless the post is published or the permalink is manually edited).
     44* **%ctax_TAXONOMY_NAME%**: A clean version of a custom taxonomy's name. Replace `TAXONOMY_NAME` with the actual taxonomy name. You can also provide a default slug for when no category/taxonomy is selected by using `??` (e.g., `%ctax_type??sales%` will use "sales" as a default).
     45* **%ctax_parent_TAXONOMY_NAME%**: Similar to `%ctax_TAXONOMY_NAME%`, but includes the immediate parent category/tag slug in the URL if a parent is selected.
     46* **%ctax_parents_TAXONOMY_NAME%**: Similar to `%ctax_TAXONOMY_NAME%`, but includes all parent category/tag slugs in the URL if parents are selected.
     47* **%custom_permalinks_TAG_NAME%**: Developers have the flexibility to define their own custom tags(replace `_TAG_NAME` with your desired name). To ensure these tags resolve to the correct permalinks, simply apply the `custom_permalinks_post_permalink_tag` filter.
    3048
    31 To disable complete redirects functionality provided by this plugin, add the filter that looks like this:
     49**Important Note:** For new posts, Custom Permalinks will keep updating the permalink while the post is in draft mode, assuming a structure is defined in the plugin settings. Once the post is published or its permalink is manually updated, the plugin will stop automatic updates for that specific post.
    3250
    33 `
    34 function yasglobal_avoid_redirect( $permalink ) {
    35   return true;
    36 }
    37 add_filter( 'custom_permalinks_avoid_redirect', 'yasglobal_avoid_redirect' );
    38 `
     51=== Custom Permalinks: Fine-Tuning with Filters ===
    3952
    40 === Disable Particular Redirects ===
     53Custom Permalinks offers a range of **filters** that empower developers to precisely control its behavior. You can explore all available filters, complete with example code snippets, in our [GitHub repository](https://github.com/samiahmedsiddiqui/custom-permalinks).
    4154
    42 To disable any specific redirect to be processed by this plugin, add the filter that looks like this:
     55**For Assistance:**
    4356
    44 `
    45 function yasglobal_avoid_redirect( $permalink ) {
    46   // Replace 'testing-hello-world/' with the permalink you want to avoid
    47   if ( 'testing-hello-world/' === $permalink ) {
    48     return true;
    49   }
     57* **Premium Users:** If you need assistance implementing these filters, please don't hesitate to reach out to us via our [Premium contact support](https://www.custompermalinks.com/contact-us/).
     58* **Other Users:** You can also directly reach out to the plugin author via [LinkedIn](https://www.linkedin.com/in/sami-ahmed-siddiqui/).
    5059
    51   return false;
    52 }
    53 add_filter( 'custom_permalinks_avoid_redirect', 'yasglobal_avoid_redirect' );
    54 `
     60=== Need Help or Found a Bug? ===
    5561
    56 === Exclude Permalink to be processed ===
     62* **Support:** For one-on-one email support, consider purchasing [Custom Permalinks Premium](https://www.custompermalinks.com/#pricing-section). While some basic support may be provided on the WordPress.org forums, email support is prioritized for premium users.
     63* **Bug Reports:** If you encounter a bug, please report it on [GitHub](https://github.com/samiahmedsiddiqui/custom-permalinks). Make sure to provide complete information to reproduce the issue. GitHub is for bug reports, not general support questions.
    5764
    58 To exclude any Permalink to be processed by the plugin, add the filter that looks like this:
    59 
    60 `
    61 function yasglobal_xml_sitemap_url( $permalink ) {
    62   if ( false !== strpos( $permalink, 'sitemap.xml' ) ) {
    63     return '__true';
    64   }
    65 
    66   return;
    67 }
    68 add_filter( 'custom_permalinks_request_ignore', 'yasglobal_xml_sitemap_url' );
    69 `
    70 
    71 === Exclude Post Type ===
    72 
    73 To remove custom permalink **form** from any post type, add the filter that looks like this:
    74 
    75 `
    76 function yasglobal_exclude_post_types( $post_type ) {
    77   // Replace 'custompost' with your post type name
    78   if ( 'custompost' === $post_type ) {
    79     return '__true';
    80   }
    81 
    82   return '__false';
    83 }
    84 add_filter( 'custom_permalinks_exclude_post_type', 'yasglobal_exclude_post_types' );
    85 `
    86 
    87 === Exclude Posts ===
    88 
    89 To exclude custom permalink **form** from any posts (based on ID, Template, etc), add the filter that looks like this:
    90 
    91 `
    92 function yasglobal_exclude_posts( $post ) {
    93   if ( 1557 === $post->ID ) {
    94     return true;
    95   }
    96 
    97   return false;
    98 }
    99 add_filter( 'custom_permalinks_exclude_posts', 'yasglobal_exclude_posts' );
    100 `
    101 
    102 === Allow Accents Letters ===
    103 
    104 To allow accents letters, please add below-mentioned line in your theme `functions.php`:
    105 
    106 `
    107 function yasglobal_permalink_allow_accents() {
    108   return true;
    109 }
    110 add_filter( 'custom_permalinks_allow_accents', 'yasglobal_permalink_allow_accents' );
    111 `
    112 
    113 === Allow Uppercase Letters ===
    114 
    115 To allow uppercase letters/words, please add below-mentioned line in your theme `functions.php`:
    116 
    117 `
    118 function yasglobal_allow_uppercaps() {
    119   return true;
    120 }
    121 add_filter( 'custom_permalinks_allow_caps', 'yasglobal_allow_uppercaps' );
    122 `
    123 
    124 === Allow Redundant Hyphens ===
    125 
    126 To allow redundant hyphens, please add below-mentioned line in your theme `functions.php`:
    127 
    128 `
    129 function yasglobal_redundant_hyphens() {
    130   return true;
    131 }
    132 add_filter( 'custom_permalinks_redundant_hyphens', 'yasglobal_redundant_hyphens' );
    133 `
    134 
    135 === Manipulate Permalink Before Saving ===
    136 
    137 To make changes in permalink before saving, please use `custom_permalink_before_saving` filter. Here is an example to see how it works.
    138 
    139 `
    140 function yasglobal_permalink_before_saving( $permalink, $post_id ) {
    141   // Check trialing slash in the permalink.
    142   if ( substr( $permalink, -1 ) !== '/' ) {
    143     // If permalink doesn't contain trialing slash then add one.
    144     $permalink .= '/';
    145   }
    146 
    147   return $permalink;
    148 }
    149 add_filter( 'custom_permalink_before_saving', 'yasglobal_permalink_before_saving', 10, 2 );
    150 `
    151 
    152 === Remove `like` Query ===
    153 
    154 To remove `like` query to being work, add below-mentioned line in your theme `functions.php`:
    155 `
    156 add_filter( 'cp_remove_like_query', '__return_false' );
    157 `
    158 
    159 Note: Use `custom_permalinks_like_query` filter if the URLs doesn't works for you after upgrading to `v1.2.9`.
    160 
    161 === Thanks for the Support ===
    162 
    163 I do not always provide active support for the Custom Permalinks plugin on the WordPress.org forums, as I have prioritized the email support. One-on-one email support is available to people who bought [Custom Permalinks Premium](https://www.custompermalinks.com/#pricing-section) only.
    164 
    165 === Bug reports ===
    166 
    167 Bug reports for Custom Permalinks are [welcomed on GitHub](https://github.com/samiahmedsiddiqui/custom-permalinks). Please note GitHub is not a support forum, and issues that aren't properly qualified as bugs will be closed.
     65If you experience any site-breaking issues after upgrading, please report them on the [WordPress Forum](https://wordpress.org/support/plugin/custom-permalinks/) or [GitHub](https://github.com/samiahmedsiddiqui/custom-permalinks) with detailed information. You can always revert to an older version by downloading it from [https://wordpress.org/plugins/custom-permalinks/advanced/](https://wordpress.org/plugins/custom-permalinks/advanced/).
    16866
    16967== Installation ==
     68You have two ways to install Custom Permalinks:
    17069
    171 This process defines you the steps to follow either you are installing through WordPress or Manually from FTP.
     70#### From within WordPress
    17271
    173 **From within WordPress**
     721.  Go to **Plugins \> Add New** in your WordPress dashboard.
     732.  Search for "Custom Permalinks".
     743.  Click "Install Now" and then "Activate" the plugin from your Plugins page.
    17475
    175 1. Visit 'Plugins > Add New'
    176 2. Search for Custom Permalinks
    177 3. Activate Custom Permalinks from your Plugins page.
     76#### Manually via FTP
    17877
    179 **Manually**
    180 
    181 1. Upload the `custom-permalinks` folder to the `/wp-content/plugins/` directory
    182 2. Activate Custom Permalinks through the 'Plugins' menu in WordPress
     781.  Download the `custom-permalinks` folder.
     792.  Upload the `custom-permalinks` folder to your `/wp-content/plugins/` directory.
     803.  Activate Custom Permalinks through the "Plugins" menu in your WordPress dashboard.
    18381
    18482== Changelog ==
    18583
    186 = 2.8.0 - Apr 29, 2025 =
     84= 3.0.0 - Jul 22, 2025 =
    18785
    188 * Bug:
    189   * Resolved pagination issue with custom permalinks (now supports /page/{number} format correctly).
    190 * Enhancements:
    191     * Added compatibility with Polylang 3.7.
    192     * Metabox is now hidden for post types that are not publicly queryable.
     86This release of Custom Permalinks brings significant enhancements to post type permalink management, introduces new customization options, and refines the overall user and developer experience.
    19387
    194 = 2.7.0 - Aug 20, 2024 =
     88**Added**
    19589
    196 * Bug
    197   * [Passing null to parameter string is deprecated](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/86)
    198   * [Fix PHP warning with empty permalink on new page/post](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/87)
    199     * [Authenticated(Editor+) Stored Cross-Site Scripting](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/96)
    200 * Enhancement:
    201     * [Improve I18N](https://github.com/samiahmedsiddiqui/custom-permalinks/pull/72)
     90  * **Post Type Permalink Structures:** Introduced robust functionality to define custom permalink structures for each public Post Type directly within the plugin settings. This allows for automatic URL generation based on predefined tags upon content creation, offering greater flexibility while still allowing manual edits.
     91  * **New Available Permalink Tags:** Expanded the list of dynamic tags that can be used in permalink structures, including:
     92    * `%parent_postname%`: For immediate parent page slugs.
     93    * `%parents_postnames%`: For all parent page slugs.
     94    * `%title%`: A dynamic slug that updates with post title changes (until published or manually edited).
     95    * `%ctax_parent_TAXONOMY_NAME%`: For immediate parent custom taxonomy slugs.
     96    * `%ctax_parents_TAXONOMY_NAME%`: For all parent custom taxonomy slugs.
     97    * `%custom_permalinks_TAG_NAME%`: Allows developers to define and resolve their own custom tags.
     98  * **WP All Import Compatibility:** Added support to generate/update permalinks when importing posts using the WP All Import plugin.
     99  * **New Filter Examples:** Included clear code examples for `custom_permalinks_post_permalink_tag` to set custom values from ACF fields, and for programmatically generating permalinks for single posts and entire post types.
    202100
    203 = 2.6.0 - Aug 15, 2024 =
     101**Improved**
    204102
    205 * Feature Additions:
    206   * Compatibility with PolyLang Plugin
    207 
    208 = 2.5.2 - Feb 14, 2023 =
    209 
    210 * Bug
    211   * [Error in new update](https://wordpress.org/support/topic/error-in-new-update-3/)
    212 
    213 = 2.5.1 - Feb 14, 2023 =
    214 
    215 * Bug
    216   * [“http//” is added in front of permalinks](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/71)
    217 
    218 = 2.5.0 - Jan 02, 2023 =
    219 
    220 * Bugs
    221   * [Retreiving info from installed plugin (GDPR)](https://wordpress.org/support/topic/retreiving-info-from-installed-plugin-gdpr/)
    222 * Enhancement
    223     * Same permalink with WPML different domain
    224 
    225 = 2.4.0 - Nov 26, 2021 =
    226 
    227 * Bugs
    228   * [filter for leading special characters](https://wordpress.org/support/topic/filter-for-leading-special-characters/)
    229   * [“search Permalinks” button doesn’t work. (part2)](https://wordpress.org/support/topic/search-permalinks-button-doesnt-work-part2/)
    230   * [PHP 8 errors on first visit of Taxonomy Permalinks tab](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/59)
    231   * [Notice: Undefined variable: site_url in custom-permalinks/admin/class-custom-permalinks-post-types-table.php on line 306](https://github.com/samiahmedsiddiqui/custom-permalinks/issues/56)
    232 * Enhancements
    233   * [Pending Post Preview Link](https://wordpress.org/support/topic/pending-post-preview-link/)
     103  * **Post Caching:** Enhanced post caching mechanisms and optimized cache deletion upon updates for better performance.
     104  * **Permalink Retrieval:** Improved logic to allow fetching posts against customized permalinks.
     105  * **Filter Documentation:** Refined existing filter descriptions and improved code formatting for clarity.
     106  * **Plugin Purpose Clarity:** Updated documentation to explicitly state that original post URLs will automatically redirect to the customized URLs, ensuring seamless transitions.
    234107
    235108= Earlier versions =
  • custom-permalinks/trunk/uninstall.php

    r2575556 r3332124  
    1515delete_post_meta_by_key( 'custom_permalink' );
    1616delete_option( 'custom_permalink_table' );
     17delete_option( 'custom_permalinks_post_types_settings' );
    1718
    1819$wp_role = get_role( 'administrator' );
     
    2021    $wp_role->remove_cap( 'cp_view_post_permalinks' );
    2122    $wp_role->remove_cap( 'cp_view_category_permalinks' );
     23    $wp_role->remove_cap( 'custom_permalinks_post_settings' );
    2224}
    2325
     
    2628    $wp_role->remove_cap( 'cp_view_post_permalinks' );
    2729    $wp_role->remove_cap( 'cp_view_category_permalinks' );
     30    $wp_role->remove_cap( 'custom_permalinks_post_settings' );
    2831
    2932    remove_role( 'custom_permalinks_manager' );
Note: See TracChangeset for help on using the changeset viewer.