# LiteSpeed Cache Third Party Integration 

## Introduction
If you have a WP plugin that populates front end content that can be publicly cached, 
in general, it should work with LSCache. 
However if you update the data generated by your plugin and the cached page is not properly purged, 
you will need to add some integration scripts. It is very simple to integrate your plugin with LSCache. 
In addition to this README, we have a template file and a few examples of plugins that required integration scripts.

## How it works
LSCache works by tagging each cacheable page. 
In its most basic form, each page is tagged with its Post ID, then sent to the server to be cached. 
When someone makes a change to the page, that request will notify the server to 
purge the cached items associated with that page's post id.

This integration allows you to customize the notifications sent to the server. 
You can tag the page with identifiers as the page is stored in the cache. 
Later, if you need to purge the cache of a certain subset of pages, 
you can use any of the identifiers you attached to the page. 
Multiple tags can be set on a single page, and a single tag may be used on multiple pages. 
This many to many mapping provides a flexible system enabling you to group pages in many ways.

For example, a page may be tagged with `MTPP_F.1 (forum), MTPP_G.4 (group), MTPP_S.georgia (state)`
because the page is in forum 1, group 4, and related to the state of Georgia. 
Then you have another page that is tagged `MTPP_F.1, MTPP_G.2, MTPP_S.iowa`. 
Then, if a change is made where all pages tagged `MTPP_F.1` need to be purged, you can tell the server to do so.

A post will automatically be purged if the following events are triggered:
- edit_post
- save_post
- deleted_post
- trashed_post
- delete_attachment

These cases cover most situations in which a cache purge is necessary. If all the correct pages are purged, you may not need to add any tags.

Another application for creating a third party integration class is to notify LSCache if your plugin generates private/transient data that cannot be cached for certain responses. Below is a list of what we consider non cacheable. If your cases are covered by these cases, there is no need to check if a page is cacheable.

A post is considered non cacheable if...
- It is an admin page
- The user is logged in
- It is a post request
- is_feed() is true
- is_trackback() is true
- is_404() is true
- is_search() is true
- No theme is used
- The URI matches any of the do not cache URI config
- The post has a category matching the do not cache category config
- The post has a tag matching the do not cache tag config
- The request has a cookie matching the do not cache cookie config
- The request has a user agent matching the do not cache user agent config

## Components

1. A class to handle the compatibility code. A template is available below.
2. Initiator for the class. Can be in the plugin's own file space or appended to the registry.

## Hook Points
There are five hook points available for use. You may also use your own hook points
prior to the 'shutdown' action to add response headers.

- litespeed_cache_detect_thirdparty - This action is used to detect whether you want
to add your functions to the below hook points. 
It is not required to use this hook point, but it is provided in case it is needed.
  - As an example, suppose I have a shop plugin and a forum plugin.
  I don't want my forums plugin to add actions when someone visits /shop/,
  so I use this hook point to determine if this is a forum page.
  If it is, I will add the rest of my hooks.
- litespeed_cache_is_cacheable - This filter is triggered when the cache checks
if the current page is cacheable. 
  - The one parameter is a boolean true/false.
  If it is false, that means another plugin determined the page was uncacheable,
  so your plugin should just pass the false forward. Otherwise, return true/false
  based on whether or not the page meets the cacheable requirements of your plugin.
  - The filter will not trigger on admin pages nor regular pages when the user is logged in. 
  You may use this to add extra checks to make sure the page is cacheable according to your requirements.
  - For example, a shopping cart shouldn't be cached.
  The cart may be different for each non logged-in visitor.
- litespeed_cache_on_purge_post - This action is triggered when the cache
is notified that a page is about to be purged. It should be used if your plugin
requires that other pages are purged as well.
  - As an example, suppose a user adds a reply to a forum topic. The topic,
  the forum, and the forum list should all be purged because they are all
  affected.
- litespeed_cache_add_purge_tags - This action is called at the end of each request.
It may be used to add any last minute purge tags.
- litespeed_cache_add_cache_tags - This action is called at the end of each request.
It may be used to add any last minute cache tags.

## Functions

The add_*_tags hooks (as well as any of your own hook points) may be used
to add tags to the response header. These are used to classify a page further
and subsequently purge a specific class.

An example use case:

Suppose I have a slideshow of 30 images. I can hook into add_cache_tags and add
a slideshow ID to the cache tag. Later on, if I need to rotate one of the images
and I know all of the slideshows that the image is in, I can hook in at
add_purge_tags and purge that slide show ID. The server will purge all the
pages with that slideshow ID, and you're guaranteed to have an up-to-date page.

Use the following functions to add your tags:
- `LiteSpeed_Cache_Tags::add_cache_tag($tag)`
- `LiteSpeed_Cache_Tags::add_purge_tag($tag)`

The `$tag` parameter has the following restrictions:
1. The name should be short but unique.
2. Name and ID are split by a period, '.'

That's it! To provide an example, I may have a tag `MSSP_SS.1` for the example above.
That would be MySlideShowPlugin_SlideShow has an ID = 1.


## Template Class
This class is a template to be used by other plugin developers that
require some minor additions to make their plugin compatible with the cache.

```php
/**
 * A template Plugin Compatibility class.
 *
 * This is used to demonstrate the various functionalities that are available
 * for third party plugins to make their plugin compatible with LiteSpeed Cache.
 *
 * The name of the class should start with LiteSpeed_Cache_ThirdParty_*.
 * You should replace the asterisk with the name of the plugin.
 * The file is named the same as the class, except with dashes, '-', instead of underscores, '_'
 * and with all lowercase letters.
 * 
 */
class LiteSpeed_Cache_ThirdParty_Plugin
{
    const CACHETAG_KIDS = 'MTPP_K.';
    const CACHETAG_GROUP = 'MTPP_G.';

    /**
     * Detect if the page requested is related to my plugin. If it is related,
     * I need to add my functions to the needed hooks.
     *
     */
    public static function detect()
    {
        if (isMyPluginEnabled()) {
            add_filter('litespeed_cache_is_cacheable', 'LiteSpeed_Cache_ThirdParty_Plugin::is_cacheable');
            add_action('litespeed_cache_on_purge_post', 'LiteSpeed_Cache_ThirdParty_Plugin::on_purge');
            add_action('litespeed_cache_add_purge_tags', 'LiteSpeed_Cache_ThirdParty_Plugin::add_purge_tags');
            add_action('litespeed_cache_add_cache_tags', 'LiteSpeed_Cache_ThirdParty_Plugin::add_cache_tags');
        }
    }

    /**
     * This filter is used to let the cache know if a page is cacheable.
     *
     * @param $cacheable true/false, whether a previous filter determined this page is cacheable or not.
     * @return true if cacheable, false if not.
     */
    public static function is_cacheable($cacheable)
    {
        if (!$cacheable) {
            return false;
        }

        global $myPluginsData;

        // An example use case is if a page should only be shown to a specific user.
        if ($myPluginsData->has_private_information()) {
            return false;
        }
        return true;
    }

    /**
     * This action is triggered when a page needs to be purged for whatever reason.
     * e.g. updating the post, deleting the post.
     * This is useful if your plugin needs to purge other, related pages.
     *
     * @param $post_id The id of the posts about to be purged.
     */
    public static function on_purge($post_id)
    {
        global $myPluginsData;

        $sister = $myPluginsData->get_sibling_post($myPluginsData->get_post($post_id));
        if ($sister) {
            LiteSpeed_Cache_Tags::add_purge_tags(self::CACHETAG_KIDS . $sister->id);
        }
    }

    /**
     * This action can be used to add last minute purge tags.
     * This example is purging all pages that are in a group when a page in that
     * group is requested.
     */
    public static function add_purge_tags()
    {
        global $myPluginsData;

        if ($myPluginsData->is_in_a_group()) {
            LiteSpeed_Cache_Tags::add_purge_tags(self::CACHETAG_GROUP . $myPluginsData->get_group_id());
        }
    }

    /**
     * This action can be used to add last minute cache tags.
     * This example is adding the plugin specific cache tags that can be used
     * to purge later if needed.
     */
    public static function add_cache_tags()
    {
        global $myPluginsData;

        if ($myPluginsData->is_a_child()) {
            LiteSpeed_Cache_Tags::add_purge_tags(self::CACHETAG_KIDS . $myPluginsData->get_id());
        }

        if ($myPluginsData->is_in_a_group()) {
            LiteSpeed_Cache_Tags::add_purge_tags(self::CACHETAG_GROUP . $myPluginsData->get_group_id());
        }
    }

}

add_action('litespeed_cache_detect_thirdparty', 'LiteSpeed_Cache_ThirdParty_Plugin::detect');


```



