How to add a virtual page in WordPress?

There are many use cases for a virtual page. Let’s say showing the invoice or payment status page for a payment plugin in the front-end, without compromising active theme. I needed one recently to show payment statuses. Here is how I implemented it.

1. Hook “init” to catch the specific slug

First we need to check current page slug to see if that is our target page inside the init hook.

class My_Virtual_Page {
    public function __construct() {
        add_action( 'init', array( $this, 'init' ) );
    }

    /**
     * Hook to add the virtual page
     */
    public function init() {
       if ( get_option( 'permalink_structure' ) ) {
            $param = trim( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '/' );
        } else {
            parse_str( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ), $params );
            $param = ( isset( $params['page_id'] ) ? $params['page_id'] : false );
        }

        if( $param == 'my-payment-page' ) {
            //Enqueue any page specific styles and scripts here
            add_filter( 'the_posts', array( $this, 'my_virtual_payment_page' ) );
        }
    }
}

Let’s say our virtual page is my-payment-page. So, the expected URL is either mydomain.com/my-payment-page or mydomain.com/?page_id=my-payment-page.

If we find our target parameter inside the init hook, we en-queue any required styles and scripts and add a filter for the content.

Note: The “slug” have to be unique to avoid conflict with any existing page.

2. Prepare the page object

Inside the filter, we prepare the content of the virtual page. Its pretty straight forward. We create a basic post object and fill the object with the custom values we want to display.

class My_Virtual_Page {
    /*........*/
    /**
     * @param $posts
     * The virtual page filter
     *
     * @return array
     */
    function my_virtual_payment_page( $posts ) {
        global $wp, $wp_query;

        //Just double checking. Can be ignored.
        if ( strcasecmp( $wp->request, 'my-payment-page' ) !== 0 ) {
            return $posts;
        }

        $content = '<p>HTML Page content here!</p>';

        $post = $this->my_virtual_post_object( 'my-payment-page', __( 'Virtual payment page', 'domain' ), $content );
    }

    /**
     * @param $slug
     * @param $title
     * @param $content
     *
     * Generate the post object dynamically
     *
     * @return stdClass
     */
    function swp_post_object( $slug, $title, $content ) {
        $post                        = new stdClass;
        $post->ID                    = -1;
        $post->post_author           = 1;
        $post->post_date             = current_time( 'mysql' );
        $post->post_date_gmt         = current_time( 'mysql', 1 );
        $post->post_content          = $content;
        $post->post_title            = $title;
        $post->post_excerpt          = '';
        $post->post_status           = 'publish';
        $post->comment_status        = 'closed';
        $post->ping_status           = 'closed';
        $post->post_password         = '';
        $post->post_name             = $slug;
        $post->to_ping               = '';
        $post->pinged                = '';
        $post->modified              = $post->post_date;
        $post->modified_gmt          = $post->post_date_gmt;
        $post->post_content_filtered = '';
        $post->post_parent           = 0;
        $post->guid                  = get_home_url( 1, '/' . $slug );
        $post->menu_order            = 0;
        $post->post_tyle             = 'page';
        $post->post_mime_type        = '';
        $post->comment_count         = 0;

        return $post;
    }
}

3. Display the page

Finally we display the post by returning the post object. Before doing that, we need to set the flags of $wp_query to simulate that a post is found.

class My_Virtual_Page {
    /*........*/
    function my_virtual_payment_page( $posts ) {
        /*.......*/
        $post = $this->my_virtual_post_object( 'my-payment-page', __( 'Virtual payment page', 'domain' ), $content );

        // set filter results
        $posts = array( $post );

        // reset wp_query properties to simulate a found page
        $wp_query->is_page     = true;
        $wp_query->is_singular = true;
        $wp_query->is_home     = false;
        $wp_query->is_archive  = false;
        $wp_query->is_category = false;
        unset( $wp_query->query['error'] );
        $wp_query->query_vars['error'] = '';
        $wp_query->is_404              = false;

        return ( $posts );
    }
    /*........*/
}

Putting it all together

class My_Virtual_Page {
    public function __construct() {
        add_action( 'init', array( $this, 'init' ) );
    }

    /**
     * Hook to add the virtual page
     */
    public function init() {
       if ( get_option( 'permalink_structure' ) ) {
            $param = trim( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '/' );
        } else {
            parse_str( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ), $params );
            $param = ( isset( $params['page_id'] ) ? $params['page_id'] : false );
        }

        if( $param == 'my-payment-page' ) {
            //Enqueue any page specific styles and scripts here
            add_filter( 'the_posts', array( $this, 'my_virtual_payment_page' ) );
        }
    }

    /**
     * @param $posts
     * The virtual page filter
     *
     * @return array
     */
    function my_virtual_payment_page( $posts ) {
        global $wp, $wp_query;

        //Just double checking. Can be ignored.
        if ( strcasecmp( $wp->request, 'my-payment-page' ) !== 0 ) {
            return $posts;
        }

        $content = '<p>HTML Page content here!</p>';

        $post = $this->my_virtual_post_object( 'my-payment-page', __( 'Virtual payment page', 'domain' ), $content );

        // set filter results
        $posts = array( $post );

        // reset wp_query properties to simulate a found page
        $wp_query->is_page     = true;
        $wp_query->is_singular = true;
        $wp_query->is_home     = false;
        $wp_query->is_archive  = false;
        $wp_query->is_category = false;
        unset( $wp_query->query['error'] );
        $wp_query->query_vars['error'] = '';
        $wp_query->is_404              = false;

        return ( $posts );
    }

    /**
     * @param $slug
     * @param $title
     * @param $content
     *
     * Generate the post object dynamically
     *
     * @return stdClass
     */
    function swp_post_object( $slug, $title, $content ) {
        $post                        = new stdClass;
        $post->ID                    = -1;
        $post->post_author           = 1;
        $post->post_date             = current_time( 'mysql' );
        $post->post_date_gmt         = current_time( 'mysql', 1 );
        $post->post_content          = $content;
        $post->post_title            = $title;
        $post->post_excerpt          = '';
        $post->post_status           = 'publish';
        $post->comment_status        = 'closed';
        $post->ping_status           = 'closed';
        $post->post_password         = '';
        $post->post_name             = $slug;
        $post->to_ping               = '';
        $post->pinged                = '';
        $post->modified              = $post->post_date;
        $post->modified_gmt          = $post->post_date_gmt;
        $post->post_content_filtered = '';
        $post->post_parent           = 0;
        $post->guid                  = get_home_url( 1, '/' . $slug );
        $post->menu_order            = 0;
        $post->post_tyle             = 'page';
        $post->post_mime_type        = '';
        $post->comment_count         = 0;

        return $post;
    }
}

That’s it. Enjoy!

Menu

CSS Dropdown Menu

Let’s create a dropdown menu with CSS and HTML only. There are lots of fancy menus out there. This is just an experimental HTML-CSS based multi-level menu with room for improvements. Someone starting to learn CSS might find this helpful.

To quickly explain how it’s done, menu is contained within ul and each menu item is contained within individual li elements. Menu item with children has a class .children and that adds some extra styling for the child menu segment. The child segment is hidden by default and is only displayed on mouse over (:hover). There is a small arrow before the child segment. Style for that can be found in the :before segment for child ul. This arrow also ensures continuous mouse over presence on the parent li. If the arrow is removed, the space between the parent li and child ul will break the continuity of mouse over effect. That can be fixed using padding to the children.

The nav element wrapping the whole thing is just to create a container and set a width for demo purpose.

Here is the demo code on JSFiddle:

Advanced object selection using Mootools

Mootools’ “Selectors” utility has some really strong feature. It really saves a lot of time and sweat. Using this property, one can select a tag (any one can!), tag with specific name, id or any other attribute (huh!) and most amazingly part of a property value matching to some string…

Say you want to select all the div tags which has ID starting with ‘my’. How can you do that? Continue reading

Basic HTML – Part 2

This is the continuation of my previous post Basic HTML – Part 1. On this phase of the post, I’m going to explain the table and form tags.

Table:

Table is a very important tag and widely used for alignment management of web pages. It is defined by the <table> tag. Some hierarchy are needed to be maintained for tables. First the table, then row, then column and finally content of the column. Continue reading