File "class-archive-mapping.php"

Full Path: /home/arielcor/public_html/wp-content/plugins/visual-portfolio/classes/class-archive-mapping.php
File size: 32.97 KB
MIME-type: text/x-php
Charset: utf-8

<?php
/**
 * Archive Mapping.
 *
 * @package visual-portfolio
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

use Visual_Portfolio_Settings as Settings;

/**
 * Class Visual_Portfolio_Archive_Mapping
 */
class Visual_Portfolio_Archive_Mapping {
    /**
     * Archive Page.
     *
     * @var int $archive_page
     */
    private $archive_page = null;

    /**
     * Posts per page.
     *
     * @var integer
     */
    private $posts_per_page = -1;

    /**
     * Permalink settings.
     *
     * @var array
     */
    private $permalinks = array();

    /**
     * Visual_Portfolio_Archive_Mapping constructor.
     */
    public function __construct() {
        add_action( 'init', array( $this, 'init' ), 9 );
    }

    /**
     * Initialize archive.
     *
     * @see __construct
     */
    public function init() {
        $this->archive_page   = Settings::get_option( 'portfolio_archive_page', 'vp_general' );
        $this->posts_per_page = Settings::get_option( 'archive_page_items_per_page', 'vp_general' );
        $this->permalinks     = self::get_permalink_structure();

        if ( isset( $this->archive_page ) && ! empty( $this->archive_page ) && Visual_Portfolio_Custom_Post_Type::portfolio_post_type_is_registered() ) {

            $this->init_rewrite_rules();

            if ( -1 === (int) $this->posts_per_page ) {
                $this->posts_per_page = 9999;
            }

            add_action( 'pre_get_posts', array( $this, 'maybe_override_archive' ) );
            add_action( 'pre_post_update', array( $this, 'pre_page_update' ), 10, 2 );
            add_action( 'deleted_post', array( $this, 'delete_archive_page' ), 10, 1 );
            add_action( 'trashed_post', array( $this, 'delete_archive_page' ), 10, 1 );
            add_action( 'update_option_vp_general', array( $this, 'flush_rewrite_rules_after_update' ), 10, 3 );
            add_action( 'vpf_extend_query_args', array( $this, 'extend_query_args' ), 10, 2 );
            add_filter( 'vpf_layout_element_options', array( $this, 'unset_pagination_archive_page' ), 10, 1 );

            // Add a post display state for special Portfolio Archive page.
            add_filter( 'display_post_states', array( $this, 'add_display_post_states' ), 10, 2 );

            // Add Permalinks.
            add_action( 'admin_init', array( $this, 'permalink_settings_init' ) );
            add_action( 'admin_init', array( $this, 'permalink_settings_save' ), 12 );
            add_filter( 'post_type_link', array( $this, 'portfolio_permalink_replacements' ), 1, 2 );
            add_filter( 'vpf_extend_filter_items', array( $this, 'add_filter_items' ), 10, 2 );
            add_filter( 'the_title', array( $this, 'set_archive_title' ), 10, 2 );
            add_filter( 'body_class', array( $this, 'add_body_archive_classes' ), 10, 1 );
        }

        self::create_archive_page();
    }

    /**
     * Filters the list of CSS body class names for the current archive.
     *
     * @param array $classes - An array of body class names.
     * @return string
     */
    public function add_body_archive_classes( $classes ) {
        if (
            isset( $_REQUEST['vp_preview_post_id'] ) &&
            ! empty( $_REQUEST['vp_preview_post_id'] ) &&
            isset( $_REQUEST['vp_preview_nonce'] ) &&
            ! empty( $_REQUEST['vp_preview_nonce'] ) &&
            wp_verify_nonce( sanitize_key( $_REQUEST['vp_preview_nonce'] ), 'vp-ajax-nonce' )
        ) {
            $post_id = intval( $_REQUEST['vp_preview_post_id'] );
        }

        $post_id = $post_id ?? get_the_ID() ?? null;

        if ( $post_id && get_post_meta( $post_id, '_vp_post_type_mapped', true ) ) {
            $classes[] = 'visual-portfolio-archive';
            $classes[] = 'archive';
            $classes[] = 'post-type-archive';

            $unused_classes = array( 'single', 'single-page', 'page', 'postid-' . $post_id, 'page-id-' . $post_id );
            foreach ( $unused_classes as $unused_class ) {
                $founding_key = array_search( $unused_class, $classes, true );
                if ( false !== $founding_key ) {
                    unset( $classes[ $founding_key ] );
                }
            }
        }
        return $classes;
    }

    /**
     * Change Title for Archive Taxonomy pages.
     *
     * @param string $title - Post title.
     * @param int    $id - Post ID.
     * @return string
     */
    public function set_archive_title( $title, $id = 0 ) {
        if ( $id && get_post_meta( $id, '_vp_post_type_mapped', true ) ) {
            global $wp_query;

            if ( isset( $wp_query->query['vp_category'] ) ) {
                $category = get_term_by( 'slug', $wp_query->query['vp_category'], 'portfolio_category' );
                // translators: %s - taxonomy name.
                $title = sprintf( esc_html__( 'Portfolio Category: %s', 'visual-portfolio' ), esc_html( ucfirst( $category->name ) ) );
            }

            if ( isset( $wp_query->query['portfolio_tag'] ) ) {
                $tag = get_term_by( 'slug', $wp_query->query['portfolio_tag'], 'portfolio_tag' );
                // translators: %s - taxonomy name.
                $title = sprintf( esc_html__( 'Portfolio Tag: %s', 'visual-portfolio' ), esc_html( ucfirst( $tag->name ) ) );
            }
        }
        return $title;
    }

    /**
     * Add filter items.
     *
     * @param array $terms - Current terms.
     * @param array $vp_options - Current vp_list options.
     * @return array
     */
    public function add_filter_items( $terms, $vp_options ) {
        if (
            isset( $_REQUEST['vp_preview_post_id'] ) &&
            ! empty( $_REQUEST['vp_preview_post_id'] ) &&
            isset( $_REQUEST['vp_preview_nonce'] ) &&
            ! empty( $_REQUEST['vp_preview_nonce'] ) &&
            wp_verify_nonce( sanitize_key( $_REQUEST['vp_preview_nonce'] ), 'vp-ajax-nonce' )
        ) {
            $post_id = intval( $_REQUEST['vp_preview_post_id'] );
        }

        $post_id = $post_id ?? get_the_ID() ?? null;

        if (
            $post_id &&
            get_post_meta( $post_id, '_vp_post_type_mapped', true ) &&
            'current_query' === $vp_options['posts_source'] &&
            'post-based' === $vp_options['content_source']
        ) {

            $query_opts = Visual_Portfolio_Get::get_query_params( $vp_options, true );
            // Get active item.
            $active_item     = Visual_Portfolio_Get::get_filter_active_item( $query_opts );
            $portfolio_query = new WP_Query(
                array(
                    'post_type'      => 'portfolio',
                    'posts_per_page' => -1,
                )
            );
            $term_items      = Visual_Portfolio_Get::get_posts_terms( $portfolio_query, $active_item );

            // Add 'All' active item.
            if ( ! empty( $term_items['terms'] ) && $vp_options['filter_text_all'] ) {
                array_unshift(
                    $term_items['terms'],
                    array(
                        'filter'      => '*',
                        'label'       => $vp_options['filter_text_all'],
                        'description' => false,
                        'count'       => false,
                        'id'          => 0,
                        'parent'      => 0,
                        'active'      => ! $term_items['there_is_active'],
                        'url'         => Visual_Portfolio_Get::get_pagenum_link(
                            array(
                                'vp_filter' => '',
                                'vp_page'   => 1,
                            )
                        ),
                        'class'       => 'vp-filter__item' . ( ! $term_items['there_is_active'] ? ' vp-filter__item-active' : '' ),
                    )
                );
            }
            if ( ! empty( $term_items['terms'] ) ) {
                $terms = $term_items['terms'];
            }
        }

        return $terms;
    }

    /**
     * Init permalink settings.
     *
     * @return void
     */
    public function permalink_settings_init() {
        add_settings_section(
            'portfolio-permalink',
            esc_html__( 'Portfolio permalinks', 'visual-portfolio' ),
            array( $this, 'settings' ),
            'permalink'
        );

        add_settings_field(
            'vp_category_slug',
            esc_html__( 'Portfolio category base', 'visual-portfolio' ),
            array( $this, 'slug_input' ),
            'permalink',
            'optional',
            array(
                'id'          => 'vp_category_slug',
                'placeholder' => esc_attr_x( 'portfolio-category', 'slug', 'visual-portfolio' ),
                'value'       => 'category_base',
            )
        );

        add_settings_field(
            'vp_tag_slug',
            esc_html__( 'Portfolio tag base', 'visual-portfolio' ),
            array( $this, 'slug_input' ),
            'permalink',
            'optional',
            array(
                'id'          => 'vp_tag_slug',
                'placeholder' => esc_attr_x( 'portfolio-tag', 'slug', 'visual-portfolio' ),
                'value'       => 'tag_base',
            )
        );
    }

    /**
     * Get permalink settings for things like portfolios and taxonomies.
     *
     * The permalink settings are stored to the option instead of
     * being blank and inheritting from the locale. This speeds up page loading
     * times by negating the need to switch locales on each page load.
     *
     * This is more inline with WP core behavior which does not localize slugs.
     *
     * @param boolean $replace_portfolio_slug - replace portfolio slug automatically. Used in post type registration.
     *
     * @return array
     */
    public static function get_permalink_structure( $replace_portfolio_slug = false ) {
        $saved_permalinks = (array) get_option( 'portfolio_permalinks', array() );
        $permalinks       = wp_parse_args(
            array_filter( $saved_permalinks ),
            array(
                'portfolio_base'         => '%portfolio_page_slug%',
                'category_base'          => _x( 'portfolio-category', 'slug', 'visual-portfolio' ),
                'tag_base'               => _x( 'portfolio-tag', 'slug', 'visual-portfolio' ),
                'attribute_base'         => '',
            )
        );

        if ( $saved_permalinks !== $permalinks ) {
            update_option( 'portfolio_permalinks', $permalinks );
        }

        // Replace portfolio page slug.
        if ( $replace_portfolio_slug && strpos( $permalinks['portfolio_base'], '%portfolio_page_slug%' ) !== false ) {
            $permalinks['portfolio_base'] = str_replace( '%portfolio_page_slug%', self::get_portfolio_slug(), $permalinks['portfolio_base'] );
        }

        $permalinks['portfolio_base'] = ltrim( $permalinks['portfolio_base'], '/\\' );

        $permalinks['portfolio_base'] = untrailingslashit( $permalinks['portfolio_base'] );
        $permalinks['category_base']  = untrailingslashit( $permalinks['category_base'] );
        $permalinks['tag_base']       = untrailingslashit( $permalinks['tag_base'] );

        return $permalinks;
    }

    /**
     * Show a slug input box.
     *
     * @param array $attributes - Setting attributes.
     * @return void
     */
    public function slug_input( $attributes ) {
        $id          = $attributes['id'];
        $placeholder = $attributes['placeholder'];
        $value       = $attributes['value'];
        ?>
        <input
            name="<?php echo esc_attr( $id ); ?>"
            id="<?php echo esc_attr( $id ); ?>"
            value="<?php echo esc_html( isset( $this->permalinks[ $value ] ) ? $this->permalinks[ $value ] : '' ); ?>"
            placeholder="<?php echo esc_attr( $placeholder ); ?>"
            type="text"
            class="regular-text code"
        />
        <?php
    }

    /**
     * Show the settings.
     */
    public function settings() {
        /* translators: %s: Home URL */
        echo wp_kses_post( wpautop( sprintf( __( 'If you like, you may enter custom structures for your portfolio URLs here. For example, using <code>portfolio</code> would make your portfolio links like <code>%sportfolio/sample-portfolio/</code>. This setting affects portfolio URLs only, not things such as portfolio categories. We also recommend you use the <code>%%portfolio_page_slug%%</code> slug, which will automatically use the slug of you Portfolio Archive page.', 'visual-portfolio' ), esc_url( home_url( '/' ) ) ) ) );

        $page_slug    = '%portfolio_page_slug%';
        $default_slug = _x( 'portfolio', 'default-slug', 'visual-portfolio' );
        $current_base = trailingslashit( $this->permalinks['portfolio_base'] );
        $structures   = array(
            0  => '',
            1  => '/' . trailingslashit( $page_slug ),
            2  => '/' . trailingslashit( $page_slug ) . trailingslashit( '%portfolio_category%' ),

            // Only used in the html output in the settings.
            99 => '/' . trailingslashit( $default_slug ),
        );

        ?>
        <table class="form-table vp-permalink-structure">
            <tbody>
                <tr>
                    <th><label><input name="portfolio_permalink" type="radio" value="<?php echo esc_attr( $structures[0] ); ?>" class="vp-permalink-radio" <?php checked( $structures[99], $current_base ); ?> /> <?php esc_html_e( 'Default', 'visual-portfolio' ); ?></label></th>
                    <td><code class="default-example"><?php echo esc_html( home_url() ); ?>/?portfolio=sample-portfolio</code> <code class="non-default-example"><?php echo esc_html( home_url() ); ?>/<?php echo esc_html( $default_slug ); ?>/sample-portfolio/</code></td>
                </tr>
                <tr>
                    <th><label><input name="portfolio_permalink" type="radio" value="<?php echo esc_attr( $structures[1] ); ?>" class="vp-permalink-radio" <?php checked( $structures[1], $current_base ); ?> /> <?php esc_html_e( 'Portfolio base', 'visual-portfolio' ); ?></label></th>
                    <td><code><?php echo esc_html( home_url() ); ?>/<?php echo esc_html( $page_slug ); ?>/sample-portfolio/</code></td>
                </tr>
                <tr>
                    <th><label><input name="portfolio_permalink" type="radio" value="<?php echo esc_attr( $structures[2] ); ?>" class="vp-permalink-radio" <?php checked( $structures[2], $current_base ); ?> /> <?php esc_html_e( 'Portfolio base with category', 'visual-portfolio' ); ?></label></th>
                    <td><code><?php echo esc_html( home_url() ); ?>/<?php echo esc_html( $page_slug ); ?>/portfolio-category/sample-portfolio/</code></td>
                </tr>
                <tr>
                    <th><label><input name="portfolio_permalink" id="portfolio_custom_selection" type="radio" value="custom" class="tog" <?php checked( in_array( $current_base, $structures, true ), false ); ?> />
                        <?php esc_html_e( 'Custom base', 'visual-portfolio' ); ?></label></th>
                    <td>
                        <input name="portfolio_permalink_structure" id="portfolio_permalink_structure" type="text" value="<?php echo esc_attr( $current_base ? $current_base : '' ); ?>" class="regular-text code"> <span class="description"><?php esc_html_e( 'Enter a custom base to use. A base must be set or WordPress will use default instead.', 'visual-portfolio' ); ?></span>
                    </td>
                </tr>
            </tbody>
        </table>
        <?php wp_nonce_field( 'vp-permalinks', 'vp-permalinks-nonce' ); ?>
        <script type="text/javascript">
            jQuery( function( $ ) {
                $( 'input.vp-permalink-radio' ).on( 'change', function() {
                    $( '#portfolio_permalink_structure' ).val( $( this ).val() );
                } );

                $('.permalink-structure input').on( 'change', function() {
                    $( '.vp-permalink-structure' ).find( 'code.non-default-example, code.default-example' ).hide();

                    if ( $( this ).val() ) {
                        $( '.vp-permalink-structure code.non-default-example' ).show();
                        $( '.vp-permalink-structure input' ).removeAttr( 'disabled' );
                    } else {
                        $( '.vp-permalink-structure code.default-example' ).show();
                        $( '.vp-permalink-structure input:eq(0)' ).trigger( 'click' );
                        $( '.vp-permalink-structure input' ).attr( 'disabled', 'disabled' );
                    }
                } );

                $( '.permalink-structure input:checked' ).trigger( 'change' );
                $( '#portfolio_permalink_structure' ).on( 'focus', function() {
                    $( '#portfolio_custom_selection' ).trigger( 'click' );
                } );
            } );
        </script>
        <?php
    }

    /**
     * Save the permalink settings.
     */
    public function permalink_settings_save() {
        if ( ! is_admin() ) {
            return;
        }

        // We need to save the options ourselves; settings api does not trigger save for the permalinks page.
        if (
            isset( $_POST['permalink_structure'], $_POST['vp-permalinks-nonce'], $_POST['vp_category_slug'], $_POST['vp_tag_slug'] ) &&
            wp_verify_nonce( sanitize_key( $_POST['vp-permalinks-nonce'] ), 'vp-permalinks' )
        ) {
            $permalinks                  = (array) get_option( 'portfolio_permalinks', array() );
            $permalinks['category_base'] = sanitize_text_field( wp_unslash( $_POST['vp_category_slug'] ) );
            $permalinks['tag_base']      = sanitize_text_field( wp_unslash( $_POST['vp_tag_slug'] ) );

            // Generate portfolio base.
            $portfolio_base = isset( $_POST['portfolio_permalink'] ) ? sanitize_option( 'permalink_structure', wp_unslash( $_POST['portfolio_permalink'] ) ) : '';

            if ( 'custom' === $portfolio_base ) {
                if ( isset( $_POST['portfolio_permalink_structure'] ) ) {
                    $portfolio_base = preg_replace( '#/+#', '/', '/' . str_replace( '#', '', trim( sanitize_option( 'permalink_structure', wp_unslash( $_POST['portfolio_permalink_structure'] ) ) ) ) );
                } else {
                    $portfolio_base = '/';
                }

                // This is an invalid base structure and breaks pages.
                if ( '/%portfolio_category%/' === trailingslashit( $portfolio_base ) ) {
                    $portfolio_base = '/%portfolio_page_slug%' . $portfolio_base;
                }
            } elseif ( empty( $portfolio_base ) ) {
                $portfolio_base = '/' . _x( 'portfolio', 'default-slug', 'visual-portfolio' );
            }

            $permalinks['portfolio_base'] = $portfolio_base;

            update_option( 'portfolio_permalinks', $permalinks );
        }
    }

    /**
     * Initialize archive rewrite rules.
     *
     * @return void
     */
    public function init_rewrite_rules() {
        $slug = get_post_field( 'post_name', $this->archive_page );
        add_rewrite_tag( '%vp_page_query%', '([^&]+)' );
        add_rewrite_tag( '%vp_page_archive%', '([^&]+)' );
        add_rewrite_tag( '%vp_category%', '([^&]+)' );

        add_rewrite_rule(
            '^' . $slug . '/page/?([0-9]{1,})/?',
            'index.php?post_type=portfolio&vp_page_archive=1&vp_page_query=$matches[1]',
            'top'
        );
        add_rewrite_rule(
            '^' . $this->permalinks['category_base'] . '/([^/]*)/?',
            'index.php?post_type=portfolio&vp_page_archive=1&vp_category=$matches[1]',
            'top'
        );
        add_rewrite_rule(
            '^' . $this->permalinks['tag_base'] . '/([^/]*)/?',
            'index.php?post_type=portfolio&vp_page_archive=1&portfolio_tag=$matches[1]',
            'top'
        );
    }

    /**
     * Replace tags in Portfolio Permalinks.
     *
     * @param  string  $permalink - current permalink.
     * @param  WP_Post $post - current post.
     * @return string
     */
    public function portfolio_permalink_replacements( $permalink, $post ) {
        // Category slug.
        if ( strpos( $permalink, '%portfolio_category%' ) !== false ) {
            $terms = get_the_terms( $post, 'portfolio_category' );
            if ( ! is_wp_error( $terms ) && ! empty( $terms ) && is_object( $terms[0] ) ) {
                $term_slug = array_pop( $terms )->slug;
            } else {
                $term_slug = 'no-portfolio_category';
            }

            $permalink = str_replace( '%portfolio_category%', $term_slug, $permalink );
        }

        return $permalink;
    }

    /**
     * Override an archive page based on passed query arguments.
     *
     * @param WP_Query $query The query to check.
     */
    public function maybe_override_archive( $query ) {
        if ( is_admin() ) {
            return;
        }

        $post_type = 'portfolio';

        // Maybe Redirect.
        if ( is_page() ) {
            $object_id = get_queried_object_id();
            $post_meta = get_post_meta( $object_id, '_vp_post_type_mapped', true );
            if ( ! $post_meta && get_query_var( 'paged' ) ) {
                return;
            }
        }

        if ( is_post_type_archive( $post_type ) && '' !== $this->archive_page && $query->is_main_query() ) {
            $post_id = absint( $this->archive_page );
            $post_id = Visual_Portfolio_3rd_WPML::get_object_id( $post_id );
            $query->set( 'post_type', 'page' );
            $query->set( 'page_id', $post_id );
            $query->set( 'original_archive_type', 'page' );
            $query->set( 'original_archive_id', $post_type );
            $query->set( 'term_tax', '' );
            $query->is_archive           = false;
            $query->is_single            = true;
            $query->is_singular          = true;
            $query->is_page              = true;
            $query->is_post_type_archive = false;

            if (
                isset( $query->query['vp_category'] ) &&
                ! empty( $query->query['vp_category'] ) &&
                ! isset( $query->query['vp_filter'] )
            ) {
                $query->set( 'vp_filter', 'portfolio_category:' . $query->query['vp_category'] );
            }

            if (
                isset( $query->query['vp_category'] ) &&
                ! empty( $query->query['vp_category'] ) &&
                (
                    (
                        // phpcs:ignore WordPress.Security.NonceVerification
                        isset( $_GET['vp_filter'] ) && ! empty( $_GET['vp_filter'] )
                    ) ||
                    (
                        // phpcs:ignore WordPress.Security.NonceVerification
                        isset( $_REQUEST['vpf_ajax_call'] ) && settype( sanitize_text_field( wp_unslash( $_REQUEST['vpf_ajax_call'] ) ), 'bool' )
                    )
                )
            ) {
                $query->set( 'vp_filter', '' );
            }

            if (
                isset( $query->query['vp_page_query'] ) &&
                ! empty( $query->query['vp_page_query'] ) &&
                (
                    // phpcs:ignore WordPress.Security.NonceVerification
                    isset( $_REQUEST['vpf_ajax_call'] ) && settype( sanitize_text_field( wp_unslash( $_REQUEST['vpf_ajax_call'] ) ), 'bool' )
                )
            ) {
                unset( $query->query['vp_page_query'] );
                unset( $query->query_vars['vp_page_query'] );
            }
        }
    }

    /**
     * Substituting query parameters before block output.
     *
     * @param array $args - Query arguments.
     * @param array $options - Block options.
     * @return array
     */
    public function extend_query_args( $args, $options ) {
        if (
            isset( $_REQUEST['vp_preview_post_id'] ) &&
            ! empty( $_REQUEST['vp_preview_post_id'] ) &&
            isset( $_REQUEST['vp_preview_nonce'] ) &&
            ! empty( $_REQUEST['vp_preview_nonce'] ) &&
            wp_verify_nonce( sanitize_key( $_REQUEST['vp_preview_nonce'] ), 'vp-ajax-nonce' )
        ) {
            $post_id = intval( $_REQUEST['vp_preview_post_id'] );
        }

        $post_id = $post_id ?? $args['page_id'] ?? null;

        if ( $post_id && 'current_query' === $options['posts_source'] ) {
            $post_meta = get_post_meta( (int) $post_id, '_vp_post_type_mapped', true );
            if ( ! empty( $post_meta ) && $post_meta ) {

                $args['post_type'] = $post_meta;

                if (
                    isset( $args['vp_page_archive'] ) &&
                    $args['vp_page_archive'] &&
                    isset( $args['vp_page_query'] ) &&
                    ! empty( $args['vp_page_query'] )
                ) {
                    $args['paged'] = $args['vp_page_query'];
                }

                if ( isset( $args['page_id'] ) ) {
                    unset( $args['page_id'] );
                }

                unset( $args['p'] );
                $args['posts_per_page'] = $this->posts_per_page;
            }
        }
        return $args;
    }

    /**
     * If not set default paged style - Delete pagination on archive pagination pages: /pages/<page_number>.
     *
     * @param array $options - Block Options.
     * @return array
     */
    public function unset_pagination_archive_page( $options ) {
        global $wp_query;
        if ( $wp_query && isset( $wp_query->query_vars ) && is_array( $wp_query->query_vars ) && 'current_query' === $options['posts_source'] ) {
            $is_page_archive = $wp_query->query_vars['vp_page_archive'] ?? false;
            if ( $is_page_archive ) {
                foreach ( $options['layout_elements'] as $position => $container ) {
                    if ( ! empty( $container['elements'] ) ) {
                        $key = array_search( 'pagination', $container['elements'], true );
                        if ( false !== $key && isset( $options['pagination'] ) ) {
                            if ( 'paged' === $options['pagination'] || is_tax() ) {
                                // phpcs:ignore WordPress.Security.NonceVerification
                                $vp_page = isset( $_REQUEST['vp_page'] ) && ! empty( $_REQUEST['vp_page'] ) ? sanitize_option( 'posts_per_page', wp_unslash( $_REQUEST['vp_page'] ) ) : null;

                                $options['start_page'] = $wp_query->query_vars['vp_page_query'] ?? $vp_page ?? 1;
                            } else {
                                unset( $options['layout_elements'][ $position ]['elements'][ $key ] );
                            }
                        }
                    }
                }
            }
        }
        return $options;
    }

    /**
     * Update Post meta mapped after save general archive page option.
     *
     * @param int $post_id - Post ID.
     * @return int
     */
    public static function save_archive_page_option( $post_id ) {

        if ( is_numeric( $post_id ) ) {

            self::delete_post_type_mapped_meta();

            visual_portfolio()->defer_flush_rewrite_rules();

            update_post_meta( (int) $post_id, '_vp_post_type_mapped', 'portfolio' );
        }

        return $post_id;
    }

    /**
     * Delete pages list transient if page title updated.
     * Rewrite flush rules if archive slug changed.
     *
     * @param int   $post_ID - Post ID.
     * @param array $data - Save Post data.
     * @return void
     */
    public function pre_page_update( $post_ID, $data ) {
        if (
            'page' === $data['post_type'] &&
            (int) $this->archive_page === (int) $post_ID &&
            get_post_field( 'post_name', $post_ID ) !== $data['post_name']
        ) {
            visual_portfolio()->defer_flush_rewrite_rules();
        }
    }

    /**
     * Delete pages list transient if page status set as trashed.
     * Also delete archive page and set old slug for default archive permalinks.
     *
     * @param int $post_ID - Post ID.
     * @return void
     */
    public function delete_archive_page( $post_ID ) {
        if ( ! empty( $this->archive_page ) && (int) $post_ID === (int) $this->archive_page ) {
            Settings::update_option( 'portfolio_archive_page', 'vp_general', '' );

            self::delete_post_type_mapped_meta();
        }
    }

    /**
     * Rewrite Flush Rules after update portfolio page option.
     *
     * @param  array  $old_value - Old value before update.
     * @param  array  $value - New value after update.
     * @param  string $option - Name of option.
     * @return void
     */
    public function flush_rewrite_rules_after_update( $old_value, $value, $option ) {
        if (
            isset( $old_value['portfolio_archive_page'] ) &&
            isset( $value['portfolio_archive_page'] ) &&
            'vp_general' === $option
        ) {
            if (
                ! empty( $value['portfolio_archive_page'] ) &&
                $old_value['portfolio_archive_page'] === $value['portfolio_archive_page']
            ) {
                visual_portfolio()->defer_flush_rewrite_rules();
            }

            if (
                empty( $value['portfolio_archive_page'] ) &&
                $old_value['portfolio_archive_page'] !== $value['portfolio_archive_page'] &&
                is_numeric( $old_value['portfolio_archive_page'] )
            ) {
                self::delete_post_type_mapped_meta();

                visual_portfolio()->defer_flush_rewrite_rules();
            }
        }
    }

    /**
     * Remove mapped post meta from all pages.
     *
     * @return void
     */
    private static function delete_post_type_mapped_meta() {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM %s
                WHERE meta_key = '_vp_post_type_mapped'",
                $wpdb->postmeta
            )
        );
    }

    /**
     * Create default archive page.
     *
     * @param string $custom_slug - Default Archive Slug.
     * @return void
     */
    public static function create_archive_page( $custom_slug = 'portfolio' ) {
        if ( ! get_option( '_vp_add_archive_page' ) && ! get_option( '_vp_trying_to_add_archive_page' ) ) {

            add_option( '_vp_trying_to_add_archive_page', true );

            $args = array(
                'post_title'    => esc_html__( 'Portfolio', 'visual-portfolio' ),
                'post_status'   => 'publish',
                'post_type'     => 'page',
                'post_name'     => $custom_slug,
            );

            // Insert the post into the database.
            $post_id = wp_insert_post( $args );

            if ( ! is_wp_error( $post_id ) ) {

                Settings::update_option( 'portfolio_archive_page', 'vp_general', $post_id );

                visual_portfolio()->defer_flush_rewrite_rules();

                self::save_archive_page_option( $post_id );

                $post = get_post( $post_id );

                $slug = $post->post_name;

                wp_update_post(
                    wp_slash(
                        array(
                            'ID'           => $post_id,
                            'post_content' => '<!-- wp:visual-portfolio/block {"block_id":"' . hash( 'crc32b', $slug . $post_id ) . '","content_source":"post-based","posts_source":"current_query","items_gap":30,"layout_elements":{"top":{"elements":["filter"],"align":"center"},"items":{"elements":["items"]},"bottom":{"elements":["pagination"],"align":"center"}},"pagination":"paged"} /-->',
                        )
                    )
                );

                add_option( '_vp_add_archive_page', $post_id );
            }
        }
    }

    /**
     * Add a post display state for special Portfolio Archive page in the pages list table.
     *
     * @param array   $post_states - An array of post display states.
     * @param WP_Post $post        - The current post object.
     * @return array $post_states  - An array of post display states.
     */
    public function add_display_post_states( $post_states, $post ) {
        if ( 'page' === $post->post_type ) {
            // If successful, returns the post type slug.
            $post_type = get_post_meta( $post->ID, '_vp_post_type_mapped', true );
            if ( $post_type && ! empty( $post_type ) ) {
                $post_states[] = esc_html__( 'Portfolio Page', 'visual-portfolio' );
            }
        }
        return $post_states;
    }

    /**
     * Get Portfolio Archive Slug.
     *
     * @return string
     */
    public static function get_portfolio_slug() {
        // When deleting the archive page, we leave the old slug without overwriting the permalinks.
        // In this case, instead of the archives page, a standard archives page with the corresponding template is substituted.
        $custom_slug = _x( 'portfolio', 'default-slug', 'visual-portfolio' );

        $archive_page = Settings::get_option( 'portfolio_archive_page', 'vp_general' );

        if ( isset( $archive_page ) && ! empty( $archive_page ) ) {
            // If there is a selected page of archives, we substitute its slug.
            $custom_slug = get_post_field( 'post_name', $archive_page );
        }

        return $custom_slug;
    }
}
new Visual_Portfolio_Archive_Mapping();