Skip to content

An utility plugin for asynchronous injection and cache management of dynamic content generated by WordPress on front-end, helping code savyy users to improve page load times, overall performance and user experience even for membership and ecommerce websites.

License

Notifications You must be signed in to change notification settings

celsobessa/oca-asynchronous-content-organizer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OCA - Asynchronous Content Organizer

An (Work in Progress) utility for asynchronous injection and cache management of dynamic content generated by WordPress on front-end, helping code savyy users to improve page load times, overall performance and user experience even for membership and ecommerce websites.

Important: this plugin is in active development and is not production ready. You may find some instability and bugs here and there. We are not responsible for any damage to you webiste. Use at your own risk.

The purpose of this plugin is to help code savyy site owners to improve page load times, overall performance and user experience in all kind of websites, including membership, ecommerce and websites with dynamic content. It allows the lazy load of diferent pieces of content in the same page by acting as a proxy for asynchronous injection of content generated on front-end pages, giving better content injection and cache management.

OCA allows you:

  • To create a queue of ajax requests to WordPress backend and OCA will add them to the page automatically
  • Show different dynamic generated content to logged-in and logged-out users
  • Server HTML fragment cache : improve page generations time and server usage by caching code fragments. You can even cache different content for logged/non-logged users. If your server has a backend cache (e.g. Redis or Memcached), fragments can be used across different pages, speeding your website even more.
  • Content cache in the browser: for browsers supporting Storage API, dynamic content can be cached in the localStorage, improving content generations speed and avoiding unnecessary server requests. Again, you can have different cache rules for logged and non-logged users.

A good use case for using OCA - Asynchronous Content Organizer is to show a fast and more "basic" page to all users and then add extra content or whistles and bells only when the user device and connections allows for a more "heavy" experience.

Another use case is to improve page caching in membership websites by creating an standard page for all users and then addin g and customizing the content according to user privileges.

With OCA - Asynchronous Content Organizer you create a queue of ajax requests to WordPress backend and OCA will add them to the page automatically. For each item in the queue you must provide at least a function to output content. You may use either a native WordPress function (like wp_tag_cloud) or a custom function. You may also use different functions to logged-in and logged-out users, allowing different content depending on user status.

** THIS is a work in progress in it's early stages, don't use in production.** Also you will probably find a lot of errors and discrepancies. Be gentle when reporting them, please.

Instalation

  1. Download OCA - Asynchronous Content Organizer latest release zip from Github repository
  2. Upload oca-asynchronous-content-organizer folder to the /wp-content/plugins/ directory
  3. Activate the plugin through the 'Plugins' menu in WordPress

Using OCA - Asynchronous Content Organizer ==

Using OCA - Asynchronous Content Organizer consists basically of registering AJAX requests ad OCA Jobs in a function hooked to wp_enqueue_scripts action hook within your theme (most of time, it will be in functions.php) or plugin (we strongly recommend using a Site Specific Plugin for all you custom code).

Your function must call $oca_manager global and push a job using $oca_manager->add_job method with a array of argument. This array must contain at least function_name and OCA will append the content to any HTML element with ID equals to main. If there's no main element and container parameter is empty, nothing will be injected.

Below you will find all OCA job parameters and their default values. After that, you can find some usage examples.

OCA job parameters

OCA's add_job method accepts the following parameters with the default values:

$defaults = array(
    'function_name'				=> '',
    'function_args'				=> array(''),
    'function_output'			=> 'return',
    'nopriv_function_name'		=> '',
    'nopriv_function_args'		=> array(''),
    'nopriv_function_output'	=> '',
    'backend_cache'				=> true,
    'frontend_cache_priv'		=> false,
    'frontend_cache_nopriv'		=> false,
    'container'					=> '#main',
    'triger'					=> 'window.load',
    'timeout'					=> 20000,
    'placement'					=> 'append',
    'loaderEnable'				=> false,
    'loaderMessage'				=> 'loading content...',
    'loaderMessageWhile'		=> 'loading content...<br><small>(it may take a while)</small>',
    'callback'					=> false,
);
```php

The inline documentation within `class-oca-asynchronous-content-organizer-queue-manager.php` file explains all parameters in detail:

```php
	/**
	 * Adds a job to OCA oca_queue.
	 *
	 * It adds a job to OCA queue. First, checkes if job is already on queue, then validate arguments and if they are valid, adds
	 * the job to queue and to hashes array. Returns 'job added to queue' on success or erros like 'job already on queue',
	 * 'job arguments invalid' on such cases.
	 *
	 * @since	0.2.0
	 * @since	0.2.4	added loaderEnable and loaderMessage to argument defaults array
	 * @since	0.2.5	timeout default changed to 60000
	 * @since	0.2.6	added callback argument/defaults array
	 * @since	0.4.0	timeout default changed to 20000
	 * @access public
	 * @param array $args {
	 *     arguments for the OCA job
	 *
	 *     @type string     $function_name				Name of the function to be called by privileged users.
	 * 													If nopriv_function_name is empty, the same function from
	 * 													$function_name will be used for non-privileged users.
	 * 													If function name is bypass, it will not trigger the request if the user is
	 * 													privileged or if the user non-privileged and there's no specificied
	 * 													nopriv_function_name. Default value is (empty).
	 *     @type array      $function_args				An array of arguments for the function specified by $function_name.
	 * 													If your args are already an array, you must nest inside this array.
	 *                                          		Default is value of array('') (an empty array).
	 *     @type array      $function_output			The type of behavior the function specificed by $function_name has: does it echoes or does it return data?
	 *                                          		Default is value of 'echo'.
	 *     @type string     $nopriv_function_name		Name of the function to be called by non-privileged users. If
	 * 													nopriv_function_name is left empty, the same function from
	 * 													$function_name will be used for non-privileged users.
	 * 													If $nopriv_function name is 'bypass', it will not trigger the request
	 * 													if the user is non-privileged. Default value is '' (empty).
	 *     @type array      $nopriv_function_args		An array of arguments for the function specified by $nopriv_function_name.
	 * 													If your args are already an array, you must nest it inside this array.
	 *                                          		Default is value of array('') (an empty array).
	 *     @type array      nopriv_$function_output		The type of behavior the function specified by $nopriv_function_name has: does it echoes or does it return data? If nopriv_function_output is left empty, the same function from $function_name will be used for non-privileged users.
	 *                                          		Default is value of ''.
	 *     @type bool      backend_cache				should OCA cache the response (true) on backend?
	 *                                          		Default value is true.
	 *     @type mixed     frontend_cache_priv			should OCA cache the response in front end for privileged users and
	 * 													following which cache purging policy? Fires before content is
	 * 													injected. If is set to 'purgeonchange', content cache is purged when
	 *													privileges (status) change. Valid values are:
	 * 													- false: no cache on frontend
	 * 													- true: cache on frontend
	 * 													- 'purgeonchcange': cache in frontend. Purge cache on status change
	 * 													Any other values are invalid and equals to false. Default is false.
	 *     @type mixed     frontend_cache_nopriv		should OCA cache the response in front end for non-privileged users
	 * 													and following which cache purging policy? Fires before content is
	 * 													injected. If is set to 'purgeonchange', content cache is purged when
	 *													privileges (status) change. Valid values are:
	 * 													- false: no cache on frontend
	 * 													- true: cache on frontend
	 * 													- 'purgeonchcange': cache in frontend. Purge cache on status change
	 * 													Any other values are invalid and equals to false. Default is false.
	 *     @type string    container					An jQuery/CSS3 selector of the element to inject content
	 *                                          		Default is value '#main' (WordPress default theme main content area)
	 *     @type string    trigger						A event for triggering the loading processes. For now, it accepts only window.load. Future versions will allow other triggers ad document.load, click, etc.
	 *                                          		Default is value 'window.load'
	 *     @type integer   timeout						A number for jQuery timeout, in miliseconds.
	 *                                          		Default is value 20000 (20000 miliseconds or 20 seconds)
	 *     @type string    placement					Where the content should be injected: appended, prepended or to replace contente on element specificied by $container
	 *                                          		Default is value 'apped'
	 *     @type bool      loaderEnable					Should OCA show a loading message?
	 *                                          		Default is value false
	 *     @type string    loaderMessage				The placeholder message while content is being fetched and loaded. It works onlye if #loaderEnable is true
	 *     @type string    loaderMessageWhile			The placeholder message while content is being fetched and loaded for qeues with more than 3 jobs
	 *                                          		Default is value 'loading content...<br><small>(it may take a while)</small>'
	 * @return string 'job added to queue', 'job already on queue' or 'job arguments invalid'
	*/

You can find further information on frontend_cache_priv and frontend_cache_nopriv parameters in the FAQ below

CSS Classes for item injection status

OCA adds and/or remove css classes to container elements in different situations or phases of job processing. This way, you can control the element styling and visibility using CSS depending upon the phase or status of job.

  • oca-waiting: added when item processing starts;
  • oca-error: added when the ajax call returns an error;
  • oca-loaded: used when the ajax call is successful;
  • oca-bypassed: when the item is bypassed;
  • oca-unknown-status: used in certain errors in OCA javascript

You could, for example, hide an element for all users and show it only if the job is done succesfully.

Filters

OCA comes with 3 filters: 'Oca/Content_Fetcher/User_Data', 'Oca/Content_Fetcher/Nopriv_Fetcher_Response', and 'Oca/Content_Fetcher/Fetcher_Response'.

'Oca/Content_Fetcher/User_Data' filter

This filter is used to add arbitrary data to the data property of the is_user_logged response. It passes an empty string and is triggered after OCA checks the user status.

$user_info = array(
    'userStatus'	=> $user_status,
    'userData'		=> $user_data
);

You could use, for example, to send user membership information.

'Oca/Content_Fetcher/Nopriv_Fetcher_Response' filter

This filter is triggered right before the response is sanitized and sent for privileged users, it passes 3 arguments: $response, $function_name and $this->function_args.

'Oca/Content_Fetcher/Fetcher_Response' filter

This filter is triggered right before the response is sanitized and sent for privileged users, it passes 3 arguments: $response, $function_name and $this->function_args.

Examples for how to use OCA - Asynchronous Content Organizer

The following examples show different ways of using OCA, from the most simples to the mos complex cases. Baby steps, yeah.

Example 1: Adding a Tag Cloud to the theme footer

This example shows how to add a cloud tag to main content area of the Twenty-Seventeen Theme.

/**
    * registers custom functions with OCA - Asynchronous Content Organizer queue Manager
    * to be lazy loaded using ajax
    *
    * @access public
    * @return void
    */
function oca_demo_1_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        // job 1 arguments
        $oca_job_1_args = array(
            'function_name'			=> 'wp_tag_cloud', // function name
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_1_register_scripts', 9);

Notice that we are using wp_tag_cloud, which returns a string, instead of wp_tag_cloud which outputs the content. We could use wp_tag_cloud as well, but when using functions which outputs content, we need to set the function_output parameter to 'echo', as shown in example 5.

Example 2: Adding a Tag Cloud to the theme footer

This example shows how to add a cloud tag to footer of the Twenty-Seventeen Theme, by passing the container parameter.

function oca_demo_2_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        // job 1 arguments
        $oca_job_1_args = array(
            'function_name'			=> 'wp_tag_cloud', // function name
            'container' 			=> '#colophon', // where OCA will inject the content
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_2_register_scripts', 9);

Example 3: Customizing the Tag Cloud in the theme footer

This example builds upon the previous one and shows how to pass aditional paramenter to the function responsible to generate a cloud tag to footer of the Twenty-Seventeen Theme. We will change the smallest and largest sizes and change order to DESCendent.

function oca_demo_3_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        $cloud_args = array(
            'smallest'  => 12,
            'largest'   => 36,
            'order'     => 'DESC',
        );

        // job 1 arguments
        $oca_job_1_args = array(
            'function_name'			=> 'wp_tag_cloud', // function name
            'function_args' 		=> array( $cloud_args) , // arguments to be passed to wp_tag_cloud function
            'container' 			=> '#colophon', // where OCA will inject the content
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_3_register_scripts', 9);

Example 4: Different content for logged and non-logged users

This example shows how to inject different content based on it's privileges.

Depending on your page cache configuration, if you use WP/PHP conditionals for showing different content, one of more of the following may happen:

A. Page cache will be purged often, wasting server resources and not improving user experience as it could. B. Page cache will present non-logged content for logged user and vice-versa C. logged users won't have any cache at all.

Using OCA, we leverage page cache because we just need the page cache to serve the same content for all users, and the content for logged users will be fetched using AJAX.

Let's say we want to show the only 10 tags for non-logged users, using smaller sizes, while for logged in users we will show up to 20 tags, using larger font sizes and showing the post count for each tag.

In this case, we need to use nopriv_function_name and nopriv_function_args parameters to get different content between privileged (logged-in) users and non-privileged (non-logged) users.

function oca_demo_4_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        $cloud_args = array(
            'smallest'   => 14,
            'largest'    => 48,
            'order'      => 'DESC',
            'show_count' => true,
            'number'     => 20

        );

        $nopriv_cloud_args = array(
            'smallest'  => 12,
            'largest'   => 36,
            'order'     => 'DESC',
            'number'    => 10
        );

        // job 1 arguments
        $oca_job_1_args = array(
            'function_name'			=> 'wp_tag_cloud', // function name
            'function_args' 		=> array( $cloud_args ), // arguments to be passed to wp_tag_cloud function
            'nopriv_function_name'	=> 'wp_tag_cloud', // function name
            'nopriv_function_args' 	=> array( $nopriv_cloud_args ), // arguments to be passed to wp_tag_cloud function
            'container' 			=> '#colophon', // where OCA will inject the content
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_4_register_scripts', 9);

Example 5: Using functions which output content

As stated in example 1, OCA expects functions which return content by default, but functions which already prints (echoes) content can be used as well. In this case, we need to use function_output and/or nopriv_function_output to tell OCA what to expect.

In this example we want to show non-privileged users a list of post categories while using a cloud tag for privilege users. In this cases, we use 'wp_list_categories' as nopriv_function_name parameter and 'echo' as nopriv_function_output parameter, while keeping the previous example function_name and function_args from the previous example without changing the output (which, by default, is 'return').

function oca_demo_5_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        $cloud_args = array(
            'smallest'   => 14,
            'largest'    => 48,
            'order'      => 'DESC',
            'show_count' => true,
            'number'     => 20

        );

        // job 1 arguments
        $oca_job_1_args = array(
            'function_name'			 => 'wp_tag_cloud', // function name
            'function_args' 		 => array( $cloud_args ), // arguments to be passed to wp_tag_cloud function
            'nopriv_function_name'	 => 'wp_list_categories', // function name
            'nopriv_function_output' => 'echo',
            'container' 			 => '#colophon', // where OCA will inject the content
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_5_register_scripts', 9);

In cases like this, OCA will use PHP's output buffer to control the output.

Example 6: Using Custom Functions

Let's imagine you have a membership website and all your posts shows only the excerpt to non-logged users. Now you want to add a teaser message after your content offering 50% discount code as incentive for readers to be become members.

In this case, you need to create a function to output the teaser content and then tell OCA to use your function.

A simple teaser function could be something like this:

function oca_demo_6_teaser(){
    $html = '<div class="teaser">';
    $html .= '<h3>Become a member for 50% less!</h3>';
    $html .= '<p>This month, you can become a subscriber and get all our content for only half the price</p>';
    $html .= '<p>Use the code <em>OCAMADNESS</em> in the checkout</p>';
    $html .= '</div>';
    return $html;
}

Now, your OCA job would be like this:

function oca_demo_6_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        // job 1 arguments
        $oca_job_1_args = array(
            'function_name' => 'oca_demo_6_teaser', // function name
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_6_register_scripts', 9);

Example 7: Using Custom Functions and Changing placement behavior

Build upon the previous example, your membership website shows only the excerpt to non-logged users as well, but now you want to add a teaser message before your content offering 50% discount code as incentive.

Again, you need to create a function to output the teaser content and then tell OCA to use your function, but now you must set the placement parameter to 'prepend'.

The teaser function is the same form the previous example:

function oca_demo_7_teaser(){
    $html = '<div class="teaser">';
    $html .= '<h3>Become a member for 50% less!</h3>';
    $html .= '<p>This month, you can become a subscriber and get all our content for only half the price</p>';
    $html .= '<p>Use the code <em>OCAMADNESS</em> in the checkout</p>';
    $html .= '</div>';
    return $html;
}

But in this case, your OCA job must use the placement parameter:

function oca_demo_7_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        // job 1 arguments
        $oca_job_1_args = array(
            'function_name' => 'oca_demo_7_teaser', // function name
            'placement'     => 'append', // should we 'append' (default), 'prepend' or 'replace' the content in the container?
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_7_register_scripts', 9);

Example 8: Different content and behavior for logged-in and non-logged users

In example 7 we showed a we injected a teaser message offering 50% discount code before your content for non-logged users. This time, we will also show a thank you message for logged-in users instead of the teaser.

For non-logged users he teaser function is the same form the previous example:

function oca_demo_8_teaser(){
    $html = '<div class="teaser">';
    $html .= '<h3>Become a member for 50% less!</h3>';
    $html .= '<p>This month, you can become a subscriber and get all our content for only half the price</p>';
    $html .= '<p>Use the code <em>OCAMADNESS</em> in the checkout</p>';
    $html .= '</div>';
    return $html;
}

For logged users, we will show another simple message:

function oca_demo_8_thank_you(){
    $html = '<div class="teaser thank-you">';
    $html .= '<h3>Thanks for being a supporter</h3>';
    $html .= '<p>We really appreciate being a member and becoming our supporter.</p>';
    $html .= '<p>Don\'t forget to redeem your <em>PERKs</em> in your profile page</p>';
    $html .= '</div>';
    return $html;
}

But in this case, your OCA job must use the placement parameter:

function oca_demo_8_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        // job 1 arguments
        $oca_job_1_args = array(
            'function_name' => 'oca_demo_8_teaser', // function name
            'nopriv_function_name' => 'oca_demo_8_thank_you', // function name
            'placement'     => 'append', // should we 'append' (default), 'prepend' or 'replace' the content in the container?
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_8_register_scripts', 9);

Example 9: Basic front-end caching for improved performance

For improving user experience and speed perception for users using browser supporting localStorage API, you can set frontend_cache_priv and frontend_cache_nopriv rules. In this exeample, we will use a simple cache rule for the generated content.

The teaser and thank you functions are the same from the previous example, but notice frontend_cache_priv and frontend_cache_nopriv parameter are both set to true in the OCA job arguments. This will make OCA keep bot the responses in localStorage and retrieve each one according to users status.

As the content will be in the local cache, it will appear way faster in the page, while avoid new requests when the user reenter a page previously visited. In fact, if you use the same functions with the same exactly parameters in different pages, all pages benefit from cached content and load faster in most cases.

Although it's a powerful tool, don't rely on localStorage, specially for users in private mode, because each browser have different rules and behaviors for it. As a rule of thumb, think that users in private mode will not have a front-end cache when using private mode. Also, keep in mind that each browser will have different rules for maximum localStorage size in bytes, gargabe collections, etc, so a content may be deleted before the expiration time you set.

Finally, a word about security: never store any kind of credentials or password in localStorage.

// For non-logged users
function oca_demo_9_teaser(){
    $html = '<div class="teaser">';
    $html .= '<h3>Become a member for 50% less!</h3>';
    $html .= '<p>This month, you can become a subscriber and get all our content for only half the price</p>';
    $html .= '<p>Use the code <em>OCAMADNESS</em> in the checkout</p>';
    $html .= '</div>';
    return $html;
}

//For logged users
function oca_demo_9_thank_you(){
    $html = '<div class="teaser thank-you">';
    $html .= '<h3>Thanks for being a supporter</h3>';
    $html .= '<p>We really appreciate being a member and becoming our supporter.</p>';
    $html .= '<p>Don\'t forget to redeem your <em>PERKs</em> in your profile page</p>';
    $html .= '</div>';
    return $html;
}

// register OCA jobs
function oca_demo_9_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        // job 1 arguments. notice the frontend_cache_priv and frontend_cache_nopriv paramenters
        // check all possible values in the inline documentation in `class-oca-asynchronous-content-organizer-queue-manager.php` :
        $oca_job_1_args = array(
            'function_name'         => 'oca_demo_9_teaser', // function name
            'nopriv_function_name'  => 'oca_demo_9_thank_you', // function name
            'placement'             => 'append', // should we 'append' (default), 'prepend' or 'replace' the content in the container?
            'frontend_cache_priv'   => true,
            'frontend_cache_nopriv' => true // we should cache response for both logged and non-logged users
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_9_register_scripts', 9);

Example 10: Different frontend_cache rules for logged-in users

Building upon the previous example, we can have different cache rules for logged and non-logged users. In this example we will cache the content for non-logged users but not for logged-in users. To achieve this, we use frontend_cache_nopriv as true and frontend_cache_priv as false (or just don't set it!)

// For non-logged users
function oca_demo_10_teaser(){
    $html = '<div class="teaser">';
    $html .= '<h3>Become a member for 50% less!</h3>';
    $html .= '<p>This month, you can become a subscriber and get all our content for only half the price</p>';
    $html .= '<p>Use the code <em>OCAMADNESS</em> in the checkout</p>';
    $html .= '</div>';
    return $html;
}

//For logged users
function oca_demo_10_thank_you(){
    $html = '<div class="teaser thank-you">';
    $html .= '<h3>Thanks for being a supporter</h3>';
    $html .= '<p>We really appreciate being a member and becoming our supporter.</p>';
    $html .= '<p>Don\'t forget to redeem your <em>PERKs</em> in your profile page</p>';
    $html .= '</div>';
    return $html;
}

// register OCA jobs
function oca_demo_10_register_scripts() {

    // check if OCA class exists
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) ){

        // get oca_manager global
        global $oca_manager;

        // job 1 arguments. notice the 'frontend_cache' paramenter
        // check all possible values in the inline documentation in `class-oca-asynchronous-content-organizer-queue-manager.php` :
        $oca_job_1_args = array(
            'function_name'     => 'oca_demo_10_teaser', // function name
            'nopriv_function_name' => 'oca_demo_10_thank_you', // function name
            'placement'         => 'append', // should we 'append' (default), 'prepend' or 'replace' the content in the container?
            'frontend_cache_priv'   => true,
            'frontend_cache_nopriv' => false // we should cache response for both logged and non-logged users
        );

        // push job to queue
        $oca_manager->add_job( $oca_job_1_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_10_register_scripts', 9);

Although it's a powerful tool, don't rely on localStorage, specially for users in private mode, because each browser have different rules and behaviors for it. As a rule of thumb, think that users in private mode will not have a front-end cache when using private mode. Also, keep in mind that each browser will have different rules for maximum localStorage size in bytes, gargabe collections, etc, so a content may be deleted before the expiration time you set.

Finally, a word about security: never store any kind of credentials or password in localStorage.

Example 11: Multiple jobs for complex behaviors

Let's say that you want to provide full single post content for your logged users only and you want to provide a second menu with two extra items -- My Profile and My Subscription -- to those users right after the top navigation. Once again, if you rely solely on WP/PHP and page cache, you will probably get the same problems mentioned on Example 4:

A. Page cache will be purged often, wasting server resources and not improving user experience as it could. B. Page cache will present non-logged content for logged user and vice-versa C. logged users won't have any cache at all.

OCA allows you to use several strategies to leverage cache in here. One of them is to use 2 jobs with different settings:

  • Job 1 will manage the main content generation, like in previous examples.
  • Job 2 will manage the menu generation.

Let's see the code:

The page will be generated with the non-logged version content (excerpt) for both type users, leveraging page cache. Then, on window.load event, OCA will kick in, check if user is logged in and place "content loading" in the main area (the element with #main.

If the user is loggged in, job 1 will fetch the post content and replace the content in element with id="main". If user is not logged, will do nothing. Also, we won't set neither frontend_cache_priv and frontend_cache_nopriv parameters so nothing will be cached in the frontend

Following the same logic, job 2 will fetch a custom menu for logged in users only and do nothing for non-logged users. Because there is no sensitive information in the menu itself, we will set job 2 frontend_cache_nopriv parameter to true, without purging between user privileges changes.

Notice that we pass an array with $post_id to the function_args parameter. The reason is that OCA uses a hash of all parameters to create the cache identifier. If we don't use some distintice informatio between posts, OCA will get the same cache content for all posts.

//For logged users, fetch post content
function oca_demo_11_get_content($post_id){

    // get post object
    global $post;

    // apply the_content filter to post_content and return it;
    $html = apply_filters('the_content', $post->post_content;

    $html .= '<div class="teaser thank-you">';
    $html .= '<h3>Thanks for being a supporter</h3>';
    $html .= '<p>We really appreciate being a member and becoming our supporter.</p>';
    $html .= '<p>Don\'t forget to redeem your <em>PERKs</em> in your profile page</p>';
    $html .= '</div>';
    return $html;

}

//For logged users, get a custom menu
function oca_demo_11_get_priv_menu(){
    $html = wp_nav_menu(
        array(
        'theme_location'	=> 'custom_menu',
        'depth'             => 5,
        'container'         => 'div',
        'container_class'   => '',
        'menu_class'		=> 'custom_menu'
        'walker' 			=> new wp_bootstrap_navwalker,
        'container'			=>	null,
        'echo'				=> false,
    ));
    return $html;
}

// register OCA jobs
function oca_demo_11_register_scripts() {

    // check if OCA class exists and if we are in a single post
    if ( class_exists( 'Oca_Asynchronous_Content_Organizer' ) && is_single() ){

        // get oca_manager global
        global $oca_manager;

        // job 1 arguments. notice the 'bypass' for nopriv_function_name
        $oca_job_1_args = array(
            'function_name'        => 'oca_demo_11_get_content', // function name
            'function_args'        => array(
                $post_id // we need this parameter to create different cache identifiers for each post
            ),
            'nopriv_function_name' => 'bypass', //don't make any request for non-logged users
            'placement'            => 'replace',
        );

        // job 2 arguments.
        $oca_job_2_args = array(
            'function_name'         => 'oca_demo_11_get_priv_menu', // function name
            'nopriv_function_name'  => 'bypass', //don't make any request for non-logged users
            'container'             => '.navigation-top',
            'placement'             => 'append',
            'frontend_cache_nopriv' => true // we should cache response for both logged and non-logged users
        );

        // push jobs to queue
        $oca_manager->add_job( $oca_job_1_args );
        $oca_manager->add_job( $oca_job_2_args );
    }
}
add_action('wp_enqueue_scripts', 'oca_demo_11_register_scripts', 9);

Example 12: Using Javascript Callbacks

Say you want to run a Javascript function after the new content arrives. That's easy, you just need to make sure the function is available when OCA kicks in and pass the function name to the callback parameters, like this

        // job 2 arguments.
        $oca_job_2_args = array(
            'function_name'         => 'oca_demo_11_get_priv_menu', // function name
            'nopriv_function_name'  => 'bypass', //don't make any request for non-logged users
            'container'             => '.navigation-top',
            'placement'             => 'append',
            'frontend_cache_priv'   => 'true' // cache in the front end for privileged users,
            'callback'              => 'my_own_callback'
        );

Browser Compatibility

  • TO DO

Frequently Asked Questions

What values are valid for frontend_cache_priv and frontend_cache_nopriv parameters?

    /**
	 *  false: no cache on frontend
	 *  true: cache on frontend for browsers supporting localStorage
	 * 	'purgeonchange': aPurge on privileges status change
	 *
     *  Default value is false.
    */

Privilege status change means: whenever a logged-in user logs out or the other way around. Purge on privileges status change means: when the status changes, the cache for the oposite status is purged. e.g. when a user logs out and the rule is frontend_cache rules is priv|purge, the priv cached content will be purged.

How to Contribute

Please review the contributing guidelines.

Acknowledgements

Parts of this upluginuse techniques, ideas and code from Chris Ferdinandi and others.

"If I have seen further it is by standing on the shoulders of Giants."

Isaac Newton

License

The code is available under the GPLv2.

About

An utility plugin for asynchronous injection and cache management of dynamic content generated by WordPress on front-end, helping code savyy users to improve page load times, overall performance and user experience even for membership and ecommerce websites.

Resources

License

Stars

Watchers

Forks

Packages