` tag.
127 |
128 | Is possible to set HTML class attribute for this container using
129 | **`"ajax_template_loading_class"`** filter hook.
130 |
131 | Callbacks hooking this filter receive same 4 arguments passed by `"ajax_template_loading_content"`.
132 |
133 | Note that `
` container is always added to page, even when no temporary content is used, but
134 | by default it is hidden using in-line CSS, but using `"ajax_template_loading_class"` filter, is
135 | possible to add classes to the `
` and so be able to style it via CSS: among other things is possible
136 | to use a *loader image* by setting it as background image CSS property of a class
137 | added via this filter.
138 |
139 |
140 | ## Nested calls
141 |
142 | If in template loaded using `ajax_template_part()` there are additional calls to same function,
143 | *nested* templates are loaded as expected, and in the same ajax request: don't expect
144 | *another* ajax request triggered when *parent* ajax-required template as been loaded.
145 |
146 | That applies in the exact manner to any `get_template_part()` call inside ajax loaded templates.
147 |
148 | ## Stay safe
149 |
150 | To put `ajax_template_part()` calls in your templates makes your site *require* this plugin is
151 | installed, and active, otherwise you'll get fatal error because of function not declared.
152 |
153 | To avoid such problems, e.g. if you deactivate plugin by accident, can be a good idea put this on top
154 | of your `functions.php`:
155 |
156 | ``` php
157 | if ( ! function_exists( 'ajax_template_part' ) ) {
158 | function ajax_template_part( $name = '', $slug = '' ) {
159 | return get_template_part( $name, $slug );
160 | }
161 | }
162 | if ( ! function_exists( 'ajax_template_part_content' ) ) {
163 | function ajax_template_part_content( $content = '', $name = '', $slug = '' ) {
164 | return get_template_part( $name, $slug );
165 | }
166 | }
167 | ```
168 |
169 | In this way your theme will *gracefully degrade* to `get_template_part` if plugin is not active for
170 | any reason.
171 |
172 | ----
173 |
174 | # Cache
175 |
176 | Ajax template loading and content generation may be heavy, so plugin *needs* a way to cache them.
177 |
178 | Cache is active by default when the constant `WP_DEBUG` is set to `FALSE`, this should be a pretty
179 | common way to turn it on for production and off in locale / development environments.
180 |
181 | However, using the **`"ajax_template_cache"`** filter is possible to customize when enable or disable caching.
182 | Only argument this hook passes to hooking callbacks is the current cache status and have to return
183 | a boolean: `TRUE` means cache active.
184 |
185 | This plugin can work with different types of caches:
186 |
187 | - if in the system is installed an external object cache, then this plugin will use that, nothing is left to do.
188 | - if no external object cache is in use, this plugin uses [Stash](http://www.stashphp.com) to cache data.
189 | This library can make use of different "drivers": FileSystem, APC, Memcached, Redis...
190 | By default plugin uses FileSystem driver and *no* configuration is required to use that.
191 | However is possible to use any supported driver, if system has the requirements.
192 |
193 | ## Use an alternative cache driver
194 |
195 | To use a different driver there are 2 filters available:
196 |
197 | - **`"ajax_template_cache_driver"`**: hooking callback must return the fully qualified name
198 | of the class to use, one among:
199 | - `Stash\Driver\Sqlite`
200 | - `Stash\Driver\Memcache`
201 | - `Stash\Driver\APC`
202 | - `Stash\Driver\Redis`
203 | - `Stash\Driver\Composite`
204 |
205 | please refer to [Stash documentation](http://www.stashphp.com/Drivers.html) for details.
206 |
207 | - **`"ajax_template_{$driver}_driver_conf"`** is the filter to be used to configure the chosen driver.
208 | Hooking callback must return the configuration array, see Stash documentation for details.
209 |
210 | As example, configuration to use Memcache driver should be something like this:
211 |
212 | ```php
213 | add_filter( 'ajax_template_cache_driver', function() {
214 | return '\Stash\Driver\Memcache';
215 | });
216 |
217 | add_filter( "ajax_template_memcache_driver_conf", function() {
218 | return [ 'servers' => ['127.0.0.1', '11211'] ];
219 | });
220 | ```
221 |
222 | ## Cache expiration
223 |
224 | By default contents are cached for 1 hour. Consider that if content (e.g. posts) is updated
225 | and the old content is cached, updated content will not be shown until cache expires.
226 |
227 | Is possible to change default expiration time using `"ajax_template_cache_ttl"` filter hook.
228 | Hooking callbacks receives and have to return cache "time to live" value in seconds.
229 | Note that setting a value under 30 seconds will be skipped and default will be used.
230 |
231 | If you need to disable cache don't use this filter but `"ajax_template_cache"` or set `WP_DEBUG` to true
232 | (not recommended for production, highly recommended for development environments).
233 |
234 | ---------
235 |
236 | #License
237 |
238 | This plugin is released under MIT license.
239 |
--------------------------------------------------------------------------------
/ajax-template-part.php:
--------------------------------------------------------------------------------
1 | =5.5",
26 | "composer/installers": "~1.0",
27 | "tedivm/stash": "~0.14.0"
28 | },
29 | "autoload": {
30 | "psr-4": {
31 | "GM\\ATP\\": "src/"
32 | },
33 | "files": [
34 | "inc/helpers.php"
35 | ]
36 | },
37 | "config": {
38 | "optimize-autoloader": true
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/inc/helpers.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP;
12 |
13 | use Stash\DriverList;
14 | use Stash\Interfaces\DriverInterface;
15 | use Stash\Pool;
16 |
17 | /**
18 | * Plugin activation callback.
19 | */
20 | function activate()
21 | {
22 | wp_schedule_event(time(), 'daily', 'ajaxtemplatepart_cache_purge');
23 | }
24 |
25 | /**
26 | * Plugin deactivation callback.
27 | */
28 | function deactivate()
29 | {
30 | $stash = get_stash();
31 | $stash instanceof Pool and $stash->flush();
32 | $timestamp = wp_next_scheduled('ajaxtemplatepart_cache_purge');
33 | $timestamp and wp_unschedule_event($timestamp, 'ajaxtemplatepart_cache_purge');
34 | }
35 |
36 | /**
37 | * @param string $name
38 | * @param string $slug
39 | * @param string $content
40 | */
41 | function template_part($name, $slug = '', $content = '')
42 | {
43 | if (defined('DOING_AJAX') && DOING_AJAX) {
44 | return \get_template_part($name, $slug);
45 | }
46 |
47 | $path = dirname(__DIR__).'/ajax-template-part.php';
48 | $templater = new Templater($GLOBALS['wp'], $GLOBALS['wp_query'], $path);
49 | $templater->addJs()->addHtml($name, $slug, $content);
50 | }
51 |
52 | /**
53 | * AJAX callback.
54 | */
55 | function ajax_callback()
56 | {
57 | if (! defined('DOING_AJAX') || ! DOING_AJAX) {
58 | return;
59 | }
60 |
61 | $loader = new Loader();
62 |
63 | /**
64 | * No caching when WP_DEBUG is true, but can be filtered via "ajax_template_cache" hook.
65 | * If external object cache is active use that (via Transients API) otherwise
66 | * use Stash, by default with FileSystem driver, but driver and its options can be customized
67 | * via "ajax_template_cache_driver" and "ajax_template_{$driver}_driver_conf" filter hooks.
68 | */
69 | $provider = new Cache\Provider();
70 | $handler = $provider->isEnabled() ? get_cache_handler() : null;
71 |
72 | if ($handler) {
73 | $provider->setHandler($handler);
74 | $loader->setCacheProvider($provider);
75 | }
76 |
77 | $loader->getData();
78 |
79 | exit();
80 | }
81 |
82 | /**
83 | * @return \Stash\Pool|void
84 | */
85 | function get_stash()
86 | {
87 | $drivers = DriverList::getAvailableDrivers();
88 | $provider = new Cache\StashDriverProvider(new FileSystem(), $drivers);
89 | $class = $provider->getDriverClass();
90 | if (! class_exists($class)) {
91 | return;
92 | }
93 |
94 | $driver = new $class();
95 | if ($driver instanceof DriverInterface) {
96 | $options = $provider->getDriverOptions($class);
97 | $driver->setOptions($options);
98 |
99 | return new Pool($driver);
100 | }
101 | }
102 |
103 | /**
104 | * @return \GM\ATP\Cache\HandlerInterface
105 | */
106 | function get_cache_handler()
107 | {
108 | static $handler;
109 | if (! is_null($handler)) {
110 | return $handler;
111 | }
112 |
113 | $transient = new Cache\TransientHandler();
114 | if ($transient->isAvailable()) {
115 | $handler = $transient;
116 |
117 | return $transient;
118 | }
119 |
120 | $handler = new Cache\StashHandler(get_stash());
121 |
122 | return $handler;
123 | }
124 |
125 | /**
126 | * Purge cache.
127 | */
128 | function cache_purge()
129 | {
130 | $provider = new Cache\Provider();
131 | $handler = $provider->isEnabled() ? get_cache_handler() : null;
132 | $handler instanceof Cache\StashHandler and $handler->getStash()->purge();
133 | }
134 |
--------------------------------------------------------------------------------
/js/atp.js:
--------------------------------------------------------------------------------
1 | (function ($, ATP) {
2 |
3 | "use strict"
4 |
5 | ATP.methods = {};
6 | ATP.map = {ids: [], spans: {}, slugs: {}, posts: {}};
7 |
8 | ATP.methods.checkResponse = function (response) {
9 | return typeof response.success !== 'undefined'
10 | && response.success
11 | && typeof response.data === 'object';
12 | };
13 |
14 | ATP.methods.onError = function () {
15 | $(ATP.map.ids).each(function (id, el) {
16 | $('#' + el).remove();
17 | });
18 | $(document).trigger('ajax_template_part_error', ATP.map);
19 | $(document).trigger('ajax_template_part_done');
20 |
21 | };
22 |
23 | ATP.methods.onSuccess = function (data) {
24 | $.each(ATP.map.ids, function (id, el) {
25 | var $el = $('#' + el);
26 | if ($el.length && typeof data[el] === 'string') {
27 | $el.replaceWith(data[el]).show();
28 | } else {
29 | $el.remove();
30 | }
31 | });
32 | $(document).trigger('ajax_template_part_success', ATP.map);
33 | $(document).trigger('ajax_template_part_done');
34 | };
35 |
36 | ATP.methods.callAjax = function () {
37 | return $.ajax({
38 | url: ATP.info.ajax_url,
39 | dataType: 'json',
40 | type: 'POST',
41 | data: {
42 | action: 'ajaxtemplatepart',
43 | query_data: ATP.info.query_data,
44 | files_data: ATP.map.slugs,
45 | posts_data: ATP.map.posts
46 | }
47 | });
48 | };
49 |
50 | ATP.methods.parseDom = function () {
51 | $('span[data-ajaxtemplate]').each(function (i, el) {
52 | var $el = $(el);
53 | var id = $el.attr('id');
54 | var rawData = decodeURIComponent($el.data('ajaxtemplate').replace(/\+/g, ' '));
55 | var templateData = $.parseJSON(rawData);
56 | var name = templateData.name;
57 | var slug = templateData.slug;
58 | var post = $el.data('post');
59 | if (typeof id === 'string' && id !== '' && typeof name === 'string' && name !== '') {
60 | if (typeof slug !== 'string') {
61 | slug = false;
62 | }
63 | if (Number(post) <= 0) {
64 | post = false;
65 | }
66 | ATP.map.ids.push(id);
67 | ATP.map.spans[id] = $el;
68 | ATP.map.slugs[id] = [name, slug];
69 | ATP.map.posts[id] = post;
70 | }
71 | $el.data('ajaxtemplate', null);
72 | });
73 | };
74 |
75 | ATP.methods.update = function () {
76 | if (ATP.map.ids.length < 1) {
77 | return false;
78 | }
79 | ATP.methods
80 | .callAjax()
81 | .done(function (response) {
82 | if (ATP.methods.checkResponse(response)) {
83 | ATP.methods.onSuccess(response.data);
84 | } else {
85 | ATP.methods.onError();
86 | }
87 | })
88 | .fail(function () {
89 | ATP.methods.onError();
90 | });
91 | };
92 |
93 | $(document).ready(function () {
94 | ATP.methods.parseDom();
95 | ATP.methods.update();
96 | });
97 |
98 | $(document).on('ajax_template_part_done', function () {
99 | ATP.map = {ids: [], spans: {}, slugs: {}, posts: {}};
100 | });
101 |
102 | })(jQuery, AjaxTemplatePartData);
103 |
--------------------------------------------------------------------------------
/js/atp.min.js:
--------------------------------------------------------------------------------
1 | (function(b,a){a.methods={};a.map={ids:[],spans:{},slugs:{},posts:{}};a.methods.checkResponse=function(c){return typeof c.success!=="undefined"&&c.success&&typeof c.data==="object"};a.methods.onError=function(){b(a.map.ids).each(function(d,c){b("#"+c).remove()});b(document).trigger("ajax_template_part_error",a.map);b(document).trigger("ajax_template_part_done")};a.methods.onSuccess=function(c){b.each(a.map.ids,function(f,e){var d=b("#"+e);if(d.length&&typeof c[e]==="string"){d.replaceWith(c[e]).show()}else{d.remove()}});b(document).trigger("ajax_template_part_success",a.map);b(document).trigger("ajax_template_part_done")};a.methods.callAjax=function(){return b.ajax({url:a.info.ajax_url,dataType:"json",type:"POST",data:{action:"ajaxtemplatepart",query_data:a.info.query_data,files_data:a.map.slugs,posts_data:a.map.posts}})};a.methods.parseDom=function(){b("span[data-ajaxtemplate]").each(function(j,f){var l=b(f);var e=l.attr("id");var c=decodeURIComponent(l.data("ajaxtemplate").replace(/\+/g," "));var g=b.parseJSON(c);var d=g.name;var h=g.slug;var k=l.data("post");if(typeof e==="string"&&e!==""&&typeof d==="string"&&d!==""){if(typeof h!=="string"){h=false}if(Number(k)<=0){k=false}a.map.ids.push(e);a.map.spans[e]=l;a.map.slugs[e]=[d,h];a.map.posts[e]=k}l.data("ajaxtemplate",null)})};a.methods.update=function(){if(a.map.ids.length<1){return false}a.methods.callAjax().done(function(c){if(a.methods.checkResponse(c)){a.methods.onSuccess(c.data)}else{a.methods.onError()}}).fail(function(){a.methods.onError()})};b(document).ready(function(){a.methods.parseDom();a.methods.update()});b(document).on("ajax_template_part_done",function(){a.map={ids:[],spans:{},slugs:{},posts:{}}})})(jQuery,AjaxTemplatePartData);
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014-2016 Giuseppe Mazzapica
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/src/Cache/HandlerInterface.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP\Cache;
12 |
13 | /**
14 | * @author Giuseppe Mazzapica
15 | * @license http://opensource.org/licenses/MIT MIT
16 | * @package ATP
17 | */
18 | interface HandlerInterface
19 | {
20 | /**
21 | * @param $key
22 | */
23 | public function get($key);
24 |
25 | /**
26 | * @param string $key
27 | * @param mixed $value
28 | * @param int $expiration
29 | */
30 | public function set($key, $value, $expiration);
31 |
32 | /**
33 | * @param string $key
34 | */
35 | public function clear($key);
36 |
37 | /**
38 | * @return bool
39 | */
40 | public function isAvailable();
41 | }
42 |
--------------------------------------------------------------------------------
/src/Cache/Provider.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP\Cache;
12 |
13 | /**
14 | * @author Giuseppe Mazzapica
15 | * @license http://opensource.org/licenses/MIT MIT
16 | * @package ATP
17 | */
18 | class Provider implements ProviderInterface
19 | {
20 | /**
21 | * @var \GM\ATP\Cache\HandlerInterface
22 | */
23 | private $handler;
24 |
25 | /**
26 | * @var string
27 | */
28 | private $id;
29 |
30 | /**
31 | * @var int
32 | */
33 | private $ttl;
34 |
35 | /**
36 | * @return \GM\ATP\Cache\HandlerInterface
37 | */
38 | public function getHandler()
39 | {
40 | return $this->handler;
41 | }
42 |
43 | /**
44 | * @param \GM\ATP\Cache\HandlerInterface $handler
45 | */
46 | public function setHandler(HandlerInterface $handler)
47 | {
48 | $this->handler = $handler;
49 | }
50 |
51 | /**
52 | * @param array $id1
53 | * @param array $id2
54 | * @return mixed
55 | */
56 | public function get(array $id1, array $id2)
57 | {
58 | $key = $this->id($id1, $id2);
59 |
60 | return $this->getHandler()->get($key);
61 | }
62 |
63 | /**
64 | * @param array $value
65 | * @param array $id1
66 | * @param array $id2
67 | * @return mixed
68 | */
69 | public function set(array $value, array $id1, array $id2)
70 | {
71 | $key = $this->id($id1, $id2);
72 |
73 | return $this->getHandler()->set($key, $value, $this->ttl());
74 | }
75 |
76 | /**
77 | * @return bool
78 | */
79 | public function isEnabled()
80 | {
81 | return apply_filters('ajax_template_cache', (defined('WP_DEBUG') && ! WP_DEBUG));
82 | }
83 |
84 | /**
85 | * @return bool
86 | */
87 | public function shouldCache()
88 | {
89 | if ($this->isEnabled()) {
90 | $handler = $this->getHandler();
91 |
92 | return $handler instanceof HandlerInterface && $handler->isAvailable();
93 | }
94 |
95 | return false;
96 | }
97 |
98 | /**
99 | * @param array $id1
100 | * @param array $id2
101 | * @return string
102 | */
103 | private function id(array $id1, array $id2)
104 | {
105 | if (! is_null($this->id)) {
106 | return $this->id;
107 | }
108 |
109 | $a = array_filter($id1);
110 | $b = array_filter($id2);
111 | ksort($a);
112 | ksort($b);
113 | $this->id = 'GM_ATP_'.md5(serialize($a).serialize($b));
114 |
115 | return $this->id;
116 | }
117 |
118 | /**
119 | * @return int
120 | */
121 | private function ttl()
122 | {
123 | if (is_null($this->ttl)) {
124 | $ttl = apply_filters('ajax_template_cache_ttl', HOUR_IN_SECONDS);
125 | $this->ttl = is_scalar($ttl) && (int)$ttl > 30 ? (int)$ttl : HOUR_IN_SECONDS;
126 | }
127 |
128 | return $this->ttl;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/Cache/ProviderInterface.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP\Cache;
12 |
13 | /**
14 | * @author Giuseppe Mazzapica
15 | * @license http://opensource.org/licenses/MIT MIT
16 | * @package ATP
17 | */
18 | interface ProviderInterface
19 | {
20 | /**
21 | * @param array $id1
22 | * @param array $id2
23 | */
24 | public function get(array $id1, array $id2);
25 |
26 | /**
27 | * @param array $value
28 | * @param array $id1
29 | * @param array $id2
30 | */
31 | public function set(array $value, array $id1, array $id2);
32 |
33 | /**
34 | * @return bool
35 | */
36 | public function isEnabled();
37 |
38 | /**
39 | * @return bool
40 | */
41 | public function shouldCache();
42 |
43 | /**
44 | * @return HandlerInterface
45 | */
46 | public function getHandler();
47 |
48 | /**
49 | * @param \GM\ATP\Cache\HandlerInterface $handler
50 | */
51 | public function setHandler(HandlerInterface $handler);
52 | }
53 |
--------------------------------------------------------------------------------
/src/Cache/StashDriverProvider.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP\Cache;
12 |
13 | use GM\ATP\FileSystem;
14 | use Stash\Driver\FileSystem as FileSystemDriver;
15 | use Stash\Interfaces\DriverInterface;
16 |
17 | /**
18 | * @author Giuseppe Mazzapica
19 | * @license http://opensource.org/licenses/MIT MIT
20 | * @package ATP
21 | */
22 | class StashDriverProvider
23 | {
24 | /**
25 | * @var \GM\ATP\FileSystem
26 | */
27 | private $fs;
28 |
29 | /**
30 | * @var array
31 | */
32 | private $drivers;
33 |
34 | /**
35 | * @param \GM\ATP\FileSystem $fs
36 | * @param array $drivers
37 | */
38 | public function __construct(FileSystem $fs, array $drivers = [])
39 | {
40 | $this->fs = $fs;
41 | $this->drivers = $drivers;
42 | }
43 |
44 | /**
45 | * @return string
46 | */
47 | public function getDriverClass()
48 | {
49 | $driver = apply_filters('ajax_template_cache_driver', FileSystemDriver::class);
50 |
51 | if (
52 | ! is_subclass_of($driver, DriverInterface::class, true)
53 | || $driver === FileSystemDriver::class
54 | ) {
55 | return FileSystemDriver::class;
56 | }
57 |
58 | /** @var callable $cb */
59 | $cb = [$driver, 'isAvailable'];
60 |
61 | return $cb() ? $driver : FileSystemDriver::class;
62 | }
63 |
64 | /**
65 | * @param $driver_class
66 | * @return string
67 | */
68 | public function getDriverName($driver_class)
69 | {
70 | return array_search($driver_class, $this->drivers, true);
71 | }
72 |
73 | /**
74 | * @param $driver_class
75 | * @return array
76 | */
77 | public function getDriverOptions($driver_class)
78 | {
79 | $driver_name = $this->getDriverName($driver_class);
80 | if (empty($driver_name) || $driver_name === 'FileSystem') {
81 | $path = $this->fs->getFolder();
82 |
83 | return $path ? ['path' => $path] : [];
84 | }
85 |
86 | $name = strtolower($driver_name);
87 |
88 | return apply_filters("ajax_template_{$name}_driver_conf", []);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Cache/StashHandler.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP\Cache;
12 |
13 | use Stash\Interfaces\ItemInterface;
14 | use Stash\Interfaces\PoolInterface;
15 |
16 | /**
17 | * @author Giuseppe Mazzapica
18 | * @license http://opensource.org/licenses/MIT MIT
19 | * @package ATP
20 | */
21 | class StashHandler implements HandlerInterface
22 | {
23 | /**
24 | * @var \Stash\Interfaces\PoolInterface
25 | */
26 | private $stash;
27 |
28 | /**
29 | * @param \Stash\Interfaces\PoolInterface $pool
30 | */
31 | public function __construct(PoolInterface $pool)
32 | {
33 | return $this->stash = $pool;
34 | }
35 |
36 | /**
37 | * @param string $key
38 | * @return bool|\Exception
39 | */
40 | public function clear($key)
41 | {
42 | try {
43 | $item = $this->getItem($key);
44 | if (! $item instanceof ItemInterface) {
45 | return false;
46 | }
47 |
48 | $item->clear();
49 |
50 | return true;
51 | } catch (\Exception $e) {
52 | return false;
53 | }
54 | }
55 |
56 | /**
57 | * @param $key
58 | * @return mixed
59 | */
60 | public function get($key)
61 | {
62 | try {
63 | $item = $this->getItem($key);
64 | if (! $item instanceof ItemInterface) {
65 | return false;
66 | }
67 | $cached = $item->get();
68 |
69 | return $item->isMiss() ? false : $cached;
70 | } catch (\Exception $e) {
71 | return false;
72 | }
73 | }
74 |
75 | /**
76 | * @param string $key
77 | * @param mixed $value
78 | * @param int $expiration
79 | * @return bool
80 | */
81 | public function set($key, $value, $expiration)
82 | {
83 | try {
84 | $item = $this->getItem($key);
85 | if (! $item instanceof ItemInterface) {
86 | return false;
87 | }
88 | $item->clear();
89 | $item->lock();
90 |
91 | return $item->set($value, $expiration);
92 | } catch (\Exception $e) {
93 | return false;
94 | }
95 | }
96 |
97 | /**
98 | * @return bool
99 | */
100 | public function isAvailable()
101 | {
102 | return call_user_func([get_class($this->getStash()->getDriver()), 'isAvailable']);
103 | }
104 |
105 | /**
106 | * @return \Stash\Interfaces\PoolInterface
107 | */
108 | public function getStash()
109 | {
110 | return $this->stash;
111 | }
112 |
113 | /**
114 | * @param $key
115 | * @return \Stash\Interfaces\ItemInterface|null
116 | */
117 | private function getItem($key)
118 | {
119 | try {
120 | return $this->stash->getItem($key);
121 | } catch (\Exception $e) {
122 | return;
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/Cache/TransientHandler.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP\Cache;
12 |
13 | /**
14 | * @author Giuseppe Mazzapica
15 | * @license http://opensource.org/licenses/MIT MIT
16 | * @package ATP
17 | */
18 | class TransientHandler implements HandlerInterface
19 | {
20 | /**
21 | * @param string $key
22 | * @return bool
23 | */
24 | public function clear($key)
25 | {
26 | return delete_transient($key);
27 | }
28 |
29 | /**
30 | * @param string $key
31 | * @return bool
32 | */
33 | public function get($key)
34 | {
35 | return get_transient($key) ? : false;
36 | }
37 |
38 | /**
39 | * @param string $key
40 | * @param mixed $value
41 | * @param int $expiration
42 | * @return bool
43 | */
44 | public function set($key, $value, $expiration)
45 | {
46 | return set_transient($key, $value, $expiration);
47 | }
48 |
49 | /**
50 | * @return bool
51 | */
52 | public function isAvailable()
53 | {
54 | return apply_filters('ajax_template_force_transient', wp_using_ext_object_cache());
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/FileSystem.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP;
12 |
13 | /**
14 | * @author Giuseppe Mazzapica
15 | * @license http://opensource.org/licenses/MIT MIT
16 | * @package ATP
17 | */
18 | class FileSystem
19 | {
20 | /**
21 | * @return string
22 | */
23 | public function getFolder()
24 | {
25 | $upload = wp_upload_dir();
26 | $path = trailingslashit($upload['basedir']).'ajax_query_template/cache';
27 |
28 | return wp_mkdir_p($path) ? $path : '';
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Loader.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP;
12 |
13 | /**
14 | * @author Giuseppe Mazzapica
15 | * @license http://opensource.org/licenses/MIT MIT
16 | * @package ATP
17 | */
18 | class Loader
19 | {
20 | /**
21 | * @var \GM\ATP\Cache\Provider
22 | */
23 | private $cache;
24 |
25 | /**
26 | * @var string[]
27 | */
28 | private $output = [];
29 |
30 | /**
31 | * @var array
32 | */
33 | private $query_data = [];
34 |
35 | /**
36 | * @var array
37 | */
38 | private $templates_data = [];
39 |
40 | /**
41 | * @var array
42 | */
43 | private $posts_data = [];
44 |
45 | /**
46 | * Return data from AJAX request.
47 | */
48 | public function getData()
49 | {
50 | if (! defined('DOING_AJAX') || ! DOING_AJAX) {
51 | return;
52 | }
53 |
54 | $this->parseRequest();
55 |
56 | if (empty($this->templates_data)) {
57 | wp_send_json_error();
58 | }
59 |
60 | $this->getCache();
61 | $this->loadTemplates();
62 | $this->setCache();
63 | $this->output ? wp_send_json_success($this->output) : wp_send_json_error($this->output);
64 | }
65 |
66 | /**
67 | * @param \GM\ATP\Cache\Provider $cache
68 | */
69 | public function setCacheProvider(Cache\Provider $cache)
70 | {
71 | $this->cache = $cache;
72 | }
73 |
74 | /**
75 | * @return mixed
76 | */
77 | public function getCacheProvider()
78 | {
79 | return $this->cache;
80 | }
81 |
82 | /**
83 | * Parse HTTP request and collect request data.
84 | */
85 | private function parseRequest()
86 | {
87 | $request = filter_input_array(INPUT_POST, [
88 | 'files_data' => [
89 | 'filter' => FILTER_UNSAFE_RAW,
90 | 'flags' => FILTER_REQUIRE_ARRAY,
91 | ],
92 | 'query_data' => [
93 | 'filter' => FILTER_UNSAFE_RAW,
94 | 'flags' => FILTER_REQUIRE_ARRAY,
95 | ],
96 | 'posts_data' => [
97 | 'filter' => FILTER_SANITIZE_NUMBER_INT,
98 | 'flags' => FILTER_REQUIRE_ARRAY,
99 | ]
100 | ]);
101 |
102 | $this->query_data = $request['query_data'] ? : [];
103 | $this->templates_data = $request['files_data'];
104 | $posts_data = array_filter(array_unique($request['posts_data']));
105 | $this->posts_data = (count($posts_data) > 1) ? $request['posts_data'] : [];
106 | }
107 |
108 | /**
109 | * Load template parts, setting up context.
110 | */
111 | private function loadTemplates()
112 | {
113 | $this->buildGlobals();
114 | $this->output = [];
115 | foreach ($this->templates_data as $id => $template) {
116 | if (strpos($id, 'ajaxtemplate-') !== 0) {
117 | continue;
118 | }
119 | $data = $this->checkTemplateData($template);
120 | if (! $data) {
121 | $this->output[$id] = $this->loadTemplate($data, $id);
122 | }
123 | }
124 | }
125 |
126 | /**
127 | * Build global context.
128 | */
129 | private function buildGlobals()
130 | {
131 | global $wp, $wp_query, $wp_the_query, $wp_did_header;
132 | $wp_did_header = 1;
133 | $wp->init();
134 | $wp->query_vars = $this->query_data;
135 | $wp->query_posts();
136 | $wp->register_globals();
137 | isset($wp_the_query) or $wp_the_query = $wp_query;
138 | }
139 |
140 | /**
141 | * Sanitize template parts data from request.
142 | *
143 | * @param $template
144 | * @return array
145 | */
146 | private function checkTemplateData($template)
147 | {
148 | $data = array_values((array)$template);
149 | if (empty($data) || ! is_string($data[0])) {
150 | return [];
151 | }
152 | $args = [filter_var($data[0], FILTER_SANITIZE_STRING)];
153 | if (! empty($data) && is_string($data[1])) {
154 | $args[] = filter_var($data[1], FILTER_SANITIZE_STRING);
155 | }
156 |
157 | return array_filter($args);
158 | }
159 |
160 | /**
161 | * Load a single template part, by calling get_template_part().
162 | *
163 | * @param $args
164 | * @param $id
165 | * @return string
166 | */
167 | private function loadTemplate($args, $id)
168 | {
169 | if (isset($this->posts_data[$id]) && (int)$this->posts_data[$id] > 0) {
170 | $GLOBALS['post'] = get_post($this->posts_data[$id]);
171 | setup_postdata($GLOBALS['post']);
172 | }
173 | ob_start();
174 | @call_user_func_array('get_template_part', $args);
175 | $result = ob_get_clean();
176 |
177 | return $result;
178 | }
179 |
180 | /**
181 | * Get cached data if available.
182 | */
183 | private function getCache()
184 | {
185 | if (! $this->shouldCache()) {
186 | return;
187 | }
188 | if (! empty($this->posts_data)) {
189 | $this->query_data['posts_data'] = $this->posts_data;
190 | }
191 | $cached = $this->getCacheProvider()->get($this->templates_data, $this->query_data);
192 | if (! empty($cached) && is_array($cached)) {
193 | wp_send_json_success($cached);
194 | }
195 | }
196 |
197 | /**
198 | * Stores data in cache on shutdown.
199 | */
200 | private function setCache()
201 | {
202 | if (! $this->shouldCache() || empty($this->output)) {
203 | return;
204 | }
205 |
206 | add_action('shutdown', function () {
207 | $this->getCacheProvider()->set($this->output, $this->templates_data, $this->query_data);
208 | });
209 | }
210 |
211 | /**
212 | * Is cache available and enabled?
213 | *
214 | * @return bool
215 | */
216 | private function shouldCache()
217 | {
218 | $provider = $this->getCacheProvider();
219 |
220 | return $provider instanceof Cache\Provider && $provider->shouldCache();
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/Templater.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace GM\ATP;
12 |
13 | /**
14 | * @author Giuseppe Mazzapica
15 | * @license http://opensource.org/licenses/MIT MIT
16 | * @package ATP
17 | */
18 | class Templater
19 | {
20 | /**
21 | * @var \WP
22 | */
23 | private $wp;
24 |
25 | /**
26 | * @var \WP_Query
27 | */
28 | private $query;
29 |
30 | /**
31 | * @var string
32 | */
33 | private $path;
34 |
35 | /**
36 | * @var bool
37 | */
38 | private $debug;
39 |
40 | /**
41 | * @param \WP $wp
42 | * @param \WP_Query $query
43 | * @param string $path
44 | */
45 | public function __construct(\WP $wp, \WP_Query $query, $path)
46 | {
47 | $this->wp = $wp;
48 | $this->query = $query;
49 | $this->path = $path;
50 | $this->debug = defined('WP_DEBUG') && WP_DEBUG;
51 | }
52 |
53 | /**
54 | * @param string $raw_name
55 | * @param string $raw_slug
56 | * @param string $content
57 | * @return static
58 | */
59 | public function addHtml($raw_name, $raw_slug, $content = '')
60 | {
61 | static $n = 0;
62 | $n++;
63 | $name = filter_var($raw_name, FILTER_SANITIZE_URL);
64 | $slug = filter_var($raw_slug, FILTER_SANITIZE_URL);
65 | $ajaxTemplate = urlencode(json_encode(['name' => $name, 'slug' => $slug]));
66 |
67 | global $post;
68 | $post_attr = '';
69 | $post instanceof \WP_Post and $post_attr = sprintf(' data-post="%d"', $post->ID);
70 | empty($content) and $content = $this->htmlContent($name, $slug);
71 |
72 | $class = $this->htmlClass($name, $slug);
73 | $attr = empty($content) && empty($class) ? ' style="display:none!important;"' : '';
74 | $id = md5($name.$slug.$n);
75 | $attr .= " id=\"ajaxtemplate-{$id}\"";
76 | $format = '%s';
77 | printf($format, $attr, $post_attr, $ajaxTemplate, $class, $content);
78 |
79 | return $this;
80 | }
81 |
82 | /**
83 | * @return static
84 | */
85 | public function addJs()
86 | {
87 | if (wp_script_is('ajax-template-part', 'queue')) {
88 | return $this;
89 | }
90 | $args = ['ajax-template-part', $this->jsUrl(), ['jquery'], $this->jsVer(), true];
91 | call_user_func_array('wp_enqueue_script', $args);
92 | $ajax_url = apply_filters('ajax_template_ajax_url', admin_url('admin-ajax.php'));
93 | $data = [
94 | 'info' => [
95 | 'ajax_url' => $ajax_url,
96 | 'query_data' => $this->wp->query_vars,
97 | ],
98 | ];
99 | wp_localize_script('ajax-template-part', 'AjaxTemplatePartData', $data);
100 |
101 | return $this;
102 | }
103 |
104 | /**
105 | * @param string $name
106 | * @param string $slug
107 | * @return string
108 | */
109 | private function htmlClass($name, $slug)
110 | {
111 | $class = apply_filters('ajax_template_loading_class', '', $name, $slug, $this->query);
112 | if (! is_string($class) || empty($class)) {
113 | return '';
114 | }
115 |
116 | return sprintf(' class="%s"', esc_attr(trim($class)));
117 | }
118 |
119 | /**
120 | * @param string $name
121 | * @param string $slug
122 | * @return string
123 | */
124 | private function htmlContent($name, $slug)
125 | {
126 | $content = apply_filters('ajax_template_loading_content', '', $name, $slug, $this->query);
127 | if (! is_string($content) || empty($content)) {
128 | return '';
129 | }
130 |
131 | return esc_html($content);
132 | }
133 |
134 | /**
135 | * @return string
136 | */
137 | private function jsUrl()
138 | {
139 | $min = $this->debug ? '' : '.min';
140 |
141 | return plugins_url("/js/atp{$min}.js", $this->path);
142 | }
143 |
144 | /**
145 | * @return string
146 | */
147 | private function jsVer()
148 | {
149 | if ($this->debug) {
150 | return (string)time();
151 | }
152 |
153 | return @filemtime(dirname($this->path).'/js/atp.min.js') ? : null;
154 | }
155 | }
156 |
--------------------------------------------------------------------------------