Comet Cache - Version 160211

Version Description

= v160211 =

Requires PHP v5.4+. The latest version of Comet Cache is a complete rewrite (OOP design). Faster! and even more dependable. For further details, please see http://cometcache.com/new-minimum-php-version-php-5-4/

Download this release

Release Info

Developer raamdev
Plugin Icon 128x128 Comet Cache
Version 160211
Comparing to
See all releases

Version 160211

Files changed (124) hide show
  1. CHANGELOG.md +534 -0
  2. LICENSE.txt +676 -0
  3. comet-cache.php +17 -0
  4. plugin.php +66 -0
  5. readme.txt +509 -0
  6. src/client-s/css/admin-bar.min.css +2 -0
  7. src/client-s/css/menu-pages.min.css +2 -0
  8. src/client-s/images/auto-clear-ss.png +0 -0
  9. src/client-s/images/bg.png +0 -0
  10. src/client-s/images/clear-cache-ops0-ss.png +0 -0
  11. src/client-s/images/clear-cache-ops1-ss.png +0 -0
  12. src/client-s/images/clear-cache-ops2-ss.png +0 -0
  13. src/client-s/images/clear-response.png +0 -0
  14. src/client-s/images/clear.png +0 -0
  15. src/client-s/images/cloudfront-logo.png +0 -0
  16. src/client-s/images/gzip.png +0 -0
  17. src/client-s/images/inline-icon-logo.svg +5 -0
  18. src/client-s/images/inline-icon.svg +5 -0
  19. src/client-s/images/keycdn-logo.png +0 -0
  20. src/client-s/images/maxcdn-logo.png +0 -0
  21. src/client-s/images/opcache.png +0 -0
  22. src/client-s/images/options-lite.png +0 -0
  23. src/client-s/images/salt.png +0 -0
  24. src/client-s/images/source-code-ss.png +0 -0
  25. src/client-s/images/spinner.png +0 -0
  26. src/client-s/images/tach.png +0 -0
  27. src/client-s/images/wipe-response.png +0 -0
  28. src/client-s/images/wipe.png +0 -0
  29. src/client-s/js/menu-pages.js +191 -0
  30. src/client-s/js/menu-pages.min.js +7 -0
  31. src/includes/api.php +16 -0
  32. src/includes/classes/AbsBase.php +233 -0
  33. src/includes/classes/AbsBaseAp.php +59 -0
  34. src/includes/classes/Actions.php +295 -0
  35. src/includes/classes/AdvCacheBackCompat.php +67 -0
  36. src/includes/classes/AdvancedCache.php +64 -0
  37. src/includes/classes/ApiBase.php +123 -0
  38. src/includes/classes/Conflicts.php +102 -0
  39. src/includes/classes/FeedUtils.php +273 -0
  40. src/includes/classes/MenuPage.php +34 -0
  41. src/includes/classes/MenuPageOptions.php +1056 -0
  42. src/includes/classes/Plugin.php +486 -0
  43. src/includes/classes/VsUpgrades.php +200 -0
  44. src/includes/closures/Ac/AbortUtils.php +11 -0
  45. src/includes/closures/Ac/AcPluginUtils.php +25 -0
  46. src/includes/closures/Ac/BrowserUtils.php +34 -0
  47. src/includes/closures/Ac/NcDebugConsts.php +293 -0
  48. src/includes/closures/Ac/NcDebugUtils.php +209 -0
  49. src/includes/closures/Ac/ObUtils.php +353 -0
  50. src/includes/closures/Ac/PostloadUtils.php +187 -0
  51. src/includes/closures/Ac/ShutdownUtils.php +18 -0
  52. src/includes/closures/Plugin/ActionUtils.php +16 -0
  53. src/includes/closures/Plugin/BbPressUtils.php +61 -0
  54. src/includes/closures/Plugin/CleanupUtils.php +19 -0
  55. src/includes/closures/Plugin/CondUtils.php +13 -0
  56. src/includes/closures/Plugin/CronUtils.php +80 -0
  57. src/includes/closures/Plugin/DbUtils.php +13 -0
  58. src/includes/closures/Plugin/DirUtils.php +79 -0
  59. src/includes/closures/Plugin/HtaccessUtils.php +273 -0
  60. src/includes/closures/Plugin/InstallUtils.php +535 -0
  61. src/includes/closures/Plugin/MenuPageUtils.php +215 -0
  62. src/includes/closures/Plugin/NoticeUtils.php +296 -0
  63. src/includes/closures/Plugin/OptionUtils.php +70 -0
  64. src/includes/closures/Plugin/PostUtils.php +42 -0
  65. src/includes/closures/Plugin/UpdateUtils.php +46 -0
  66. src/includes/closures/Plugin/UrlUtils.php +22 -0
  67. src/includes/closures/Plugin/UserUtils.php +118 -0
  68. src/includes/closures/Plugin/WcpAuthorUtils.php +92 -0
  69. src/includes/closures/Plugin/WcpCommentUtils.php +96 -0
  70. src/includes/closures/Plugin/WcpFeedUtils.php +116 -0
  71. src/includes/closures/Plugin/WcpHomeBlogUtils.php +101 -0
  72. src/includes/closures/Plugin/WcpJetpackUtils.php +24 -0
  73. src/includes/closures/Plugin/WcpOpcacheUtils.php +69 -0
  74. src/includes/closures/Plugin/WcpPluginUtils.php +22 -0
  75. src/includes/closures/Plugin/WcpPostTypeUtils.php +66 -0
  76. src/includes/closures/Plugin/WcpPostUtils.php +176 -0
  77. src/includes/closures/Plugin/WcpSettingUtils.php +31 -0
  78. src/includes/closures/Plugin/WcpSitemapUtils.php +47 -0
  79. src/includes/closures/Plugin/WcpTermUtils.php +139 -0
  80. src/includes/closures/Plugin/WcpUpdaterUtils.php +99 -0
  81. src/includes/closures/Plugin/WcpUtils.php +341 -0
  82. src/includes/closures/Plugin/WcpWooCommerceUtils.php +24 -0
  83. src/includes/closures/Shared/BlogUtils.php +31 -0
  84. src/includes/closures/Shared/CacheDirUtils.php +643 -0
  85. src/includes/closures/Shared/CacheLockUtils.php +77 -0
  86. src/includes/closures/Shared/CachePathConsts.php +140 -0
  87. src/includes/closures/Shared/CachePathUtils.php +349 -0
  88. src/includes/closures/Shared/ConditionalUtils.php +327 -0
  89. src/includes/closures/Shared/DomainMappingUtils.php +264 -0
  90. src/includes/closures/Shared/EscapeUtils.php +17 -0
  91. src/includes/closures/Shared/FsUtils.php +323 -0
  92. src/includes/closures/Shared/HookUtils.php +249 -0
  93. src/includes/closures/Shared/HttpUtils.php +166 -0
  94. src/includes/closures/Shared/I18nUtils.php +44 -0
  95. src/includes/closures/Shared/IpAddrUtils.php +83 -0
  96. src/includes/closures/Shared/PatternUtils.php +48 -0
  97. src/includes/closures/Shared/ReplaceUtils.php +43 -0
  98. src/includes/closures/Shared/ServerUtils.php +65 -0
  99. src/includes/closures/Shared/StringUtils.php +87 -0
  100. src/includes/closures/Shared/SysUtils.php +92 -0
  101. src/includes/closures/Shared/TokenUtils.php +300 -0
  102. src/includes/closures/Shared/TrimUtils.php +36 -0
  103. src/includes/closures/Shared/UrlUtils.php +131 -0
  104. src/includes/functions/i18n-utils.php +22 -0
  105. src/includes/functions/wp-cache-postload.php +33 -0
  106. src/includes/plugin.php +17 -0
  107. src/includes/stub.php +32 -0
  108. src/includes/templates/ac-plugin.txt +27 -0
  109. src/includes/templates/advanced-cache.txt +225 -0
  110. src/includes/templates/gzip-htaccess.txt +10 -0
  111. src/includes/templates/htaccess/back-compat/v151114-2.txt +2 -0
  112. src/includes/templates/htaccess/back-compat/v151114.txt +7 -0
  113. src/includes/translations/comet-cache.pot +1881 -0
  114. src/includes/uninstall.php +18 -0
  115. src/vendor/autoload.php +7 -0
  116. src/vendor/composer/ClassLoader.php +413 -0
  117. src/vendor/composer/LICENSE +21 -0
  118. src/vendor/composer/autoload_classmap.php +9 -0
  119. src/vendor/composer/autoload_namespaces.php +9 -0
  120. src/vendor/composer/autoload_psr4.php +13 -0
  121. src/vendor/composer/autoload_real.php +50 -0
  122. src/vendor/composer/installed.json +340 -0
  123. src/vendor/websharks/css-minifier/CHANGELOG.md +3 -0
  124. src/vendor/websharks/css-minifier/LICENSE.txt +664 -0
CHANGELOG.md ADDED
@@ -0,0 +1,534 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ = v160211 =
2
+
3
+ **Announcing Comet Cache, formerly ZenCache!**
4
+
5
+ We are very excited to announce the release of [Comet Cache](http://cometcache.com), an advanced WordPress caching plugin inspired by simplicity. Comet Cache is the successor to ZenCache (and Quick Cache before that), a very popular WordPress caching plugin that has been downloaded over 1 million times and has won acclaim for its speed, simplicity, and ease of configuration. [Read the full announcement here](https://cometcache.com/r/announcing-comet-cache-formerly-zencache/).
6
+
7
+ = v160103 =
8
+
9
+ - **Bug Fix**: Fixed an issue that was unexpectedly producing "Failed to update your /.htaccess file" error messages. The `.htaccess` routines are now more intelligent and take into consideration which plugin options are enabled and which options require updating the `.htaccess` file. This also improves performance by avoiding unnecessary read/writes to the `.htaccess` file. Props @patdumond. See [Issue #641](https://github.com/websharks/zencache/issues/641).
10
+ - **Bug Fix**: When `allow_url_fopen` is disabled via the PHP configuration, the Auto-Cache Engine is unable to read the XML Sitemap and was silently failing with only PHP Warning in the PHP error log. The Auto-Cache Engine currently requires [PHP URL-aware fopen wrappers](http://zencache.com/r/allow_url_fopen/). A new Dashboard notice displays an error message when `allow_url_fopen` is disabled and prevents the Auto-Cache Engine from attempting to run. See [Issue #644](https://github.com/websharks/zencache/issues/644).
11
+ - **Bug Fix**: Fixed an Auto-Cache Engine bug that was producing false-positive Dashboard errors related to timeouts: "Problematic XML Sitemap URL - WP_Http says: Operation timed out after 5001 milliseconds with 0 bytes received." Due to the way ZenCache was checking the XML Sitemap URL more than necessary, timeouts were more likely to occur. We now only check the URL repeatedly when a failure has been encountered. If the URL is confirmed as working, we don't check the URL again until the Auto-Cache Engine runs (every 15 minutes by default) or until the Plugin Options are saved. If you are still seeing timeout errors after this update, please see [this article](http://zencache.com/kb-article/why-am-i-seeing-auto-cache-engine-timeout-errors/). See [Issue #643](https://github.com/websharks/zencache/issues/643).
12
+ - **Bug Fix**: Fixed an Auto-Cache Engine bug where ZenCache would would check both the non-SSL (`http://`) and the SSL (`https://`) version of the XML Sitemap URL when the Site Address was configured to use SSL. See [Issue #643](https://github.com/websharks/zencache/issues/643).
13
+ - **Enhancement**: It's now possible to override the ZenCache Nonce exclusion globally (dangerous) or only for Logged-In Users (safer). Please see [this article](http://zencache.com/r/kb-article-what-are-wordpress-nonces-and-why-are-they-not-cache-compatible/) for full details. [Issue #637](https://github.com/websharks/zencache/issues/637).
14
+ - **Akismet Compatibility**: Fixed a bug with Akismet compatibility where ZenCache was not properly disabling the Akismet Comment Nonce, which resulted in pages being unnecessarily excluded from the cache due to the presence of the `akismet_comment_nonce` in the markup. Props @Kalfer. See [Issue #642](https://github.com/websharks/zencache/issues/642).
15
+
16
+ = v151220 =
17
+
18
+ - **New Feature!**: It's now possible to customize the Cache Cleanup Schedule and set your own schedule in _ZenCache → Plugin Options → Manual Cache Clearing → Cache Cleanup Schedule_. ZenCache uses [`wp_get_schedules()`](https://codex.wordpress.org/Function_Reference/wp_get_schedules) when generating the list of available schedules, which makes it possible to create your own custom cron schedule using a plugin like WP Crontrol and use that to define a custom Cache Cleanup Schedule. Props @kristineds. See [Issue #472](https://github.com/websharks/zencache/issues/472).
19
+ - **New Feature!** It's now possible to configure the Pro Plugin Updater to check for Beta versions (Release Candidates) of the plugin. See _ZenCache → Plugin Updater → Beta Testers_. We publish Release Candidates a week or so before official releases to allow for additional testing and early preview of upcoming features and bug fixes. If you're not already on our Beta Testers mailing list, you can signup [here](http://zencache.com/r/zencache-beta-testers-list/). Props @kristineds. See [Issue #352](https://github.com/websharks/zencache/issues/352).
20
+ - **New Feature!** It's now possible to clear [Expired WordPress Transients](https://codex.wordpress.org/Transients_API) from the Clear Cache Option Menu in the WordPress Admin Bar. The WordPress Transients API has no functionality to automatically clean up expired transients; doing so is left for plugin authors and we've found that very few plugins that use the Transient API actually clean up expired transients properly, which can lead to your database being full of expired transient data that is no longer used. Props @kristineds. See [Issue #459](https://github.com/websharks/zencache/issues/459).
21
+ - **New Feature!** Client-Side Caching now includes a new URI Exclusion Patterns for Client-Side Caching, allowing you to exclude specific URIs from being cached by a client-side browser when Client-Side Caching is enabled. Props @renzms. See [Issue #498](https://github.com/websharks/zencache/issues/498).
22
+ - **Bug Fix**: Fixed a bug that was generating a "Failed to update your `/.htaccess` file" error message. The routines that update your `.htaccess` file were failing if your `.htaccess` file already contained the word "ZenCache" (case-insensitive). See [Issue #617](https://github.com/websharks/zencache/issues/617) and [Issue #620](https://github.com/websharks/zencache/issues/620).
23
+ - **Bug Fix**: Fixed a bug that in some scenarios could cause custom `.htaccess` rules to be lost if your `.htaccess` file was using the exact same comment start and end markers as ZenCache (`# BEGIN ZenCache` and `# END ZenCache`). If you were using those exact same start and end markers, ZenCache was replacing whatever was between them with a copy of the ZenCache `.htaccess` rules. ZenCache now uses a unique marker (`WmVuQ2FjaGU`) to identify its own code blocks inside your `.htaccess` file. See [Issue #620](https://github.com/websharks/zencache/issues/620).
24
+ - **Bug Fix**: Fixed a bug with clearing cache files for paginated pages where the `pagination_base` had been changed from the default `page` to something else (e.g., a translated string). The WP Rewrite API is now used to include `pagination_base` and `comments_pagination_base` when building paths to cache files to determine which cache files should be cleared. Props @renzms. See [Issue #607](https://github.com/websharks/zencache/issues/607).
25
+ - **Bug Fix**: Fixed a bug with the Static CDN Filters that affected sites using a permalink structure that ended with `.htm` or `.html`. This bug was supposed to be fixed back in v151002 but another bug prevented the fix from working properly. This release fixes the issue once and for all. See [Issue #495](https://github.com/websharks/zencache/issues/495#issuecomment-160324313).
26
+ - **Bug Fix**: With Logged-In User caching enabled, there were some scenarios where the user cache was being cleared on every page load when a user was logged in. ZenCache now hooks into `updated_user_metadata` instead of `update_user_metadata` to clear a specific user cache. Props @kristineds. See [Issue #623](https://github.com/websharks/zencache/issues/623).
27
+ - **Bug Fix**: Fixed a bug where the Auto-Cache Engine cron event would disappear in some scenarios. Props @patdumond, @MarioKnight. See [Issue #613](https://github.com/websharks/zencache/issues/613).
28
+ - **Bug Fix**: ZenCache no longer caches any pages that contain `_wpnonce` in the markup. This ensures that pages containing time-sensitive `nonce` values are not cached. Props @kristineds and @jaswsinc. See [Issue #601](https://github.com/websharks/zencache/issues/601).
29
+ - **Bug Fix**: This release attempts to resolve reports of Error 500 and Internal Server Error issues when running with PHP 5.6 + OPcache. Instead of clearing the entire Opcode cache whenever the plugin options are saved, we only invalidate the `advanced-cache.php` file, which is rebuilt each time you update your configuration options. Props @jaswsinc. Also props to @MarioKnight and @CotswoldPhoto for helping us narrow this down. See [Issue #624](https://github.com/websharks/zencache/issues/624).
30
+ - **Bug Fix**: Fixed a bug where ZenCache could inadvertently corrupt the `.htaccess` file if it contained invalid UTF-8 characters. This has been fixed by first checking the `.htaccess` file for invalid UTF-8 characters via `wp_check_invalid_utf8()`. See [Issue #633](https://github.com/websharks/zencache/issues/633#issuecomment-165493039).
31
+ - **Enhancement**: Improved WP Cron-related configuration and validation of custom cron schedules. See [PR #197](https://github.com/websharks/zencache-pro/pull/197).
32
+ - **Enhancement**: ZenCache now clears the cache whenever a WordPress plugin is activated or deactivated. Many WordPress plugins change content on the front-end of the site, so this enhancement ensures that an old cache file is never served to visitors. If you want to disable this automatic behavior, see [this article](http://zencache.com/kb-article/how-do-i-prevent-zencache-from-clearing-the-cache-upon-plugin-activation-or-deactivation/). Props @renzms. See [Issue #424](https://github.com/websharks/zencache/issues/424).
33
+ - **Enhancement**: The Auto-Cache Engine diagnostic reporting for XML Sitemap-related errors has been greatly improved. The Dashboard notice now explains exactly what error is occurring when the Auto-Cache Engine attempts to verify that the XML Sitemap exists. Props @jaswsinc. See [Issue #615](https://github.com/websharks/zencache/issues/615) and [Issue #618](https://github.com/websharks/zencache/issues/618).
34
+ - **Enhancement**: Auto-Cache Engine XML Sitemap error checking is now more intelligent. When configured, the XML Sitemap URL is now checked upon every `admin_init` to verify that the XML Sitemap is accessible and valid. If a previous error has been fixed, the error message will disappear immediately instead of taking 15 minutes (the Auto-Cache Engine run cycle). See [Issue #616](https://github.com/websharks/zencache/issues/616).
35
+ - **Enhancement**: Static CDN Filters are no longer applied by default for Logged-In Users. This was causing some confusion because Logged-In User caching is disabled by default. There is no harm in applying Static CDN Filters to all users (logged-in or not logged-in), in fact we recommend it, however we now disable this functionality by default to avoid confusion. A new option allows you to control this behavior and enable Static CDN Filters for Logged-In users; see _ZenCache → Logged-In Users → Static CDN Filters Enabled for Logged-In Users & Comment Authors?_ Props @kristineds, @lkraav, and @isaumya. See [Issue #486](https://github.com/websharks/zencache/issues/486).
36
+ - **Enhancement**: The Static CDN Filters now attempt to obey the `ZENCACHE_ALLOWED` and `DONOTCACHEPAGE` constants so that Static CDN Filters are not applied when caching is disabled. However, due to the way Static CDN Filters are implemented this is not always reliable. If you need to programmatically disable Static CDN Filters, see [this article](http://zencache.com/kb-article/how-do-i-disable-static-cdn-filters-via-php/). See [Issue #628](https://github.com/websharks/zencache/issues/628).
37
+ - **Enhancement**: When activating ZenCache for the first time, a new Dashboard message now includes a helpful link to the options page to enable caching and review the options. Props @kristineds. See [Issue #112](https://github.com/websharks/zencache/issues/112).
38
+ - **Enhancement**: The automatic Cache Cleanup schedule that cleans up (deletes) expired/stale cache files has been changed from once every day to once every hour. Running the cleanup hourly makes ZenCache smarter when configured in certain ways and saves disk space. Props @kristineds. See [Issue #472](https://github.com/websharks/zencache/issues/472).
39
+ - **Enhancement:** The new default value for "Clear the PHP OPcache Too?" when clearing the cache manually is now disabled. Instead of having this option enabled by default, we automatically invalidate only the `advanced-cache.php` file in PHP's opcode cache whenever you update your configuration options, which allows ZenCache to function as expected. That was the most important reason for needing to clear the opcode cache in previous versions. Props @jaswsinc. Also props to @MarioKnight and @CotswoldPhoto for helping us narrow this down. See [Issue #624](https://github.com/websharks/zencache/issues/624).
40
+ - **Enhancement**: Improved `.htaccess` utilities so that an exclusive lock is acquired when reading+writing to the file. This helps avoid inadvertently corrupting the `.htaccess` file in scenarios where another process might be reading/writing to it at the same time. See [Issue #633](https://github.com/websharks/zencache/issues/633).
41
+ - **Multisite Enhancement**: The Auto-Cache Engine has a new configuration option when running on WP Multisite Networks that allows you to define whether or not the Auto-Cache Engine should look for XML Sitemaps on each of the child blogs. This now defaults to being off; i.e., by default, child blogs are no longer checked. Props @jaswsinc. See [Issue #618](https://github.com/websharks/zencache/issues/618#issuecomment-158695842).
42
+ - **Multisite Enhancement**: The Auto-Cache Engine XML Sitemap diagnostic reporting is now always disabled for child blogs and only errors related to a primary sitemap location are displayed on the Dashboard. Props @jaswsinc. See [Issue #618](https://github.com/websharks/zencache/issues/618#issuecomment-158695842).
43
+ - **bbPress Compatibility:** This release improves compatibility with bbPress when ZenCache Logged-In User caching is enabled. In this scenario, bbPress may generate links for Admins that contain time-sensitive `_wpnonce` values which could expire if cached and result in certain admin-related actions failing. ZenCache no longer caches pages that contain `_wpnonce` in the markup. This ensures that pages containing time-sensitive `nonce` values are not cached. Props @kristineds, @jaswsinc, and @clavaque. See [Issue #601](https://github.com/websharks/zencache/issues/601).
44
+ - **Akismet Compatibility:** ZenCache no longer caches pages that contain `akismet_comment_nonce` in the markup. This ensures that a page that contains a time-sensitive `nonce` value does not get cached. Props @kristineds and @jaswsinc. See [Issue #601](https://github.com/websharks/zencache/issues/601).
45
+ - **Akismet Compatibility:** ZenCache now automatically disables the Akismet Comment Nonce using [the `akismet_comment_nonce` filter](https://github.com/git-mirror/wordpress-akismet/blob/2.5.6/akismet.php#L333), which improves compatibility between Akismet and the page caching functionality provided by ZenCache. This ensures that pages do not contain time-sensitive `nonce` values that should not be cached. If you'd like to revert this behavior, please see [this article](http://zencache.com/kb-article/how-do-i-prevent-zencache-from-disabling-the-akismet-comment-nonce/). Props @kristineds and @jaswsinc. See [Issue #601](https://github.com/websharks/zencache/issues/601).
46
+ - **WooCommerce Compatibility:** This release improves compatibility with the WooCommerce Product Stock feature. When the Product Stock changes, ZenCache will now clear the cache file for the associated Product to ensure that the stock quantity that appears on the site remains up-to-date. See [Issue #597](https://github.com/websharks/zencache/issues/597).
47
+
48
+ = v151114 =
49
+
50
+ - **Announcement: The next version of ZenCache will require PHP 5.4+.** As of December 1st, 2015, the minimum PHP version required to run ZenCache will change to PHP 5.4+. This release of ZenCache will be the last version to support PHP 5.3. Please see announcement with further details: [New Minimum PHP Version: PHP 5.4](http://zencache.com/r/new-minimum-php-version-php-5-4/)
51
+ - **Announcement: The next version of ZenCache will not support the PHP APC Extension**. As of December 1st, 2015, ZenCache will no longer run with the PHP APC extension enabled. Please see announcement with further details: [PHP APC Extension No Longer Supported](http://zencache.com/r/php-apc-extension-no-longer-supported/)
52
+ - **New Feature!** The Clear Cache button in the Admin Bar now includes a sub-menu with several new options for clearing the cache from anywhere on your site. You can clear the cache for just the Home Page, the Current URL, a Specific URL, PHP's OPCache (if active), or the CDN Cache (when Static CDN Filters are configured). This menu comes in two flavors and can be customized (or disabled entirely) inside _ZenCache → Plugin Options → Manual Cache Clearing_. Props @jaswsinc. See [Issue #596](https://github.com/websharks/zencache/issues/596#issuecomment-152786080).
53
+ - **New Feature!** It's now possible to specify a list of Custom URLs whose cache files should be cleared whenever you update a Post/Page, approve a Comment, or make other changes where ZenCache detects that a Post/Page cache should be cleared to keep your site up-to-date. See _ZenCache → Plugin Options → Automatic Cache Clearing → Misc. Auto-Clear Options → Auto-Clear a List of Custom URLs Too?_ Props @kristineds. See [Issue #111](https://github.com/websharks/zencache/issues/111).
54
+ - **New Feature!** A new watered-down Regular Expression syntax is now supported in several existing ZenCache features, including the new list of Custom URLs to Auto-Clear, URI Exclusion Patterns, HTTP Referrer Exclusion Patterns, User-Agent Exclusion Patterns, and the HTML Compressor CSS Exclusion Patterns and JavaScript Exclusion Patterns. This new syntax greatly increases the power and flexibility of each of these features and makes things possible like the much-requested ability to Auto-Clear the Home Page or Posts Page of a site whenever a post cache is cleared. For more information on this new watered-down Regular Expression syntax, [this KB Article](http://zencache.com/r/watered-down-regex-syntax/). Props @kristineds @jaswsinc. See [Issue #191](https://github.com/websharks/zencache/issues/191).
55
+ - **Bug Fix**: Fixed a bug with Static CDN Filters and Cross-Origin Resource Sharing (CORS) that was generating a "Cross-Origin Request Blocked" error. ZenCache will now update the root `.htaccess` file to include a `Header set Access-Control-Allow-Origin "*"` rule for `ttf`, `ttc`, `otf`, `eot`, `woff`, `woff2`, `font.css`, `css`, and `js` files whenever the Static CDN Filters are enabled. This is necessary to avoid "Cross-Origin Request Blocked" errors. Note that if you are already experiencing this error, you should create and configure a new CDN hostname to resolve the issue. In our tests it appears that some CDNs are caching the initial header response received by the server, which means it's necessary to send the `Access-Control-Allow-Origin` header _before_ configuring the Static CDN Filters with a CDN hostname. See [Issue #427](https://github.com/websharks/zencache/issues/427#issuecomment-121774596).
56
+ - **Bug Fix**: Removed `eot,ttf,otf,woff` font extensions from the Static CDN Filter Blacklisted Extensions. These were added in a previous release in an attempt to resolve an issue with Cross-Origin Resource Sharing (CORS), however now that the HTML Compressor has been updated to work with Static CDN Filters, the CSS compressed by the HTML Compressor is now served from the CDN. Fonts are most likely to be referenced by CSS, which is static. So when Static CDN Filters are applied, the CSS is getting moved to the CDN and the fonts are then expected to live on the CDN too. By excluding them from the Static CDN Filter, we were creating a problem instead of solving one. This release removes the font extensions from the default Blacklisted Extensions so that fonts can be hosted on the CDN alongside the CSS that references them. See [Issue #427](https://github.com/websharks/zencache/issues/427#issuecomment-121777790).
57
+ - **Bug Fix**: Fixed a bug related to updating plugins with WP-CLI on a site that was running ZenCache Pro. While ZenCache Pro updates must still be done through the ZenCache Pro Updater inside the Dashboard, updating other plugins via WP-CLI was generating a harmless ZenCache exception: "Invalid argument; host token empty!". With this fix, ZenCache will properly detect when WP-CLI is running to avoid these errors. Props @MarioKnight @renzms. See [Issue #563](https://github.com/websharks/zencache/issues/563).
58
+ - **Bug Fix**: Fixed a bug where post previews were being cached when Logged-In User Caching and GET Request caching were both enabled (both are disabled by default). This release now detects previews in this scenario and excludes those requests from being cached. Props @clavaque @kristineds. See [Issue #583](https://github.com/websharks/zencache/issues/583).
59
+ - **Bug Fix**: Fixed an issue where a PHP Notice was generated when an inactive WordPress component was being upgraded. This issue did not have any adverse affect on the site, but this fix resolves the issue so that the notice won't appear in PHP logs. See [Issue #589](https://github.com/websharks/zencache/issues/589).
60
+ - **Bug Fix**: Fixed a bug where a commented-out `WP_CACHE` definition in `wp-config.php` (such as what WP Super Cache leaves behind) was being incorrectly ignored and resulted in caching being silently disabled. Props @davidfavor. See [Issue #591](https://github.com/websharks/zencache/issues/591).
61
+ - **Bug Fix**: Fixed a bug where in some scenarios a page might not be cached due to a stray `AUTH_COOKIE` or `SECURE_AUTH_COOKIE` cookie, even when the user is not logged in. Props @jaswsinc @renzms. See [Issue #592](https://github.com/websharks/zencache/issues/592).
62
+ - **Enhancement**: Excluded several unnecessary files from the plugin zip file that were being used during the build process but were not necessary to run the plugin. Props @bridgeport. See [Issue #579](https://github.com/websharks/zencache/issues/579).
63
+ - **Enhancement**: When the Cache Directory location is changed, ZenCache now cleans up the old Cache Directory and any old cache files instead of leaving them behind. Props @clavaque @renzms. See [Issue #580](https://github.com/websharks/zencache/issues/580).
64
+ - **Enhancement**: Added a new API Function that allows a site owner to clear the cache for a specific URL via `zencache::clearUrl($url);`. See [this article](http://zencache.com/kb-article/clearing-the-cache-dynamically/#toc-988085ad) for further details. Props @kristineds. See [Issue #590](https://github.com/websharks/zencache/issues/590).
65
+ - **Enhancement**: Improved the HTML Notes generated by ZenCache when s2Member (a membership plugin for WordPress) is specifically disabling caching. s2Member automatically disables caching on certain pages that are required to remain dynamic. The HTML Notes generated by ZenCache now explain when this is happening. Props @renzms. See [Issue #504](https://github.com/websharks/zencache/issues/504).
66
+ - **Enhancement**: In Logged-In User Caching, the "Yes, but DON'T maintain a separate cache for each user" option has been hidden because this particular option has the potential to create a security issue if not configured properly. If you were already using this option, it will not be hidden and it will continue to work. Otherwise, if you require this particular option you can now enable it using a filter (see [this comment](https://github.com/websharks/zencache/issues/497#issuecomment-146060440)). Props @renzms @jaswsinc. See [Issue #497](https://github.com/websharks/zencache/issues/497).
67
+ - **Enhancement**: The Auto-Cache Engine now detects when the configured XML Sitemap is not valid or is unreachable and displays an appropriate notice. Props @kristineds and @jaswsinc. See [Issue #555](https://github.com/websharks/zencache/issues/555).
68
+
69
+ = v151004 =
70
+
71
+ - **Bug Fix**: Fixed a bug introduced in the previous release that was resulting in a "Fatal error: Using $this when not in object context" for sites running PHP 5.3. (PHP 5.4+ sites were unaffected.) Props @jaswsinc. See [Issue #581](https://github.com/websharks/zencache/issues/581).
72
+
73
+ = v151002 =
74
+
75
+ - **New Feature!** Cache Statistics is a completely new ZenCache Pro feature that will help site owners better understand their WordPress site cache. An easy-to-access Cache Stats menu button in the Admin Bar is accompanied by a whole new page that shows Current Cache Totals (including total number of cache files and total size of cache on the disk), Current Disk Health (including total disk capacity and total available), Current System Health (including memory usage and load average), and two beautiful Polar Area pie charts that show you both current and historical data on Cache File Counts and Cache File Sizes with a 30-Day High, Current Total, Page Cache total, and HTML Compressor total for each chart. Props to @jaswsinc. See [Issue #83](https://github.com/websharks/zencache/issues/83).
76
+ - **New Feature!** It's now possible to specify which WordPress Roles/Capabilities are allowed to clear the cache from the WordPress Admin bar. A new option inside _Dashboard → ZenCache → Plugin Options → Manual Cache Clearing_ allows specifying a comma-delimited list of Roles and/or Capabilities under, "Also allow others to clear the cache from their Admin Bar?". If a user has an allowed Role/Capability, they will see the "Clear Cache" button in their Admin Bar. This feature is also compatible with WordPress Multisite Networks, allowing a Network Administrator to define which Child Site roles should allow a Child Site user to see the "Clear Cache" button for their Child Site. Props @danielmt2k @jaswsinc. See [Issue #68](https://github.com/websharks/zencache/issues/68).
77
+ - **New Feature!** It's now possible to "Disable Cache Expiration If Server Load Average is High" (see _Dashboard → ZenCache → Plugin Options → Cache Expiration Time_). This allows you to provide a specific load average that should cause ZenCache to disable cache expiration and help reduce stress on the server; i.e., to avoid generating a new version of the cache while the server is very busy. Props @jaswsinc. See [Issue #347](https://github.com/websharks/zencache/issues/347).
78
+ - **New Feature!** It's now possible to manually clear the CDN Cache when Static CDN Filters are enabled. Inside the Static CDN Filters options panel there's a new "Clear CDN Cache" button and there's also a new option ("Clear the CDN Cache Too?") inside _Dashboard → ZenCache → Plugin Options → Manual Cache Clearing_ that allows you to specify whether or not you'd like the CDN Cache to be cleared whenever the "Clear Cache" button is clicked (either from the Admin Bar or from inside the Plugin Options). Props @kristineds @jaswsinc. See [Issue #488](https://github.com/websharks/zencache/issues/488).
79
+ - **New Feature!** When your server has the [PHP OPCache Extension](http://zencache.com/r/php-opcache/) installed, ZenCache can now be configured to also clear the PHP opcode cache whenever you clear the cache manually using the ZenCache "Clear Cache" button. See _ZenCache Options → Manual Cache Clearing → Clear the PHP OPCache Too?_ (note that this option only appears if you have the OPCache Extension installed). Props @jaswsinc. See [Issue #489](https://github.com/websharks/zencache/issues/489).
80
+ - **Bug Fix**: Fixed an HTML Compressor bug related to CSS pseudo-classes where spaces between the class name and pseudo-class name were being removed when CSS was minified. Props @patdumond @jaswsinc. See [Issue #544](https://github.com/websharks/zencache/issues/544).
81
+ - **Bug Fix**: Fixed an HTML Compressor bug related to `<noscript>` tags. The HTML Compressor now makes no adjustments to anything inside `<noscript>` tags, and the same has always been true for IE conditional comments as well. Props @rtrevellyan @jaswsinc. See [Issue #65](https://github.com/websharks/html-compressor/issues/65).
82
+ - **Bug Fix**: Fixed an issue related to a popular NGINX server configuration (`try_files $uri $uri/ /index.php?q=$uri&$args;`) that was preventing the entire site from being cached. ZenCache disables caching by default for all requests that include a query string (see _Dashboard → ZenCache → Plugin Options → GET Requests_) and this particular NGINX configuration passes _all_ requests to WordPress with a `?q=` query variable, which was resulting in ZenCache disabling caching on all pages. This release implements better detection for NGINX and works around this scenario. Props @jaswsinc. See [Issue #561](https://github.com/websharks/zencache/issues/561).
83
+ - **Bug Fix**: Fixed a bug with the Static CDN Filters that affected sites using a permalink structure that ended with `.htm` or `.html`. Generally, files that end in `.htm` or `.html` are considered static files, hence the reason ZenCache was rewriting URLs with those extensions to point at the configured CDN. However, if a site uses `.htm` or `.html` in their permalink structure, all links to Posts/Pages within the site will appear to be static files when they are in fact dynamic and therefore should not be rewritten. ZenCache now checks the permalink structure and excludes `.htm` and `.html` from the allowed extensions when the permalink structure is using one of these. Props @jaswsinc. See [Issue #495](https://github.com/websharks/zencache/issues/495).
84
+ - **Bug Fix**: Fixed an SSL issue with the HTML Compressor that was causing problems in some hosting environments where the hosting server was incorrectly setting `$_SERVER['REQUEST_SCHEME']` to `http` even when the WordPress Site URL and Home URL were set to use `https://`. As a result of this improper server configuration, the combined CSS/JS files generated by the HTML Compressor were being served over HTTP even when a site was configured to use HTTPS. This release applies a workaround for this improper server configuration that no longer looks at `$_SERVER['REQUEST_SCHEME']`. See [Issue #413](https://github.com/websharks/zencache/issues/413) and [Issue #73](https://github.com/websharks/html-compressor/issues/73).
85
+ - **Multisite Bug Fix**: Fixed a bug in the Auto-Cache Engine that was resulting in duplicate Cron Jobs being created for each Child Site. The Auto-Cache Engine now only runs from the Main Site in a network, as it should. When the Auto-Cache Engine runs on the Main Site, it will also run for each of the Child Blogs (see [this article](http://zencache.com/r/kb-article-how-does-the-auto-cache-engine-cache-child-blogs-in-a-multisite-network/) for more information). Props @jaswsinc. See [Issue #543](https://github.com/websharks/zencache/issues/543).
86
+ - **Enhancement**: Improved HTML Compressor HTTP connection handling, timeouts, protocol, BOM markers, exceptions, `Referer:` and `User-Agent:` headers. Props @LittleBastard77 @jaswsinc. See [Issue #391](https://github.com/websharks/zencache/issues/391) and [Issue #69](https://github.com/websharks/html-compressor/issues/69).
87
+ - **Enhancement**: Manual Cache Clearing options have now been separated from Automatic Cache Clearing options inside the ZenCache Plugin Options to improve the differentiation between these.
88
+ - **Enhancement**: New icons in the ZenCache Plugin Options help improve the visual representation of each panel.
89
+ - **Enhancement**: The installed plugin version is now shown at the top of the ZenCache Options Page. When a newer version of the plugin is available, an "update available" link appears. Props @renzms. See [Issue #502](https://github.com/websharks/zencache/issues/502).
90
+ - **Enhancement**: New transition in/out effects on the Cache Cleared Dashboard notifications. Props @jaswsinc. See [Issue #538](https://github.com/websharks/zencache/issues/538).
91
+
92
+ = v150821 =
93
+
94
+ - **Multisite Domain Mapping Compatibility**: ZenCache Pro is now fully compatible with the [WordPress MU Domain Mapping plugin](https://wordpress.org/plugins/wordpress-mu-domain-mapping/), including Multisite Networks using domain mapping on top-level domains, sub-domains, and sub-directories. Multiple variations of each site spread across an unlimited number of domain mappings and/or the original blog domain/path is also supported. All other features of ZenCache Pro, including the Auto-Cache Engine, Static CDN Filters, and HTML Compression are also now compatible with domain mapping. Props @jaswsinc. See [Issue #339](https://github.com/websharks/zencache/issues/339).
95
+ - **bbPress Compatibility**: This release greatly improves compatibility with bbPress. Events like creating a new Forum, creating a new Topic, and posting a reply to a Topic (including threaded replies), now properly clear the necessary cache files to ensure that cached bbPress pages remain up-to-date. Props @jaswsinc. See [Issue #168](https://github.com/websharks/zencache/issues/168).
96
+ - **Bug Fix**: Fixed a bug where, in some rare cases, `wp-config.php` would end up with two `WP_CACHE` definitions. Props @jaswsinc. See [Issue #509](https://github.com/websharks/zencache/issues/509).
97
+ - **Bug Fix**: Saving a Post as a Draft was incorrectly purging XML Sitemap cache files. Props @jaswsinc. See [Issue #368](https://github.com/websharks/zencache/issues/368).
98
+ - **Bug Fix:** The HTML Compressor now strips UTF-8 Byte Order Markers (BOMs) from CSS files generated by preprocessors such as Sass. Files consolidated by the HTML Compressor include an `@charset` rule already, making a BOM unnecessary. In fact, if BOMs are not stripped, some browsers will choke on portions of a consolidated CSS file, because a BOM could potentially appear in the middle of the file; i.e., at the _wrong_ place. Symptoms included mysterious missing styles in portions of the site, fonts not loading at all times, or font-based icons (e.g., FontAwesome) to render mystery glyphs instead of the icons. Props @jaswsinc. See [Issue #52](https://github.com/websharks/html-compressor/issues/52).
99
+ - **Bug Fix:** Improved HTML Compressor strict data type comparison when analyzing `$_SERVER` environment variables to detect an `https://` connection URL. We now detect `(integer)443` and `(string)443`. In addition, we can now pick up `$_SERVER['HTTPS']` being any of `1|on|yes|true` (caSe insensitive). Props @jaswsinc. See [Issue #61](https://github.com/websharks/html-compressor/issues/61).
100
+ - **Multisite Bug Fix**: Fixed a bug where the Clear Cache button wouldn't clear Child-Site Logged-In User Home Page cache files on WordPress Multisite Networks. Props @jaswsinc. See [Issue #409](https://github.com/websharks/zencache/issues/409)
101
+ - **Multisite Bug Fix**: Fixed a bug where the Home Page cache was not clearing on Child Sites in a Multisite Network. Props @jaswsinc. See [Issue #409](https://github.com/websharks/zencache/issues/409)
102
+ - **Multisite Bug Fix**: Fixed an issue where the Auto-Cache Engine would fail to auto-cache child blogs in a multisite network whenever the network was being served from a sub-directory. Props @jaswsinc. See [Issue #537](https://github.com/websharks/zencache/issues/537).
103
+ - **Multisite Bug Fix**: Fixed a bug with 404 Request caching on Multisite Networks where ZenCache Pro was not considering that each child blog in a multisite network will have its own 404 error page. Props @jaswsinc. See [Issue #539](https://github.com/websharks/zencache/issues/539).
104
+ - **Multisite Bug Fix**: Fixed a bug where clearing the cache from the main site of a multisite network, when there are child blogs in sub-directories, resulted in all child blogs being cleared from the cache, not just the main site as would be expected. This has been resolved and only the main site is cleared when clicking the Clear Cache button. Note that the Wipe Cache button can still be used to clear the cache for all sites in a Multisite Network. Props @jaswsinc. See [Issue #540](https://github.com/websharks/zencache/issues/540).
105
+ - **Multisite Bug Fix**: Fixed a bug where Wiping the cache on a multisite network resulted in the very next page view being cached incorrectly. Props @jaswsinc. See [Issue #541](https://github.com/websharks/zencache/issues/541).
106
+ - **Enhancement**: Improved compatibility with any Custom Post Type that uses hierarchies. Props @jaswsinc.
107
+
108
+ = v150716 =
109
+
110
+ - **Bug Fix**: Fixed a fatal error that was occurring on some sites after upgrading to v150629. We discovered the fatal error was related to PHP's outdated and buggy APC extension, which is often active on sites running PHP 5.3 and PHP 5.4. We now prevent activation of ZenCache on systems with the APC extension enabled and show a warning message instead. We've written [a KB Article that further explains the APC Extension Warning](http://zencache.com/kb-article/why-does-zencache-show-an-apc-extension-warning/) and offers advice to site owners who are currently running APC. See also [Issue #511](https://github.com/websharks/zencache/issues/511).
111
+ - **Bug Fix**: Fixed a bug in the Dynamic Version Salt filter that was generating PHP notices and warnings. See [Issue #522](https://github.com/websharks/zencache/issues/522).
112
+ - **Bug Fix**: Fixed an issue with backwards compatibility that was preventing some AC Plugins from working properly. See [Issue #514](https://github.com/websharks/zencache/issues/514).
113
+ - **Enhancement**: Added a stats pinger, similar to the WordPress stats collector, that reports the server OS, PHP version, MySQL version, WordPress version, and the ZenCache version, securely and anonymously to WebSharks. See [this KB Article](http://zencache.com/kb-article/what-information-does-zencache-pro-report-to-websharks/) for more information.
114
+
115
+ = v150626 =
116
+
117
+ - **Restructured Codebase**: The entire ZenCache codebase has been restructured to improve performance, enhance flexibility, and make it easier to build in new features!
118
+ - **New Feature!** The free version of ZenCache now supports several new options that were previously only available in the Pro version. You can now toggle the Auto-Clear Cache routines for the Home Page, Posts Page, Author Page, Category Archives, Tag Archives, Custom Term Archives, RSS/RDF/Atom Feeds, and XML Sitemaps. This gives you more control over exactly when ZenCache purges the cache for these parts of your site. See _ZenCache → Plugin Options → Clearing the Cache_ for further details.
119
+ - **New Feature!** URI Exclusion Patterns are now available in ZenCache Lite! This previously Pro-only feature is now available in the free version of ZenCache and allows you to exclude a list of URIs from being cached by ZenCache. See _ZenCache → Plugin Options → URI Exclusion Patterns_ for further details.
120
+ - **New Feature!** HTTP Referrer Exclusion Patterns are now available in ZenCache Lite! This previously Pro-only feature is now available in the free version of ZenCache and allows you to define a list of referring URLs or domains that send you traffic. When ZenCache sees a request coming from one of those URLs or domains, it will not cache that particular request. See _ZenCache → Plugin Options → HTTP Referrer Exclusion Patterns_ for further details.
121
+ - **New Pro Feature!**: HTML Compression now supports compressing JSON (in addition to the already supported HTML, JavaScript, and CSS compression). Props @jaswsinc. See [Issue #469](https://github.com/websharks/zencache/issues/469).
122
+ - **New Pro Feature!**: Static CDN Filters now supports multiple CDN hostnames. This allows you to configure more than one CDN hostname, also referred to as Domain Sharding. This makes it possible for site owners to work around web browser concurrency limits, allowing the browser to download many resources simultaneously, which increases overall speed. Props to @isaumya and @jaswsinc. See [Issue #468](https://github.com/websharks/zencache/issues/468).
123
+ - **Enhancement** (Pro): Static CDN Filters now includes proper support for WordPress Multisite Networks, including support for subdomains (full support for Domain Mapping coming in the next release). If you're running a WordPress Multisite Network and want to configure a CDN, see [this KB Article](http://zencache.com/kb-article/static-cdn-filters-for-wordpress-multisite-networks/) for further details.
124
+ - **Enhancement** (Pro): Static CDN Filters now also apply to any static files that are referenced inside CSS files. Props @jaswsinc. See [Issue #461](https://github.com/websharks/zencache/issues/461).
125
+ - **Enhancement**: Completed a major restructure of the entire codebase to improve modularity and dependency management. Props @jaswsinc.
126
+ - **Enhancement** (Pro): Static CDN Filters now supports the ability to configure separate CDN hostname(s) for each domain (or subdomain) that you run in a WordPress Multisite Network. Props @jaswsinc. See [Issue #475](https://github.com/websharks/zencache/issues/475).
127
+ - **Enhancement** (Pro): Static CDN Filters now support subdomains when ZenCache is running inside a WordPress Multisite Network. Props @jaswsinc. See [Issue #439](https://github.com/websharks/zencache/issues/439).
128
+ - **Bug Fix** (Pro): Static CDN Filters were not being applied to the primary site on WP Multisite installations that used subdomains. Props to @isaumya for discovering this bug. See [Issue #470](https://github.com/websharks/zencache/issues/470).
129
+
130
+ = v150409 =
131
+
132
+ - **Enhancement (includes improved CloudFlare support)**: Improvements to IP address detection, including added support for CloudFlare IP forwarding, multiple IPs in a single header, and the ability to customize the lookup order and/or add/remove sources that are searched when looking for the current IP address. It's also possible to revert to the old IP address detection behavior (see [How do I customize remote IP detection?](http://zencache.com/kb-article/how-do-i-customize-ip-address-detection/)). Props @jaswsinc. [Issue #449](https://github.com/websharks/zencache/pull/449)
133
+ - **Enhancement** (Pro): Files being served by the HTML Compressor were being sent without a `Vary: Accept-Encoding` header, which caused some page speed testing services to give a lower rating to sites using ZenCache. ZenCache now ensures this header is sent via an `.htaccess` file inside the HTML Compressor cache directory (requires Apache 2.1+). Props @jaswsinc. [Issue #436](https://github.com/websharks/zencache/issues/436).
134
+ - **Enhancement** (Pro): Static CDN Filters now also filter any static resources inside Text Widgets, so that those resources can be cached by your CDN. Props @jaswsinc. [Issue #430](https://github.com/websharks/zencache/issues/430)
135
+ - **Enhancement** (Pro): Static CDN Filters now apply to minified and compressed CSS/JS files generated by the HTML Compressor; these files will now be cached by your CDN. Props @jaswsinc. [Issue #429](https://github.com/websharks/zencache/issues/429)
136
+ - **Bug Fix**: Fixed a bug related to the Quick Cache migration that resulted in caching being disabled despite ZenCache being enabled. Uninstalling Quick Cache was removing `define('WP_CACHE', TRUE);` from the `wp-config.php` file. ZenCache now makes sure that caching remains enabled after uninstalling Quick Cache during the ZenCache migration process. [Issue #450](https://github.com/websharks/zencache/issues/450).
137
+ - **Bug Fix**: Fixed a minor UI issue where the ZenCache Dashboard icon would occasionally flash to a black color when refreshing the Dashboard. Props @jaswsinc. [Issue #453](https://github.com/websharks/zencache/issues/453).
138
+ - **Bug Fix**: When ZenCache was running on an installation of PHP with `open_basedir` restrictions applied, calls to `is_dir()` were triggering a PHP Warning while looking for a writable temporary directory. This bug has been fixed. [Issue #456](https://github.com/websharks/zencache/issues/456).
139
+ - **Bug Fix**: Fixed a bug where changing the permalink for a published post would result in the cache file for the old permalink being left behind and as a result both the old and the new permalink would be accessible, instead of WordPress redirecting the old permalink to the new one. This has been fixed and ZenCache now properly clears the old cache file when changing the permalink on a published post. [Issue #359](https://github.com/websharks/zencache/issues/359).
140
+ - **Bug Fix**: Fixed a bug where transitioning a Published post back to Pending or Draft would not automatically clear the cache file. This resulted in the post remaining accessible on the frontend despite being set as Pending or Draft. ZenCache now properly clears the cache file automatically when transitioning from Published to Pending or Draft, which prevents access to the post as expected. [Issue #441](https://github.com/websharks/zencache/issues/441).
141
+ - **Bug Fix** (Pro): Some users reported seeing `Warning: trim() expects parameter 1 to be string`. This was produced by a low-impact bug that has been fixed in this release. [Issue #455](https://github.com/websharks/zencache/issues/455).
142
+ - **Bug Fix** (Pro): Some users running the HTML Compressor reported seeing 403 Forbidden errors related to loading the compressed/minified files. This was a permissions-related issue that has been resolved in this release. Props @superian and @jaswsinc. See [Issue #50](https://github.com/websharks/html-compressor/issues/50).
143
+ - **Bug Fix** (Pro): Some HTTP requests made by the HTML Compressor to servers configured to reject HTTP requests that don't include a User-Agent were failing. A User-Agent is now always used in all requests. Props @jaswsinc. See [Issue #49](https://github.com/websharks/html-compressor/issues/49).
144
+ - **Minimum Required PHP Version Bumped to PHP v5.3.2+:** The minimum PHP version required for ZenCache has been bumped up to PHP v5.3.2+ (from PHP v5.3+). The ZenCache cache locking mechanism, specifically the use of `flock()`, requires behavior introduced in PHP v5.3.2. [Issue #444](https://github.com/websharks/zencache/issues/444).
145
+
146
+ = v150314 =
147
+
148
+ - **Bug Fix**: Fixed a bug in the Quick Cache back compat. method `\zencache\share\apply_wp_filters()`. It was not passing the ZenCache-filtered value through the Quick Cache back compat. routine. This bug affected sites that had disabled automatic cache clearing routines via a filter.
149
+ - **Bug Fix** (Pro): Updated Static CDN Filters whitelist to include font file extensions `eot,ttf,otf,woff`, as site owners may wish to serve fonts with these extensions from the CDN (this would require a custom CORS configuration; see [this article](http://davidwalsh.name/cdn-fonts) for instructions). See [#427](https://github.com/websharks/zencache/issues/427).
150
+ - **Bug Fix** (Pro): Updated Static CDN Filters blacklist to exclude font file extensions `eot,ttf,otf,woff` by default, as they require custom CORS configuration to display properly. See [#427](https://github.com/websharks/zencache/issues/427).
151
+ - **Bug Fix** (Pro): The Static CDN Filters feature was calling a protected method that was requiring PHP v5.4+ but now the Static CDN Filters feature works with PHP v5.3+. See [#426](https://github.com/websharks/zencache/issues/426).
152
+ - **Bug Fix**: Fixed a zero-byte `advanced-cache.php` bug related to migrating from Quick Cache. When you deleted/uninstalled Quick Cache after upgrading to ZenCache, the Quick Cache uninstallation process would empty the `advanced-cache.php` file, resulting in the site no longer being cached, despite ZenCache being active. ZenCache now continuously checks to make sure that `advanced-cache.php` is installed properly. See [#432](https://github.com/websharks/zencache/issues/432).
153
+
154
+ = v150218 =
155
+
156
+ **Announcing ZenCache, formerly Quick Cache!**
157
+
158
+ We are very excited to announce the release of [ZenCache](http://zencache.com), an advanced WordPress caching plugin inspired by simplicity. ZenCache is the successor to Quick Cache, a very popular WordPress caching plugin that has been downloaded over 1 million times and won acclaim for its speed, simplicity, and ease of configuration. [Read the full announcement here](http://zencache.com/announcing-zencache-formerly-quick-cache/).
159
+
160
+ ZenCache is an evolution of Quick Cache. It comes with a completely refactored codebase, subtle UI enhancements, internal optimizations; along with full backward compatibility with all previous versions of Quick Cache Lite and Quick Cache Pro. In fact, installing ZenCache on a site that was previously running Quick Cache is a piece of cake! Watch [this video](http://zencache.com/kb-article/how-to-migrate-from-quick-cache-lite-to-zencache-lite/) to learn more. All configuration options are preserved.
161
+
162
+ - **New Pro Feature: CDN Integration:** With the announcement and release of ZenCache we are excited to also announce a new, highly-requested feature: Static CDN Filters. This feature allows you to serve some and/or ALL static files on your site from a CDN of your choosing. See also: [Introduction to Static CDN Filters](http://zencache.com/kb-article/introduction-to-static-cdn-filters/).
163
+ - **Full Backwards Compatibility with Quick Cache and Quick Cache Pro:** ZenCache is fully backwards compatible with all previous versions of Quick Cache Lite and Quick Cache Pro. Simply install ZenCache and your existing Quick Cache options will preserved by ZenCache.
164
+
165
+ = v150129 =
166
+
167
+ - **Bug Fix** (Pro): Fixed a bug where the Pro Updater would fail when FTP or SFTP details via the WordPress Dashboard are required to perform updates. Props @jaswsinc. See [#389](https://github.com/websharks/quick-cache/issues/389).
168
+ - **Bug Fix**: Several fixes for a stubborn bug related to "Fatal Error: 'Unable to clear dir'" error messages and errors referencing "SplFileInfo::getMTime(): stat failed". Props @jaswsinc. See [#397](https://github.com/websharks/quick-cache/issues/397).
169
+ - **Bug Fix** (Pro): Fixed a bug where the `qcAC` variable (used to force-enable/disable GET Request caching) was not respected properly whenever a URL contained a query string and a user was currently logged into the site. Props @jaswsinc. See [#401](https://github.com/websharks/quick-cache/issues/401).
170
+
171
+ = v141231 =
172
+
173
+ - **Bug Fix**: Addressed another issue related to "Fatal Error: 'Unable to clear dir'" and tmp directories that don't get cleared by Quick Cache. This fix discards iteration references before renaming the tmp directories. Props @jaswsinc. See [#288](https://github.com/websharks/quick-cache/issues/288).
174
+ - **Bug Fix**: We have had a few reports of getMTime Stat failing when clearing the cache. This fix clears the Stat Cache before iteration when deleting files from the cache directory. Props @jaswsinc. See [#385](https://github.com/websharks/quick-cache/issues/385).
175
+ - **Enhancement**: Added a new filter (`quick_cache\\share_disable_cache_locking`) to allow disabling cache locking. Simply return boolean `TRUE` to this filter to disable cache locking. This may be useful for site owners who are experiencing issues with cache locking on web hosting platforms with filesystems that don't properly support locking. Note that this filter must be applied using an Advanced Cache Plugin (see **Dashboard → Quick Cache → Plugin Options → Theme/Plugin Developers**). See also [#387](https://github.com/websharks/quick-cache/issues/387).
176
+ - **Enhancement**: Added a new filter that allows forcing Semaphore cache locking on hosting platforms where `sem_get()` is available and would result in improved performance. Return `sem` to the `quick_cache\\share::cache_lock_lock_type` filter to force Semaphore cache locking, or `flock` to use the default method that uses `flock()`. Note that this filter must be applied using an Advanced Cache Plugin (see **Dashboard → Quick Cache → Plugin Options → Theme/Plugin Developers**). See also [#387](https://github.com/websharks/quick-cache/issues/387).
177
+
178
+ = v141205 =
179
+
180
+ - **Bug Fix**: Addressed another issue with "Fatal Error: 'Unable to clear dir'" messages by adding new blocking methods for cache lock and unlock, making it so that cache writes (including clearing, purging, wiping) all gain an exclusive lock on the cache directory while work is underway. Props @jaswsinc. See [#288](https://github.com/websharks/quick-cache/issues/288).
181
+ - **Bug Fix**: Fixed a Home Page clearing bug that arose in the previous release as the result of an extra leading `\/` in one of our regex patterns. Props @jaswsinc. See [#365](https://github.com/websharks/quick-cache/issues/365).
182
+
183
+ = v141110 =
184
+
185
+ - **Quick Cache is changing its name to ZenCache!** See [the blog post](http://www.websharks-inc.com/post/quick-cache-is-changing-its-name/) for full details.
186
+ - **Enhancement**: All dashboard notices will now include the number of files cleared for each notice. Props @jaswsinc. See [#351](https://github.com/websharks/quick-cache/pull/351).
187
+ - **Enhancement**: Quick Cache is now capable of doing atomic clearing/purging/wiping. On a Multisite network, this is now accomplished on a blog-specific basis, without needing to scan the entire network-wide cache directory. This should improve performance considerably on large networks (i.e. those with VERY large cache directories). Props @jaswsinc. See [#288](https://github.com/websharks/quick-cache/issues/288) and [#351](https://github.com/websharks/quick-cache/pull/351).
188
+ - **Enhancement**: Added a new class file (`/includes/utils-feed.php`) and refactored the XML feed clearing routine. With these utilities in place, the `auto_clear_xml_feeds_cache()` method is now much easier to deal with and comprehend. Props @jaswsinc. See [#351](https://github.com/websharks/quick-cache/pull/351).
189
+ - **Enhancement**: Quick Cache now scans each scheme sub-directory, i.e. `/http/` and `/https/`, separately to help break apart what was previously a much larger directory scan for sites that serve pages over both schemes. This will improve performance on both standard and Multisite network installs. See [#351](https://github.com/websharks/quick-cache/pull/351).
190
+ - **Enhancement**: Refactored codebase to improve modularity by creating new methods in `includes/share.php` for network-wide clearing/purging/wiping, host/blog-specific clearing/purging/wiping, recursively deleting a directory, assisting in translations, and several new string utilities. Props @jaswsinc. See [#351](https://github.com/websharks/quick-cache/pull/351).
191
+ - **Enhancement**: Renamed all `\quick_cache\plugin::auto_purge_*` methods, giving them the proper prefix of `auto_clear_*` instead to reflect proper Wipe/Clear/Purge terminology. Along with this, all of the `auto_purge_` option keys have been renamed as well. See new [Wipe/Clear/Purge wiki article](http://www.websharks-inc.com/r/quick-cache-wipeclearpurge-terminology-wiki/) for a description of these terms. Props @jaswsinc. See [#351](https://github.com/websharks/quick-cache/pull/351).
192
+ - **Enhancement**: All URI and Referrer exclusion patterns are now caSe insensitive. WordPress v4.0 will serve URIs without cAse sensitivity. All of the Quick Cache codebase has been updated to support caSe insensitive clearing/purging/wiping; along with caSe insensitive pattern matching against URIs. Props @jaswsinc. See [#351](https://github.com/websharks/quick-cache/pull/351).
193
+ - **Bug Fix (Pro)**: Fixed a bug with the Auto-Clear Author Cache routine that was presenting dashboard notices even when `change_notifications_enable` was off. Props @jaswsinc. See [#351](https://github.com/websharks/quick-cache/pull/351).
194
+ - **Bug Fix (Pro)**: Fixed a bug with the HTML Compressor where `style`, `link` and/or `script` tags could become out of order in certain scenarios. See: [#45](https://github.com/websharks/html-compressor/issues/45)
195
+ - **Bug Fix (Pro)**: Several HTML Compressor bug fixes related to JavaScript compression routines. See [#38](https://github.com/websharks/html-compressor/issues/38)
196
+ - **Bug Fix**: Fixed a bug that was occasionally generating "Fatal Error: 'Unable to clear dir'" messages. The root cause of this is believed to be non-atomic purging of cache directories which, on a busy site, could result in new cache files being created before a purging routine was finished clearing the directory. Clearing/purging/wiping is now atomic in all scenarios. Props @jaswsinc. See [#288](https://github.com/websharks/quick-cache/issues/288) and [#351](https://github.com/websharks/quick-cache/pull/351).
197
+ - **Bug Fix**: Fixed a bug in the Auto-Clear Custom Post Type Archive Cache that was not checking for the `$post_id` in `$this->static[__FUNCTION__]`. Props @jaswsinc. See [#351](https://github.com/websharks/quick-cache/pull/351).
198
+ - **Bug Fix**: Fixed a bug with the uninstaller whereby attempting to uninstall after receiving a notice that PHP 5.3+ is required would result in a blank screen and require manual removal of the plugin. See [#334](https://github.com/websharks/quick-cache/issues/334).
199
+ - **Bug Fix**: Fixed an issue with detecting cacheable requests that was, in rare instances, resulting in blank white pages for some site owners. See [#279](https://github.com/websharks/quick-cache/issues/279).
200
+
201
+ = v141001 =
202
+
203
+ - **Enhancement**: Improved Dashboard messaging for the `auto_clear_cache()` routine. See [#328](https://github.com/websharks/quick-cache/issues/328).
204
+ - **Enhancement (Pro)**: Improved consistency of Auto-Cache Engine User-Agent string by removing WordPress-version-specific identifier. See [#315](https://github.com/websharks/quick-cache/issues/315).
205
+ - **Enhancement (Pro)**: It is now possible to disable the automatic clear and wipe cache routines. If you have a very large site with many cache files, this feature allows you manual control over when the cache gets cleared or wiped. For complete documentation on this feature, see [Quick Cache Wiki - Clear and Wipe Cache Routines](https://github.com/websharks/quick-cache/wiki/Clear-Cache-and-Wipe-Cache-Routines). See also [#23](https://github.com/websharks/quick-cache/issues/23).
206
+ - **Bug Fix**: Fixes an issue with the Pro Preview mode where saving the plugin options may inadvertently save Pro-only options that could later cause issues if the plugin is upgraded to the Pro version. See [#311](https://github.com/websharks/quick-cache/pull/311#issuecomment-54491922).
207
+ - **Bug Fix**: Fixes an edge-case where the proper cache files do not get purged when a user with the Editor role publishes a new post with both a category and a tag associated with the post. See [#313](https://github.com/websharks/quick-cache/issues/313).
208
+ - **Bug Fix**: When Quick Cache purges/resets the cache for a post with a Custom Post Type, any cache files for the associated Custom Post Type archive view are now auto-purged, along with any associated XML feed cache files. See [#280](https://github.com/websharks/quick-cache/issues/280).
209
+ - **Bug Fix**: Fixed missing trailing slash in Directory / Expiration Time configuration panel. This was purely a visual inconsistency and had no effect on Quick Cache's functionality. See [#267](https://github.com/websharks/quick-cache/issues/267).
210
+ - **Bug Fix**: When saving custom CSS using JetPack's Custom CSS module, Quick Cache now properly purges the site cache to ensure that outdated cache files are not served to visitors. See [#246](https://github.com/websharks/quick-cache/issues/246).
211
+ - **Bug Fix**: When changes are made to WordPress General, Reading, Discussion, or Permalink settings (Dashboard -> Settings), Quick Cache now clears the cache to prevent an outdated cache file from being served to visitors. See [#223](https://github.com/websharks/quick-cache/issues/223).
212
+ - **Bug Fix**: When an active plugin is updated, or when an active theme or the parent theme for an active child theme is updated, or when WordPress Core is updated, Quick Cache now properly clears the cache to ensure that an outdated cache file does not get served to a visitor. See [#145](https://github.com/websharks/quick-cache/issues/145) and [#327](https://github.com/websharks/quick-cache/issues/327).
213
+ - **Bug Fix**: When a new comment is posted, Quick Cache now properly purges the cache files for any paginated comment pages. See [#336](https://github.com/websharks/quick-cache/issues/336).
214
+ - **Bug Fix (Pro)**: Fixed a bug where the HTML Compressor, when enabled with the "remove extra whitespace in the final HTML" option, would incorrectly remove `<!DOCTYPE html>`. See [#299](https://github.com/websharks/quick-cache/issues/299).
215
+ - **Bug Fix (Pro)**: Fixed a bug in the HTML Compressor that would, in certain scenarios, leave behind fragments of HTML comments. See [#295](https://github.com/websharks/quick-cache/issues/295).
216
+ - **Bug Fix (Pro)**: The Auto-Cache Engine now has an option to configure a delay between each request when pre-caching the site. There were some reports of the Auto-Cache Engine causing load issues with large sites on servers that sometimes had trouble handling many requests. See [#294](https://github.com/websharks/quick-cache/issues/294).
217
+ - **Bug Fix (Pro)**: Fixes an Auto-Cache Engine scheduling issue that may, in some scenarios, prevent it from running as expected. See [#291](https://github.com/websharks/quick-cache/issues/291).
218
+ - **Bug Fix (Pro)**: Fixed a bug with the HTML Compressor where `style`, `link` and/or `script` tags could end up out of order in certain scenarios. See [#45](https://github.com/websharks/html-compressor/issues/45).
219
+ - **Bug Fix (Pro)**: Fixed a bug in the HTML Compressor related to JavaScript compression routines. See [#38](https://github.com/websharks/html-compressor/issues/38).
220
+
221
+ = v140829 =
222
+
223
+ - **SECURITY FIX - Please upgrade immediately**: Fixes a security related to cached cookies sent in the header of a request. This only affects sites running plugins that might send cookie data via the header. See [#253](https://github.com/websharks/Quick Cache/issues/253)
224
+ - **Enhancement**: Auto-Purge RSS Feeds. Quick Cache will now automatically purge the cache for RSS/RDF/Atom Feeds when Feed Caching is enabled. This new option will purge the cache for the master feed, the master comments feed, feeds associated with comments on a Post/Page, term-related feeds (including mixed term-related feeds), and author-related feeds when you update a Post/Page, approve a Comment, or make other changes where Quick Cache can detect that certain types of Feeds should be purged. See [#182](https://github.com/websharks/Quick Cache/issues/182)
225
+ - **Enhancement**: Improve handling of symlink creation for 404 cache files by using atomic symlink creation to decrease the possibility of encountering a race condition. See [#242](https://github.com/websharks/Quick Cache/issues/242).
226
+ - **Enhancement**: Improved portability of `advanced-cache.php`. This will help reduce configuration overhead for site owners when migrating a WordPress installation from one server to another. See [#258](https://github.com/websharks/Quick Cache/issues/258).
227
+ - **Enhancement**: Option Panels now have proper HTML anchor tags so that they work better with browser extensions that rely on anchor tags being available. See [#260.](https://github.com/websharks/Quick Cache/issues/260)
228
+ - **Enhancement**: The Plugin Deactivation Safeguards option has been renamed to Plugin Deletion Safeguards. When Plugin Deletion Safeguards are disabled, deactivating and deleting the plugin will now erase your options for the plugin, erase directories/files created by the plugin, remove the advanced-cache.php file, terminate CRON jobs, etc. It completely erases itself, but only when you disable Plugin Deletion Safeguards (enabled by default to prevent accidental loss of data). See [#261](https://github.com/websharks/Quick Cache/issues/261).
229
+ - **Enhancement (Pro)**: HTML Compressor now includes FOPEN as transport layer fallback in case cURL is not available. See [#15](https://github.com/websharks/html-compressor/issues/15)
230
+ - **Enhancement (Pro)**: HTML Compressor now writes files atomically; this will help avoid race conditions when writing cache files. See [#273](https://github.com/websharks/Quick Cache/issues/273)
231
+ - **Enhancement (Pro)**: Improved error handling for the Auto-Cache Engine. There were some scenarios where `XMLReader()` would fail with a PHP Warning notice when it was unable to properly parse the sitemap. See [#250](https://github.com/websharks/Quick Cache/issues/250).
232
+ - **Bug Fix**: The cache directory is now properly removed when deleting the plugin from the WordPress Dashboard plugins list. See [#261](https://github.com/websharks/Quick Cache/issues/261).
233
+ - **Bug Fix**: WooCommerce compatibility fix for a bug where cart session data appeared to get cached across sessions. See [#253](https://github.com/websharks/Quick Cache/issues/253)
234
+ - **Bug Fix (Pro)**: The plugin upgrade notice no longer appears on Child Blogs in a Multisite Network. There was no security risk here; while the upgrade notice was shown, Child Blog admins who did not have permission to upgrade Network-activated plugins were unable to do anything with the message. See [#259](https://github.com/websharks/Quick Cache/issues/259).
235
+ - **Bug Fix (Pro)**: Fixed a bug where, in certain scenarios, a WordPress Plugin may break the JavaScript that controls the Clear Cache button on the Dashboard. See [#272](https://github.com/websharks/Quick Cache/issues/259).
236
+ - **Bug Fix (Pro)**: CSS files are now excluded from compression by the HTML Compressor when included inside conditional comments. See [#35](https://github.com/websharks/html-compressor/issues/35)
237
+ - **Bug Fix (Pro)**: HTML Compressor now preserves whitespace inside CSS `calc()` statements. See [#286](https://github.com/websharks/Quick Cache/issues/286).
238
+
239
+ = v140725 =
240
+
241
+ - **Enhancement**: Improved overall performance by optimizing the auto-purge routines. See also: [#130](https://github.com/websharks/Quick Cache/issues/130)
242
+ - **Enhancement**: The "GET Requests" UI Panel now explains that you can use `?zcAC=0` to disable caching when you ARE caching GET Requests. See also: [#210](https://github.com/websharks/Quick Cache/issues/210).
243
+ - **New Pro Feature: Auto-Purge XML Sitemaps**. If you're generating XML Sitemaps with a plugin like Google XML Sitemaps, you can now tell Quick Cache to automatically purge any cached sitemap files whenever it purges a Post/Page cache. You may also specify a list of XML Sitemap patterns to clear, if you have multiple sitemap files. See also: [#169](https://github.com/websharks/Quick Cache/issues/169)
244
+ - **Enhancement (Pro)**: The Quick Cache Pro Updater now accepts a License Key in place of the WebSharks password.
245
+ - **Enhancement (Pro)**: In a Multisite Network, the Auto-Cache Engine will now also auto-cache each child blog. See also: [#169](https://github.com/websharks/Quick Cache/issues/169)
246
+ - **Bug Fix**: Fixed a bug that was causing unapproved, spam, and trash comments to unnecessarily purge the cache. See also: [#159](https://github.com/websharks/Quick Cache/issues/159)
247
+ - **Bug Fix**: A custom `WP_CONTENT_DIR` is now obeyed in the scenario where it's set to a path outside of `ABSPATH`. See also: [#95](https://github.com/websharks/Quick Cache/issues/95)
248
+ - **Bug Fix**: The UI now correctly displays custom `WP_CONTENT_DIR` in the "Directory/Expiration Time" options panel. See also: [#206](https://github.com/websharks/Quick Cache/issues/206)
249
+ - **Bug Fix**: Quick Cache LITE now correctly sets the `Quick Cache_PRO` constant to false. See also: [#229](https://github.com/websharks/Quick Cache/issues/229)
250
+ - **Bug Fix**: Workaround for broken page navigation on the front page of some sites. This is a WordPress `redirect_canonical()` bug workaround. See also: [#209](https://github.com/websharks/Quick Cache/issues/209)
251
+ - **Bug Fix (Pro)**: 404 Caching now properly returns a 404 HTTP Status code when serving a cached 404 page. See also: [#197](https://github.com/websharks/Quick Cache/issues/197)
252
+ - **Bug Fix (Pro)**: The HTML Compressor now properly preserves `[]` character whitespace during CSS compression. See also: [#25](https://github.com/websharks/html-compressor/issues/25)
253
+ - **Bug Fix (Pro)**: The Pro Updater upgrade link now points to the correction location when displayed from a Child Blog in a Multisite Network. See also: [#205](https://github.com/websharks/Quick Cache/issues/205)
254
+ - **Bug Fix (Pro)**: The Auto-Cache Engine now correctly handles the sitemap when `home_url()` differs from `site_url()`.
255
+ - **Bug Fix (Pro)**: The "Dynamic Version Salt" options panel now correctly displays the last saved value. See also: [#231](https://github.com/websharks/Quick Cache/issues/231)
256
+
257
+ = v140605 =
258
+
259
+ - **New Feature**: Branched Cache Structure. Cache files are now written to the cache directory using a more intuitive format of `PROTOCOL`/`HOSTNAME`/`PERMALINK` (e.g., `http/example-com/sample-page.html`). For more details, please see <http://www.websharks-inc.com/r/Quick Cache-branched-cache-structure-wiki/>
260
+ - **New Feature**: 404 Page caching. It's now possible to enable/disable the caching of 404 requests. Enabling this feature generates a single cache file for your 404 Page and then symlinks future 404 requests to that cache file. See *Dashboard -> Quick Cache -> Plugin Options -> 404 Requests*.
261
+ - **New Feature (Pro)**: HTML Compressor (experimental). This new experimental feature automatically combines and compresses CSS/JS/HTML code. See *Dashboard -> Quick Cache -> Plugin Options -> HTML Compressor*. For more details about how this feature works, please see <https://github.com/websharks/HTML-Compressor>
262
+ - **New Feature (Pro)**: Auto-Cache Engine. When enabled, the Auto-Cache Engine will pre-cache your site at 15-minute intervals, rebuilding cache files when necessary (it will not rebuild cache files until they have expired). This helps eliminate the slowness a user may experience when visiting a page on your site that has not yet been cached. See *Dashboard -> Quick Cache -> Plugin Options -> Auto-Cache Engine*.
263
+ - **New Feature**: Auto-Purge "Author Page". When a single Post/Page is changed in some way, Quick Cache can also purge any existing cache files for the associated Author Page. See *Dashboard -> Quick Cache -> Plugin Options -> Clearing the Cache*. (This option is enabled by default; disabling this requires Quick Cache Pro.)
264
+ - **New Feature**: Auto-Purge "Category Archives". When a single Post/Page is changed in some way, Quick Cache can also purge any existing cache files for the associated Category archive views. See *Dashboard -> Quick Cache -> Plugin Options -> Clearing the Cache*. (This option is enabled by default; disabling this requires Quick Cache Pro.)
265
+ - **New Feature**: Auto-Purge "Tag Archives". When a single Post/Page is changed in some way, Quick Cache can also purge any existing cache files for the associated Tag archive views. See *Dashboard -> Quick Cache -> Plugin Options -> Clearing the Cache*. (This option is enabled by default; disabling this requires Quick Cache Pro.)
266
+ - **New Feature**: Auto-Purge "Custom Term Archives". When a single Post/Page is changed in some way, Quick Cache can also purge any custom Terms that may have their own Term archive views. See *Dashboard -> Quick Cache -> Plugin Options -> Clearing the Cache*. (This option is enabled by default; disabling this requires Quick Cache Pro.)
267
+ - **Enhancement**: Improved conflict handling of other plugins using `ob_start()`. See <https://github.com/websharks/Quick Cache/issues/97>
268
+ - **Enhancement**: Added a postload filter for `status_header` so that Quick Cache can properly detect calls to the WP core function `status_header()`
269
+ - **Enhancement**: The Quick Cache cache directory has been changed to `wp-content/cache/Quick Cache/` to provide better organization of cache files and avoid interfering with another plugin that may also be writing to the `wp-content/cache/` directory. See <https://github.com/websharks/Quick Cache/issues/123>
270
+ - **Enhancement**: New detailed debugging notes (see *Dashboard -> Quick Cache -> Plugin Options -> Enable/Disable*). There is now an extra option to show detailed debugging information in addition to the Quick Cache notes in the HTML source. For now, this feature only applies when the HTML Compressor is enabled.
271
+ - **Enhancement**: Better Debugging Notices. If Quick Cache is not caching a particular page (such as when a logged-in user visits the site and logged-in user caching is not enabled), Quick Cache will now report why that page is not being cached in the HTML notes.
272
+ - **Enhancement**: Improved compatibility with the Nav Menu Roles plugin. See <https://github.com/websharks/Quick Cache/issues/164>
273
+ - **Bug Fix**: Obey custom content directories. If you have customized your `WP_CONTENT_DIR` and `WP_CONTENT_URL` constants to point somewhere other than the default, Quick Cache will now obey those and use your custom directory for storing cache files. See <https://github.com/websharks/Quick Cache/issues/95>
274
+ - **Bug Fix**: Scheduled posts now trigger the clearing of any associated archive views when those posts go live (assuming you have those archive views set to Auto-Purge in *Dashboard -> Quick Cache -> Plugin Options -> Clearing the Cache*). See <https://github.com/websharks/Quick Cache/issues/26>
275
+ - **Bug Fix**: Fixed a bug where saving a post as `draft` would trigger the Auto-Purge Post routine and clear the cache for that post. Now only purges post status `publish` and `private` and when transitioning from `publish` or `private` post status to `draft`, `future`, or `private`. See <https://github.com/websharks/Quick Cache/issues/43>
276
+ - **Bug Fix**: Split/paginated comments and multi-page Posts/Page cache files are now purged properly when the post cache is purged. See <https://github.com/websharks/Quick Cache/issues/75>
277
+
278
+ = v140104 =
279
+
280
+ * **New Options for Feed Caching**. It's now possible to control RSS, RDF, and Atom Feed caching. The new default is for feed caching to be disabled, which resolves an issue where new posts don't show up in the feed until the cache is cleared. This version of Quick Cache disables feed caching to prevent this from happening. If you wish to cache feeds, you can enable feed caching in the options. See: <https://github.com/websharks/Quick Cache/issues/44>
281
+ * **New Automatic Updater for Quick Cache Pro**. Quick Cache Pro now includes an automatic updater which lets you to keep Quick Cache Pro updated right from within your WordPress Dashboard. To upgrade to a new version of Quick Cache Pro using the Automatic Updater, simply fill in your WebSharks-Inc.com credentials in the new Plugin Updater sub-panel (**Quick Cache Pro -› Plugin Updater**). See: <https://github.com/websharks/Quick Cache/issues/21>
282
+
283
+ = v131224 =
284
+
285
+ * **New Lite Enhancement**. The Home Page cache and Posts Page cache are now automatically purged when necessary (such as when a new post is published). See: <https://github.com/websharks/Quick Cache/issues/40>
286
+ * Improved Quick Cache version check notice.
287
+ * Improved Quick Cache options validation.
288
+ * **Bug Fix**. Quick Cache was previously not properly excluding systematic WordPress areas reliably, e.g. any file that begins with `wp-` and/or the `xmlrpc` file. These are now properly auto-excluded. On Multisite installations, `/files/` is also auto-excluded from being cached. This bug required fixing incorrect instances of `[?$]` in regex patterns. See: <https://github.com/websharks/Quick Cache/issues/41>
289
+ * **Multisite Enhancement**. When running Quick Cache on Multisite Network installation, only allow the plugin to be "Network Activated" (becuase that is how Quick Cache is designed to work). See: <https://github.com/websharks/Quick Cache/issues/50>
290
+ * **Multisite Enhancement**. New 'Wipe' button allows a site owner to clear (wipe) the cache for all sites in a Multisite Network at once. See: <https://github.com/websharks/Quick Cache/issues/48>
291
+ * **Multisite Bug Fix**. Clearing the cache on a Multisite Network configured to use sub-directories now works properly. See: <https://github.com/websharks/Quick Cache/issues/39>
292
+ * **Multisite Bug Fix**. Fixed unmatched closing parenthesis in regex. See: <https://github.com/websharks/Quick Cache/issues/37>
293
+ * **Multisite Bug Fix**. Added support for `PATH_CURRENT_SITE` and `$GLOBALS['base']`.
294
+ * **Multisite Bug Fix**. Removed depreciated VHOST code that was causing issues with clearing the cache.
295
+
296
+ = v131206 =
297
+
298
+ * **New Pro Feature**. It's now possible for developers to add custom PHP code to the cache clearing routines (e.g. custom code which might consider things like APC or memcache also). This requires [Quick Cache Pro](http://www.websharks-inc.com/product/Quick Cache/). Please check your Dashboard under: **Quick Cache Pro -› Clearing the Cache**. See also: [this screenshot](https://f.cloud.github.com/assets/1563559/1692324/7ae902c4-5e78-11e3-98ba-acbb08b30585.png).
299
+ * **Multisite Bug Fix**. Unable to clear the cache when running sub-directories. See: <https://github.com/websharks/Quick Cache/issues/30>
300
+ * **Multisite Bug Fix**. The "Clear Cache" button was not displayed for child blogs in a network. Fixed in this release.
301
+
302
+ = v131205 =
303
+
304
+ * Added hook to `wp_set_comment_status` to purge the comment cache when a comment status changes.
305
+ * Ignore `set_time_limit()` errors in case function is disabled in PHP configuration. This is a temporary fix and will be handled more appropriately in a future maintenance release. See also: <https://github.com/websharks/Quick Cache/issues/20>
306
+ * Added Raam Dev to the contributors list. Raam will now be leading the development of Quick Cache and Quick Cache Pro.
307
+
308
+ = v131128 =
309
+
310
+ * **New Plugin Architecture for Quick Cache.** This release introduces a new way for theme/plugin developers to modify the way Quick Cache operates at the `advanced-cache.php` level (e.g. very early-on). For further details on this, please check your Dashboard under: `Quick Cache -› Theme/Plugin Developers`. See also: <https://github.com/websharks/Quick Cache/issues/17>
311
+ * **Compatibility.** This release further improves PHP v5.3 detection. Quick Cache will now generate an administrative notice instead of a PHP exception; allowing the plugin to be activated, but without actually loading the plugin under this scenario. A notice to the site owner is helpful in cases where the plugin is NOT being updated through the Dashboard. This will remove the risk of crashing a site that's attempting to run Quick Cache w/o PHP v5.3+ installed. See also: <https://github.com/websharks/Quick Cache/issues/13>
312
+
313
+ = v131127 =
314
+
315
+ * **Compatibility.** This release improves PHP v5.3 detection. Quick Cache will now generate an administrative notice instead of a PHP exception; allowing the plugin to be activated, but without actually loading the plugin under this scenario. A notice to the site owner is helpful in cases where the plugin is NOT being updated through the Dashboard. This will remove the risk of crashing a site that's attempting to run Quick Cache w/o PHP v5.3+ installed. See also: <https://github.com/websharks/Quick Cache/issues/13>
316
+ * **New Pro Feature.** Clear Home Page (and/or Posts Page) on auto-purge. See: <https://github.com/websharks/Quick Cache/issues/11>
317
+ * **Bug Fix (Options -Indexes).** Removing unnecessary `.htaccess` file from the `/wp-content/plugins/Quick Cache/` directory that prevented directory indexing, as this is not compatible in all hosting environents. See: <https://github.com/websharks/Quick Cache/issues/9>
318
+ * **Bug Fix (ABSPATH).** Incorrect detection of the `/wp-config.php` file on sites that move this file up one directory. Fixed in this release. See: <https://github.com/websharks/Quick Cache/issues/7>
319
+ * **Bug Fix (Parse Error).** Correcting code that deals with an edge case where the `/wp-config.php` file may become corrupted upon deactivation of the Quick Cache plugin through the WP Dashboard. Fixed in this release. See: <https://github.com/websharks/Quick Cache/issues/6>
320
+ * **Bug Fix (Error Reporting).** Improving error message via Dashboard whenever permissions are an issue in one specific scenario. See: <https://github.com/websharks/Quick Cache/issues/16>
321
+ * **Enhancement (Pro Preview).** Adding a more visible way to disable Pro Preview mode in the Lite version of Quick Cache. See: <https://github.com/websharks/Quick Cache/issues/2>
322
+ * **Emergency Scenario** Adding notes in several sections of the `reamde.txt` file regarding "what to do in an emergency scenario".
323
+ * **See Also** <https://github.com/websharks/Quick Cache/issues?page=1&state=closed>
324
+
325
+ = v131121 =
326
+
327
+ * Updated to support all features and functionality of WordPress v3.7+ (this new release of Quick Cache requires WordPress v3.7+). The Quick Cache plugin is now being actively maintained and future updates and improvements will be released periodically by lead developer Jason Caldwell. The popularity of this plugin and recent acknowldegments at WordCamp in Boston have inspired Jason to revamp Quick Cache!
328
+ * The latest version of Quick Cache is a complete rewrite (OOP design). Faster! and even more dependable. NOTE: the free version of Quick Cache (this new LITE version); while it remains fully functional and is more-than-adequate for most sites; is now limited in some ways. The following advanced features from the previous release are no longer available in the lite version: a custom MD5 Version Salt; custom Exclusion Patterns; the Clear Cache button in the admin bar. These, and MANY other brand new features are now available only in the pro version of the plugin. For further details, please see: <http://www.websharks-inc.com/product/Quick Cache/>.
329
+ * Bug fix. Quick Cache now considers the `HTTPS` evironment variable in order to prevent cache collisions on sites that serve pages over SSL. Nothing to configure, this is now built into the Quick Cache engine.
330
+ * UI updates. An improved user interface makes configuring this plugin a dream! Quick Cache got an awesome makeover in this release.
331
+ * Improved support for multisite networks. It's never been easier to run Quick Cache on a multisite network. For further details, please see: **Dashboard -› Network Admin -› Quick Cache** when/if you have Multisite Networking enabled in WordPress.
332
+ * Update; PUT and DELETE requests are now considered by Quick Cache. By default, Quick Cache does NOT serve cached pages to users who are logged in, or to users who have left comments recently. Quick Cache also excludes administrative pages, login pages, POST/PUT/DELETE/GET(w/ query string) requests and/or CLI processes.
333
+ * Dropping support for `ob_gzhandler()`; and the like. Quick Cache will now throw PHP exceptions to warn you about this should it be an issue in your hosting environment. If you want to enable GZIP, please follow the instructions provided by Quick Cache and avoid the use of `ob_gzhandler()` as this is not a recommended way to enable GZIP on any hosting platform.
334
+ * Truly atomic cache file write updates. Removing support for SEM vs. FLOCK for file locking. Quick Cache no longer needs a mutex file. Cache file updates are written to a temp file and renamed for the best reliability and improved speed too!
335
+ * Localization. Quick Cache is now translatable. This release adds support for gettext translations, a very popular method for translating WordPress plugins. All parts of the Quick Cache plugin can be localized now. The source code was updated with calls to the `__` function and a new text domain was added: `Quick Cache`. PO translation files should be placed in your plugins directory, example: `/wp-content/plugins/Quick Cache-en_US.mo`; or in `WP_LANG_DIR/plugins/Quick Cache-en_US.mo`.
336
+ * Capability requirement. This release of Quick Cache requires that an Administrator be logged-in with the Capability of `activate_plugins`. This is a default Capability that comes with the Administrator Role in WordPress. So, unless you've modified your WordPress Roles/Capabilities in some extremely creative way, this should not impact you; just something to be aware of.
337
+ * **(Pro Version)** There is now a pro version of this plugin available. Please see: <http://www.websharks-inc.com/product/Quick Cache/>. The initial set of pro features include: the ability to cache logged-in users too! (VERY powerful, particularly for membership sites); a new improved "Clear Cache" button in the admin bar (along with an option to enable/disable this feature); the ability to disable Dashboard notifications related to automatic clearing/purging on change detections; Import/Export functionality for Quick Cache configuration files; URI exclusion patterns (now supporting wildcards too); User-Agent exclusion patterns (now supporting wildcards too); HTTP referrer exclusion patterns (now supporting wildcards too); an MD5 Version Salt; and rockstar support for all Quick Cache features.
338
+ * **(Pro Version)** Regarding URI/User-Agent/HTTP Referrer exclusion patterns. If you configured any of these options in the previous release and would like to continue to use them in this release, please upgrade to the pro version or contact lead developer Jason Caldwell for assistance. Note: if you had these options configured in the previous release, once you upgrade to the pro version they will come back just like they were. Either that, or you may choose to continue using the previous version of Quick Cache where this functionality still exists.
339
+ * Lite version source code now available on GitHub also: <https://github.com/websharks/Quick Cache>.
340
+
341
+ = v111203 =
342
+
343
+ * Updated to support WordPress® v3.3. Backward compatibily remains for WordPress® v3.2.x.
344
+
345
+ = v110720 =
346
+
347
+ * Bug fix. Corrected XSS security issue associated with the handling of ``$_SERVER["REQUEST_URI"]`` inside the comment lines that Quick Cache introduces at the bottom of the source code.
348
+ * Bug fix. Corrected cosmetic issue in WordPress v3.2 related to the positioning of the Clear Cache button.
349
+
350
+ = v110709 =
351
+
352
+ * Routine maintenance. No signifigant changes.
353
+
354
+ = v110708 =
355
+
356
+ * Routine maintenance. No signifigant changes.
357
+ * Compatibility with WordPress v3.2.
358
+
359
+ = v110523 =
360
+
361
+ * **Versioning.** Starting with this release, versions will follow this format: `yymmdd`. The version for this release is: `110523`.
362
+ * Routine maintenance. No signifigant changes.
363
+
364
+ = v2.3.6 =
365
+
366
+ * Routine maintenance. No signifigant changes.
367
+
368
+ = v2.3.5 =
369
+
370
+ * Bug fix. Under the right scenario, errors regarding the function `is_user_logged_in()` in the second phase of `advanced-cache.php` have been resolved in this release of Quick Cache.
371
+ * Compatibility. Quick Cache is now capable of dealing with themes/plugins that attempt to use `ob_start("ob_gzhandler")` inside a `header.php` file, or in other places that may create a problem in the nesting order of output buffers. For instance, this release of Quick Cache resolves some incompatiblities with Headway themes for WordPress®. Please note that GZIP should be enabled at the Apache level ( i.e. with an .htaccess file ), or in PHP using `zlib.output_compression = on`. Both of these methods are preferred over `ob_start("ob_gzhandler")`. If you must use `ob_start("ob_gzhandler")`, please make this declaration inside your `/wp-config.php` file, and NOT inside `/header.php`, as this creates a problem that Quick Cache must work around, and could ultimately prevent GZIP from working at all if you do it this way. For further details on how to enable GZIP with Quick Cache, please see the included `/readme.txt` file.
372
+
373
+ = v2.3.2 =
374
+
375
+ * Compatiblity. References to `dirname()` that were processed by the Quick Cache `/advanced-cache.php` handler should have been using `WP_CONTENT_DIR` for improved compatibility with WordPress® installations that may use non-standardized installation directories and/or symlinks.
376
+ * New Filter available for developers. Multisite Super Admins can now give their Child Blog owners the ability to manually clear the cache for their own site in the Network. Quick Cache accomplishes this by making the "Clear Cache" button visible in the administrative header for Child Blog owners. If you wish to enable this, you can use this Filter: `add_filter("ws_plugin__qcache_ms_user_can_see_admin_header_controls", "__return_true");`. This button is always visible to Super Admins. Adding this Filter makes it visible to all child Blog Owners as well.
377
+
378
+ = v2.3.1 =
379
+
380
+ * Framework updated; general cleanup.
381
+ * Optimizations. Further internal optimizations applied through configuration checksums that allow Quick Cache to load with even less overhead now.
382
+ * Bug fix. Quick Cache was suffering from a bug regression related to stale Last-Modified headers being sent with cached copies. This has been resolved in Quick Cache v2.3.1+.
383
+
384
+ = v2.3 =
385
+
386
+ * Framework updated; general cleanup.
387
+ * Updated with static class methods. Quick Cache now uses PHP's SPL autoload functionality to further optimize all of its routines.
388
+
389
+ = v2.2.8 =
390
+
391
+ * Framework updated; general cleanup.
392
+ * Updated for compatibility with WordPress® 3.1.
393
+
394
+ = v2.2.7 =
395
+
396
+ * Framework updated. General cleanup.
397
+
398
+ = v2.2.6 =
399
+
400
+ * Updated to disable caching on database failures that do not trigger a `5xx` error code. Quick Cache is now capable of disabling the cache engine dynamically on all database connection failures within WordPress®.
401
+
402
+ = v2.2.5 =
403
+
404
+ * Updated to support all `5xx` error codes. Quick Cache now monitors the `status_header` function for `5xx` error codes. If a `5xx` status header is detected, caching is automatically disabled, as it should be.
405
+
406
+ = v2.2.3 =
407
+
408
+ * Framework updated. General cleanup.
409
+
410
+ = v2.2.2 =
411
+
412
+ * Minor updates to the Ajax clearing routines that were implemented in v2.2.1.
413
+ * This update also adds compatibility with (offline) localhost installations of WordPress® (WAMP/MAMP).
414
+
415
+ = v2.2.1 =
416
+
417
+ * Support for `glob()` has been added to Quick Cache. In previous versions, it was impossible to pinpoint a specific cache file through Dynamic Pruning routines ( at least, not with 100% accuracy ). This was because an MD5 Version Salt *could* have been generated; based on arbitrary conditionals, set by the site owner. Quick Cache now stores its cache files with three MD5 hash strings, producing longer file names; but with the added benefit of improved Multisite compatibility, and improvements in optimization overall. Quick Cache can now handle dynamic pruning with 100% accuracy. Even supporting complex Multisite installations, with or without `SUBDOMAIN_INSTALL`.
418
+ * New feature. Quick Cache now integrates a `Clear Quick Cache` button into the WordPress® Dashboard. This makes it easy to force a "cache reset, via <code>ajax</code>", without having to navigate through the Quick Cache menu for this simple task. Another great benefit to this new button, is that it works in all Dashboard views, even in a Multisite installation across different backends. If you're running a Multisite installation, you can use this new button to clear the cache for a particular site/blog in your network, without interrupting others.
419
+ * Bug fix. The Constant `Quick Cache_ALLOWED` was being defined too early in the buffering routine. This has been resolved in v2.2.1.
420
+ * Optimization of `advanced-cache.php`. A few things have been streamlined even further.
421
+ * Added compatibility for the [WP Maintenance Mode](http://wordpress.org/extend/plugins/wp-maintenance-mode/) plugin, and also the [Maintenance Mode](http://wordpress.org/extend/plugins/maintenance-mode/) plugin. Quick Cache will disable itself when these plugins are enabled for maintenance.
422
+ * Added compatibility for other Maintenance Mode plugins that are capable of sending a `Status: 503` header, or a `Retry-After:` header.
423
+ * Added compatibility for plugins that create PHP sessions. Quick Cache will automatically disable itself when a PHP session is active.
424
+ * Added compatiblity for web hosts that insert a port number into the `$_SERVER["HTTP_HOST"]` variable. Quick Cache is now capable of handling this gracefully.
425
+ * Improvement. Removed references to `$blog_id = 1` in favor of `is_main_site()`; providing support for Multisite Mode, where there are multiple sites, instead of just multiple blogs.
426
+ * Updated Dynamic Pruning Hooks for Custom Post Types, and Custom Taxonomies in WordPress® 3.0+.
427
+ * Extended compatiblity for Quick Cache on SSL enabled blogs.
428
+
429
+ = v2.1.9 =
430
+
431
+ * Framework updated; general cleanup.
432
+ * Updated minimum requirements to WordPress® 3.0.
433
+
434
+ = v2.1.8 =
435
+
436
+ * Framework updated to WS-P-3.0.
437
+
438
+ = v2.1.7 =
439
+
440
+ * Bug fix. A bug related to gzinflate variations handled by the WP_Http class has been resolved. This was preventing Quick Cache from validating a custom MD5 Version Salt on some servers.
441
+ * Framework updated to WS-P-2.3.
442
+
443
+ = v2.1.6 =
444
+
445
+ * Auto-Cache Engine. References to `ws_plugin__qcache_curl_get()`, have been replaced by `c_ws_plugin__qcache_utils_urls::remote()`, which makes use of `wp_remote_request()` through the WP_Http class. This removes an absolute dependency on the cURL extension for PHP. This also gives Quick Cache/WordPress® the ability to decide with method of communication to use for HTTP requests; based on what the installation server has available. Note: this only affects the Auto-Cache Engine for Quick Cache, which is completely optional.
446
+ * Compatibility. Quick Cache is now smarter about the way it reports errors. For example, when/if there are directory permission issues with your `wp-content` directory; Quick Cache can help with this, in a more intuitive fashion.
447
+ * Compatibility. Support has been added for WordPress® 3.0 with Multisite/Networking enabled.
448
+ * Updated minimum requirements to WordPress® 2.9.2.
449
+ * Framework updated to WS-P-2.2.
450
+
451
+ = v2.1.5 =
452
+
453
+ * A new option for Dynamic Cache Pruning was added. You can now choose `Single + Front Page`. This makes it possible to Create or Edit a Post/Page, and have the cache automatically updated for that specific Post/Page. And.. in addition, your Front Page ( aka: Home Page ) will also be refreshed at the same time.
454
+ * A minor bug was fixed in the Dynamic Cache Pruning routines. This bug was originally introduced in Quick Cache v2.1.1, and has now been corrected in v2.1.5. This bug, under certain circumstances, was preventing Quick Cache from locating an expired md5 cache file, for some Posts/Pages being updated.
455
+ * Advanced feature addition. Quick Cache now comes bundled with a robust Auto-Cache Engine. This is an optional feature, for VERY advanced users. You'll find the new Auto-Cache Engine listed right along with all of the other Quick Cache options. This works in conjunction with an XML Sitemap.
456
+
457
+ = v2.1.4 =
458
+
459
+ * Advanced feature addition. You can now prevent caching dynamically whenever pages on your site receive traffic from specific URLs, specific domains, or even specific word fragments found within the HTTP_REFERER. This feature is very advanced, and will NOT impact your site unless you decide to use it for one reason or another.
460
+
461
+ = v2.1.3 =
462
+
463
+ * Added `De-Activation Safeguards` to the Quick Cache options panel.
464
+ * Updated the Quick Cache options panel. It's been given a make-over.
465
+ * Stable tag updated in support of tagged releases within the repository at WordPress.org.
466
+
467
+ = v2.1.2 =
468
+
469
+ * WebSharks Framework for Plugins has been updated to P-2.1.
470
+ * Updated caching routines in support of hosting providers running with CGI/FastCGI. Quick Cache has been tested with VPS.net, HostGator, BlueHost, (mt) Media Temple (gs) and (dv), The Rackspace Cloud, and several dedicated servers ( including some Amazon EC2 instances ) running with Apache; including support for both `mod_php` and also `CGI/FastCGI` implementations. Quick Cache should work fine with any Apache/PHP combination. Please report all bugs through the [Support Forum](http://www.primothemes.com/forums/viewforum.php?f=5).
471
+ * An issue was discovered with WordPress® MU `/files/` being accessed through `htaccess/mod_rewrite`. Quick Cache has been updated to exclude all `/files/` served under WordPress® MU, which is the way it should be. Requests that contain `/files/` are a reference to WordPress® Media, and there is no reason, to cache, or send no-cache headers, for Media. Quick Cache now ignores all references to `/files/` under WordPress® MU. This problem was not affecting all installations of WPMU, because there already are/were scans in place for Content-Type headers. However, under some CGI/FastCGI implementations, this was not getting picked on WMPU with `mod_rewrite` rules. This has been resolved in v2.1.2.
472
+
473
+ = v2.1.1 =
474
+
475
+ * A WPMU bug was corrected in Quick Cache v2.1.1. This bug was related to `HTTP_HOST` detection under WordPress® MU installations that were using sub-domains. Please thank `QuickSander` for reporting this important issue.
476
+
477
+ = v2.1 =
478
+
479
+ * Quick Cache has added further support for themes and plugins that dynamically set `Content-Type` headers through PHP routines. Quick Cache is now smart enough to automatically disable itself whenever a theme or plugin sends a `Content-Type` header that would be incompatible with Quick Cache. In other words, any `Content-Type` header that is not a variation of `HTML, XHTML or XML`.
480
+ * Quick Cache has also been upgraded to support the preservation of scripted headers sent by PHP routines. If a plugin or theme sends scripted headers ( using the `header()` function in PHP ), those headers will be preserved. They'll be stored along with the cache. This allows them to be sent back to the browser whenever a cached version is served on subsequent visits to the original file.
481
+ * Compatability checked against WordPress.org 2.9.1, 2.9.2 &amp; WordPress MU 2.9.1, 2.9.2. Everything looks good. No changes required.
482
+
483
+ = v2.0 =
484
+
485
+ * A few tweaks to the options panel.
486
+ * Documentation updated, several small improvements in error reporting.
487
+ * Additional error checking to support an even wider range of hosting providers.
488
+ * Added automation routines for safe re-activation after an upgrade is performed.
489
+
490
+ = v1.9 =
491
+
492
+ * Additional support added for WordPress® MU 2.8.6+.
493
+ * Security file `Quick Cache-mu.php` added specifically for MU installations. WordPress® MU is a special ( multi-user ) version of WordPress®. If you're running WordPress® MU, check the [readme.txt] file for WordPress® MU notations.
494
+
495
+ = v1.8 =
496
+
497
+ * Re-organized core framework. Updated to: P-2.0.
498
+ * Updated to support WP 2.9+.
499
+
500
+ = v1.7 =
501
+
502
+ * Updated documentation. Added some additional code samples.
503
+ * Tested with WP 2.8.5. Everything ok.
504
+
505
+ = v1.6 =
506
+
507
+ * We've added the ability to enable Double-Caching ( client-side caching ). Full documentation is provided in the Quick Cache options panel. This feature is for those of you who just want blazing fast speed and are not concerned as much about reliability and control. We don't recommend turning this on unless you realize what you're doing.
508
+
509
+ = v1.5 =
510
+
511
+ * Support for Dynamic Cache Pruning has been improved. Full documentation is provided in the Quick Cache options panel.
512
+ * Additional feature-specific documentation has been added to assist novice webmasters during configuration.
513
+
514
+ = v1.4 =
515
+
516
+ * Garbage collection has been further optimized for speed and performance on extremely high traffic sites.
517
+ * PHP Ternary expressions are now supported in your Version Salt. This takes your Version Salt to a whole new level.
518
+ * Additional code samples have been provided for Version Salts; showing you how to deal with mobile devices and other tricky situations.
519
+
520
+ = v1.3 =
521
+
522
+ * We've implemented both Semaphore ( `sem_get` ) and `flock()` mutex. If you're on a Cloud Computing Model ( such as the Rackspace® Cloud ), then you'll want to go with flock() unless they tell you otherwise. In all other cases we recommend the use of Semaphores over Flock because it is generally more reliable. The folks over at Rackspace® have suggested the use of flock() because of the way their Cloud handles multi-threading. In either case, flock() will be fully functional in any hosting environment, so it makes a great fallback in case you experience any problems.
523
+
524
+ = v1.2 =
525
+
526
+ * We've implemented a way for plugin developers to disallow caching during certain routines or on specific pages. You can set the following PHP Constant at runtime to disable caching. `define("Quick Cache_ALLOWED", false)`. We have also added backward compatibility for WP Super Cache, so that `define("DONOTCACHEPAGE", true)` will also be supported by plugins that have previously been written for compatibility with Super Cache. In other words, Quick Cache looks for either of these two Constants.
527
+
528
+ = v1.1 =
529
+
530
+ * Added the ability to create a Version Salt. This is a feature offered ONLY by Quick Cache. Full documentation is provided in the Quick Cache options panel. This can become very useful for sites that provide membership services or have lots and lots of plugins installed that makes their site incompatible with WP Super Cache. With Quick Cache, you'll now have more control over the entire caching process using a custom Version Salt tailored to your specific needs.
531
+
532
+ = v1.0 =
533
+
534
+ * Initial release.
LICENSE.txt ADDED
@@ -0,0 +1,676 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ GNU GENERAL PUBLIC LICENSE
4
+ Version 3, 29 June 2007
5
+
6
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
7
+ Everyone is permitted to copy and distribute verbatim copies
8
+ of this license document, but changing it is not allowed.
9
+
10
+ Preamble
11
+
12
+ The GNU General Public License is a free, copyleft license for
13
+ software and other kinds of works.
14
+
15
+ The licenses for most software and other practical works are designed
16
+ to take away your freedom to share and change the works. By contrast,
17
+ the GNU General Public License is intended to guarantee your freedom to
18
+ share and change all versions of a program--to make sure it remains free
19
+ software for all its users. We, the Free Software Foundation, use the
20
+ GNU General Public License for most of our software; it applies also to
21
+ any other work released this way by its authors. You can apply it to
22
+ your programs, too.
23
+
24
+ When we speak of free software, we are referring to freedom, not
25
+ price. Our General Public Licenses are designed to make sure that you
26
+ have the freedom to distribute copies of free software (and charge for
27
+ them if you wish), that you receive source code or can get it if you
28
+ want it, that you can change the software or use pieces of it in new
29
+ free programs, and that you know you can do these things.
30
+
31
+ To protect your rights, we need to prevent others from denying you
32
+ these rights or asking you to surrender the rights. Therefore, you have
33
+ certain responsibilities if you distribute copies of the software, or if
34
+ you modify it: responsibilities to respect the freedom of others.
35
+
36
+ For example, if you distribute copies of such a program, whether
37
+ gratis or for a fee, you must pass on to the recipients the same
38
+ freedoms that you received. You must make sure that they, too, receive
39
+ or can get the source code. And you must show them these terms so they
40
+ know their rights.
41
+
42
+ Developers that use the GNU GPL protect your rights with two steps:
43
+ (1) assert copyright on the software, and (2) offer you this License
44
+ giving you legal permission to copy, distribute and/or modify it.
45
+
46
+ For the developers' and authors' protection, the GPL clearly explains
47
+ that there is no warranty for this free software. For both users' and
48
+ authors' sake, the GPL requires that modified versions be marked as
49
+ changed, so that their problems will not be attributed erroneously to
50
+ authors of previous versions.
51
+
52
+ Some devices are designed to deny users access to install or run
53
+ modified versions of the software inside them, although the manufacturer
54
+ can do so. This is fundamentally incompatible with the aim of
55
+ protecting users' freedom to change the software. The systematic
56
+ pattern of such abuse occurs in the area of products for individuals to
57
+ use, which is precisely where it is most unacceptable. Therefore, we
58
+ have designed this version of the GPL to prohibit the practice for those
59
+ products. If such problems arise substantially in other domains, we
60
+ stand ready to extend this provision to those domains in future versions
61
+ of the GPL, as needed to protect the freedom of users.
62
+
63
+ Finally, every program is threatened constantly by software patents.
64
+ States should not allow patents to restrict development and use of
65
+ software on general-purpose computers, but in those that do, we wish to
66
+ avoid the special danger that patents applied to a free program could
67
+ make it effectively proprietary. To prevent this, the GPL assures that
68
+ patents cannot be used to render the program non-free.
69
+
70
+ The precise terms and conditions for copying, distribution and
71
+ modification follow.
72
+
73
+ TERMS AND CONDITIONS
74
+
75
+ 0. Definitions.
76
+
77
+ "This License" refers to version 3 of the GNU General Public License.
78
+
79
+ "Copyright" also means copyright-like laws that apply to other kinds of
80
+ works, such as semiconductor masks.
81
+
82
+ "The Program" refers to any copyrightable work licensed under this
83
+ License. Each licensee is addressed as "you". "Licensees" and
84
+ "recipients" may be individuals or organizations.
85
+
86
+ To "modify" a work means to copy from or adapt all or part of the work
87
+ in a fashion requiring copyright permission, other than the making of an
88
+ exact copy. The resulting work is called a "modified version" of the
89
+ earlier work or a work "based on" the earlier work.
90
+
91
+ A "covered work" means either the unmodified Program or a work based
92
+ on the Program.
93
+
94
+ To "propagate" a work means to do anything with it that, without
95
+ permission, would make you directly or secondarily liable for
96
+ infringement under applicable copyright law, except executing it on a
97
+ computer or modifying a private copy. Propagation includes copying,
98
+ distribution (with or without modification), making available to the
99
+ public, and in some countries other activities as well.
100
+
101
+ To "convey" a work means any kind of propagation that enables other
102
+ parties to make or receive copies. Mere interaction with a user through
103
+ a computer network, with no transfer of a copy, is not conveying.
104
+
105
+ An interactive user interface displays "Appropriate Legal Notices"
106
+ to the extent that it includes a convenient and prominently visible
107
+ feature that (1) displays an appropriate copyright notice, and (2)
108
+ tells the user that there is no warranty for the work (except to the
109
+ extent that warranties are provided), that licensees may convey the
110
+ work under this License, and how to view a copy of this License. If
111
+ the interface presents a list of user commands or options, such as a
112
+ menu, a prominent item in the list meets this criterion.
113
+
114
+ 1. Source Code.
115
+
116
+ The "source code" for a work means the preferred form of the work
117
+ for making modifications to it. "Object code" means any non-source
118
+ form of a work.
119
+
120
+ A "Standard Interface" means an interface that either is an official
121
+ standard defined by a recognized standards body, or, in the case of
122
+ interfaces specified for a particular programming language, one that
123
+ is widely used among developers working in that language.
124
+
125
+ The "System Libraries" of an executable work include anything, other
126
+ than the work as a whole, that (a) is included in the normal form of
127
+ packaging a Major Component, but which is not part of that Major
128
+ Component, and (b) serves only to enable use of the work with that
129
+ Major Component, or to implement a Standard Interface for which an
130
+ implementation is available to the public in source code form. A
131
+ "Major Component", in this context, means a major essential component
132
+ (kernel, window system, and so on) of the specific operating system
133
+ (if any) on which the executable work runs, or a compiler used to
134
+ produce the work, or an object code interpreter used to run it.
135
+
136
+ The "Corresponding Source" for a work in object code form means all
137
+ the source code needed to generate, install, and (for an executable
138
+ work) run the object code and to modify the work, including scripts to
139
+ control those activities. However, it does not include the work's
140
+ System Libraries, or general-purpose tools or generally available free
141
+ programs which are used unmodified in performing those activities but
142
+ which are not part of the work. For example, Corresponding Source
143
+ includes interface definition files associated with source files for
144
+ the work, and the source code for shared libraries and dynamically
145
+ linked subprograms that the work is specifically designed to require,
146
+ such as by intimate data communication or control flow between those
147
+ subprograms and other parts of the work.
148
+
149
+ The Corresponding Source need not include anything that users
150
+ can regenerate automatically from other parts of the Corresponding
151
+ Source.
152
+
153
+ The Corresponding Source for a work in source code form is that
154
+ same work.
155
+
156
+ 2. Basic Permissions.
157
+
158
+ All rights granted under this License are granted for the term of
159
+ copyright on the Program, and are irrevocable provided the stated
160
+ conditions are met. This License explicitly affirms your unlimited
161
+ permission to run the unmodified Program. The output from running a
162
+ covered work is covered by this License only if the output, given its
163
+ content, constitutes a covered work. This License acknowledges your
164
+ rights of fair use or other equivalent, as provided by copyright law.
165
+
166
+ You may make, run and propagate covered works that you do not
167
+ convey, without conditions so long as your license otherwise remains
168
+ in force. You may convey covered works to others for the sole purpose
169
+ of having them make modifications exclusively for you, or provide you
170
+ with facilities for running those works, provided that you comply with
171
+ the terms of this License in conveying all material for which you do
172
+ not control copyright. Those thus making or running the covered works
173
+ for you must do so exclusively on your behalf, under your direction
174
+ and control, on terms that prohibit them from making any copies of
175
+ your copyrighted material outside their relationship with you.
176
+
177
+ Conveying under any other circumstances is permitted solely under
178
+ the conditions stated below. Sublicensing is not allowed; section 10
179
+ makes it unnecessary.
180
+
181
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
182
+
183
+ No covered work shall be deemed part of an effective technological
184
+ measure under any applicable law fulfilling obligations under article
185
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
186
+ similar laws prohibiting or restricting circumvention of such
187
+ measures.
188
+
189
+ When you convey a covered work, you waive any legal power to forbid
190
+ circumvention of technological measures to the extent such circumvention
191
+ is effected by exercising rights under this License with respect to
192
+ the covered work, and you disclaim any intention to limit operation or
193
+ modification of the work as a means of enforcing, against the work's
194
+ users, your or third parties' legal rights to forbid circumvention of
195
+ technological measures.
196
+
197
+ 4. Conveying Verbatim Copies.
198
+
199
+ You may convey verbatim copies of the Program's source code as you
200
+ receive it, in any medium, provided that you conspicuously and
201
+ appropriately publish on each copy an appropriate copyright notice;
202
+ keep intact all notices stating that this License and any
203
+ non-permissive terms added in accord with section 7 apply to the code;
204
+ keep intact all notices of the absence of any warranty; and give all
205
+ recipients a copy of this License along with the Program.
206
+
207
+ You may charge any price or no price for each copy that you convey,
208
+ and you may offer support or warranty protection for a fee.
209
+
210
+ 5. Conveying Modified Source Versions.
211
+
212
+ You may convey a work based on the Program, or the modifications to
213
+ produce it from the Program, in the form of source code under the
214
+ terms of section 4, provided that you also meet all of these conditions:
215
+
216
+ a) The work must carry prominent notices stating that you modified
217
+ it, and giving a relevant date.
218
+
219
+ b) The work must carry prominent notices stating that it is
220
+ released under this License and any conditions added under section
221
+ 7. This requirement modifies the requirement in section 4 to
222
+ "keep intact all notices".
223
+
224
+ c) You must license the entire work, as a whole, under this
225
+ License to anyone who comes into possession of a copy. This
226
+ License will therefore apply, along with any applicable section 7
227
+ additional terms, to the whole of the work, and all its parts,
228
+ regardless of how they are packaged. This License gives no
229
+ permission to license the work in any other way, but it does not
230
+ invalidate such permission if you have separately received it.
231
+
232
+ d) If the work has interactive user interfaces, each must display
233
+ Appropriate Legal Notices; however, if the Program has interactive
234
+ interfaces that do not display Appropriate Legal Notices, your
235
+ work need not make them do so.
236
+
237
+ A compilation of a covered work with other separate and independent
238
+ works, which are not by their nature extensions of the covered work,
239
+ and which are not combined with it such as to form a larger program,
240
+ in or on a volume of a storage or distribution medium, is called an
241
+ "aggregate" if the compilation and its resulting copyright are not
242
+ used to limit the access or legal rights of the compilation's users
243
+ beyond what the individual works permit. Inclusion of a covered work
244
+ in an aggregate does not cause this License to apply to the other
245
+ parts of the aggregate.
246
+
247
+ 6. Conveying Non-Source Forms.
248
+
249
+ You may convey a covered work in object code form under the terms
250
+ of sections 4 and 5, provided that you also convey the
251
+ machine-readable Corresponding Source under the terms of this License,
252
+ in one of these ways:
253
+
254
+ a) Convey the object code in, or embodied in, a physical product
255
+ (including a physical distribution medium), accompanied by the
256
+ Corresponding Source fixed on a durable physical medium
257
+ customarily used for software interchange.
258
+
259
+ b) Convey the object code in, or embodied in, a physical product
260
+ (including a physical distribution medium), accompanied by a
261
+ written offer, valid for at least three years and valid for as
262
+ long as you offer spare parts or customer support for that product
263
+ model, to give anyone who possesses the object code either (1) a
264
+ copy of the Corresponding Source for all the software in the
265
+ product that is covered by this License, on a durable physical
266
+ medium customarily used for software interchange, for a price no
267
+ more than your reasonable cost of physically performing this
268
+ conveying of source, or (2) access to copy the
269
+ Corresponding Source from a network server at no charge.
270
+
271
+ c) Convey individual copies of the object code with a copy of the
272
+ written offer to provide the Corresponding Source. This
273
+ alternative is allowed only occasionally and noncommercially, and
274
+ only if you received the object code with such an offer, in accord
275
+ with subsection 6b.
276
+
277
+ d) Convey the object code by offering access from a designated
278
+ place (gratis or for a charge), and offer equivalent access to the
279
+ Corresponding Source in the same way through the same place at no
280
+ further charge. You need not require recipients to copy the
281
+ Corresponding Source along with the object code. If the place to
282
+ copy the object code is a network server, the Corresponding Source
283
+ may be on a different server (operated by you or a third party)
284
+ that supports equivalent copying facilities, provided you maintain
285
+ clear directions next to the object code saying where to find the
286
+ Corresponding Source. Regardless of what server hosts the
287
+ Corresponding Source, you remain obligated to ensure that it is
288
+ available for as long as needed to satisfy these requirements.
289
+
290
+ e) Convey the object code using peer-to-peer transmission, provided
291
+ you inform other peers where the object code and Corresponding
292
+ Source of the work are being offered to the general public at no
293
+ charge under subsection 6d.
294
+
295
+ A separable portion of the object code, whose source code is excluded
296
+ from the Corresponding Source as a System Library, need not be
297
+ included in conveying the object code work.
298
+
299
+ A "User Product" is either (1) a "consumer product", which means any
300
+ tangible personal property which is normally used for personal, family,
301
+ or household purposes, or (2) anything designed or sold for incorporation
302
+ into a dwelling. In determining whether a product is a consumer product,
303
+ doubtful cases shall be resolved in favor of coverage. For a particular
304
+ product received by a particular user, "normally used" refers to a
305
+ typical or common use of that class of product, regardless of the status
306
+ of the particular user or of the way in which the particular user
307
+ actually uses, or expects or is expected to use, the product. A product
308
+ is a consumer product regardless of whether the product has substantial
309
+ commercial, industrial or non-consumer uses, unless such uses represent
310
+ the only significant mode of use of the product.
311
+
312
+ "Installation Information" for a User Product means any methods,
313
+ procedures, authorization keys, or other information required to install
314
+ and execute modified versions of a covered work in that User Product from
315
+ a modified version of its Corresponding Source. The information must
316
+ suffice to ensure that the continued functioning of the modified object
317
+ code is in no case prevented or interfered with solely because
318
+ modification has been made.
319
+
320
+ If you convey an object code work under this section in, or with, or
321
+ specifically for use in, a User Product, and the conveying occurs as
322
+ part of a transaction in which the right of possession and use of the
323
+ User Product is transferred to the recipient in perpetuity or for a
324
+ fixed term (regardless of how the transaction is characterized), the
325
+ Corresponding Source conveyed under this section must be accompanied
326
+ by the Installation Information. But this requirement does not apply
327
+ if neither you nor any third party retains the ability to install
328
+ modified object code on the User Product (for example, the work has
329
+ been installed in ROM).
330
+
331
+ The requirement to provide Installation Information does not include a
332
+ requirement to continue to provide support service, warranty, or updates
333
+ for a work that has been modified or installed by the recipient, or for
334
+ the User Product in which it has been modified or installed. Access to a
335
+ network may be denied when the modification itself materially and
336
+ adversely affects the operation of the network or violates the rules and
337
+ protocols for communication across the network.
338
+
339
+ Corresponding Source conveyed, and Installation Information provided,
340
+ in accord with this section must be in a format that is publicly
341
+ documented (and with an implementation available to the public in
342
+ source code form), and must require no special password or key for
343
+ unpacking, reading or copying.
344
+
345
+ 7. Additional Terms.
346
+
347
+ "Additional permissions" are terms that supplement the terms of this
348
+ License by making exceptions from one or more of its conditions.
349
+ Additional permissions that are applicable to the entire Program shall
350
+ be treated as though they were included in this License, to the extent
351
+ that they are valid under applicable law. If additional permissions
352
+ apply only to part of the Program, that part may be used separately
353
+ under those permissions, but the entire Program remains governed by
354
+ this License without regard to the additional permissions.
355
+
356
+ When you convey a copy of a covered work, you may at your option
357
+ remove any additional permissions from that copy, or from any part of
358
+ it. (Additional permissions may be written to require their own
359
+ removal in certain cases when you modify the work.) You may place
360
+ additional permissions on material, added by you to a covered work,
361
+ for which you have or can give appropriate copyright permission.
362
+
363
+ Notwithstanding any other provision of this License, for material you
364
+ add to a covered work, you may (if authorized by the copyright holders of
365
+ that material) supplement the terms of this License with terms:
366
+
367
+ a) Disclaiming warranty or limiting liability differently from the
368
+ terms of sections 15 and 16 of this License; or
369
+
370
+ b) Requiring preservation of specified reasonable legal notices or
371
+ author attributions in that material or in the Appropriate Legal
372
+ Notices displayed by works containing it; or
373
+
374
+ c) Prohibiting misrepresentation of the origin of that material, or
375
+ requiring that modified versions of such material be marked in
376
+ reasonable ways as different from the original version; or
377
+
378
+ d) Limiting the use for publicity purposes of names of licensors or
379
+ authors of the material; or
380
+
381
+ e) Declining to grant rights under trademark law for use of some
382
+ trade names, trademarks, or service marks; or
383
+
384
+ f) Requiring indemnification of licensors and authors of that
385
+ material by anyone who conveys the material (or modified versions of
386
+ it) with contractual assumptions of liability to the recipient, for
387
+ any liability that these contractual assumptions directly impose on
388
+ those licensors and authors.
389
+
390
+ All other non-permissive additional terms are considered "further
391
+ restrictions" within the meaning of section 10. If the Program as you
392
+ received it, or any part of it, contains a notice stating that it is
393
+ governed by this License along with a term that is a further
394
+ restriction, you may remove that term. If a license document contains
395
+ a further restriction but permits relicensing or conveying under this
396
+ License, you may add to a covered work material governed by the terms
397
+ of that license document, provided that the further restriction does
398
+ not survive such relicensing or conveying.
399
+
400
+ If you add terms to a covered work in accord with this section, you
401
+ must place, in the relevant source files, a statement of the
402
+ additional terms that apply to those files, or a notice indicating
403
+ where to find the applicable terms.
404
+
405
+ Additional terms, permissive or non-permissive, may be stated in the
406
+ form of a separately written license, or stated as exceptions;
407
+ the above requirements apply either way.
408
+
409
+ 8. Termination.
410
+
411
+ You may not propagate or modify a covered work except as expressly
412
+ provided under this License. Any attempt otherwise to propagate or
413
+ modify it is void, and will automatically terminate your rights under
414
+ this License (including any patent licenses granted under the third
415
+ paragraph of section 11).
416
+
417
+ However, if you cease all violation of this License, then your
418
+ license from a particular copyright holder is reinstated (a)
419
+ provisionally, unless and until the copyright holder explicitly and
420
+ finally terminates your license, and (b) permanently, if the copyright
421
+ holder fails to notify you of the violation by some reasonable means
422
+ prior to 60 days after the cessation.
423
+
424
+ Moreover, your license from a particular copyright holder is
425
+ reinstated permanently if the copyright holder notifies you of the
426
+ violation by some reasonable means, this is the first time you have
427
+ received notice of violation of this License (for any work) from that
428
+ copyright holder, and you cure the violation prior to 30 days after
429
+ your receipt of the notice.
430
+
431
+ Termination of your rights under this section does not terminate the
432
+ licenses of parties who have received copies or rights from you under
433
+ this License. If your rights have been terminated and not permanently
434
+ reinstated, you do not qualify to receive new licenses for the same
435
+ material under section 10.
436
+
437
+ 9. Acceptance Not Required for Having Copies.
438
+
439
+ You are not required to accept this License in order to receive or
440
+ run a copy of the Program. Ancillary propagation of a covered work
441
+ occurring solely as a consequence of using peer-to-peer transmission
442
+ to receive a copy likewise does not require acceptance. However,
443
+ nothing other than this License grants you permission to propagate or
444
+ modify any covered work. These actions infringe copyright if you do
445
+ not accept this License. Therefore, by modifying or propagating a
446
+ covered work, you indicate your acceptance of this License to do so.
447
+
448
+ 10. Automatic Licensing of Downstream Recipients.
449
+
450
+ Each time you convey a covered work, the recipient automatically
451
+ receives a license from the original licensors, to run, modify and
452
+ propagate that work, subject to this License. You are not responsible
453
+ for enforcing compliance by third parties with this License.
454
+
455
+ An "entity transaction" is a transaction transferring control of an
456
+ organization, or substantially all assets of one, or subdividing an
457
+ organization, or merging organizations. If propagation of a covered
458
+ work results from an entity transaction, each party to that
459
+ transaction who receives a copy of the work also receives whatever
460
+ licenses to the work the party's predecessor in interest had or could
461
+ give under the previous paragraph, plus a right to possession of the
462
+ Corresponding Source of the work from the predecessor in interest, if
463
+ the predecessor has it or can get it with reasonable efforts.
464
+
465
+ You may not impose any further restrictions on the exercise of the
466
+ rights granted or affirmed under this License. For example, you may
467
+ not impose a license fee, royalty, or other charge for exercise of
468
+ rights granted under this License, and you may not initiate litigation
469
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
470
+ any patent claim is infringed by making, using, selling, offering for
471
+ sale, or importing the Program or any portion of it.
472
+
473
+ 11. Patents.
474
+
475
+ A "contributor" is a copyright holder who authorizes use under this
476
+ License of the Program or a work on which the Program is based. The
477
+ work thus licensed is called the contributor's "contributor version".
478
+
479
+ A contributor's "essential patent claims" are all patent claims
480
+ owned or controlled by the contributor, whether already acquired or
481
+ hereafter acquired, that would be infringed by some manner, permitted
482
+ by this License, of making, using, or selling its contributor version,
483
+ but do not include claims that would be infringed only as a
484
+ consequence of further modification of the contributor version. For
485
+ purposes of this definition, "control" includes the right to grant
486
+ patent sublicenses in a manner consistent with the requirements of
487
+ this License.
488
+
489
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
490
+ patent license under the contributor's essential patent claims, to
491
+ make, use, sell, offer for sale, import and otherwise run, modify and
492
+ propagate the contents of its contributor version.
493
+
494
+ In the following three paragraphs, a "patent license" is any express
495
+ agreement or commitment, however denominated, not to enforce a patent
496
+ (such as an express permission to practice a patent or covenant not to
497
+ sue for patent infringement). To "grant" such a patent license to a
498
+ party means to make such an agreement or commitment not to enforce a
499
+ patent against the party.
500
+
501
+ If you convey a covered work, knowingly relying on a patent license,
502
+ and the Corresponding Source of the work is not available for anyone
503
+ to copy, free of charge and under the terms of this License, through a
504
+ publicly available network server or other readily accessible means,
505
+ then you must either (1) cause the Corresponding Source to be so
506
+ available, or (2) arrange to deprive yourself of the benefit of the
507
+ patent license for this particular work, or (3) arrange, in a manner
508
+ consistent with the requirements of this License, to extend the patent
509
+ license to downstream recipients. "Knowingly relying" means you have
510
+ actual knowledge that, but for the patent license, your conveying the
511
+ covered work in a country, or your recipient's use of the covered work
512
+ in a country, would infringe one or more identifiable patents in that
513
+ country that you have reason to believe are valid.
514
+
515
+ If, pursuant to or in connection with a single transaction or
516
+ arrangement, you convey, or propagate by procuring conveyance of, a
517
+ covered work, and grant a patent license to some of the parties
518
+ receiving the covered work authorizing them to use, propagate, modify
519
+ or convey a specific copy of the covered work, then the patent license
520
+ you grant is automatically extended to all recipients of the covered
521
+ work and works based on it.
522
+
523
+ A patent license is "discriminatory" if it does not include within
524
+ the scope of its coverage, prohibits the exercise of, or is
525
+ conditioned on the non-exercise of one or more of the rights that are
526
+ specifically granted under this License. You may not convey a covered
527
+ work if you are a party to an arrangement with a third party that is
528
+ in the business of distributing software, under which you make payment
529
+ to the third party based on the extent of your activity of conveying
530
+ the work, and under which the third party grants, to any of the
531
+ parties who would receive the covered work from you, a discriminatory
532
+ patent license (a) in connection with copies of the covered work
533
+ conveyed by you (or copies made from those copies), or (b) primarily
534
+ for and in connection with specific products or compilations that
535
+ contain the covered work, unless you entered into that arrangement,
536
+ or that patent license was granted, prior to 28 March 2007.
537
+
538
+ Nothing in this License shall be construed as excluding or limiting
539
+ any implied license or other defenses to infringement that may
540
+ otherwise be available to you under applicable patent law.
541
+
542
+ 12. No Surrender of Others' Freedom.
543
+
544
+ If conditions are imposed on you (whether by court order, agreement or
545
+ otherwise) that contradict the conditions of this License, they do not
546
+ excuse you from the conditions of this License. If you cannot convey a
547
+ covered work so as to satisfy simultaneously your obligations under this
548
+ License and any other pertinent obligations, then as a consequence you may
549
+ not convey it at all. For example, if you agree to terms that obligate you
550
+ to collect a royalty for further conveying from those to whom you convey
551
+ the Program, the only way you could satisfy both those terms and this
552
+ License would be to refrain entirely from conveying the Program.
553
+
554
+ 13. Use with the GNU Affero General Public License.
555
+
556
+ Notwithstanding any other provision of this License, you have
557
+ permission to link or combine any covered work with a work licensed
558
+ under version 3 of the GNU Affero General Public License into a single
559
+ combined work, and to convey the resulting work. The terms of this
560
+ License will continue to apply to the part which is the covered work,
561
+ but the special requirements of the GNU Affero General Public License,
562
+ section 13, concerning interaction through a network will apply to the
563
+ combination as such.
564
+
565
+ 14. Revised Versions of this License.
566
+
567
+ The Free Software Foundation may publish revised and/or new versions of
568
+ the GNU General Public License from time to time. Such new versions will
569
+ be similar in spirit to the present version, but may differ in detail to
570
+ address new problems or concerns.
571
+
572
+ Each version is given a distinguishing version number. If the
573
+ Program specifies that a certain numbered version of the GNU General
574
+ Public License "or any later version" applies to it, you have the
575
+ option of following the terms and conditions either of that numbered
576
+ version or of any later version published by the Free Software
577
+ Foundation. If the Program does not specify a version number of the
578
+ GNU General Public License, you may choose any version ever published
579
+ by the Free Software Foundation.
580
+
581
+ If the Program specifies that a proxy can decide which future
582
+ versions of the GNU General Public License can be used, that proxy's
583
+ public statement of acceptance of a version permanently authorizes you
584
+ to choose that version for the Program.
585
+
586
+ Later license versions may give you additional or different
587
+ permissions. However, no additional obligations are imposed on any
588
+ author or copyright holder as a result of your choosing to follow a
589
+ later version.
590
+
591
+ 15. Disclaimer of Warranty.
592
+
593
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
594
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
595
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
596
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
597
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
598
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
599
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
600
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
601
+
602
+ 16. Limitation of Liability.
603
+
604
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
605
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
606
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
607
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
608
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
609
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
610
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
611
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
612
+ SUCH DAMAGES.
613
+
614
+ 17. Interpretation of Sections 15 and 16.
615
+
616
+ If the disclaimer of warranty and limitation of liability provided
617
+ above cannot be given local legal effect according to their terms,
618
+ reviewing courts shall apply local law that most closely approximates
619
+ an absolute waiver of all civil liability in connection with the
620
+ Program, unless a warranty or assumption of liability accompanies a
621
+ copy of the Program in return for a fee.
622
+
623
+ END OF TERMS AND CONDITIONS
624
+
625
+ How to Apply These Terms to Your New Programs
626
+
627
+ If you develop a new program, and you want it to be of the greatest
628
+ possible use to the public, the best way to achieve this is to make it
629
+ free software which everyone can redistribute and change under these terms.
630
+
631
+ To do so, attach the following notices to the program. It is safest
632
+ to attach them to the start of each source file to most effectively
633
+ state the exclusion of warranty; and each file should have at least
634
+ the "copyright" line and a pointer to where the full notice is found.
635
+
636
+ <one line to give the program's name and a brief idea of what it does.>
637
+ Copyright (C) <year> <name of author>
638
+
639
+ This program is free software: you can redistribute it and/or modify
640
+ it under the terms of the GNU General Public License as published by
641
+ the Free Software Foundation, either version 3 of the License, or
642
+ (at your option) any later version.
643
+
644
+ This program is distributed in the hope that it will be useful,
645
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
646
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
647
+ GNU General Public License for more details.
648
+
649
+ You should have received a copy of the GNU General Public License
650
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
651
+
652
+ Also add information on how to contact you by electronic and paper mail.
653
+
654
+ If the program does terminal interaction, make it output a short
655
+ notice like this when it starts in an interactive mode:
656
+
657
+ <program> Copyright (C) <year> <name of author>
658
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
659
+ This is free software, and you are welcome to redistribute it
660
+ under certain conditions; type `show c' for details.
661
+
662
+ The hypothetical commands `show w' and `show c' should show the appropriate
663
+ parts of the General Public License. Of course, your program's commands
664
+ might be different; for a GUI interface, you would use an "about box".
665
+
666
+ You should also get your employer (if you work as a programmer) or school,
667
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
668
+ For more information on this, and how to apply and follow the GNU GPL, see
669
+ <http://www.gnu.org/licenses/>.
670
+
671
+ The GNU General Public License does not permit incorporating your program
672
+ into proprietary programs. If your program is a subroutine library, you
673
+ may consider it more useful to permit linking proprietary applications with
674
+ the library. If this is what you want to do, use the GNU Lesser General
675
+ Public License instead of this License. But first, please read
676
+ <http://www.gnu.org/philosophy/why-not-lgpl.html>.
comet-cache.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Version: 160211
4
+ Text Domain: comet-cache
5
+ Plugin Name: Comet Cache
6
+ Network: true
7
+
8
+ Author: WebSharks, Inc.
9
+ Author URI: http://websharks-inc.com/
10
+
11
+ Plugin URI: http://cometcache.com/
12
+ Description: Comet Cache is an advanced WordPress caching plugin inspired by simplicity.
13
+ */
14
+ if (!defined('WPINC')) {
15
+ exit('Do NOT access this file directly: '.basename(__FILE__));
16
+ }
17
+ require_once dirname(__FILE__).'/plugin.php';
plugin.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (!defined('WPINC')) {
3
+ exit('Do NOT access this file directly: '.basename(__FILE__));
4
+ }
5
+ $GLOBALS['wp_php_rv'] = '5.3.2'; //php-required-version// // Leaving this at v5.3.2 so that we can have more control over Dashboard messages below.
6
+
7
+ if (require(dirname(__FILE__).'/src/vendor/websharks/wp-php-rv/src/includes/check.php')) {
8
+ ${__FILE__}['apc_enabled'] = (extension_loaded('apc') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) && filter_var(ini_get('apc.cache_by_default'), FILTER_VALIDATE_BOOLEAN) && stripos((string)ini_get('apc.filters'), 'comet-cache') === false) ? true : false;
9
+
10
+ if ((!version_compare(PHP_VERSION, '5.4', '>=') || ${__FILE__}['apc_enabled'])) {
11
+
12
+ if (!version_compare(PHP_VERSION, '5.4', '>=') && is_admin()) {
13
+ ${__FILE__}['php54_notice'] = '<h3 style="margin:.5em 0 .25em 0;">'.__('<strong>NOTICE: Comet Cache Minimum PHP Version</strong></h3>', 'comet-cache');
14
+ ${__FILE__}['php54_notice'] .= '<p style="margin-top:0;">'.sprintf(__('<strong>As of December 1st, 2015 Comet Cache requires PHP 5.4 or higher.</strong> Your server is currently running PHP v%1$s. You will need to upgrade to PHP 5.4 or higher to run this version of Comet Cache.', 'comet-cache'), esc_html(PHP_VERSION)).'</p>';
15
+ ${__FILE__}['php54_notice'] .= '<p style="margin-top:0;">'.__('Learn more about this change here: <a href="http://cometcache.com/r/new-minimum-php-version-php-5-4/" target="_blank">New Minimum PHP Version: PHP 5.4</a>', 'comet-cache').'</p>';
16
+ if (${__FILE__}['apc_enabled']) {
17
+ ${__FILE__}['php54_notice'] .= '<p style="margin-top:0;">'.__('Your server is also running the <strong>outdated PHP APC extension</strong>. Please see: <a href="http://cometcache.com/r/php-apc-extension-no-longer-supported/" target="_blank">PHP APC Extension No Longer Supported</a>', 'comet-cache').'</p>';
18
+ }
19
+
20
+ add_action(
21
+ 'all_admin_notices', create_function(
22
+ '', 'if(!current_user_can(\'activate_plugins\'))'.
23
+ ' return;'."\n".// User missing capability.
24
+
25
+ 'echo \''.// Wrap `$notice` inside a WordPress error.
26
+
27
+ '<div class="error">'.
28
+ ' '.str_replace("'", "\\'", ${__FILE__}['php54_notice']).
29
+ '</div>'.
30
+
31
+ '\';'
32
+ )
33
+ );
34
+ } elseif (${__FILE__}['apc_enabled'] && is_admin()) {
35
+ ${__FILE__}['apc_deprecated_notice'] = '<h3 style="margin:.5em 0 .25em 0;">'.__('<strong>NOTICE: Comet Cache + PHP APC Extension</strong></h3>', 'comet-cache');
36
+ ${__FILE__}['apc_deprecated_notice'] .= '<p style="margin-top:0;">'.sprintf(__('<strong>As of December 1st, 2015 Comet Cache no longer runs with the outdated PHP APC extension.</strong> It appears that you\'re currently running PHP v%1$s with APC enabled. You will need to follow one of the actions below to run this version of Comet Cache.', 'comet-cache'), esc_html(PHP_VERSION)).'</p>';
37
+ ${__FILE__}['apc_deprecated_notice'] .= __('<h4 style="margin:0 0 .5em 0; font-size:1.25em;"><span class="dashicons dashicons-lightbulb"></span> Options Available (Action Required):</h4>', 'comet-cache');
38
+ ${__FILE__}['apc_deprecated_notice'] .= '<ul style="margin-left:2em; list-style:disc;">';
39
+ ${__FILE__}['apc_deprecated_notice'] .= ' <li>'.__('Please add <code>ini_set(\'apc.cache_by_default\', false);</code> to the top of your <code>/wp-config.php</code> file. That will get rid of this message and allow Comet Cache to run without issue.', 'comet-cache').'</li>';
40
+ ${__FILE__}['apc_deprecated_notice'] .= ' <li>'.__('Or, contact your web hosting provider and ask about upgrading to PHP v5.5+; which includes the new <a href="http://cometcache.com/r/php-opcache-extension/" target="_blank">OPcache extension for PHP</a>. The new OPcache extension replaces APC in modern versions of PHP.', 'comet-cache').'</li>';
41
+ ${__FILE__}['apc_deprecated_notice'] .= '</ul>';
42
+ ${__FILE__}['apc_deprecated_notice'] .= '<p style="margin-top:0;">'.__('To learn more about this change, please see the announcement: <a href="http://cometcache.com/r/php-apc-extension-no-longer-supported/" target="_blank">PHP APC Extension No Longer Supported</a>', 'comet-cache').'</p>';
43
+
44
+ add_action(
45
+ 'all_admin_notices', create_function(
46
+ '', 'if(!current_user_can(\'activate_plugins\'))'.
47
+ ' return;'."\n".// User missing capability.
48
+
49
+ 'echo \''.// Wrap `$notice` inside a WordPress error.
50
+
51
+ '<div class="error">'.
52
+ ' '.str_replace("'", "\\'", ${__FILE__}['apc_deprecated_notice']).
53
+ '</div>'.
54
+
55
+ '\';'
56
+ )
57
+ );
58
+ }
59
+ } else {
60
+ require_once dirname(__FILE__).'/src/includes/plugin.php';
61
+ }
62
+ } else {
63
+ wp_php_rv_notice('Comet Cache');
64
+ }
65
+
66
+ unset(${__FILE__}); // Housekeeping.
readme.txt ADDED
@@ -0,0 +1,509 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Comet Cache ===
2
+
3
+ Stable tag: 160211
4
+ Requires at least: 4.1
5
+ Tested up to: 4.5-alpha
6
+ Text Domain: comet-cache
7
+
8
+ License: GPLv2 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
+
11
+ Contributors: WebSharks, JasWSInc, raamdev
12
+ Donate link: http://websharks-inc.com/r/wp-theme-plugin-donation/
13
+ Tags: cache, speed, performance, fast, caching, advanced cache, wp cache, static, client-side cache, rss cache, feed cache, gzip compression, page cache
14
+
15
+ Comet Cache is an advanced WordPress caching plugin inspired by simplicity. Speed up your site (BIG time!) with an intelligent and easy-to-use cache.
16
+
17
+ == Description ==
18
+
19
+ If you care about the speed of your site, Comet Cache is one of those plugins that you absolutely MUST have installed :-) Comet Cache takes a real-time snapshot (building a cache) of every Page, Post, Category, Link, etc. These snapshots are then stored (cached) intuitively, so they can be referenced later, in order to save all of that processing time that has been dragging your site down and costing you money.
20
+
21
+ The Comet Cache plugin uses configuration options that you select from the options panel. See: **Comet Cache -› Options** in your Dashboard. Once a file has been cached, Comet Cache uses advanced techniques that allow it to recognize when it should and should not serve a cached version of the file. By default, Comet Cache does not serve cached pages to users who are logged in, or to users who have left comments recently. Comet Cache also excludes administrative pages, login pages, POST/PUT/DELETE/GET(w/ query string) requests and/or CLI processes.
22
+
23
+ = Features =
24
+
25
+ - SIMPLE and well-documented configuration (just enable and you're all set!).
26
+ - Options to control the automatic cache clearing behavior for Home and Posts Page, Author Page, Category, Tag, and Custom Term Archives, Custom Post Type Archives, RSS/RDF/ATOM Feeds, and XML Sitemaps.
27
+ - URI exclusion patterns (now supporting wildcards too).
28
+ - User-Agent exclusion patterns (now supporting wildcards too).
29
+ - HTTP referrer exclusion patterns (now supporting wildcards too).
30
+ - The ability to set an automatic expiration time for cache files.
31
+ - Client-Side Caching (to allow double-caching in the client-side browser).
32
+ - Caching for 404 requests to reduce the impact of those requests on the server.
33
+ - RSS, RDF, and Atom Feed caching.
34
+ - The ability to cache or ignore URLs that contain query strings (GET Requests).
35
+ - An Advanced Cache Plugin system for theme and plugin developers.
36
+
37
+ = Pro Features =
38
+
39
+ - The ability to cache logged-in users too! (VERY powerful, particularly for membership sites).
40
+ - A new improved "Clear Cache" button in the admin bar (along with an option to enable/disable this feature).
41
+ - The ability to disable Dashboard notifications related to automatic clearing/purging on change detections.
42
+ - The ability to clear Markdown-related cache files generated by the s2Clean theme for WordPress (if installed).
43
+ - The ability to run custom PHP code whenever the cache is cleared.
44
+ - Cache Statistics to help you gain insight into the status of your site cache.
45
+ - Import/Export functionality for Comet Cache configuration files.
46
+ - A Dynamic Version Salt (customize the caching engine).
47
+ - HTML Compressor to automatically combine and compresses CSS/JS/HTML code.
48
+ - Auto-Cache Engine to pre-cache your site at 15-minute intervals.
49
+ - Static CDN Filters to serve some and/or ALL static files on your site from a CDN of your choosing, including support for Multiple CDN Host Names, Domain Sharding, and WordPress Multisite Networks.
50
+ - An Automatic Updater to update Comet Cache Pro from your WordPress Dashboard.
51
+ - Rockstar support for all Comet Cache features.
52
+
53
+ TIP: you can preview Pro features in the free version by clicking the "Preview Pro Features" link at the top of your Comet Cache options.
54
+
55
+ == Screenshots ==
56
+
57
+ 1. Step 1: Enable Comet Cache
58
+ 2. Step 2: Save All Changes; that's it!
59
+ 3. One-click Clear Cache button
60
+ 4. Plugin Deletion Safeguards
61
+ 5. Intelligent and automatic cache clearing
62
+ 6. Cache Directory
63
+ 7. Cache Expiration Time
64
+ 8. Client-Side Cache
65
+ 9. GET Requests
66
+ 10. 404 Requests
67
+ 11. RSS, RDF, and Atom Feeds
68
+ 12. URI Exclusion Patterns
69
+ 13. HTTP Referrer Exclusion Patterns
70
+ 14. User-Agent Exclusion Patterns
71
+ 15. GZIP Compression
72
+ 16. Theme/Plugin Developers
73
+
74
+ == Installation ==
75
+
76
+ **Quick Tip:** WordPress® can only deal with one cache plugin being activated at a time. Please uninstall any existing cache plugins that you've tried in the past. In other words, if you've installed W3 Total Cache, WP Super Cache, DB Cache Reloaded, or any other caching plugin, uninstall them all before installing Comet Cache. One way to check, is to make sure this file: `wp-content/advanced-cache.php` and/or `wp-content/object-cache.php` are NOT present; and if they are, delete these files BEFORE installing Comet Cache. Those files will only be present if you have a caching plugin already installed. If you don't see them, you're ready to install Comet Cache :-).
77
+
78
+ **A note for existing ZenCache users:** Comet Cache is the successor to ZenCache and will automatically detect any existing ZenCache options and migrate that options over to Comet Cache. For further details, please see the [migration FAQ](https://cometcache.com/r/zencache-migration-faq/).
79
+
80
+ = Comet Cache is Very Easy to Install =
81
+
82
+ 1. Upload the `/comet-cache` folder to your `/wp-content/plugins/` directory.
83
+ 2. Activate the plugin through the Plugins menu in WordPress®.
84
+ 3. Navigate to the **Comet Cache** panel & enable it.
85
+
86
+ = How will I know Comet Cache is Working? =
87
+
88
+ First of all, make sure that you've enabled Comet Cache. After you activate the plugin in WordPress, go to the Comet Cache options panel and enable caching (you can't miss the big yellow checkbox). Then scroll to the bottom and click Save All Changes. All of the other options on that page are already pre-configured for typical usage. Skip them all for now. You can go back through all of these later and fine-tune things the way you like them.
89
+
90
+ Once Comet Cache has been enabled, **you'll need to log out** (and/or clear browser cookies). Cache files are NOT served to visitors who are logged in, and that includes you too :-) Cache files are NOT served to recent commenters either. If you've commented (or replied to a comment lately); please clear your browser cookies before testing.
91
+
92
+ **To verify that Comet Cache is working**, navigate your site like a normal visitor would. Right-click on any page (choose View Source), then scroll to the very bottom of the document. At the bottom, you'll find comments that show Comet Cache stats and information. You should also notice that page-to-page navigation is lightning fast compared to what you experienced prior to installing Comet Cache.
93
+
94
+ = Running Comet Cache On A WordPress® Multisite Installation =
95
+
96
+ WordPress® Multisite Networking is a special consideration in WordPress®. If Comet Cache is installed under a Multisite Network installation, it will be enabled for ALL blogs the same way. The centralized config options for Comet Cache, can only be modified by a Super Administrator operating on the main site. Comet Cache has internal processing routines that prevent configuration changes, including menu displays; for anyone other than a Super Administrator operating on the main site.
97
+
98
+ = EMERGENCY: If All Else Fails (How-To Remove Comet Cache) =
99
+
100
+ Ordinarily you can just deactivate Comet Cache from the plugins menu in WordPress. However, if you're having a more serious issue, please follow the instructions here.
101
+
102
+ 1. Log into your site via FTP; perhaps using [FileZilla](http://www.youtube.com/watch?v=joXUMhr8PhU).
103
+ 2. Delete this file: `/wp-content/advanced-cache.php`
104
+ 3. Delete this directory: `/wp-content/plugins/comet-cache/`
105
+ 4. Remove this line from your `/wp-config.php` file: `define('WP_CACHE', TRUE);`
106
+
107
+ Comet Cache is now completely uninstalled and you can start fresh :-)
108
+
109
+ == Frequently Asked Questions ==
110
+
111
+ = I already have ZenCache installed; how do I install Comet Cache? =
112
+
113
+ Comet Cache is the successor to ZenCache and will automatically detect any existing ZenCache options and migrate those options over to Comet Cache. For further details, please see the [migration FAQ](https://cometcache.com/r/zencache-migration-faq/).
114
+
115
+ = How do I know that Comet Cache is working the way it should be? =
116
+
117
+ First of all, make sure that you've enabled Comet Cache. After you activate the plugin, go to the Comet Cache options panel and enable it, then scroll to the bottom and click Save All Changes. All of the other options on that page are already pre-configured for typical usage. Skip them all for now. You can go back through all of them later and fine-tune things the way you like them.
118
+
119
+ Once Comet Cache has been enabled, **you'll need to log out** (and/or clear browser cookies). Cache files are NOT served to visitors who are logged in, and that includes you too :-) Cache files are NOT served to recent commenters either. If you've commented (or replied to a comment lately); please clear your browser cookies before testing.
120
+
121
+ **To verify that Comet Cache is working**, navigate your site like a normal visitor would. Right-click on any page (choose View Source), then scroll to the very bottom of the document. At the bottom, you'll find comments that show Comet Cache stats and information. You should also notice that page-to-page navigation is lightning fast compared to what you experienced prior to installing Comet Cache.
122
+
123
+ = What is the down side to running Comet Cache? =
124
+
125
+ There is NOT one! Comet Cache is a MUST HAVE for every WordPress® powered site. In fact, we really can't think of any site running WordPress® that would want to be without it. To put it another way, the WordPress® software itself comes with a built in action reference for an `advanced-cache.php` file, because WordPress® developers realize the importance of such as plugin. The `/wp-content/advanced-cache.php` file is named as such, because the WordPress® developers expect it to be there when caching is enabled by a plugin. If you don't have the `/wp-content/advanced-cache.php` file yet, it is because you have not enabled Comet Cache from the options panel yet.
126
+
127
+ = So why does WordPress® need to be cached? =
128
+
129
+ To understand how Comet Cache works, first you have to understand what a cached file is, and why it is absolutely necessary for your site and every visitor that comes to it. WordPress® (by its very definition) is a database-driven publishing platform. That means you have all these great tools on the back-end of your site to work with, but it also means that every time a Post/Page/Category is accessed on your site, dozens of connections to the database have to be made, and literally thousands of PHP routines run in harmony behind-the-scenes to make everything jive. The problem is, for every request that a browser sends to your site, all of these routines and connections have to be made (yes, every single time). Geesh, what a waste of processing power, memory, and other system resources. After all, most of the content on your site remains the same for at least a few minutes at a time. If you've been using WordPress® for very long, you've probably noticed that (on average) your site does not load up as fast as other sites on the web. Now you know why!
130
+
131
+ In computer science, a cache (pronounced /kash/) is a collection of data duplicating original values stored elsewhere or computed earlier, where the original data is expensive to fetch (owing to longer access time) or to compute, compared to the cost of reading the cache. In other words, a cache is a temporary storage area where frequently accessed data can be stored for rapid access. Once the data is stored in the cache, it can be used in the future by accessing the cached copy rather than re-fetching or recomputing the original data.
132
+
133
+ = Where & why are the cache files stored on my server? =
134
+
135
+ The cache files are stored in a special directory: `/wp-content/cache/comet-cache`. This directory needs to remain writable, just like the `/wp-content/uploads` directory on many WordPress® installations. The `/comet-cache/cache` directory is where cache files reside. These files are stored using an intutive directory structure that named based on the request URL (`HTTPS/HTTP_HOST/REQUEST_URI`). See also: **Dashboard -› Comet Cache -› Cache Directory/Expiration Time** for further details.
136
+
137
+ Whenever a request comes in from someone on the web, Comet Cache checks to see if it can serve a cached file; e.g. it looks at the `HTTPS/HTTP_HOST/REQUEST_URI` environent variables, then it checks the `/comet-cache/cache` directory. If a cache file has been built already, and it matches an existing `HTTPS.HTTP_HOST.REQUEST_URI` combination; and it is not too old (see: **Dashboard -› Comet Cache -› Cache Directory/Expiration Time**), then it will serve that file instead of asking WordPress® to regenerate it. This adds tremendous speed to your site and reduces server load.
138
+
139
+ If you have GZIP compression enabled, then the cache file is also sent to the browser with compression (recommended). Modern web browsers that support this technique will definitely take advantage of it. After all, if it is easier to email a zip file, it's also easier to download a web page that way. That is why on-the-fly GZIP compression for web pages is recommended. This is supported by all modern browsers.
140
+
141
+ If you want to enable GZIP, create an `.htaccess` file in your WordPress® installation directory and put the following few lines in it. Alternatively, if you already have an `.htaccess` file, just add these lines to it, and that is all there is to it. GZIP is now enabled!
142
+
143
+ <IfModule deflate_module>
144
+ <IfModule filter_module>
145
+ AddOutputFilterByType DEFLATE text/plain text/html
146
+ AddOutputFilterByType DEFLATE text/xml application/xml application/xhtml+xml application/xml-dtd
147
+ AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml image/svg+xml
148
+ AddOutputFilterByType DEFLATE text/css text/javascript application/javascript application/x-javascript
149
+ AddOutputFilterByType DEFLATE font/otf font/opentype application/font-otf application/x-font-otf
150
+ AddOutputFilterByType DEFLATE font/ttf font/truetype application/font-ttf application/x-font-ttf
151
+ </IfModule>
152
+ </IfModule>
153
+
154
+ If your installation of Apache does not have `mod_deflate` installed. You can also enable GZIP compression using PHP configuration alone. In your `php.ini` file, you can simply add the following line anywhere: `zlib.output_compression = on`
155
+
156
+ = What happens if a user logs in? Are cache files used then? =
157
+
158
+ By default, Comet Cache does NOT serve cached pages to users who are logged in, or to users who have left comments recently. Comet Cache also excludes administrative pages, login pages, POST/PUT/DELETE/GET(w/ query string) requests and/or CLI processes. That being said, the Pro version of Comet Cache DOES make it possible to cache pages even when users ARE logged-in; adding even more speed! This is particularly helpful on membership sites; e.g. sites that run plugins like s2Member™ for instance.
159
+
160
+ = Will comments and other dynamic parts of my blog update immediately? =
161
+
162
+ It depends on your configuration of Comet Cache. There is an automatic expiration system (the garbage collector), which runs through WordPress® behind-the-scene, according to your Expiration setting (see: **Dashboard -› Comet Cache -› Cache Directory/Expiration Time**). There is also a built-in expiration time on existing files that is checked before any cache file is served up, which also uses your Expiration setting. In addition; whenever you update a Post or a Page, Comet Cache can automatically prune that particular file from the cache so it instantly becomes fresh again. Otherwise, your visitors would need to wait for the previous cached version to expire.
163
+
164
+ By default, Comet Cache does NOT serve cached pages to users who are logged in, or to users who have left comments recently. Comet Cache also excludes administrative pages, login pages, POST/PUT/DELETE/GET(w/ query string) requests and/or CLI processes.
165
+
166
+ = How do I enable GZIP compression? Is GZIP supported? =
167
+
168
+ There is no need to use an `.htaccess` file with this plugin; caching is handled by WordPress®/PHP alone. That being said, if you also want to take advantage of GZIP compression (and we do recommend this), then you WILL need an `.htaccess` file to accomplish that part. This plugin fully supports GZIP compression on its output. However, it does not handle GZIP compression directly. We purposely left GZIP compression out of this plugin, because GZIP compression is something that should really be enabled at the Apache level or inside your `php.ini` file. GZIP compression can be used for things like JavaScript and CSS files as well, so why bother turning it on for only WordPress-generated pages when you can enable GZIP at the server level and cover all the bases!
169
+
170
+ If you want to enable GZIP, create an `.htaccess` file in your WordPress® installation directory and put the following few lines in it. Alternatively, if you already have an `.htaccess` file, just add these lines to it, and that is all there is to it. GZIP is now enabled!
171
+
172
+ <IfModule deflate_module>
173
+ <IfModule filter_module>
174
+ AddOutputFilterByType DEFLATE text/plain text/html
175
+ AddOutputFilterByType DEFLATE text/xml application/xml application/xhtml+xml application/xml-dtd
176
+ AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml image/svg+xml
177
+ AddOutputFilterByType DEFLATE text/css text/javascript application/javascript application/x-javascript
178
+ AddOutputFilterByType DEFLATE font/otf font/opentype application/font-otf application/x-font-otf
179
+ AddOutputFilterByType DEFLATE font/ttf font/truetype application/font-ttf application/x-font-ttf
180
+ </IfModule>
181
+ </IfModule>
182
+
183
+ If your installation of Apache does not have `mod_deflate` installed. You can also enable gzip compression using PHP configuration alone. In your `php.ini` file, you can simply add the following line anywhere: `zlib.output_compression = on`
184
+
185
+ = I'm a plugin developer. How can I prevent certain files from being cached? =
186
+
187
+ <?php
188
+ define('COMET_CACHE_ALLOWED', FALSE); // The easiest way.
189
+ // or $_SERVER['COMET_CACHE_ALLOWED'] = FALSE; // Also very easy.
190
+ // or define('DONOTCACHEPAGE', TRUE); // For compatibility with other cache plugins.
191
+
192
+ When your script finishes execution, Comet Cache will know that it should NOT cache that particular page. It does not matter where or when you define this Constant; e.g. `define('COMET_CACHE_ALLOWED', FALSE);` because Comet Cache is the last thing to run during execution. So as long as you define this Constant at some point in your routines, everything will be fine.
193
+
194
+ Comet Cache also provides support for `define('DONOTCACHEPAGE', TRUE)`, which is used by the WP Super Cache plugin as well. Another option is: `$_SERVER['COMET_CACHE_ALLOWED'] = FALSE`. The `$_SERVER` array method is useful if you need to disable caching at the Apache level using `mod_rewrite`. The `$_SERVER` array is filled with all environment variables, so if you use `mod_rewrite` to set the `COMET_CACHE_ALLOWED` environment variable, that will end up in `$_SERVER['COMET_CACHE_ALLOWED']`. All of these methods have the same end result, so it's up to you which one you'd like to use.
195
+
196
+ = What should my expiration setting be? =
197
+
198
+ If you don't update your site much, you could set this to `6 months`; optimizing everything even further. The longer the cache expiration time is, the greater your performance gain. Alternatively, the shorter the expiration time, the fresher everything will remain on your site. A default value of `7 days` (recommended expiration time), is a good conservative middle-ground.
199
+
200
+ Keep in mind that your expiration setting is only one part of the big picture. Comet Cache will also purge the cache automatically as changes are made to the site (i.e. you edit a post, someone comments on a post, you change your theme, you add a new navigation menu item, etc., etc.). Thus, your expiration time is really just a fallback; e.g. the maximum amount of time that a cache file could ever possibly live.
201
+
202
+ That being said, you could set this to just `60 seconds` and you would still see huge differences in speed and performance. If you're just starting out with Comet Cache (perhaps a bit nervous about old cache files being served to your visitors); you could set this to something like `30 minutes` and experiment with it while you build confidence in Comet Cache. It's not necessary, but many site owners have reported this makes them feel like they're more-in-control when the cache has a short expiration time. All-in-all, it's a matter of preference :-)
203
+
204
+ = EMERGENCY: If all else fails, how can I remove Comet Cache? =
205
+
206
+ Ordinarily you can just deactivate Comet Cache from the plugins menu in WordPress. However, if you're having a more serious issue, please follow the instructions here.
207
+
208
+ 1. Log into your site via FTP; perhaps using [FileZilla](http://www.youtube.com/watch?v=joXUMhr8PhU).
209
+ 2. Delete this file: `/wp-content/advanced-cache.php`
210
+ 3. Delete this directory: `/wp-content/plugins/comet-cache/`
211
+ 4. Remove this line from your `/wp-config.php` file: `define('WP_CACHE', TRUE);`
212
+
213
+ Comet Cache is now completely uninstalled and you can start fresh :-)
214
+
215
+ == Further Details ==
216
+
217
+ = So Why Does WordPress® Need To Be Cached? =
218
+
219
+ To understand how Comet Cache works, first you have to understand what a cached file is, and why it is absolutely necessary for your site and every visitor that comes to it. WordPress® (by its very definition) is a database-driven publishing platform. That means you have all these great tools on the back-end of your site to work with, but it also means that every time a Post/Page/Category is accessed on your site, dozens of connections to the database have to be made, and literally thousands of PHP routines run in harmony behind-the-scenes to make everything jive. The problem is, for every request that a browser sends to your site, all of these routines and connections have to be made (yes, every single time). Geesh, what a waste of processing power, memory, and other system resources. After all, most of the content on your site remains the same for at least a few minutes at a time. If you've been using WordPress® for very long, you've probably noticed that (on average) your site does not load up as fast as other sites on the web. Now you know why!
220
+
221
+ = The Definition Of A Cached File (from the Wikipedia) =
222
+
223
+ In computer science, a cache (pronounced /kash/) is a collection of data duplicating original values stored elsewhere or computed earlier, where the original data is expensive to fetch (owing to longer access time) or to compute, compared to the cost of reading the cache. In other words, a cache is a temporary storage area where frequently accessed data can be stored for rapid access. Once the data is stored in the cache, it can be used in the future by accessing the cached copy rather than re-fetching or recomputing the original data.
224
+
225
+ = Prepare To Be Amazed / It's Time To Speed Things Up =
226
+
227
+ Comet Cache is extremely reliable, because it runs completely in PHP code, and does not hand important decisions off to the `mod_rewrite` engine or browser cache; also making Comet Cache MUCH easier to setup and configure.
228
+
229
+ In addition, Comet Cache actually sends a no-cache header (yes, a no-cache header); which allows it to remain in control at all times. It might seem weird that a caching plugin would send a no-cache header :-). Well, no-cache headers are a key component in this plugin, and they will NOT affect performance negatively. On the contrary, this is how the system can accurately serve cache files to public users vs. users who are logged-in, commenters, etc.
230
+
231
+ If you care about the speed of your site, Comet Cache is one of those plugins that you absolutely MUST have installed :-) Comet Cache takes a real-time snapshot (building a cache) of every Page, Post, Category, Link, etc. These snapshots are then stored (cached) intuitively, so they can be referenced later, in order to save all of that processing time that has been dragging your site down and costing you money.
232
+
233
+ The Comet Cache plugin uses configuration options that you select from the options panel. See: **Comet Cache -› Options** in your Dashboard. Once a file has been cached, Comet Cache uses advanced techniques that allow it to recognize when it should and should not serve a cached version of the file. By default, Comet Cache does not serve cached pages to users who are logged in, or to users who have left comments recently. Comet Cache also excludes administrative pages, login pages, POST/PUT/DELETE/GET(w/ query string) requests and/or CLI processes.
234
+
235
+ = Running Comet Cache On A WordPress® Multisite Installation =
236
+
237
+ WordPress® Multisite Networking is a special consideration in WordPress®. If Comet Cache is installed under a Multisite Network installation, it will be enabled for ALL blogs the same way. The centralized config options for Comet Cache, can only be modified by a Super Administrator operating on the main site. Comet Cache has internal processing routines that prevent configuration changes, including menu displays; for anyone other than a Super Administrator operating on the main site.
238
+
239
+ = How To Enable GZIP Compression for Even Greater Speeds =
240
+
241
+ You don't have to use an `.htaccess` file to enjoy the performance enhancements provided by this plugin; caching is handled by WordPress®/PHP alone. That being said, if you want to take advantage of GZIP compression (and we do recommend this), then you WILL need an `.htaccess` file to accomplish that part. This plugin fully supports GZIP compression on its output. However, it does not handle GZIP compression directly. We purposely left GZIP compression out of this plugin, because GZIP compression is something that should really be enabled at the Apache level or inside your `php.ini` file. GZIP compression can be used for things like JavaScript and CSS files as well, so why bother turning it on for only WordPress-generated pages when you can enable GZIP at the server level and cover all the bases!
242
+
243
+ If you want to enable GZIP, create an `.htaccess` file in your WordPress® installation directory and put the following few lines in it. Alternatively, if you already have an `.htaccess` file, just add these lines to it, and that is all there is to it. GZIP is now enabled!
244
+
245
+ <IfModule deflate_module>
246
+ <IfModule filter_module>
247
+ AddOutputFilterByType DEFLATE text/plain text/html
248
+ AddOutputFilterByType DEFLATE text/xml application/xml application/xhtml+xml application/xml-dtd
249
+ AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml image/svg+xml
250
+ AddOutputFilterByType DEFLATE text/css text/javascript application/javascript application/x-javascript
251
+ AddOutputFilterByType DEFLATE font/otf font/opentype application/font-otf application/x-font-otf
252
+ AddOutputFilterByType DEFLATE font/ttf font/truetype application/font-ttf application/x-font-ttf
253
+ </IfModule>
254
+ </IfModule>
255
+
256
+ If your installation of Apache does not have `mod_deflate` installed. You can also enable GZIP compression using PHP configuration alone. In your `php.ini` file, you can simply add the following line anywhere: `zlib.output_compression = on`
257
+
258
+ = EMERGENCY: If All Else Fails (How-To Remove Comet Cache) =
259
+
260
+ Ordinarily you can just deactivate Comet Cache from the plugins menu in WordPress. However, if you're having a more serious issue, please follow the instructions here.
261
+
262
+ 1. Log into your site via FTP; perhaps using [FileZilla](http://www.youtube.com/watch?v=joXUMhr8PhU).
263
+ 2. Delete this file: `/wp-content/advanced-cache.php`
264
+ 3. Delete this directory: `/wp-content/plugins/comet-cache/`
265
+ 4. Remove this line from your `/wp-config.php` file: `define('WP_CACHE', TRUE);`
266
+
267
+ Comet Cache is now completely uninstalled and you can start fresh :-)
268
+
269
+ == Pro Features ==
270
+
271
+ = Comet Cache Pro Features =
272
+
273
+ - The ability to cache logged-in users too! (VERY powerful, particularly for membership sites).
274
+ - A new improved "Clear Cache" button in the admin bar (along with an option to enable/disable this feature).
275
+ - The ability to disable Dashboard notifications related to automatic clearing/purging on change detections.
276
+ - The ability to clear Markdown-related cache files generated by the s2Clean theme for WordPress (if installed).
277
+ - The ability to run custom PHP code whenever the cache is cleared.
278
+ - Cache Statistics to help you gain insight into the status of your site cache.
279
+ - Import/Export functionality for Comet Cache configuration files.
280
+ - A Dynamic Version Salt (customize the caching engine).
281
+ - HTML Compressor to automatically combine and compresses CSS/JS/HTML code.
282
+ - Auto-Cache Engine to pre-cache your site at 15-minute intervals.
283
+ - Static CDN Filters to serve some and/or ALL static files on your site from a CDN of your choosing, including support for Multiple CDN Host Names, Domain Sharding, and WordPress Multisite Networks.
284
+ - An Automatic Updater to update Comet Cache Pro from your WordPress Dashboard.
285
+ - Rockstar support for all Comet Cache features.
286
+
287
+ **TIP:** you can preview Pro features in the free version by clicking the "**Preview Pro Features**" link at the top of your Comet Cache options.
288
+
289
+ == Pro Installation ==
290
+
291
+ Comet Cache Pro is a wholly contained plugin that _does not_ require Comet Cache Lite to be installed. To install Comet Cache Pro,
292
+
293
+ 1. Deactivate and delete Comet Cache Lite, if it is currently installed
294
+ 1. Download Comet Cache Pro from your account at WebSharks-Inc.com
295
+ 1. From your WordPress Dashboard, go to **Dashboard -> Plugins -> Add New** and then click on the **Upload Plugin** button at the top
296
+ 1. Select the Comet Cache Pro zip file you downloaded in step 2 and click "Install Now"
297
+ 1. After the plugin finishes installing, click the "Activate Plugin" link
298
+
299
+ Once the plugin is active, you can go to **Dashboard -> Comet Cache -> Plugin Options -> Enable/Disable** and Enable Comet Cache.
300
+
301
+ Also, to stay updated with the latest version of Comet Cache Pro, be sure to also configure **Dashboard -> Comet Cache -> Plugin Updater**.
302
+
303
+ == Software Requirements ==
304
+
305
+ In addition to the [WordPress Requirements](http://wordpress.org/about/requirements/), Comet Cache requires the following minimum versions:
306
+
307
+ - PHP 5.4+
308
+ - Apache 2.1+
309
+
310
+ == License ==
311
+
312
+ Copyright: © 2013 [WebSharks, Inc.](http://www.websharks-inc.com/bizdev/) (coded in the USA)
313
+
314
+ Released under the terms of the [GNU General Public License](http://www.gnu.org/licenses/gpl-2.0.html).
315
+
316
+ = Credits / Additional Acknowledgments =
317
+
318
+ * Software designed for WordPress®.
319
+ - GPL License <http://codex.wordpress.org/GPL>
320
+ - WordPress® <http://wordpress.org>
321
+ * Some JavaScript extensions require jQuery.
322
+ - GPL-Compatible License <http://jquery.org/license>
323
+ - jQuery <http://jquery.com/>
324
+ * CSS framework and some JavaScript functionality provided by Bootstrap.
325
+ - GPL-Compatible License <http://getbootstrap.com/getting-started/#license-faqs>
326
+ - Bootstrap <http://getbootstrap.com/>
327
+ * Icons provided by Font Awesome.
328
+ - GPL-Compatible License <http://fortawesome.github.io/Font-Awesome/license/>
329
+ - Font Awesome <http://fortawesome.github.io/Font-Awesome/>
330
+
331
+ == Upgrade Notice ==
332
+
333
+ = v160211 =
334
+
335
+ Requires PHP v5.4+. The latest version of Comet Cache is a complete rewrite (OOP design). Faster! and even more dependable. For further details, please see http://cometcache.com/new-minimum-php-version-php-5-4/
336
+
337
+ == Changelog ==
338
+
339
+ = v160211 =
340
+
341
+ **Announcing Comet Cache, formerly ZenCache!**
342
+
343
+ We are very excited to announce the release of [Comet Cache](http://cometcache.com), an advanced WordPress caching plugin inspired by simplicity. Comet Cache is the successor to ZenCache (and Quick Cache before that), a very popular WordPress caching plugin that has been downloaded over 1 million times and has won acclaim for its speed, simplicity, and ease of configuration. [Read the full announcement here](https://cometcache.com/r/announcing-comet-cache-formerly-zencache/).
344
+
345
+ = v160103 =
346
+
347
+ - **Bug Fix**: Fixed an issue that was unexpectedly producing "Failed to update your /.htaccess file" error messages. The `.htaccess` routines are now more intelligent and take into consideration which plugin options are enabled and which options require updating the `.htaccess` file. This also improves performance by avoiding unnecessary read/writes to the `.htaccess` file. Props @patdumond. See [Issue #641](https://github.com/websharks/zencache/issues/641).
348
+ - **Bug Fix**: When `allow_url_fopen` is disabled via the PHP configuration, the Auto-Cache Engine is unable to read the XML Sitemap and was silently failing with only PHP Warning in the PHP error log. The Auto-Cache Engine currently requires [PHP URL-aware fopen wrappers](http://zencache.com/r/allow_url_fopen/). A new Dashboard notice displays an error message when `allow_url_fopen` is disabled and prevents the Auto-Cache Engine from attempting to run. See [Issue #644](https://github.com/websharks/zencache/issues/644).
349
+ - **Bug Fix**: Fixed an Auto-Cache Engine bug that was producing false-positive Dashboard errors related to timeouts: "Problematic XML Sitemap URL - WP_Http says: Operation timed out after 5001 milliseconds with 0 bytes received." Due to the way ZenCache was checking the XML Sitemap URL more than necessary, timeouts were more likely to occur. We now only check the URL repeatedly when a failure has been encountered. If the URL is confirmed as working, we don't check the URL again until the Auto-Cache Engine runs (every 15 minutes by default) or until the Plugin Options are saved. If you are still seeing timeout errors after this update, please see [this article](http://zencache.com/kb-article/why-am-i-seeing-auto-cache-engine-timeout-errors/). See [Issue #643](https://github.com/websharks/zencache/issues/643).
350
+ - **Bug Fix**: Fixed an Auto-Cache Engine bug where ZenCache would would check both the non-SSL (`http://`) and the SSL (`https://`) version of the XML Sitemap URL when the Site Address was configured to use SSL. See [Issue #643](https://github.com/websharks/zencache/issues/643).
351
+ - **Enhancement**: It's now possible to override the ZenCache Nonce exclusion globally (dangerous) or only for Logged-In Users (safer). Please see [this article](http://zencache.com/r/kb-article-what-are-wordpress-nonces-and-why-are-they-not-cache-compatible/) for full details. [Issue #637](https://github.com/websharks/zencache/issues/637).
352
+ - **Akismet Compatibility**: Fixed a bug with Akismet compatibility where ZenCache was not properly disabling the Akismet Comment Nonce, which resulted in pages being unnecessarily excluded from the cache due to the presence of the `akismet_comment_nonce` in the markup. Props @Kalfer. See [Issue #642](https://github.com/websharks/zencache/issues/642).
353
+
354
+ = v151220 =
355
+
356
+ - **New Feature!**: It's now possible to customize the Cache Cleanup Schedule and set your own schedule in _ZenCache → Plugin Options → Manual Cache Clearing → Cache Cleanup Schedule_. ZenCache uses [`wp_get_schedules()`](https://codex.wordpress.org/Function_Reference/wp_get_schedules) when generating the list of available schedules, which makes it possible to create your own custom cron schedule using a plugin like WP Crontrol and use that to define a custom Cache Cleanup Schedule. Props @kristineds. See [Issue #472](https://github.com/websharks/zencache/issues/472).
357
+ - **New Feature!** It's now possible to configure the Pro Plugin Updater to check for Beta versions (Release Candidates) of the plugin. See _ZenCache → Plugin Updater → Beta Testers_. We publish Release Candidates a week or so before official releases to allow for additional testing and early preview of upcoming features and bug fixes. If you're not already on our Beta Testers mailing list, you can signup [here](http://zencache.com/r/zencache-beta-testers-list/). Props @kristineds. See [Issue #352](https://github.com/websharks/zencache/issues/352).
358
+ - **New Feature!** It's now possible to clear [Expired WordPress Transients](https://codex.wordpress.org/Transients_API) from the Clear Cache Option Menu in the WordPress Admin Bar. The WordPress Transients API has no functionality to automatically clean up expired transients; doing so is left for plugin authors and we've found that very few plugins that use the Transient API actually clean up expired transients properly, which can lead to your database being full of expired transient data that is no longer used. Props @kristineds. See [Issue #459](https://github.com/websharks/zencache/issues/459).
359
+ - **New Feature!** Client-Side Caching now includes a new URI Exclusion Patterns for Client-Side Caching, allowing you to exclude specific URIs from being cached by a client-side browser when Client-Side Caching is enabled. Props @renzms. See [Issue #498](https://github.com/websharks/zencache/issues/498).
360
+ - **Bug Fix**: Fixed a bug that was generating a "Failed to update your `/.htaccess` file" error message. The routines that update your `.htaccess` file were failing if your `.htaccess` file already contained the word "ZenCache" (case-insensitive). See [Issue #617](https://github.com/websharks/zencache/issues/617) and [Issue #620](https://github.com/websharks/zencache/issues/620).
361
+ - **Bug Fix**: Fixed a bug that in some scenarios could cause custom `.htaccess` rules to be lost if your `.htaccess` file was using the exact same comment start and end markers as ZenCache (`# BEGIN ZenCache` and `# END ZenCache`). If you were using those exact same start and end markers, ZenCache was replacing whatever was between them with a copy of the ZenCache `.htaccess` rules. ZenCache now uses a unique marker (`WmVuQ2FjaGU`) to identify its own code blocks inside your `.htaccess` file. See [Issue #620](https://github.com/websharks/zencache/issues/620).
362
+ - **Bug Fix**: Fixed a bug with clearing cache files for paginated pages where the `pagination_base` had been changed from the default `page` to something else (e.g., a translated string). The WP Rewrite API is now used to include `pagination_base` and `comments_pagination_base` when building paths to cache files to determine which cache files should be cleared. Props @renzms. See [Issue #607](https://github.com/websharks/zencache/issues/607).
363
+ - **Bug Fix**: Fixed a bug with the Static CDN Filters that affected sites using a permalink structure that ended with `.htm` or `.html`. This bug was supposed to be fixed back in v151002 but another bug prevented the fix from working properly. This release fixes the issue once and for all. See [Issue #495](https://github.com/websharks/zencache/issues/495#issuecomment-160324313).
364
+ - **Bug Fix**: With Logged-In User caching enabled, there were some scenarios where the user cache was being cleared on every page load when a user was logged in. ZenCache now hooks into `updated_user_metadata` instead of `update_user_metadata` to clear a specific user cache. Props @kristineds. See [Issue #623](https://github.com/websharks/zencache/issues/623).
365
+ - **Bug Fix**: Fixed a bug where the Auto-Cache Engine cron event would disappear in some scenarios. Props @patdumond, @MarioKnight. See [Issue #613](https://github.com/websharks/zencache/issues/613).
366
+ - **Bug Fix**: ZenCache no longer caches any pages that contain `_wpnonce` in the markup. This ensures that pages containing time-sensitive `nonce` values are not cached. Props @kristineds and @jaswsinc. See [Issue #601](https://github.com/websharks/zencache/issues/601).
367
+ - **Bug Fix**: This release attempts to resolve reports of Error 500 and Internal Server Error issues when running with PHP 5.6 + OPcache. Instead of clearing the entire Opcode cache whenever the plugin options are saved, we only invalidate the `advanced-cache.php` file, which is rebuilt each time you update your configuration options. Props @jaswsinc. Also props to @MarioKnight and @CotswoldPhoto for helping us narrow this down. See [Issue #624](https://github.com/websharks/zencache/issues/624).
368
+ - **Bug Fix**: Fixed a bug where ZenCache could inadvertently corrupt the `.htaccess` file if it contained invalid UTF-8 characters. This has been fixed by first checking the `.htaccess` file for invalid UTF-8 characters via `wp_check_invalid_utf8()`. See [Issue #633](https://github.com/websharks/zencache/issues/633#issuecomment-165493039).
369
+ - **Enhancement**: Improved WP Cron-related configuration and validation of custom cron schedules. See [PR #197](https://github.com/websharks/zencache-pro/pull/197).
370
+ - **Enhancement**: ZenCache now clears the cache whenever a WordPress plugin is activated or deactivated. Many WordPress plugins change content on the front-end of the site, so this enhancement ensures that an old cache file is never served to visitors. If you want to disable this automatic behavior, see [this article](http://zencache.com/kb-article/how-do-i-prevent-zencache-from-clearing-the-cache-upon-plugin-activation-or-deactivation/). Props @renzms. See [Issue #424](https://github.com/websharks/zencache/issues/424).
371
+ - **Enhancement**: The Auto-Cache Engine diagnostic reporting for XML Sitemap-related errors has been greatly improved. The Dashboard notice now explains exactly what error is occurring when the Auto-Cache Engine attempts to verify that the XML Sitemap exists. Props @jaswsinc. See [Issue #615](https://github.com/websharks/zencache/issues/615) and [Issue #618](https://github.com/websharks/zencache/issues/618).
372
+ - **Enhancement**: Auto-Cache Engine XML Sitemap error checking is now more intelligent. When configured, the XML Sitemap URL is now checked upon every `admin_init` to verify that the XML Sitemap is accessible and valid. If a previous error has been fixed, the error message will disappear immediately instead of taking 15 minutes (the Auto-Cache Engine run cycle). See [Issue #616](https://github.com/websharks/zencache/issues/616).
373
+ - **Enhancement**: Static CDN Filters are no longer applied by default for Logged-In Users. This was causing some confusion because Logged-In User caching is disabled by default. There is no harm in applying Static CDN Filters to all users (logged-in or not logged-in), in fact we recommend it, however we now disable this functionality by default to avoid confusion. A new option allows you to control this behavior and enable Static CDN Filters for Logged-In users; see _ZenCache → Logged-In Users → Static CDN Filters Enabled for Logged-In Users & Comment Authors?_ Props @kristineds, @lkraav, and @isaumya. See [Issue #486](https://github.com/websharks/zencache/issues/486).
374
+ - **Enhancement**: The Static CDN Filters now attempt to obey the `ZENCACHE_ALLOWED` and `DONOTCACHEPAGE` constants so that Static CDN Filters are not applied when caching is disabled. However, due to the way Static CDN Filters are implemented this is not always reliable. If you need to programmatically disable Static CDN Filters, see [this article](http://zencache.com/kb-article/how-do-i-disable-static-cdn-filters-via-php/). See [Issue #628](https://github.com/websharks/zencache/issues/628).
375
+ - **Enhancement**: When activating ZenCache for the first time, a new Dashboard message now includes a helpful link to the options page to enable caching and review the options. Props @kristineds. See [Issue #112](https://github.com/websharks/zencache/issues/112).
376
+ - **Enhancement**: The automatic Cache Cleanup schedule that cleans up (deletes) expired/stale cache files has been changed from once every day to once every hour. Running the cleanup hourly makes ZenCache smarter when configured in certain ways and saves disk space. Props @kristineds. See [Issue #472](https://github.com/websharks/zencache/issues/472).
377
+ - **Enhancement:** The new default value for "Clear the PHP OPcache Too?" when clearing the cache manually is now disabled. Instead of having this option enabled by default, we automatically invalidate only the `advanced-cache.php` file in PHP's opcode cache whenever you update your configuration options, which allows ZenCache to function as expected. That was the most important reason for needing to clear the opcode cache in previous versions. Props @jaswsinc. Also props to @MarioKnight and @CotswoldPhoto for helping us narrow this down. See [Issue #624](https://github.com/websharks/zencache/issues/624).
378
+ - **Enhancement**: Improved `.htaccess` utilities so that an exclusive lock is acquired when reading+writing to the file. This helps avoid inadvertently corrupting the `.htaccess` file in scenarios where another process might be reading/writing to it at the same time. See [Issue #633](https://github.com/websharks/zencache/issues/633).
379
+ - **Multisite Enhancement**: The Auto-Cache Engine has a new configuration option when running on WP Multisite Networks that allows you to define whether or not the Auto-Cache Engine should look for XML Sitemaps on each of the child blogs. This now defaults to being off; i.e., by default, child blogs are no longer checked. Props @jaswsinc. See [Issue #618](https://github.com/websharks/zencache/issues/618#issuecomment-158695842).
380
+ - **Multisite Enhancement**: The Auto-Cache Engine XML Sitemap diagnostic reporting is now always disabled for child blogs and only errors related to a primary sitemap location are displayed on the Dashboard. Props @jaswsinc. See [Issue #618](https://github.com/websharks/zencache/issues/618#issuecomment-158695842).
381
+ - **bbPress Compatibility:** This release improves compatibility with bbPress when ZenCache Logged-In User caching is enabled. In this scenario, bbPress may generate links for Admins that contain time-sensitive `_wpnonce` values which could expire if cached and result in certain admin-related actions failing. ZenCache no longer caches pages that contain `_wpnonce` in the markup. This ensures that pages containing time-sensitive `nonce` values are not cached. Props @kristineds, @jaswsinc, and @clavaque. See [Issue #601](https://github.com/websharks/zencache/issues/601).
382
+ - **Akismet Compatibility:** ZenCache no longer caches pages that contain `akismet_comment_nonce` in the markup. This ensures that a page that contains a time-sensitive `nonce` value does not get cached. Props @kristineds and @jaswsinc. See [Issue #601](https://github.com/websharks/zencache/issues/601).
383
+ - **Akismet Compatibility:** ZenCache now automatically disables the Akismet Comment Nonce using [the `akismet_comment_nonce` filter](https://github.com/git-mirror/wordpress-akismet/blob/2.5.6/akismet.php#L333), which improves compatibility between Akismet and the page caching functionality provided by ZenCache. This ensures that pages do not contain time-sensitive `nonce` values that should not be cached. If you'd like to revert this behavior, please see [this article](http://zencache.com/kb-article/how-do-i-prevent-zencache-from-disabling-the-akismet-comment-nonce/). Props @kristineds and @jaswsinc. See [Issue #601](https://github.com/websharks/zencache/issues/601).
384
+ - **WooCommerce Compatibility:** This release improves compatibility with the WooCommerce Product Stock feature. When the Product Stock changes, ZenCache will now clear the cache file for the associated Product to ensure that the stock quantity that appears on the site remains up-to-date. See [Issue #597](https://github.com/websharks/zencache/issues/597).
385
+
386
+ = v151114 =
387
+
388
+ - **Announcement: The next version of ZenCache will require PHP 5.4+.** As of December 1st, 2015, the minimum PHP version required to run ZenCache will change to PHP 5.4+. This release of ZenCache will be the last version to support PHP 5.3. Please see announcement with further details: [New Minimum PHP Version: PHP 5.4](http://zencache.com/r/new-minimum-php-version-php-5-4/)
389
+ - **Announcement: The next version of ZenCache will not support the PHP APC Extension**. As of December 1st, 2015, ZenCache will no longer run with the PHP APC extension enabled. Please see announcement with further details: [PHP APC Extension No Longer Supported](http://zencache.com/r/php-apc-extension-no-longer-supported/)
390
+ - **New Feature!** The Clear Cache button in the Admin Bar now includes a sub-menu with several new options for clearing the cache from anywhere on your site. You can clear the cache for just the Home Page, the Current URL, a Specific URL, PHP's OPCache (if active), or the CDN Cache (when Static CDN Filters are configured). This menu comes in two flavors and can be customized (or disabled entirely) inside _ZenCache → Plugin Options → Manual Cache Clearing_. Props @jaswsinc. See [Issue #596](https://github.com/websharks/zencache/issues/596#issuecomment-152786080).
391
+ - **New Feature!** It's now possible to specify a list of Custom URLs whose cache files should be cleared whenever you update a Post/Page, approve a Comment, or make other changes where ZenCache detects that a Post/Page cache should be cleared to keep your site up-to-date. See _ZenCache → Plugin Options → Automatic Cache Clearing → Misc. Auto-Clear Options → Auto-Clear a List of Custom URLs Too?_ Props @kristineds. See [Issue #111](https://github.com/websharks/zencache/issues/111).
392
+ - **New Feature!** A new watered-down Regular Expression syntax is now supported in several existing ZenCache features, including the new list of Custom URLs to Auto-Clear, URI Exclusion Patterns, HTTP Referrer Exclusion Patterns, User-Agent Exclusion Patterns, and the HTML Compressor CSS Exclusion Patterns and JavaScript Exclusion Patterns. This new syntax greatly increases the power and flexibility of each of these features and makes things possible like the much-requested ability to Auto-Clear the Home Page or Posts Page of a site whenever a post cache is cleared. For more information on this new watered-down Regular Expression syntax, [this KB Article](http://zencache.com/r/watered-down-regex-syntax/). Props @kristineds @jaswsinc. See [Issue #191](https://github.com/websharks/zencache/issues/191).
393
+ - **Bug Fix**: Fixed a bug with Static CDN Filters and Cross-Origin Resource Sharing (CORS) that was generating a "Cross-Origin Request Blocked" error. ZenCache will now update the root `.htaccess` file to include a `Header set Access-Control-Allow-Origin "*"` rule for `ttf`, `ttc`, `otf`, `eot`, `woff`, `woff2`, `font.css`, `css`, and `js` files whenever the Static CDN Filters are enabled. This is necessary to avoid "Cross-Origin Request Blocked" errors. Note that if you are already experiencing this error, you should create and configure a new CDN hostname to resolve the issue. In our tests it appears that some CDNs are caching the initial header response received by the server, which means it's necessary to send the `Access-Control-Allow-Origin` header _before_ configuring the Static CDN Filters with a CDN hostname. See [Issue #427](https://github.com/websharks/zencache/issues/427#issuecomment-121774596).
394
+ - **Bug Fix**: Removed `eot,ttf,otf,woff` font extensions from the Static CDN Filter Blacklisted Extensions. These were added in a previous release in an attempt to resolve an issue with Cross-Origin Resource Sharing (CORS), however now that the HTML Compressor has been updated to work with Static CDN Filters, the CSS compressed by the HTML Compressor is now served from the CDN. Fonts are most likely to be referenced by CSS, which is static. So when Static CDN Filters are applied, the CSS is getting moved to the CDN and the fonts are then expected to live on the CDN too. By excluding them from the Static CDN Filter, we were creating a problem instead of solving one. This release removes the font extensions from the default Blacklisted Extensions so that fonts can be hosted on the CDN alongside the CSS that references them. See [Issue #427](https://github.com/websharks/zencache/issues/427#issuecomment-121777790).
395
+ - **Bug Fix**: Fixed a bug related to updating plugins with WP-CLI on a site that was running ZenCache Pro. While ZenCache Pro updates must still be done through the ZenCache Pro Updater inside the Dashboard, updating other plugins via WP-CLI was generating a harmless ZenCache exception: "Invalid argument; host token empty!". With this fix, ZenCache will properly detect when WP-CLI is running to avoid these errors. Props @MarioKnight @renzms. See [Issue #563](https://github.com/websharks/zencache/issues/563).
396
+ - **Bug Fix**: Fixed a bug where post previews were being cached when Logged-In User Caching and GET Request caching were both enabled (both are disabled by default). This release now detects previews in this scenario and excludes those requests from being cached. Props @clavaque @kristineds. See [Issue #583](https://github.com/websharks/zencache/issues/583).
397
+ - **Bug Fix**: Fixed an issue where a PHP Notice was generated when an inactive WordPress component was being upgraded. This issue did not have any adverse affect on the site, but this fix resolves the issue so that the notice won't appear in PHP logs. See [Issue #589](https://github.com/websharks/zencache/issues/589).
398
+ - **Bug Fix**: Fixed a bug where a commented-out `WP_CACHE` definition in `wp-config.php` (such as what WP Super Cache leaves behind) was being incorrectly ignored and resulted in caching being silently disabled. Props @davidfavor. See [Issue #591](https://github.com/websharks/zencache/issues/591).
399
+ - **Bug Fix**: Fixed a bug where in some scenarios a page might not be cached due to a stray `AUTH_COOKIE` or `SECURE_AUTH_COOKIE` cookie, even when the user is not logged in. Props @jaswsinc @renzms. See [Issue #592](https://github.com/websharks/zencache/issues/592).
400
+ - **Enhancement**: Excluded several unnecessary files from the plugin zip file that were being used during the build process but were not necessary to run the plugin. Props @bridgeport. See [Issue #579](https://github.com/websharks/zencache/issues/579).
401
+ - **Enhancement**: When the Cache Directory location is changed, ZenCache now cleans up the old Cache Directory and any old cache files instead of leaving them behind. Props @clavaque @renzms. See [Issue #580](https://github.com/websharks/zencache/issues/580).
402
+ - **Enhancement**: Added a new API Function that allows a site owner to clear the cache for a specific URL via `zencache::clearUrl($url);`. See [this article](http://zencache.com/kb-article/clearing-the-cache-dynamically/#toc-988085ad) for further details. Props @kristineds. See [Issue #590](https://github.com/websharks/zencache/issues/590).
403
+ - **Enhancement**: Improved the HTML Notes generated by ZenCache when s2Member (a membership plugin for WordPress) is specifically disabling caching. s2Member automatically disables caching on certain pages that are required to remain dynamic. The HTML Notes generated by ZenCache now explain when this is happening. Props @renzms. See [Issue #504](https://github.com/websharks/zencache/issues/504).
404
+ - **Enhancement**: In Logged-In User Caching, the "Yes, but DON'T maintain a separate cache for each user" option has been hidden because this particular option has the potential to create a security issue if not configured properly. If you were already using this option, it will not be hidden and it will continue to work. Otherwise, if you require this particular option you can now enable it using a filter (see [this comment](https://github.com/websharks/zencache/issues/497#issuecomment-146060440)). Props @renzms @jaswsinc. See [Issue #497](https://github.com/websharks/zencache/issues/497).
405
+ - **Enhancement**: The Auto-Cache Engine now detects when the configured XML Sitemap is not valid or is unreachable and displays an appropriate notice. Props @kristineds and @jaswsinc. See [Issue #555](https://github.com/websharks/zencache/issues/555).
406
+
407
+ = v151004 =
408
+
409
+ - **Bug Fix**: Fixed a bug introduced in the previous release that was resulting in a "Fatal error: Using $this when not in object context" for sites running PHP 5.3. (PHP 5.4+ sites were unaffected.) Props @jaswsinc. See [Issue #581](https://github.com/websharks/zencache/issues/581).
410
+
411
+ = v151002 =
412
+
413
+ - **New Feature!** Cache Statistics is a completely new ZenCache Pro feature that will help site owners better understand their WordPress site cache. An easy-to-access Cache Stats menu button in the Admin Bar is accompanied by a whole new page that shows Current Cache Totals (including total number of cache files and total size of cache on the disk), Current Disk Health (including total disk capacity and total available), Current System Health (including memory usage and load average), and two beautiful Polar Area pie charts that show you both current and historical data on Cache File Counts and Cache File Sizes with a 30-Day High, Current Total, Page Cache total, and HTML Compressor total for each chart. Props to @jaswsinc. See [Issue #83](https://github.com/websharks/zencache/issues/83).
414
+ - **New Feature!** It's now possible to specify which WordPress Roles/Capabilities are allowed to clear the cache from the WordPress Admin bar. A new option inside _Dashboard → ZenCache → Plugin Options → Manual Cache Clearing_ allows specifying a comma-delimited list of Roles and/or Capabilities under, "Also allow others to clear the cache from their Admin Bar?". If a user has an allowed Role/Capability, they will see the "Clear Cache" button in their Admin Bar. This feature is also compatible with WordPress Multisite Networks, allowing a Network Administrator to define which Child Site roles should allow a Child Site user to see the "Clear Cache" button for their Child Site. Props @danielmt2k @jaswsinc. See [Issue #68](https://github.com/websharks/zencache/issues/68).
415
+ - **New Feature!** It's now possible to "Disable Cache Expiration If Server Load Average is High" (see _Dashboard → ZenCache → Plugin Options → Cache Expiration Time_). This allows you to provide a specific load average that should cause ZenCache to disable cache expiration and help reduce stress on the server; i.e., to avoid generating a new version of the cache while the server is very busy. Props @jaswsinc. See [Issue #347](https://github.com/websharks/zencache/issues/347).
416
+ - **New Feature!** It's now possible to manually clear the CDN Cache when Static CDN Filters are enabled. Inside the Static CDN Filters options panel there's a new "Clear CDN Cache" button and there's also a new option ("Clear the CDN Cache Too?") inside _Dashboard → ZenCache → Plugin Options → Manual Cache Clearing_ that allows you to specify whether or not you'd like the CDN Cache to be cleared whenever the "Clear Cache" button is clicked (either from the Admin Bar or from inside the Plugin Options). Props @kristineds @jaswsinc. See [Issue #488](https://github.com/websharks/zencache/issues/488).
417
+ - **New Feature!** When your server has the [PHP OPCache Extension](http://zencache.com/r/php-opcache/) installed, ZenCache can now be configured to also clear the PHP opcode cache whenever you clear the cache manually using the ZenCache "Clear Cache" button. See _ZenCache Options → Manual Cache Clearing → Clear the PHP OPCache Too?_ (note that this option only appears if you have the OPCache Extension installed). Props @jaswsinc. See [Issue #489](https://github.com/websharks/zencache/issues/489).
418
+ - **Bug Fix**: Fixed an HTML Compressor bug related to CSS pseudo-classes where spaces between the class name and pseudo-class name were being removed when CSS was minified. Props @patdumond @jaswsinc. See [Issue #544](https://github.com/websharks/zencache/issues/544).
419
+ - **Bug Fix**: Fixed an HTML Compressor bug related to `<noscript>` tags. The HTML Compressor now makes no adjustments to anything inside `<noscript>` tags, and the same has always been true for IE conditional comments as well. Props @rtrevellyan @jaswsinc. See [Issue #65](https://github.com/websharks/html-compressor/issues/65).
420
+ - **Bug Fix**: Fixed an issue related to a popular NGINX server configuration (`try_files $uri $uri/ /index.php?q=$uri&$args;`) that was preventing the entire site from being cached. ZenCache disables caching by default for all requests that include a query string (see _Dashboard → ZenCache → Plugin Options → GET Requests_) and this particular NGINX configuration passes _all_ requests to WordPress with a `?q=` query variable, which was resulting in ZenCache disabling caching on all pages. This release implements better detection for NGINX and works around this scenario. Props @jaswsinc. See [Issue #561](https://github.com/websharks/zencache/issues/561).
421
+ - **Bug Fix**: Fixed a bug with the Static CDN Filters that affected sites using a permalink structure that ended with `.htm` or `.html`. Generally, files that end in `.htm` or `.html` are considered static files, hence the reason ZenCache was rewriting URLs with those extensions to point at the configured CDN. However, if a site uses `.htm` or `.html` in their permalink structure, all links to Posts/Pages within the site will appear to be static files when they are in fact dynamic and therefore should not be rewritten. ZenCache now checks the permalink structure and excludes `.htm` and `.html` from the allowed extensions when the permalink structure is using one of these. Props @jaswsinc. See [Issue #495](https://github.com/websharks/zencache/issues/495).
422
+ - **Bug Fix**: Fixed an SSL issue with the HTML Compressor that was causing problems in some hosting environments where the hosting server was incorrectly setting `$_SERVER['REQUEST_SCHEME']` to `http` even when the WordPress Site URL and Home URL were set to use `https://`. As a result of this improper server configuration, the combined CSS/JS files generated by the HTML Compressor were being served over HTTP even when a site was configured to use HTTPS. This release applies a workaround for this improper server configuration that no longer looks at `$_SERVER['REQUEST_SCHEME']`. See [Issue #413](https://github.com/websharks/zencache/issues/413) and [Issue #73](https://github.com/websharks/html-compressor/issues/73).
423
+ - **Multisite Bug Fix**: Fixed a bug in the Auto-Cache Engine that was resulting in duplicate Cron Jobs being created for each Child Site. The Auto-Cache Engine now only runs from the Main Site in a network, as it should. When the Auto-Cache Engine runs on the Main Site, it will also run for each of the Child Blogs (see [this article](http://zencache.com/r/kb-article-how-does-the-auto-cache-engine-cache-child-blogs-in-a-multisite-network/) for more information). Props @jaswsinc. See [Issue #543](https://github.com/websharks/zencache/issues/543).
424
+ - **Enhancement**: Improved HTML Compressor HTTP connection handling, timeouts, protocol, BOM markers, exceptions, `Referer:` and `User-Agent:` headers. Props @LittleBastard77 @jaswsinc. See [Issue #391](https://github.com/websharks/zencache/issues/391) and [Issue #69](https://github.com/websharks/html-compressor/issues/69).
425
+ - **Enhancement**: Manual Cache Clearing options have now been separated from Automatic Cache Clearing options inside the ZenCache Plugin Options to improve the differentiation between these.
426
+ - **Enhancement**: New icons in the ZenCache Plugin Options help improve the visual representation of each panel.
427
+ - **Enhancement**: The installed plugin version is now shown at the top of the ZenCache Options Page. When a newer version of the plugin is available, an "update available" link appears. Props @renzms. See [Issue #502](https://github.com/websharks/zencache/issues/502).
428
+ - **Enhancement**: New transition in/out effects on the Cache Cleared Dashboard notifications. Props @jaswsinc. See [Issue #538](https://github.com/websharks/zencache/issues/538).
429
+
430
+ = v150821 =
431
+
432
+ - **Multisite Domain Mapping Compatibility**: ZenCache Pro is now fully compatible with the [WordPress MU Domain Mapping plugin](https://wordpress.org/plugins/wordpress-mu-domain-mapping/), including Multisite Networks using domain mapping on top-level domains, sub-domains, and sub-directories. Multiple variations of each site spread across an unlimited number of domain mappings and/or the original blog domain/path is also supported. All other features of ZenCache Pro, including the Auto-Cache Engine, Static CDN Filters, and HTML Compression are also now compatible with domain mapping. Props @jaswsinc. See [Issue #339](https://github.com/websharks/zencache/issues/339).
433
+ - **bbPress Compatibility**: This release greatly improves compatibility with bbPress. Events like creating a new Forum, creating a new Topic, and posting a reply to a Topic (including threaded replies), now properly clear the necessary cache files to ensure that cached bbPress pages remain up-to-date. Props @jaswsinc. See [Issue #168](https://github.com/websharks/zencache/issues/168).
434
+ - **Bug Fix**: Fixed a bug where, in some rare cases, `wp-config.php` would end up with two `WP_CACHE` definitions. Props @jaswsinc. See [Issue #509](https://github.com/websharks/zencache/issues/509).
435
+ - **Bug Fix**: Saving a Post as a Draft was incorrectly purging XML Sitemap cache files. Props @jaswsinc. See [Issue #368](https://github.com/websharks/zencache/issues/368).
436
+ - **Bug Fix:** The HTML Compressor now strips UTF-8 Byte Order Markers (BOMs) from CSS files generated by preprocessors such as Sass. Files consolidated by the HTML Compressor include an `@charset` rule already, making a BOM unnecessary. In fact, if BOMs are not stripped, some browsers will choke on portions of a consolidated CSS file, because a BOM could potentially appear in the middle of the file; i.e., at the _wrong_ place. Symptoms included mysterious missing styles in portions of the site, fonts not loading at all times, or font-based icons (e.g., FontAwesome) to render mystery glyphs instead of the icons. Props @jaswsinc. See [Issue #52](https://github.com/websharks/html-compressor/issues/52).
437
+ - **Bug Fix:** Improved HTML Compressor strict data type comparison when analyzing `$_SERVER` environment variables to detect an `https://` connection URL. We now detect `(integer)443` and `(string)443`. In addition, we can now pick up `$_SERVER['HTTPS']` being any of `1|on|yes|true` (caSe insensitive). Props @jaswsinc. See [Issue #61](https://github.com/websharks/html-compressor/issues/61).
438
+ - **Multisite Bug Fix**: Fixed a bug where the Clear Cache button wouldn't clear Child-Site Logged-In User Home Page cache files on WordPress Multisite Networks. Props @jaswsinc. See [Issue #409](https://github.com/websharks/zencache/issues/409)
439
+ - **Multisite Bug Fix**: Fixed a bug where the Home Page cache was not clearing on Child Sites in a Multisite Network. Props @jaswsinc. See [Issue #409](https://github.com/websharks/zencache/issues/409)
440
+ - **Multisite Bug Fix**: Fixed an issue where the Auto-Cache Engine would fail to auto-cache child blogs in a multisite network whenever the network was being served from a sub-directory. Props @jaswsinc. See [Issue #537](https://github.com/websharks/zencache/issues/537).
441
+ - **Multisite Bug Fix**: Fixed a bug with 404 Request caching on Multisite Networks where ZenCache Pro was not considering that each child blog in a multisite network will have its own 404 error page. Props @jaswsinc. See [Issue #539](https://github.com/websharks/zencache/issues/539).
442
+ - **Multisite Bug Fix**: Fixed a bug where clearing the cache from the main site of a multisite network, when there are child blogs in sub-directories, resulted in all child blogs being cleared from the cache, not just the main site as would be expected. This has been resolved and only the main site is cleared when clicking the Clear Cache button. Note that the Wipe Cache button can still be used to clear the cache for all sites in a Multisite Network. Props @jaswsinc. See [Issue #540](https://github.com/websharks/zencache/issues/540).
443
+ - **Multisite Bug Fix**: Fixed a bug where Wiping the cache on a multisite network resulted in the very next page view being cached incorrectly. Props @jaswsinc. See [Issue #541](https://github.com/websharks/zencache/issues/541).
444
+ - **Enhancement**: Improved compatibility with any Custom Post Type that uses hierarchies. Props @jaswsinc.
445
+
446
+ = v150716 =
447
+
448
+ - **Bug Fix**: Fixed a fatal error that was occurring on some sites after upgrading to v150629. We discovered the fatal error was related to PHP's outdated and buggy APC extension, which is often active on sites running PHP 5.3 and PHP 5.4. We now prevent activation of ZenCache on systems with the APC extension enabled and show a warning message instead. We've written [a KB Article that further explains the APC Extension Warning](http://zencache.com/kb-article/why-does-zencache-show-an-apc-extension-warning/) and offers advice to site owners who are currently running APC. See also [Issue #511](https://github.com/websharks/zencache/issues/511).
449
+ - **Bug Fix**: Fixed a bug in the Dynamic Version Salt filter that was generating PHP notices and warnings. See [Issue #522](https://github.com/websharks/zencache/issues/522).
450
+ - **Bug Fix**: Fixed an issue with backwards compatibility that was preventing some AC Plugins from working properly. See [Issue #514](https://github.com/websharks/zencache/issues/514).
451
+ - **Enhancement**: Added a stats pinger, similar to the WordPress stats collector, that reports the server OS, PHP version, MySQL version, WordPress version, and the ZenCache version, securely and anonymously to WebSharks. See [this KB Article](http://zencache.com/kb-article/what-information-does-zencache-pro-report-to-websharks/) for more information.
452
+
453
+ = v150626 =
454
+
455
+ - **Restructured Codebase**: The entire ZenCache codebase has been restructured to improve performance, enhance flexibility, and make it easier to build in new features!
456
+ - **New Feature!** The free version of ZenCache now supports several new options that were previously only available in the Pro version. You can now toggle the Auto-Clear Cache routines for the Home Page, Posts Page, Author Page, Category Archives, Tag Archives, Custom Term Archives, RSS/RDF/Atom Feeds, and XML Sitemaps. This gives you more control over exactly when ZenCache purges the cache for these parts of your site. See _ZenCache → Plugin Options → Clearing the Cache_ for further details.
457
+ - **New Feature!** URI Exclusion Patterns are now available in ZenCache Lite! This previously Pro-only feature is now available in the free version of ZenCache and allows you to exclude a list of URIs from being cached by ZenCache. See _ZenCache → Plugin Options → URI Exclusion Patterns_ for further details.
458
+ - **New Feature!** HTTP Referrer Exclusion Patterns are now available in ZenCache Lite! This previously Pro-only feature is now available in the free version of ZenCache and allows you to define a list of referring URLs or domains that send you traffic. When ZenCache sees a request coming from one of those URLs or domains, it will not cache that particular request. See _ZenCache → Plugin Options → HTTP Referrer Exclusion Patterns_ for further details.
459
+ - **New Pro Feature!**: HTML Compression now supports compressing JSON (in addition to the already supported HTML, JavaScript, and CSS compression). Props @jaswsinc. See [Issue #469](https://github.com/websharks/zencache/issues/469).
460
+ - **New Pro Feature!**: Static CDN Filters now supports multiple CDN hostnames. This allows you to configure more than one CDN hostname, also referred to as Domain Sharding. This makes it possible for site owners to work around web browser concurrency limits, allowing the browser to download many resources simultaneously, which increases overall speed. Props to @isaumya and @jaswsinc. See [Issue #468](https://github.com/websharks/zencache/issues/468).
461
+ - **Enhancement** (Pro): Static CDN Filters now includes proper support for WordPress Multisite Networks, including support for subdomains (full support for Domain Mapping coming in the next release). If you're running a WordPress Multisite Network and want to configure a CDN, see [this KB Article](http://zencache.com/kb-article/static-cdn-filters-for-wordpress-multisite-networks/) for further details.
462
+ - **Enhancement** (Pro): Static CDN Filters now also apply to any static files that are referenced inside CSS files. Props @jaswsinc. See [Issue #461](https://github.com/websharks/zencache/issues/461).
463
+ - **Enhancement**: Completed a major restructure of the entire codebase to improve modularity and dependency management. Props @jaswsinc.
464
+ - **Enhancement** (Pro): Static CDN Filters now supports the ability to configure separate CDN hostname(s) for each domain (or subdomain) that you run in a WordPress Multisite Network. Props @jaswsinc. See [Issue #475](https://github.com/websharks/zencache/issues/475).
465
+ - **Enhancement** (Pro): Static CDN Filters now support subdomains when ZenCache is running inside a WordPress Multisite Network. Props @jaswsinc. See [Issue #439](https://github.com/websharks/zencache/issues/439).
466
+ - **Bug Fix** (Pro): Static CDN Filters were not being applied to the primary site on WP Multisite installations that used subdomains. Props to @isaumya for discovering this bug. See [Issue #470](https://github.com/websharks/zencache/issues/470).
467
+
468
+ = v150409 =
469
+
470
+ - **Enhancement (includes improved CloudFlare support)**: Improvements to IP address detection, including added support for CloudFlare IP forwarding, multiple IPs in a single header, and the ability to customize the lookup order and/or add/remove sources that are searched when looking for the current IP address. It's also possible to revert to the old IP address detection behavior (see [How do I customize remote IP detection?](http://zencache.com/kb-article/how-do-i-customize-ip-address-detection/)). Props @jaswsinc. [Issue #449](https://github.com/websharks/zencache/pull/449)
471
+ - **Enhancement** (Pro): Files being served by the HTML Compressor were being sent without a `Vary: Accept-Encoding` header, which caused some page speed testing services to give a lower rating to sites using ZenCache. ZenCache now ensures this header is sent via an `.htaccess` file inside the HTML Compressor cache directory (requires Apache 2.1+). Props @jaswsinc. [Issue #436](https://github.com/websharks/zencache/issues/436).
472
+ - **Enhancement** (Pro): Static CDN Filters now also filter any static resources inside Text Widgets, so that those resources can be cached by your CDN. Props @jaswsinc. [Issue #430](https://github.com/websharks/zencache/issues/430)
473
+ - **Enhancement** (Pro): Static CDN Filters now apply to minified and compressed CSS/JS files generated by the HTML Compressor; these files will now be cached by your CDN. Props @jaswsinc. [Issue #429](https://github.com/websharks/zencache/issues/429)
474
+ - **Bug Fix**: Fixed a bug related to the Quick Cache migration that resulted in caching being disabled despite ZenCache being enabled. Uninstalling Quick Cache was removing `define('WP_CACHE', TRUE);` from the `wp-config.php` file. ZenCache now makes sure that caching remains enabled after uninstalling Quick Cache during the ZenCache migration process. [Issue #450](https://github.com/websharks/zencache/issues/450).
475
+ - **Bug Fix**: Fixed a minor UI issue where the ZenCache Dashboard icon would occasionally flash to a black color when refreshing the Dashboard. Props @jaswsinc. [Issue #453](https://github.com/websharks/zencache/issues/453).
476
+ - **Bug Fix**: When ZenCache was running on an installation of PHP with `open_basedir` restrictions applied, calls to `is_dir()` were triggering a PHP Warning while looking for a writable temporary directory. This bug has been fixed. [Issue #456](https://github.com/websharks/zencache/issues/456).
477
+ - **Bug Fix**: Fixed a bug where changing the permalink for a published post would result in the cache file for the old permalink being left behind and as a result both the old and the new permalink would be accessible, instead of WordPress redirecting the old permalink to the new one. This has been fixed and ZenCache now properly clears the old cache file when changing the permalink on a published post. [Issue #359](https://github.com/websharks/zencache/issues/359).
478
+ - **Bug Fix**: Fixed a bug where transitioning a Published post back to Pending or Draft would not automatically clear the cache file. This resulted in the post remaining accessible on the frontend despite being set as Pending or Draft. ZenCache now properly clears the cache file automatically when transitioning from Published to Pending or Draft, which prevents access to the post as expected. [Issue #441](https://github.com/websharks/zencache/issues/441).
479
+ - **Bug Fix** (Pro): Some users reported seeing `Warning: trim() expects parameter 1 to be string`. This was produced by a low-impact bug that has been fixed in this release. [Issue #455](https://github.com/websharks/zencache/issues/455).
480
+ - **Bug Fix** (Pro): Some users running the HTML Compressor reported seeing 403 Forbidden errors related to loading the compressed/minified files. This was a permissions-related issue that has been resolved in this release. Props @superian and @jaswsinc. See [Issue #50](https://github.com/websharks/html-compressor/issues/50).
481
+ - **Bug Fix** (Pro): Some HTTP requests made by the HTML Compressor to servers configured to reject HTTP requests that don't include a User-Agent were failing. A User-Agent is now always used in all requests. Props @jaswsinc. See [Issue #49](https://github.com/websharks/html-compressor/issues/49).
482
+ - **Minimum Required PHP Version Bumped to PHP v5.3.2+:** The minimum PHP version required for ZenCache has been bumped up to PHP v5.3.2+ (from PHP v5.3+). The ZenCache cache locking mechanism, specifically the use of `flock()`, requires behavior introduced in PHP v5.3.2. [Issue #444](https://github.com/websharks/zencache/issues/444).
483
+
484
+ = v150314 =
485
+
486
+ - **Bug Fix**: Fixed a bug in the Quick Cache back compat. method `\zencache\share\apply_wp_filters()`. It was not passing the ZenCache-filtered value through the Quick Cache back compat. routine. This bug affected sites that had disabled automatic cache clearing routines via a filter.
487
+ - **Bug Fix** (Pro): Updated Static CDN Filters whitelist to include font file extensions `eot,ttf,otf,woff`, as site owners may wish to serve fonts with these extensions from the CDN (this would require a custom CORS configuration; see [this article](http://davidwalsh.name/cdn-fonts) for instructions). See [#427](https://github.com/websharks/zencache/issues/427).
488
+ - **Bug Fix** (Pro): Updated Static CDN Filters blacklist to exclude font file extensions `eot,ttf,otf,woff` by default, as they require custom CORS configuration to display properly. See [#427](https://github.com/websharks/zencache/issues/427).
489
+ - **Bug Fix** (Pro): The Static CDN Filters feature was calling a protected method that was requiring PHP v5.4+ but now the Static CDN Filters feature works with PHP v5.3+. See [#426](https://github.com/websharks/zencache/issues/426).
490
+ - **Bug Fix**: Fixed a zero-byte `advanced-cache.php` bug related to migrating from Quick Cache. When you deleted/uninstalled Quick Cache after upgrading to ZenCache, the Quick Cache uninstallation process would empty the `advanced-cache.php` file, resulting in the site no longer being cached, despite ZenCache being active. ZenCache now continuously checks to make sure that `advanced-cache.php` is installed properly. See [#432](https://github.com/websharks/zencache/issues/432).
491
+
492
+ = v150218 =
493
+
494
+ **Announcing ZenCache, formerly Quick Cache!**
495
+
496
+ We are very excited to announce the release of [ZenCache](http://zencache.com), an advanced WordPress caching plugin inspired by simplicity. ZenCache is the successor to Quick Cache, a very popular WordPress caching plugin that has been downloaded over 1 million times and won acclaim for its speed, simplicity, and ease of configuration. [Read the full announcement here](http://zencache.com/announcing-zencache-formerly-quick-cache/).
497
+
498
+ ZenCache is an evolution of Quick Cache. It comes with a completely refactored codebase, subtle UI enhancements, internal optimizations; along with full backward compatibility with all previous versions of Quick Cache Lite and Quick Cache Pro. In fact, installing ZenCache on a site that was previously running Quick Cache is a piece of cake! Watch [this video](http://zencache.com/kb-article/how-to-migrate-from-quick-cache-lite-to-zencache-lite/) to learn more. All configuration options are preserved.
499
+
500
+ - **New Pro Feature: CDN Integration:** With the announcement and release of ZenCache we are excited to also announce a new, highly-requested feature: Static CDN Filters. This feature allows you to serve some and/or ALL static files on your site from a CDN of your choosing. See also: [Introduction to Static CDN Filters](http://zencache.com/kb-article/introduction-to-static-cdn-filters/).
501
+ - **Full Backwards Compatibility with Quick Cache and Quick Cache Pro:** ZenCache is fully backwards compatible with all previous versions of Quick Cache Lite and Quick Cache Pro. Simply install ZenCache and your existing Quick Cache options will preserved by ZenCache.
502
+
503
+ = v150129 =
504
+
505
+ - **Bug Fix** (Pro): Fixed a bug where the Pro Updater would fail when FTP or SFTP details via the WordPress Dashboard are required to perform updates. Props @jaswsinc. See [#389](https://github.com/websharks/quick-cache/issues/389).
506
+ - **Bug Fix**: Several fixes for a stubborn bug related to "Fatal Error: 'Unable to clear dir'" error messages and errors referencing "SplFileInfo::getMTime(): stat failed". Props @jaswsinc. See [#397](https://github.com/websharks/quick-cache/issues/397).
507
+ - **Bug Fix** (Pro): Fixed a bug where the `qcAC` variable (used to force-enable/disable GET Request caching) was not respected properly whenever a URL contained a query string and a user was currently logged into the site. Props @jaswsinc. See [#401](https://github.com/websharks/quick-cache/issues/401).
508
+
509
+ For older Changelog entries, please see the `CHANGELOG.md` file.
src/client-s/css/admin-bar.min.css ADDED
@@ -0,0 +1,2 @@
 
 
1
+ 
2
+ /*# sourceMappingURL=admin-bar.min.css.map */
src/client-s/css/menu-pages.min.css ADDED
@@ -0,0 +1,2 @@
 
 
1
+ @font-face{font-family:sharkicons;src:url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.eot");src:url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.eot?#iefix") format("embedded-opentype"),url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.ttf") format("truetype"),url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.woff") format("woff"),url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.svg#sharkicons") format("svg");font-weight:normal;font-style:normal}.si::before{font:normal normal normal 14px/1 sharkicons;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;font-smoothing:antialiased;display:inline-block;font-size:inherit;text-decoration:inherit;text-transform:none}.si-broom::before{content:""}.si-comment-mail-one::before{content:""}.si-comment-mail::before{content:""}.si-s2member::before{content:""}.si-websharks::before{content:""}.si-wp-kb-articles::before{content:""}.si-zencache-logo::before{content:""}.si-zencache::before{content:""}.si-wp-sharks::before{content:""}.si-wp-sharks-fin::before{content:""}.si-comet_cache::before{content:""}.si-comet_cache-logo::before{content:""}.si-comet_cache-comet::before{content:""}.si-feat-watch::before{content:""}.si-feat-server::before{content:""}.si-feat-layers::before{content:""}.si-feat-box::before{content:""}.si-feat-ellipsis::before{content:""}.si-typi-group::before{content:""}.si-enty-bookmark::before{content:""}.si-enty-bookmarks::before{content:""}.si-enty-open-book::before{content:""}.si-enty-archive::before{content:""}.si-enty-area-graph::before{content:""}.si-enty-bucket::before{content:""}.si-enty-colors::before{content:""}.si-enty-copy::before{content:""}.si-enty-drive::before{content:""}.si-enty-feather::before{content:""}.si-enty-gauge::before{content:""}.si-enty-hand::before{content:""}.si-enty-lab-flask::before{content:""}.si-enty-mask::before{content:""}.si-enty-medal::before{content:""}.si-enty-exclamation::before{content:""}.si-enty-palette::before{content:""}.si-enty-ruler::before{content:""}.si-enty-shop::before{content:""}.si-enty-basket::before{content:""}.si-enty-cart::before{content:""}.si-enty-traffic-cone::before{content:""}.si-enty-tree::before{content:""}.si-enty-trophy::before{content:""}.si-enty-v-card::before{content:""}.si-enty-google-hangouts::before{content:""}.si-eleg-line-graph::before{content:""}.si-eleg-male::before{content:""}.si-eleg-female::before{content:""}.si-eleg-atom::before{content:""}.si-broc-cart::before{content:""}.si-broc-crap::before{content:""}.si-broc-atom::before{content:""}.si-icom-headphones::before{content:""}.si-icom-barcode::before{content:""}.si-icom-user::before{content:""}.si-icom-users::before{content:""}.si-icom-user-plus::before{content:""}.si-icom-user-minus::before{content:""}.si-icom-user-check::before{content:""}.si-icom-user-tie::before{content:""}.si-icom-key::before{content:""}.si-icom-key2::before{content:""}.si-icom-happy::before{content:""}.si-icom-happy2::before{content:""}.si-icom-smile::before{content:""}.si-icom-smile2::before{content:""}.si-icom-tongue::before{content:""}.si-icom-tongue2::before{content:""}.si-icom-sad::before{content:""}.si-icom-sad2::before{content:""}.si-icom-wink::before{content:""}.si-icom-wink2::before{content:""}.si-icom-grin::before{content:""}.si-icom-grin2::before{content:""}.si-icom-cool::before{content:""}.si-icom-cool2::before{content:""}.si-icom-angry::before{content:""}.si-icom-angry2::before{content:""}.si-icom-evil::before{content:""}.si-icom-evil2::before{content:""}.si-icom-shocked::before{content:""}.si-icom-shocked2::before{content:""}.si-icom-baffled::before{content:""}.si-icom-baffled2::before{content:""}.si-icom-confused::before{content:""}.si-icom-confused2::before{content:""}.si-icom-neutral::before{content:""}.si-icom-neutral2::before{content:""}.si-icom-hipster::before{content:""}.si-icom-hipster2::before{content:""}.si-icom-wondering::before{content:""}.si-icom-wondering2::before{content:""}.si-icom-sleepy::before{content:""}.si-icom-sleepy2::before{content:""}.si-icom-frustrated::before{content:""}.si-icom-frustrated2::before{content:""}.si-icom-crying::before{content:""}.si-icom-crying2::before{content:""}.si-icom-spell-check::before{content:""}.si-icom-command-key::before{content:""}.si-icom-shift-key::before{content:""}.si-icom-control-key::before{content:""}.si-icom-option-key::before{content:""}.si-icom-wordpress::before{content:""}.si-icom-wordpress-square::before{content:""}.si-icom-yahoo::before{content:""}.si-icom-linux::before{content:""}.si-icom-finder::before{content:""}.si-icom-android::before{content:""}.si-icom-reddit::before{content:""}.si-icom-paypal::before{content:""}.si-icom-git::before{content:""}.si-octi-alignment-align::before{content:""}.si-octi-alignment-aligned-to::before{content:""}.si-octi-alignment-unalign::before{content:""}.si-octi-bookmark::before{content:""}.si-octi-broadcast::before{content:""}.si-octi-browser::before{content:""}.si-octi-checklist::before{content:""}.si-octi-circuit-board::before{content:""}.si-octi-clippy::before{content:""}.si-octi-cloud-download::before{content:""}.si-octi-cloud-upload::before{content:""}.si-octi-comment::before{content:""}.si-octi-comments::before{content:""}.si-octi-tach::before{content:""}.si-octi-device-camera::before{content:""}.si-octi-device-camera-video::before{content:""}.si-octi-device-desktop::before{content:""}.si-octi-diff::before{content:""}.si-octi-file-binary::before{content:""}.si-octi-file-media::before{content:""}.si-octi-file-submodule::before{content:""}.si-octi-file-symlink-directory::before{content:""}.si-octi-file-symlink-file::before{content:""}.si-octi-fold::before{content:""}.si-octi-git-branch::before{content:""}.si-octi-git-commit::before{content:""}.si-octi-git-compare::before{content:""}.si-octi-git-merge::before{content:""}.si-octi-git-pull-request::before{content:""}.si-octi-graph::before{content:""}.si-octi-home::before{content:""}.si-octi-horizontal-rule::before{content:""}.si-octi-key::before{content:""}.si-octi-light-bulb::before{content:""}.si-octi-link-external::before{content:""}.si-octi-lock::before{content:""}.si-octi-markdown::before{content:""}.si-octi-microscope::before{content:""}.si-octi-mirror::before{content:""}.si-octi-move-down::before{content:""}.si-octi-move-left::before{content:""}.si-octi-move-right::before{content:""}.si-octi-move-up::before{content:""}.si-octi-mute::before{content:""}.si-octi-organization::before{content:""}.si-octi-package::before{content:""}.si-octi-paintcan::before{content:""}.si-octi-person::before{content:""}.si-octi-plug::before{content:""}.si-octi-podium::before{content:""}.si-octi-pulse::before{content:""}.si-octi-puzzle::before{content:""}.si-octi-repo::before{content:""}.si-octi-repo-clone::before{content:""}.si-octi-repo-force-push::before{content:""}.si-octi-repo-forked::before{content:""}.si-octi-repo-pull::before{content:""}.si-octi-repo-push::before{content:""}.si-octi-rocket::before{content:""}.si-octi-ruby::before{content:""}.si-octi-screen-full::before{content:""}.si-octi-screen-normal::before{content:""}.si-octi-sign-in::before{content:""}.si-octi-sign-out::before{content:""}.si-octi-split::before{content:""}.si-octi-squirrel::before{content:""}.si-octi-steps::before{content:""}.si-octi-tag::before{content:""}.si-octi-telescope::before{content:""}.si-octi-terminal::before{content:""}.si-octi-unfold::before{content:""}.si-octi-versions::before{content:""}.si-glass::before{content:""}.si-music::before{content:""}.si-search::before{content:""}.si-envelope-o::before{content:""}.si-heart::before{content:""}.si-star::before{content:""}.si-star-o::before{content:""}.si-user::before{content:""}.si-film::before{content:""}.si-th-large::before{content:""}.si-th::before{content:""}.si-th-list::before{content:""}.si-check::before{content:""}.si-close::before{content:""}.si-search-plus::before{content:""}.si-search-minus::before{content:""}.si-power-off::before{content:""}.si-signal::before{content:""}.si-cog::before{content:""}.si-trash-o::before{content:""}.si-home::before{content:""}.si-file-o::before{content:""}.si-clock-o::before{content:""}.si-road::before{content:""}.si-download::before{content:""}.si-arrow-circle-o-down::before{content:""}.si-arrow-circle-o-up::before{content:""}.si-inbox::before{content:""}.si-play-circle-o::before{content:""}.si-repeat::before{content:""}.si-refresh::before{content:""}.si-list-alt::before{content:""}.si-lock::before{content:""}.si-flag::before{content:""}.si-headphones::before{content:""}.si-volume-off::before{content:""}.si-volume-down::before{content:""}.si-volume-up::before{content:""}.si-qrcode::before{content:""}.si-barcode::before{content:""}.si-tag::before{content:""}.si-tags::before{content:""}.si-book::before{content:""}.si-bookmark::before{content:""}.si-print::before{content:""}.si-camera::before{content:""}.si-font::before{content:""}.si-bold::before{content:""}.si-italic::before{content:""}.si-text-height::before{content:""}.si-text-width::before{content:""}.si-align-left::before{content:""}.si-align-center::before{content:""}.si-align-right::before{content:""}.si-align-justify::before{content:""}.si-list::before{content:""}.si-dedent::before{content:""}.si-indent::before{content:""}.si-video-camera::before{content:""}.si-image::before{content:""}.si-pencil::before{content:""}.si-map-marker::before{content:""}.si-adjust::before{content:""}.si-tint::before{content:""}.si-edit::before{content:""}.si-share-square-o::before{content:""}.si-check-square-o::before{content:""}.si-arrows::before{content:""}.si-step-backward::before{content:""}.si-fast-backward::before{content:""}.si-backward::before{content:""}.si-play::before{content:""}.si-pause::before{content:""}.si-stop::before{content:""}.si-forward::before{content:""}.si-fast-forward::before{content:""}.si-step-forward::before{content:""}.si-eject::before{content:""}.si-chevron-left::before{content:""}.si-chevron-right::before{content:""}.si-plus-circle::before{content:""}.si-minus-circle::before{content:""}.si-times-circle::before{content:""}.si-check-circle::before{content:""}.si-question-circle::before{content:""}.si-info-circle::before{content:""}.si-crosshairs::before{content:""}.si-times-circle-o::before{content:""}.si-check-circle-o::before{content:""}.si-ban::before{content:""}.si-arrow-left::before{content:""}.si-arrow-right::before{content:""}.si-arrow-up::before{content:""}.si-arrow-down::before{content:""}.si-mail-forward::before{content:""}.si-expand::before{content:""}.si-compress::before{content:""}.si-plus::before{content:""}.si-minus::before{content:""}.si-asterisk::before{content:""}.si-exclamation-circle::before{content:""}.si-gift::before{content:""}.si-leaf::before{content:""}.si-fire::before{content:""}.si-eye::before{content:""}.si-eye-slash::before{content:""}.si-exclamation-triangle::before{content:""}.si-plane::before{content:""}.si-calendar::before{content:""}.si-random::before{content:""}.si-comment::before{content:""}.si-magnet::before{content:""}.si-chevron-up::before{content:""}.si-chevron-down::before{content:""}.si-retweet::before{content:""}.si-shopping-cart::before{content:""}.si-folder::before{content:""}.si-folder-open::before{content:""}.si-arrows-v::before{content:""}.si-arrows-h::before{content:""}.si-bar-chart::before{content:""}.si-twitter-square::before{content:""}.si-facebook-square::before{content:""}.si-camera-retro::before{content:""}.si-key::before{content:""}.si-cogs::before{content:""}.si-comments::before{content:""}.si-thumbs-o-up::before{content:""}.si-thumbs-o-down::before{content:""}.si-star-half::before{content:""}.si-heart-o::before{content:""}.si-sign-out::before{content:""}.si-linkedin-square::before{content:""}.si-thumb-tack::before{content:""}.si-external-link::before{content:""}.si-sign-in::before{content:""}.si-trophy::before{content:""}.si-github-square::before{content:""}.si-upload::before{content:""}.si-lemon-o::before{content:""}.si-phone::before{content:""}.si-square-o::before{content:""}.si-bookmark-o::before{content:""}.si-phone-square::before{content:""}.si-twitter::before{content:""}.si-facebook::before{content:""}.si-github::before{content:""}.si-unlock::before{content:""}.si-credit-card::before{content:""}.si-feed::before{content:""}.si-hdd-o::before{content:""}.si-bullhorn::before{content:""}.si-bell-o::before{content:""}.si-certificate::before{content:""}.si-hand-o-right::before{content:""}.si-hand-o-left::before{content:""}.si-hand-o-up::before{content:""}.si-hand-o-down::before{content:""}.si-arrow-circle-left::before{content:""}.si-arrow-circle-right::before{content:""}.si-arrow-circle-up::before{content:""}.si-arrow-circle-down::before{content:""}.si-globe::before{content:""}.si-wrench::before{content:""}.si-tasks::before{content:""}.si-filter::before{content:""}.si-briefcase::before{content:""}.si-arrows-alt::before{content:""}.si-group::before{content:""}.si-chain::before{content:""}.si-cloud::before{content:""}.si-flask::before{content:""}.si-cut::before{content:""}.si-copy::before{content:""}.si-paperclip::before{content:""}.si-floppy-o::before{content:""}.si-square::before{content:""}.si-bars::before{content:""}.si-list-ul::before{content:""}.si-list-ol::before{content:""}.si-strikethrough::before{content:""}.si-underline::before{content:""}.si-table::before{content:""}.si-magic::before{content:""}.si-truck::before{content:""}.si-pinterest::before{content:""}.si-pinterest-square::before{content:""}.si-google-plus-square::before{content:""}.si-google-plus::before{content:""}.si-money::before{content:""}.si-caret-down::before{content:""}.si-caret-up::before{content:""}.si-caret-left::before{content:""}.si-caret-right::before{content:""}.si-columns::before{content:""}.si-sort::before{content:""}.si-sort-desc::before{content:""}.si-sort-asc::before{content:""}.si-envelope::before{content:""}.si-linkedin::before{content:""}.si-rotate-left::before{content:""}.si-gavel::before{content:""}.si-dashboard::before{content:""}.si-comment-o::before{content:""}.si-comments-o::before{content:""}.si-bolt::before{content:""}.si-sitemap::before{content:""}.si-umbrella::before{content:""}.si-clipboard::before{content:""}.si-lightbulb-o::before{content:""}.si-exchange::before{content:""}.si-cloud-download::before{content:""}.si-cloud-upload::before{content:""}.si-user-md::before{content:""}.si-stethoscope::before{content:""}.si-suitcase::before{content:""}.si-bell::before{content:""}.si-coffee::before{content:""}.si-cutlery::before{content:""}.si-file-text-o::before{content:""}.si-building-o::before{content:""}.si-hospital-o::before{content:""}.si-ambulance::before{content:""}.si-medkit::before{content:""}.si-fighter-jet::before{content:""}.si-beer::before{content:""}.si-h-square::before{content:""}.si-plus-square::before{content:""}.si-angle-double-left::before{content:""}.si-angle-double-right::before{content:""}.si-angle-double-up::before{content:""}.si-angle-double-down::before{content:""}.si-angle-left::before{content:""}.si-angle-right::before{content:""}.si-angle-up::before{content:""}.si-angle-down::before{content:""}.si-desktop::before{content:""}.si-laptop::before{content:""}.si-tablet::before{content:""}.si-mobile::before{content:""}.si-circle-o::before{content:""}.si-quote-left::before{content:""}.si-quote-right::before{content:""}.si-spinner::before{content:""}.si-circle::before{content:""}.si-mail-reply::before{content:""}.si-github-alt::before{content:""}.si-folder-o::before{content:""}.si-folder-open-o::before{content:""}.si-smile-o::before{content:""}.si-frown-o::before{content:""}.si-meh-o::before{content:""}.si-gamepad::before{content:""}.si-keyboard-o::before{content:""}.si-flag-o::before{content:""}.si-flag-checkered::before{content:""}.si-terminal::before{content:""}.si-code::before{content:""}.si-mail-reply-all::before{content:""}.si-star-half-empty::before{content:""}.si-location-arrow::before{content:""}.si-crop::before{content:""}.si-code-fork::before{content:""}.si-chain-broken::before{content:""}.si-question::before{content:""}.si-info::before{content:""}.si-exclamation::before{content:""}.si-superscript::before{content:""}.si-subscript::before{content:""}.si-eraser::before{content:""}.si-puzzle-piece::before{content:""}.si-microphone::before{content:""}.si-microphone-slash::before{content:""}.si-shield::before{content:""}.si-calendar-o::before{content:""}.si-fire-extinguisher::before{content:""}.si-rocket::before{content:""}.si-maxcdn::before{content:""}.si-chevron-circle-left::before{content:""}.si-chevron-circle-right::before{content:""}.si-chevron-circle-up::before{content:""}.si-chevron-circle-down::before{content:""}.si-html5::before{content:""}.si-css3::before{content:""}.si-anchor::before{content:""}.si-unlock-alt::before{content:""}.si-bullseye::before{content:""}.si-ellipsis-h::before{content:""}.si-ellipsis-v::before{content:""}.si-rss-square::before{content:""}.si-play-circle::before{content:""}.si-ticket::before{content:""}.si-minus-square::before{content:""}.si-minus-square-o::before{content:""}.si-level-up::before{content:""}.si-level-down::before{content:""}.si-check-square::before{content:""}.si-pencil-square::before{content:""}.si-external-link-square::before{content:""}.si-share-square::before{content:""}.si-compass::before{content:""}.si-caret-square-o-down::before{content:""}.si-caret-square-o-up::before{content:""}.si-caret-square-o-right::before{content:""}.si-eur::before{content:""}.si-gbp::before{content:""}.si-dollar::before{content:""}.si-inr::before{content:""}.si-cny::before{content:""}.si-rouble::before{content:""}.si-krw::before{content:""}.si-bitcoin::before{content:""}.si-file::before{content:""}.si-file-text::before{content:""}.si-sort-alpha-asc::before{content:""}.si-sort-alpha-desc::before{content:""}.si-sort-amount-asc::before{content:""}.si-sort-amount-desc::before{content:""}.si-sort-numeric-asc::before{content:""}.si-sort-numeric-desc::before{content:""}.si-thumbs-up::before{content:""}.si-thumbs-down::before{content:""}.si-youtube-square::before{content:""}.si-youtube::before{content:""}.si-xing::before{content:""}.si-xing-square::before{content:""}.si-youtube-play::before{content:""}.si-dropbox::before{content:""}.si-stack-overflow::before{content:""}.si-instagram::before{content:""}.si-flickr::before{content:""}.si-adn::before{content:""}.si-bitbucket::before{content:""}.si-bitbucket-square::before{content:""}.si-tumblr::before{content:""}.si-tumblr-square::before{content:""}.si-long-arrow-down::before{content:""}.si-long-arrow-up::before{content:""}.si-long-arrow-left::before{content:""}.si-long-arrow-right::before{content:""}.si-apple::before{content:""}.si-windows::before{content:""}.si-android::before{content:""}.si-linux::before{content:""}.si-dribbble::before{content:""}.si-skype::before{content:""}.si-foursquare::before{content:""}.si-trello::before{content:""}.si-female::before{content:""}.si-male::before{content:""}.si-gittip::before{content:""}.si-sun-o::before{content:""}.si-moon-o::before{content:""}.si-archive::before{content:""}.si-bug::before{content:""}.si-vk::before{content:""}.si-weibo::before{content:""}.si-renren::before{content:""}.si-pagelines::before{content:""}.si-stack-exchange::before{content:""}.si-arrow-circle-o-right::before{content:""}.si-arrow-circle-o-left::before{content:""}.si-caret-square-o-left::before{content:""}.si-dot-circle-o::before{content:""}.si-wheelchair::before{content:""}.si-vimeo-square::before{content:""}.si-try::before{content:""}.si-plus-square-o::before{content:""}.si-space-shuttle::before{content:""}.si-slack::before{content:""}.si-envelope-square::before{content:""}.si-wordpress::before{content:""}.si-openid::before{content:""}.si-bank::before{content:""}.si-graduation-cap::before{content:""}.si-yahoo::before{content:""}.si-google::before{content:""}.si-reddit::before{content:""}.si-reddit-square::before{content:""}.si-stumbleupon-circle::before{content:""}.si-stumbleupon::before{content:""}.si-delicious::before{content:""}.si-digg::before{content:""}.si-pied-piper::before{content:""}.si-pied-piper-alt::before{content:""}.si-drupal::before{content:""}.si-joomla::before{content:""}.si-language::before{content:""}.si-fax::before{content:""}.si-building::before{content:""}.si-child::before{content:""}.si-paw::before{content:""}.si-spoon::before{content:""}.si-cube::before{content:""}.si-cubes::before{content:""}.si-behance::before{content:""}.si-behance-square::before{content:""}.si-steam::before{content:""}.si-steam-square::before{content:""}.si-recycle::before{content:""}.si-automobile::before{content:""}.si-cab::before{content:""}.si-tree::before{content:""}.si-spotify::before{content:""}.si-deviantart::before{content:""}.si-soundcloud::before{content:""}.si-database::before{content:""}.si-file-pdf-o::before{content:""}.si-file-word-o::before{content:""}.si-file-excel-o::before{content:""}.si-file-powerpoint-o::before{content:""}.si-file-image-o::before{content:""}.si-file-archive-o::before{content:""}.si-file-audio-o::before{content:""}.si-file-movie-o::before{content:""}.si-file-code-o::before{content:""}.si-vine::before{content:""}.si-codepen::before{content:""}.si-jsfiddle::before{content:""}.si-life-bouy::before{content:""}.si-circle-o-notch::before{content:""}.si-ra::before{content:""}.si-empire::before{content:""}.si-git-square::before{content:""}.si-git::before{content:""}.si-hacker-news::before{content:""}.si-tencent-weibo::before{content:""}.si-qq::before{content:""}.si-wechat::before{content:""}.si-paper-plane::before{content:""}.si-paper-plane-o::before{content:""}.si-history::before{content:""}.si-circle-thin::before{content:""}.si-header::before{content:""}.si-paragraph::before{content:""}.si-sliders::before{content:""}.si-share-alt::before{content:""}.si-share-alt-square::before{content:""}.si-bomb::before{content:""}.si-futbol-o::before{content:""}.si-tty::before{content:""}.si-binoculars::before{content:""}.si-plug::before{content:""}.si-slideshare::before{content:""}.si-twitch::before{content:""}.si-yelp::before{content:""}.si-newspaper-o::before{content:""}.si-wifi::before{content:""}.si-calculator::before{content:""}.si-paypal::before{content:""}.si-google-wallet::before{content:""}.si-cc-visa::before{content:""}.si-cc-mastercard::before{content:""}.si-cc-discover::before{content:""}.si-cc-amex::before{content:""}.si-cc-paypal::before{content:""}.si-cc-stripe::before{content:""}.si-bell-slash::before{content:""}.si-bell-slash-o::before{content:""}.si-trash::before{content:""}.si-copyright::before{content:""}.si-at::before{content:""}.si-eyedropper::before{content:""}.si-paint-brush::before{content:""}.si-birthday-cake::before{content:""}.si-area-chart::before{content:""}.si-pie-chart::before{content:""}.si-line-chart::before{content:""}.si-lastfm::before{content:""}.si-lastfm-square::before{content:""}.si-toggle-off::before{content:""}.si-toggle-on::before{content:""}.si-bicycle::before{content:""}.si-bus::before{content:""}.si-ioxhost::before{content:""}.si-angellist::before{content:""}.si-cc::before{content:""}.si-ils::before{content:""}.si-meanpath::before{content:""}.si-buysellads::before{content:""}.si-connectdevelop::before{content:""}.si-dashcube::before{content:""}.si-forumbee::before{content:""}.si-leanpub::before{content:""}.si-sellsy::before{content:""}.si-shirtsinbulk::before{content:""}.si-simplybuilt::before{content:""}.si-skyatlas::before{content:""}.si-cart-plus::before{content:""}.si-cart-arrow-down::before{content:""}.si-diamond::before{content:""}.si-ship::before{content:""}.si-user-secret::before{content:""}.si-motorcycle::before{content:""}.si-street-view::before{content:""}.si-heartbeat::before{content:""}.si-venus::before{content:""}.si-mars::before{content:""}.si-mercury::before{content:""}.si-intersex::before{content:""}.si-transgender-alt::before{content:""}.si-venus-double::before{content:""}.si-mars-double::before{content:""}.si-venus-mars::before{content:""}.si-mars-stroke::before{content:""}.si-mars-stroke-v::before{content:""}.si-mars-stroke-h::before{content:""}.si-neuter::before{content:""}.si-genderless::before{content:""}.si-facebook-official::before{content:""}.si-pinterest-p::before{content:""}.si-whatsapp::before{content:""}.si-server::before{content:""}.si-user-plus::before{content:""}.si-user-times::before{content:""}.si-bed::before{content:""}.si-viacoin::before{content:""}.si-train::before{content:""}.si-subway::before{content:""}.si-medium::before{content:""}.si-y-combinator::before{content:""}.si-optin-monster::before{content:""}.si-opencart::before{content:""}.si-expeditedssl::before{content:""}.si-battery-4::before{content:""}.si-battery-3::before{content:""}.si-battery-2::before{content:""}.si-battery-1::before{content:""}.si-battery-0::before{content:""}.si-mouse-pointer::before{content:""}.si-i-cursor::before{content:""}.si-object-group::before{content:""}.si-object-ungroup::before{content:""}.si-sticky-note::before{content:""}.si-sticky-note-o::before{content:""}.si-cc-jcb::before{content:""}.si-cc-diners-club::before{content:""}.si-clone::before{content:""}.si-balance-scale::before{content:""}.si-hourglass-o::before{content:""}.si-hourglass-1::before{content:""}.si-hourglass-2::before{content:""}.si-hourglass-3::before{content:""}.si-hourglass::before{content:""}.si-hand-grab-o::before{content:""}.si-hand-paper-o::before{content:""}.si-hand-scissors-o::before{content:""}.si-hand-lizard-o::before{content:""}.si-hand-spock-o::before{content:""}.si-hand-pointer-o::before{content:""}.si-hand-peace-o::before{content:""}.si-trademark::before{content:""}.si-registered::before{content:""}.si-creative-commons::before{content:""}.si-gg::before{content:""}.si-gg-circle::before{content:""}.si-tripadvisor::before{content:""}.si-odnoklassniki::before{content:""}.si-odnoklassniki-square::before{content:""}.si-get-pocket::before{content:""}.si-wikipedia-w::before{content:""}.si-safari::before{content:""}.si-chrome::before{content:""}.si-firefox::before{content:""}.si-opera::before{content:""}.si-internet-explorer::before{content:""}.si-television::before{content:""}.si-contao::before{content:""}.si-500px::before{content:""}.si-amazon::before{content:""}.si-calendar-plus-o::before{content:""}.si-calendar-minus-o::before{content:""}.si-calendar-times-o::before{content:""}.si-calendar-check-o::before{content:""}.si-industry::before{content:""}.si-map-pin::before{content:""}.si-map-signs::before{content:""}.si-map-o::before{content:""}.si-map::before{content:""}.si-commenting::before{content:""}.si-commenting-o::before{content:""}.si-houzz::before{content:""}.si-vimeo::before{content:""}.si-black-tie::before{content:""}.si-fonticons::before{content:""}.si-lg{font-size:1.33333333em;line-height:0.75em;vertical-align:-15%}.si-2x{font-size:2em}.si-3x{font-size:3em}.si-4x{font-size:4em}.si-5x{font-size:5em}.si-fw{text-align:center;width:1.28571429em}.si-border{border-radius:.1em;padding:.2em .25em .15em;border:solid 0.08em}.si-pull-left{float:left;margin-right:.3em}.si-pull-right{float:right;margin-left:.3em}.si-rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.si-rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.si-rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.si-flip-horizontal{-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.si-flip-vertical{-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.si-inverse{-webkit-filter:invert(100%);filter:invert(100%)}.si-spin{-webkit-animation:si-animation-spin 2s infinite linear;-moz-animation:si-animation-spin 2s infinite linear;animation:si-animation-spin 2s infinite linear}.si-pulse{-webkit-animation:si-animation-spin 1s infinite steps(8);-moz-animation:si-animation-spin 1s infinite steps(8);animation:si-animation-spin 1s infinite steps(8)}.si-ul{padding-left:0;list-style-type:none;margin-left:2.14285714em}.si-ul>li{position:relative}.si-ul>li .si-li{text-align:center;position:absolute;left:-2.14285714em;width:2.14285714em;top:0.14285714em}.si-ul>li .si-li .si-lg{left:-1.85714286em}.si-stack{width:2em;height:2em;line-height:2em;vertical-align:middle;position:relative;display:inline-block}.si-stack .si-stack-1x,.si-stack .si-stack-2x{left:0;width:100%;text-align:center;position:absolute}.si-stack .si-stack-1x{line-height:inherit}.si-stack .si-stack-2x{font-size:2em}@-webkit-keyframes si-animation-spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-moz-keyframes si-animation-spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@keyframes si-animation-spin{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);-o-transform:rotate(359deg);transform:rotate(359deg)}}.plugin-menu-page-animation-spin{-webkit-animation-duration:0.75s;-moz-animation-duration:0.75s;animation-duration:0.75s;-webkit-animation-fill-mode:both;-moz-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;-moz-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-iteration-count:infinite;-moz-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:plugin-menu-page-animation-spin;-moz-animation-name:plugin-menu-page-animation-spin;animation-name:plugin-menu-page-animation-spin}@-webkit-keyframes plugin-menu-page-animation-spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@-moz-keyframes plugin-menu-page-animation-spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(360deg)}}@keyframes plugin-menu-page-animation-spin{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}html{overflow-y:scroll}#wpwrap{position:relative}#wpwrap::after{top:0;right:0;bottom:0;left:0;z-index:-1;position:absolute;content:'';opacity:0.25;background:url("../images/bg.png")}#wpwrap .updated,#wpwrap .error{margin:1.25em 1.25em 1.25em 0.25em}.plugin-menu-page{min-width:800px;margin:1.25em 1.25em 1.25em 0.25em}.plugin-menu-page,.plugin-menu-page p{font-size:14px}.plugin-menu-page a{color:#033A63}.plugin-menu-page a:hover,.plugin-menu-page a:active{color:#467629}.plugin-menu-page p:first-child,.plugin-menu-page pre:first-child{margin-top:0}.plugin-menu-page p:last-child,.plugin-menu-page pre:last-child{margin-bottom:0}.plugin-menu-page code{border-radius:3px;padding:0.1em 0.25em;background:rgba(178,178,178,0.25);font-family:'Menlo', 'Monaco', 'Consolas', 'Courier New', monospace}.plugin-menu-page pre.code{padding:0;background:none}.plugin-menu-page pre.code>code{font-size:90%;overflow-x:auto;max-width:100%;border-radius:4px;padding:1em;display:block;color:#eee;background:#222;box-shadow:0 0 5px 1px #000 inset}.plugin-menu-page img{border:0}.plugin-menu-page img.screenshot{float:right;border-radius:4px;padding:0.5em;margin:0 0 2em 2em;background:#fff;border:1px solid #afafaf;box-shadow:0 0 5px 0 rgba(0,0,0,0.2) inset}.plugin-menu-page hr{border:0;padding:0;height:1px;margin:1em 0;background:linear-gradient(to left, transparent, rgba(0,0,0,0.75), transparent)}.plugin-menu-page label{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.plugin-menu-page label.switch-primary{font-size:130%;margin:0;padding:0.5em;border-radius:4px;display:inline;color:#000;background:#f1e982;border:1px solid rgba(0,0,0,0.07);box-shadow:-1px -1px 0 0 rgba(0,0,0,0.25) inset,1px 1px 0 0 #fff inset}.plugin-menu-page select,.plugin-menu-page textarea,.plugin-menu-page select:focus,.plugin-menu-page textarea:focus,.plugin-menu-page input:not([type='radio']):not([type='checkbox']),.plugin-menu-page input:not([type='radio']):not([type='checkbox']):focus{width:100%;line-height:1.3em;margin:0;padding:0.25em 0.5em;border-radius:4px;box-sizing:border-box;color:#333;background:#e8e8e8;border:1px solid #848484;box-shadow:0 0 2px 0 rgba(132,132,132,0.5) inset}.plugin-menu-page select,.plugin-menu-page select:focus{box-shadow:0 1px 0 0 #fff inset,0 -2px 3px 0 rgba(132,132,132,0.25) inset}.plugin-menu-page select,.plugin-menu-page select:focus,.plugin-menu-page input:not([type='radio']):not([type='checkbox']),.plugin-menu-page input:not([type='radio']):not([type='checkbox']):focus{height:2em}.plugin-menu-page select:focus,.plugin-menu-page textarea:focus,.plugin-menu-page input:not([type='radio']):not([type='checkbox']):focus{color:#000;background:#e2e2e2}.plugin-menu-page input[disabled],.plugin-menu-page select[disabled],.plugin-menu-page textarea[disabled]{opacity:0.5}.plugin-menu-page input::-webkit-input-placeholder,.plugin-menu-page textarea::-webkit-input-placeholder{font-style:italic;color:rgba(0,0,0,0.2)}.plugin-menu-page input::-moz-placeholder,.plugin-menu-page textarea::-moz-placeholder{font-style:italic;color:rgba(0,0,0,0.2)}.plugin-menu-page input:-moz-placeholder,.plugin-menu-page textarea:-moz-placeholder{font-style:italic;color:rgba(0,0,0,0.2)}.plugin-menu-page input:-ms-input-placeholder,.plugin-menu-page textarea:-ms-input-placeholder{font-style:italic;color:rgba(0,0,0,0.2)}.plugin-menu-page table{margin:1em 0}.plugin-menu-page button{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:150%;font-weight:bold;line-height:1em;outline:none;cursor:pointer;border-radius:4px;margin:0;padding:0.25em 0.5em;box-sizing:border-box;color:#fff;background:#033A63;border:1px solid rgba(0,0,0,0.5);box-shadow:0 1px 1px 0 rgba(0,0,0,0.25),-1px -1px 0 0 rgba(0,0,0,0.2) inset,1px 1px 0 0 rgba(255,255,255,0.1) inset}.plugin-menu-page button:hover{background:#467629}.plugin-menu-page button[type='submit']{background:#467629}.plugin-menu-page button[type='submit']:hover{background:#033A63}.plugin-menu-page button:active{-webkit-transform:scale(0.98, 0.98);-moz-transform:scale(0.98, 0.98);-ms-transform:scale(0.98, 0.98);-o-transform:scale(0.98, 0.98);transform:scale(0.98, 0.98)}.plugin-menu-page .info,.plugin-menu-page .notice,.plugin-menu-page .warning,.plugin-menu-page .error{border-radius:4px;padding:0.5em;margin:1em 0}.plugin-menu-page .info{background:#cadfed;border:1px solid #216095}.plugin-menu-page .notice{background:#fffde8;border:1px solid #e6db55}.plugin-menu-page .warning{background:#ffefd3;border:1px solid #e6db55}.plugin-menu-page .error{background:pink;border:1px solid #711e1e}.plugin-menu-page .monospace{font-family:'Menlo', 'Monaco', 'Consolas', 'Courier New', monospace}.plugin-menu-page textarea.monospace{white-space:pre}.plugin-menu-page .clearfix::before,.plugin-menu-page .clearfix::after{display:table;content:' '}.plugin-menu-page .clearfix::after{clear:both}.plugin-menu-page-heading .plugin-menu-page-restore-defaults,.plugin-menu-page-heading .plugin-menu-page-panel-togglers{float:right;margin:0 1em 0 0}.plugin-menu-page-heading .plugin-menu-page-panel-togglers button{background:#033A63 !important}.plugin-menu-page-heading .plugin-menu-page-upsells{float:right;clear:right;text-align:right;max-width:350px;margin:1em 0 0}.plugin-menu-page-heading .plugin-menu-page-upsells a{text-decoration:none;line-height:1.5em;margin:0 0.5em;display:inline-block}.plugin-menu-page-heading .plugin-menu-page-version{float:right;clear:right;min-width:350px;margin:0.5em 0 0 0;text-align:right}.plugin-menu-page-body{position:relative}.plugin-menu-page-section-heading{margin:1em 0}.plugin-menu-page-section-heading>small{font-size:65%;font-style:italic;margin:0;display:block;opacity:0.5}.plugin-menu-page-panel{margin:1em 0}.plugin-menu-page-panel:first-child{margin-top:0}.plugin-menu-page-panel .plugin-menu-page-panel-heading{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none;padding:10px;font-size:150%;line-height:1.125em;font-weight:bold;border-radius:4px;display:block;cursor:pointer;background:#033A63;color:#ddd !important;box-shadow:0 2px 2px 0 rgba(0,0,0,0.25)}.plugin-menu-page-panel .plugin-menu-page-panel-heading::after{font:normal normal normal 14px/1 sharkicons;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;font-smoothing:antialiased;display:inline-block;font-size:inherit;text-decoration:inherit;text-transform:none;content:""}.plugin-menu-page-panel .plugin-menu-page-panel-heading:hover,.plugin-menu-page-panel .plugin-menu-page-panel-heading.open{color:#fff !important}.plugin-menu-page-panel .plugin-menu-page-panel-heading::after{font-size:80%;float:right;margin:0 0 0 5px}.plugin-menu-page-panel .plugin-menu-page-panel-heading.open::after{content:""}.plugin-menu-page-panel-heading.pro-preview-feature{background:#216095}.pro-preview-feature::after{font-variant:small-caps !important;font-family:sans-serif !important;content:'pro version only' !important;margin-left:15px;background:#216095;color:#FFFFFF;padding:0 5px 2px 5px;font-weight:normal}.plugin-menu-page-panel-heading.pro-preview-additional-features{background:#216095}.pro-preview-additional-features::after{font-variant:small-caps !important;font-family:sans-serif !important;content:'additional pro version only features' !important;margin-left:15px;background:#216095;color:#FFFFFF;padding:0 5px 2px 5px;font-weight:normal}.plugin-menu-page-panel .plugin-menu-page-panel-heading>.si{text-align:center;width:1.28571429em;margin-right:.25em}.plugin-menu-page-panel .plugin-menu-page-panel-body{width:99%;margin:0 auto;display:none;padding:1.2em;border-bottom-left-radius:4px;border-bottom-right-radius:4px;box-sizing:border-box;color:#222;border:1px solid #848484;background:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,0.25),0 3px 1px -1px rgba(0,0,0,0.25) inset}.plugin-menu-page-panel .plugin-menu-page-panel-body.open{display:block}.plugin-menu-page-panel .plugin-menu-page-panel-body p{font-size:90%;color:#666}.plugin-menu-page-panel .plugin-menu-page-panel-body p.notice,.plugin-menu-page-panel .plugin-menu-page-panel-body p.info,.plugin-menu-page-panel .plugin-menu-page-panel-body p.warning,.plugin-menu-page-panel .plugin-menu-page-panel-body p.error{color:#000}.plugin-menu-page-panel .plugin-menu-page-panel-body h3{margin:0 0 0.5em}.plugin-menu-page-panel .plugin-menu-page-panel-body h3:first-child{margin-top:0}.plugin-menu-page-panel .plugin-menu-page-panel-body h3+p{margin-top:0}.plugin-menu-page-panel .plugin-menu-page-panel-body a.dotted{text-decoration:none;border-bottom:1px dotted}.plugin-menu-page-panel .plugin-menu-page-panel-body.pro-preview,.plugin-menu-page-panel .plugin-menu-page-panel-body .pro-preview{opacity:0.5}.plugin-menu-page-save{margin-top:2em}.plugin-menu-page-save button{line-height:1.3em;width:100%}
2
+ /*# sourceMappingURL=menu-pages.min.css.map */
src/client-s/images/auto-clear-ss.png ADDED
Binary file
src/client-s/images/bg.png ADDED
Binary file
src/client-s/images/clear-cache-ops0-ss.png ADDED
Binary file
src/client-s/images/clear-cache-ops1-ss.png ADDED
Binary file
src/client-s/images/clear-cache-ops2-ss.png ADDED
Binary file
src/client-s/images/clear-response.png ADDED
Binary file
src/client-s/images/clear.png ADDED
Binary file
src/client-s/images/cloudfront-logo.png ADDED
Binary file
src/client-s/images/gzip.png ADDED
Binary file
src/client-s/images/inline-icon-logo.svg ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <svg style="width:1em; height:1em; vertical-align:middle;" viewBox="0 0 540 540" version="1.1" xmlns="http://www.w3.org/2000/svg">
2
+ <g fill="currentColor" fill-rule="evenodd">
3
+ <path d="M401.504121,406.75213 L401.504121,404.689955 L411.032258,404.689955 L411.032258,391.132577 L374.933786,391.132577 L374.933786,404.689955 L384.461924,404.689955 L384.461924,409.430194 C383.410756,409.256979 382.360535,409.426875 381.479937,410.24554 C381.229994,410.478678 381.482076,410.939689 381.80542,410.898949 C382.284607,410.838 382.82558,410.745049 383.297493,410.899546 C383.619957,411.00503 384.332095,411.593476 383.926393,411.999866 C383.518342,412.409487 382.796999,412.138778 382.322982,412.008999 C381.748301,411.85159 381.174755,411.693791 380.601039,411.536167 C378.304976,410.906886 376.008697,410.276642 373.713769,409.646971 C373.252762,409.521133 373.097855,410.176037 373.462724,410.39127 C375.29254,411.476696 377.128351,412.568359 379.009208,413.571104 L372.222528,413.711078 L372.222528,410.422789 L358.511306,410.422789 L358.511306,404.689955 L372.454921,404.689955 L372.454921,391.132577 L342.01136,391.132577 L342.01136,414.334178 L336.588843,414.446016 L336.588843,391.132577 L322.955085,391.132577 L309.553721,410.965084 L296.074893,391.132577 L282.441135,391.132577 L282.441135,415.562803 L278.148664,415.651334 C277.996078,412.432374 277.296634,409.372547 276.050311,406.471782 C274.630121,403.166347 272.693527,400.287047 270.240472,397.833795 C267.787416,395.380543 264.908346,393.443794 261.603176,392.02349 C258.298006,390.603186 254.786316,389.893045 251.068,389.893045 C247.349684,389.893045 243.837994,390.603186 240.532824,392.02349 C237.227654,393.443794 234.348584,395.380543 231.895528,397.833795 C229.442473,400.287047 227.505879,403.166347 226.085688,406.471782 C224.69593,409.706388 223.986171,413.138774 223.956392,416.76904 L193.131557,417.404797 C193.126873,417.273179 193.12453,417.140847 193.12453,417.007802 C193.12453,415.510027 193.408564,414.102656 193.97664,412.785647 C194.544716,411.468638 195.332264,410.3195 196.339308,409.338199 C197.346352,408.356898 198.521219,407.582199 199.863944,407.014077 C201.20667,406.445955 202.678481,406.161899 204.279423,406.161899 C205.828721,406.161899 207.210158,406.355574 208.423775,406.742929 C209.637392,407.130285 210.683153,407.530546 211.561089,407.943726 C212.542311,408.511847 213.446055,409.105784 214.272347,409.725553 L222.406123,395.393467 C221.011755,394.412166 219.359195,393.50835 217.448393,392.681991 C215.795808,391.958928 213.820482,391.313345 211.522356,390.745223 C209.224231,390.177101 206.603373,389.893045 203.659707,389.893045 C199.94139,389.893045 196.4297,390.525716 193.12453,391.791078 C189.81936,393.05644 186.92738,394.876984 184.448503,397.252765 C181.969625,399.628546 180.00721,402.482022 178.561198,405.813281 C177.115186,409.144539 176.392191,412.876008 176.392191,417.007802 C176.392191,417.25649 176.394848,417.503849 176.400162,417.74988 L174.245658,417.794315 C174.245658,417.794315 175.087999,417.786868 176.400807,417.779148 C176.484553,421.507211 177.178857,424.929925 178.483734,428.047381 C179.878102,431.378639 181.814696,434.232116 184.293574,436.607897 C186.772451,438.983678 189.716074,440.830045 193.12453,442.147054 C196.532987,443.464063 200.251247,444.122558 204.279423,444.122558 C207.274733,444.122558 209.934322,443.812678 212.25827,443.192909 C214.582217,442.57314 216.570454,441.901734 218.223038,441.17867 C220.13384,440.300664 221.7864,439.293555 223.180769,438.157312 L215.046993,423.825226 C214.272344,424.599937 213.3686,425.271344 212.335734,425.839465 C211.457798,426.355939 210.373306,426.820759 209.082224,427.233938 C207.791142,427.647118 206.31933,427.853704 204.666745,427.853704 C203.01416,427.853704 201.490707,427.569648 200.096338,427.001526 C198.70197,426.433404 197.488371,425.658705 196.455505,424.677404 C195.422639,423.696103 194.60927,422.546965 194.015372,421.229956 C193.537316,420.169835 193.251668,419.051164 193.158423,417.873922 C202.575607,418.006749 212.981005,418.152715 223.984664,418.306519 C224.131578,421.546638 224.831915,424.625714 226.085688,427.543821 C227.505879,430.849256 229.442473,433.728556 231.895528,436.181808 C234.348584,438.63506 237.227654,440.571809 240.532824,441.992113 C243.837994,443.412417 247.349684,444.122558 251.068,444.122558 C254.786316,444.122558 258.298006,443.412417 261.603176,441.992113 C264.908346,440.571809 267.787416,438.63506 270.240472,436.181808 C272.693527,433.728556 274.630121,430.849256 276.050311,427.543821 C277.206549,424.852725 277.892108,422.024731 278.107001,419.059781 C279.55205,419.079825 280.996993,419.099864 282.441136,419.119887 L282.441135,442.805556 L298.941081,442.805556 L298.941081,424.21258 L309.553721,439.706726 L320.088898,424.21258 L320.088898,442.805556 L336.588843,442.805556 L336.588843,419.868975 C338.416104,419.894204 340.224242,419.919164 342.01136,419.94383 L342.01136,442.805556 L372.997173,442.805556 L372.997173,429.248177 L358.511306,429.248177 L358.511306,423.282931 L372.222528,423.282931 L372.222528,420.360422 C376.617944,420.420977 380.715146,420.477399 384.461924,420.528979 L384.461924,442.805556 L401.504121,442.805556 L401.504121,427.1808 C402.358127,427.42905 403.196311,427.730729 404.061295,427.942716 C405.088324,428.194453 406.151251,428.317554 407.209617,428.256853 C411.331163,428.02075 415.241656,425.541494 416.842165,421.670079 C417.87822,419.161429 418.072977,416.269262 417.185038,413.683801 C416.306082,411.127775 414.466184,408.959959 412.109047,407.638944 C409.622399,406.244054 406.94721,405.994134 404.15875,406.359864 C403.290131,406.473521 402.399881,406.628477 401.504121,406.75213 Z M240.3256,418.534575 C240.45433,419.471568 240.704154,420.370024 241.075075,421.229956 C241.643151,422.546965 242.417789,423.696103 243.399011,424.677404 C244.380234,425.658705 245.529279,426.433404 246.846183,427.001526 C248.163087,427.569648 249.570345,427.853704 251.068,427.853704 C252.565655,427.853704 253.972913,427.569648 255.289817,427.001526 C256.606721,426.433404 257.755766,425.658705 258.736989,424.677404 C259.718211,423.696103 260.492848,422.546965 261.060925,421.229956 C261.392306,420.461692 261.627033,419.662678 261.765105,418.832907 C254.524928,418.7323 247.348751,418.632452 240.3256,418.534575 Z M261.867818,415.987124 C261.767104,414.866426 261.49814,413.799273 261.060925,412.785647 C260.492848,411.468638 259.718211,410.3195 258.736989,409.338199 C257.755766,408.356898 256.606721,407.582199 255.289817,407.014077 C253.972913,406.445955 252.565655,406.161899 251.068,406.161899 C249.570345,406.161899 248.163087,406.445955 246.846183,407.014077 C245.529279,407.582199 244.380234,408.356898 243.399011,409.338199 C242.417789,410.3195 241.643151,411.468638 241.075075,412.785647 C240.580207,413.932933 240.300889,415.148792 240.237117,416.433252 L261.867818,415.987124 Z M67.8836769,125.325225 C63.3785388,133.310189 59.2439837,141.637966 55.48,150.308571 C41.8265984,181.760157 35,216.990281 35,256 C35,294.034476 41.5827913,328.776986 54.7485714,360.228571 C67.9143515,391.680157 86.199883,418.62084 109.605714,441.051429 C133.011546,463.482017 160.805553,480.914224 192.988571,493.348571 C225.171589,505.782919 260.27981,512 298.314286,512 C326.596332,512 351.708462,509.074315 373.651429,503.222857 C395.594395,497.371399 414.367541,491.032415 429.971429,484.205714 C448.013424,475.916149 463.617077,466.407673 476.782857,455.68 L411.277787,340.266305 C399.76048,354.619051 385.224044,366.525733 368.49607,374.342377 C341.396806,386.992291 310.001148,389.009386 281.369456,380.400615 C251.15435,371.328779 228.602806,351.955658 210.080563,326.948861 C191.761538,302.232237 175.078697,272.583628 145.520455,259.946196 C143.738128,259.173634 142.937632,257.696826 142.839541,256.181515 C142.61614,254.131916 143.632268,251.987816 145.738744,251.502369 C146.387955,251.295538 147.132567,251.293296 147.943777,251.522394 C155.068176,252.648964 163.778082,254.360162 170.440533,250.852492 C178.300319,246.717233 180.642397,238.858771 174.118159,231.943375 C161.776109,218.859391 146.404209,207.892853 132.695534,196.261293 C128.334219,192.562272 132.722903,186.400053 137.685163,189.046314 C143.887039,192.369236 152.665435,197.279821 159.672611,193.266426 C168.454186,188.232482 161.789047,179.03322 155.865745,174.917083 C144.388451,166.94282 130.658901,164.298373 117.31491,161.115842 C97.2360992,156.321381 73.4416749,146.366565 67.8836763,125.325222 Z M74.4957311,114.271151 C76.3596276,111.329113 78.2781626,108.437616 80.2513365,105.596661 C82.568357,106.969414 84.8859371,108.343172 87.2044555,109.718942 C91.8808205,112.494523 99.579881,116.155557 99.4561432,122.552033 C99.3400684,128.904976 89.182909,127.759423 85.8647941,126.003436 C81.0606202,123.462488 77.6639803,118.522842 74.4957311,114.271151 Z M100.368253,80.4241292 C103.813967,76.6660244 107.380739,73.0198871 111.068571,69.4857143 C134.474403,47.0551259 161.780796,29.8667264 192.988571,17.92 C224.196347,5.9732736 257.35411,0 292.462857,0 C320.257282,0 345.003701,2.68187794 366.702857,8.04571429 C388.402013,13.4095506 407.053255,19.5047278 422.657143,26.3314286 C440.699138,34.1333723 456.302791,42.6666203 469.468571,51.9314286 L399.830224,174.627564 C394.079708,169.248863 387.763154,164.477275 381.108518,160.2818 C369.473485,152.945841 356.849002,147.368594 345.441804,139.636027 C334.161308,131.969582 323.940229,121.004024 321.614595,107.133291 C319.710146,95.8219706 325.366186,83.3799925 336.156049,79.0092465 C339.705983,77.5616702 339.316502,72.1091169 335.794,70.9199494 C322.541882,66.4238913 308.464321,70.6401283 298.475933,80.1048174 C293.597137,84.713423 290.118807,90.754823 288.570423,97.2628604 C286.416756,106.293495 289.533292,115.144237 289.664276,124.185741 C289.724146,129.171303 288.242585,134.611919 283.665755,137.22963 C278.929243,139.921802 272.714572,136.8113 268.871036,134.045619 C260.879608,128.309872 256.243249,118.484243 248.823261,111.986542 C235.754178,100.541567 218.968889,94.1567124 201.624738,94.2060718 C200.830119,94.1610387 200.018431,94.1679198 199.17137,94.2445736 C194.248531,94.6753936 194.541707,103.033894 199.50029,102.916732 C200.323426,102.905845 201.134308,102.928643 201.951608,102.933796 C204.493778,103.322734 206.097599,105.51715 207.215899,107.855386 C209.170078,111.957618 207.198467,115.901865 204.087576,118.676461 C196.187849,125.690053 183.932537,121.170054 176.00238,116.953468 C158.396294,107.59483 141.588599,97.5468897 123.222636,89.6054878 C115.682951,86.3397828 108.058388,83.2939995 100.368253,80.4241292 Z M305.05878,481.662148 C303.441605,482.432293 301.131356,482.971394 298.744098,482.971394 C291.312797,482.971394 286.923323,478.042468 286.923323,470.109977 C286.923323,461.753907 291.659334,456.863487 299.013628,456.863487 C301.439389,456.863487 303.441605,457.441096 304.866259,458.134226 L305.559334,456.093343 C304.866259,455.746778 302.902547,454.861111 299.013628,454.861111 C290.04216,454.861111 284.459057,461.060776 284.459057,470.109977 C284.459057,480.391409 290.927755,484.97377 298.051024,484.97377 C301.516398,484.97377 304.250193,484.28064 305.674846,483.58751 L305.05878,481.662148 Z M320.960996,455.323198 L318.49673,455.323198 L308.293129,484.550191 L310.718891,484.550191 L314.14576,474.538309 L325.196453,474.538309 L328.661827,484.550191 L331.164597,484.550191 L320.960996,455.323198 Z M314.761827,472.612947 L318.111688,463.024645 C318.727755,461.176298 319.228309,459.48198 319.61335,457.749154 L319.728863,457.749154 C320.152408,459.443472 320.614458,461.137791 321.269029,463.178674 L324.580386,472.612947 L314.761827,472.612947 Z M354.382602,481.662148 C352.765427,482.432293 350.455178,482.971394 348.067921,482.971394 C340.636619,482.971394 336.247145,478.042468 336.247145,470.109977 C336.247145,461.753907 340.983156,456.863487 348.33745,456.863487 C350.763211,456.863487 352.765427,457.441096 354.190081,458.134226 L354.883156,456.093343 C354.190081,455.746778 352.226369,454.861111 348.33745,454.861111 C339.365982,454.861111 333.782879,461.060776 333.782879,470.109977 C333.782879,480.391409 340.251577,484.97377 347.374846,484.97377 C350.84022,484.97377 353.574015,484.28064 354.998668,483.58751 L354.382602,481.662148 Z M379.833848,455.323198 L377.446591,455.323198 L377.446591,468.069093 L362.160441,468.069093 L362.160441,455.323198 L359.773184,455.323198 L359.773184,484.550191 L362.160441,484.550191 L362.160441,470.109977 L377.446591,470.109977 L377.446591,484.550191 L379.833848,484.550191 L379.833848,455.323198 Z M401.819721,482.547815 L389.113349,482.547815 L389.113349,470.186991 L400.472075,470.186991 L400.472075,468.184615 L389.113349,468.184615 L389.113349,457.325574 L401.16515,457.325574 L401.16515,455.323198 L386.726092,455.323198 L386.726092,484.550191 L401.819721,484.550191 L401.819721,482.547815 Z"></path>
4
+ </g>
5
+ </svg>
src/client-s/images/inline-icon.svg ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <svg style="width:1em; height:1em; vertical-align:middle;" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg">
2
+ <g fill="currentColor" fill-rule="evenodd" transform="translate(42.000000, 8.000000)">
3
+ <path d="M20.0228571,144.712941 C33.1429227,114.481261 50.948459,88.5855196 73.44,67.0249412 C95.931541,45.4643628 122.171279,28.9426457 152.16,17.4592941 C182.148721,5.97594258 214.01126,0.234352941 247.748571,0.234352941 C274.457276,0.234352941 298.237039,2.81220952 319.088571,7.968 C339.940104,13.1237905 357.862782,18.9825554 372.857143,25.5444706 C390.194372,33.0438022 405.188508,41.2460731 417.84,50.1515294 L344.04,180.217412 C336.54282,174.592913 328.342902,169.202849 319.44,164.047059 C311.474246,160.297393 301.985769,156.664959 290.974286,153.149647 C279.962802,149.634335 267.428642,147.876706 253.371429,147.876706 C238.845642,147.876706 225.491489,150.454562 213.308571,155.610353 C201.125653,160.766143 190.46576,167.796661 181.328571,176.702118 C172.191383,185.607574 165.04574,196.036176 159.891429,207.988235 C154.737117,219.940295 152.16,232.712403 152.16,246.304941 C152.16,259.89748 154.854259,272.669587 160.242857,284.621647 C165.631456,296.573707 173.011382,307.002308 182.382857,315.907765 C191.754333,324.813221 202.765651,331.843739 215.417143,336.999529 C228.068635,342.15532 241.891354,344.733176 256.885714,344.733176 C271.880075,344.733176 285.234227,342.858372 296.948571,339.108706 C308.662916,335.35904 318.502817,331.140729 326.468571,326.453647 C335.840047,321.297857 344.039965,315.204741 351.068571,308.174118 L424.868571,438.24 C412.21708,448.551581 397.222944,457.691254 379.885714,465.659294 C364.891354,472.221209 346.851534,478.314325 325.765714,483.938824 C304.679895,489.563322 280.548707,492.375529 253.371429,492.375529 C216.822674,492.375529 183.085869,486.399589 152.16,474.447529 C121.234131,462.49547 94.5258267,445.739402 72.0342857,424.178824 C49.5427447,402.618245 31.9714918,376.722504 19.32,346.490824 C6.66850817,316.259143 0.342857143,282.864183 0.342857143,246.304941 C0.342857143,208.808283 6.90279154,174.944622 20.0228571,144.712941 Z M359.028468,89.6757544 C354.193819,114.834352 318.727708,129.062286 289.622289,123.360582 C231.931191,112.058992 233.574912,78.3273265 119,114.402803 C189.843343,36.3809073 246.335162,20.870029 307.139305,32.2495407 C336.291795,37.7064142 363.868372,64.5142171 359.028468,89.6757544 Z"></path>
4
+ </g>
5
+ </svg>
src/client-s/images/keycdn-logo.png ADDED
Binary file
src/client-s/images/maxcdn-logo.png ADDED
Binary file
src/client-s/images/opcache.png ADDED
Binary file
src/client-s/images/options-lite.png ADDED
Binary file
src/client-s/images/salt.png ADDED
Binary file
src/client-s/images/source-code-ss.png ADDED
Binary file
src/client-s/images/spinner.png ADDED
Binary file
src/client-s/images/tach.png ADDED
Binary file
src/client-s/images/wipe-response.png ADDED
Binary file
src/client-s/images/wipe.png ADDED
Binary file
src/client-s/js/menu-pages.js ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+ 'use strict'; // Standards.
3
+
4
+ var plugin = {
5
+ namespace: 'comet_cache'
6
+ },
7
+ $window = $(window),
8
+ $document = $(document);
9
+
10
+ plugin.onReady = function () {
11
+
12
+ plugin.$menuPage = $('#plugin-menu-page');
13
+ plugin.vars = window[plugin.namespace + '_menu_page_vars'];
14
+
15
+ $('.plugin-menu-page-panel-heading', plugin.$menuPage).on('click', plugin.togglePanel);
16
+ $('.plugin-menu-page-panels-open', plugin.$menuPage).on('click', plugin.toggleAllPanelsOpen);
17
+ $('.plugin-menu-page-panels-close', plugin.$menuPage).on('click', plugin.toggleAllPanelsClose);
18
+
19
+ $('[data-action]', plugin.$menuPage).on('click', plugin.doDataAction);
20
+ $('[data-toggle-target]', plugin.$menuPage).on('click', plugin.doDataToggleTarget);
21
+
22
+ $('select[name$="_enable\\]"]', plugin.$menuPage).not('.-no-if-enabled').on('change', plugin.enableDisable).trigger('change');
23
+
24
+
25
+ };
26
+
27
+ plugin.toggleAllPanelsOpen = function (event) {
28
+ plugin.preventDefault(event);
29
+
30
+ $('.plugin-menu-page-panel-heading', plugin.$menuPage).addClass('open')
31
+ .next('.plugin-menu-page-panel-body').addClass('open');
32
+ };
33
+
34
+ plugin.toggleAllPanelsClose = function (event) {
35
+ plugin.preventDefault(event);
36
+
37
+ $('.plugin-menu-page-panel-heading', plugin.$menuPage).removeClass('open')
38
+ .next('.plugin-menu-page-panel-body').removeClass('open');
39
+ };
40
+
41
+ plugin.togglePanel = function (event) {
42
+ plugin.preventDefault(event);
43
+
44
+ $(this).toggleClass('open') // Heading and body.
45
+ .next('.plugin-menu-page-panel-body').toggleClass('open');
46
+ };
47
+
48
+ plugin.doDataAction = function (event) {
49
+ plugin.preventDefault(event);
50
+
51
+ var $this = $(this),
52
+ data = $this.data();
53
+ if (typeof data.confirmation !== 'string' || confirm(data.confirmation))
54
+ location.href = data.action;
55
+ };
56
+
57
+ plugin.enableDisable = function (event) {
58
+ var $this = $(this),
59
+ thisValue = $this.val(),
60
+ thisName = $this.attr('name'),
61
+ enabled = Number(thisValue) >= 1,
62
+
63
+ $thisPanelBody = $this.closest('.plugin-menu-page-panel-body'),
64
+
65
+ targetIfEnabled = $this.data('target'), // Optional specifier.
66
+ $targetIfEnableds = targetIfEnabled ? $(targetIfEnabled, $thisPanelBody)
67
+ .filter('.plugin-menu-page-panel-if-enabled') : null,
68
+
69
+ $parentIfEnabled = $this.closest('.plugin-menu-page-panel-if-enabled'),
70
+ $childIfEnableds = $parentIfEnabled.find('> .plugin-menu-page-panel-if-enabled'),
71
+
72
+ $panelIfEnableds = $thisPanelBody.find('> .plugin-menu-page-panel-if-enabled');
73
+
74
+ if (enabled) {
75
+ if (targetIfEnabled) {
76
+ $targetIfEnableds.css('opacity', 1).find(':input').removeAttr('readonly');
77
+ } else if ($parentIfEnabled.length) {
78
+ $childIfEnableds.css('opacity', 1).find(':input').removeAttr('readonly');
79
+ } else {
80
+ $panelIfEnableds.css('opacity', 1).find(':input').removeAttr('readonly');
81
+ }
82
+ } else {
83
+ if (targetIfEnabled) {
84
+ $targetIfEnableds.css('opacity', 0.4).find(':input').attr('readonly', 'readonly');
85
+ } else if ($parentIfEnabled.length) {
86
+ $childIfEnableds.css('opacity', 0.4).find(':input').attr('readonly', 'readonly');
87
+ } else {
88
+ $panelIfEnableds.css('opacity', 0.4).find(':input').attr('readonly', 'readonly');
89
+ }
90
+ }
91
+ };
92
+
93
+ plugin.doDataToggleTarget = function (event) {
94
+ plugin.preventDefault(event);
95
+
96
+ var $this = $(this),
97
+ $target = $($this.data('toggleTarget'));
98
+
99
+ if ($target.is(':visible')) {
100
+ $target.hide();
101
+ $this.find('.si')
102
+ .removeClass('si-eye-slash')
103
+ .addClass('si-eye');
104
+ } else {
105
+ $target.show();
106
+ $this.find('.si')
107
+ .removeClass('si-eye')
108
+ .addClass('si-eye-slash');
109
+ }
110
+ };
111
+
112
+ plugin.handleCacheClearAdminBarOpsChange = function (event) {
113
+ var $select = $(this),
114
+ val = $select.val(),
115
+ $ss = $('.-clear-cache-ops-ss', plugin.$menuPage);
116
+ $ss.attr('src', $ss.attr('src').replace(/ops[0-9]\-ss\.png$/, 'ops' + val + '-ss.png'));
117
+ };
118
+
119
+ plugin.handleCdnHostsChange = function (event) {
120
+ var $cdnHosts = $(this),
121
+ $cdnHost = $('input[name$="\[cdn_host\]"]', plugin.$menuPage);
122
+
123
+ if ($.trim($cdnHosts.val())) {
124
+ if ($cdnHost.val()) {
125
+ $cdnHost.data('hiddenValue', $cdnHost.val());
126
+ }
127
+ $cdnHost.attr('disabled', 'disabled').val('');
128
+ } else {
129
+ if (!$cdnHost.val()) {
130
+ $cdnHost.val($cdnHost.data('hiddenValue'));
131
+ }
132
+ $cdnHost.removeAttr('disabled');
133
+ $cdnHosts.val('');
134
+ }
135
+ };
136
+
137
+
138
+
139
+ plugin.bytesToSizeLabel = function (bytes, decimals) {
140
+ if (typeof bytes !== 'number' || bytes <= 1) {
141
+ return bytes === 1 ? '1 byte' : '0 bytes';
142
+ } // See: <http://jas.xyz/1gOCXob>
143
+ if (typeof decimals !== 'number' || decimals <= 0) {
144
+ decimals = 0; // Default; integer.
145
+ }
146
+ var base = 1024, // 1 Kilobyte base (binary).
147
+ baseLog = Math.floor(Math.log(bytes) / Math.log(base)),
148
+ sizes = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
149
+ sizeInBaseLog = (bytes / Math.pow(base, baseLog));
150
+
151
+ return sizeInBaseLog.toFixed(decimals) + ' ' + sizes[baseLog];
152
+ };
153
+
154
+ plugin.numberFormat = function (number, decimals) {
155
+ if (typeof number !== 'number') {
156
+ return String(number);
157
+ } // See: <http://jas.xyz/1JlFD9P>
158
+ if (typeof decimals !== 'number' || decimals <= 0) {
159
+ decimals = 0; // Default; integer.
160
+ }
161
+ return number.toFixed(decimals).replace(/./g, function (m, o, s) {
162
+ return o && m !== '.' && ((s.length - o) % 3 === 0) ? ',' + m : m;
163
+ });
164
+ };
165
+
166
+ plugin.escHtml = function (string) {
167
+ var entityMap = {
168
+ '&': '&amp;',
169
+ '<': '&lt;',
170
+ '>': '&gt;',
171
+ '"': '&quot;',
172
+ "'": '&#39;'
173
+ };
174
+ return String(string).replace(/[&<>"']/g, function (specialChar) {
175
+ return entityMap[specialChar];
176
+ });
177
+ };
178
+
179
+ plugin.preventDefault = function (event, stop) {
180
+ if (!event) {
181
+ return; // Not possible.
182
+ }
183
+ event.preventDefault(); // Always.
184
+
185
+ if (stop) {
186
+ event.stopImmediatePropagation();
187
+ }
188
+ };
189
+ $document.ready(plugin.onReady); // On DOM ready.
190
+
191
+ })(jQuery);
src/client-s/js/menu-pages.min.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ (function(b){var a={namespace:"comet_cache"},d=b(window),c=b(document);a.onReady=function(){
2
+
3
+ ;a.$menuPage=b("#plugin-menu-page");a.vars=window[a.namespace+"_menu_page_vars"];b(".plugin-menu-page-panel-heading",a.$menuPage).on("click",a.togglePanel);b(".plugin-menu-page-panels-open",a.$menuPage).on("click",a.toggleAllPanelsOpen);b(".plugin-menu-page-panels-close",a.$menuPage).on("click",a.toggleAllPanelsClose);b("[data-action]",a.$menuPage).on("click",a.doDataAction);b("[data-toggle-target]",a.$menuPage).on("click",a.doDataToggleTarget);b('select[name$="_enable\\]"]',a.$menuPage).not(".-no-if-enabled").on("change",a.enableDisable).trigger("change");
4
+
5
+ };a.toggleAllPanelsOpen=function(e){a.preventDefault(e);b(".plugin-menu-page-panel-heading",a.$menuPage).addClass("open").next(".plugin-menu-page-panel-body").addClass("open")};a.toggleAllPanelsClose=function(e){a.preventDefault(e);b(".plugin-menu-page-panel-heading",a.$menuPage).removeClass("open").next(".plugin-menu-page-panel-body").removeClass("open")};a.togglePanel=function(e){a.preventDefault(e);b(this).toggleClass("open").next(".plugin-menu-page-panel-body").toggleClass("open")};a.doDataAction=function(e){a.preventDefault(e);var g=b(this),f=g.data();if(typeof f.confirmation!=="string"||confirm(f.confirmation)){location.href=f.action}};a.enableDisable=function(e){var m=b(this),g=m.val(),f=m.attr("name"),l=Number(g)>=1,i=m.closest(".plugin-menu-page-panel-body"),n=m.data("target"),h=n?b(n,i).filter(".plugin-menu-page-panel-if-enabled"):null,k=m.closest(".plugin-menu-page-panel-if-enabled"),j=k.find("> .plugin-menu-page-panel-if-enabled"),o=i.find("> .plugin-menu-page-panel-if-enabled");if(l){if(n){h.css("opacity",1).find(":input").removeAttr("readonly")}else{if(k.length){j.css("opacity",1).find(":input").removeAttr("readonly")}else{o.css("opacity",1).find(":input").removeAttr("readonly")}}}else{if(n){h.css("opacity",0.4).find(":input").attr("readonly","readonly")}else{if(k.length){j.css("opacity",0.4).find(":input").attr("readonly","readonly")}else{o.css("opacity",0.4).find(":input").attr("readonly","readonly")}}}};a.doDataToggleTarget=function(f){a.preventDefault(f);var g=b(this),e=b(g.data("toggleTarget"));if(e.is(":visible")){e.hide();g.find(".si").removeClass("si-eye-slash").addClass("si-eye")}else{e.show();g.find(".si").removeClass("si-eye").addClass("si-eye-slash")}};a.handleCacheClearAdminBarOpsChange=function(g){var f=b(this),h=f.val(),e=b(".-clear-cache-ops-ss",a.$menuPage);e.attr("src",e.attr("src").replace(/ops[0-9]\-ss\.png$/,"ops"+h+"-ss.png"))};a.handleCdnHostsChange=function(f){var g=b(this),e=b('input[name$="[cdn_host]"]',a.$menuPage);if(b.trim(g.val())){if(e.val()){e.data("hiddenValue",e.val())}e.attr("disabled","disabled").val("")}else{if(!e.val()){e.val(e.data("hiddenValue"))}e.removeAttr("disabled");g.val("")}};
6
+
7
+ ;a.bytesToSizeLabel=function(f,e){if(typeof f!=="number"||f<=1){return f===1?"1 byte":"0 bytes"}if(typeof e!=="number"||e<=0){e=0}var i=1024,j=Math.floor(Math.log(f)/Math.log(i)),h=["bytes","KB","MB","GB","TB","PB","EB","ZB","YB"],g=(f/Math.pow(i,j));return g.toFixed(e)+" "+h[j]};a.numberFormat=function(f,e){if(typeof f!=="number"){return String(f)}if(typeof e!=="number"||e<=0){e=0}return f.toFixed(e).replace(/./g,function(g,i,h){return i&&g!=="."&&((h.length-i)%3===0)?","+g:g})};a.escHtml=function(f){var e={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};return String(f).replace(/[&<>"']/g,function(g){return e[g]})};a.preventDefault=function(f,e){if(!f){return}f.preventDefault();if(e){f.stopImmediatePropagation()}};c.ready(a.onReady)})(jQuery);
src/includes/api.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * API Classes.
4
+ *
5
+ * @since 150422 Rewrite.
6
+ */
7
+ namespace WebSharks\Comet_Cache;
8
+
9
+ if (!defined('WPINC')) {
10
+ exit('Do NOT access this file directly: '.basename(__FILE__));
11
+ }
12
+ class_alias(__NAMESPACE__.'\\ApiBase', GLOBAL_NS);
13
+
14
+ if (!class_exists('zencache')) {
15
+ class_alias(__NAMESPACE__.'\\ApiBase', 'zencache');
16
+ }
src/includes/classes/AbsBase.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Abstract Base.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ abstract class AbsBase
10
+ {
11
+ /**
12
+ * @type null|plugin Plugin reference.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ */
16
+ protected $plugin;
17
+
18
+ /**
19
+ * @type array Instance cache.
20
+ *
21
+ * @since 150422 Rewrite.
22
+ */
23
+ protected $cache = array();
24
+
25
+ /**
26
+ * @type array Global static cache ref.
27
+ *
28
+ * @since 150422 Rewrite.
29
+ */
30
+ protected $static = array();
31
+
32
+ /**
33
+ * @type array Global static cache.
34
+ *
35
+ * @since 150422 Rewrite.
36
+ */
37
+ protected static $global_static = array();
38
+
39
+ /**
40
+ * @type \stdClass Overload properties.
41
+ *
42
+ * @since 150422 Rewrite.
43
+ */
44
+ protected $overload;
45
+
46
+ /**
47
+ * Class constructor.
48
+ *
49
+ * @since 150422 Rewrite.
50
+ */
51
+ public function __construct()
52
+ {
53
+ $this->plugin = &$GLOBALS[GLOBAL_NS];
54
+
55
+ $class = get_called_class();
56
+
57
+ if (empty(static::$global_static[$class])) {
58
+ static::$global_static[$class] = array();
59
+ }
60
+ $this->static = &static::$global_static[$class];
61
+
62
+ $this->overload = new \stdClass();
63
+ }
64
+
65
+ /**
66
+ * Instance (singleton).
67
+ *
68
+ * @since 151002 Directory stats.
69
+ *
70
+ * @return AbsBase Instance.
71
+ */
72
+ public static function instance()
73
+ {
74
+ static $instance; // Per class.
75
+
76
+ if (isset($instance)) {
77
+ return $instance;
78
+ }
79
+ return ($instance = new static());
80
+ }
81
+
82
+ /**
83
+ * Magic/overload `isset()` checker.
84
+ *
85
+ * @param string $property Property to check.
86
+ *
87
+ * @return bool TRUE if `isset($this->overload->{$property})`.
88
+ *
89
+ * @see http://php.net/manual/en/language.oop5.overloading.php
90
+ */
91
+ public function __isset($property)
92
+ {
93
+ $property = (string) $property; // Force string.
94
+
95
+ return isset($this->overload->{$property});
96
+ }
97
+
98
+ /**
99
+ * Magic/overload property getter.
100
+ *
101
+ * @param string $property Property to get.
102
+ *
103
+ * @throws \Exception If the `$overload` property is undefined.
104
+ *
105
+ * @return mixed The value of `$this->overload->{$property}`.
106
+ *
107
+ * @see http://php.net/manual/en/language.oop5.overloading.php
108
+ */
109
+ public function __get($property)
110
+ {
111
+ $property = (string) $property; // Force string.
112
+
113
+ if (property_exists($this->overload, $property)) {
114
+ return $this->overload->{$property};
115
+ }
116
+ throw new \Exception(sprintf(__('Undefined overload property: `%1$s`.', 'comet-cache'), $property));
117
+ }
118
+
119
+ /**
120
+ * Magic/overload property setter.
121
+ *
122
+ * @param string $property Property to set.
123
+ * @param mixed $value The value for this property.
124
+ *
125
+ * @throws \Exception We do NOT allow magic/overload properties to be set.
126
+ * Magic/overload properties in this class are read-only.
127
+ *
128
+ * @see http://php.net/manual/en/language.oop5.overloading.php
129
+ */
130
+ public function __set($property, $value)
131
+ {
132
+ $property = (string) $property; // Force string.
133
+
134
+ throw new \Exception(sprintf(__('Refused to set overload property: `%1$s`.', 'comet-cache'), $property));
135
+ }
136
+
137
+ /**
138
+ * Magic `unset()` handler.
139
+ *
140
+ * @param string $property Property to unset.
141
+ *
142
+ * @throws \Exception We do NOT allow magic/overload properties to be unset.
143
+ * Magic/overload properties in this class are read-only.
144
+ *
145
+ * @see http://php.net/manual/en/language.oop5.overloading.php
146
+ */
147
+ public function __unset($property)
148
+ {
149
+ $property = (string) $property; // Force string.
150
+
151
+ throw new \Exception(sprintf(__('Refused to unset overload property: `%1$s`.', 'comet-cache'), $property));
152
+ }
153
+
154
+ /*
155
+ * Cache key generation helpers.
156
+ */
157
+
158
+ /**
159
+ * Construct & acquires a cache key.
160
+ *
161
+ * @param string $function `__FUNCTION__` is suggested here.
162
+ * @param mixed|array $args The cachable arguments to the calling function.
163
+ * @param string $___prop For internal use only. This defaults to `cache`.
164
+ *
165
+ * @return mixed|null Current value, else `NULL` if the key is not set yet.
166
+ *
167
+ * @note This function returns by reference. The use of `&` is highly recommended when calling this utility.
168
+ * See also: <http://php.net/manual/en/language.references.return.php>
169
+ */
170
+ public function &cacheKey($function, $args = array(), $___prop = 'cache')
171
+ {
172
+ $function = (string) $function;
173
+ $args = (array) $args;
174
+
175
+ if (!isset($this->{$___prop}[$function])) {
176
+ $this->{$___prop}[$function] = null;
177
+ }
178
+ $cache_key = &$this->{$___prop}[$function];
179
+
180
+ foreach ($args as $_arg) {
181
+ // Use each arg as a key.
182
+
183
+ switch (gettype($_arg)) {
184
+ case 'integer':
185
+ $_key = (integer) $_arg;
186
+ break; // Break switch handler.
187
+
188
+ case 'double':
189
+ case 'float':
190
+ $_key = (string) $_arg;
191
+ break; // Break switch handler.
192
+
193
+ case 'boolean':
194
+ $_key = (integer) $_arg;
195
+ break; // Break switch handler.
196
+
197
+ case 'array':
198
+ case 'object':
199
+ $_key = sha1(serialize($_arg));
200
+ break; // Break switch handler.
201
+
202
+ case 'NULL':
203
+ case 'resource':
204
+ case 'unknown type':
205
+ default:
206
+ $_key = "\0".(string) $_arg;
207
+ }
208
+ if (!isset($cache_key[$_key])) {
209
+ $cache_key[$_key] = null;
210
+ }
211
+ $cache_key = &$cache_key[$_key];
212
+ }
213
+ return $cache_key;
214
+ }
215
+
216
+ /**
217
+ * Construct & acquires a static key.
218
+ *
219
+ * @param string $function See {@link cacheKey()}.
220
+ * @param mixed|array $args See {@link cacheKey()}.
221
+ *
222
+ * @return mixed|null See {@link cacheKey()}.
223
+ *
224
+ * @note This function returns by reference. The use of `&` is highly recommended when calling this utility.
225
+ * See also: <http://php.net/manual/en/language.references.return.php>
226
+ */
227
+ public function &staticKey($function, $args = array())
228
+ {
229
+ $key = &$this->cacheKey($function, $args, 'static');
230
+
231
+ return $key; // By reference.
232
+ }
233
+ }
src/includes/classes/AbsBaseAp.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Abstract Base for Advanced Cache and Plugin.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ abstract class AbsBaseAp extends AbsBase
10
+ {
11
+ /**
12
+ * Class constructor.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ */
16
+ public function __construct()
17
+ {
18
+ parent::__construct();
19
+
20
+ $closures_dir = dirname(dirname(__FILE__)).'/closures/Shared';
21
+ $self = $this; // Reference for closures.
22
+
23
+ foreach (scandir($closures_dir) as $_closure) {
24
+ if (substr($_closure, -4) === '.php') {
25
+ require $closures_dir.'/'.$_closure;
26
+ }
27
+ }
28
+ unset($_closure); // Housekeeping.
29
+ }
30
+
31
+ /**
32
+ * Magic/overload property setter.
33
+ *
34
+ * @param string $property Property to set.
35
+ * @param mixed $value The value for this property.
36
+ *
37
+ * @see http://php.net/manual/en/language.oop5.overloading.php
38
+ */
39
+ public function __set($property, $value)
40
+ {
41
+ $property = (string) $property;
42
+ $this->{$property} = $value;
43
+ }
44
+
45
+ /**
46
+ * Closure overloading.
47
+ *
48
+ * @since 150422 Rewrite.
49
+ */
50
+ public function __call($closure, $args)
51
+ {
52
+ $closure = (string) $closure;
53
+
54
+ if (isset($this->{$closure}) && is_callable($this->{$closure})) {
55
+ return call_user_func_array($this->{$closure}, $args);
56
+ }
57
+ throw new \Exception(sprintf(__('Undefined method/closure: `%1$s`.', 'comet-cache'), $closure));
58
+ }
59
+ }
src/includes/classes/Actions.php ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Actions.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class Actions extends AbsBase
10
+ {
11
+ /**
12
+ * Allowed actions.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ */
16
+ protected $allowed_actions = array(
17
+ 'wipeCache',
18
+ 'clearCache',
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+ 'saveOptions',
33
+ 'restoreDefaultOptions',
34
+
35
+
36
+
37
+
38
+
39
+ 'dismissNotice',
40
+ );
41
+
42
+ /**
43
+ * Class constructor.
44
+ *
45
+ * @since 150422 Rewrite.
46
+ */
47
+ public function __construct()
48
+ {
49
+ parent::__construct();
50
+
51
+ if (empty($_REQUEST[GLOBAL_NS])) {
52
+ return; // Not applicable.
53
+ }
54
+ foreach ((array) $_REQUEST[GLOBAL_NS] as $_action => $_args) {
55
+ if (is_string($_action) && method_exists($this, $_action)) {
56
+ if (in_array($_action, $this->allowed_actions, true)) {
57
+ $this->{$_action}($_args); // Do action!
58
+ }
59
+ }
60
+ }
61
+ unset($_action, $_args); // Housekeeping.
62
+ }
63
+
64
+ /**
65
+ * Action handler.
66
+ *
67
+ * @since 150422 Rewrite.
68
+ *
69
+ * @param mixed Input action argument(s).
70
+ */
71
+ protected function wipeCache($args)
72
+ {
73
+ if (!is_multisite() || !$this->plugin->currentUserCanWipeCache()) {
74
+ return; // Nothing to do.
75
+ }
76
+ if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) {
77
+ return; // Unauthenticated POST data.
78
+ }
79
+ $counter = $this->plugin->wipeCache(true);
80
+
81
+
82
+
83
+ $redirect_to = self_admin_url('/admin.php');
84
+ $query_args = array('page' => GLOBAL_NS, GLOBAL_NS.'_cache_wiped' => '1');
85
+ $redirect_to = add_query_arg(urlencode_deep($query_args), $redirect_to);
86
+
87
+ wp_redirect($redirect_to).exit();
88
+ }
89
+
90
+ /**
91
+ * Action handler.
92
+ *
93
+ * @since 150422 Rewrite.
94
+ *
95
+ * @param mixed Input action argument(s).
96
+ */
97
+ protected function clearCache($args)
98
+ {
99
+ if (!$this->plugin->currentUserCanClearCache()) {
100
+ return; // Not allowed to clear.
101
+ }
102
+ if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) {
103
+ return; // Unauthenticated POST data.
104
+ }
105
+ $counter = $this->plugin->clearCache(true);
106
+
107
+
108
+
109
+ $redirect_to = self_admin_url('/admin.php'); // Redirect preparations.
110
+ $query_args = array('page' => GLOBAL_NS, GLOBAL_NS.'_cache_cleared' => '1');
111
+ $redirect_to = add_query_arg(urlencode_deep($query_args), $redirect_to);
112
+
113
+ wp_redirect($redirect_to).exit();
114
+ }
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+
137
+
138
+ /**
139
+ * Action handler.
140
+ *
141
+ * @since 150422 Rewrite.
142
+ *
143
+ * @param mixed Input action argument(s).
144
+ */
145
+ protected function saveOptions($args)
146
+ {
147
+ if (!current_user_can($this->plugin->cap)) {
148
+ return; // Nothing to do.
149
+ }
150
+ if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) {
151
+ return; // Unauthenticated POST data.
152
+ }
153
+ if (!empty($_FILES[GLOBAL_NS]['tmp_name']['import_options'])) {
154
+ $import_file_contents = file_get_contents($_FILES[GLOBAL_NS]['tmp_name']['import_options']);
155
+ unlink($_FILES[GLOBAL_NS]['tmp_name']['import_options']); // Deleted uploaded file.
156
+
157
+ $args = wp_slash(json_decode($import_file_contents, true));
158
+
159
+ unset($args['crons_setup']); // CANNOT be imported!
160
+ unset($args['last_pro_update_check']); // CANNOT be imported!
161
+ unset($args['last_pro_stats_log']); // CANNOT be imported!
162
+ }
163
+ $args = $this->plugin->trimDeep(stripslashes_deep((array) $args));
164
+ $this->plugin->updateOptions($args); // Save/update options.
165
+
166
+ // Ensures `autoCacheMaybeClearPrimaryXmlSitemapError()` always validates the XML Sitemap when saving options (when applicable)
167
+ delete_transient(GLOBAL_NS.'-'.md5($this->plugin->options['auto_cache_sitemap_url']));
168
+
169
+ $redirect_to = self_admin_url('/admin.php'); // Redirect preparations.
170
+ $query_args = array('page' => GLOBAL_NS, GLOBAL_NS.'_updated' => '1');
171
+
172
+ $this->plugin->autoWipeCache(); // May produce a notice.
173
+
174
+ global $is_apache, $is_nginx;
175
+
176
+ if ($this->plugin->options['enable']) {
177
+ if (!($add_wp_cache_to_wp_config = $this->plugin->addWpCacheToWpConfig())) {
178
+ $query_args[GLOBAL_NS.'_wp_config_wp_cache_add_failure'] = '1';
179
+ }
180
+ if ($is_apache && !($add_wp_htaccess = $this->plugin->addWpHtaccess())) {
181
+ $query_args[GLOBAL_NS.'_wp_htaccess_add_failure'] = '1';
182
+ }
183
+ if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) {
184
+ $query_args[GLOBAL_NS.'_wp_htaccess_nginx_notice'] = '1';
185
+ }
186
+ if (!($add_advanced_cache = $this->plugin->addAdvancedCache())) {
187
+ $query_args[GLOBAL_NS.'_advanced_cache_add_failure'] = $add_advanced_cache === null ? 'advanced-cache' : '1';
188
+ }
189
+ if (!$this->plugin->options['auto_cache_enable']) {
190
+ $this->plugin->dismissMainNotice('allow_url_fopen_disabled'); // Dismiss and check again on `admin_init` via `autoCacheMaybeClearPhpIniError()`
191
+ }
192
+ if (!$this->plugin->options['auto_cache_enable'] || !$this->plugin->options['auto_cache_sitemap_url']) {
193
+ $this->plugin->dismissMainNotice('xml_sitemap_missing'); // Dismiss and check again on `admin_init` via `autoCacheMaybeClearPrimaryXmlSitemapError()`
194
+ }
195
+ $this->plugin->updateBlogPaths(); // Multisite networks only.
196
+ } else {
197
+ if (!($remove_wp_cache_from_wp_config = $this->plugin->removeWpCacheFromWpConfig())) {
198
+ $query_args[GLOBAL_NS.'_wp_config_wp_cache_remove_failure'] = '1';
199
+ }
200
+ if ($is_apache && !($remove_wp_htaccess = $this->plugin->removeWpHtaccess())) {
201
+ $query_args[GLOBAL_NS.'_wp_htaccess_remove_failure'] = '1';
202
+ }
203
+ if (!($remove_advanced_cache = $this->plugin->removeAdvancedCache())) {
204
+ $query_args[GLOBAL_NS.'_advanced_cache_remove_failure'] = '1';
205
+ }
206
+ $this->plugin->dismissMainNotice('xml_sitemap_missing'); // Dismiss notice when disabling plugin
207
+ $this->plugin->dismissMainNotice('allow_url_fopen_disabled'); // Dismiss notice when disabling plugin
208
+ }
209
+ $redirect_to = add_query_arg(urlencode_deep($query_args), $redirect_to);
210
+
211
+ wp_redirect($redirect_to).exit();
212
+ }
213
+
214
+ /**
215
+ * Action handler.
216
+ *
217
+ * @since 150422 Rewrite.
218
+ *
219
+ * @param mixed Input action argument(s).
220
+ */
221
+ protected function restoreDefaultOptions($args)
222
+ {
223
+ if (!current_user_can($this->plugin->cap)) {
224
+ return; // Nothing to do.
225
+ }
226
+ if (is_multisite() && !current_user_can($this->plugin->network_cap)) {
227
+ return; // Nothing to do.
228
+ }
229
+ if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) {
230
+ return; // Unauthenticated POST data.
231
+ }
232
+ $this->plugin->restoreDefaultOptions(); // Restore defaults.
233
+
234
+ $redirect_to = self_admin_url('/admin.php'); // Redirect preparations.
235
+ $query_args = array('page' => GLOBAL_NS, GLOBAL_NS.'_restored' => '1');
236
+
237
+ $this->plugin->autoWipeCache(); // May produce a notice.
238
+
239
+ global $is_apache, $is_nginx;
240
+
241
+ if ($this->plugin->options['enable']) {
242
+ if (!($add_wp_cache_to_wp_config = $this->plugin->addWpCacheToWpConfig())) {
243
+ $query_args[GLOBAL_NS.'_wp_config_wp_cache_add_failure'] = '1';
244
+ }
245
+ if ($is_apache && !($add_wp_htaccess = $this->plugin->addWpHtaccess())) {
246
+ $query_args[GLOBAL_NS.'_wp_htaccess_add_failure'] = '1';
247
+ }
248
+ if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) {
249
+ $query_args[GLOBAL_NS.'_wp_htaccess_nginx_notice'] = '1';
250
+ }
251
+ if (!($add_advanced_cache = $this->plugin->addAdvancedCache())) {
252
+ $query_args[GLOBAL_NS.'_advanced_cache_add_failure'] = $add_advanced_cache === null ? 'advanced-cache' : '1';
253
+ }
254
+ $this->plugin->updateBlogPaths(); // Multisite networks only.
255
+ } else {
256
+ if (!($remove_wp_cache_from_wp_config = $this->plugin->removeWpCacheFromWpConfig())) {
257
+ $query_args[GLOBAL_NS.'_wp_config_wp_cache_remove_failure'] = '1';
258
+ }
259
+ if ($is_apache && !($remove_wp_htaccess = $this->plugin->removeWpHtaccess())) {
260
+ $query_args[GLOBAL_NS.'_wp_htaccess_remove_failure'] = '1';
261
+ }
262
+ if (!($remove_advanced_cache = $this->plugin->removeAdvancedCache())) {
263
+ $query_args[GLOBAL_NS.'_advanced_cache_remove_failure'] = '1';
264
+ }
265
+ }
266
+ $redirect_to = add_query_arg(urlencode_deep($query_args), $redirect_to);
267
+
268
+ wp_redirect($redirect_to).exit();
269
+ }
270
+
271
+
272
+
273
+
274
+
275
+ /**
276
+ * Action handler.
277
+ *
278
+ * @since 150422 Rewrite.
279
+ *
280
+ * @param mixed Input action argument(s).
281
+ */
282
+ protected function dismissNotice($args)
283
+ {
284
+ if (!current_user_can($this->plugin->cap)) {
285
+ return; // Nothing to do.
286
+ }
287
+ if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) {
288
+ return; // Unauthenticated POST data.
289
+ }
290
+ $args = $this->plugin->trimDeep(stripslashes_deep((array) $args));
291
+ $this->plugin->dismissNotice($args['key']);
292
+
293
+ wp_redirect(remove_query_arg(GLOBAL_NS)).exit();
294
+ }
295
+ }
src/includes/classes/AdvCacheBackCompat.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * AC back compat.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class AdvCacheBackCompat
10
+ {
11
+ /**
12
+ * Back compat. with `zcAC` and `zcABC`.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ */
16
+ public static function zcRequestVars()
17
+ {
18
+ $super_gs = array(
19
+ '_GET' => &$_GET,
20
+ '_REQUEST' => &$_REQUEST,
21
+ );
22
+ $zc_suffixes = array('AC', 'ABC');
23
+
24
+ foreach ($super_gs as $_super_g_key => &$_super_g_value) {
25
+ foreach ($zc_suffixes as $_zc_suffix) {
26
+ if (array_key_exists('zc'.$_zc_suffix, $_super_g_value)) {
27
+ if ($_super_g_key === '_GET' && !isset($_GET['cc'.$_zc_suffix])) {
28
+ $_GET['cc'.$_zc_suffix] = $_super_g_value['zc'.$_zc_suffix];
29
+ }
30
+ foreach ($super_gs as $__super_g_key => &$__super_g_value) {
31
+ unset($__super_g_value['zc'.$_zc_suffix]);
32
+ }
33
+ unset($__super_g_key, $__super_g_value); // Housekeeping.
34
+ }
35
+ }
36
+ }
37
+ unset($_super_g_key, $_super_g_value, $_zc_suffix);
38
+ }
39
+
40
+ /**
41
+ * Back compat. with `ZENCACHE_` constants.
42
+ *
43
+ * @since 150422 Rewrite.
44
+ */
45
+ public static function zenCacheConstants()
46
+ {
47
+ if (!($constants = get_defined_constants(true)) || empty($constants['user'])) {
48
+ return; // Nothing to do; i.e. no user-defined constants.
49
+ }
50
+ foreach ($constants['user'] as $_constant => $_value) {
51
+ if (stripos($_constant, 'ZENCACHE_') !== 0) {
52
+ continue; // Nothing to do here.
53
+ }
54
+ if (!($_constant_sub_name = substr($_constant, 12))) {
55
+ continue; // Nothing to do here.
56
+ }
57
+ if (!defined(GLOBAL_NS.'_'.$_constant_sub_name)) {
58
+ define(GLOBAL_NS.'_'.$_constant_sub_name, $_value);
59
+ }
60
+ }
61
+ unset($_constant, $_value); // Housekeeping.
62
+
63
+ if (isset($_SERVER['ZENCACHE_ALLOWED']) && !isset($_SERVER[GLOBAL_NS.'_ALLOWED'])) {
64
+ $_SERVER[GLOBAL_NS.'_ALLOWED'] = $_SERVER['ZENCACHE_ALLOWED'];
65
+ }
66
+ }
67
+ }
src/includes/classes/AdvancedCache.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Advanced cache.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class AdvancedCache extends AbsBaseAp
10
+ {
11
+ /**
12
+ * Flagged as `TRUE` if running.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ *
16
+ * @type bool `TRUE` if running.
17
+ */
18
+ public $is_running = false;
19
+
20
+ /**
21
+ * Microtime; for debugging.
22
+ *
23
+ * @since 150422 Rewrite.
24
+ *
25
+ * @type float Microtime; for debugging.
26
+ */
27
+ public $timer = 0;
28
+
29
+ /**
30
+ * Class constructor/cache handler.
31
+ *
32
+ * @since 150422 Rewrite.
33
+ */
34
+ public function __construct()
35
+ {
36
+ parent::__construct();
37
+
38
+ $closures_dir = dirname(dirname(__FILE__)).'/closures/Ac';
39
+ $self = $this; // Reference for closures.
40
+
41
+ foreach (scandir($closures_dir) as $_closure) {
42
+ if (substr($_closure, -4) === '.php') {
43
+ require $closures_dir.'/'.$_closure;
44
+ }
45
+ }
46
+ unset($_closure); // Housekeeping.
47
+
48
+ if (!defined('WP_CACHE') || !WP_CACHE || !COMET_CACHE_ENABLE) {
49
+ return; // Not enabled.
50
+ }
51
+ if (defined('WP_INSTALLING') || defined('RELOCATE')) {
52
+ return; // N/A; installing|relocating.
53
+ }
54
+ $this->is_running = true;
55
+ $this->timer = microtime(true);
56
+
57
+ $this->loadAcPlugins();
58
+ $this->registerShutdownFlag();
59
+ $this->maybeIgnoreUserAbort();
60
+ $this->maybeStopBrowserCaching();
61
+
62
+ $this->maybeStartOutputBuffering();
63
+ }
64
+ }
src/includes/classes/ApiBase.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * API Base Class.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class ApiBase
10
+ {
11
+ /**
12
+ * Current CC plugin instance.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ *
16
+ * @return \comet_cache\plugin instance.
17
+ */
18
+ public static function plugin()
19
+ {
20
+ return $GLOBALS[GLOBAL_NS];
21
+ }
22
+
23
+ /**
24
+ * Gives you the current version string.
25
+ *
26
+ * @since 150422 Rewrite.
27
+ *
28
+ * @return string Current version string.
29
+ */
30
+ public static function version()
31
+ {
32
+ return VERSION; // Via constant.
33
+ }
34
+
35
+ /**
36
+ * Gives you the current array of configured options.
37
+ *
38
+ * @since 150422 Rewrite.
39
+ *
40
+ * @return array Current array of options.
41
+ */
42
+ public static function options()
43
+ {
44
+ return $GLOBALS[GLOBAL_NS]->options;
45
+ }
46
+
47
+ /**
48
+ * Purges expired cache files, leaving all others intact.
49
+ *
50
+ * @since 150422 Rewrite.
51
+ *
52
+ * @note This occurs automatically over time via WP Cron;
53
+ * but this will force an immediate purge if you so desire.
54
+ *
55
+ * @return int Total files purged (if any).
56
+ */
57
+ public static function purge()
58
+ {
59
+ return $GLOBALS[GLOBAL_NS]->purgeCache();
60
+ }
61
+
62
+ /**
63
+ * This erases the entire cache for the current blog.
64
+ *
65
+ * @since 150422 Rewrite.
66
+ *
67
+ * @note In a multisite network this impacts only the current blog,
68
+ * it does not clear the cache for other child blogs.
69
+ *
70
+ * @return int Total files cleared (if any).
71
+ */
72
+ public static function clear()
73
+ {
74
+ return $GLOBALS[GLOBAL_NS]->clearCache();
75
+ }
76
+
77
+ /**
78
+ * This erases the cache for a specific post ID.
79
+ *
80
+ * @since 150626 Adding support for new API methods.
81
+ *
82
+ * @param int $post_id Post ID.
83
+ *
84
+ * @return int Total files cleared (if any).
85
+ */
86
+ public static function clearPost($post_id)
87
+ {
88
+ return $GLOBALS[GLOBAL_NS]->autoClearPostCache($post_id);
89
+ }
90
+
91
+ /**
92
+ * This clears the cache for a specific URL.
93
+ *
94
+ * @since 151114 Adding support for custom URLs.
95
+ *
96
+ * @param string $url Input URL to clear.
97
+ *
98
+ * @return int Total files cleared (if any).
99
+ */
100
+ public static function clearUrl($url)
101
+ {
102
+ $regex = $GLOBALS[GLOBAL_NS]->buildCachePathRegexFromWcUrl($url);
103
+ return $GLOBALS[GLOBAL_NS]->deleteFilesFromCacheDir($regex);
104
+ }
105
+
106
+
107
+
108
+ /**
109
+ * This wipes out the entire cache.
110
+ *
111
+ * @since 150422 Rewrite.
112
+ *
113
+ * @note On a standard WP installation this is the same as comet_cache::clear();
114
+ * but on a multisite installation it impacts the entire network
115
+ * (i.e. wipes the cache for all blogs in the network).
116
+ *
117
+ * @return int Total files wiped (if any).
118
+ */
119
+ public static function wipe()
120
+ {
121
+ return $GLOBALS[GLOBAL_NS]->wipeCache();
122
+ }
123
+ }
src/includes/classes/Conflicts.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Conflicts.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class Conflicts
10
+ {
11
+ /**
12
+ * Check.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ */
16
+ public static function check()
17
+ {
18
+ if (static::doCheck()) {
19
+ static::maybeEnqueueNotice();
20
+ }
21
+ return $GLOBALS[GLOBAL_NS.'_conflicting_plugin'];
22
+ }
23
+
24
+ /**
25
+ * Perform check.
26
+ *
27
+ * @since 150422 Rewrite.
28
+ */
29
+ protected static function doCheck()
30
+ {
31
+ if (!empty($GLOBALS[GLOBAL_NS.'_conflicting_plugin'])) {
32
+ return $GLOBALS[GLOBAL_NS.'_conflicting_plugin'];
33
+ }
34
+ $conflicting_plugin_slugs = array(
35
+ 'zencache', 'zencache-pro', 'quick-cache', 'quick-cache-pro',
36
+ str_replace('_', '-', GLOBAL_NS).(IS_PRO ? '' : '-pro'),
37
+ 'wp-super-cache', 'w3-total-cache', 'hyper-cache', 'wp-rocket',
38
+ );
39
+ $active_plugins = (array) get_option('active_plugins', array());
40
+ $active_sitewide_plugins = is_multisite() ? array_keys((array) get_site_option('active_sitewide_plugins', array())) : array();
41
+ $active_plugins = array_unique(array_merge($active_plugins, $active_sitewide_plugins));
42
+
43
+ foreach ($active_plugins as $_active_plugin_basename) {
44
+ if (!($_active_plugin_slug = strstr($_active_plugin_basename, '/', true))) {
45
+ continue; // Nothing to check in this case.
46
+ }
47
+ if (in_array($_active_plugin_slug, $conflicting_plugin_slugs, true)) {
48
+ if (in_array($_active_plugin_slug, array('zencache', 'zencache-pro', 'quick-cache', 'quick-cache-pro'), true)) {
49
+ add_action('admin_init', function () use ($_active_plugin_basename) {
50
+ if (function_exists('deactivate_plugins')) { // Can deactivate?
51
+ deactivate_plugins($_active_plugin_basename, true);
52
+ }
53
+ }, -1000);
54
+ } else {
55
+ return ($GLOBALS[GLOBAL_NS.'_conflicting_plugin'] = $_active_plugin_slug);
56
+ }
57
+ }
58
+ }
59
+ return ($GLOBALS[GLOBAL_NS.'_conflicting_plugin'] = ''); // i.e. No conflicting plugins.
60
+ }
61
+
62
+ /**
63
+ * Maybe enqueue dashboard notice.
64
+ *
65
+ * @since 150422 Rewrite.
66
+ */
67
+ protected function maybeEnqueueNotice()
68
+ {
69
+ if (!empty($GLOBALS[GLOBAL_NS.'_uninstalling'])) {
70
+ return; // Not when uninstalling.
71
+ }
72
+ if (empty($GLOBALS[GLOBAL_NS.'_conflicting_plugin'])) {
73
+ return; // Not conflicts.
74
+ }
75
+ add_action('all_admin_notices', function () {
76
+ if (!empty($GLOBALS[GLOBAL_NS.'_conflicting_plugin_lite_pro'])) {
77
+ return; // Already did this in one plugin or the other.
78
+ }
79
+ $construct_name = function ($slug_or_ns) {
80
+ $name = trim(strtolower((string) $slug_or_ns));
81
+ $name = preg_replace('/[_\-]+(?:lite|pro)$/', '', $name);
82
+ $name = preg_replace('/[^a-z0-9]/', ' ', $name);
83
+ $name = str_replace('cache', 'Cache', ucwords($name));
84
+
85
+ return $name; // e.g., `x-cache` becomes `X Cache`.
86
+ };
87
+ $this_plugin_name = NAME; // See `src/includes/stub.php` for details.
88
+ $conflicting_plugin_name = $construct_name($GLOBALS[GLOBAL_NS.'_conflicting_plugin']);
89
+
90
+ if (strcasecmp($this_plugin_name, $conflicting_plugin_name) === 0) {
91
+ $this_plugin_name = $this_plugin_name.' '.__('Pro', 'comet-cache');
92
+ $conflicting_plugin_name = $conflicting_plugin_name.' '.__('Lite', 'comet-cache');
93
+ $GLOBALS[GLOBAL_NS.'_conflicting_plugin_lite_pro'] = true;
94
+ }
95
+ echo '<div class="error">'.// Error notice.
96
+ ' <p>'.// Running one or more conflicting plugins at the same time.
97
+ ' '.sprintf(__('<strong>%1$s</strong> is NOT running. A conflicting plugin, <strong>%2$s</strong>, is currently active. Please deactivate the %2$s plugin to clear this message.', 'comet-cache'), esc_html($this_plugin_name), esc_html($conflicting_plugin_name)).
98
+ ' </p>'.
99
+ '</div>';
100
+ });
101
+ }
102
+ }
src/includes/classes/FeedUtils.php ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Feed Utils.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class FeedUtils extends AbsBase
10
+ {
11
+ /**
12
+ * @type string WordPress `home_url()`.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ */
16
+ protected $home_url;
17
+
18
+ /**
19
+ * @type string Default feed type; e.g. `rss2`.
20
+ *
21
+ * @since 150422 Rewrite.
22
+ */
23
+ protected $default_feed;
24
+
25
+ /**
26
+ * @type bool Using SEO-friendly permalinks?
27
+ *
28
+ * @since 150422 Rewrite.
29
+ */
30
+ protected $seo_friendly_permalinks;
31
+
32
+ /**
33
+ * @type array All unique feed types.
34
+ *
35
+ * @since 150422 Rewrite.
36
+ */
37
+ protected $feed_types;
38
+
39
+ /**
40
+ * Class constructor.
41
+ *
42
+ * @since 150422 Rewrite.
43
+ */
44
+ public function __construct()
45
+ {
46
+ parent::__construct();
47
+
48
+ $this->home_url = rtrim(home_url(), '/');
49
+ $this->default_feed = get_default_feed(); // Default feed type.
50
+ $this->seo_friendly_permalinks = (boolean) get_option('permalink_structure');
51
+ $this->feed_types = array_unique(array($this->default_feed, 'rdf', 'rss', 'rss2', 'atom'));
52
+ }
53
+
54
+ /**
55
+ * Feed link variations.
56
+ *
57
+ * @since 150422 Rewrite.
58
+ *
59
+ * @param string $type_prefix A feed type prefix; optional.
60
+ *
61
+ * @return array An array of all feed link variations.
62
+ */
63
+ public function feedLinkVariations($type_prefix = '')
64
+ {
65
+ $variations = array(); // Initialize.
66
+
67
+ foreach ($this->feed_types as $_feed_type) {
68
+ $variations[] = get_feed_link((string) $type_prefix.$_feed_type);
69
+ }
70
+ unset($_feed_type); // Housekeeping.
71
+
72
+ return $variations;
73
+ }
74
+
75
+ /**
76
+ * Post comments; feed link variations.
77
+ *
78
+ * @since 150422 Rewrite.
79
+ *
80
+ * @param \WP_Post A WordPress post class instance.
81
+ *
82
+ * @return array An array of all feed link variations.
83
+ */
84
+ public function postCommentsFeedLinkVariations(\WP_Post $post)
85
+ {
86
+ $variations = array(); // Initialize.
87
+
88
+ foreach ($this->feed_types as $_feed_type) {
89
+ $variations[] = get_post_comments_feed_link($post->ID, $_feed_type);
90
+ }
91
+ unset($_feed_type); // Housekeeping.
92
+
93
+ return $variations;
94
+ }
95
+
96
+ /**
97
+ * Post author; feed link variations.
98
+ *
99
+ * @since 150422 Rewrite.
100
+ *
101
+ * @param \WP_Post A WordPress post class instance.
102
+ *
103
+ * @return array An array of all feed link variations.
104
+ */
105
+ public function postAuthorFeedLinkVariations(\WP_Post $post)
106
+ {
107
+ $variations = array(); // Initialize.
108
+
109
+ foreach ($this->feed_types as $_feed_type) {
110
+ $variations[] = get_author_feed_link($post->post_author, $_feed_type);
111
+ }
112
+ if ($this->seo_friendly_permalinks && ($post_author = get_userdata($post->post_author))) {
113
+ foreach ($this->feed_types as $_feed_type) {
114
+ $variations[] = add_query_arg(urlencode_deep(array('author' => $post->post_author)), $this->home_url.'/feed/'.urlencode($_feed_type).'/');
115
+ $variations[] = add_query_arg(urlencode_deep(array('author' => $post_author->user_nicename)), $this->home_url.'/feed/'.urlencode($_feed_type).'/');
116
+ }
117
+ }
118
+ unset($_feed_type); // Housekeeping.
119
+
120
+ return $variations;
121
+ }
122
+
123
+ /**
124
+ * Post type archive; feed link variations.
125
+ *
126
+ * @since 150422 Rewrite.
127
+ *
128
+ * @param \WP_Post A WordPress post class instance.
129
+ *
130
+ * @return array An array of all feed link variations.
131
+ */
132
+ public function postTypeArchiveFeedLinkVariations(\WP_Post $post)
133
+ {
134
+ $variations = array(); // Initialize.
135
+
136
+ foreach ($this->feed_types as $_feed_type) {
137
+ $variations[] = get_post_type_archive_feed_link($post->post_type, $_feed_type);
138
+ }
139
+ unset($_feed_type); // Housekeeping.
140
+
141
+ return $variations;
142
+ }
143
+
144
+ /**
145
+ * Post terms; feed link variations.
146
+ *
147
+ * @since 150422 Rewrite.
148
+ *
149
+ * @param \WP_Post A WordPress post class instance.
150
+ * @param bool $include_regex_wildcard_keys Defaults to a `FALSE` value.
151
+ *
152
+ * @return array An array of all feed link variations.
153
+ *
154
+ * @note If `$include_regex_wildcard_keys` is `TRUE`:
155
+ * This method may return some associative keys also.
156
+ * For string/associative keys, each key is `[feed type]::[regex]`, and the feed link/URL
157
+ * will contain a single `*` wildcard character where the `[regex]` pattern should go.
158
+ */
159
+ public function postTermFeedLinkVariations(\WP_Post $post, $include_regex_wildcard_keys = false)
160
+ {
161
+ $variations = $post_terms = array(); // Initialize.
162
+
163
+ if (!is_array($post_taxonomies = get_object_taxonomies($post, 'objects')) || !$post_taxonomies) {
164
+ return $variations; // Nothing to do here; post has no terms.
165
+ }
166
+ foreach ($post_taxonomies as $_post_taxonomy) {
167
+ if (is_array($_post_taxonomy_terms = wp_get_post_terms($post->ID, $_post_taxonomy->name)) && $_post_taxonomy_terms) {
168
+ $post_terms = array_merge($post_terms, $_post_taxonomy_terms);
169
+ }
170
+ unset($_post_taxonomy, $_post_taxonomy_terms);
171
+ }
172
+ foreach ($post_terms as $_post_term) {
173
+ foreach ($this->feed_types as $_feed_type) {
174
+ $_term_feed_link = get_term_feed_link($_post_term->term_id, $_post_term->taxonomy, $_feed_type);
175
+ $variations[] = $_term_feed_link; // Add this variation; always.
176
+
177
+ if ($include_regex_wildcard_keys && $_term_feed_link && strpos($_term_feed_link, '?') === false) {
178
+ // Quick example: `(?:123|slug)`; to consider both of these variations.
179
+ $_term_id_or_slug = '(?:'.preg_quote($_post_term->term_id, '/').
180
+ '|'.preg_quote(preg_replace('/[^a-z0-9\/.]/i', '-', $_post_term->slug), '/').')';
181
+
182
+ // Quick example: `http://www.example.com/tax/term/feed`;
183
+ // with a wildcard this becomes: `http://www.example.com/tax/*/feed`.
184
+ $_term_feed_link_with_wildcard = preg_replace('/\/[^\/]+\/feed([\/?#]|$)/', '/*/feed'.'${1}', $_term_feed_link);
185
+
186
+ // Quick example: `http://www.example.com/tax/*/feed`;
187
+ // becomes: `\/http\/www\.example\.com\/tax\/.*?(?=[\/\-]?(?:123|slug)[\/\-]).*?\/feed`
188
+ // ... this covers variations that use: `/tax/term,term/feed/`.
189
+ // ... also covers variations that use: `/tax/term/tax/term/feed/`.
190
+ $variations[$_feed_type.'::.*?(?=[\/\-]?'.$_term_id_or_slug.'[\/\-]).*?'] = $_term_feed_link_with_wildcard;
191
+ // This may also pick up false-positives. Not much we can do about this.
192
+ // For instance, if another feed has the same word/slug in what is actually a longer/different term.
193
+ // Or, if another feed has the same word/slug in what is actually the name of a taxonomy.
194
+ }
195
+ }
196
+ unset($_feed_type, $_term_feed_link, $_term_id_or_slug, $_term_feed_link_with_wildcard); // Housekeeping.
197
+
198
+ if ($this->seo_friendly_permalinks && is_object($_taxonomy = get_taxonomy($_post_term->taxonomy))) {
199
+ if ($_taxonomy->name === 'category') {
200
+ $_taxonomy_query_var = 'cat';
201
+ } else {
202
+ $_taxonomy_query_var = $_taxonomy->query_var;
203
+ }
204
+ foreach ($this->feed_types as $_feed_type) {
205
+ $variations[] = add_query_arg(urlencode_deep(array($_taxonomy_query_var => $_post_term->term_id)), $this->home_url.'/feed/'.urlencode($_feed_type).'/');
206
+ }
207
+ foreach ($this->feed_types as $_feed_type) {
208
+ $variations[] = add_query_arg(urlencode_deep(array($_taxonomy_query_var => $_post_term->slug)), $this->home_url.'/feed/'.urlencode($_feed_type).'/');
209
+ }
210
+ }
211
+ unset($_taxonomy, $_taxonomy_query_var, $_feed_type); // Housekeeping.
212
+ }
213
+ unset($_post_term); // Housekeeping.
214
+
215
+ return $variations;
216
+ }
217
+
218
+ /**
219
+ * Convert variations into regex fragments for a call to `deleteFilesFromHostCacheDir()`.
220
+ *
221
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
222
+ *
223
+ * @param array $variations An array of variations (URLs) built by other class members.
224
+ *
225
+ * @return array An array of regex fragments for a call to `deleteFilesFromHostCacheDir()`.
226
+ */
227
+ public function convertVariationsToHostCachePathRegexFrags(array $variations)
228
+ {
229
+ $regex_frags = array();
230
+ $is_multisite = is_multisite();
231
+ $can_consider_domain_mapping = $is_multisite && $this->plugin->canConsiderDomainMapping();
232
+ $flags = CACHE_PATH_NO_SCHEME | CACHE_PATH_NO_HOST // Default flags.
233
+ | CACHE_PATH_NO_USER | CACHE_PATH_NO_VSALT | CACHE_PATH_NO_EXT;
234
+ // Flags: note that we DO allow for query string data in these regex fragments.
235
+
236
+ foreach ($variations as $_key => $_url) {
237
+ $_url = trim((string) $_url); // Force string value.
238
+
239
+ if ($_url && $is_multisite && $can_consider_domain_mapping) {
240
+ // Shortest possible URI; i.e., consider domain mapping.
241
+ $_url = $this->plugin->domainMappingUrlFilter($_url);
242
+ $_is_url_domain_mapped = $_url && $this->plugin->domainMappingBlogId($_url);
243
+ } else {
244
+ $_is_url_domain_mapped = false; // No, obviously.
245
+ }
246
+ if (!$_url || !($_url_parts = $this->plugin->parseUrl($_url)) || empty($_url_parts['host'])) {
247
+ continue; // Invalid variation.
248
+ }
249
+ $_host_base_dir_tokens = $this->plugin->hostBaseDirTokens(false, $_is_url_domain_mapped, !empty($_url_parts['path']) ? $_url_parts['path'] : '/');
250
+ $_host_url = rtrim('http://'.$_url_parts['host'].$_host_base_dir_tokens, '/');
251
+ $_host_cache_path = $this->plugin->buildCachePath($_host_url, '', '', $flags);
252
+
253
+ if (is_string($_key) && strpos($_key, '::') !== false && strpos($_url, '*') !== false) {
254
+ list($_feed_type, $_wildcard_regex) = explode('::', $_key, 2); // This regex replaces wildcards.
255
+ $_cache_path = $this->plugin->buildCachePath($_url, '', '', $flags | CACHE_PATH_ALLOW_WILDCARDS);
256
+ $_relative_cache_path = preg_replace('/^'.preg_quote($_host_cache_path, '/').'(?:\/|$)/i', '', $_cache_path);
257
+ $_relative_cache_path_regex = preg_replace('/\\\\\*/', $_wildcard_regex, preg_quote($_relative_cache_path, '/'));
258
+ } else {
259
+ $_cache_path = $this->plugin->buildCachePath($_url, '', '', $flags); // Default flags.
260
+ $_relative_cache_path = preg_replace('/^'.preg_quote($_host_cache_path, '/').'(?:\/|$)/i', '', $_cache_path);
261
+ $_relative_cache_path_regex = preg_quote($_relative_cache_path, '/');
262
+ }
263
+ if ($_relative_cache_path_regex) {
264
+ $regex_frags[] = $_relative_cache_path_regex; // No leading slash.
265
+ }
266
+ }
267
+ unset($_key, $_url); // Housekeeping; for all temporary vars used above.
268
+ unset($_is_url_domain_mapped, $_url_parts, $_host_base_dir_tokens, $_host_url, $_host_cache_path);
269
+ unset($_feed_type, $_wildcard_regex, $_cache_path, $_relative_cache_path, $_relative_cache_path_regex);
270
+
271
+ return $regex_frags ? array_unique($regex_frags) : $regex_frags;
272
+ }
273
+ }
src/includes/classes/MenuPage.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Menu Page.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class MenuPage extends AbsBase
10
+ {
11
+ /**
12
+ * Constructor.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ *
16
+ * @param string $menu_page Menu page.
17
+ */
18
+ public function __construct($menu_page = '')
19
+ {
20
+ parent::__construct();
21
+
22
+ if ($menu_page) {
23
+ switch ($menu_page) {
24
+ case 'options':
25
+ new MenuPageOptions();
26
+ break;
27
+
28
+
29
+
30
+
31
+ }
32
+ }
33
+ }
34
+ }
src/includes/classes/MenuPageOptions.php ADDED
@@ -0,0 +1,1056 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Options Page.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class MenuPageOptions extends MenuPage
10
+ {
11
+ /**
12
+ * Constructor.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ */
16
+ public function __construct()
17
+ {
18
+ parent::__construct(); // Parent constructor.
19
+
20
+ global $is_nginx; // WP global for web server checks below.
21
+
22
+ echo '<form id="plugin-menu-page" class="plugin-menu-page" method="post" enctype="multipart/form-data"'.
23
+ ' action="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS, '_wpnonce' => wp_create_nonce())), self_admin_url('/admin.php'))).'">'."\n";
24
+
25
+ /* ----------------------------------------------------------------------------------------- */
26
+
27
+ echo '<div class="plugin-menu-page-heading">'."\n";
28
+
29
+ if (is_multisite()) {
30
+ echo '<button type="button" class="plugin-menu-page-wipe-cache" style="float:right; margin-left:15px;" title="'.esc_attr(__('Wipe Cache (Start Fresh); clears the cache for all sites in this network at once!', 'comet-cache')).'"'.
31
+ ' data-action="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS, '_wpnonce' => wp_create_nonce(), GLOBAL_NS => array('wipeCache' => '1'))), self_admin_url('/admin.php'))).'">'.
32
+ ' '.__('Wipe', 'comet-cache').' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/wipe.png')).'" style="width:16px; height:16px;" /></button>'."\n";
33
+ }
34
+ echo ' <button type="button" class="plugin-menu-page-clear-cache" style="float:right;" title="'.esc_attr(__('Clear Cache (Start Fresh)', 'comet-cache').((is_multisite()) ? __('; affects the current site only.', 'comet-cache') : '')).'"'.
35
+ ' data-action="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS, '_wpnonce' => wp_create_nonce(), GLOBAL_NS => array('clearCache' => '1'))), self_admin_url('/admin.php'))).'">'.
36
+ ' '.__('Clear', 'comet-cache').' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/clear.png')).'" style="width:16px; height:16px;" /></button>'."\n";
37
+
38
+ echo ' <button type="button" class="plugin-menu-page-restore-defaults"'.// Restores default options.
39
+ ' data-confirmation="'.esc_attr(__('Restore default plugin options? You will lose all of your current settings! Are you absolutely sure about this?', 'comet-cache')).'"'.
40
+ ' data-action="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS, '_wpnonce' => wp_create_nonce(), GLOBAL_NS => array('restoreDefaultOptions' => '1'))), self_admin_url('/admin.php'))).'">'.
41
+ ' '.__('Restore', 'comet-cache').' <i class="si si-ambulance"></i></button>'."\n";
42
+
43
+ echo ' <div class="plugin-menu-page-panel-togglers" title="'.esc_attr(__('All Panels', 'comet-cache')).'">'."\n";
44
+ echo ' <button type="button" class="plugin-menu-page-panels-open"><i class="si si-chevron-down"></i></button>'."\n";
45
+ echo ' <button type="button" class="plugin-menu-page-panels-close"><i class="si si-chevron-up"></i></button>'."\n";
46
+ echo ' </div>'."\n";
47
+
48
+ echo ' <div class="plugin-menu-page-upsells">'."\n";
49
+ if (IS_PRO && current_user_can($this->plugin->update_cap)) {
50
+ echo '<a href="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS.'-pro-updater')), self_admin_url('/admin.php'))).'"><i class="si si-magic"></i> '.__('Pro Updater', 'comet-cache').'</a>'."\n";
51
+ }
52
+ if (!IS_PRO) {
53
+ echo ' <a href="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS, GLOBAL_NS.'_pro_preview' => '1')), self_admin_url('/admin.php'))).'"><i class="si si-eye"></i> '.__('Preview Pro Features', 'comet-cache').'</a>'."\n";
54
+ echo ' <a href="'.esc_attr('http://cometcache.com/prices/').'" target="_blank"><i class="si si-heart-o"></i> '.__('Pro Upgrade', 'comet-cache').'</a>'."\n";
55
+ }
56
+ echo ' <a href="'.esc_attr('http://cometcache.com/r/comet-cache-subscribe/').'" target="_blank"><i class="si si-envelope"></i> '.__('Newsletter', 'comet-cache').'</a>'."\n";
57
+ echo ' <a href="'.esc_attr('http://cometcache.com/r/comet-cache-beta-testers-list/').'" target="_blank"><i class="si si-envelope"></i> '.__('Beta Testers', 'comet-cache').'</a>'."\n";
58
+ echo ' </div>'."\n";
59
+
60
+ if (IS_PRO) {
61
+ echo '<div class="plugin-menu-page-version">'."\n";
62
+ echo ' '.sprintf(__('%1$s&trade; Pro v%2$s', 'comet-cache'), esc_html(NAME), esc_html(VERSION))."\n";
63
+
64
+ if ($this->plugin->options['latest_pro_version'] && version_compare(VERSION, $this->plugin->options['latest_pro_version'], '<')) {
65
+ echo '(<a href="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS.'-pro-updater')), self_admin_url('/admin.php'))).'" style="font-weight:bold;">'.__('update available', 'comet-cache').'</a>)'."\n";
66
+ } else {
67
+ echo '(<a href="'.esc_attr('https://cometcache.com/changelog/').'" target="_blank">'.__('changelog', 'comet-cache').'</a>)'."\n";
68
+ }
69
+ echo '</div>'."\n";
70
+ } else { // For the lite version (default behavior).
71
+ echo '<div class="plugin-menu-page-version">'."\n";
72
+ echo ' '.sprintf(__('%1$s&trade; v%2$s', 'comet-cache'), esc_html(NAME), esc_html(VERSION))."\n";
73
+
74
+ if ($this->plugin->options['latest_lite_version'] && version_compare(VERSION, $this->plugin->options['latest_lite_version'], '<')) {
75
+ echo '(<a href="'.esc_attr(self_admin_url('/plugins.php')).'" style="font-weight:bold;">'.__('update available', 'comet-cache').'</a>)'."\n";
76
+ } else {
77
+ echo '(<a href="'.esc_attr('http://cometcache.com/changelog-lite/').'" target="_blank">'.__('changelog', 'comet-cache').'</a>)'."\n";
78
+ }
79
+ echo '</div>'."\n";
80
+ }
81
+ echo ' <img src="'.$this->plugin->url('/src/client-s/images/options-'.(IS_PRO ? 'pro' : 'lite').'.png').'" alt="'.esc_attr(__('Plugin Options', 'comet-cache')).'" />'."\n";
82
+
83
+ echo '</div>'."\n";
84
+
85
+ /* ----------------------------------------------------------------------------------------- */
86
+
87
+ echo '<hr />'."\n";
88
+
89
+ /* ----------------------------------------------------------------------------------------- */
90
+
91
+ if (!empty($_REQUEST[GLOBAL_NS.'_updated'])) {
92
+ echo '<div class="plugin-menu-page-notice notice">'."\n";
93
+ echo ' <i class="si si-thumbs-up"></i> '.__('Options updated successfully.', 'comet-cache')."\n";
94
+ echo '</div>'."\n";
95
+ }
96
+ if (!empty($_REQUEST[GLOBAL_NS.'_restored'])) {
97
+ echo '<div class="plugin-menu-page-notice notice">'."\n";
98
+ echo ' <i class="si si-thumbs-up"></i> '.__('Default options successfully restored.', 'comet-cache')."\n";
99
+ echo '</div>'."\n";
100
+ }
101
+ if (!empty($_REQUEST[GLOBAL_NS.'_cache_wiped'])) {
102
+ echo '<div class="plugin-menu-page-notice notice">'."\n";
103
+ echo ' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/wipe.png')).'" /> '.__('Cache wiped across all sites; recreation will occur automatically over time.', 'comet-cache')."\n";
104
+ echo '</div>'."\n";
105
+ }
106
+ if (!empty($_REQUEST[GLOBAL_NS.'_cache_cleared'])) {
107
+ echo '<div class="plugin-menu-page-notice notice">'."\n";
108
+ if (is_multisite() && is_main_site()) {
109
+ echo '<img src="'.esc_attr($this->plugin->url('/src/client-s/images/clear.png')).'" /> '.__('Cache cleared for main site; recreation will occur automatically over time.', 'comet-cache')."\n";
110
+ } else {
111
+ echo '<img src="'.esc_attr($this->plugin->url('/src/client-s/images/clear.png')).'" /> '.__('Cache cleared for this site; recreation will occur automatically over time.', 'comet-cache')."\n";
112
+ }
113
+ echo '</div>'."\n";
114
+ }
115
+ if (!empty($_REQUEST[GLOBAL_NS.'_wp_htaccess_add_failure'])) {
116
+ echo '<div class="plugin-menu-page-notice error">'."\n";
117
+ echo ' <i class="si si-thumbs-down"></i> '.sprintf(__('Failed to update your <code>/.htaccess</code> file automatically. Most likely a permissions error. Please make sure it has permissions <code>644</code> or higher (perhaps <code>666</code>). Once you\'ve done this, please try saving the %1$s options again.', 'comet-cache'), esc_html(NAME))."\n";
118
+ echo '</div>'."\n";
119
+ }
120
+ if (!empty($_REQUEST[GLOBAL_NS.'_wp_htaccess_remove_failure'])) {
121
+ echo '<div class="plugin-menu-page-notice error">'."\n";
122
+ echo ' <i class="si si-thumbs-down"></i> '.sprintf(__('Failed to update your <code>/.htaccess</code> file automatically. Most likely a permissions error. Please make sure it has permissions <code>644</code> or higher (perhaps <code>666</code>). Once you\'ve done this, please try saving the %1$s options again.', 'comet-cache'), esc_html(NAME))."\n";
123
+ echo '</div>'."\n";
124
+ }
125
+ if (!empty($_REQUEST[GLOBAL_NS.'_wp_htaccess_nginx_notice'])) {
126
+ echo '<div class="plugin-menu-page-notice error">'."\n";
127
+ echo ' <i class="si si-thumbs-down"></i> '.__('It appears that your server is running NGINX and does not support <code>.htaccess</code> rules. Please <a href="http://cometcache.com/r/kb-article-recommended-nginx-server-configuration/" target="_new">update your server configuration manually</a>. If you\'ve already updated your NGINX configuration, you can safely <a href="http://cometcache.com/r/kb-article-how-do-i-disable-the-nginx-htaccess-notice/" target="_new">ignore this message</a>.', 'comet-cache')."\n";
128
+ echo '</div>'."\n";
129
+ }
130
+ if (!empty($_REQUEST[GLOBAL_NS.'_wp_config_wp_cache_add_failure'])) {
131
+ echo '<div class="plugin-menu-page-notice error">'."\n";
132
+ echo ' <i class="si si-thumbs-down"></i> '.__('Failed to update your <code>/wp-config.php</code> file automatically. Please add the following line to your <code>/wp-config.php</code> file (right after the opening <code>&lt;?php</code> tag; on it\'s own line). <pre class="code"><code>&lt;?php<br />define(\'WP_CACHE\', TRUE);</code></pre>', 'comet-cache')."\n";
133
+ echo '</div>'."\n";
134
+ }
135
+ if (!empty($_REQUEST[GLOBAL_NS.'_wp_config_wp_cache_remove_failure'])) {
136
+ echo '<div class="plugin-menu-page-notice error">'."\n";
137
+ echo ' <i class="si si-thumbs-down"></i> '.__('Failed to update your <code>/wp-config.php</code> file automatically. Please remove the following line from your <code>/wp-config.php</code> file, or set <code>WP_CACHE</code> to a <code>FALSE</code> value. <pre class="code"><code>define(\'WP_CACHE\', TRUE);</code></pre>', 'comet-cache')."\n";
138
+ echo '</div>'."\n";
139
+ }
140
+ if (!empty($_REQUEST[GLOBAL_NS.'_advanced_cache_add_failure'])) {
141
+ echo '<div class="plugin-menu-page-notice error">'."\n";
142
+ if ($_REQUEST[GLOBAL_NS.'_advanced_cache_add_failure'] === 'advanced-cache') {
143
+ echo '<i class="si si-thumbs-down"></i> '.sprintf(__('Failed to update your <code>/wp-content/advanced-cache.php</code> file. Cannot write stat file: <code>%1$s/%2$s-advanced-cache</code>. Please be sure this directory exists (and that it\'s writable): <code>%1$s</code>. Please use directory permissions <code>755</code> or higher (perhaps <code>777</code>). Once you\'ve done this, please try again.', 'comet-cache'), esc_html($this->plugin->cacheDir()), esc_html(strtolower(SHORT_NAME)))."\n";
144
+ } else {
145
+ echo '<i class="si si-thumbs-down"></i> '.__('Failed to update your <code>/wp-content/advanced-cache.php</code> file. Most likely a permissions error. Please create an empty file here: <code>/wp-content/advanced-cache.php</code> (just an empty PHP file, with nothing in it); give it permissions <code>644</code> or higher (perhaps <code>666</code>). Once you\'ve done this, please try again.', 'comet-cache')."\n";
146
+ }
147
+ echo '</div>'."\n";
148
+ }
149
+ if (!empty($_REQUEST[GLOBAL_NS.'_advanced_cache_remove_failure'])) {
150
+ echo '<div class="plugin-menu-page-notice error">'."\n";
151
+ echo ' <i class="si si-thumbs-down"></i> '.__('Failed to remove your <code>/wp-content/advanced-cache.php</code> file. Most likely a permissions error. Please delete (or empty the contents of) this file: <code>/wp-content/advanced-cache.php</code>.', 'comet-cache')."\n";
152
+ echo '</div>'."\n";
153
+ }
154
+ if (!IS_PRO && $this->plugin->isProPreview()) {
155
+ echo '<div class="plugin-menu-page-notice info">'."\n";
156
+ echo '<a href="'.add_query_arg(urlencode_deep(array('page' => GLOBAL_NS)), self_admin_url('/admin.php')).'" class="pull-right" style="margin:0 0 15px 25px; float:right; font-variant:small-caps; text-decoration:none;">'.__('close', 'comet-cache').' <i class="si si-eye-slash"></i></a>'."\n";
157
+ echo ' <i class="si si-eye"></i> '.sprintf(__('<strong>Pro Features (Preview)</strong> ~ New option panels below. Please explore before <a href="http://cometcache.com/prices/" target="_blank">upgrading <i class="si si-heart-o"></i></a>.<br /><small>NOTE: the free version of %1$s (this lite version) is more-than-adequate for most sites. Please upgrade only if you desire advanced features or would like to support the developer.</small>', 'comet-cache'), esc_html(NAME))."\n";
158
+ echo '</div>'."\n";
159
+ }
160
+ if (!$this->plugin->options['enable']) {
161
+ echo '<div class="plugin-menu-page-notice warning">'."\n";
162
+ echo ' <i class="si si-warning"></i> '.sprintf(__('%1$s is currently disabled; please review options below.', 'comet-cache'), esc_html(NAME))."\n";
163
+ echo '</div>'."\n";
164
+ }
165
+ /* ----------------------------------------------------------------------------------------- */
166
+
167
+ echo '<div class="plugin-menu-page-body">'."\n";
168
+
169
+ /* ----------------------------------------------------------------------------------------- */
170
+
171
+ echo '<h2 class="plugin-menu-page-section-heading">'.
172
+ ' '.__('Basic Configuration (Required)', 'comet-cache').
173
+ ' <small><span>'.sprintf(__('Review these basic options and %1$s&trade; will be ready-to-go!', 'comet-cache'), esc_html(NAME)).'</span></small>'.
174
+ '</h2>';
175
+
176
+ /* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
177
+
178
+ echo '<div class="plugin-menu-page-panel">'."\n";
179
+
180
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.((!$this->plugin->options['enable']) ? ' open' : '').'">'."\n";
181
+ echo ' <i class="si si-enty-gauge"></i> '.__('Enable/Disable', 'comet-cache')."\n";
182
+ echo ' </a>'."\n";
183
+
184
+ echo ' <div class="plugin-menu-page-panel-body'.((!$this->plugin->options['enable']) ? ' open' : '').' clearfix">'."\n";
185
+ echo ' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/tach.png')).'" style="float:right; width:100px; margin-left:1em;" />'."\n";
186
+ echo ' <p style="float:right; font-size:120%; font-weight:bold;">'.sprintf(__('%1$s&trade; = SPEED<em>!!</em>', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
187
+ echo ' <p><label class="switch-primary"><input type="radio" name="'.esc_attr(GLOBAL_NS).'[saveOptions][enable]" value="1"'.checked($this->plugin->options['enable'], '1', false).' /> '.sprintf(__('Yes, enable %1$s&trade;', 'comet-cache'), esc_html(NAME)).' <i class="si si-magic si-flip-horizontal"></i></label> &nbsp;&nbsp;&nbsp; <label><input type="radio" name="'.esc_attr(GLOBAL_NS).'[saveOptions][enable]" value="0"'.checked($this->plugin->options['enable'], '0', false).' /> '.__('No, disable.', 'comet-cache').'</label></p>'."\n";
188
+ echo ' <p class="info" style="font-family:\'Georgia\', serif; font-size:110%; margin-top:1.5em;">'.sprintf(__('<strong>HUGE Time-Saver:</strong> Approx. 95%% of all WordPress sites running %1$s, simply enable it here; and that\'s it :-) <strong>No further configuration is necessary (really).</strong> All of the other options (down below) are already tuned for the BEST performance on a typical WordPress installation. Simply enable %1$s here and click "Save All Changes". If you get any warnings please follow the instructions given. Otherwise, you\'re good <i class="si si-smile-o"></i>. This plugin is designed to run just fine like it is. Take it for a spin right away; you can always fine-tune things later if you deem necessary.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
189
+ echo ' <hr />'."\n";
190
+ echo ' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/source-code-ss.png')).'" class="screenshot" />'."\n";
191
+ echo ' <h3>'.sprintf(__('How Can I Tell %1$s is Working?', 'comet-cache'), esc_html(NAME)).'</h3>'."\n";
192
+ echo ' <p>'.sprintf(__('First of all, please make sure that you\'ve enabled %1$s here; then scroll down to the bottom of this page and click "Save All Changes". All of the other options (below) are already pre-configured for typical usage. Feel free to skip them all for now. You can go back through all of these later and fine-tune things the way you like them.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
193
+ echo ' <p>'.sprintf(__('Once %1$s has been enabled, <strong>you\'ll need to log out (and/or clear browser cookies)</strong>. By default, cache files are NOT served to visitors who are logged-in, and that includes you too ;-) Cache files are NOT served to recent comment authors either. If you\'ve commented (or replied to a comment lately); please clear your browser cookies before testing.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
194
+ echo ' <p>'.sprintf(__('<strong>To verify that %1$s is working</strong>, navigate your site like a normal visitor would. Right-click on any page (choose View Source), then scroll to the very bottom of the document. At the bottom, you\'ll find comments that show %1$s stats and information. You should also notice that page-to-page navigation is <i class="si si-flash"></i> <strong>lightning fast</strong> now that %1$s is running; and it gets faster over time!', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
195
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][debugging_enable]" class="-no-if-enabled">'."\n";
196
+ echo ' <option value="1"'.selected($this->plugin->options['debugging_enable'], '1', false).'>'.__('Yes, enable notes in the source code so I can see it\'s working (recommended).', 'comet-cache').'</option>'."\n";
197
+ echo ' <option value="2"'.selected($this->plugin->options['debugging_enable'], '2', false).'>'.__('Yes, enable notes in the source code AND show debugging details (not recommended for production).', 'comet-cache').'</option>'."\n";
198
+ echo ' <option value="0"'.selected($this->plugin->options['debugging_enable'], '0', false).'>'.__('No, I don\'t want my source code to contain any of these notes.', 'comet-cache').'</option>'."\n";
199
+ echo ' </select></p>'."\n";
200
+ echo ' </div>'."\n";
201
+
202
+ echo '</div>'."\n";
203
+
204
+ /* ----------------------------------------------------------------------------------------- */
205
+
206
+ echo '<div class="plugin-menu-page-panel">'."\n";
207
+
208
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
209
+ echo ' <i class="si si-shield"></i> '.__('Plugin Deletion Safeguards', 'comet-cache')."\n";
210
+ echo ' </a>'."\n";
211
+
212
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
213
+ echo ' <i class="si si-shield si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
214
+ echo ' <h3>'.__('Uninstall on Plugin Deletion; or Safeguard Options?', 'comet-cache').'</h3>'."\n";
215
+ echo ' <p>'.sprintf(__('<strong>Tip:</strong> By default, if you delete %1$s using the plugins menu in WordPress, nothing is lost. However, if you want to completely uninstall %1$s you should set this to <code>Yes</code> and <strong>THEN</strong> deactivate &amp; delete %1$s from the plugins menu in WordPress. This way %1$s will erase your options for the plugin, erase directories/files created by the plugin, remove the <code>advanced-cache.php</code> file, terminate CRON jobs, etc. It erases itself from existence completely.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
216
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][uninstall_on_deletion]">'."\n";
217
+ echo ' <option value="0"'.selected($this->plugin->options['uninstall_on_deletion'], '0', false).'>'.__('Safeguard my options and the cache (recommended).', 'comet-cache').'</option>'."\n";
218
+ echo ' <option value="1"'.selected($this->plugin->options['uninstall_on_deletion'], '1', false).'>'.sprintf(__('Yes, uninstall (completely erase) %1$s on plugin deletion.', 'comet-cache'), esc_html(NAME)).'</option>'."\n";
219
+ echo ' </select></p>'."\n";
220
+ echo ' </div>'."\n";
221
+
222
+ echo '</div>'."\n";
223
+
224
+ /* ----------------------------------------------------------------------------------------- */
225
+
226
+ echo '<h2 class="plugin-menu-page-section-heading">'.
227
+ ' '.__('Advanced Configuration (All Optional)', 'comet-cache').
228
+ ' <small>'.__('Recommended for advanced site owners only; already pre-configured for most WP installs.', 'comet-cache').'</small>'.
229
+ '</h2>';
230
+
231
+ /* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
232
+
233
+ if (IS_PRO || $this->plugin->isProPreview()) {
234
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO ? ' pro-preview' : '').'">'."\n";
235
+
236
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
237
+ echo ' <i class="si si-broom"></i> '.__('Manual Cache Clearing', 'comet-cache')."\n";
238
+ echo ' </a>'."\n";
239
+
240
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
241
+ echo ' <h3>'.__('Clearing the Cache Manually', 'comet-cache').'</h3>'."\n";
242
+ echo ' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/clear-cache-ops1-ss.png')).'" class="-clear-cache-ops-ss screenshot" />'."\n";
243
+ echo ' <p>'.sprintf(__('Once %1$s is enabled, you will find this new option in your WordPress Admin Bar (screenshot on right). Clicking this button will clear the cache and you can start fresh at anytime (e.g., you can do this manually; and as often as you wish).', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
244
+ echo ' <p>'.sprintf(__('Depending on the structure of your site, there could be many reasons to clear the cache. However, the most common reasons are related to Post/Page edits or deletions, Category/Tag edits or deletions, and Theme changes. %1$s handles most scenarios all by itself. However, many site owners like to clear the cache manually; for a variety of reasons (just to force a refresh).', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
245
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_admin_bar_enable]" data-target=".-cache-clear-admin-bar-options, .-cache-clear-admin-bar-roles-caps" style="width:auto;">'."\n";
246
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_admin_bar_enable'], '1', false).'>'.__('Yes, enable &quot;Clear Cache&quot; button in admin bar', 'comet-cache').'</option>'."\n";
247
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_admin_bar_enable'], '0', false).'>'.__('No, I don\'t intend to clear the cache manually.', 'comet-cache').'</option>'."\n";
248
+ echo ' </select>'."\n";
249
+ echo ' <span class="plugin-menu-page-panel-if-enabled -cache-clear-admin-bar-options"><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_admin_bar_options_enable]" class="-no-if-enabled" style="width:auto;">'."\n";
250
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_admin_bar_options_enable'], '1', false).'>'.__('w/ dropdown options.', 'comet-cache').'</option>'."\n";
251
+ echo ' <option value="2"'.selected($this->plugin->options['cache_clear_admin_bar_options_enable'], '2', false).'>'.__('w/ dropdown options in split menu.', 'comet-cache').'</option>'."\n";
252
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_admin_bar_options_enable'], '0', false).'>'.__('w/o dropdown options.', 'comet-cache').'</option>'."\n";
253
+ echo ' </select></span></p>'."\n";
254
+
255
+ if (is_multisite()) {
256
+ echo ' <div class="plugin-menu-page-panel-if-enabled -cache-clear-admin-bar-roles-caps">'."\n";
257
+ echo ' <h4 style="margin-bottom:0;">'.__('Also allow Child Sites in a Network to clear the cache from their Admin Bar?', 'comet-cache').'</h4>'."\n";
258
+ echo ' <p style="margin-top:2px;">'.sprintf(__('In a Multisite Network, each child site can clear its own cache. If you want child sites to see the "Clear Cache" button in their WordPress Admin Bar, you can specify a comma-delimited list of <a href="http://cometcache.com/r/wp-roles-caps/" target="_blank">Roles and/or Capabilities</a> that are allowed. For example, if I want Administrators to be capable of clearing the cache from their Admin Bar, I could enter <code>administrator</code> here. If I also want to allow Editors, I can use a comma-delimited list: <code>administrator,editor</code>. Or, I could use a single Capability of: <code>edit_others_posts</code>; which covers both Administrators &amp; Editors at the same time.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
259
+ echo ' <p style="margin-bottom:0;"><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_admin_bar_roles_caps]" value="'.esc_attr($this->plugin->options['cache_clear_admin_bar_roles_caps']).'" /></p>'."\n";
260
+ echo ' <p style="margin-top:0;">'.sprintf(__('<strong>Note:</strong> As a security measure, in addition to the Role(s) and/or Capabilities that you list here, each child site owner must also have the ability to <code>%1$s</code>.', 'comet-cache'), esc_html(IS_PRO ? $this->plugin->clear_min_cap : 'edit_posts')).'</p>'."\n";
261
+ echo ' </select></p>'."\n";
262
+ echo ' </div>'."\n";
263
+ } else {
264
+ echo ' <div class="plugin-menu-page-panel-if-enabled -cache-clear-admin-bar-roles-caps">'."\n";
265
+ echo ' <h4 style="margin-bottom:0;">'.__('Also allow others to clear the cache from their Admin Bar?', 'comet-cache').'</h4>'."\n";
266
+ echo ' <p style="margin-top:2px;">'.sprintf(__('If you want others to see the "Clear Cache" button in their WordPress Admin Bar, you can specify a comma-delimited list of <a href="http://cometcache.com/r/wp-roles-caps/" target="_blank">Roles and/or Capabilities</a> that are allowed. For example, if I want Editors to be capable of clearing the cache from their Admin Bar, I could enter <code>editor</code> here. If I also want to allow Authors, I can use a comma-delimited list: <code>editor,author</code>. Or, I could use a single Capability of: <code>publish_posts</code>; which covers both Editors &amp; Authors at the same time.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
267
+ echo ' <p style="margin-bottom:0;"><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_admin_bar_roles_caps]" value="'.esc_attr($this->plugin->options['cache_clear_admin_bar_roles_caps']).'" /></p>'."\n";
268
+ echo ' <p style="margin-top:0;">'.sprintf(__('<strong>Note:</strong> As a security measure, in addition to the Role(s) and/or Capabilities that you list here, each user must also have the ability to <code>%1$s</code>.', 'comet-cache'), esc_html(IS_PRO ? $this->plugin->clear_min_cap : 'edit_posts')).'</p>'."\n";
269
+ echo ' </select></p>'."\n";
270
+ echo ' </div>'."\n";
271
+ }
272
+ if ($this->plugin->functionIsPossible('opcache_reset')) {
273
+ echo ' <hr />'."\n";
274
+ echo ' <h3>'.__('Clear the <a href="http://cometcache.com/r/php-opcache/" target="_blank">PHP OPcache</a> Too?', 'comet-cache').'</h3>'."\n";
275
+ echo ' <p>'.sprintf(__('If you clear the cache manually, do you want %1$s to clear the PHP OPcache too? This is not necessary, but if you want a truly clean start, this will clear all PHP files in the server\'s opcode cache also. Note: If you don\'t already know what the PHP OPcache is, it is suggested that you leave this disabled. It really is not necessary. This is just an added feature for advanced users.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
276
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_opcache_enable]" class="-no-if-enabled">'."\n";
277
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_opcache_enable'], '0', false).'>'.__('No, I don\'t use the PHP OPcache extension; or, I don\'t want the opcode cache cleared.', 'comet-cache').'</option>'."\n";
278
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_opcache_enable'], '1', false).'>'.__('Yes, if the PHP OPcache extension is enabled, also clear the entire PHP opcode cache.', 'comet-cache').'</option>'."\n";
279
+ echo ' </select></p>'."\n";
280
+ }
281
+ if ($this->plugin->functionIsPossible('s2clean')) {
282
+ echo ' <hr />'."\n";
283
+ echo ' <h3>'.__('Clear the <a href="http://websharks-inc.com/product/s2clean/" target="_blank">s2Clean</a> Cache Too?', 'comet-cache').'</h3>'."\n";
284
+ echo ' <p>'.sprintf(__('If the s2Clean theme is installed, and you clear the cache manually, %1$s can clear the s2Clean Markdown cache too (if you\'ve enabled Markdown processing with s2Clean).', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
285
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_s2clean_enable]" class="-no-if-enabled">'."\n";
286
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_s2clean_enable'], '1', false).'>'.__('Yes, if the s2Clean theme is installed, also clear s2Clean-related caches.', 'comet-cache').'</option>'."\n";
287
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_s2clean_enable'], '0', false).'>'.__('No, I don\'t use s2Clean; or, I don\'t want s2Clean-related caches cleared.', 'comet-cache').'</option>'."\n";
288
+ echo ' </select></p>'."\n";
289
+ }
290
+ echo ' <hr />'."\n";
291
+ echo ' <h3>'.__('Evaluate Custom PHP Code when Clearing the Cache?', 'comet-cache').'</h3>'."\n";
292
+ echo ' <p>'.sprintf(__('If you have any custom routines you\'d like to process when the cache is cleared manually, please enter PHP code here. If your PHP code outputs a message, it will be displayed along with any other notes from %1$s itself. This feature is intended for developers, and it may come in handy if you need to clear any system caches not already covered by %1$s configuration options.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
293
+ echo ' <p style="margin-bottom:0;"><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_eval_code]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['cache_clear_eval_code']).'</textarea></p>'."\n";
294
+ echo ' <p class="info" style="margin-top:0;">'.__('<strong>Example:</strong> <code>&lt;?php apc_clear_cache(); echo \'&lt;p&gt;Also cleared APC cache.&lt;/p&gt;\'; ?&gt;</code>', 'comet-cache').'</p>'."\n";
295
+
296
+ echo ' <hr />'."\n";
297
+ echo ' <h3>'.__('Clear the CDN Cache Too?', 'comet-cache').'</h3>'."\n";
298
+ echo ' <p>'.sprintf(__('If you clear the cache manually, do you want %1$s to automatically bump the CDN invalidation counter too? i.e., automatically increment the <code>?%2$s=[counter]</code> in all static CDN URLs?', 'comet-cache'), esc_html(NAME), esc_html($this->plugin->options['cdn_invalidation_var'])).'</p>'."\n";
299
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_cdn_enable]" class="-no-if-enabled">'."\n";
300
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_cdn_enable'], '0', false).'>'.__('No, I don\'t use Static CDN Filters; or, I don\'t want the CDN cache cleared.', 'comet-cache').'</option>'."\n";
301
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_cdn_enable'], '1', false).'>'.__('Yes, if Static CDN Filters are enabled, also clear the CDN cache.', 'comet-cache').'</option>'."\n";
302
+ echo ' </select></p>'."\n";
303
+ echo ' </div>'."\n";
304
+
305
+ echo '</div>'."\n";
306
+ }
307
+ /* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
308
+
309
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO && $this->plugin->isProPreview() ? ' pro-preview' : '').'">'."\n";
310
+
311
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.((!IS_PRO && $this->plugin->isProPreview()) ? ' pro-preview-additional-features' : '').'">'."\n";
312
+ echo ' <i class="si si-server"></i> '.__('Automatic Cache Clearing', 'comet-cache')."\n";
313
+ echo ' </a>'."\n";
314
+
315
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
316
+
317
+ echo ' <h3>'.__('Clearing the Cache Automatically', 'comet-cache').'</h3>'."\n";
318
+ echo ' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/auto-clear-ss.png')).'" class="screenshot" />'."\n";
319
+ echo ' <p>'.sprintf(__('This is built into the %1$s plugin; i.e., this functionality is "always on". If you edit a Post/Page (or delete one), %1$s will automatically clear the cache file(s) associated with that content. This way a new updated version of the cache will be created automatically the next time this content is accessed. Simple updates like this occur each time you make changes in the Dashboard, and %1$s will notify you of these as they occur. %1$s monitors changes to Posts (of any kind, including Pages), Categories, Tags, Links, Themes (even Users), and more.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
320
+ if (IS_PRO || $this->plugin->isProPreview()) {
321
+ echo ' <div class="'.(!IS_PRO ? 'pro-preview-feature' : '').'">'."\n";
322
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][change_notifications_enable]" class="-no-if-enabled" style="width:auto;">'."\n";
323
+ echo ' <option value="1"'.selected($this->plugin->options['change_notifications_enable'], '1', false).'>'.sprintf(__('Yes, enable %1$s notifications in the Dashboard when changes are detected &amp; one or more cache files are cleared automatically.', 'comet-cache'), esc_html(NAME)).'</option>'."\n";
324
+ echo ' <option value="0"'.selected($this->plugin->options['change_notifications_enable'], '0', false).'>'.sprintf(__('No, I don\'t want to know (don\'t really care) what %1$s is doing behind-the-scene.', 'comet-cache'), esc_html(NAME)).'</option>'."\n";
325
+ echo ' </select></p>'."\n";
326
+ echo ' </div>'."\n";
327
+ }
328
+ echo ' <hr />'."\n";
329
+ echo ' <h3>'.__('Primary Page Options', 'comet-cache').'</h3>'."\n";
330
+
331
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear Designated "Home Page" Too?', 'comet-cache').'</h4>'."\n";
332
+ echo ' <p style="margin-top:2px;">'.sprintf(__('On many sites, the Home Page (aka: the Front Page) offers an archive view of all Posts (or even Pages). Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the "Home Page"?', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
333
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_home_page_enable]" class="-no-if-enabled">'."\n";
334
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_home_page_enable'], '1', false).'>'.__('Yes, if any single Post/Page is cleared/reset; also clear the "Home Page".', 'comet-cache').'</option>'."\n";
335
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_home_page_enable'], '0', false).'>'.__('No, my Home Page does not provide a list of Posts/Pages; e.g., this is not necessary.', 'comet-cache').'</option>'."\n";
336
+ echo ' </select></p>'."\n";
337
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear Designated "Posts Page" Too?', 'comet-cache').'</h4>'."\n";
338
+ echo ' <p style="margin-top:2px;">'.sprintf(__('On many sites, the Posts Page (aka: the Blog Page) offers an archive view of all Posts (or even Pages). Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the "Posts Page"?', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
339
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_posts_page_enable]" class="-no-if-enabled">'."\n";
340
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_posts_page_enable'], '1', false).'>'.__('Yes, if any single Post/Page is cleared/reset; also clear the "Posts Page".', 'comet-cache').'</option>'."\n";
341
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_posts_page_enable'], '0', false).'>'.__('No, I don\'t use a separate Posts Page; e.g., my Home Page IS my Posts Page.', 'comet-cache').'</option>'."\n";
342
+ echo ' </select></p>'."\n";
343
+
344
+ echo ' <hr />'."\n";
345
+ echo ' <h3>'.__('Author, Archive, and Tag/Term Options', 'comet-cache').'</h3>'."\n";
346
+
347
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear "Author Page" Too?', 'comet-cache').'</h4>'."\n";
348
+ echo ' <p style="margin-top:2px;">'.sprintf(__('On many sites, each author has a related "Author Page" that offers an archive view of all posts associated with that author. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the related "Author Page"?', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
349
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_author_page_enable]" class="-no-if-enabled">'."\n";
350
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_author_page_enable'], '1', false).'>'.__('Yes, if any single Post/Page is cleared/reset; also clear the "Author Page".', 'comet-cache').'</option>'."\n";
351
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_author_page_enable'], '0', false).'>'.__('No, my site doesn\'t use multiple authors and/or I don\'t have any "Author Page" archive views.', 'comet-cache').'</option>'."\n";
352
+ echo ' </select></p>'."\n";
353
+
354
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear "Category Archives" Too?', 'comet-cache').'</h4>'."\n";
355
+ echo ' <p style="margin-top:2px;">'.sprintf(__('On many sites, each post is associated with at least one Category. Each category then has an archive view that contains all the posts within that category. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Category archive views?', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
356
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_term_category_enable]" class="-no-if-enabled">'."\n";
357
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_term_category_enable'], '1', false).'>'.__('Yes, if any single Post/Page is cleared/reset; also clear the associated Category archive views.', 'comet-cache').'</option>'."\n";
358
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_term_category_enable'], '0', false).'>'.__('No, my site doesn\'t use Categories and/or I don\'t have any Category archive views.', 'comet-cache').'</option>'."\n";
359
+ echo ' </select></p>'."\n";
360
+
361
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear "Tag Archives" Too?', 'comet-cache').'</h4>'."\n";
362
+ echo ' <p style="margin-top:2px;">'.sprintf(__('On many sites, each post may be associated with at least one Tag. Each tag then has an archive view that contains all the posts assigned that tag. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Tag archive views?', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
363
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_term_post_tag_enable]" class="-no-if-enabled">'."\n";
364
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_term_post_tag_enable'], '1', false).'>'.__('Yes, if any single Post/Page is cleared/reset; also clear the associated Tag archive views.', 'comet-cache').'</option>'."\n";
365
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_term_post_tag_enable'], '0', false).'>'.__('No, my site doesn\'t use Tags and/or I don\'t have any Tag archive views.', 'comet-cache').'</option>'."\n";
366
+ echo ' </select></p>'."\n";
367
+
368
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear "Custom Term Archives" Too?', 'comet-cache').'</h4>'."\n";
369
+ echo ' <p style="margin-top:2px;">'.sprintf(__('Most sites do not use any custom Terms so it should be safe to leave this disabled. However, if your site uses custom Terms and they have their own Term archive views, you may want to clear those when the associated post is cleared. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Tag archive views?', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
370
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_term_other_enable]" class="-no-if-enabled">'."\n";
371
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_term_other_enable'], '1', false).'>'.__('Yes, if any single Post/Page is cleared/reset; also clear any associated custom Term archive views.', 'comet-cache').'</option>'."\n";
372
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_term_other_enable'], '0', false).'>'.__('No, my site doesn\'t use any custom Terms and/or I don\'t have any custom Term archive views.', 'comet-cache').'</option>'."\n";
373
+ echo ' </select></p>'."\n";
374
+
375
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear "Custom Post Type Archives" Too?', 'comet-cache').'</h4>'."\n";
376
+ echo ' <p style="margin-top:2px;">'.sprintf(__('Most sites do not use any Custom Post Types so it should be safe to disable this option. However, if your site uses Custom Post Types and they have their own Custom Post Type archive views, you may want to clear those when any associated post is cleared. Therefore, if a single Post with a Custom Post Type is changed in some way; and %1$s clears/resets the cache for that post, would you like %1$s to also clear any existing cache files for the associated Custom Post Type archive views?', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
377
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_custom_post_type_enable]" class="-no-if-enabled">'."\n";
378
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_custom_post_type_enable'], '1', false).'>'.__('Yes, if any single Post with a Custom Post Type is cleared/reset; also clear any associated Custom Post Type archive views.', 'comet-cache').'</option>'."\n";
379
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_custom_post_type_enable'], '0', false).'>'.__('No, my site doesn\'t use any Custom Post Types and/or I don\'t have any Custom Post Type archive views.', 'comet-cache').'</option>'."\n";
380
+ echo ' </select></p>'."\n";
381
+
382
+ echo ' <hr />'."\n";
383
+ echo ' <h3>'.__('Feed-Related Options', 'comet-cache').'</h3>'."\n";
384
+
385
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear "RSS/RDF/ATOM Feeds" Too?', 'comet-cache').'</h4>'."\n";
386
+ echo ' <p style="margin-top:2px;">'.sprintf(__('If you enable Feed Caching (below), this can be quite handy. If enabled, when you update a Post/Page, approve a Comment, or make other changes where %1$s can detect that certain types of Feeds should be cleared to keep your site up-to-date, then %1$s will do this for you automatically. For instance, the blog\'s master feed, the blog\'s master comments feed, feeds associated with comments on a Post/Page, term-related feeds (including mixed term-related feeds), author-related feeds, etc. Under various circumstances (i.e., as you work in the Dashboard) these can be cleared automatically to keep your site up-to-date.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
387
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_xml_feeds_enable]" class="-no-if-enabled">'."\n";
388
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_xml_feeds_enable'], '1', false).'>'.__('Yes, automatically clear RSS/RDF/ATOM Feeds from the cache when certain changes occur.', 'comet-cache').'</option>'."\n";
389
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_xml_feeds_enable'], '0', false).'>'.__('No, I don\'t have Feed Caching enabled, or I prefer not to automatically clear Feeds.', 'comet-cache').'</option>'."\n";
390
+ echo ' </select></p>'."\n";
391
+
392
+ echo ' <hr />'."\n";
393
+ echo ' <h3>'.__('Sitemap-Related Options', 'comet-cache').'</h3>'."\n";
394
+
395
+ echo ' <h4 style="margin-bottom:0;">'.__('Auto-Clear "XML Sitemaps" Too?', 'comet-cache').'</h4>'."\n";
396
+ echo ' <p style="margin-top:2px;">'.sprintf(__('If you\'re generating XML Sitemaps with a plugin like <a href="http://wordpress.org/plugins/google-sitemap-generator/" target="_blank">Google XML Sitemaps</a>, you can tell %1$s to automatically clear the cache of any XML Sitemaps whenever it clears a Post/Page. Note: This does NOT clear the XML Sitemap itself of course, only the cache. The point being, to clear the cache and allow changes to a Post/Page to be reflected by a fresh copy of your XML Sitemap; sooner rather than later.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
397
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_xml_sitemaps_enable]" data-target=".-cache-clear-xml-sitemap-patterns">'."\n";
398
+ echo ' <option value="1"'.selected($this->plugin->options['cache_clear_xml_sitemaps_enable'], '1', false).'>'.__('Yes, if any single Post/Page is cleared/reset; also clear the cache for any XML Sitemaps.', 'comet-cache').'</option>'."\n";
399
+ echo ' <option value="0"'.selected($this->plugin->options['cache_clear_xml_sitemaps_enable'], '0', false).'>'.__('No, my site doesn\'t use any XML Sitemaps and/or I prefer NOT to clear the cache for XML Sitemaps.', 'comet-cache').'</option>'."\n";
400
+ echo ' </select></p>'."\n";
401
+ echo ' <div class="plugin-menu-page-panel-if-enabled -cache-clear-xml-sitemap-patterns">'."\n";
402
+ echo ' <p>'.__('<strong style="font-size:110%;">XML Sitemap Patterns...</strong> A default value of <code>/sitemap**.xml</code> covers all XML Sitemaps for most installations. However, you may customize this further if you deem necessary. <strong>Please list one pattern per line.</strong> These XML Sitemap Pattern searches are performed against the <a href="https://gist.github.com/jaswsinc/338b6eb03a36c048c26f" target="_blank">REQUEST_URI</a>. A wildcard <code>**</code> character can also be used when necessary; e.g., <code>/sitemap**.xml</code> (where <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes). Other special characters include: <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache').'</p>'."\n";
403
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_xml_sitemap_patterns]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['cache_clear_xml_sitemap_patterns']).'</textarea></p>'."\n";
404
+ if (is_multisite()) {
405
+ echo ' <p class="info" style="display:block; margin-top:-15px;">'.__('In a Multisite Network, each child blog (whether it be a sub-domain, a sub-directory, or a mapped domain); will automatically change the leading <code>http://[sub.]domain/[sub-directory]</code> used in pattern matching. In short, there is no need to add sub-domains or sub-directories for each child blog in these patterns. Please include only the <a href="https://gist.github.com/jaswsinc/338b6eb03a36c048c26f" target="_blank">REQUEST_URI</a> (i.e., the path) which leads to the XML Sitemap on all child blogs in the network.', 'comet-cache').'</p>'."\n";
406
+ }
407
+ echo ' </div>'."\n";
408
+
409
+ if (IS_PRO || $this->plugin->isProPreview()) {
410
+ echo ' <hr />'."\n";
411
+ echo ' <h3 class="'.(!IS_PRO ? 'pro-preview-feature' : '').'">'.__('Misc. Auto-Clear Options', 'comet-cache').'</h3>'."\n";
412
+ echo '<h4 style="margin-bottom:0;">'.__('Auto-Clear a List of Custom URLs Too?', 'comet-cache').'</h4>'."\n";
413
+ echo '<p style="margin-top:2px;">'.sprintf(__('When you update a Post/Page, approve a Comment, or make other changes where %1$s can detect that a Post/Page cache should be cleared to keep your site up-to-date; then %1$s will also clear a list of custom URLs that you list here. <strong>Please list one URL per line.</strong> A wildcard <code>*</code> character can also be used when necessary; e.g., <code>/category/abc-followed-by-*</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href ="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
414
+ echo '<p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_clear_urls]" spellcheck="false" wrap="off" rows="5">'.format_to_edit($this->plugin->options['cache_clear_urls']).'</textarea></p>'."\n";
415
+ }
416
+ echo ' </div>'."\n";
417
+
418
+ echo '</div>'."\n";
419
+
420
+ /* ----------------------------------------------------------------------------------------- */
421
+
422
+ if (IS_PRO || $this->plugin->isProPreview()) {
423
+ echo '<div class="plugin-menu-page-panel">'."\n";
424
+
425
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.(!IS_PRO ? ' pro-preview-feature' : '').'">'."\n";
426
+ echo ' <i class="si si-pie-chart"></i> '.__('Cache-Related Statistics', 'comet-cache')."\n";
427
+ echo ' </a>'."\n";
428
+
429
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
430
+ echo ' <i class="si si-pie-chart si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
431
+ echo ' <h3>'.__('Enable Cache-Related Stats &amp; Charts?', 'comet-cache').'</h3>'."\n";
432
+ echo ' <p>'.sprintf(__('%1$s can collect and display cache-related statistics (including charts). Stats are displayed in the WordPress Admin Bar, and also in your Dashboard under: <strong>%1$s → Stats/Charts</strong>. Cache-related stats provide you with a quick look at what\'s happening behind-the-scenes. Your site grows faster and faster as the cache grows larger in size.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
433
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][stats_enable]" data-target=".-stats-admin-bar-enable">'."\n";
434
+ echo ' <option value="1"'.selected($this->plugin->options['stats_enable'], '1', false).'>'.__('Yes, enable stats collection &amp; the menu page in WordPress for viewing stats.', 'comet-cache').'</option>'."\n";
435
+ echo ' <option value="0"'.selected($this->plugin->options['stats_enable'], '0', false).'>'.__('No, I have a VERY large site and I want to avoid any unnecessary directory scans.', 'comet-cache').'</option>'."\n";
436
+ echo ' </select></p>'."\n";
437
+ echo ' <p class="info">'.sprintf(__('<strong>Note:</strong> %1$s does a great job of collecting stats, in ways that don\'t cause a performance issue. In addition, as your cache grows larger than several hundred files in total size, statistics are collected less often and at longer intervals. All of that being said, if you run a VERY large site (e.g., more than 20K posts), you might want to disable stats collection in favor of blazing fast speeds not impeded by any directory scans needed to collect stats.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
438
+ echo ' <hr />'."\n";
439
+
440
+ echo ' <div class="plugin-menu-page-panel-if-enabled -stats-admin-bar-enable">'."\n";
441
+ echo ' <h3>'.__('Show Stats in the WordPress Admin Bar?', 'comet-cache').'</h3>'."\n";
442
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][stats_admin_bar_enable]" data-target=".-stats-admin-bar-roles-caps">'."\n";
443
+ echo ' <option value="1"'.selected($this->plugin->options['stats_admin_bar_enable'], '1', false).'>'.__('Yes, enable stats in the WordPress admin bar.', 'comet-cache').'</option>'."\n";
444
+ echo ' <option value="0"'.selected($this->plugin->options['stats_admin_bar_enable'], '0', false).'>'.__('No, I\'ll review stats from the menu page in WordPress if I need to.', 'comet-cache').'</option>'."\n";
445
+ echo ' </select></p>'."\n";
446
+ if (is_multisite()) {
447
+ echo ' <div class="plugin-menu-page-panel-if-enabled -stats-admin-bar-roles-caps">'."\n";
448
+ echo ' <h4 style="margin-bottom:0;">'.__('Allow Child Sites in a Network to See Stats in Admin Bar?', 'comet-cache').'</h4>'."\n";
449
+ echo ' <p style="margin-top:2px;">'.sprintf(__('In a Multisite Network, each child site has stats of its own. If you want child sites to see cache-related stats in their WordPress Admin Bar, you can specify a comma-delimited list of <a href="http://cometcache.com/r/wp-roles-caps/" target="_blank">Roles and/or Capabilities</a> that are allowed to see stats. For example, if I want the Administrator to see stats in their Admin Bar, I could enter <code>administrator</code> here. If I also want to show stats to Editors, I can use a comma-delimited list: <code>administrator,editor</code>. Or, I could use a single Capability of: <code>edit_others_posts</code>; which covers both Administrators &amp; Editors at the same time.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
450
+ echo ' <p style="margin-bottom:0;"><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][stats_admin_bar_roles_caps]" value="'.esc_attr($this->plugin->options['stats_admin_bar_roles_caps']).'" /></p>'."\n";
451
+ echo ' <p style="margin-top:0;">'.sprintf(__('<strong>Note:</strong> As a security measure, in addition to the Role(s) and/or Capabilities that you list here, each child site owner must also have the ability to <code>%1$s</code>.', 'comet-cache'), esc_html(IS_PRO ? $this->plugin->stats_min_cap : 'edit_posts')).'</p>'."\n";
452
+ echo ' </select></p>'."\n";
453
+ echo ' </div>'."\n";
454
+ } else {
455
+ echo ' <div class="plugin-menu-page-panel-if-enabled -stats-admin-bar-roles-caps">'."\n";
456
+ echo ' <h4 style="margin-bottom:0;">'.__('Allow Others to See Stats in Admin Bar?', 'comet-cache').'</h4>'."\n";
457
+ echo ' <p style="margin-top:2px;">'.sprintf(__('If you want others to see cache-related stats in their WordPress Admin Bar, you can specify a comma-delimited list of <a href="http://cometcache.com/r/wp-roles-caps/" target="_blank">Roles and/or Capabilities</a> that are allowed to see stats. For example, if I want Editors to see stats in their Admin Bar, I could enter <code>editor</code> here. If I also want to show stats to Authors, I can use a comma-delimited list: <code>editor,author</code>. Or, I could use a single Capability of: <code>publish_posts</code>; which covers both Editors &amp; Authors at the same time.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
458
+ echo ' <p style="margin-bottom:0;"><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][stats_admin_bar_roles_caps]" value="'.esc_attr($this->plugin->options['stats_admin_bar_roles_caps']).'" /></p>'."\n";
459
+ echo ' <p style="margin-top:0;">'.sprintf(__('<strong>Note:</strong> As a security measure, in addition to the Role(s) and/or Capabilities that you list here, each user must also have the ability to <code>%1$s</code>.', 'comet-cache'), esc_html(IS_PRO ? $this->plugin->stats_min_cap : 'edit_posts')).'</p>'."\n";
460
+ echo ' </select></p>'."\n";
461
+ echo ' </div>'."\n";
462
+ }
463
+ echo ' </div>'."\n";
464
+ echo ' </div>'."\n";
465
+
466
+ echo '</div>'."\n";
467
+ }
468
+ /* ----------------------------------------------------------------------------------------- */
469
+
470
+ echo '<div class="plugin-menu-page-panel">'."\n";
471
+
472
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
473
+ echo ' <i class="si si-folder-open"></i> '.__('Cache Directory', 'comet-cache')."\n";
474
+ echo ' </a>'."\n";
475
+
476
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
477
+ echo ' <h3>'.__('Base Cache Directory (Must be Writable; i.e., <a href="http://cometcache.com/r/wp-file-permissions/" target="_blank">Permissions</a> <code>755</code> or Higher)', 'comet-cache').'</h3>'."\n";
478
+ echo ' <p>'.sprintf(__('This is where %1$s will store the cached version of your site. If you\'re not sure how to deal with directory permissions, don\'t worry too much about this. If there is a problem, %1$s will let you know about it. By default, this directory is created by %1$s and the permissions are setup automatically. In most cases there is nothing more you need to do.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
479
+ echo ' <table style="width:100%;"><tr><td style="width:1px; font-weight:bold; white-space:nowrap;">'.esc_html(WP_CONTENT_DIR).'/</td><td><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][base_dir]" value="'.esc_attr($this->plugin->options['base_dir']).'" /></td><td style="width:1px; font-weight:bold; white-space:nowrap;">/</td></tr></table>'."\n";
480
+ echo ' </div>'."\n";
481
+
482
+ echo '</div>'."\n";
483
+
484
+ /* ----------------------------------------------------------------------------------------- */
485
+
486
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO && $this->plugin->isProPreview() ? ' pro-preview' : '').'">'."\n";
487
+
488
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.((!IS_PRO && $this->plugin->isProPreview()) ? ' pro-preview-additional-features' : '').'">'."\n";
489
+ echo ' <i class="si si-clock-o"></i> '.__('Cache Expiration Time', 'comet-cache')."\n";
490
+ echo ' </a>'."\n";
491
+
492
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
493
+ echo ' <i class="si si-clock-o si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
494
+ echo ' <h3>'.__('Automatic Expiration Time (Max Age)', 'comet-cache').'</h3>'."\n";
495
+ echo ' <p>'.__('If you don\'t update your site much, you could set this to <code>6 months</code> and optimize everything even further. The longer the Cache Expiration Time is, the greater your performance gain. Alternatively, the shorter the Expiration Time, the fresher everything will remain on your site. A default value of <code>7 days</code> (recommended); is a good conservative middle-ground.', 'comet-cache').'</p>'."\n";
496
+ echo ' <p>'.sprintf(__('Keep in mind that your Expiration Time is only one part of the big picture. %1$s will also clear the cache automatically as changes are made to the site (i.e., you edit a post, someone comments on a post, you change your theme, you add a new navigation menu item, etc., etc.). Thus, your Expiration Time is really just a fallback; e.g., the maximum amount of time that a cache file could ever possibly live.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
497
+ echo ' <p>'.sprintf(__('All of that being said, you could set this to just <code>60 seconds</code> and you would still see huge differences in speed and performance. If you\'re just starting out with %1$s (perhaps a bit nervous about old cache files being served to your visitors); you could set this to something like <code>30 minutes</code> and experiment with it while you build confidence in %1$s. It\'s not necessary to do so, but many site owners have reported this makes them feel like they\'re more-in-control when the cache has a short expiration time. All-in-all, it\'s a matter of preference <i class="si si-smile-o"></i>.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
498
+ echo ' <p><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_max_age]" value="'.esc_attr($this->plugin->options['cache_max_age']).'" /></p>'."\n";
499
+ echo ' <p class="info">'.__('<strong>Tip:</strong> the value that you specify here MUST be compatible with PHP\'s <a href="http://php.net/manual/en/function.strtotime.php" target="_blank" style="text-decoration:none;"><code>strtotime()</code></a> function. Examples: <code>30 seconds</code>, <code>2 hours</code>, <code>7 days</code>, <code>6 months</code>, <code>1 year</code>.', 'comet-cache').'</p>'."\n";
500
+ echo ' <p class="info">'.sprintf(__('<strong>Note:</strong> %1$s will never serve a cache file that is older than what you specify here (even if one exists in your cache directory; stale cache files are never used). In addition, a WP Cron job will automatically cleanup your cache directory (once per hour); purging expired cache files periodically. This prevents a HUGE cache from building up over time, creating a potential storage issue.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
501
+
502
+ echo ' <hr />'."\n";
503
+
504
+ echo ' <h3>'.__('Cache Cleanup Schedule', 'comet-cache').'</h3>'."\n";
505
+ echo ' <p>'.sprintf(__('If you have an extremely large site and you lower the default Cache Expiration Time of <code>7 days</code>, expired cache files can build up more quickly. By default, %1$s cleans up expired cache files via <a href="http://cometcache.com/r/wp_cron-functions/" target="_blank">WP Cron</a> at an <code>hourly</code> interval, but you can tell %1$s to use a custom Cache Cleanup Schedule below to run the cleanup process more or less frequently, depending on your specific needs.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
506
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_cleanup_schedule]">'."\n";
507
+ foreach (wp_get_schedules() as $_wp_cron_schedule_key => $_wp_cron_schedule) {
508
+ echo ' <option value="'.esc_attr($_wp_cron_schedule_key).'"'.selected($this->plugin->options['cache_cleanup_schedule'], $_wp_cron_schedule_key, false).'>'.esc_html($_wp_cron_schedule['display']).'</option>'."\n";
509
+ } // This builds the list of options using WP_Cron schedules configured for this WP installation.
510
+ unset($_wp_cron_schedule_key, $_wp_cron_schedule);
511
+ echo ' </select></p>'."\n";
512
+
513
+ if (IS_PRO || $this->plugin->isProPreview()) {
514
+ $_sys_getloadavg_unavailable = ($this->plugin->isProPreview() ? false : !$this->plugin->sysLoadAverages());
515
+ echo ' <div>'."\n";
516
+ echo ' <hr />'."\n";
517
+ echo ' <h3 class="'.(!IS_PRO ? 'pro-preview-feature' : '').'" style="'.($_sys_getloadavg_unavailable ? 'opacity: 0.5;' : '').'">'.__('Disable Cache Expiration If Server Load Average is High?', 'comet-cache').'</h3>'."\n";
518
+ echo ' <p style="'.($_sys_getloadavg_unavailable ? 'opacity: 0.5;' : '').'">'.sprintf(__('If you have high traffic at certain times of the day, %1$s can be told to check the current load average via <a href="http://cometcache.com/r/system-load-average-via-php/" target="_blank"><code>sys_getloadavg()</code></a>. If your server\'s load average has been high in the last 15 minutes or so, cache expiration is disabled automatically to help reduce stress on the server; i.e., to avoid generating a new version of the cache while the server is very busy.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
519
+ echo ' <p style="'.($_sys_getloadavg_unavailable ? 'opacity: 0.5;' : '').'">'.sprintf(__('To enable this functionality you should first determine what a high load average is for your server. If you log into your machine via SSH you can run the <code>top</code> command to get a feel for what a high load average looks like. Once you know the number, you can enter it in the field below; e.g., <code>1.05</code> might be a high load average for a server with one CPU. See also: <a href="http://cometcache.com/r/understanding-load-average/" target="_blank">Understanding Load Average</a>', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
520
+ echo ' <p><input '.($_sys_getloadavg_unavailable ? 'disabled' : '').' type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_max_age_disable_if_load_average_is_gte]" value="'.esc_attr($this->plugin->options['cache_max_age_disable_if_load_average_is_gte']).'" /></p>'."\n";
521
+ if ($_sys_getloadavg_unavailable && stripos(PHP_OS, 'win') === 0) { // See: <http://jas.xyz/1HZsZ9v>
522
+ echo ' <p class="warning">'.__('<strong>Note:</strong> It appears that your server is running Windows. The <code>sys_getloadavg()</code> function has not been implemented in PHP for Windows servers yet.', 'comet-cache').'</p>'."\n";
523
+ } elseif ($_sys_getloadavg_unavailable && stripos(PHP_OS, 'win') !== 0) {
524
+ echo ' <p class="warning">'.__('<strong>Note:</strong> <code>sys_getloadavg()</code> has been disabled by your web hosting company or is not available on your server.', 'comet-cache').'</p>'."\n";
525
+ }
526
+ echo ' </div>'."\n";
527
+ }
528
+ echo ' </div>'."\n";
529
+
530
+ echo '</div>'."\n";
531
+
532
+ /* ----------------------------------------------------------------------------------------- */
533
+
534
+ echo '<div class="plugin-menu-page-panel">'."\n";
535
+
536
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
537
+ echo ' <i class="si si-octi-tach"></i> '.__('Client-Side Cache', 'comet-cache')."\n";
538
+ echo ' </a>'."\n";
539
+
540
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
541
+ echo ' <i class="si si-desktop si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
542
+ echo ' <h3>'.__('Allow Double-Caching In The Client-Side Browser?', 'comet-cache').'</h3>'."\n";
543
+ echo ' <p>'.__('Recommended setting: <code>No</code> (for membership sites, very important). Otherwise, <code>Yes</code> would be better (if users do NOT log in/out of your site).', 'comet-cache').'</p>'."\n";
544
+ echo ' <p>'.sprintf(__('%1$s handles content delivery through its ability to communicate with a browser using PHP. If you allow a browser to (cache) the caching system itself, you are momentarily losing some control; and this can have a negative impact on users that see more than one version of your site; e.g., one version while logged-in, and another while NOT logged-in. For instance, a user may log out of your site, but upon logging out they report seeing pages on the site which indicate they are STILL logged in (even though they\'re not — that\'s bad). This can happen if you allow a client-side cache, because their browser may cache web pages they visited while logged into your site which persist even after logging out. Sending no-cache headers will work to prevent this issue.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
545
+ echo ' <p>'.__('All of that being said, if all you care about is blazing fast speed and users don\'t log in/out of your site (only you do); you can safely set this to <code>Yes</code> (recommended in this case). Allowing a client-side browser cache will improve speed and reduce outgoing bandwidth when this option is feasible.', 'comet-cache').'</p>'."\n";
546
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][allow_browser_cache]">'."\n";
547
+ echo ' <option value="0"'.selected($this->plugin->options['allow_browser_cache'], '0', false).'>'.__('No, prevent a client-side browser cache (safest option).', 'comet-cache').'</option>'."\n";
548
+ echo ' <option value="1"'.selected($this->plugin->options['allow_browser_cache'], '1', false).'>'.__('Yes, I will allow a client-side browser cache of pages on the site.', 'comet-cache').'</option>'."\n";
549
+ echo ' </select></p>'."\n";
550
+ echo ' <p class="info">'.__('<strong>Tip:</strong> Setting this to <code>No</code> is highly recommended when running a membership plugin like <a href="http://wordpress.org/plugins/s2member/" target="_blank">s2Member</a> (as one example). In fact, many plugins like s2Member will send <a href="http://codex.wordpress.org/Function_Reference/nocache_headers" target="_blank">nocache_headers()</a> on their own, so your configuration here will likely be overwritten when you run such plugins (which is better anyway). In short, if you run a membership plugin, you should NOT allow a client-side browser cache.', 'comet-cache').'</p>'."\n";
551
+ echo ' <p class="info">'.__('<strong>Tip:</strong> Setting this to <code>No</code> will NOT impact static content; e.g., CSS, JS, images, or other media. This setting pertains only to dynamic PHP scripts which produce content generated by WordPress.', 'comet-cache').'</p>'."\n";
552
+ echo ' <p class="info">'.sprintf(__('<strong>Advanced Tip:</strong> if you have this set to <code>No</code>, but you DO want to allow a few special URLs to be cached by the browser; you can add this parameter to your URL <code>?%2$sABC=1</code>. This tells %1$s that it\'s OK for the browser to cache that particular URL. In other words, the <code>%2$sABC=1</code> parameter tells %1$s NOT to send no-cache headers to the browser.', 'comet-cache'), esc_html(NAME), esc_html(strtolower(SHORT_NAME))).'</p>'."\n";
553
+ echo ' <h3>'.__('Exclusion Patterns for Client-Side Caching', 'comet-cache').'</h3>'."\n";
554
+ echo ' <p>'.__('When you enable Client-Side Caching above, you may want to prevent certain pages on your site from being cached by a client-side browser. This is where you will enter those if you need to (one per line). Searches are performed against the <a href="https://gist.github.com/jaswsinc/338b6eb03a36c048c26f" target="_blank" style="text-decoration:none;"><code>REQUEST_URI</code></a>; i.e., <code>/path/?query</code> (caSe insensitive). So, don\'t put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard <code>*</code> character can also be used when necessary; e.g., <code>/category/abc-followed-by-*</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href ="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache').'</p>'."\n";
555
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][exclude_client_side_uris]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['exclude_client_side_uris']).'</textarea></p>'."\n";
556
+ echo ' <p class="info">'.__('<strong>Tip:</strong> let\'s use this example URL: <code>http://www.example.com/post/example-post-123</code>. To exclude this URL, you would put this line into the field above: <code>/post/example-post-123</code>. Or, you could also just put in a small fragment, like: <code>example</code> or <code>example-*-123</code> and that would exclude any URI containing that word fragment.', 'comet-cache').'</p>'."\n";
557
+ echo ' <p class="info">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line.', 'comet-cache').'</p>'."\n";
558
+ echo ' </div>'."\n";
559
+
560
+ echo '</div>'."\n";
561
+
562
+ /* ----------------------------------------------------------------------------------------- */
563
+
564
+ if (IS_PRO || $this->plugin->isProPreview()) {
565
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO ? ' pro-preview' : '').'">'."\n";
566
+
567
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.(!IS_PRO ? ' pro-preview-feature' : '').'">'."\n";
568
+ echo ' <i class="si si-octi-organization"></i> '.__('Logged-In Users', 'comet-cache')."\n";
569
+ echo ' </a>'."\n";
570
+
571
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
572
+ echo ' <i class="si si-group si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
573
+ echo ' <h3>'.__('Caching Enabled for Logged-In Users &amp; Comment Authors?', 'comet-cache').'</h3>'."\n";
574
+ echo ' <p>'.__('This should almost ALWAYS be set to <code>No</code>. Most sites will NOT want to cache content generated while a user is logged-in. Doing so could result in a cache of dynamic content generated specifically for a particular user, where the content being cached may contain details that pertain only to the user that was logged-in when the cache was generated. Imagine visiting a website that says you\'re logged-in as Billy Bob (but you\'re not Billy Bob; NOT good). In short, do NOT turn this on unless you know what you\'re doing.', 'comet-cache').'</p>'."\n";
575
+ echo ' <i class="si si-sitemap si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
576
+ echo ' <p>'.sprintf(__('<strong>Exception (Membership Sites):</strong> If you run a site with many users and the majority of your traffic comes from users who ARE logged-in, please choose: <code>Yes (maintain separate cache)</code>. %1$s will operate normally; but when a user is logged-in, the cache is user-specific. %1$s will intelligently refresh the cache when/if a user submits a form on your site with the GET or POST method. Or, if you make changes to their account (or another plugin makes changes to their account); including user <a href="http://codex.wordpress.org/Function_Reference/update_user_option" target="_blank">option</a>|<a href="http://codex.wordpress.org/Function_Reference/update_user_meta" target="_blank">meta</a> additions, updates &amp; deletions too. However, please note that enabling this feature (e.g., user-specific cache entries); will eat up MUCH more disk space. That being said, the benefits of this feature for most sites will outweigh the disk overhead (e.g., it\'s NOT an issue in most cases). Unless you are short on disk space (or you have MANY thousands of users), the disk overhead is neglible.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
577
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][when_logged_in]">'."\n";
578
+ echo ' <option value="0"'.selected($this->plugin->options['when_logged_in'], '0', false).'>'.__('No, do NOT cache; or serve a cache file when a user is logged-in (safest option).', 'comet-cache').'</option>'."\n";
579
+ echo ' <option value="postload"'.selected($this->plugin->options['when_logged_in'], 'postload', false).'>'.__('Yes, and maintain a separate cache for each user (recommended for membership sites).', 'comet-cache').'</option>'."\n";
580
+ if ($this->plugin->options['when_logged_in'] === '1' || get_site_option(GLOBAL_NS.'_when_logged_in_was_1')) {
581
+ update_site_option(GLOBAL_NS.'_when_logged_in_was_1', '1');
582
+ echo ' <option value="1"'.selected($this->plugin->options['when_logged_in'], '1', false).'>'.__('Yes, but DON\'T maintain a separate cache for each user (I know what I\'m doing).', 'comet-cache').'</option>'."\n";
583
+ }
584
+ echo ' </select></p>'."\n";
585
+ if ($this->plugin->options['when_logged_in'] === '1' && $this->plugin->applyWpFilters(GLOBAL_NS.'_when_logged_in_no_admin_bar', true)) {
586
+ echo '<p class="warning">'.sprintf(__('<strong>Warning:</strong> Whenever you enable caching for logged-in users (without a separate cache for each user), the WordPress Admin Bar <em>must</em> be disabled to prevent one user from seeing another user\'s details in the Admin Bar. <strong>Given your current configuration, %1$s will automatically hide the WordPress Admin Bar on the front-end of your site.</strong>', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
587
+ }
588
+ echo ' <p class="info">'.__('<strong>Note:</strong> For most sites, the majority of their traffic (if not all of their traffic) comes from visitors who are not logged in, so disabling the cache for logged-in users is NOT ordinarily a performance issue. When a user IS logged-in, disabling the cache is considered ideal, because a logged-in user has a session open with your site; and the content they view should remain very dynamic in this scenario.', 'comet-cache').'</p>'."\n";
589
+ echo ' <p class="info">'.sprintf(__('<strong>Note:</strong> This setting includes some users who AREN\'T actually logged into the system, but who HAVE authored comments recently. %1$s includes comment authors as part of it\'s logged-in user check. This way comment authors will be able to see updates to the comment thread immediately; and, so that any dynamically-generated messages displayed by your theme will work as intended. In short, %1$s thinks of a comment author as a logged-in user, even though technically they are not. ~ Users who gain access to password-protected Posts/Pages are also included.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
590
+ echo ' <hr />'."\n";
591
+ echo ' <h3>'.__('Static CDN Filters Enabled for Logged-In Users &amp; Comment Authors?', 'comet-cache').'</h3>'."\n";
592
+ echo ' <p>'.__('While this defaults to a value of <code>No</code>, it should almost always be set to <code>Yes</code>. This value defaults to <code>No</code> only because Logged-In User caching (see above) defaults to <code>No</code> and setting this value to <code>Yes</code> by default can cause confusion for some users. Once you understand that Static CDN Filters can be applied safely for all visitors (logged-in or not logged-in), please choose <code>Yes</code> in the dropdown below. If you are not using Static CDN Filters, the value below is ignored.', 'comet-cache').'</p>'."\n";
593
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_when_logged_in]">'."\n";
594
+ echo ' <option value="0"'.selected($this->plugin->options['cdn_when_logged_in'], '0', false).'>'.__('No, disable Static CDN Filters when a user is logged-in.', 'comet-cache').'</option>'."\n";
595
+ echo ' <option value="postload"'.selected($this->plugin->options['cdn_when_logged_in'], 'postload', false).'>'.__('Yes, enable Static CDN Filters for logged-in users (recommended) .', 'comet-cache').'</option>'."\n";
596
+ echo ' </select></p>'."\n";
597
+ echo ' <p class="info">'.__('<strong>Note:</strong> Static CDN Filters serve <em>static</em> resources. Static resources, are, simply put, static. Thus, it is not a problem to cache these resources for any visitor (logged-in or not logged-in). To avoid confusion, this defaults to a value of <code>No</code>, and we ask that you set it to <code>Yes</code> on your own so that you\'ll know to expect this behavior; i.e., that static resources will always be served from the CDN (logged-in or not logged-in) even though Logged-In User caching may be disabled above.', 'comet-cache').'</p>'."\n";
598
+ echo ' </div>'."\n";
599
+
600
+ echo '</div>'."\n";
601
+ }
602
+ /* ----------------------------------------------------------------------------------------- */
603
+
604
+ echo '<div class="plugin-menu-page-panel">'."\n";
605
+
606
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
607
+ echo ' <i class="si si-question-circle"></i> '.__('GET Requests', 'comet-cache')."\n";
608
+ echo ' </a>'."\n";
609
+
610
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
611
+ echo ' <i class="si si-question-circle si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
612
+ echo ' <h3>'.__('Caching Enabled for GET (Query String) Requests?', 'comet-cache').'</h3>'."\n";
613
+ echo ' <p>'.__('This should almost ALWAYS be set to <code>No</code>. UNLESS, you\'re using unfriendly Permalinks. In other words, if all of your URLs contain a query string (e.g., <code>/?key=value</code>); you\'re using unfriendly Permalinks. Ideally, you would refrain from doing this; and instead, update your Permalink options immediately; which also optimizes your site for search engines. That being said, if you really want to use unfriendly Permalinks, and ONLY if you\'re using unfriendly Permalinks, you should set this to <code>Yes</code>; and don\'t worry too much, the sky won\'t fall on your head :-)', 'comet-cache').'</p>'."\n";
614
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][get_requests]">'."\n";
615
+ echo ' <option value="0"'.selected($this->plugin->options['get_requests'], '0', false).'>'.__('No, do NOT cache (or serve a cache file) when a query string is present.', 'comet-cache').'</option>'."\n";
616
+ echo ' <option value="1"'.selected($this->plugin->options['get_requests'], '1', false).'>'.__('Yes, I would like to cache URLs that contain a query string.', 'comet-cache').'</option>'."\n";
617
+ echo ' </select></p>'."\n";
618
+ echo ' <p class="info">'.__('<strong>Note:</strong> POST requests (i.e., forms with <code>method=&quot;post&quot;</code>) are always excluded from the cache, which is the way it should be. Any <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html" target="_blank">POST/PUT/DELETE</a> request should NEVER (ever) be cached. CLI (and self-serve) requests are also excluded from the cache (always). A CLI request is one that comes from the command line; commonly used by CRON jobs and other automated routines. A self-serve request is an HTTP connection established from your site -› to your site. For instance, a WP Cron job, or any other HTTP request that is spawned not by a user, but by the server itself.', 'comet-cache').'</p>'."\n";
619
+ echo ' <p class="info">'.sprintf(__('<strong>Advanced Tip:</strong> If you are NOT caching GET requests (recommended), but you DO want to allow some special URLs that include query string parameters to be cached; you can add this special parameter to any URL <code>?%2$sAC=1</code>. This tells %1$s that it\'s OK to cache that particular URL, even though it contains query string arguments. If you ARE caching GET requests and you want to force %1$s to NOT cache a specific request, you can add this special parameter to any URL <code>?%2$sAC=0</code>.', 'comet-cache'), esc_html(NAME), esc_html(strtolower(SHORT_NAME))).'</p>'."\n";
620
+ echo ' </div>'."\n";
621
+
622
+ echo '</div>'."\n";
623
+
624
+ /* ----------------------------------------------------------------------------------------- */
625
+
626
+ echo '<div class="plugin-menu-page-panel">'."\n";
627
+
628
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
629
+ echo ' <i class="si si-chain-broken"></i> '.__('404 Requests', 'comet-cache')."\n";
630
+ echo ' </a>'."\n";
631
+
632
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
633
+ echo ' <i class="si si-question-circle si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
634
+ echo ' <h3>'.__('Caching Enabled for 404 Requests?', 'comet-cache').'</h3>'."\n";
635
+ echo ' <p>'.sprintf(__('When this is set to <code>No</code>, %1$s will ignore all 404 requests and no cache file will be served. While this is fine for most site owners, caching the 404 page on a high-traffic site may further reduce server load. When this is set to <code>Yes</code>, %1$s will cache the 404 page (see <a href="https://codex.wordpress.org/Creating_an_Error_404_Page" target="_blank">Creating an Error 404 Page</a>) and then serve that single cache file to all future 404 requests.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
636
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cache_404_requests]">'."\n";
637
+ echo ' <option value="0"'.selected($this->plugin->options['cache_404_requests'], '0', false).'>'.__('No, do NOT cache (or serve a cache file) for 404 requests.', 'comet-cache').'</option>'."\n";
638
+ echo ' <option value="1"'.selected($this->plugin->options['cache_404_requests'], '1', false).'>'.__('Yes, I would like to cache the 404 page and serve the cached file for 404 requests.', 'comet-cache').'</option>'."\n";
639
+ echo ' </select></p>'."\n";
640
+ echo ' <p class="info">'.sprintf(__('<strong>How does %1$s cache 404 requests?</strong> %1$s will create a special cache file (<code>----404----.html</code>, see Advanced Tip below) for the first 404 request and then <a href="http://www.php.net/manual/en/function.symlink.php" target="_blank">symlink</a> future 404 requests to this special cache file. That way you don\'t end up with lots of 404 cache files that all contain the same thing (the contents of the 404 page). Instead, you\'ll have one 404 cache file and then several symlinks (i.e., references) to that 404 cache file.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
641
+ echo ' <p class="info">'.__('<strong>Advanced Tip:</strong> The default 404 cache filename (<code>----404----.html</code>) is designed to minimize the chance of a collision with a cache file for a real page with the same name. However, if you want to override this default and define your own 404 cache filename, you can do so by adding <code>define(\'COMET_CACHE_404_CACHE_FILENAME\', \'your-404-cache-filename\');</code> to your <code>wp-config.php</code> file (note that the <code>.html</code> extension should be excluded when defining a new filename).', 'comet-cache').'</p>'."\n";
642
+ echo ' </div>'."\n";
643
+
644
+ echo '</div>'."\n";
645
+
646
+ /* ----------------------------------------------------------------------------------------- */
647
+
648
+ echo '<div class="plugin-menu-page-panel">'."\n";
649
+
650
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
651
+ echo ' <i class="si si-feed"></i> '.__('RSS, RDF, and Atom Feeds', 'comet-cache')."\n";
652
+ echo ' </a>'."\n";
653
+
654
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
655
+ echo ' <i class="si si-question-circle si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
656
+ echo ' <h3>'.__('Caching Enabled for RSS, RDF, Atom Feeds?', 'comet-cache').'</h3>'."\n";
657
+ echo ' <p>'.__('This should almost ALWAYS be set to <code>No</code>. UNLESS, you\'re sure that you want to cache your feeds. If you use a web feed management provider like Google® Feedburner and you set this option to <code>Yes</code>, you may experience delays in the detection of new posts. <strong>NOTE:</strong> If you do enable this, it is highly recommended that you also enable automatic Feed Clearing too. Please see the section above: "Automatic Cache Clearing". Find the sub-section titled: "Auto-Clear RSS/RDF/ATOM Feeds".', 'comet-cache').'</p>'."\n";
658
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][feeds_enable]" class="-no-if-enabled">'."\n";
659
+ echo ' <option value="0"'.selected($this->plugin->options['feeds_enable'], '0', false).'>'.__('No, do NOT cache (or serve a cache file) when displaying a feed.', 'comet-cache').'</option>'."\n";
660
+ echo ' <option value="1"'.selected($this->plugin->options['feeds_enable'], '1', false).'>'.__('Yes, I would like to cache feed URLs.', 'comet-cache').'</option>'."\n";
661
+ echo ' </select></p>'."\n";
662
+ echo ' <p class="info">'.__('<strong>Note:</strong> This option affects all feeds served by WordPress, including the site feed, the site comment feed, post-specific comment feeds, author feeds, search feeds, and category and tag feeds. See also: <a href="http://codex.wordpress.org/WordPress_Feeds" target="_blank">WordPress Feeds</a>.', 'comet-cache').'</p>'."\n";
663
+ echo ' </div>'."\n";
664
+
665
+ echo '</div>'."\n";
666
+
667
+ /* ----------------------------------------------------------------------------------------- */
668
+
669
+ echo '<div class="plugin-menu-page-panel">'."\n";
670
+
671
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
672
+ echo ' <i class="si si-ban"></i> '.__('URI Exclusion Patterns', 'comet-cache')."\n";
673
+ echo ' </a>'."\n";
674
+
675
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
676
+ echo ' <h3>'.__('Don\'t Cache These Special URI Exclusion Patterns?', 'comet-cache').'</h3>'."\n";
677
+ echo ' <p>'.__('Sometimes there are certain cases where a particular file, or a particular group of files, should never be cached. This is where you will enter those if you need to (one per line). Searches are performed against the <a href="https://gist.github.com/jaswsinc/338b6eb03a36c048c26f" target="_blank" style="text-decoration:none;"><code>REQUEST_URI</code></a>; i.e., <code>/path/?query</code> (caSe insensitive). So, don\'t put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard <code>*</code> character can also be used when necessary; e.g., <code>/category/abc-followed-by-*</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href ="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache').'</p>'."\n";
678
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][exclude_uris]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['exclude_uris']).'</textarea></p>'."\n";
679
+
680
+ echo ' <p class="info">'.__('<strong>Tip:</strong> let\'s use this example URL: <code>http://www.example.com/post/example-post-123</code>. To exclude this URL, you would put this line into the field above: <code>/post/example-post-123</code>. Or, you could also just put in a small fragment, like: <code>example</code> or <code>example-*-123</code> and that would exclude any URI containing that word fragment.', 'comet-cache').'</p>'."\n";
681
+ echo ' <p class="info">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line.', 'comet-cache').'</p>'."\n";
682
+ echo ' </div>'."\n";
683
+
684
+ echo '</div>'."\n";
685
+
686
+ /* ----------------------------------------------------------------------------------------- */
687
+
688
+ echo '<div class="plugin-menu-page-panel">'."\n";
689
+
690
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
691
+ echo ' <i class="si si-ban"></i> '.__('HTTP Referrer Exclusion Patterns', 'comet-cache')."\n";
692
+ echo ' </a>'."\n";
693
+
694
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
695
+ echo ' <h3>'.__('Don\'t Cache These Special HTTP Referrer Exclusion Patterns?', 'comet-cache').'</h3>'."\n";
696
+ echo ' <p>'.__('Sometimes there are special cases where a particular referring URL (or referring domain) that sends you traffic; or even a particular group of referring URLs or domains that send you traffic; should result in a page being loaded on your site that is NOT from the cache (and that resulting page should never be cached). This is where you will enter those if you need to (one per line). Searches are performed against the <a href="http://www.php.net//manual/en/reserved.variables.server.php" target="_blank" style="text-decoration:none;"><code>HTTP_REFERER</code></a> (caSe insensitive). A wildcard <code>*</code> character can also be used when necessary; e.g., <code>*.domain.com</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href ="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache').'</p>'."\n";
697
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][exclude_refs]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['exclude_refs']).'</textarea></p>'."\n";
698
+ echo ' <p class="info">'.__('<strong>Tip:</strong> let\'s use this example URL: <code>http://www.referring-domain.com/search/?q=search+terms</code>. To exclude this referring URL, you could put this line into the field above: <code>www.referring-domain.com</code>. Or, you could also just put in a small fragment, like: <code>/search/</code> or <code>q=*</code>; and that would exclude any referrer containing that word fragment.', 'comet-cache').'</p>'."\n";
699
+ echo ' <p class="info">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line.', 'comet-cache').'</p>'."\n";
700
+ echo ' </div>'."\n";
701
+
702
+ echo '</div>'."\n";
703
+
704
+ /* ----------------------------------------------------------------------------------------- */
705
+
706
+ echo '<div class="plugin-menu-page-panel">'."\n";
707
+
708
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
709
+ echo ' <i class="si si-ban"></i> '.__('User-Agent Exclusion Patterns', 'comet-cache')."\n";
710
+ echo ' </a>'."\n";
711
+
712
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
713
+ echo ' <h3>'.__('Don\'t Cache These Special User-Agent Exclusion Patterns?', 'comet-cache').'</h3>'."\n";
714
+ echo ' <p>'.__('Sometimes there are special cases when a particular user-agent (e.g., a specific browser or a specific type of device); should be shown a page on your site that is NOT from the cache (and that resulting page should never be cached). This is where you will enter those if you need to (one per line). Searches are performed against the <a href="http://www.php.net//manual/en/reserved.variables.server.php" target="_blank" style="text-decoration:none;"><code>HTTP_USER_AGENT</code></a> (caSe insensitive). A wildcard <code>*</code> character can also be used when necessary; e.g., <code>Android *; Chrome/* Mobile</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href ="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache').'</p>'."\n";
715
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][exclude_agents]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['exclude_agents']).'</textarea></p>'."\n";
716
+ echo ' <p class="info">'.sprintf(__('<strong>Tip:</strong> if you wanted to exclude iPhones put this line into the field above: <code>iPhone;*AppleWebKit</code>. Or, you could also just put in a small fragment, like: <code>iphone</code>; and that would exclude any user-agent containing that word fragment. Note, this is just an example. With a default installation of %1$s, there is no compelling reason to exclude iOS devices (or any mobile device for that matter).', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
717
+ echo ' <p class="info">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line.', 'comet-cache').'</p>'."\n";
718
+ echo ' </div>'."\n";
719
+
720
+ echo '</div>'."\n";
721
+
722
+ /* ----------------------------------------------------------------------------------------- */
723
+
724
+ if (IS_PRO || $this->plugin->isProPreview()) {
725
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO ? ' pro-preview' : '').'">'."\n";
726
+
727
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.(!IS_PRO ? ' pro-preview-feature' : '').'">'."\n";
728
+ echo ' <i class="si si-sitemap"></i> '.__('Auto-Cache Engine', 'comet-cache')."\n";
729
+ echo ' </a>'."\n";
730
+
731
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
732
+ echo ' <i class="si si-question-circle si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
733
+ echo ' <h3>'.__('Enable the Auto-Cache Engine?', 'comet-cache').'</h3>'."\n";
734
+ echo ' <p>'.sprintf(__('After using %1$s for awhile (or any other page caching plugin, for that matter); it becomes obvious that at some point (based on your configured Expiration Time) %1$s has to refresh itself. It does this by ditching its cached version of a page, reloading the database-driven content, and then recreating the cache with the latest data. This is a never ending regeneration cycle that is based entirely on your configured Expiration Time.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
735
+ echo ' <p>'.__('Understanding this, you can see that 99% of your visitors are going to receive a lightning fast response from your server. However, there will always be around 1% of your visitors that land on a page for the very first time (before it\'s been cached), or land on a page that needs to have its cache regenerated, because the existing cache has become outdated. We refer to this as a <em>First-Come Slow-Load Issue</em>. Not a huge problem, but if you\'re optimizing your site for every ounce of speed possible, the Auto-Cache Engine can help with this. The Auto-Cache Engine has been designed to combat this issue by taking on the responsibility of being that first visitor to a page that has not yet been cached, or has an expired cache. The Auto-Cache Engine is powered, in part, by <a href="http://codex.wordpress.org/Category:WP-Cron_Functions" target="_blank">WP-Cron</a> (already built into WordPress). The Auto-Cache Engine runs at 15-minute intervals via WP-Cron. It also uses the <a href="http://core.trac.wordpress.org/browser/trunk/wp-includes/http.php" target="_blank">WP_Http</a> class, which is also built into WordPress already.', 'comet-cache').'</p>'."\n";
736
+ echo ' <p>'.__('The Auto-Cache Engine obtains its list of URLs to auto-cache, from two different sources. It can read an <a href="http://wordpress.org/extend/plugins/google-sitemap-generator/" target="_blank">XML Sitemap</a> and/or a list of specific URLs that you supply. If you supply both sources, it will use both sources collectively. The Auto-Cache Engine takes ALL of your other configuration options into consideration too, including your Expiration Time, as well as any cache exclusion rules.', 'comet-cache').'</p>'."\n";
737
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][auto_cache_enable]" data-target=".-auto-cache-options">'."\n";
738
+ echo ' <option value="0"'.(!IS_PRO ? '' : selected($this->plugin->options['auto_cache_enable'], '0', false)).'>'.__('No, leave the Auto-Cache Engine disabled please.', 'comet-cache').'</option>'."\n";
739
+ echo ' <option value="1"'.(!IS_PRO ? ' selected' : selected($this->plugin->options['auto_cache_enable'], '1', false)).'>'.__('Yes, I want the Auto-Cache Engine to keep pages cached automatically.', 'comet-cache').'</option>'."\n";
740
+ echo ' </select></p>'."\n";
741
+
742
+ echo ' <hr />'."\n";
743
+
744
+ echo ' <div class="plugin-menu-page-panel-if-enabled -auto-cache-options">'."\n";
745
+ echo ' <h3>'.__('XML Sitemap URL (or an XML Sitemap Index)', 'comet-cache').'</h3>'."\n";
746
+ echo ' <table style="width:100%;"><tr><td style="width:1px; font-weight:bold; white-space:nowrap;">'.esc_html(home_url('/')).'</td><td><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][auto_cache_sitemap_url]" value="'.esc_attr($this->plugin->options['auto_cache_sitemap_url']).'" /></td></tr></table>'."\n";
747
+ if (is_multisite()) {
748
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][auto_cache_ms_children_too]">'."\n";
749
+ echo ' <option value="0"'.selected($this->plugin->options['auto_cache_ms_children_too'], '0', false).'>'.__('All URLs in this network are in the sitemap for the main site.', 'comet-cache').'</option>'."\n";
750
+ echo ' <option value="1"'.selected($this->plugin->options['auto_cache_ms_children_too'], '1', false).'>'.__('Using the path I\'ve given, look for blog-specific sitemaps in each child blog also.', 'comet-cache').'</option>'."\n";
751
+ echo ' </select></p>'."\n";
752
+ echo ' <p class="info" style="display:block; margin-top:0;">'.sprintf(__('<strong>↑</strong> If enabled here, each child blog can be auto-cached too. %1$s will dynamically change the leading <code>%2$s</code> as necessary; for each child blog in the network. %1$s supports both sub-directory &amp; sub-domain networks, including domain mapping plugins. For more information about how the Auto-Cache Engine caches child blogs, see <a href="http://cometcache.com/r/kb-article-how-does-the-auto-cache-engine-cache-child-blogs-in-a-multisite-network/" target="_blank">this article</a>.', 'comet-cache'), esc_html(NAME), esc_html(home_url('/'))).'</p>'."\n";
753
+ }
754
+ echo ' <hr />'."\n";
755
+
756
+ echo ' <h3>'.__('And/Or; a List of URLs to Auto-Cache (One Per Line)', 'comet-cache').'</h3>'."\n";
757
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][auto_cache_other_urls]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['auto_cache_other_urls']).'</textarea></p>'."\n";
758
+ echo ' <p class="info" style="display:block; margin-top:-5px;">'.__('<strong>Note:</strong> Wildcards are NOT supported here. If you are going to supply a list of URLs above, each line must contain one full URL for the Auto-Cache Engine to auto-cache. If you have many URLs, we recommend using an <a href="https://en.wikipedia.org/wiki/Sitemaps" target="_blank">XML Sitemap</a>.', 'comet-cache').'</p>'."\n";
759
+
760
+ echo ' <hr />'."\n";
761
+
762
+ echo ' <h3>'.__('Auto-Cache Delay Timer (in Milliseconds)', 'comet-cache').'</h3>'."\n";
763
+ echo ' <p>'.__('As the Auto-Cache Engine runs through each URL, you can tell it to wait X number of milliseconds between each connection that it makes. It is strongly suggested that you DO have some small delay here. Otherwise, you run the risk of hammering your own web server with multiple repeated connections whenever the Auto-Cache Engine is running. This is especially true on very large sites; where there is the potential for hundreds of repeated connections as the Auto-Cache Engine goes through a long list of URLs. Adding a delay between each connection will prevent the Auto-Cache Engine from placing a heavy load on the processor that powers your web server. A value of <code>500</code> milliseconds is suggested here (half a second). If you experience problems, you can bump this up a little at a time, in increments of <code>500</code> milliseconds; until you find a happy place for your server. <em>Please note that <code>1000</code> milliseconds = <code>1</code> full second.</em>', 'comet-cache').'</p>'."\n";
764
+ echo ' <p><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][auto_cache_delay]" value="'.esc_attr($this->plugin->options['auto_cache_delay']).'" /></p>'."\n";
765
+
766
+ echo ' <hr />'."\n";
767
+
768
+ echo ' <h3>'.__('Auto-Cache User-Agent String', 'comet-cache').'</h3>'."\n";
769
+ echo ' <table style="width:100%;"><tr><td><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][auto_cache_user_agent]" value="'.esc_attr($this->plugin->options['auto_cache_user_agent']).'" /></td><td style="width:1px; font-weight:bold; white-space:nowrap;">; '.esc_html(SLUG_TD.' '.VERSION).'</td></tr></table>'."\n";
770
+ echo ' <p class="info" style="display:block;">'.__('This is how the Auto-Cache Engine identifies itself when connecting to URLs. See <a href="http://en.wikipedia.org/wiki/User_agent" target="_blank">User Agent</a> in the Wikipedia.', 'comet-cache').'</p>'."\n";
771
+ echo ' </div>'."\n";
772
+ echo ' </div>'."\n";
773
+
774
+ echo '</div>'."\n";
775
+ }
776
+ /* ----------------------------------------------------------------------------------------- */
777
+
778
+ if (IS_PRO || $this->plugin->isProPreview()) {
779
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO ? ' pro-preview' : '').'">'."\n";
780
+
781
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.(!IS_PRO ? ' pro-preview-feature' : '').'">'."\n";
782
+ echo ' <i class="si si-html5"></i> '.__('HTML Compression', 'comet-cache')."\n";
783
+ echo ' </a>'."\n";
784
+
785
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
786
+ echo ' <i class="si si-question-circle si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
787
+ echo ' <h3>'.__('Enable WebSharks™ HTML Compression?', 'comet-cache').'</h3>'."\n";
788
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_enable]" data-target=".-htmlc-options">'."\n";
789
+ echo ' <option value="0"'.(!IS_PRO ? '' : selected($this->plugin->options['htmlc_enable'], '0', false)).'>'.__('No, do NOT compress HTML/CSS/JS code at runtime.', 'comet-cache').'</option>'."\n";
790
+ echo ' <option value="1"'.(!IS_PRO ? ' selected' : selected($this->plugin->options['htmlc_enable'], '1', false)).'>'.__('Yes, I want to compress HTML/CSS/JS for blazing fast speeds.', 'comet-cache').'</option>'."\n";
791
+ echo ' </select></p>'."\n";
792
+ echo ' <p class="info" style="display:block;">'.__('<strong>Note:</strong> This is experimental. Please <a href="https://github.com/websharks/comet-cache/issues" target="_blank">report issues here</a>.', 'comet-cache').'</p>'."\n";
793
+ echo ' <hr />'."\n";
794
+ echo ' <div class="plugin-menu-page-panel-if-enabled -htmlc-options">'."\n";
795
+ echo ' <h3>'.__('HTML Compression Options', 'comet-cache').'</h3>'."\n";
796
+ echo ' <p>'.__('You can <a href="https://github.com/websharks/html-compressor" target="_blank">learn more about all of these options here</a>.', 'comet-cache').'</p>'."\n";
797
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_compress_combine_head_body_css]" autocomplete="off">'."\n";
798
+ echo ' <option value="1"'.selected($this->plugin->options['htmlc_compress_combine_head_body_css'], '1', false).'>'.__('Yes, combine CSS from &lt;head&gt; and &lt;body&gt; into fewer files.', 'comet-cache').'</option>'."\n";
799
+ echo ' <option value="0"'.selected($this->plugin->options['htmlc_compress_combine_head_body_css'], '0', false).'>'.__('No, do not combine CSS from &lt;head&gt; and &lt;body&gt; into fewer files.', 'comet-cache').'</option>'."\n";
800
+ echo ' </select></p>'."\n";
801
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_compress_css_code]" autocomplete="off">'."\n";
802
+ echo ' <option value="1"'.selected($this->plugin->options['htmlc_compress_css_code'], '1', false).'>'.__('Yes, compress the code in any unified CSS files.', 'comet-cache').'</option>'."\n";
803
+ echo ' <option value="0"'.selected($this->plugin->options['htmlc_compress_css_code'], '0', false).'>'.__('No, do not compress the code in any unified CSS files.', 'comet-cache').'</option>'."\n";
804
+ echo ' </select></p>'."\n";
805
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_compress_combine_head_js]" autocomplete="off">'."\n";
806
+ echo ' <option value="1"'.selected($this->plugin->options['htmlc_compress_combine_head_js'], '1', false).'>'.__('Yes, combine JS from &lt;head&gt; into fewer files.', 'comet-cache').'</option>'."\n";
807
+ echo ' <option value="0"'.selected($this->plugin->options['htmlc_compress_combine_head_js'], '0', false).'>'.__('No, do not combine JS from &lt;head&gt; into fewer files.', 'comet-cache').'</option>'."\n";
808
+ echo ' </select></p>'."\n";
809
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_compress_combine_footer_js]" autocomplete="off">'."\n";
810
+ echo ' <option value="1"'.selected($this->plugin->options['htmlc_compress_combine_footer_js'], '1', false).'>'.__('Yes, combine JS footer scripts into fewer files.', 'comet-cache').'</option>'."\n";
811
+ echo ' <option value="0"'.selected($this->plugin->options['htmlc_compress_combine_footer_js'], '0', false).'>'.__('No, do not combine JS footer scripts into fewer files.', 'comet-cache').'</option>'."\n";
812
+ echo ' </select></p>'."\n";
813
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_compress_combine_remote_css_js]" autocomplete="off">'."\n";
814
+ echo ' <option value="1"'.selected($this->plugin->options['htmlc_compress_combine_remote_css_js'], '1', false).'>'.__('Yes, combine CSS/JS from remote resources too.', 'comet-cache').'</option>'."\n";
815
+ echo ' <option value="0"'.selected($this->plugin->options['htmlc_compress_combine_remote_css_js'], '0', false).'>'.__('No, do not combine CSS/JS from remote resources.', 'comet-cache').'</option>'."\n";
816
+ echo ' </select></p>'."\n";
817
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_compress_js_code]" autocomplete="off">'."\n";
818
+ echo ' <option value="1"'.selected($this->plugin->options['htmlc_compress_js_code'], '1', false).'>'.__('Yes, compress the code in any unified JS files.', 'comet-cache').'</option>'."\n";
819
+ echo ' <option value="0"'.selected($this->plugin->options['htmlc_compress_js_code'], '0', false).'>'.__('No, do not compress the code in any unified JS files.', 'comet-cache').'</option>'."\n";
820
+ echo ' </select></p>'."\n";
821
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_compress_inline_js_code]" autocomplete="off">'."\n";
822
+ echo ' <option value="1"'.selected($this->plugin->options['htmlc_compress_inline_js_code'], '1', false).'>'.__('Yes, compress inline JavaScript snippets.', 'comet-cache').'</option>'."\n";
823
+ echo ' <option value="0"'.selected($this->plugin->options['htmlc_compress_inline_js_code'], '0', false).'>'.__('No, do not compress inline JavaScript snippets.', 'comet-cache').'</option>'."\n";
824
+ echo ' </select></p>'."\n";
825
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_compress_html_code]" autocomplete="off">'."\n";
826
+ echo ' <option value="1"'.selected($this->plugin->options['htmlc_compress_html_code'], '1', false).'>'.__('Yes, compress (remove extra whitespace) in the final HTML code too.', 'comet-cache').'</option>'."\n";
827
+ echo ' <option value="0"'.selected($this->plugin->options['htmlc_compress_html_code'], '0', false).'>'.__('No, do not compress the final HTML code.', 'comet-cache').'</option>'."\n";
828
+ echo ' </select></p>'."\n";
829
+ echo ' <hr />'."\n";
830
+ echo ' <h3>'.__('CSS Exclusion Patterns?', 'comet-cache').'</h3>'."\n";
831
+ echo ' <p>'.__('Sometimes there are special cases when a particular CSS file should NOT be consolidated or compressed in any way. This is where you will enter those if you need to (one per line). Searches are performed against the <code>&lt;link href=&quot;&quot;&gt;</code> value, and also against the contents of any inline <code>&lt;style&gt;</code> tags (caSe insensitive). A wildcard <code>*</code> character can also be used when necessary; e.g., <code>xy*-framework</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href ="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache').'</p>'."\n";
832
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_css_exclusions]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['htmlc_css_exclusions']).'</textarea></p>'."\n";
833
+ echo ' <p class="info" style="display:block;">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line.', 'comet-cache').'</p>'."\n";
834
+ echo ' <h3>'.__('JavaScript Exclusion Patterns?', 'comet-cache').'</h3>'."\n";
835
+ echo ' <p>'.__('Sometimes there are special cases when a particular JS file should NOT be consolidated or compressed in any way. This is where you will enter those if you need to (one per line). Searches are performed against the <code>&lt;script src=&quot;&quot;&gt;</code> value, and also against the contents of any inline <code>&lt;script&gt;</code> tags (caSe insensitive). A wildcard <code>*</code> character can also be used when necessary; e.g., <code>xy*-framework</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href ="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache').'</p>'."\n";
836
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_js_exclusions]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['htmlc_js_exclusions']).'</textarea></p>'."\n";
837
+ echo ' <p class="info" style="display:block;">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line.', 'comet-cache').'</p>'."\n";
838
+ echo ' <h3>'.__('URI Exclusions for HTML Compressor?', 'comet-cache').'</h3>'."\n";
839
+ echo ' <p>'.__('When you enable HTML Compression above, you may want to prevent certain pages on your site from being cached by the HTML Compressor. This is where you will enter those if you need to (one per line). Searches are performed against the <a href="https://gist.github.com/jaswsinc/338b6eb03a36c048c26f" target="_blank" style="text-decoration:none;"><code>REQUEST_URI</code></a>; i.e., <code>/path/?query</code> (caSe insensitive). So, don\'t put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard <code>*</code> character can also be used when necessary; e.g., <code>/category/abc-followed-by-*</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href ="http://cometcache.com/r/watered-down-regex-syntax/" target="_blank">this KB article</a>.', 'comet-cache').'</p>'."\n";
840
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_uri_exclusions]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['htmlc_uri_exclusions']).'</textarea></p>'."\n";
841
+ echo ' <p class="info">'.__('<strong>Tip:</strong> let\'s use this example URL: <code>http://www.example.com/post/example-post-123</code>. To exclude this URL, you would put this line into the field above: <code>/post/example-post-123</code>. Or, you could also just put in a small fragment, like: <code>example</code> or <code>example-*-123</code> and that would exclude any URI containing that word fragment.', 'comet-cache').'</p>'."\n";
842
+ echo ' <p class="info">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line.', 'comet-cache').'</p>'."\n";
843
+ echo ' <hr />'."\n";
844
+ echo ' <h3>'.__('HTML Compression Cache Expiration', 'comet-cache').'</h3>'."\n";
845
+ echo ' <p><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][htmlc_cache_expiration_time]" value="'.esc_attr($this->plugin->options['htmlc_cache_expiration_time']).'" /></p>'."\n";
846
+ echo ' <p class="info" style="display:block;">'.__('<strong>Tip:</strong> the value that you specify here MUST be compatible with PHP\'s <a href="http://php.net/manual/en/function.strtotime.php" target="_blank" style="text-decoration:none;"><code>strtotime()</code></a> function. Examples: <code>2 hours</code>, <code>7 days</code>, <code>6 months</code>, <code>1 year</code>.', 'comet-cache').'</p>'."\n";
847
+ echo ' <p>'.sprintf(__('<strong>Note:</strong> This does NOT impact the overall cache expiration time that you configure with %1$s. It only impacts the sub-routines provided by the HTML Compressor. In fact, this expiration time is mostly irrelevant. The HTML Compressor uses an internal checksum, and it also checks <code>filemtime()</code> before using an existing cache file. The HTML Compressor class also handles the automatic cleanup of your cache directories to keep it from growing too large over time. Therefore, unless you have VERY little disk space there is no reason to set this to a lower value (even if your site changes dynamically quite often). If anything, you might like to increase this value which could help to further reduce server load. You can <a href="https://github.com/websharks/HTML-Compressor" target="_blank">learn more here</a>. We recommend setting this value to at least double that of your overall %1$s expiration time.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
848
+ echo ' </div>'."\n";
849
+ echo ' </div>'."\n";
850
+
851
+ echo '</div>'."\n";
852
+ }
853
+ /* ----------------------------------------------------------------------------------------- */
854
+
855
+ echo '<div class="plugin-menu-page-panel">'."\n";
856
+
857
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
858
+ echo ' <i class="si si-file-archive-o"></i> '.__('GZIP Compression', 'comet-cache')."\n";
859
+ echo ' </a>'."\n";
860
+
861
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
862
+ echo ' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/gzip.png')).'" class="screenshot" />'."\n";
863
+ echo ' <h3>'.__('<a href="https://developers.google.com/speed/articles/gzip" target="_blank">GZIP Compression</a> (Optional; Highly Recommended)', 'comet-cache').'</h3>'."\n";
864
+ echo ' <p>'.__('You don\'t have to use an <code>.htaccess</code> file to enjoy the performance enhancements provided by this plugin; caching is handled automatically by WordPress/PHP alone. That being said, if you want to take advantage of the additional speed enhancements associated w/ GZIP compression (and we do recommend this), then you WILL need an <code>.htaccess</code> file to accomplish that part.', 'comet-cache').'</p>'."\n";
865
+ echo ' <p>'.sprintf(__('%1$s fully supports GZIP compression on its output. However, it does not handle GZIP compression directly. We purposely left GZIP compression out of this plugin, because GZIP compression is something that should really be enabled at the Apache level or inside your <code>php.ini</code> file. GZIP compression can be used for things like JavaScript and CSS files as well, so why bother turning it on for only WordPress-generated pages when you can enable GZIP at the server level and cover all the bases!', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
866
+ echo ' <p>'.__('If you want to enable GZIP, create an <code>.htaccess</code> file in your WordPress® installation directory, and put the following few lines in it. Alternatively, if you already have an <code>.htaccess</code> file, just add these lines to it, and that is all there is to it. GZIP is now enabled in the recommended way! See also: <a href="https://developers.google.com/speed/articles/gzip" target="_blank"><i class="si si-youtube-play"></i> video about GZIP Compression</a>.', 'comet-cache').'</p>'."\n";
867
+ echo ' <pre class="code"><code>'.esc_html(file_get_contents(dirname(dirname(__FILE__)).'/templates/gzip-htaccess.txt')).'</code></pre>'."\n";
868
+ echo ' <hr />'."\n";
869
+ echo ' <p class="info" style="display:block;"><strong>Or</strong>, if your server is missing <code>mod_deflate</code>/<code>mod_filter</code>; open your <strong>php.ini</strong> file and add this line: <a href="http://php.net/manual/en/zlib.configuration.php" target="_blank" style="text-decoration:none;"><code>zlib.output_compression = on</code></a></p>'."\n";
870
+ echo ' </div>'."\n";
871
+
872
+ echo '</div>'."\n";
873
+
874
+ /* ----------------------------------------------------------------------------------------- */
875
+
876
+ if (IS_PRO || $this->plugin->isProPreview()) {
877
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO ? ' pro-preview' : '').'">'."\n";
878
+
879
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.(!IS_PRO ? ' pro-preview-feature' : '').'">'."\n";
880
+ echo ' <i class="si si-cloud"></i> '.__('Static CDN Filters', 'comet-cache')."\n";
881
+ echo ' </a>'."\n";
882
+
883
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
884
+ echo ' <button type="button" class="plugin-menu-page-clear-cdn-cache" style="float:right; margin:0 0 1em 1em;" title="'.esc_attr(__('Clear CDN Cache (Bump CDN Invalidation Counter)', 'comet-cache')).'">'.__('Clear CDN Cache', 'comet-cache').' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/clear.png')).'" style="width:16px; height:16px;" /></button>'."\n";
885
+ echo ' <h3>'.__('Enable Static CDN Filters (e.g., MaxCDN/CloudFront)?', 'comet-cache').'</h3>'."\n";
886
+ echo ' <p>'.sprintf(__('This feature allows you to serve some and/or ALL static files on your site from a CDN of your choosing. This is made possible through content/URL filters exposed by WordPress and implemented by %1$s. All it requires is that you setup a CDN host name sourced by your WordPress installation domain. You enter that CDN host name below and %1$s will do the rest! Super easy, and it doesn\'t require any DNS changes either. :-) Please <a href="http://cometcache.com/r/static-cdn-filters-general-instructions/" target="_blank">click here</a> for a general set of instructions.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
887
+ echo ' <p>'.__('<strong>What\'s a CDN?</strong> It\'s a Content Delivery Network (i.e., a network of optimized servers) designed to cache static resources served from your site (e.g., JS/CSS/images and other static files) onto it\'s own servers, which are located strategically in various geographic areas around the world. Integrating a CDN for static files can dramatically improve the speed and performance of your site, lower the burden on your own server, and reduce latency associated with visitors attempting to access your site from geographic areas of the world that might be very far away from the primary location of your own web servers.', 'comet-cache').'</p>'."\n";
888
+ if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) {
889
+ echo '<div class="plugin-menu-page-notice error">'."\n";
890
+ echo ' <i class="si si-thumbs-down"></i> '.__('It appears that your server is running NGINX and does not support <code>.htaccess</code> rules. Please <a href="http://cometcache.com/r/kb-article-recommended-nginx-server-configuration/" target="_new">update your server configuration manually</a>. Note that updating your NGINX server configuration <em>before</em> enabling Static CDN Filters is recommended to prevent any <a href="http://cometcache.com/r/kb-article-what-are-cross-origin-request-blocked-cors-errors/" target="_new">CORS errors</a> with your CDN. If you\'ve already updated your NGINX configuration, you can safely <a href="http://cometcache.com/r/kb-article-how-do-i-disable-the-nginx-htaccess-notice/" target="_new">ignore this message</a>.', 'comet-cache')."\n";
891
+ echo '</div>'."\n";
892
+ }
893
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_enable]" data-target=".-static-cdn-filter-options">'."\n";
894
+ echo ' <option value="0"'.(!IS_PRO ? '' : selected($this->plugin->options['cdn_enable'], '0', false)).'>'.__('No, I do NOT want CDN filters applied at runtime.', 'comet-cache').'</option>'."\n";
895
+ echo ' <option value="1"'.(!IS_PRO ? ' selected' : selected($this->plugin->options['cdn_enable'], '1', false)).'>'.__('Yes, I want CDN filters applied w/ my configuration below.', 'comet-cache').'</option>'."\n";
896
+ echo ' </select></p>'."\n";
897
+
898
+ echo ' <hr />'."\n";
899
+
900
+ echo ' <div class="plugin-menu-page-panel-if-enabled -static-cdn-filter-options">'."\n";
901
+
902
+ echo ' <h3>'.__('CDN Host Name (Required)', 'comet-cache').'</h3>'."\n";
903
+
904
+ echo ' <p class="info" style="display:block;">'.// This note includes three graphics. One for MaxCDN; another for CloudFront, and another for KeyCDN.
905
+ ' <a href="http://cometcache.com/r/keycdn/" target="_blank"><img src="'.esc_attr($this->plugin->url('/src/client-s/images/keycdn-logo.png')).'" style="width:90px; float:right; margin: 18px 10px 0 18px;" /></a>'.
906
+ ' <a href="http://cometcache.com/r/amazon-cloudfront/" target="_blank"><img src="'.esc_attr($this->plugin->url('/src/client-s/images/cloudfront-logo.png')).'" style="width:75px; float:right; margin: 8px 10px 0 25px;" /></a>'.
907
+ ' <a href="http://cometcache.com/r/maxcdn/" target="_blank"><img src="'.esc_attr($this->plugin->url('/src/client-s/images/maxcdn-logo.png')).'" style="width:125px; float:right; margin: 20px 0 0 25px;" /></a>'.
908
+ ' '.__('This field is really all that\'s necessary to get Static CDN Filters working! However, it does requires a little bit of work on your part. You need to setup and configure a CDN before you can fill in this field. Once you configure a CDN, you\'ll receive a host name (provided by your CDN), which you\'ll enter here; e.g., <code>js9dgjsl4llqpp.cloudfront.net</code>. We recommend <a href="http://cometcache.com/r/maxcdn/" target="_blank">MaxCDN</a>, <a href="http://cometcache.com/r/amazon-cloudfront/" target="_blank">Amazon CloudFront</a>, <a href="http://cometcache.com/r/keycdn/" target="_blank">KeyCDN</a>, and/or <a href="http://cometcache.com/r/cdn77/" target="_blank">CDN77</a> but this should work with many of the most popular CDNs. Please read <a href="http://cometcache.com/r/static-cdn-filters-general-instructions/" target="_blank">this article</a> for a general set of instructions. We also have a <a href="http://cometcache.com/r/static-cdn-filters-maxcdn/" target="_blank">MaxCDN tutorial</a>, <a href="http://cometcache.com/r/static-cdn-filters-cloudfront/" target="_blank">CloudFront tutorial</a>, <a href="http://cometcache.com/r/static-cdn-filters-keycdn/" target="_blank">KeyCDN tutorial</a>, and a <a href="http://cometcache.com/r/static-cdn-filters-cdn77/" target="_blank">CDN77 tutorial</a> to walk you through the process.', 'comet-cache').'</p>'."\n";
909
+ echo ' <p><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_host]" value="'.esc_attr($this->plugin->options['cdn_hosts'] ? '' : $this->plugin->options['cdn_host']).'"'.($this->plugin->options['cdn_hosts'] ? ' disabled="disabled"' : '').' /></p>'."\n";
910
+
911
+ echo ' <hr />'."\n";
912
+
913
+ echo ' <h3>'.__('Multiple CDN Host Names for Domain Sharding and Multisite Networks (Optional)', 'comet-cache').'</h3>'."\n";
914
+ echo ' <p>'.sprintf(__('%1$s also supports multiple CDN Host Names for any given domain. Using multiple CDN Host Names (instead of just one, as seen above) is referred to as <strong><a href="http://cometcache.com/r/domain-sharding/" target="_blank">Domain Sharding</a></strong> (<a href="http://cometcache.com/r/domain-sharding/" target="_blank">click here to learn more</a>). If you configure multiple CDN Host Names (i.e., if you implement Domain Sharding), %1$s will use the first one that you list for static resources loaded in the HTML <code>&lt;head&gt;</code> section, the last one for static resources loaded in the footer, and it will choose one at random for all other static resource locations. Configuring multiple CDN Host Names can improve speed! This is a way for advanced site owners to work around concurrency limits in popular browsers; i.e., making it possible for browsers to download many more resources simultaneously, resulting in a faster overall completion time. In short, this tells the browser that your website will not be overloaded by concurrent requests, because static resources are in fact being served by a content-delivery network (i.e., multiple CDN host names). If you use this functionality for Domain Sharding, we suggest that you setup one CDN Distribution (aka: Pull Zone), and then create multiple CNAME records pointing to that distribution. You can enter each of your CNAMES in the field below, as instructed.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
915
+ echo ' <p class="info" style="display:block;">'.sprintf(__('<strong>On WordPress Multisite Network installations</strong>, this field also allows you to configure different CDN Host Names for each domain (or sub-domain) that you run from a single installation of WordPress. For more information about configuring Static CDN Filters on a WordPress Multisite Network, see this tutorial: <a href="http://cometcache.com/r/static-cdn-filters-for-wordpress-multisite-networks/" target="_blank">Static CDN Filters for WordPress Multisite Networks</a>.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
916
+ echo ' <p style="margin-bottom:0;"><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_hosts]" rows="5" spellcheck="false" autocomplete="off" placeholder="'.esc_attr('e.g., '.$this->plugin->hostToken(false, true).' = cdn1.'.$this->plugin->hostToken(false, true).', cdn2.'.$this->plugin->hostToken(false, true).', cdn3.'.$this->plugin->hostToken(false, true)).'" wrap="off" style="white-space:nowrap;">'.esc_textarea($this->plugin->options['cdn_hosts']).'</textarea></p>'."\n";
917
+ echo ' <p style="margin-top:0;">'.sprintf(__('<strong>↑ Syntax:</strong> This is a line-delimited list of domain mappings. Each line should start with your WordPress domain name (e.g., <code>%1$s</code>), followed by an <code>=</code> sign, followed by a comma-delimited list of CDN Host Names associated with the domain in that line. If you\'re running a Multisite Network installation of WordPress, you might have multiple configuration lines. Otherwise, you should only need one line to configure multiple CDN Host Names for a standard WordPress installation.', 'comet-cache'), esc_html($this->plugin->hostToken(false, true))).'</p>'."\n";
918
+
919
+ echo ' <hr />'."\n";
920
+
921
+ echo ' <h3>'.__('CDN Supports HTTPS Connections?', 'comet-cache').'</h3>'."\n";
922
+ echo ' <p><select name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_over_ssl]" autocomplete="off">'."\n";
923
+ echo ' <option value="0"'.selected($this->plugin->options['cdn_over_ssl'], '0', false).'>'.__('No, I don\'t serve content over https://; or I haven\'t configured my CDN w/ an SSL certificate.', 'comet-cache').'</option>'."\n";
924
+ echo ' <option value="1"'.selected($this->plugin->options['cdn_over_ssl'], '1', false).'>'.__('Yes, I\'ve configured my CDN w/ an SSL certificate; I need https:// enabled.', 'comet-cache').'</option>'."\n";
925
+ echo ' </select></p>'."\n";
926
+
927
+ echo ' <hr />'."\n";
928
+
929
+ echo ' <h3 style="margin-bottom:0;">'.
930
+ '<a href="#" class="dotted" data-toggle-target=".'.esc_attr(GLOBAL_NS.'-static-cdn-filters--more-options').'">'.
931
+ '<i class="si si-eye"></i> '.__('Additional Options (For Advanced Users)', 'comet-cache').' <i class="si si-eye"></i>'.
932
+ '</a>'.
933
+ '</h3>'."\n";
934
+
935
+ echo ' <div class="'.esc_attr(GLOBAL_NS.'-static-cdn-filters--more-options').'" style="'.(!IS_PRO ? '' : 'display:none; ').'margin-top:1em;">'."\n";
936
+ echo ' <p class="info" style="display:block;">'.__('Everything else below is 100% completely optional; i.e., not required to enjoy the benefits of Static CDN Filters.', 'comet-cache').'</p>'."\n";
937
+
938
+ echo ' <hr />'."\n";
939
+
940
+ echo ' <h3>'.__('Whitelisted File Extensions (Optional; Comma-Delimited)', 'comet-cache').'</h3>'."\n";
941
+ echo ' <p><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_whitelisted_extensions]" value="'.esc_attr($this->plugin->options['cdn_whitelisted_extensions']).'" /></p>'."\n";
942
+ echo ' <p>'.__('If you leave this empty a default set of extensions are taken from WordPress itself. The default set of whitelisted file extensions includes everything supported by the WordPress media library.', 'comet-cache').(IS_PRO ? ' '.__('This includes the following: <code style="white-space:normal; word-wrap:break-word;">'.esc_html(implode(',', CdnFilters::defaultWhitelistedExtensions())).'</code>', 'comet-cache') : '').'</p>'."\n";
943
+
944
+ echo ' <h3>'.__('Blacklisted File Extensions (Optional; Comma-Delimited)', 'comet-cache').'</h3>'."\n";
945
+ echo ' <p><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_blacklisted_extensions]" value="'.esc_attr($this->plugin->options['cdn_blacklisted_extensions']).'" /></p>'."\n";
946
+ echo ' <p>'.__('With or without a whitelist, you can force exclusions by explicitly blacklisting certain file extensions of your choosing. Please note, the <code>php</code> extension will never be considered a static resource; i.e., it is automatically blacklisted at all times.', 'comet-cache').'</p>'."\n";
947
+
948
+ echo ' <hr />'."\n";
949
+
950
+ echo ' <h3>'.__('Whitelisted URI Inclusion Patterns (Optional; One Per Line)', 'comet-cache').'</h3>'."\n";
951
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_whitelisted_uri_patterns]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['cdn_whitelisted_uri_patterns']).'</textarea></p>'."\n";
952
+ echo ' <p class="info" style="display:block;">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one inclusion pattern per line.', 'comet-cache').'</p>'."\n";
953
+ echo ' <p>'.__('If provided, only local URIs matching one of the patterns you list here will be served from your CDN Host Name. URI patterns are caSe-insensitive. A wildcard <code>*</code> will match zero or more characters in any of your patterns. A caret <code>^</code> symbol will match zero or more characters that are NOT the <code>/</code> character. For instance, <code>*/wp-content/*</code> here would indicate that you only want to filter URLs that lead to files located inside the <code>wp-content</code> directory. Adding an additional line with <code>*/wp-includes/*</code> would filter URLs in the <code>wp-includes</code> directory also. <strong>If you leave this empty</strong>, ALL files matching a static file extension will be served from your CDN; i.e., the default behavior.', 'comet-cache').'</p>'."\n";
954
+ echo ' <p>'.__('Please note that URI patterns are tested against a file\'s path (i.e., a file\'s URI, and NOT its full URL). A URI always starts with a leading <code>/</code>. To clarify, a URI is the portion of the URL which comes after the host name. For instance, given the following URL: <code>http://example.com/path/to/style.css?ver=3</code>, the URI you are matching against would be: <code>/path/to/style.css?ver=3</code>. To whitelist this URI, you could use a line that contains something like this: <code>/path/to/*.css*</code>', 'comet-cache').'</p>'."\n";
955
+
956
+ echo ' <h3>'.__('Blacklisted URI Exclusion Patterns (Optional; One Per Line)', 'comet-cache').'</h3>'."\n";
957
+ echo ' <p><textarea name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_blacklisted_uri_patterns]" rows="5" spellcheck="false" class="monospace">'.format_to_edit($this->plugin->options['cdn_blacklisted_uri_patterns']).'</textarea></p>'."\n";
958
+ echo ' <p>'.__('With or without a whitelist, you can force exclusions by explicitly blacklisting certain URI patterns. URI patterns are caSe-insensitive. A wildcard <code>*</code> will match zero or more characters in any of your patterns. A caret <code>^</code> symbol will match zero or more characters that are NOT the <code>/</code> character. For instance, <code>*/wp-content/*/dynamic.pdf*</code> would exclude a file with the name <code>dynamic.pdf</code> located anywhere inside a sub-directory of <code>wp-content</code>.', 'comet-cache').'</p>'."\n";
959
+ echo ' <p class="info" style="display:block;">'.__('<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line.', 'comet-cache').'</p>'."\n";
960
+
961
+ echo ' <hr />'."\n";
962
+
963
+ echo ' <h3>'.__('Query String Invalidation Variable Name', 'comet-cache').'</h3>'."\n";
964
+ echo ' <p><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][cdn_invalidation_var]" value="'.esc_attr($this->plugin->options['cdn_invalidation_var']).'" /></p>'."\n";
965
+ echo ' <p>'.sprintf(__('Each filtered URL (which then leads to your CDN) will include this query string variable as an easy way to invalidate the CDN cache at any time. Invalidating the CDN cache is simply a matter of changing the global invalidation counter (i.e., the value assigned to this query string variable). %1$s manages invalidations automatically; i.e., %1$s will automatically bump an internal counter each time you upgrade a WordPress component (e.g., a plugin, theme, or WP itself). Or, if you ask %1$s to invalidate the CDN cache (e.g., a manual clearing of the CDN cache); the internal counter is bumped then too. In short, %1$s handles cache invalidations for you reliably. This option simply allows you to customize the query string variable name which makes cache invalidations possible. <strong>Please note, the default value is adequate for most sites. You can change this if you like, but it\'s not necessary.</strong>', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
966
+ echo ' <p class="info" style="display:block;">'.sprintf(__('<strong>Tip:</strong> You can also tell %1$s to automatically bump the CDN Invalidation Counter whenever you clear the cache manually. See: <strong>%1$s → Manual Cache Clearing → Clear the CDN Cache Too?</strong>', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
967
+ echo ' <p class="info" style="display:block;">'.sprintf(__('<strong>Note:</strong> If you empty this field, it will effectively disable the %1$s invalidation system for Static CDN Filters; i.e., the query string variable will NOT be included if you do not supply a variable name.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
968
+ echo ' </div>'."\n";
969
+ echo ' </div>'."\n";
970
+ echo ' </div>'."\n";
971
+
972
+ echo '</div>'."\n";
973
+ }
974
+ /* ----------------------------------------------------------------------------------------- */
975
+
976
+ if (IS_PRO || $this->plugin->isProPreview()) {
977
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO ? ' pro-preview' : '').'">'."\n";
978
+
979
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.(!IS_PRO ? ' pro-preview-feature' : '').'">'."\n";
980
+ echo ' <i class="si si-octi-versions"></i> '.__('Dynamic Version Salt', 'comet-cache')."\n";
981
+ echo ' </a>'."\n";
982
+
983
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
984
+ echo ' <img src="'.esc_attr($this->plugin->url('/src/client-s/images/salt.png')).'" class="screenshot" />'."\n";
985
+ echo ' <h3>'.__('<i class="si si-flask"></i> <span style="display:inline-block; padding:5px; border-radius:3px; background:#FFFFFF; color:#354913;"><span style="font-weight:bold; font-size:80%;">GEEK ALERT</span></span> This is for VERY advanced users only...', 'comet-cache').'</h3>'."\n";
986
+ echo ' <p>'.sprintf(__('<em>Note: Understanding the %1$s <a href="http://cometcache.com/r/kb-branched-cache-structure/" target="_blank">Branched Cache Structure</a> is a prerequisite to understanding how Dynamic Version Salts are added to the mix.</em>', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
987
+ echo ' <p>'.__('A Version Salt gives you the ability to dynamically create multiple variations of the cache, and those dynamic variations will be served on subsequent visits; e.g., if a visitor has a specific cookie (of a certain value) they will see pages which were cached with that version (i.e., w/ that Version Salt: the value of the cookie). A Version Salt can really be anything.', 'comet-cache').'</p>'."\n";
988
+ echo ' <p>'.__('A Version Salt can be a single variable like <code>$_COOKIE[\'my_cookie\']</code>, or it can be a combination of multiple variables, like <code>$_COOKIE[\'my_cookie\'].$_COOKIE[\'my_other_cookie\']</code>. (When using multiple variables, please separate them with a dot, as shown in the example.)', 'comet-cache').'</p>'."\n";
989
+ echo ' <p>'.__('Experts could even use PHP ternary expressions that evaluate into something. For example: <code>((preg_match(\'/iPhone/i\', $_SERVER[\'HTTP_USER_AGENT\'])) ? \'iPhones\' : \'\')</code>. This would force a separate version of the cache to be created for iPhones (e.g., <code>/cache/PROTOCOL/HOST/REQUEST-URI.v/iPhones.html</code>).', 'comet-cache').'</p>'."\n";
990
+ echo ' <p>'.__('For more documentation, please see <a href="http://cometcache.com/r/kb-dynamic-version-salts/" target="_blank">Dynamic Version Salts</a>.', 'comet-cache').'</p>'."\n";
991
+ echo ' <hr />'."\n";
992
+ echo ' <h3>'.sprintf(__('Create a Dynamic Version Salt For %1$s? &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size:90%%; opacity:0.5;">150%% OPTIONAL</span>', 'comet-cache'), esc_html(NAME)).'</h3>'."\n";
993
+ echo ' <table style="width:100%;"><tr><td style="width:1px; font-weight:bold; white-space:nowrap;">/cache/PROTOCOL/HOST/REQUEST_URI.</td><td><input type="text" name="'.esc_attr(GLOBAL_NS).'[saveOptions][version_salt]" value="'.esc_attr($this->plugin->options['version_salt']).'" class="monospace" placeholder="$_COOKIE[\'my_cookie\']" /></td><td style="width:1px; font-weight:bold; white-space:nowrap;"></td></tr></table>'."\n";
994
+ echo ' <p class="info" style="display:block;">'.__('<a href="http://php.net/manual/en/language.variables.superglobals.php" target="_blank">Super Globals</a> work here; <a href="http://codex.wordpress.org/Editing_wp-config.php#table_prefix" target="_blank"><code>$GLOBALS[\'table_prefix\']</code></a> is a popular one.<br />Or, perhaps a PHP Constant defined in <code>/wp-config.php</code>; such as <code>WPLANG</code> or <code>DB_HOST</code>.', 'comet-cache').'</p>'."\n";
995
+ echo ' <p class="notice" style="display:block;">'.__('<strong>Important:</strong> your Version Salt is scanned for PHP syntax errors via <a href="http://phpcodechecker.com/" target="_blank"><code>phpCodeChecker.com</code></a>. If errors are found, you\'ll receive a notice in the Dashboard.', 'comet-cache').'</p>'."\n";
996
+ echo ' <p class="info" style="display:block;">'.__('If you\'ve enabled a separate cache for each user (optional) that\'s perfectly OK. A Version Salt works with user caching too.', 'comet-cache').'</p>'."\n";
997
+ echo ' </div>'."\n";
998
+
999
+ echo '</div>'."\n";
1000
+ }
1001
+ /* ----------------------------------------------------------------------------------------- */
1002
+
1003
+ echo '<div class="plugin-menu-page-panel">'."\n";
1004
+
1005
+ echo ' <a href="#" class="plugin-menu-page-panel-heading">'."\n";
1006
+ echo ' <i class="si si-octi-plug"></i> '.__('Theme/Plugin Developers', 'comet-cache')."\n";
1007
+ echo ' </a>'."\n";
1008
+
1009
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
1010
+ echo ' <i class="si si-puzzle-piece si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
1011
+ echo ' <h3>'.__('Developing a Theme or Plugin for WordPress?', 'comet-cache').'</h3>'."\n";
1012
+ echo ' <p>'.sprintf(__('<strong>Tip:</strong> %1$s can be disabled temporarily. If you\'re a theme/plugin developer, you can set a flag within your PHP code to disable the cache engine at runtime. Perhaps on a specific page, or in a specific scenario. In your PHP script, set: <code>$_SERVER[\'COMET_CACHE_ALLOWED\'] = FALSE;</code> or <code>define(\'COMET_CACHE_ALLOWED\', FALSE)</code>. %1$s is also compatible with: <code>define(\'DONOTCACHEPAGE\', TRUE)</code>. It does\'t matter where or when you define one of these, because %1$s is the last thing to run before script execution ends.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
1013
+ echo ' <hr />'."\n";
1014
+ echo ' <h3>'.sprintf(__('Writing "Advanced Cache" Plugins Specifically for %1$s', 'comet-cache'), esc_html(NAME)).'</h3>'."\n";
1015
+ echo ' <p>'.sprintf(__('Theme/plugin developers can take advantage of the %1$s plugin architecture by creating PHP files inside this special directory: <code>/wp-content/ac-plugins/</code>. There is an <a href="http://cometcache.com/r/ac-plugin-example/" target="_blank">example plugin file @ GitHub</a> (please review it carefully and ask questions). If you develop a plugin for %1$s, please share it with the community by publishing it in the plugins respository at WordPress.org.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
1016
+ echo ' <p class="info">'.sprintf(__('<strong>Why does %1$s have it\'s own plugin architecture?</strong> WordPress loads the <code>advanced-cache.php</code> drop-in file (for caching purposes) very early-on; before any other plugins or a theme. For this reason, %1$s implements it\'s own watered-down version of functions like <code>add_action()</code>, <code>do_action()</code>, <code>add_filter()</code>, <code>apply_filters()</code>.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
1017
+ echo ' </div>'."\n";
1018
+
1019
+ echo '</div>'."\n";
1020
+
1021
+ /* ----------------------------------------------------------------------------------------- */
1022
+
1023
+ if (IS_PRO || $this->plugin->isProPreview()) {
1024
+ echo '<div class="plugin-menu-page-panel'.(!IS_PRO ? ' pro-preview' : '').'">'."\n";
1025
+
1026
+ echo ' <a href="#" class="plugin-menu-page-panel-heading'.(!IS_PRO ? ' pro-preview-feature' : '').'">'."\n";
1027
+ echo ' <i class="si si-arrow-circle-o-up"></i> '.__('Import/Export Options', 'comet-cache')."\n";
1028
+ echo ' </a>'."\n";
1029
+
1030
+ echo ' <div class="plugin-menu-page-panel-body clearfix">'."\n";
1031
+ echo ' <i class="si si-arrow-circle-o-up si-4x" style="float:right; margin: 0 0 0 25px;"></i>'."\n";
1032
+ echo ' <h3>'.sprintf(__('Import Options from Another %1$s Installation?', 'comet-cache'), esc_html(NAME)).'</h3>'."\n";
1033
+ echo ' <p>'.sprintf(__('Upload your <code>%1$s-options.json</code> file and click "Save All Changes" below. The options provided by your import file will override any that exist currently.', 'comet-cache'), GLOBAL_NS).'</p>'."\n";
1034
+ echo ' <p><input type="file" name="'.esc_attr(GLOBAL_NS).'[import_options]" /></p>'."\n";
1035
+ echo ' <hr />'."\n";
1036
+ echo ' <h3>'.sprintf(__('Export Existing Options from this %1$s Installation?', 'comet-cache'), esc_html(NAME)).'</h3>'."\n";
1037
+ echo ' <button type="button" class="plugin-menu-page-export-options" style="float:right; margin: 0 0 0 25px;"'.// Exports existing options from this installation.
1038
+ ' data-action="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS, '_wpnonce' => wp_create_nonce(), GLOBAL_NS => array('exportOptions' => '1'))), self_admin_url('/admin.php'))).'">'.
1039
+ ' '.sprintf(__('%1$s-options.json', 'comet-cache'), GLOBAL_NS).' <i class="si si-arrow-circle-o-down"></i></button>'."\n";
1040
+ echo ' <p>'.sprintf(__('Download your existing options and import them all into another %1$s installation; saves time on future installs.', 'comet-cache'), esc_html(NAME)).'</p>'."\n";
1041
+ echo ' </div>'."\n";
1042
+
1043
+ echo '</div>'."\n";
1044
+ }
1045
+ /* ----------------------------------------------------------------------------------------- */
1046
+
1047
+ echo '<div class="plugin-menu-page-save">'."\n";
1048
+ echo ' <button type="submit">'.__('Save All Changes', 'comet-cache').' <i class="si si-save"></i></button>'."\n";
1049
+ echo '</div>'."\n";
1050
+
1051
+ /* ----------------------------------------------------------------------------------------- */
1052
+
1053
+ echo '</div>'."\n";
1054
+ echo '</form>';
1055
+ }
1056
+ }
src/includes/classes/Plugin.php ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Comet Cache Plugin.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class Plugin extends AbsBaseAp
10
+ {
11
+ /**
12
+ * Enable plugin hooks?
13
+ *
14
+ * @since 150422 Rewrite.
15
+ *
16
+ * @type bool If `FALSE`, run without hooks.
17
+ */
18
+ public $enable_hooks = true;
19
+
20
+ /**
21
+ * Pro-only option keys.
22
+ *
23
+ * @since 150422 Rewrite.
24
+ *
25
+ * @type array Pro-only option keys.
26
+ */
27
+ public $pro_only_option_keys = array();
28
+
29
+ /**
30
+ * Default options.
31
+ *
32
+ * @since 150422 Rewrite.
33
+ *
34
+ * @type array Default options.
35
+ */
36
+ public $default_options = array();
37
+
38
+ /**
39
+ * Configured options.
40
+ *
41
+ * @since 150422 Rewrite.
42
+ *
43
+ * @type array Configured options.
44
+ */
45
+ public $options = array();
46
+
47
+ /**
48
+ * WordPress capability.
49
+ *
50
+ * @since 150422 Rewrite.
51
+ *
52
+ * @type string WordPress capability.
53
+ */
54
+ public $cap = 'activate_plugins';
55
+
56
+ /**
57
+ * WordPress capability.
58
+ *
59
+ * @since 150422 Rewrite.
60
+ *
61
+ * @type string WordPress capability.
62
+ */
63
+ public $update_cap = 'update_plugins';
64
+
65
+ /**
66
+ * WordPress capability.
67
+ *
68
+ * @since 150422 Rewrite.
69
+ *
70
+ * @type string WordPress capability.
71
+ */
72
+ public $network_cap = 'manage_network_plugins';
73
+
74
+ /**
75
+ * WordPress capability.
76
+ *
77
+ * @since 150422 Rewrite.
78
+ *
79
+ * @type string WordPress capability.
80
+ */
81
+ public $uninstall_cap = 'delete_plugins';
82
+
83
+
84
+
85
+
86
+
87
+ /**
88
+ * Cache directory.
89
+ *
90
+ * @since 150422 Rewrite.
91
+ *
92
+ * @type string Cache directory; relative to the configured base directory.
93
+ */
94
+ public $cache_sub_dir = 'cache';
95
+
96
+
97
+
98
+
99
+
100
+ /**
101
+ * Plugin constructor.
102
+ *
103
+ * @since 150422 Rewrite.
104
+ *
105
+ * @param bool $enable_hooks Defaults to `TRUE`.
106
+ */
107
+ public function __construct($enable_hooks = true)
108
+ {
109
+ parent::__construct();
110
+
111
+ $closures_dir = dirname(dirname(__FILE__)).'/closures/Plugin';
112
+ $self = $this; // Reference for closures.
113
+
114
+ foreach (scandir($closures_dir) as $_closure) {
115
+ if (substr($_closure, -4) === '.php') {
116
+ require $closures_dir.'/'.$_closure;
117
+ }
118
+ }
119
+ unset($_closure); // Housekeeping.
120
+ /* -------------------------------------------------------------- */
121
+
122
+ if (!($this->enable_hooks = (boolean) $enable_hooks)) {
123
+ return; // Stop here; construct without hooks.
124
+ }
125
+ /* -------------------------------------------------------------- */
126
+
127
+ add_action('after_setup_theme', array($this, 'setup'));
128
+ register_activation_hook(PLUGIN_FILE, array($this, 'activate'));
129
+ register_deactivation_hook(PLUGIN_FILE, array($this, 'deactivate'));
130
+ }
131
+
132
+ /**
133
+ * Plugin Setup.
134
+ *
135
+ * @since 150422 Rewrite.
136
+ */
137
+ public function setup()
138
+ {
139
+ if (!is_null($setup = &$this->cacheKey(__FUNCTION__))) {
140
+ return; // Already setup.
141
+ }
142
+ $setup = -1; // Flag as having been setup.
143
+
144
+ if ($this->enable_hooks) {
145
+ $this->doWpAction('before_'.GLOBAL_NS.'_'.__FUNCTION__, get_defined_vars());
146
+ }
147
+ /* -------------------------------------------------------------- */
148
+
149
+ load_plugin_textdomain(SLUG_TD); // Text domain.
150
+
151
+ $this->pro_only_option_keys = array(
152
+ 'cache_max_age_disable_if_load_average_is_gte',
153
+
154
+ 'change_notifications_enable',
155
+
156
+ 'cache_clear_admin_bar_enable',
157
+ 'cache_clear_admin_bar_options_enable',
158
+ 'cache_clear_admin_bar_roles_caps',
159
+
160
+ 'cache_clear_cdn_enable',
161
+ 'cache_clear_opcache_enable',
162
+ 'cache_clear_s2clean_enable',
163
+ 'cache_clear_eval_code',
164
+ 'cache_clear_urls',
165
+
166
+ 'when_logged_in',
167
+ 'version_salt',
168
+
169
+ 'htmlc_enable',
170
+ 'htmlc_css_exclusions',
171
+ 'htmlc_js_exclusions',
172
+ 'htmlc_uri_exclusions',
173
+ 'htmlc_cache_expiration_time',
174
+ 'htmlc_compress_combine_head_body_css',
175
+ 'htmlc_compress_combine_head_js',
176
+ 'htmlc_compress_combine_footer_js',
177
+ 'htmlc_compress_combine_remote_css_js',
178
+ 'htmlc_compress_inline_js_code',
179
+ 'htmlc_compress_css_code',
180
+ 'htmlc_compress_js_code',
181
+ 'htmlc_compress_html_code',
182
+
183
+ 'auto_cache_enable',
184
+ 'auto_cache_max_time',
185
+ 'auto_cache_delay',
186
+ 'auto_cache_sitemap_url',
187
+ 'auto_cache_ms_children_too',
188
+ 'auto_cache_other_urls',
189
+ 'auto_cache_user_agent',
190
+
191
+ 'cdn_enable',
192
+ 'cdn_host',
193
+ 'cdn_hosts',
194
+ 'cdn_invalidation_var',
195
+ 'cdn_invalidation_counter',
196
+ 'cdn_over_ssl',
197
+ 'cdn_when_logged_in',
198
+ 'cdn_whitelisted_extensions',
199
+ 'cdn_blacklisted_extensions',
200
+ 'cdn_whitelisted_uri_patterns',
201
+ 'cdn_blacklisted_uri_patterns',
202
+
203
+ 'stats_enable',
204
+ 'stats_admin_bar_enable',
205
+ 'stats_admin_bar_roles_caps',
206
+
207
+ 'dir_stats_history_days',
208
+ 'dir_stats_refresh_time',
209
+ 'dir_stats_auto_refresh_max_resources',
210
+
211
+ 'pro_update_check',
212
+ 'pro_update_check_stable',
213
+ 'latest_pro_version',
214
+ 'last_pro_update_check',
215
+ 'pro_update_username',
216
+ 'pro_update_password',
217
+ 'last_pro_stats_log',
218
+ );
219
+ $this->default_options = array(
220
+ /* Core/systematic plugin options. */
221
+
222
+ 'version' => VERSION,
223
+ 'welcomed' => '0', // `0|1` welcomed yet?
224
+
225
+ 'crons_setup' => '0', // A timestamp when last set up.
226
+ 'crons_setup_on_namespace' => '', // The namespace on which they were set up.
227
+ 'crons_setup_with_cache_cleanup_schedule' => '', // The cleanup schedule selected by site owner during last setup.
228
+ 'crons_setup_on_wp_with_schedules' => '', // A sha1 hash of `wp_get_schedules()`
229
+
230
+ /* Primary switch; enable? */
231
+
232
+ 'enable' => '0', // `0|1`.
233
+
234
+ /* Related to debugging. */
235
+
236
+ 'debugging_enable' => '1',
237
+ // `0|1|2` // 2 indicates greater debugging detail.
238
+
239
+ /* Related to cache directory. */
240
+
241
+ 'base_dir' => 'cache/comet-cache', // Relative to `WP_CONTENT_DIR`.
242
+ 'cache_max_age' => '7 days', // `strtotime()` compatible.
243
+ 'cache_max_age_disable_if_load_average_is_gte' => '', // Load average; server-specific.
244
+ 'cache_cleanup_schedule' => 'hourly', // `every15m`, `hourly`, `twicedaily`, `daily`
245
+
246
+ /* Related to cache clearing. */
247
+
248
+ 'change_notifications_enable' => '1', // `0|1`.
249
+
250
+ 'cache_clear_admin_bar_enable' => '1', // `0|1`.
251
+ 'cache_clear_admin_bar_options_enable' => '1', // `0|1|2`.
252
+ 'cache_clear_admin_bar_roles_caps' => '', // Comma-delimited list of roles/caps.
253
+
254
+ 'cache_clear_cdn_enable' => '0', // `0|1`.
255
+ 'cache_clear_opcache_enable' => '0', // `0|1`.
256
+ 'cache_clear_s2clean_enable' => '0', // `0|1`.
257
+ 'cache_clear_eval_code' => '', // PHP code.
258
+ 'cache_clear_urls' => '', // Line-delimited list of URLs.
259
+ 'cache_clear_transients_enable' => '0', // `0|1`
260
+
261
+ 'cache_clear_xml_feeds_enable' => '1', // `0|1`.
262
+
263
+ 'cache_clear_xml_sitemaps_enable' => '1', // `0|1`.
264
+ 'cache_clear_xml_sitemap_patterns' => '/sitemap**.xml',
265
+ // Empty string or line-delimited patterns.
266
+
267
+ 'cache_clear_home_page_enable' => '1', // `0|1`.
268
+ 'cache_clear_posts_page_enable' => '1', // `0|1`.
269
+
270
+ 'cache_clear_custom_post_type_enable' => '1', // `0|1`.
271
+ 'cache_clear_author_page_enable' => '1', // `0|1`.
272
+
273
+ 'cache_clear_term_category_enable' => '1', // `0|1`.
274
+ 'cache_clear_term_post_tag_enable' => '1', // `0|1`.
275
+ 'cache_clear_term_other_enable' => '0', // `0|1`.
276
+
277
+ /* Misc. cache behaviors. */
278
+
279
+ 'allow_browser_cache' => '0', // `0|1`.
280
+ 'when_logged_in' => '0', // `0|1|postload`.
281
+ 'get_requests' => '0', // `0|1`.
282
+ 'feeds_enable' => '0', // `0|1`.
283
+ 'cache_404_requests' => '0', // `0|1`.
284
+ 'cache_nonce_values' => '0', // `0|1`.
285
+ 'cache_nonce_values_when_logged_in' => '0', // `0|1`.
286
+
287
+ /* Related to exclusions. */
288
+
289
+ 'exclude_uris' => '', // Empty string or line-delimited patterns.
290
+ 'exclude_client_side_uris' => '', // Line-delimited list of URIs.
291
+ 'exclude_refs' => '', // Empty string or line-delimited patterns.
292
+ 'exclude_agents' => 'w3c_validator', // Empty string or line-delimited patterns.
293
+
294
+ /* Related to version salt. */
295
+
296
+ 'version_salt' => '', // Any string value.
297
+
298
+ /* Related to HTML compressor. */
299
+
300
+ 'htmlc_enable' => '0', // Enable HTML compression?
301
+ 'htmlc_css_exclusions' => '', // Empty string or line-delimited patterns.
302
+ 'htmlc_js_exclusions' => '.php?', // Empty string or line-delimited patterns.
303
+ 'htmlc_uri_exclusions' => '', // Empty string or line-delimited patterns.
304
+ 'htmlc_cache_expiration_time' => '14 days', // `strtotime()` compatible.
305
+
306
+ 'htmlc_compress_combine_head_body_css' => '1', // `0|1`.
307
+ 'htmlc_compress_combine_head_js' => '1', // `0|1`.
308
+ 'htmlc_compress_combine_footer_js' => '1', // `0|1`.
309
+ 'htmlc_compress_combine_remote_css_js' => '1', // `0|1`.
310
+ 'htmlc_compress_inline_js_code' => '1', // `0|1`.
311
+ 'htmlc_compress_css_code' => '1', // `0|1`.
312
+ 'htmlc_compress_js_code' => '1', // `0|1`.
313
+ 'htmlc_compress_html_code' => '1', // `0|1`.
314
+
315
+ /* Related to auto-cache engine. */
316
+
317
+ 'auto_cache_enable' => '0', // `0|1`.
318
+ 'auto_cache_max_time' => '900', // In seconds.
319
+ 'auto_cache_delay' => '500', // In milliseconds.
320
+ 'auto_cache_sitemap_url' => 'sitemap.xml', // Relative to `site_url()`.
321
+ 'auto_cache_ms_children_too' => '0', // `0|1`. Try child blogs too?
322
+ 'auto_cache_other_urls' => '', // A line-delimited list of any other URLs.
323
+ 'auto_cache_user_agent' => 'WordPress',
324
+
325
+ /* Related to CDN functionality. */
326
+
327
+ 'cdn_enable' => '0', // `0|1`; enable CDN filters?
328
+
329
+ 'cdn_host' => '', // e.g., `d1v41qemfjie0l.cloudfront.net`
330
+ 'cdn_hosts' => '', // e.g., line-delimited list of CDN hosts.
331
+
332
+ 'cdn_invalidation_var' => 'iv', // A query string variable name.
333
+ 'cdn_invalidation_counter' => '1', // Current version counter.
334
+
335
+ 'cdn_over_ssl' => '0', // `0|1`; enable SSL compat?
336
+ 'cdn_when_logged_in' => '0', // `0|1`; enable when logged in?
337
+
338
+ 'cdn_whitelisted_extensions' => '', // Whitelisted extensions.
339
+ // This is a comma-delimited list. Delimiters may include of these: `[|;,\s]`.
340
+ // Defaults to all extensions supported by the WP media library; i.e. `wp_get_mime_types()`.
341
+
342
+ 'cdn_blacklisted_extensions' => '', // Blacklisted extensions.
343
+ // This is a comma-delimited list. Delimiters may include of these: `[|;,\s]`.
344
+
345
+ 'cdn_whitelisted_uri_patterns' => '', // A line-delimited list of inclusion patterns.
346
+ // Wildcards `*` are supported here. Matched against local file URIs.
347
+
348
+ 'cdn_blacklisted_uri_patterns' => '', // A line-delimited list of exclusion patterns.
349
+ // Wildcards `*` are supported here. Matched against local file URIs.
350
+
351
+ /* Related to statistics/charts. */
352
+
353
+ 'stats_enable' => is_multisite() && wp_is_large_network() ? '0' : '1',
354
+ 'stats_admin_bar_enable' => '1', // `0|1`; enable stats in admin bar?
355
+ 'stats_admin_bar_roles_caps' => '', // Comma-delimited list of roles/caps.
356
+
357
+ 'dir_stats_auto_refresh_max_resources' => '1500', // Don't use cache if less than this.
358
+ 'dir_stats_refresh_time' => '15 minutes', // `strtotime()` compatible.
359
+ 'dir_stats_history_days' => '30', // Numeric; number of days.
360
+
361
+ /* Related to automatic pro updates. */
362
+
363
+ 'lite_update_check' => '0', // `0|1`; enable?
364
+ 'latest_lite_version' => VERSION, // Latest version.
365
+ 'last_lite_update_check' => '0', // Timestamp.
366
+
367
+ 'pro_update_check' => '1', // `0|1`; enable?
368
+ 'pro_update_check_stable' => '1', // `0` for beta/RC checks; defaults to `1`
369
+ 'latest_pro_version' => VERSION, // Latest version.
370
+ 'last_pro_update_check' => '0', // Timestamp.
371
+
372
+ 'pro_update_username' => '', // Username.
373
+ 'pro_update_password' => '', // Password or license key.
374
+
375
+ /* Related to stats logging. */
376
+
377
+ 'last_pro_stats_log' => '0', // Timestamp.
378
+
379
+ /* Related to uninstallation routines. */
380
+
381
+ 'uninstall_on_deletion' => '0', // `0|1`.
382
+ );
383
+ $this->default_options = $this->applyWpFilters(GLOBAL_NS.'_default_options', $this->default_options);
384
+ $this->options = $this->getOptions(); // Filters, validates, and returns plugin options.
385
+
386
+ $this->cap = $this->applyWpFilters(GLOBAL_NS.'_cap', $this->cap);
387
+ $this->update_cap = $this->applyWpFilters(GLOBAL_NS.'_update_cap', $this->update_cap);
388
+ $this->network_cap = $this->applyWpFilters(GLOBAL_NS.'_network_cap', $this->network_cap);
389
+ $this->uninstall_cap = $this->applyWpFilters(GLOBAL_NS.'_uninstall_cap', $this->uninstall_cap);
390
+
391
+ /* -------------------------------------------------------------- */
392
+
393
+ if (!$this->enable_hooks || strcasecmp(PHP_SAPI, 'cli') === 0) {
394
+ return; // Stop here; setup without hooks.
395
+ }
396
+ /* -------------------------------------------------------------- */
397
+
398
+ add_action('init', array($this, 'checkAdvancedCache'));
399
+ add_action('init', array($this, 'checkBlogPaths'));
400
+ add_action('init', array($this, 'checkCronSetup'), PHP_INT_MAX);
401
+ add_action('wp_loaded', array($this, 'actions'));
402
+
403
+ add_action('admin_init', array($this, 'checkVersion'));
404
+ add_action('admin_init', array($this, 'maybeCheckLatestLiteVersion'));
405
+
406
+
407
+
408
+
409
+
410
+
411
+
412
+
413
+
414
+ add_action('admin_enqueue_scripts', array($this, 'enqueueAdminStyles'));
415
+ add_action('admin_enqueue_scripts', array($this, 'enqueueAdminScripts'));
416
+
417
+ add_action('admin_menu', array($this, 'addMenuPages'));
418
+ add_action('network_admin_menu', array($this, 'addNetworkMenuPages'));
419
+
420
+ add_action('all_admin_notices', array($this, 'allAdminNotices'));
421
+
422
+ add_filter('plugin_action_links_'.plugin_basename(PLUGIN_FILE), array($this, 'addSettingsLink'));
423
+
424
+ add_filter('enable_live_network_counts', array($this, 'updateBlogPaths'));
425
+
426
+ add_action('activated_plugin', array($this, 'autoClearOnPluginActivationDeactivation'), 10, 2);
427
+ add_action('deactivated_plugin', array($this, 'autoClearOnPluginActivationDeactivation'), 10, 2);
428
+ add_action('admin_init', array($this, 'autoClearCacheOnSettingChanges'));
429
+ add_action('safecss_save_pre', array($this, 'autoClearCacheOnJetpackCustomCss'), 10, 1);
430
+ add_action('upgrader_process_complete', array($this, 'autoClearOnUpgraderProcessComplete'), 10, 2);
431
+
432
+ add_action('switch_theme', array($this, 'autoClearCache'));
433
+ add_action('wp_create_nav_menu', array($this, 'autoClearCache'));
434
+ add_action('wp_update_nav_menu', array($this, 'autoClearCache'));
435
+ add_action('wp_delete_nav_menu', array($this, 'autoClearCache'));
436
+
437
+ add_action('save_post', array($this, 'autoClearPostCache'));
438
+ add_action('delete_post', array($this, 'autoClearPostCache'));
439
+ add_action('clean_post_cache', array($this, 'autoClearPostCache'));
440
+ add_action('post_updated', array($this, 'autoClearAuthorPageCache'), 10, 3);
441
+ add_action('pre_post_update', array($this, 'autoClearPostCacheTransition'), 10, 2);
442
+ add_action('woocommerce_product_set_stock', array($this, 'autoClearPostCacheOnWooCommerceSetStock'), 10, 1);
443
+
444
+ add_action('added_term_relationship', array($this, 'autoClearPostTermsCache'), 10, 1);
445
+ add_action('delete_term_relationships', array($this, 'autoClearPostTermsCache'), 10, 1);
446
+
447
+ add_action('trackback_post', array($this, 'autoClearCommentPostCache'));
448
+ add_action('pingback_post', array($this, 'autoClearCommentPostCache'));
449
+ add_action('comment_post', array($this, 'autoClearCommentPostCache'));
450
+ add_action('transition_comment_status', array($this, 'autoClearCommentPostCacheTransition'), 10, 3);
451
+
452
+ add_action('create_term', array($this, 'autoClearCache'));
453
+ add_action('edit_terms', array($this, 'autoClearCache'));
454
+ add_action('delete_term', array($this, 'autoClearCache'));
455
+
456
+ add_action('add_link', array($this, 'autoClearCache'));
457
+ add_action('edit_link', array($this, 'autoClearCache'));
458
+ add_action('delete_link', array($this, 'autoClearCache'));
459
+
460
+
461
+
462
+ if ($this->options['enable'] && $this->applyWpFilters(GLOBAL_NS.'_disable_akismet_comment_nonce', true)) {
463
+ add_filter('akismet_comment_nonce', function() {
464
+ return 'disabled-by-'.SLUG_TD; // MUST return a string literal that is not 'true' or '' (an empty string). See <http://bit.ly/1YItpdE>
465
+ }); // See also why the Akismet nonce should be disabled: <http://jas.xyz/1R23f5c>
466
+ }
467
+
468
+
469
+
470
+
471
+
472
+
473
+ /* -------------------------------------------------------------- */
474
+
475
+ if (!is_multisite() || is_main_site()) { // Main site only.
476
+ add_filter('cron_schedules', array($this, 'extendCronSchedules'));
477
+ add_action('_cron_'.GLOBAL_NS.'_cleanup', array($this, 'cleanupCache'));
478
+
479
+
480
+ }
481
+ /* -------------------------------------------------------------- */
482
+
483
+ $this->doWpAction('after_'.GLOBAL_NS.'_'.__FUNCTION__, get_defined_vars());
484
+ $this->doWpAction(GLOBAL_NS.'_'.__FUNCTION__.'_complete', get_defined_vars());
485
+ }
486
+ }
src/includes/classes/VsUpgrades.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Version-Specific Upgrades.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ class VsUpgrades extends AbsBase
10
+ {
11
+ /**
12
+ * @type string Version they are upgrading from.
13
+ *
14
+ * @since 150422 Rewrite.
15
+ */
16
+ protected $prev_version = '';
17
+
18
+ /**
19
+ * Class constructor.
20
+ *
21
+ * @since 150422 Rewrite.
22
+ *
23
+ * @param string $prev_version Version they are upgrading from.
24
+ */
25
+ public function __construct($prev_version)
26
+ {
27
+ parent::__construct();
28
+
29
+ $this->prev_version = (string) $prev_version;
30
+ $this->runHandlers(); // Run upgrade(s).
31
+ }
32
+
33
+ /**
34
+ * Runs upgrade handlers in the proper order.
35
+ *
36
+ * @since 150422 Rewrite.
37
+ */
38
+ protected function runHandlers()
39
+ {
40
+ $this->fromLte150807();
41
+ $this->fromLte151107();
42
+ $this->fromLte151114();
43
+ $this->fromZenCache();
44
+ }
45
+
46
+ /**
47
+ * Before we changed errors and blog-specific storage on a MS network.
48
+ *
49
+ * @since 151002 Improving multisite compat.
50
+ */
51
+ protected function fromLte150807()
52
+ {
53
+ if (version_compare($this->prev_version, '150807', '<=')) {
54
+ delete_site_option(GLOBAL_NS.'_errors'); // No longer necessary.
55
+
56
+ if (is_multisite() && is_array($child_blogs = wp_get_sites())) {
57
+ $current_site = get_current_site(); // Current site.
58
+ foreach ($child_blogs as $_child_blog) {
59
+ switch_to_blog($_child_blog['blog_id']);
60
+
61
+ delete_option(GLOBAL_NS.'_errors');
62
+ delete_option(GLOBAL_NS.'_notices');
63
+ delete_option(GLOBAL_NS.'_options');
64
+ delete_option(GLOBAL_NS.'_apc_warning_bypass');
65
+
66
+ if ((integer) $_child_blog['blog_id'] !== (integer) $current_site->blog_id) {
67
+ wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_auto_cache');
68
+ wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup');
69
+ }
70
+ restore_current_blog(); // Restore current blog.
71
+ }
72
+ unset($_child_blog); // Housekeeping.
73
+ }
74
+ if (is_array($existing_options = get_site_option(GLOBAL_NS.'_options'))) {
75
+ if (isset($existing_options['admin_bar_enable'])) {
76
+ $this->plugin->options['cache_clear_admin_bar_enable'] = $existing_options['admin_bar_enable'];
77
+ $this->plugin->updateOptions($this->plugin->options); // Save/update options.
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Before we changed the CDN Blacklisted Extensions and implemented htaccess tweaks to fix CORS errors.
85
+ * Also, before we changed the watered-down regex syntax for exclusion patterns.
86
+ *
87
+ * @since 151114 Adding `.htaccess` tweaks.
88
+ */
89
+ protected function fromLte151107()
90
+ {
91
+ if (version_compare($this->prev_version, '151107', '<=')) {
92
+ if (is_array($existing_options = get_site_option(GLOBAL_NS.'_options'))) {
93
+ if (!empty($existing_options['cache_clear_xml_sitemap_patterns']) && strpos($existing_options['cache_clear_xml_sitemap_patterns'], '**') === false) {
94
+ $this->plugin->options['cache_clear_xml_sitemap_patterns'] = str_replace('*', '**', $existing_options['cache_clear_xml_sitemap_patterns']);
95
+ }
96
+ if (!empty($existing_options['exclude_uris']) && strpos($existing_options['exclude_uris'], '**') === false) {
97
+ $this->plugin->options['exclude_uris'] = str_replace('*', '**', $existing_options['exclude_uris']);
98
+ }
99
+ if (!empty($existing_options['exclude_refs']) && strpos($existing_options['exclude_refs'], '**') === false) {
100
+ $this->plugin->options['exclude_refs'] = str_replace('*', '**', $existing_options['exclude_refs']);
101
+ }
102
+ if (!empty($existing_options['exclude_agents']) && strpos($existing_options['exclude_agents'], '**') === false) {
103
+ $this->plugin->options['exclude_agents'] = str_replace('*', '**', $existing_options['exclude_agents']);
104
+ }
105
+ if (!empty($existing_options['htmlc_css_exclusions']) && strpos($existing_options['htmlc_css_exclusions'], '**') === false) {
106
+ $this->plugin->options['htmlc_css_exclusions'] = str_replace('*', '**', $existing_options['htmlc_css_exclusions']);
107
+ }
108
+ if (!empty($existing_options['htmlc_js_exclusions']) && strpos($existing_options['htmlc_js_exclusions'], '**') === false) {
109
+ $this->plugin->options['htmlc_js_exclusions'] = str_replace('*', '**', $existing_options['htmlc_js_exclusions']);
110
+ }
111
+ if ($existing_options['cdn_blacklisted_extensions'] === 'eot,ttf,otf,woff') {
112
+ // See: <https://github.com/websharks/zencache/issues/427#issuecomment-121777790>
113
+ $this->plugin->options['cdn_blacklisted_extensions'] = $this->plugin->default_options['cdn_blacklisted_extensions'];
114
+ }
115
+ if ($this->plugin->options !== $existing_options) {
116
+ $this->plugin->updateOptions($this->plugin->options); // Save/update options.
117
+ $this->plugin->activate(); // Reactivate plugin w/ new options.
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Before we changed the htaccess comment blocks to contain a unique identifier.
125
+ *
126
+ * @since 151220 Improving `.htaccess` tweaks.
127
+ */
128
+ protected function fromLte151114()
129
+ {
130
+ if (version_compare($this->prev_version, '151114', '<=')) {
131
+ global $is_apache;
132
+
133
+ if (!$is_apache) {
134
+ return; // Not running the Apache web server.
135
+ }
136
+ if (!($htaccess_file = $this->plugin->findHtaccessFile())) {
137
+ return; // File does not exist.
138
+ }
139
+ if (!$this->plugin->findHtaccessMarker('Comet Cache')) {
140
+ return; // Template blocks are already gone.
141
+ }
142
+ if ($htaccess = $this->plugin->readHtaccessFile($htaccess_file)) {
143
+ if (is_dir($templates_dir = dirname(dirname(__FILE__)).'/templates/htaccess/back-compat')) {
144
+ $htaccess['file_contents'] = str_replace(file_get_contents($templates_dir.'/v151114.txt'), '', $htaccess['file_contents']);
145
+ $htaccess['file_contents'] = str_replace(file_get_contents($templates_dir.'/v151114-2.txt'), '', $htaccess['file_contents']);
146
+ $htaccess['file_contents'] = trim($htaccess['file_contents']);
147
+
148
+ if (!$this->plugin->writeHtaccessFile($htaccess, false)) {
149
+ return; // Failure; could not write changes.
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Before we changed the name to Comet Cache.
158
+ *
159
+ * If so, we need to uninstall and deactivate ZenCache.
160
+ *
161
+ * @since 16xxxx Rebranding.
162
+ */
163
+ protected function fromZenCache()
164
+ {
165
+ if (is_array($zencache_options = get_site_option('zencache_options'))) {
166
+ delete_site_option('zencache_errors');
167
+ delete_site_option('zencache_notices');
168
+ delete_site_option('zencache_options');
169
+
170
+ if (is_multisite()) { // Main site CRON jobs.
171
+ switch_to_blog(get_current_site()->blog_id);
172
+ wp_clear_scheduled_hook('_cron_zencache_auto_cache');
173
+ wp_clear_scheduled_hook('_cron_zencache_cleanup');
174
+ restore_current_blog(); // Restore.
175
+ } else {
176
+ wp_clear_scheduled_hook('_cron_zencache_auto_cache');
177
+ wp_clear_scheduled_hook('_cron_zencache_cleanup');
178
+ }
179
+ deactivate_plugins(array('zencache/zencache.php', 'zencache-pro/zencache-pro.php'), true);
180
+
181
+ if (!empty($zencache_options['base_dir'])) {
182
+ $this->plugin->deleteAllFilesDirsIn(WP_CONTENT_DIR.'/'.trim($zencache_options['base_dir'], '/'), true);
183
+ }
184
+ $this->plugin->deleteBaseDir(); // Let's be extra sure that the old base directory is gone.
185
+
186
+ $this->plugin->options['base_dir'] = $this->plugin->default_options['base_dir'];
187
+ $this->plugin->options['crons_setup'] = $this->plugin->default_options['crons_setup'];
188
+
189
+ $this->plugin->updateOptions($this->plugin->options); // Save/update options.
190
+ $this->plugin->activate(); // Reactivate plugin w/ new options.
191
+
192
+ $this->plugin->enqueueMainNotice(
193
+ '<p>'.sprintf(__('<strong>Woohoo! %1$s activated.</strong> :-)', 'comet-cache'), esc_html(NAME)).'</p>'.
194
+ '<p>'.sprintf(__('NOTE: Your ZenCache options were preserved by %1$s (for more details, visit the <a href="%2$s" target="_blank">Migration FAQ</a>).'.'', 'comet-cache'), esc_html(NAME), esc_attr(IS_PRO ? 'http://cometcache.com/r/zencache-pro-migration-faq/' : 'https://cometcache.com/r/zencache-migration-faq/')).'</p>'.
195
+ '<p>'.sprintf(__('To review your configuration, please see: <a href="%2$s">%1$s ⥱ Plugin Options</a>.'.'', 'comet-cache'), esc_html(NAME), esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS)), self_admin_url('/admin.php')))).'</p>'
196
+ );
197
+ }
198
+ }
199
+
200
+ }
src/includes/closures/Ac/AbortUtils.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Ignores user aborts; when/if the Auto-Cache Engine is running.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ $self->maybeIgnoreUserAbort = function () use ($self) {
10
+
11
+ };
src/includes/closures/Ac/AcPluginUtils.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Loads any advanced cache plugin files found inside `/wp-content/ac-plugins`.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ */
9
+ $self->loadAcPlugins = function () use ($self) {
10
+ if (!is_dir(WP_CONTENT_DIR.'/ac-plugins')) {
11
+ return; // Nothing to do here.
12
+ }
13
+ $GLOBALS[GLOBAL_NS.'_advanced_cache'] = $self; // Self reference.
14
+ $GLOBALS[GLOBAL_NS.'__advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache'];
15
+ if (!isset($GLOBALS['zencache__advanced_cache'])) {
16
+ $GLOBALS['zencache_advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache'];
17
+ $GLOBALS['zencache__advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache'];
18
+ }
19
+ foreach ((array) glob(WP_CONTENT_DIR.'/ac-plugins/*.php') as $_ac_plugin) {
20
+ if (is_file($_ac_plugin)) {
21
+ include_once $_ac_plugin;
22
+ }
23
+ }
24
+ unset($_ac_plugin); // Houskeeping.
25
+ };
src/includes/closures/Ac/BrowserUtils.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Sends no-cache headers (if applicable).
6
+ *
7
+ * @since 150422 Rewrite. Enhanced/altered 151220.
8
+ */
9
+ $self->maybeStopBrowserCaching = function () use ($self) {
10
+ switch ((bool) COMET_CACHE_ALLOW_BROWSER_CACHE) {
11
+
12
+ case true: // If global config allows, check exclusions.
13
+
14
+ if (isset($_GET[strtolower(SHORT_NAME).'ABC'])) {
15
+ if (!filter_var($_GET[strtolower(SHORT_NAME).'ABC'], FILTER_VALIDATE_BOOLEAN)) {
16
+ return $self->sendNoCacheHeaders(); // Disallow.
17
+ } // Else, allow client-side caching; because `ABC` is a true-ish value.
18
+ // ↑ Note that exclusion patterns are ignored in this case, in favor of `ABC`.
19
+ } elseif (COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS && preg_match(COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS, $_SERVER['REQUEST_URI'])) {
20
+ return $self->sendNoCacheHeaders(); // Disallow.
21
+ }
22
+ return; // Allow browser caching; default behavior in this mode.
23
+
24
+ case false: // Global config disallows; check inclusions.
25
+
26
+ if (isset($_GET[strtolower(SHORT_NAME).'ABC'])) {
27
+ if (filter_var($_GET[strtolower(SHORT_NAME).'ABC'], FILTER_VALIDATE_BOOLEAN)) {
28
+ return; // Allow, because `ABC` is a false-ish value.
29
+ } // Else, disallow client-side caching; because `ABC` is a true-ish value.
30
+ // ↑ Note that inclusion patterns are ignored in this case, in favor of `ABC`.
31
+ }
32
+ return $self->sendNoCacheHeaders(); // Disallow; default behavior in this mode.
33
+ }
34
+ };
src/includes/closures/Ac/NcDebugConsts.php ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ if (defined(__NAMESPACE__.'\\NC_DEBUG_PHP_SAPI_CLI')) {
5
+ return; // Already defined these.
6
+ }
7
+ /**
8
+ * No-cache because of the current {@link \PHP_SAPI}.
9
+ *
10
+ * @since 140422 First documented version.
11
+ *
12
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
13
+ */
14
+ const NC_DEBUG_PHP_SAPI_CLI = 'nc_debug_php_sapi_cli';
15
+
16
+ /**
17
+ * No-cache because of a missing http host.
18
+ *
19
+ * @since 140422 First documented version.
20
+ *
21
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
22
+ */
23
+ const NC_DEBUG_NO_SERVER_HTTP_HOST = 'nc_debug_no_server_http_host';
24
+
25
+ /**
26
+ * No-cache because of a missing `$_SERVER['REQUEST_URI']`.
27
+ *
28
+ * @since 140422 First documented version.
29
+ *
30
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
31
+ */
32
+ const NC_DEBUG_NO_SERVER_REQUEST_URI = 'nc_debug_no_server_request_uri';
33
+
34
+ /**
35
+ * No-cache because the {@link \COMET_CACHE_ALLOWED} constant says not to.
36
+ *
37
+ * @since 140422 First documented version.
38
+ *
39
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
40
+ */
41
+ const NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT = 'nc_debug_comet_cache_allowed_constant';
42
+
43
+ /**
44
+ * No-cache because the `$_SERVER['COMET_CACHE_ALLOWED']` environment variable says not to.
45
+ *
46
+ * @since 140422 First documented version.
47
+ *
48
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
49
+ */
50
+ const NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR = 'nc_debug_comet_cache_allowed_server_var';
51
+
52
+ /**
53
+ * No-cache because the {@link \DONOTCACHEPAGE} constant says not to.
54
+ *
55
+ * @since 140422 First documented version.
56
+ *
57
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
58
+ */
59
+ const NC_DEBUG_DONOTCACHEPAGE_CONSTANT = 'nc_debug_donotcachepage_constant';
60
+
61
+ /**
62
+ * No-cache because the `$_SERVER['DONOTCACHEPAGE']` environment variable says not to.
63
+ *
64
+ * @since 140422 First documented version.
65
+ *
66
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
67
+ */
68
+ const NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR = 'nc_debug_donotcachepage_server_var';
69
+
70
+ /**
71
+ * No-cache because the current request includes the `?[SHORT_NAME]AC=0` parameter.
72
+ *
73
+ * @since 140422 First documented version.
74
+ *
75
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
76
+ */
77
+ const NC_DEBUG_AC_GET_VAR = 'nc_debug_ac_get_var';
78
+
79
+ /**
80
+ * No-cache because the current request method is `POST|PUT|DELETE`.
81
+ *
82
+ * @since 140422 First documented version.
83
+ *
84
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
85
+ */
86
+ const NC_DEBUG_UNCACHEABLE_REQUEST = 'nc_debug_post_put_del_request';
87
+
88
+ /**
89
+ * No-cache because the current request originated from the server itself.
90
+ *
91
+ * @since 140422 First documented version.
92
+ *
93
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
94
+ */
95
+ const NC_DEBUG_SELF_SERVE_REQUEST = 'nc_debug_self_serve_request';
96
+
97
+ /**
98
+ * No-cache because the current request is for a feed.
99
+ *
100
+ * @since 140422 First documented version.
101
+ *
102
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
103
+ */
104
+ const NC_DEBUG_FEED_REQUEST = 'nc_debug_feed_request';
105
+
106
+ /**
107
+ * No-cache because the current request is systematic.
108
+ *
109
+ * @since 140422 First documented version.
110
+ *
111
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
112
+ */
113
+ const NC_DEBUG_WP_SYSTEMATICS = 'nc_debug_wp_systematics';
114
+
115
+ /**
116
+ * No-cache because the current request is for an administrative area.
117
+ *
118
+ * @since 140422 First documented version.
119
+ *
120
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
121
+ */
122
+ const NC_DEBUG_WP_ADMIN = 'nc_debug_wp_admin';
123
+
124
+ /**
125
+ * No-cache because the current request is multisite `/files/`.
126
+ *
127
+ * @since 140422 First documented version.
128
+ *
129
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
130
+ */
131
+ const NC_DEBUG_MS_FILES = 'nc_debug_ms_files';
132
+
133
+ /**
134
+ * No-cache because the current user is like a logged-in user.
135
+ *
136
+ * @since 140422 First documented version.
137
+ *
138
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
139
+ */
140
+ const NC_DEBUG_IS_LIKE_LOGGED_IN_USER = 'nc_debug_is_like_logged_in_user';
141
+
142
+ /**
143
+ * No-cache because the current user is logged into the site.
144
+ *
145
+ * @since 140422 First documented version.
146
+ *
147
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
148
+ */
149
+ const NC_DEBUG_IS_LOGGED_IN_USER = 'nc_debug_is_logged_in_user';
150
+
151
+ /**
152
+ * No-cache because the current user is logged into the site and the current page contains an `nonce`.
153
+ *
154
+ * @since 151220 Enhancing logged-in user caching support.
155
+ *
156
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
157
+ */
158
+ const NC_DEBUG_IS_LOGGED_IN_USER_NONCE = 'nc_debug_is_logged_in_user_nonce';
159
+
160
+ /**
161
+ * No-cache because the current page contains an `nonce`.
162
+ *
163
+ * @since 151220 Enhancing `nonce` detection.
164
+ *
165
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
166
+ */
167
+ const NC_DEBUG_PAGE_CONTAINS_NONCE = 'nc_debug_page_contains_nonce';
168
+
169
+ /**
170
+ * No-cache because it was not possible to acquire a user token.
171
+ *
172
+ * @since 140422 First documented version.
173
+ *
174
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
175
+ */
176
+ const NC_DEBUG_NO_USER_TOKEN = 'nc_debug_no_user_token';
177
+
178
+ /**
179
+ * No-cache because the current request contains a query string.
180
+ *
181
+ * @since 140422 First documented version.
182
+ *
183
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
184
+ */
185
+ const NC_DEBUG_GET_REQUEST_QUERIES = 'nc_debug_get_request_queries';
186
+
187
+ /**
188
+ * No-cache because it's a preview.
189
+ *
190
+ * @since 151114 Adding support for preview detection.
191
+ *
192
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
193
+ */
194
+ const NC_DEBUG_PREVIEW = 'nc_debug_preview';
195
+
196
+ /**
197
+ * No-cache because the current request excluded by its URI.
198
+ *
199
+ * @since 140422 First documented version.
200
+ *
201
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
202
+ */
203
+ const NC_DEBUG_EXCLUDED_URIS = 'nc_debug_excluded_uris';
204
+
205
+ /**
206
+ * No-cache because the current user-agent is excluded.
207
+ *
208
+ * @since 140422 First documented version.
209
+ *
210
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
211
+ */
212
+ const NC_DEBUG_EXCLUDED_AGENTS = 'nc_debug_excluded_agents';
213
+
214
+ /**
215
+ * No-cache because the current HTTP referrer is excluded.
216
+ *
217
+ * @since 140422 First documented version.
218
+ *
219
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
220
+ */
221
+ const NC_DEBUG_EXCLUDED_REFS = 'nc_debug_excluded_refs';
222
+
223
+ /**
224
+ * No-cache because the current request is a 404 error.
225
+ *
226
+ * @since 140422 First documented version.
227
+ *
228
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
229
+ */
230
+ const NC_DEBUG_404_REQUEST = 'nc_debug_404_request';
231
+
232
+ /**
233
+ * No-cache because the requested page is currently in maintenance mode.
234
+ *
235
+ * @since 140422 First documented version.
236
+ *
237
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
238
+ */
239
+ const NC_DEBUG_MAINTENANCE_PLUGIN = 'nc_debug_maintenance_plugin';
240
+
241
+ /**
242
+ * No-cache because the current request is being compressed by an incompatible ZLIB coding type.
243
+ *
244
+ * @since 140422 First documented version.
245
+ *
246
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
247
+ */
248
+ const NC_DEBUG_OB_ZLIB_CODING_TYPE = 'nc_debug_ob_zlib_coding_type';
249
+
250
+ /**
251
+ * No-cache because the current request resulted in a WP error message.
252
+ *
253
+ * @since 140422 First documented version.
254
+ *
255
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
256
+ */
257
+ const NC_DEBUG_WP_ERROR_PAGE = 'nc_debug_wp_error_page';
258
+
259
+ /**
260
+ * No-cache because the current request is serving an uncacheable content type.
261
+ *
262
+ * @since 140422 First documented version.
263
+ *
264
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
265
+ */
266
+ const NC_DEBUG_UNCACHEABLE_CONTENT_TYPE = 'nc_debug_uncacheable_content_type';
267
+
268
+ /**
269
+ * No-cache because the current request sent a non-2xx & non-404 status code.
270
+ *
271
+ * @since 140422 First documented version.
272
+ *
273
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
274
+ */
275
+ const NC_DEBUG_UNCACHEABLE_STATUS = 'nc_debug_uncacheable_status';
276
+
277
+ /**
278
+ * No-cache because this is a new 404 error that we are symlinking.
279
+ *
280
+ * @since 140422 First documented version.
281
+ *
282
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
283
+ */
284
+ const NC_DEBUG_1ST_TIME_404_SYMLINK = 'nc_debug_1st_time_404_symlink';
285
+
286
+ /**
287
+ * No-cache because we detected an early buffer termination.
288
+ *
289
+ * @since 140605 Improving output buffer.
290
+ *
291
+ * @type string A unique string identifier in the set of `NC_DEBUG_` constants.
292
+ */
293
+ const NC_DEBUG_EARLY_BUFFER_TERMINATION = 'nc_debug_early_buffer_termination';
src/includes/closures/Ac/NcDebugUtils.php ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * An array of debug info.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @type array An array of debug info; i.e. `reason_code` and `reason` (optional).
10
+ */
11
+ $self->debug_info = array('reason_code' => '', 'reason' => '');
12
+
13
+ /*
14
+ * Used to setup debug info (if enabled).
15
+ *
16
+ * @since 150422 Rewrite.
17
+ *
18
+ * @param string $reason_code One of the `NC_DEBUG_` constants.
19
+ * @param string $reason Optionally override the built-in description with a custom message.
20
+ */
21
+ $self->maybeSetDebugInfo = function ($reason_code, $reason = '') use ($self) {
22
+ if (!COMET_CACHE_DEBUGGING_ENABLE) {
23
+ return; // Nothing to do.
24
+ }
25
+ $reason = (string) $reason;
26
+ if (!($reason_code = (string) $reason_code)) {
27
+ return; // Not applicable.
28
+ }
29
+ $self->debug_info = array('reason_code' => $reason_code, 'reason' => $reason);
30
+ };
31
+
32
+ /*
33
+ * Echoes `NC_DEBUG_` info in the WordPress `shutdown` phase (if applicable).
34
+ *
35
+ * @since 150422 Rewrite.
36
+ *
37
+ * @attaches-to `shutdown` hook in WordPress w/ a late priority.
38
+ */
39
+ $self->maybeEchoNcDebugInfo = function () use ($self) {
40
+ if (!COMET_CACHE_DEBUGGING_ENABLE) {
41
+ return; // Nothing to do.
42
+ }
43
+ if (is_admin()) {
44
+ return; // Not applicable.
45
+ }
46
+ if (strcasecmp(PHP_SAPI, 'cli') === 0) {
47
+ return; // Let's not run the risk here.
48
+ }
49
+ if ($self->debug_info && $self->hasACacheableContentType() && $self->is_a_wp_content_type) {
50
+ echo (string) $self->maybeGetNcDebugInfo($self->debug_info['reason_code'], $self->debug_info['reason']);
51
+ }
52
+ };
53
+
54
+ /*
55
+ * Gets `NC_DEBUG_` info (if applicable).
56
+ *
57
+ * @since 150422 Rewrite.
58
+ *
59
+ * @param string $reason_code One of the `NC_DEBUG_` constants.
60
+ * @param string $reason Optional; to override the default description with a custom message.
61
+ *
62
+ * @return string The debug info; i.e. full description (if applicable).
63
+ */
64
+ $self->maybeGetNcDebugInfo = function ($reason_code = '', $reason = '') use ($self) {
65
+ if (!COMET_CACHE_DEBUGGING_ENABLE) {
66
+ return ''; // Not applicable.
67
+ }
68
+ $reason = (string) $reason;
69
+ if (!($reason_code = (string) $reason_code)) {
70
+ return ''; // Not applicable.
71
+ }
72
+ if (!$reason) {
73
+ switch ($reason_code) {
74
+ case NC_DEBUG_PHP_SAPI_CLI:
75
+ $reason = __('because `PHP_SAPI` reports that you are currently running from the command line.', 'comet-cache');
76
+ break; // Break switch handler.
77
+
78
+ case NC_DEBUG_NO_SERVER_HTTP_HOST:
79
+ $reason = __('because `$_SERVER[\'HTTP_HOST\']` is missing from your server configuration.', 'comet-cache');
80
+ break; // Break switch handler.
81
+
82
+ case NC_DEBUG_NO_SERVER_REQUEST_URI:
83
+ $reason = __('because `$_SERVER[\'REQUEST_URI\']` is missing from your server configuration.', 'comet-cache');
84
+ break; // Break switch handler.
85
+
86
+ case NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT:
87
+ if ($self->functionIsPossible('did_action') && did_action('ws_plugin__s2member_during_no_cache_constants')) {
88
+ $reason = __('because the s2Member plugin set the PHP constant `COMET_CACHE_ALLOWED` to a boolean-ish `FALSE` value at runtime. The s2Member plugin is serving content that must remain dynamic on this particular page, and therefore this page was intentionally not cached for a very good reason.', 'comet-cache');
89
+ } else {
90
+ $reason = __('because the PHP constant `COMET_CACHE_ALLOWED` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache');
91
+ }
92
+ break; // Break switch handler.
93
+
94
+ case NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR:
95
+ $reason = __('because the environment variable `$_SERVER[\'COMET_CACHE_ALLOWED\']` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache');
96
+ break; // Break switch handler.
97
+
98
+ case NC_DEBUG_DONOTCACHEPAGE_CONSTANT:
99
+ $reason = __('because the PHP constant `DONOTCACHEPAGE` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache');
100
+ break; // Break switch handler.
101
+
102
+ case NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR:
103
+ $reason = __('because the environment variable `$_SERVER[\'DONOTCACHEPAGE\']` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache');
104
+ break; // Break switch handler.
105
+
106
+ case NC_DEBUG_AC_GET_VAR:
107
+ $reason = sprintf(__('because `$_GET[\'%1$sAC\']` is set to a boolean-ish FALSE value.', 'comet-cache'), strtolower(SHORT_NAME));
108
+ break; // Break switch handler.
109
+
110
+ case NC_DEBUG_UNCACHEABLE_REQUEST:
111
+ $reason = __('because `$_SERVER[\'REQUEST_METHOD\']` is `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `TRACE` or `CONNECT`. These request methods should never (ever) be cached in any way.', 'comet-cache');
112
+ break; // Break switch handler.
113
+
114
+ case NC_DEBUG_SELF_SERVE_REQUEST:
115
+ $reason = __('because `[current IP address]` === `$_SERVER[\'SERVER_ADDR\']`; i.e. a self-serve request. DEVELOPER TIP: if you are testing on a localhost installation, please add `define(\'LOCALHOST\', TRUE);` to your `/wp-config.php` file while you run tests :-) Remove it (or set it to a `FALSE` value) once you go live on the web.', 'comet-cache');
116
+ break; // Break switch handler.
117
+
118
+ case NC_DEBUG_FEED_REQUEST:
119
+ $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a `/feed`; and the configuration of this site says not to cache XML-based feeds.', 'comet-cache');
120
+ break; // Break switch handler.
121
+
122
+ case NC_DEBUG_WP_SYSTEMATICS:
123
+ $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a `wp-` or `xmlrpc` file; i.e. a WordPress systematic file. WordPress systematics are never (ever) cached in any way.', 'comet-cache');
124
+ break; // Break switch handler.
125
+
126
+ case NC_DEBUG_WP_ADMIN:
127
+ $reason = __('because `$_SERVER[\'REQUEST_URI\']` or the `is_admin()` function indicates this is an administrative area of the site.', 'comet-cache');
128
+ break; // Break switch handler.
129
+
130
+ case NC_DEBUG_MS_FILES:
131
+ $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a Multisite Network; and this was a request for `/files/*`, not a page.', 'comet-cache');
132
+ break; // Break switch handler.
133
+
134
+ case NC_DEBUG_IS_LOGGED_IN_USER:
135
+ case NC_DEBUG_IS_LIKE_LOGGED_IN_USER:
136
+ $reason = __('because the current user visiting this page (usually YOU), appears to be logged-in. The current configuration says NOT to cache pages for logged-in visitors. This message may also appear if you have an active PHP session on this site, or if you\'ve left (or replied to) a comment recently. If this message continues, please clear your cookies and try again.', 'comet-cache');
137
+ break; // Break switch handler.
138
+
139
+ case NC_DEBUG_IS_LOGGED_IN_USER_NONCE:
140
+ $reason = __('because the current page contains `_wpnonce` or `akismet_comment_nonce`. While your current configuration states that pages SHOULD be cache for logged-in visitors, `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details.', 'comet-cache');
141
+ break; // Break switch handler.
142
+
143
+ case NC_DEBUG_PAGE_CONTAINS_NONCE:
144
+ $reason = __('because the current page contains `_wpnonce` or `akismet_comment_nonce`. Note that `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details.', 'comet-cache');
145
+ break; // Break switch handler.
146
+
147
+ case NC_DEBUG_NO_USER_TOKEN:
148
+ $reason = sprintf(__('because the current user appeared to be logged into the site (in one way or another); but %1$s was unable to formulate a User Token for them. Please report this as a possible bug.', 'comet-cache'), NAME);
149
+ break; // Break switch handler.
150
+
151
+ case NC_DEBUG_GET_REQUEST_QUERIES:
152
+ $reason = __('because `$_GET` contains query string data. The current configuration says NOT to cache GET requests with a query string.', 'comet-cache');
153
+ break; // Break switch handler.
154
+
155
+ case NC_DEBUG_PREVIEW:
156
+ $reason = __('because `$_REQUEST` indicates this is simply a preview of something to come.', 'comet-cache');
157
+ break; // Break switch handler.
158
+
159
+ case NC_DEBUG_EXCLUDED_URIS:
160
+ $reason = __('because `$_SERVER[\'REQUEST_URI\']` matches a configured URI Exclusion Pattern on this installation.', 'comet-cache');
161
+ break; // Break switch handler.
162
+
163
+ case NC_DEBUG_EXCLUDED_AGENTS:
164
+ $reason = __('because `$_SERVER[\'HTTP_USER_AGENT\']` matches a configured User-Agent Exclusion Pattern on this installation.', 'comet-cache');
165
+ break; // Break switch handler.
166
+
167
+ case NC_DEBUG_EXCLUDED_REFS:
168
+ $reason = __('because `$_SERVER[\'HTTP_REFERER\']` and/or `$_GET[\'_wp_http_referer\']` matches a configured HTTP Referrer Exclusion Pattern on this installation.', 'comet-cache');
169
+ break; // Break switch handler.
170
+
171
+ case NC_DEBUG_404_REQUEST:
172
+ $reason = __('because the WordPress `is_404()` Conditional Tag says the current page is a 404 error. The current configuration says NOT to cache 404 errors.', 'comet-cache');
173
+ break; // Break switch handler.
174
+
175
+ case NC_DEBUG_MAINTENANCE_PLUGIN:
176
+ $reason = __('because a plugin running on this installation says this page is in Maintenance Mode; i.e. is not available publicly at this time.', 'comet-cache');
177
+ break; // Break switch handler.
178
+
179
+ case NC_DEBUG_OB_ZLIB_CODING_TYPE:
180
+ $reason = sprintf(__('because %1$s is unable to cache already-compressed output. Please use `mod_deflate` w/ Apache; or use `zlib.output_compression` in your `php.ini` file. %1$s is NOT compatible with `ob_gzhandler()` and others like this.', 'comet-cache'), NAME);
181
+ break; // Break switch handler.
182
+
183
+ case NC_DEBUG_WP_ERROR_PAGE:
184
+ $reason = __('because the contents of this document contain `<body id="error-page">`, which indicates this is an auto-generated WordPress error message.', 'comet-cache');
185
+ break; // Break switch handler.
186
+
187
+ case NC_DEBUG_UNCACHEABLE_CONTENT_TYPE:
188
+ $reason = __('because a `Content-Type:` header was set via PHP at runtime. The header contains a MIME type which is NOT a variation of HTML or XML. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins.', 'comet-cache');
189
+ break; // Break switch handler.
190
+
191
+ case NC_DEBUG_UNCACHEABLE_STATUS:
192
+ $reason = __('because a `Status:` header (or an `HTTP/` header) was set via PHP at runtime. The header contains a non-`2xx` status code. This indicates the current page was not loaded successfully. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins.', 'comet-cache');
193
+ break; // Break switch handler.
194
+
195
+ case NC_DEBUG_1ST_TIME_404_SYMLINK:
196
+ $reason = sprintf(__('because the WordPress `is_404()` Conditional Tag says the current page is a 404 error; and this is the first time it\'s happened on this page. Your current configuration says that 404 errors SHOULD be cached, so %1$s built a cached symlink which points future requests for this location to your already-cached 404 error document. If you reload this page (assuming you don\'t clear the cache before you do so); you should get a cached version of your 404 error document. This message occurs ONCE for each new/unique 404 error request.', 'comet-cache'), NAME);
197
+ break; // Break switch handler.
198
+
199
+ case NC_DEBUG_EARLY_BUFFER_TERMINATION:
200
+ $reason = sprintf(__('because %1$s detected an early output buffer termination. This may happen when a theme/plugin ends, cleans, or flushes all output buffers before reaching the PHP shutdown phase. It\'s not always a bad thing. Sometimes it is necessary for a theme/plugin to do this. However, in this scenario it is NOT possible to cache the output; since %1$s is effectively disabled at runtime when this occurs.', 'comet-cache'), NAME);
201
+ break; // Break switch handler.
202
+
203
+ default: // Default case handler.
204
+ $reason = __('due to an unexpected behavior in the application. Please report this as a bug!', 'comet-cache');
205
+ break; // Break switch handler.
206
+ }
207
+ }
208
+ return "\n".'<!-- '.htmlspecialchars(sprintf(__('%1$s is NOT caching this page, %2$s', 'comet-cache'), NAME, $reason)).' -->';
209
+ };
src/includes/closures/Ac/ObUtils.php ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Calculated protocol; one of `http://` or `https://`.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @type float One of `http://` or `https://`.
10
+ */
11
+ $self->protocol = '';
12
+
13
+ /*
14
+ * Host token for this request.
15
+ *
16
+ * @since 150821 Improving multisite compat.
17
+ *
18
+ * @type string Host token for this request.
19
+ */
20
+ $self->host_token = '';
21
+
22
+ /*
23
+ * Host base/dir tokens for this request.
24
+ *
25
+ * @since 150821 Improving multisite compat.
26
+ *
27
+ * @type string Host base/dir tokens for this request.
28
+ */
29
+ $self->host_base_dir_tokens = '';
30
+
31
+ /*
32
+ * Calculated version salt; set by site configuration data.
33
+ *
34
+ * @since 150422 Rewrite.
35
+ *
36
+ * @type string|mixed Any scalar value does fine.
37
+ */
38
+ $self->version_salt = '';
39
+
40
+ /*
41
+ * Relative cache path for the current request.
42
+ *
43
+ * @since 150422 Rewrite.
44
+ *
45
+ * @type string Cache path for the current request.
46
+ */
47
+ $self->cache_path = '';
48
+
49
+ /*
50
+ * Absolute cache file path for the current request.
51
+ *
52
+ * @since 150422 Rewrite.
53
+ *
54
+ * @type string Absolute cache file path for the current request.
55
+ */
56
+ $self->cache_file = '';
57
+
58
+ /*
59
+ * Relative 404 cache path for the current request.
60
+ *
61
+ * @since 150422 Rewrite.
62
+ *
63
+ * @type string 404 cache path for the current request.
64
+ */
65
+ $self->cache_path_404 = '';
66
+
67
+ /*
68
+ * Absolute 404 cache file path for the current request.
69
+ *
70
+ * @since 150422 Rewrite.
71
+ *
72
+ * @type string Absolute 404 cache file path for the current request.
73
+ */
74
+ $self->cache_file_404 = '';
75
+
76
+ /*
77
+ * Version salt followed by the current request location.
78
+ *
79
+ * @since 150422 Rewrite.
80
+ *
81
+ * @type string Version salt followed by the current request location.
82
+ */
83
+ $self->salt_location = '';
84
+
85
+ /*
86
+ * Calculated max age; i.e., before expiration.
87
+ *
88
+ * @since 151002 Load average checks in pro version.
89
+ *
90
+ * @type integer Calculated max age; i.e., before expiration.
91
+ */
92
+ $self->cache_max_age = 0;
93
+
94
+ /*
95
+ * Start output buffering (if applicable); or serve a cache file (if possible).
96
+ *
97
+ * @since 150422 Rewrite.
98
+ *
99
+ * @note This is a vital part of Comet Cache. This method serves existing (fresh) cache files.
100
+ * It is also responsible for beginning the process of collecting the output buffer.
101
+ */
102
+ $self->maybeStartOutputBuffering = function () use ($self) {
103
+ if (strcasecmp(PHP_SAPI, 'cli') === 0) {
104
+ return $self->maybeSetDebugInfo(NC_DEBUG_PHP_SAPI_CLI);
105
+ }
106
+ if (empty($_SERVER['HTTP_HOST']) || !$self->hostToken()) {
107
+ return $self->maybeSetDebugInfo(NC_DEBUG_NO_SERVER_HTTP_HOST);
108
+ }
109
+ if (empty($_SERVER['REQUEST_URI'])) {
110
+ return $self->maybeSetDebugInfo(NC_DEBUG_NO_SERVER_REQUEST_URI);
111
+ }
112
+ if (defined('COMET_CACHE_ALLOWED') && !COMET_CACHE_ALLOWED) {
113
+ return $self->maybeSetDebugInfo(NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT);
114
+ }
115
+ if (isset($_SERVER['COMET_CACHE_ALLOWED']) && !$_SERVER['COMET_CACHE_ALLOWED']) {
116
+ return $self->maybeSetDebugInfo(NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR);
117
+ }
118
+ if (defined('DONOTCACHEPAGE')) {
119
+ return $self->maybeSetDebugInfo(NC_DEBUG_DONOTCACHEPAGE_CONSTANT);
120
+ }
121
+ if (isset($_SERVER['DONOTCACHEPAGE'])) {
122
+ return $self->maybeSetDebugInfo(NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR);
123
+ }
124
+ if (isset($_GET[strtolower(SHORT_NAME).'AC']) && !filter_var($_GET[strtolower(SHORT_NAME).'AC'], FILTER_VALIDATE_BOOLEAN)) {
125
+ return $self->maybeSetDebugInfo(NC_DEBUG_AC_GET_VAR);
126
+ }
127
+ if ($self->isUncacheableRequestMethod()) {
128
+ return $self->maybeSetDebugInfo(NC_DEBUG_UNCACHEABLE_REQUEST);
129
+ }
130
+ if (isset($_SERVER['SERVER_ADDR']) && $self->currentIp() === $_SERVER['SERVER_ADDR']) {
131
+ if ((!IS_PRO || !$self->isAutoCacheEngine()) && !$self->isLocalhost()) {
132
+ return $self->maybeSetDebugInfo(NC_DEBUG_SELF_SERVE_REQUEST);
133
+ }
134
+ }
135
+ if (!COMET_CACHE_FEEDS_ENABLE && $self->isFeed()) {
136
+ return $self->maybeSetDebugInfo(NC_DEBUG_FEED_REQUEST);
137
+ }
138
+ if (preg_match('/\/(?:wp\-[^\/]+|xmlrpc)\.php(?:[?]|$)/i', $_SERVER['REQUEST_URI'])) {
139
+ return $self->maybeSetDebugInfo(NC_DEBUG_WP_SYSTEMATICS);
140
+ }
141
+ if (is_admin() || preg_match('/\/wp-admin(?:[\/?]|$)/i', $_SERVER['REQUEST_URI'])) {
142
+ return $self->maybeSetDebugInfo(NC_DEBUG_WP_ADMIN);
143
+ }
144
+ if (is_multisite() && preg_match('/\/files(?:[\/?]|$)/i', $_SERVER['REQUEST_URI'])) {
145
+ return $self->maybeSetDebugInfo(NC_DEBUG_MS_FILES);
146
+ }
147
+ if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $self->isLikeUserLoggedIn()) {
148
+ return $self->maybeSetDebugInfo(NC_DEBUG_IS_LIKE_LOGGED_IN_USER);
149
+ }
150
+ if (!COMET_CACHE_GET_REQUESTS && $self->requestContainsUncacheableQueryVars()) {
151
+ return $self->maybeSetDebugInfo(NC_DEBUG_GET_REQUEST_QUERIES);
152
+ }
153
+ if (!empty($_REQUEST['preview'])) {
154
+ return $self->maybeSetDebugInfo(NC_DEBUG_PREVIEW);
155
+ }
156
+ if (COMET_CACHE_EXCLUDE_URIS && preg_match(COMET_CACHE_EXCLUDE_URIS, $_SERVER['REQUEST_URI'])) {
157
+ return $self->maybeSetDebugInfo(NC_DEBUG_EXCLUDED_URIS);
158
+ }
159
+ if (COMET_CACHE_EXCLUDE_AGENTS && !empty($_SERVER['HTTP_USER_AGENT']) && (!IS_PRO || !$self->isAutoCacheEngine())) {
160
+ if (preg_match(COMET_CACHE_EXCLUDE_AGENTS, $_SERVER['HTTP_USER_AGENT'])) {
161
+ return $self->maybeSetDebugInfo(NC_DEBUG_EXCLUDED_AGENTS);
162
+ }
163
+ }
164
+ if (COMET_CACHE_EXCLUDE_REFS && !empty($_REQUEST['_wp_http_referer'])) {
165
+ if (preg_match(COMET_CACHE_EXCLUDE_REFS, stripslashes($_REQUEST['_wp_http_referer']))) {
166
+ return $self->maybeSetDebugInfo(NC_DEBUG_EXCLUDED_REFS);
167
+ }
168
+ }
169
+ if (COMET_CACHE_EXCLUDE_REFS && !empty($_SERVER['HTTP_REFERER'])) {
170
+ if (preg_match(COMET_CACHE_EXCLUDE_REFS, $_SERVER['HTTP_REFERER'])) {
171
+ return $self->maybeSetDebugInfo(NC_DEBUG_EXCLUDED_REFS);
172
+ }
173
+ }
174
+ $self->protocol = $self->isSsl() ? 'https://' : 'http://';
175
+ $self->host_token = $self->hostToken();
176
+ $self->host_base_dir_tokens = $self->hostBaseDirTokens();
177
+
178
+ $self->version_salt = ''; // Initialize the version salt.
179
+
180
+ $self->version_salt = $self->applyFilters(get_class($self).'__version_salt', $self->version_salt);
181
+ $self->version_salt = $self->applyFilters(GLOBAL_NS.'_version_salt', $self->version_salt);
182
+
183
+ $self->cache_path = $self->buildCachePath($self->protocol.$self->host_token.$_SERVER['REQUEST_URI'], '', $self->version_salt);
184
+ $self->cache_file = COMET_CACHE_DIR.'/'.$self->cache_path; // Not considering a user cache. That's done in the postload phase.
185
+
186
+ $self->cache_path_404 = $self->buildCachePath($self->protocol.$self->host_token.rtrim($self->host_base_dir_tokens, '/').'/'.COMET_CACHE_404_CACHE_FILENAME);
187
+ $self->cache_file_404 = COMET_CACHE_DIR.'/'.$self->cache_path_404; // Not considering a user cache at all here--ever.
188
+
189
+ $self->salt_location = ltrim($self->version_salt.' '.$self->protocol.$self->host_token.$_SERVER['REQUEST_URI']);
190
+
191
+ $self->cache_max_age = strtotime('-'.COMET_CACHE_MAX_AGE);
192
+
193
+ if (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN === 'postload' && $self->isLikeUserLoggedIn()) {
194
+ $self->postload['when_logged_in'] = true; // Enable postload check.
195
+ } elseif (is_file($self->cache_file) && (!$self->cache_max_age || filemtime($self->cache_file) >= $self->cache_max_age)) {
196
+ list($headers, $cache) = explode('<!--headers-->', file_get_contents($self->cache_file), 2);
197
+
198
+ $headers_list = $self->headersList();
199
+ foreach (unserialize($headers) as $_header) {
200
+ if (!in_array($_header, $headers_list, true) && stripos($_header, 'Last-Modified:') !== 0) {
201
+ header($_header); // Only cacheable/safe headers are stored in the cache.
202
+ }
203
+ }
204
+ unset($_header); // Just a little housekeeping.
205
+
206
+ if (COMET_CACHE_DEBUGGING_ENABLE && $self->isHtmlXmlDoc($cache)) {
207
+ $total_time = number_format(microtime(true) - $self->timer, 5, '.', '');
208
+ $cache .= "\n".'<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->';
209
+ // translators: This string is actually NOT translatable because the `__()` function is not available at this point in the processing.
210
+ $cache .= "\n".'<!-- '.htmlspecialchars(sprintf(__('%1$s fully functional :-) Cache file served for (%2$s) in %3$s seconds, on: %4$s.', 'comet-cache'), NAME, $self->salt_location, $total_time, date('M jS, Y @ g:i a T'))).' -->';
211
+ }
212
+ exit($cache); // Exit with cache contents.
213
+ } else {
214
+ ob_start(array($self, 'outputBufferCallbackHandler'));
215
+ }
216
+ return; // Return value not applicable.
217
+ };
218
+
219
+ /*
220
+ * Output buffer handler; i.e. the cache file generator.
221
+ *
222
+ * @note We CANNOT depend on any WP functionality here; it will cause problems.
223
+ * Anything we need from WP should be saved in the postload phase as a scalar value.
224
+ *
225
+ * @since 150422 Rewrite.
226
+ *
227
+ * @param string $buffer The buffer from {@link \ob_start()}.
228
+ * @param int $phase A set of bitmask flags.
229
+ *
230
+ * @throws \Exception If unable to handle output buffering for any reason.
231
+ *
232
+ * @return string|bool The output buffer, or `FALSE` to indicate no change.
233
+ *
234
+ * @attaches-to {@link \ob_start()}
235
+ */
236
+ $self->outputBufferCallbackHandler = function ($buffer, $phase) use ($self) {
237
+ if (!($phase & PHP_OUTPUT_HANDLER_END)) {
238
+ throw new \Exception(sprintf(__('Unexpected OB phase: `%1$s`.', 'comet-cache'), $phase));
239
+ }
240
+ AdvCacheBackCompat::zenCacheConstants();
241
+
242
+ $cache = trim((string) $buffer);
243
+
244
+ if (!isset($cache[0])) {
245
+ return false; // Don't cache an empty buffer.
246
+ }
247
+ if (!isset($GLOBALS[GLOBAL_NS.'_shutdown_flag'])) {
248
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_EARLY_BUFFER_TERMINATION);
249
+ }
250
+ if (defined('COMET_CACHE_ALLOWED') && !COMET_CACHE_ALLOWED) {
251
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT);
252
+ }
253
+ if (isset($_SERVER['COMET_CACHE_ALLOWED']) && !$_SERVER['COMET_CACHE_ALLOWED']) {
254
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR);
255
+ }
256
+ if (defined('DONOTCACHEPAGE')) {
257
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_DONOTCACHEPAGE_CONSTANT);
258
+ }
259
+ if (isset($_SERVER['DONOTCACHEPAGE'])) {
260
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR);
261
+ }
262
+ if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $self->is_user_logged_in) {
263
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_IS_LOGGED_IN_USER);
264
+ }
265
+ if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $self->isLikeUserLoggedIn()) {
266
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_IS_LIKE_LOGGED_IN_USER);
267
+ }
268
+ if (!COMET_CACHE_CACHE_NONCE_VALUES && preg_match('/\b(?:_wpnonce|akismet_comment_nonce)\b/', $cache)) {
269
+ if (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN && $self->isLikeUserLoggedIn()) {
270
+ if (!COMET_CACHE_CACHE_NONCE_VALUES_WHEN_LOGGED_IN) {
271
+ return (boolean)$self->maybeSetDebugInfo(NC_DEBUG_IS_LOGGED_IN_USER_NONCE);
272
+ }
273
+ } else { // Use the default debug notice for nonce conflicts.
274
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_PAGE_CONTAINS_NONCE);
275
+ } // An nonce makes the page dynamic; i.e., NOT cache compatible.
276
+ }
277
+ if ($self->is_404 && !COMET_CACHE_CACHE_404_REQUESTS) {
278
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_404_REQUEST);
279
+ }
280
+ if (stripos($cache, '<body id="error-page">') !== false) {
281
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_WP_ERROR_PAGE);
282
+ }
283
+ if (!$self->functionIsPossible('http_response_code')) {
284
+ if (stripos($cache, '<title>database error</title>') !== false) {
285
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_WP_ERROR_PAGE);
286
+ }
287
+ }
288
+ if (!$self->hasACacheableContentType()) {
289
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_UNCACHEABLE_CONTENT_TYPE);
290
+ }
291
+ if (!$self->hasACacheableStatus()) {
292
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_UNCACHEABLE_STATUS);
293
+ }
294
+ if ($self->is_maintenance) {
295
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_MAINTENANCE_PLUGIN);
296
+ }
297
+ if ($self->functionIsPossible('zlib_get_coding_type') && zlib_get_coding_type()
298
+ && (!($zlib_oc = ini_get('zlib.output_compression')) || !filter_var($zlib_oc, FILTER_VALIDATE_BOOLEAN))) {
299
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_OB_ZLIB_CODING_TYPE);
300
+ }
301
+ # Lock the cache directory while writes take place here.
302
+
303
+ $cache_lock = $self->cacheLock(); // Lock cache directory.
304
+
305
+ # Construct a temp file for atomic cache writes.
306
+
307
+ $cache_file_tmp = $self->addTmpSuffix($self->cache_file);
308
+
309
+ # Cache directory checks. The cache file directory is created here if necessary.
310
+
311
+ if (!is_dir(COMET_CACHE_DIR) && mkdir(COMET_CACHE_DIR, 0775, true) && !is_file(COMET_CACHE_DIR.'/.htaccess')) {
312
+ file_put_contents(COMET_CACHE_DIR.'/.htaccess', $self->htaccess_deny);
313
+ }
314
+ if (!is_dir($cache_file_dir = dirname($self->cache_file))) {
315
+ $cache_file_dir_writable = mkdir($cache_file_dir, 0775, true);
316
+ }
317
+ if (empty($cache_file_dir_writable) && !is_writable($cache_file_dir)) {
318
+ throw new \Exception(sprintf(__('Cache directory not writable. %1$s needs this directory please: `%2$s`. Set permissions to `755` or higher; `777` might be needed in some cases.', 'comet-cache'), NAME, $cache_file_dir));
319
+ }
320
+ # This is where a new 404 request might be detected for the first time.
321
+
322
+ if ($self->is_404 && is_file($self->cache_file_404)) {
323
+ if (!(symlink($self->cache_file_404, $cache_file_tmp) && rename($cache_file_tmp, $self->cache_file))) {
324
+ throw new \Exception(sprintf(__('Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), $self->cache_file, $self->cache_file_404, COMET_CACHE_DIR));
325
+ }
326
+ $self->cacheUnlock($cache_lock); // Release.
327
+ return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_1ST_TIME_404_SYMLINK);
328
+ }
329
+ /* ------- Otherwise, we need to construct & store a new cache file. ----------------------------------------------- */
330
+
331
+
332
+
333
+ if (COMET_CACHE_DEBUGGING_ENABLE && $self->isHtmlXmlDoc($cache)) {
334
+ $total_time = number_format(microtime(true) - $self->timer, 5, '.', ''); // Based on the original timer.
335
+ $cache .= "\n".'<!-- '.htmlspecialchars(sprintf(__('%1$s file path: %2$s', 'comet-cache'), NAME, str_replace(WP_CONTENT_DIR, '', $self->is_404 ? $self->cache_file_404 : $self->cache_file))).' -->';
336
+ $cache .= "\n".'<!-- '.htmlspecialchars(sprintf(__('%1$s file built for (%2$s%3$s) in %4$s seconds, on: %5$s.', 'comet-cache'), NAME, $self->is_404 ? '404 [error document]' : $self->salt_location, (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN && $self->user_token ? '; '.sprintf(__('user token: %1$s', 'comet-cache'), $self->user_token) : ''), $total_time, date('M jS, Y @ g:i a T'))).' -->';
337
+ $cache .= "\n".'<!-- '.htmlspecialchars(sprintf(__('This %1$s file will auto-expire (and be rebuilt) on: %2$s (based on your configured expiration time).', 'comet-cache'), NAME, date('M jS, Y @ g:i a T', strtotime('+'.COMET_CACHE_MAX_AGE)))).' -->';
338
+ }
339
+ if ($self->is_404) {
340
+ if (file_put_contents($cache_file_tmp, serialize($self->cacheableHeadersList()).'<!--headers-->'.$cache) && rename($cache_file_tmp, $self->cache_file_404)) {
341
+ if (!(symlink($self->cache_file_404, $cache_file_tmp) && rename($cache_file_tmp, $self->cache_file))) {
342
+ throw new \Exception(sprintf(__('Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), $self->cache_file, $self->cache_file_404, COMET_CACHE_DIR));
343
+ }
344
+ $self->cacheUnlock($cache_lock); // Release.
345
+ return $cache; // Return the newly built cache; with possible debug information also.
346
+ }
347
+ } elseif (file_put_contents($cache_file_tmp, serialize($self->cacheableHeadersList()).'<!--headers-->'.$cache) && rename($cache_file_tmp, $self->cache_file)) {
348
+ $self->cacheUnlock($cache_lock); // Release.
349
+ return $cache; // Return the newly built cache; with possible debug information also.
350
+ }
351
+ @unlink($cache_file_tmp); // Clean this up (if it exists); and throw an exception with information for the site owner.
352
+ throw new \Exception(sprintf(__('%1$s: failed to write cache file for: `%2$s`; possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), NAME, $_SERVER['REQUEST_URI'], COMET_CACHE_DIR));
353
+ };
src/includes/closures/Ac/PostloadUtils.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Have we caught the main WP loaded being loaded yet?
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @type bool `TRUE` if main query has been loaded; else `FALSE`.
10
+ *
11
+ * @see wpMainQueryPostload()
12
+ */
13
+ $self->is_wp_loaded_query = false;
14
+
15
+ /*
16
+ * Is the current request a WordPress 404 error?
17
+ *
18
+ * @since 150422 Rewrite.
19
+ *
20
+ * @type bool `TRUE` if is a 404 error; else `FALSE`.
21
+ *
22
+ * @see wpMainQueryPostload()
23
+ */
24
+ $self->is_404 = false;
25
+
26
+ /*
27
+ * Last HTTP status code passed through {@link \status_header}.
28
+ *
29
+ * @since 150422 Rewrite.
30
+ *
31
+ * @type int Last HTTP status code (if applicable).
32
+ *
33
+ * @see maybeFilterStatusHeaderPostload()
34
+ */
35
+ $self->http_status = 0;
36
+
37
+ /*
38
+ * Is the current request a WordPress content type?
39
+ *
40
+ * @since 150422 Rewrite.
41
+ *
42
+ * @type bool `TRUE` if is a WP content type.
43
+ *
44
+ * @see wpMainQueryPostload()
45
+ */
46
+ $self->is_a_wp_content_type = false;
47
+
48
+ /*
49
+ * Current WordPress {@link \content_url()}.
50
+ *
51
+ * @since 150422 Rewrite.
52
+ *
53
+ * @type string Current WordPress {@link \content_url()}.
54
+ *
55
+ * @see wpMainQueryPostload()
56
+ */
57
+ $self->content_url = '';
58
+
59
+ /*
60
+ * Flag for {@link \is_user_loged_in()}.
61
+ *
62
+ * @since 150422 Rewrite.
63
+ *
64
+ * @type bool `TRUE` if {@link \is_user_loged_in()}; else `FALSE`.
65
+ *
66
+ * @see wpMainQueryPostload()
67
+ */
68
+ $self->is_user_logged_in = false;
69
+
70
+ /*
71
+ * Flag for {@link \is_maintenance()}.
72
+ *
73
+ * @since 150422 Rewrite.
74
+ *
75
+ * @type bool `TRUE` if {@link \is_maintenance()}; else `FALSE`.
76
+ *
77
+ * @see wpMainQueryPostload()
78
+ */
79
+ $self->is_maintenance = false;
80
+
81
+ /*
82
+ * Array of data targeted at the postload phase.
83
+ *
84
+ * @since 150422 Rewrite.
85
+ *
86
+ * @type array Data and/or flags that work with various postload handlers.
87
+ */
88
+ $self->postload = array(
89
+
90
+ 'filter_status_header' => true,
91
+ 'wp_main_query' => true,
92
+ 'set_debug_info' => COMET_CACHE_DEBUGGING_ENABLE,
93
+ );
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ /*
104
+ * Filters WP {@link \status_header()} (if applicable).
105
+ *
106
+ * @since 150422 Rewrite.
107
+ */
108
+ $self->maybeFilterStatusHeaderPostload = function () use ($self) {
109
+ if (empty($self->postload['filter_status_header'])) {
110
+ return; // Nothing to do in this case.
111
+ }
112
+ $_self = $self; // Reference needed below.
113
+
114
+ add_filter(
115
+ 'status_header',
116
+ function ($status_header, $status_code) use ($_self) {
117
+ if ($status_code > 0) {
118
+ $_self->http_status = (integer) $status_code;
119
+ }
120
+ return $status_header;
121
+ },
122
+ PHP_INT_MAX,
123
+ 2
124
+ );
125
+ };
126
+
127
+ /*
128
+ * Hooks `NC_DEBUG_` info into the WordPress `shutdown` phase (if applicable).
129
+ *
130
+ * @since 150422 Rewrite.
131
+ */
132
+ $self->maybeSetDebugInfoPostload = function () use ($self) {
133
+ if (!COMET_CACHE_DEBUGGING_ENABLE) {
134
+ return; // Nothing to do.
135
+ }
136
+ if (empty($self->postload['set_debug_info'])) {
137
+ return; // Nothing to do in this case.
138
+ }
139
+ if (is_admin()) {
140
+ return; // Not applicable.
141
+ }
142
+ if (strcasecmp(PHP_SAPI, 'cli') === 0) {
143
+ return; // Let's not run the risk here.
144
+ }
145
+ add_action('shutdown', array($self, 'maybeEchoNcDebugInfo'), PHP_INT_MAX - 10);
146
+ };
147
+
148
+ /*
149
+ * Grab details from WP and the Comet Cache plugin itself,
150
+ * after the main query is loaded (if at all possible).
151
+ *
152
+ * This is where we have a chance to grab any values we need from WordPress; or from the CC plugin.
153
+ * It is EXTREMEMLY important that we NOT attempt to grab any object references here.
154
+ * Anything acquired in this phase should be stored as a scalar value.
155
+ * See {@link outputBufferCallbackHandler()} for further details.
156
+ *
157
+ * @since 150422 Rewrite.
158
+ *
159
+ * @attaches-to `wp` hook.
160
+ */
161
+ $self->wpMainQueryPostload = function () use ($self) {
162
+ if (empty($self->postload['wp_main_query'])) {
163
+ return; // Nothing to do in this case.
164
+ }
165
+ if ($self->is_wp_loaded_query || is_admin()) {
166
+ return; // Nothing to do.
167
+ }
168
+ if (!is_main_query()) {
169
+ return; // Not main query.
170
+ }
171
+ $self->is_wp_loaded_query = true;
172
+ $self->is_404 = is_404();
173
+ $self->is_user_logged_in = is_user_logged_in();
174
+ $self->content_url = rtrim(content_url(), '/');
175
+ $self->is_maintenance = $self->functionIsPossible('is_maintenance') && is_maintenance();
176
+ $_self = $self; // Reference for the closure below.
177
+
178
+ add_action(
179
+ 'template_redirect',
180
+ function () use ($_self) {
181
+ $_self->is_a_wp_content_type = $_self->is_404 || $_self->is_maintenance
182
+ || is_front_page() // See <https://core.trac.wordpress.org/ticket/21602#comment:7>
183
+ || is_home() || is_singular() || is_archive() || is_post_type_archive() || is_tax() || is_search() || is_feed();
184
+ },
185
+ 11
186
+ );
187
+ };
src/includes/closures/Ac/ShutdownUtils.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Registers a shutdown flag.
6
+ *
7
+ * @since 140605 Improving output buffer.
8
+ *
9
+ * @note In `/wp-settings.php`, Comet Cache is loaded before WP registers its own shutdown function.
10
+ * Therefore, this flag is set before {@link shutdown_action_hook()} fires, and thus before {@link wp_ob_end_flush_all()}.
11
+ *
12
+ * @see http://www.php.net/manual/en/function.register-shutdown-function.php
13
+ */
14
+ $self->registerShutdownFlag = function () use ($self) {
15
+ register_shutdown_function(function () {
16
+ $GLOBALS[GLOBAL_NS.'_shutdown_flag'] = -1;
17
+ });
18
+ };
src/includes/closures/Plugin/ActionUtils.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Plugin action handler.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @attaches-to `wp_loaded` hook.
10
+ */
11
+ $self->actions = function () use ($self) {
12
+ if (!empty($_REQUEST[GLOBAL_NS])) {
13
+ new Actions();
14
+ }
15
+
16
+ };
src/includes/closures/Plugin/BbPressUtils.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Is bbPress active?
6
+ *
7
+ * @since 150821 Improving bbPress support.
8
+ *
9
+ * @return bool `TRUE` if bbPress is active.
10
+ */
11
+ $self->isBbPressActive = function () use ($self) {
12
+ return class_exists('bbPress');
13
+ };
14
+
15
+ /*
16
+ * bbPress post types.
17
+ *
18
+ * @since 150821 Improving bbPress support.
19
+ *
20
+ * @return array All bbPress post types.
21
+ */
22
+ $self->bbPressPostTypes = function () use ($self) {
23
+ if (!$self->isBbPressActive()) {
24
+ return array();
25
+ }
26
+ if (!is_null($types = &$self->cacheKey('bbPressPostTypes'))) {
27
+ return $types; // Already did this.
28
+ }
29
+ $types = array(); // Initialize.
30
+ $types[] = bbp_get_forum_post_type();
31
+ $types[] = bbp_get_topic_post_type();
32
+ $types[] = bbp_get_reply_post_type();
33
+
34
+ return $types;
35
+ };
36
+
37
+ /*
38
+ * bbPress post statuses.
39
+ *
40
+ * @since 150821 Improving bbPress support.
41
+ *
42
+ * @return array All bbPress post statuses.
43
+ */
44
+ $self->bbPressStatuses = function () use ($self) {
45
+ if (!$self->isBbPressActive()) {
46
+ return array();
47
+ }
48
+ if (!is_null($statuses = &$self->cacheKey('bbPressStatuses'))) {
49
+ return $statuses; // Already did this.
50
+ }
51
+ $statuses = array(); // Initialize.
52
+
53
+ foreach (get_post_stati(array(), 'objects') as $_key => $_status) {
54
+ if (isset($_status->label_count['domain']) && $_status->label_count['domain'] === 'bbpress') {
55
+ $statuses[] = $_status->name;
56
+ }
57
+ }
58
+ unset($_key, $_status); // Housekeeping.
59
+
60
+ return $statuses;
61
+ };
src/includes/closures/Plugin/CleanupUtils.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Runs cleanup routine via CRON job.
6
+ *
7
+ * @since 151002 While working on directory stats.
8
+ *
9
+ * @attaches-to `'_cron_'.__GLOBAL_NS__.'_cleanup'`
10
+ */
11
+ $self->cleanupCache = function () use ($self) {
12
+ if (!$self->options['enable']) {
13
+ return; // Nothing to do.
14
+ }
15
+
16
+
17
+
18
+ $self->wurgeCache(); // Purge now.
19
+ };
src/includes/closures/Plugin/CondUtils.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Is pro preview?
6
+ *
7
+ * @since 150511 Rewrite.
8
+ *
9
+ * @return bool `TRUE` if it's a pro preview.
10
+ */
11
+ $self->isProPreview = function () use ($self) {
12
+ return !empty($_REQUEST[GLOBAL_NS.'_pro_preview']);
13
+ };
src/includes/closures/Plugin/CronUtils.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Extends WP-Cron schedules.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @attaches-to `cron_schedules` filter.
10
+ *
11
+ * @param array $schedules An array of the current schedules.
12
+ *
13
+ * @return array Revised array of WP-Cron schedules.
14
+ */
15
+ $self->extendCronSchedules = function ($schedules) use ($self) {
16
+ $schedules['every15m'] = array(
17
+ 'interval' => 900,
18
+ 'display' => __('Every 15 Minutes', 'comet-cache'),
19
+ );
20
+ return $schedules;
21
+ };
22
+
23
+ /*
24
+ * Checks Cron setup, validates schedules, and reschedules events if necessary.
25
+ *
26
+ * @attaches-to `init` hook.
27
+ *
28
+ * @since 151220 Improving WP Cron setup and validation of schedules
29
+ */
30
+ $self->checkCronSetup = function () use ($self) {
31
+ if ($self->options['crons_setup'] < 1439005906
32
+ || $self->options['crons_setup_on_namespace'] !== __NAMESPACE__
33
+ || $self->options['crons_setup_with_cache_cleanup_schedule'] !== $self->options['cache_cleanup_schedule']
34
+ || $self->options['crons_setup_on_wp_with_schedules'] !== sha1(serialize(wp_get_schedules()))
35
+ || !wp_next_scheduled('_cron_'.GLOBAL_NS.'_cleanup')
36
+
37
+ ) {
38
+
39
+ wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup');
40
+ wp_schedule_event(time() + 60, $self->options['cache_cleanup_schedule'], '_cron_'.GLOBAL_NS.'_cleanup');
41
+
42
+
43
+
44
+ $self->updateOptions(
45
+ array(
46
+ 'crons_setup' => time(),
47
+ 'crons_setup_on_namespace' => __NAMESPACE__,
48
+ 'crons_setup_with_cache_cleanup_schedule' => $self->options['cache_cleanup_schedule'],
49
+ 'crons_setup_on_wp_with_schedules' => sha1(serialize(wp_get_schedules()))
50
+ )
51
+ );
52
+ }
53
+ };
54
+
55
+ /*
56
+ * Resets `crons_setup` and clears WP-Cron schedules.
57
+ *
58
+ * @since 151220 Fixing bug with Auto-Cache Engine cron disappearing in some scenarios
59
+ *
60
+ * @note This MUST happen upon uninstall and deactivation due to buggy WP_Cron behavior. Events with a custom schedule will disappear when plugin is not active (see http://bit.ly/1lGdr78).
61
+ */
62
+ $self->resetCronSetup = function ( ) use ($self) {
63
+ if (is_multisite()) { // Main site CRON jobs.
64
+ switch_to_blog(get_current_site()->blog_id);
65
+
66
+ wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup');
67
+ restore_current_blog(); // Restore current blog.
68
+ } else { // Standard WP installation.
69
+
70
+ wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup');
71
+ }
72
+ $self->updateOptions(
73
+ array( // Reset so that crons are rescheduled upon next activation
74
+ 'crons_setup' => $self->default_options['crons_setup'],
75
+ 'crons_setup_on_namespace' => $self->default_options['crons_setup_on_namespace'],
76
+ 'crons_setup_with_cache_cleanup_schedule' => $self->default_options['crons_setup_with_cache_cleanup_schedule'],
77
+ 'crons_setup_on_wp_with_schedules' => $self->default_options['crons_setup_on_wp_with_schedules']
78
+ )
79
+ );
80
+ };
src/includes/closures/Plugin/DbUtils.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * WordPress database instance.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @return \wpdb Reference for IDEs.
10
+ */
11
+ $self->wpdb = function () use ($self) {
12
+ return $GLOBALS['wpdb'];
13
+ };
src/includes/closures/Plugin/DirUtils.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * This constructs an absolute server directory path (no trailing slashes);
6
+ * which is always nested into {@link \WP_CONTENT_DIR} and the configured `base_dir` option value.
7
+ *
8
+ * @since 150422 Rewrite.
9
+ *
10
+ * @param string $rel_dir_file A sub-directory or file; relative location please.
11
+ *
12
+ * @throws \Exception If `base_dir` is empty when this method is called upon;
13
+ * i.e. if you attempt to call upon this method before {@link setup()} runs.
14
+ *
15
+ * @return string The full absolute server path to `$rel_dir_file`.
16
+ */
17
+ $self->wpContentBaseDirTo = function ($rel_dir_file) use ($self) {
18
+ $rel_dir_file = trim((string) $rel_dir_file, '\\/'." \t\n\r\0\x0B");
19
+
20
+ if (empty($self->options['base_dir'])) {
21
+ throw new \Exception(__('Missing `base_dir` option value.', 'comet-cache'));
22
+ }
23
+ $wp_content_base_dir_to = WP_CONTENT_DIR.'/'.$self->options['base_dir'];
24
+
25
+ if (isset($rel_dir_file[0])) {
26
+ $wp_content_base_dir_to .= '/'.$rel_dir_file;
27
+ }
28
+ return $wp_content_base_dir_to;
29
+ };
30
+
31
+ /*
32
+ * This constructs a relative/base directory path (no leading/trailing slashes).
33
+ * Always relative to {@link \WP_CONTENT_DIR}. Depends on the configured `base_dir` option value.
34
+ *
35
+ * @since 150422 Rewrite.
36
+ *
37
+ * @param string $rel_dir_file A sub-directory or file; relative location please.
38
+ *
39
+ * @throws \Exception If `base_dir` is empty when this method is called upon;
40
+ * i.e. if you attempt to call upon this method before {@link setup()} runs.
41
+ *
42
+ * @return string The relative/base directory path to `$rel_dir_file`.
43
+ */
44
+ $self->basePathTo = function ($rel_dir_file) use ($self) {
45
+ $rel_dir_file = trim((string) $rel_dir_file, '\\/'." \t\n\r\0\x0B");
46
+
47
+ if (empty($self->options['base_dir'])) {
48
+ throw new \Exception(__('Missing `base_dir` option value.', 'comet-cache'));
49
+ }
50
+ $base_path_to = $self->options['base_dir'];
51
+
52
+ if (isset($rel_dir_file[0])) {
53
+ $base_path_to .= '/'.$rel_dir_file;
54
+ }
55
+ return $base_path_to;
56
+ };
57
+
58
+ /**
59
+ * Get the absolute filesystem path to the root of the WordPress installation
60
+ *
61
+ * Copied verbatim from get_home_path() in wp-admin/includes/file.php
62
+ *
63
+ * @since 151114 Adding `.htaccess` tweaks.
64
+ *
65
+ * @return string Full filesystem path to the root of the WordPress installation
66
+ */
67
+ $self->wpHomePath = function () use ($self) {
68
+ $home = set_url_scheme( get_option( 'home' ), 'http' );
69
+ $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' );
70
+ if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) {
71
+ $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */
72
+ $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) );
73
+ $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos );
74
+ $home_path = trailingslashit( $home_path );
75
+ } else {
76
+ $home_path = ABSPATH;
77
+ }
78
+ return str_replace( '\\', '/', $home_path );
79
+ };
src/includes/closures/Plugin/HtaccessUtils.php ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Unique comment marker.
6
+ *
7
+ * @since 151220 Enhancing `.htaccess` tweaks.
8
+ *
9
+ * @return string Used in `.htaccess` parsing.
10
+ */
11
+ $self->htaccess_marker = 'WmVuQ2FjaGU';
12
+
13
+ /*
14
+ * Plugin options that have associated htaccess rules.
15
+ *
16
+ * @since 15xxxx Improving `.htaccess` tweaks.
17
+ *
18
+ * @return array Plugin options that have associated htaccess rules
19
+ *
20
+ * @note We keep track of this to avoid the issue described here: http://git.io/vEFIH
21
+ */
22
+ $self->options_with_htaccess_rules = array('cdn_enable');
23
+
24
+ /*
25
+ * Add template blocks to `/.htaccess` file.
26
+ *
27
+ * @since 151114 Adding `.htaccess` tweaks.
28
+ *
29
+ * @return boolean True if added successfully.
30
+ *
31
+ * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT
32
+ */
33
+ $self->addWpHtaccess = function () use ($self) {
34
+ global $is_apache;
35
+
36
+ if (!$is_apache) {
37
+ return false; // Not running the Apache web server.
38
+ }
39
+ if (!$self->options['enable']) {
40
+ return true; // Nothing to do.
41
+ }
42
+ if (!$self->needHtaccessRules()) {
43
+ if($self->findHtaccessMarker()) { // Do we need to clean up previously added rules?
44
+ $self->removeWpHtaccess(); // Fail silently since we don't need rules in place.
45
+ }
46
+ return true; // Nothing to do; no options enabled that require htaccess rules.
47
+ }
48
+ if (!$self->removeWpHtaccess()) {
49
+ return false; // Unable to remove.
50
+ }
51
+ if (!($htaccess = $self->readHtaccessFile())) {
52
+ return false; // Failure; could not read file or invalid UTF8 encountered, file may be corrupt.
53
+ }
54
+
55
+ $template_blocks = ''; // Initialize.
56
+ if (is_dir($templates_dir = dirname(dirname(dirname(__FILE__))).'/templates/htaccess')) {
57
+ foreach (scandir($templates_dir) as $_template_file) {
58
+ switch ($_template_file) {
59
+
60
+ }
61
+ }
62
+ unset($_template_file); // Housekeeping.
63
+ }
64
+
65
+ if(empty($template_blocks)) { // Do we need to add anything to htaccess?
66
+ $self->closeHtaccessFile($htaccess); // No need to write to htaccess file in this case.
67
+ return true; // Nothing to do, but no failures either.
68
+ }
69
+
70
+ $template_header = '# BEGIN '.NAME.' '.$self->htaccess_marker.' (the '.$self->htaccess_marker.' marker is required for '.NAME.'; do not remove)'."\n";
71
+ $template_footer = '# END '.NAME.' '.$self->htaccess_marker;
72
+ $htaccess['file_contents'] = $template_header.trim($template_blocks)."\n".$template_footer."\n\n".$htaccess['file_contents'];
73
+
74
+ if (!$self->writeHtaccessFile($htaccess, true)) {
75
+ return false; // Failure; could not write changes.
76
+ }
77
+
78
+ return true; // Added successfully.
79
+ };
80
+
81
+ /*
82
+ * Remove template blocks from `/.htaccess` file.
83
+ *
84
+ * @since 151114 Adding `.htaccess` tweaks.
85
+ *
86
+ * @return boolean True if removed successfully.
87
+ *
88
+ * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT
89
+ */
90
+ $self->removeWpHtaccess = function () use ($self) {
91
+ global $is_apache;
92
+
93
+ if (!$is_apache) {
94
+ return false; // Not running the Apache web server.
95
+ }
96
+ if (!($htaccess_file = $self->findHtaccessFile())) {
97
+ return true; // File does not exist.
98
+ }
99
+ if (!$self->findHtaccessMarker()) {
100
+ return true; // Template blocks are already gone.
101
+ }
102
+ if (!($htaccess = $self->readHtaccessFile())) {
103
+ return false; // Failure; could not read file, create file, or invalid UTF8 encountered, file may be corrupt.
104
+ }
105
+
106
+ $regex = '/#\s*BEGIN\s+'.preg_quote(NAME, '/').'\s+'.$self->htaccess_marker.'.*?#\s*END\s+'.preg_quote(NAME, '/').'\s+'.$self->htaccess_marker.'\s*/is';
107
+ $htaccess['file_contents'] = preg_replace($regex, '', $htaccess['file_contents']);
108
+
109
+ if (!$self->writeHtaccessFile($htaccess, false)) {
110
+ return false; // Failure; could not write changes.
111
+ }
112
+
113
+ return true; // Removed successfully.
114
+ };
115
+
116
+ /*
117
+ * Finds absolute server path to `/.htaccess` file.
118
+ *
119
+ * @since 151114 Adding `.htaccess` tweaks.
120
+ *
121
+ * @return string Absolute server path to `/.htaccess` file;
122
+ * else an empty string if unable to locate the file.
123
+ */
124
+ $self->findHtaccessFile = function () use ($self) {
125
+ $file = ''; // Initialize.
126
+ $home_path = $self->wpHomePath();
127
+
128
+ if (is_file($htaccess_file = $home_path.'.htaccess')) {
129
+ $file = $htaccess_file;
130
+ }
131
+ return $file;
132
+ };
133
+
134
+ /*
135
+ * Determines if there are any plugin options enabled that require htaccess rules to be added.
136
+ *
137
+ * @since 15xxxx Improving `.htaccess` tweaks.
138
+ *
139
+ * @return bool True when an option is enabled that requires htaccess rules, false otherwise.
140
+ */
141
+ $self->needHtaccessRules = function () use ($self) {
142
+ if(!is_array($self->options_with_htaccess_rules)) {
143
+ return false; // Nothing to do.
144
+ }
145
+ foreach ($self->options_with_htaccess_rules as $option) {
146
+ if ($self->options[$option]) {
147
+ return true; // Yes, there are options enabled that require htaccess rules.
148
+ }
149
+ }
150
+ return false; // No, there are no options enabled that require htaccess rules.
151
+ };
152
+
153
+ /*
154
+ * Utility method used to check if htaccess file contains $htaccess_marker
155
+ *
156
+ * @since 151114 Adding `.htaccess` tweaks.
157
+ *
158
+ * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin.
159
+ *
160
+ * @return bool False on failure or when marker does not exist in htaccess, true otherwise.
161
+ */
162
+ $self->findHtaccessMarker = function ($htaccess_marker = '') use ($self) {
163
+ if (!($htaccess_file = $self->findHtaccessFile())) {
164
+ return false; // File does not exist.
165
+ }
166
+ if (!is_readable($htaccess_file)) {
167
+ return false; // Not possible.
168
+ }
169
+ if (($htaccess_file_contents = file_get_contents($htaccess_file)) === false) {
170
+ return false; // Failure; could not read file.
171
+ }
172
+ if (empty($htaccess_marker)) {
173
+ $htaccess_marker = $self->htaccess_marker;
174
+ }
175
+ if (stripos($htaccess_file_contents, $htaccess_marker) === false) {
176
+ return false; // Htaccess marker is missing
177
+ }
178
+
179
+ return true; // Htaccess has the marker
180
+ };
181
+
182
+ /*
183
+ * Gets contents of `/.htaccess` file with exclusive lock to read+write. If file doesn't exist, we attempt to create it.
184
+ *
185
+ * @since 151220 Improving `.htaccess` utils.
186
+ *
187
+ * @param string $htaccess_file Absolute path to the htaccess file. Optional.
188
+ * If not provided, we attempt to find it or create it if it doesn't exist.
189
+ *
190
+ * @return array|bool Returns an array with data necessary to call $self->writeHtaccessFile():
191
+ * `fp` a file pointer resource, `file_contents` a string. Returns `false` on failure.
192
+ *
193
+ * @note If a call to this method is not followed by a call to $self->writeHtaccessFile(),
194
+ * you must make sure that you unlock and close the `fp` resource yourself.
195
+ */
196
+ $self->readHtaccessFile = function ($htaccess_file = '') use ($self) {
197
+
198
+ if (empty($htaccess_file) && !($htaccess_file = $self->findHtaccessFile())) {
199
+ if (!is_writable($self->wpHomePath()) || file_put_contents($htaccess_file = $self->wpHomePath().'.htaccess', '') === false) {
200
+ return false; // Unable to find and/or create `.htaccess`.
201
+ } // If it doesn't exist, we create the `.htaccess` file here.
202
+ }
203
+ if (!is_readable($htaccess_file) || !is_writable($htaccess_file) || (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS)) {
204
+ return false; // Not possible.
205
+ }
206
+ if (!($fp = fopen($htaccess_file, 'rb+')) || !flock($fp, LOCK_EX)) {
207
+ fclose($fp); // Just in case we opened it before failing to obtain a lock.
208
+ return false; // Failure; could not open file and obtain an exclusive lock.
209
+ }
210
+ if (($file_contents = fread($fp, filesize($htaccess_file))) && ($file_contents === wp_check_invalid_utf8($file_contents))) {
211
+ rewind($fp); // Rewind pointer to beginning of file.
212
+ return compact('fp', 'file_contents');
213
+ } else { // Failure; could not read file or invalid UTF8 encountered, file may be corrupt.
214
+ flock($fp, LOCK_UN);
215
+ fclose($fp);
216
+ return false;
217
+ }
218
+ };
219
+
220
+ /*
221
+ * Writes to `/.htaccess` file using provided file pointer.
222
+ *
223
+ * @since 151220 Improving `.htaccess` utils.
224
+ *
225
+ * @param array $htaccess Array containing `fp` file resource pointing to htaccess file and `file_contents` to write to file.
226
+ * @param bool $require_marker Whether or not to require the marker be present in contents before writing.
227
+ * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin.
228
+ *
229
+ * @return bool True on success, false on failure.
230
+ */
231
+ $self->writeHtaccessFile = function (array $htaccess, $require_marker = true, $htaccess_marker = '') use ($self) {
232
+
233
+ if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
234
+ return false; // Not possible.
235
+ }
236
+ if (!is_resource($htaccess['fp'])) {
237
+ return false;
238
+ }
239
+ $htaccess_marker = $htaccess_marker ?: $self->htaccess_marker;
240
+
241
+ $_have_marker = stripos($htaccess['file_contents'], $htaccess_marker);
242
+
243
+ // Note: rewind() necessary here because we fread() above.
244
+ if (($require_marker && $_have_marker === false) || !rewind($htaccess['fp']) || !ftruncate($htaccess['fp'], 0) || !fwrite($htaccess['fp'], $htaccess['file_contents'])) {
245
+ flock($htaccess['fp'], LOCK_UN);
246
+ fclose($htaccess['fp']);
247
+ return false; // Failure; could not write changes.
248
+ }
249
+ fflush($htaccess['fp']);
250
+ flock($htaccess['fp'], LOCK_UN);
251
+ fclose($htaccess['fp']);
252
+
253
+ return true;
254
+ };
255
+
256
+ /*
257
+ * Utility method used to unlock and close htaccess file resource.
258
+ *
259
+ * @since 151114 Adding `.htaccess` tweaks.
260
+ *
261
+ * @param array $htaccess Array containing at least an `fp` file resource pointing to htaccess file.
262
+ *
263
+ * @return bool False on failure, true otherwise.
264
+ */
265
+ $self->closeHtaccessFile = function (array $htaccess) use ($self) {
266
+ if (!is_resource($htaccess['fp'])) {
267
+ return false; // Failure; requires a valid file resource.
268
+ }
269
+ flock($htaccess['fp'], LOCK_UN);
270
+ fclose($htaccess['fp']);
271
+
272
+ return true;
273
+ };
src/includes/closures/Plugin/InstallUtils.php ADDED
@@ -0,0 +1,535 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Plugin activation hook.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @attaches-to {@link \register_activation_hook()}
10
+ */
11
+ $self->activate = function () use ($self) {
12
+ $self->setup(); // Ensure setup is complete.
13
+
14
+ if (!$self->options['welcomed'] && !$self->options['enable']) {
15
+ $settings_url = add_query_arg(urlencode_deep(array('page' => GLOBAL_NS)), network_admin_url('/admin.php'));
16
+ $self->enqueueMainNotice(sprintf(__('<strong>%1$s</strong> successfully installed! :-) <strong>Please <a href="%2$s">enable caching and review options</a>.</strong>', 'comet-cache'), esc_html(NAME), esc_attr($settings_url), array('push_to_top' => true)));
17
+ $self->updateOptions(array('welcomed' => '1'));
18
+ }
19
+
20
+ if (!$self->options['enable']) {
21
+ return; // Nothing to do.
22
+ }
23
+
24
+ $self->addWpCacheToWpConfig();
25
+ $self->addWpHtaccess();
26
+ $self->addAdvancedCache();
27
+ $self->updateBlogPaths();
28
+ $self->autoClearCache();
29
+ };
30
+
31
+ /*
32
+ * Check current plugin version that is installed in WP.
33
+ *
34
+ * @since 150422 Rewrite.
35
+ *
36
+ * @attaches-to `admin_init` hook.
37
+ */
38
+ $self->checkVersion = function () use ($self) {
39
+ $prev_version = $self->options['version'];
40
+ if (version_compare($prev_version, VERSION, '>=')) {
41
+ return; // Nothing to do; up-to-date.
42
+ }
43
+ $self->updateOptions(array('version' => VERSION));
44
+
45
+ new VsUpgrades($prev_version);
46
+
47
+ if ($self->options['enable']) {
48
+ $self->addWpCacheToWpConfig();
49
+ $self->addWpHtaccess();
50
+ $self->addAdvancedCache();
51
+ $self->updateBlogPaths();
52
+ }
53
+ $self->wipeCache(); // Fresh start now.
54
+
55
+ $self->enqueueMainNotice(sprintf(__('<strong>%1$s:</strong> detected a new version of itself. Recompiling w/ latest version... wiping the cache... all done :-)', 'comet-cache'), esc_html(NAME)), array('push_to_top' => true));
56
+ };
57
+
58
+ /*
59
+ * Plugin deactivation hook.
60
+ *
61
+ * @since 150422 Rewrite.
62
+ *
63
+ * @attaches-to {@link \register_deactivation_hook()}
64
+ */
65
+ $self->deactivate = function () use ($self) {
66
+ $self->setup(); // Ensure setup is complete.
67
+
68
+ $self->removeWpCacheFromWpConfig();
69
+ $self->removeWpHtaccess();
70
+ $self->removeAdvancedCache();
71
+ $self->clearCache();
72
+ $self->resetCronSetup();
73
+ };
74
+
75
+ /*
76
+ * Plugin uninstall hook.
77
+ *
78
+ * @since 150422 Rewrite.
79
+ */
80
+ $self->uninstall = function () use ($self) {
81
+ $self->setup(); // Ensure setup is complete.
82
+
83
+ if (!defined('WP_UNINSTALL_PLUGIN')) {
84
+ return; // Disallow.
85
+ }
86
+ if (empty($GLOBALS[GLOBAL_NS.'_uninstalling'])) {
87
+ return; // Not uninstalling.
88
+ }
89
+ if (!current_user_can($self->uninstall_cap)) {
90
+ return; // Extra layer of security.
91
+ }
92
+ $self->removeWpCacheFromWpConfig();
93
+ $self->removeWpHtaccess();
94
+ $self->removeAdvancedCache();
95
+ $self->wipeCache();
96
+ $self->resetCronSetup();
97
+
98
+ if (!$self->options['uninstall_on_deletion']) {
99
+ return; // Nothing to do here.
100
+ }
101
+ $self->deleteAdvancedCache();
102
+ $self->deleteBaseDir();
103
+
104
+ $wpdb = $self->wpdb(); // WordPress DB.
105
+ $like = '%'.$wpdb->esc_like(GLOBAL_NS).'%';
106
+
107
+ if (is_multisite()) { // Site options for a network installation.
108
+ $wpdb->query('DELETE FROM `'.esc_sql($wpdb->sitemeta).'` WHERE `meta_key` LIKE \''.esc_sql($like).'\'');
109
+
110
+ switch_to_blog(get_current_site()->blog_id); // In case it started as a standard WP installation.
111
+ $wpdb->query('DELETE FROM `'.esc_sql($wpdb->options).'` WHERE `option_name` LIKE \''.esc_sql($like).'\'');
112
+ restore_current_blog(); // Restore current blog.
113
+ //
114
+ } else { // Standard WP installation.
115
+ $wpdb->query('DELETE FROM `'.esc_sql($wpdb->options).'` WHERE `option_name` LIKE \''.esc_sql($like).'\'');
116
+ }
117
+ };
118
+
119
+ /*
120
+ * Adds `define('WP_CACHE', TRUE);` to the `/wp-config.php` file.
121
+ *
122
+ * @since 150422 Rewrite.
123
+ *
124
+ * @return string The new contents of the updated `/wp-config.php` file;
125
+ * else an empty string if unable to add the `WP_CACHE` constant.
126
+ */
127
+ $self->addWpCacheToWpConfig = function () use ($self) {
128
+ if (!$self->options['enable']) {
129
+ return ''; // Nothing to do.
130
+ }
131
+ if (!($wp_config_file = $self->findWpConfigFile())) {
132
+ return ''; // Unable to find `/wp-config.php`.
133
+ }
134
+ if (!is_readable($wp_config_file)) {
135
+ return ''; // Not possible.
136
+ }
137
+ if (!($wp_config_file_contents = file_get_contents($wp_config_file))) {
138
+ return ''; // Failure; could not read file.
139
+ }
140
+ if (!($wp_config_file_contents_no_whitespace = php_strip_whitespace($wp_config_file))) {
141
+ return ''; // Failure; file empty
142
+ }
143
+ if (preg_match('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:\-?[1-9][0-9\.]*|TRUE|([\'"])(?:[^0\'"]|[^\'"]{2,})\\2)\s*\)\s*;/i', $wp_config_file_contents_no_whitespace)) {
144
+ return $wp_config_file_contents; // It's already in there; no need to modify this file.
145
+ }
146
+ if (!($wp_config_file_contents = $self->removeWpCacheFromWpConfig())) {
147
+ return ''; // Unable to remove previous value.
148
+ }
149
+ if (!($wp_config_file_contents = preg_replace('/^\s*(\<\?php|\<\?)\s+/i', '${1}'."\n"."define('WP_CACHE', TRUE);"."\n", $wp_config_file_contents, 1))) {
150
+ return ''; // Failure; something went terribly wrong here.
151
+ }
152
+ if (strpos($wp_config_file_contents, "define('WP_CACHE', TRUE);") === false) {
153
+ return ''; // Failure; unable to add; unexpected PHP code.
154
+ }
155
+ if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
156
+ return ''; // We may NOT edit any files.
157
+ }
158
+ if (!is_writable($wp_config_file)) {
159
+ return ''; // Not possible.
160
+ }
161
+ if (!file_put_contents($wp_config_file, $wp_config_file_contents)) {
162
+ return ''; // Failure; could not write changes.
163
+ }
164
+ return $wp_config_file_contents;
165
+ };
166
+
167
+ /*
168
+ * Removes `define('WP_CACHE', TRUE);` from the `/wp-config.php` file.
169
+ *
170
+ * @since 150422 Rewrite.
171
+ *
172
+ * @return string The new contents of the updated `/wp-config.php` file;
173
+ * else an empty string if unable to remove the `WP_CACHE` constant.
174
+ */
175
+ $self->removeWpCacheFromWpConfig = function () use ($self) {
176
+ if (!($wp_config_file = $self->findWpConfigFile())) {
177
+ return ''; // Unable to find `/wp-config.php`.
178
+ }
179
+ if (!is_readable($wp_config_file)) {
180
+ return ''; // Not possible.
181
+ }
182
+ if (!($wp_config_file_contents = file_get_contents($wp_config_file))) {
183
+ return ''; // Failure; could not read file.
184
+ }
185
+ if (!($wp_config_file_contents_no_whitespace = php_strip_whitespace($wp_config_file))) {
186
+ return ''; // Failure; file empty
187
+ }
188
+ if (!preg_match('/([\'"])WP_CACHE\\1/i', $wp_config_file_contents_no_whitespace)) {
189
+ return $wp_config_file_contents; // Already gone.
190
+ }
191
+ if (preg_match('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:0|FALSE|NULL|([\'"])0?\\2)\s*\)\s*;/i', $wp_config_file_contents_no_whitespace) && !is_writable($wp_config_file)) {
192
+ return $wp_config_file_contents; // It's already disabled, and since we can't write to this file let's let this slide.
193
+ }
194
+ if (!($wp_config_file_contents = preg_replace('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:\-?[0-9\.]+|TRUE|FALSE|NULL|([\'"])[^\'"]*\\2)\s*\)\s*;/i', '', $wp_config_file_contents))) {
195
+ return ''; // Failure; something went terribly wrong here.
196
+ }
197
+ if (preg_match('/([\'"])WP_CACHE\\1/i', $wp_config_file_contents)) {
198
+ return ''; // Failure; perhaps the `/wp-config.php` file contains syntax we cannot remove safely.
199
+ }
200
+ if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) {
201
+ return ''; // We may NOT edit any files.
202
+ }
203
+ if (!is_writable($wp_config_file)) {
204
+ return ''; // Not possible.
205
+ }
206
+ if (!file_put_contents($wp_config_file, $wp_config_file_contents)) {
207
+ return ''; // Failure; could not write changes.
208
+ }
209
+ return $wp_config_file_contents;
210
+ };
211
+
212
+ /*
213
+ * Checks to make sure the `advanced-cache.php` file still exists;
214
+ * and if it doesn't, the `advanced-cache.php` is regenerated automatically.
215
+ *
216
+ * @since 150422 Rewrite.
217
+ *
218
+ * @attaches-to `init` hook.
219
+ *
220
+ * @note This runs so that remote deployments which completely wipe out an
221
+ * existing set of website files (like the AWS Elastic Beanstalk does) will NOT cause Comet Cache
222
+ * to stop functioning due to the lack of an `advanced-cache.php` file, which is generated by Comet Cache.
223
+ *
224
+ * For instance, if you have a Git repo with all of your site files; when you push those files
225
+ * to your website to deploy them, you most likely do NOT have the `advanced-cache.php` file.
226
+ * Comet Cache creates this file on its own. Thus, if it's missing (and CC is active)
227
+ * we simply regenerate the file automatically to keep Comet Cache running.
228
+ */
229
+ $self->checkAdvancedCache = function () use ($self) {
230
+ if (!$self->options['enable']) {
231
+ return; // Nothing to do.
232
+ }
233
+ if (!empty($_REQUEST[GLOBAL_NS])) {
234
+ return; // Skip on plugin actions.
235
+ }
236
+ $cache_dir = $self->cacheDir();
237
+ $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php';
238
+ $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache';
239
+
240
+ // Fixes zero-byte advanced-cache.php bug related to migrating from ZenCache
241
+ // See: <https://github.com/websharks/zencache/issues/432>
242
+
243
+ // Also fixes a missing `define('WP_CACHE', TRUE)` bug related to migrating from ZenCache
244
+ // See <https://github.com/websharks/zencache/issues/450>
245
+
246
+ if (!is_file($advanced_cache_check_file) || !is_file($advanced_cache_file) || filesize($advanced_cache_file) === 0) {
247
+ $self->addAdvancedCache();
248
+ $self->addWpCacheToWpConfig();
249
+ }
250
+ };
251
+
252
+ /*
253
+ * Creates and adds the `advanced-cache.php` file.
254
+ *
255
+ * @since 150422 Rewrite.
256
+ *
257
+ * @return bool|null `TRUE` on success. `FALSE` or `NULL` on failure.
258
+ * A special `NULL` return value indicates success with a single failure
259
+ * that is specifically related to the `[SHORT_NAME]-advanced-cache` file.
260
+ */
261
+ $self->addAdvancedCache = function () use ($self) {
262
+ if (!$self->removeAdvancedCache()) {
263
+ return false; // Still exists.
264
+ }
265
+ $cache_dir = $self->cacheDir();
266
+ $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php';
267
+ $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache';
268
+ $advanced_cache_template = dirname(dirname(dirname(__FILE__))).'/templates/advanced-cache.txt';
269
+
270
+ if (is_file($advanced_cache_file) && !is_writable($advanced_cache_file)) {
271
+ return false; // Not possible to create.
272
+ }
273
+ if (!is_file($advanced_cache_file) && !is_writable(dirname($advanced_cache_file))) {
274
+ return false; // Not possible to create.
275
+ }
276
+ if (!is_file($advanced_cache_template) || !is_readable($advanced_cache_template)) {
277
+ return false; // Template file is missing; or not readable.
278
+ }
279
+ if (!($advanced_cache_contents = file_get_contents($advanced_cache_template))) {
280
+ return false; // Template file is missing; or is not readable.
281
+ }
282
+ $possible_advanced_cache_constant_key_values = array_merge(
283
+ $self->options, // The following additional keys are dynamic.
284
+ array('cache_dir' => $self->basePathTo($self->cache_sub_dir),
285
+
286
+ )
287
+ );
288
+ if ($self->applyWpFilters(GLOBAL_NS.'_exclude_uris_client_side_too', true)) {
289
+ $possible_advanced_cache_constant_key_values['exclude_client_side_uris'] .= "\n".$self->options['exclude_uris'];
290
+ }
291
+ foreach ($possible_advanced_cache_constant_key_values as $_option => $_value) {
292
+ $_value = (string) $_value; // Force string.
293
+
294
+ switch ($_option) {
295
+ case 'exclude_uris': // Converts to regex (caSe insensitive).
296
+ case 'exclude_client_side_uris': // Converts to regex (caSe insensitive).
297
+ case 'exclude_refs': // Converts to regex (caSe insensitive).
298
+ case 'exclude_agents': // Converts to regex (caSe insensitive).
299
+
300
+
301
+
302
+ $_value = "'".$self->escSq($self->lineDelimitedPatternsToRegex($_value))."'";
303
+
304
+ break; // Break switch handler.
305
+
306
+
307
+
308
+ default: // Default case handler.
309
+
310
+ $_value = "'".$self->escSq($_value)."'";
311
+
312
+ break; // Break switch handler.
313
+ }
314
+ $advanced_cache_contents = // Fill replacement codes.
315
+ str_ireplace(
316
+ array(
317
+ "'%%".GLOBAL_NS.'_'.$_option."%%'",
318
+ "'%%".GLOBAL_NS.'_'.preg_replace('/^cache_/i', '', $_option)."%%'",
319
+ ),
320
+ $_value,
321
+ $advanced_cache_contents
322
+ );
323
+ }
324
+ unset($_option, $_value, $_values, $_response); // Housekeeping.
325
+
326
+ if (strpos(PLUGIN_FILE, WP_CONTENT_DIR) === 0) {
327
+ $plugin_file = "WP_CONTENT_DIR.'".$self->escSq(str_replace(WP_CONTENT_DIR, '', PLUGIN_FILE))."'";
328
+ } else {
329
+ $plugin_file = "'".$self->escSq(PLUGIN_FILE)."'"; // Full absolute path.
330
+ }
331
+ // Make it possible for the `advanced-cache.php` handler to find the plugin directory reliably.
332
+ $advanced_cache_contents = str_ireplace("'%%".GLOBAL_NS."_PLUGIN_FILE%%'", $plugin_file, $advanced_cache_contents);
333
+
334
+ // Ignore; this is created by Comet Cache; and we don't need to obey in this case.
335
+ #if(defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS)
336
+ # return FALSE; // We may NOT edit any files.
337
+
338
+ if (!file_put_contents($advanced_cache_file, $advanced_cache_contents)) {
339
+ return false; // Failure; could not write file.
340
+ }
341
+ $cache_lock = $self->cacheLock(); // Lock cache.
342
+
343
+ if (!is_dir($cache_dir)) {
344
+ mkdir($cache_dir, 0775, true);
345
+ }
346
+ if (is_writable($cache_dir) && !is_file($cache_dir.'/.htaccess')) {
347
+ file_put_contents($cache_dir.'/.htaccess', $self->htaccess_deny);
348
+ }
349
+ if (!is_dir($cache_dir) || !is_writable($cache_dir) || !is_file($cache_dir.'/.htaccess') || !file_put_contents($advanced_cache_check_file, time())) {
350
+ $self->cacheUnlock($cache_lock); // Release.
351
+ return; // Special return value (NULL).
352
+ }
353
+ $self->cacheUnlock($cache_lock); // Release.
354
+
355
+ $self->clearAcDropinFromOpcacheByForce();
356
+
357
+ return true;
358
+ };
359
+
360
+ /*
361
+ * Removes the `advanced-cache.php` file.
362
+ *
363
+ * @since 150422 Rewrite.
364
+ *
365
+ * @return bool `TRUE` on success. `FALSE` on failure.
366
+ *
367
+ * @note The `advanced-cache.php` file is NOT actually deleted by this routine.
368
+ * Instead of deleting the file, we simply empty it out so that it's `0` bytes in size.
369
+ *
370
+ * The reason for this is to preserve any file permissions set by the site owner.
371
+ * If the site owner previously allowed this specific file to become writable, we don't want to
372
+ * lose that permission by deleting the file; forcing the site owner to do it all over again later.
373
+ *
374
+ * An example of where this is useful is when a site owner deactivates the CC plugin,
375
+ * but later they decide that CC really is the most awesome plugin in the world and they turn it back on.
376
+ */
377
+ $self->removeAdvancedCache = function () use ($self) {
378
+ $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php';
379
+
380
+ if (!is_file($advanced_cache_file)) {
381
+ return true; // Already gone.
382
+ }
383
+ if (is_readable($advanced_cache_file) && filesize($advanced_cache_file) === 0) {
384
+ return true; // Already gone; i.e. it's empty already.
385
+ }
386
+ if (!is_writable($advanced_cache_file)) {
387
+ return false; // Not possible.
388
+ }
389
+ /* Empty the file only. This way permissions are NOT lost in cases where
390
+ a site owner makes this specific file writable for Comet Cache. */
391
+ if (file_put_contents($advanced_cache_file, '') !== 0) {
392
+ return false; // Failure.
393
+ }
394
+ $self->clearAcDropinFromOpcacheByForce();
395
+
396
+ return true;
397
+ };
398
+
399
+ /*
400
+ * Deletes the `advanced-cache.php` file.
401
+ *
402
+ * @since 150422 Rewrite.
403
+ *
404
+ * @return bool `TRUE` on success. `FALSE` on failure.
405
+ *
406
+ * @note The `advanced-cache.php` file is deleted by this routine.
407
+ */
408
+ $self->deleteAdvancedCache = function () use ($self) {
409
+ $cache_dir = $self->cacheDir();
410
+ $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php';
411
+ $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache';
412
+
413
+ if (is_file($advanced_cache_file)) {
414
+ if (!is_writable($advanced_cache_file) || !unlink($advanced_cache_file)) {
415
+ return false; // Not possible; or outright failure.
416
+ }
417
+ }
418
+ if (is_file($advanced_cache_check_file)) {
419
+ if (!is_writable($advanced_cache_check_file) || !unlink($advanced_cache_check_file)) {
420
+ return false; // Not possible; or outright failure.
421
+ }
422
+ }
423
+ $self->clearAcDropinFromOpcacheByForce();
424
+
425
+ return true; // Deletion success.
426
+ };
427
+
428
+ /*
429
+ * Checks to make sure the `[SHORT_NAME]-blog-paths` file still exists;
430
+ * and if it doesn't, the `[SHORT_NAME]-blog-paths` file is regenerated automatically.
431
+ *
432
+ * @since 150422 Rewrite.
433
+ *
434
+ * @attaches-to `init` hook.
435
+ *
436
+ * @note This runs so that remote deployments which completely wipe out an
437
+ * existing set of website files (like the AWS Elastic Beanstalk does) will NOT cause Comet Cache
438
+ * to stop functioning due to the lack of a `[SHORT_NAME]-blog-paths` file, which is generated by Comet Cache.
439
+ *
440
+ * For instance, if you have a Git repo with all of your site files; when you push those files
441
+ * to your website to deploy them, you most likely do NOT have the `[SHORT_NAME]-blog-paths` file.
442
+ * Comet Cache creates this file on its own. Thus, if it's missing (and CC is active)
443
+ * we simply regenerate the file automatically to keep Comet Cache running.
444
+ */
445
+ $self->checkBlogPaths = function () use ($self) {
446
+ if (!$self->options['enable']) {
447
+ return; // Nothing to do.
448
+ }
449
+ if (!is_multisite()) {
450
+ return; // N/A.
451
+ }
452
+ if (!empty($_REQUEST[GLOBAL_NS])) {
453
+ return; // Skip on plugin actions.
454
+ }
455
+ $cache_dir = $self->cacheDir();
456
+ $blog_paths_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-blog-paths';
457
+
458
+ if (!is_file($blog_paths_file)) {
459
+ $self->updateBlogPaths();
460
+ }
461
+ };
462
+
463
+ /*
464
+ * Creates and/or updates the `[SHORT_NAME]-blog-paths` file.
465
+ *
466
+ * @since 150422 Rewrite.
467
+ *
468
+ * @attaches-to `enable_live_network_counts` filter.
469
+ *
470
+ * @param mixed $enable_live_network_counts Optional, defaults to a `NULL` value.
471
+ *
472
+ * @return mixed The value of `$enable_live_network_counts` (passes through).
473
+ *
474
+ * @note While this routine is attached to a WP filter, we also call upon it directly at times.
475
+ */
476
+ $self->updateBlogPaths = function ($enable_live_network_counts = null) use ($self) {
477
+ $value = $enable_live_network_counts; // This hook actually rides on a filter.
478
+
479
+ if (!$self->options['enable']) {
480
+ return $value; // Nothing to do.
481
+ }
482
+ if (!is_multisite()) {
483
+ return $value; // N/A.
484
+ }
485
+ $cache_dir = $self->cacheDir();
486
+ $blog_paths_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-blog-paths';
487
+
488
+ $cache_lock = $self->cacheLock();
489
+
490
+ if (!is_dir($cache_dir)) {
491
+ mkdir($cache_dir, 0775, true);
492
+ }
493
+ if (is_writable($cache_dir) && !is_file($cache_dir.'/.htaccess')) {
494
+ file_put_contents($cache_dir.'/.htaccess', $self->htaccess_deny);
495
+ }
496
+ if (is_dir($cache_dir) && is_writable($cache_dir)) {
497
+ $paths = // Collect child `[/base]/path/`s from the WordPress database.
498
+ $self->wpdb()->get_col('SELECT `path` FROM `'.esc_sql($self->wpdb()->blogs)."` WHERE `deleted` <= '0'");
499
+
500
+ $host_base_token = $self->hostBaseToken(); // Pull this once only.
501
+
502
+ foreach ($paths as $_key => &$_path) {
503
+ if ($_path && $_path !== '/' && $host_base_token && $host_base_token !== '/') {
504
+ // Note that each `path` in the DB looks like: `[/base]/path/` (i.e., it includes base).
505
+ $_path = '/'.ltrim(preg_replace('/^'.preg_quote($host_base_token, '/').'/', '', $_path), '/');
506
+ }
507
+ if (!$_path || $_path === '/') {
508
+ unset($paths[$_key]); // Exclude main site.
509
+ }
510
+ }
511
+ unset($_key, $_path); // Housekeeping.
512
+
513
+ file_put_contents($blog_paths_file, serialize($paths));
514
+ }
515
+ $self->cacheUnlock($cache_lock); // Release.
516
+
517
+ return $value; // Pass through untouched (always).
518
+ };
519
+
520
+ /*
521
+ * Deletes base directory.
522
+ *
523
+ * @since 151002 Improving multisite compat.
524
+ *
525
+ * @return int Total files removed by this routine (if any).
526
+ */
527
+ $self->deleteBaseDir = function () use ($self) {
528
+ $counter = 0; // Initialize.
529
+
530
+ @set_time_limit(1800); // @TODO Display a warning.
531
+
532
+ $counter += $self->deleteAllFilesDirsIn($self->wpContentBaseDirTo(''), true);
533
+
534
+ return $counter;
535
+ };
src/includes/closures/Plugin/MenuPageUtils.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Adds CSS for administrative menu pages.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @attaches-to `admin_enqueue_scripts` hook.
10
+ */
11
+ $self->enqueueAdminStyles = function () use ($self) {
12
+ if (empty($_GET['page']) || strpos($_GET['page'], GLOBAL_NS) !== 0) {
13
+ return; // NOT a plugin page in the administrative area.
14
+ }
15
+ $deps = array(); // Plugin dependencies.
16
+
17
+ wp_enqueue_style(GLOBAL_NS, $self->url('/src/client-s/css/menu-pages.min.css'), $deps, VERSION, 'all');
18
+ };
19
+
20
+ /*
21
+ * Adds JS for administrative menu pages.
22
+ *
23
+ * @since 150422 Rewrite.
24
+ *
25
+ * @attaches-to `admin_enqueue_scripts` hook.
26
+ */
27
+ $self->enqueueAdminScripts = function () use ($self) {
28
+ if (empty($_GET['page']) || strpos($_GET['page'], GLOBAL_NS) !== 0) {
29
+ return; // NOT a plugin page in the administrative area.
30
+ }
31
+ $deps = array('jquery', 'chartjs'); // Plugin dependencies.
32
+
33
+ wp_enqueue_script('chartjs', set_url_scheme('//cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js'), array(), null, true);
34
+ wp_enqueue_script(GLOBAL_NS, $self->url('/src/client-s/js/menu-pages.min.js'), $deps, VERSION, true);
35
+ wp_localize_script(GLOBAL_NS, GLOBAL_NS.'_menu_page_vars', array(
36
+ '_wpnonce' => wp_create_nonce(),
37
+ 'isMultisite' => is_multisite(), // Network?
38
+ 'currentUserHasCap' => current_user_can($self->cap),
39
+ 'currentUserHasNetworkCap' => current_user_can($self->network_cap),
40
+ 'htmlCompressorEnabled' => (boolean) $self->options['htmlc_enable'],
41
+ 'ajaxURL' => site_url('/wp-load.php', is_ssl() ? 'https' : 'http'),
42
+ 'emptyStatsCountsImageUrl' => $self->url('/src/client-s/images/stats-fc-empty.png'),
43
+ 'emptyStatsFilesImageUrl' => $self->url('/src/client-s/images/stats-fs-empty.png'),
44
+ 'i18n' => array(
45
+ 'name' => NAME,
46
+ 'perSymbol' => __('%', 'comet-cache'),
47
+ 'file' => __('file', 'comet-cache'),
48
+ 'files' => __('files', 'comet-cache'),
49
+ 'pageCache' => __('Page Cache', 'comet-cache'),
50
+ 'htmlCompressor' => __('HTML Compressor', 'comet-cache'),
51
+ 'currentTotal' => __('Current Total', 'comet-cache'),
52
+ 'currentSite' => __('Current Site', 'comet-cache'),
53
+ 'xDayHigh' => __('%s Day High', 'comet-cache'),
54
+ ),
55
+ ));
56
+ };
57
+
58
+ /*
59
+ * Creates network admin menu pages.
60
+ *
61
+ * @since 150422 Rewrite.
62
+ *
63
+ * @attaches-to `network_admin_menu` hook.
64
+ */
65
+ $self->addNetworkMenuPages = function () use ($self) {
66
+ if (!is_multisite()) {
67
+ return; // Not applicable.
68
+ }
69
+ $icon = file_get_contents(dirname(dirname(dirname(dirname(__FILE__)))).'/client-s/images/inline-icon.svg');
70
+ $icon = 'data:image/svg+xml;base64,'.base64_encode($self->colorSvgMenuIcon($icon));
71
+
72
+ add_menu_page(NAME . (IS_PRO ? ' Pro' : ''), NAME . (IS_PRO ? ' Pro' : ''), $self->network_cap, GLOBAL_NS, array($self, 'menuPageOptions'), $icon);
73
+ add_submenu_page(GLOBAL_NS, __('Plugin Options', 'comet-cache'), __('Plugin Options', 'comet-cache'), $self->network_cap, GLOBAL_NS, array($self, 'menuPageOptions'));
74
+
75
+
76
+
77
+
78
+ };
79
+
80
+ /*
81
+ * Creates admin menu pages.
82
+ *
83
+ * @since 150422 Rewrite.
84
+ *
85
+ * @attaches-to `admin_menu` hook.
86
+ */
87
+ $self->addMenuPages = function () use ($self) {
88
+ if (is_multisite()) {
89
+ return; // Multisite networks MUST use network admin area.
90
+ }
91
+ $icon = file_get_contents(dirname(dirname(dirname(dirname(__FILE__)))).'/client-s/images/inline-icon.svg');
92
+ $icon = 'data:image/svg+xml;base64,'.base64_encode($self->colorSvgMenuIcon($icon));
93
+
94
+ add_menu_page(NAME . (IS_PRO ? ' Pro' : ''), NAME . (IS_PRO ? ' Pro' : ''), $self->cap, GLOBAL_NS, array($self, 'menuPageOptions'), $icon);
95
+ add_submenu_page(GLOBAL_NS, __('Plugin Options', 'comet-cache'), __('Plugin Options', 'comet-cache'), $self->cap, GLOBAL_NS, array($self, 'menuPageOptions'));
96
+
97
+
98
+
99
+
100
+ };
101
+
102
+ /*
103
+ * Adds link(s) to Comet Cache row on the WP plugins page.
104
+ *
105
+ * @since 150422 Rewrite.
106
+ *
107
+ * @attaches-to `plugin_action_links_'.plugin_basename(PLUGIN_FILE)` filter.
108
+ *
109
+ * @param array $links An array of the existing links provided by WordPress.
110
+ *
111
+ * @return array Revised array of links.
112
+ */
113
+ $self->addSettingsLink = function ($links) use ($self) {
114
+ $links[] = '<a href="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS)), self_admin_url('/admin.php'))).'">'.__('Settings', 'comet-cache').'</a>';
115
+ if (!IS_PRO) {
116
+ $links[] = '<br/><a href="'.esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS, GLOBAL_NS.'_pro_preview' => '1')), self_admin_url('/admin.php'))).'">'.__('Preview Pro Features', 'comet-cache').'</a>';
117
+ $links[] = '<a href="'.esc_attr('http://cometcache.com/prices/').'" target="_blank">'.__('Upgrade', 'comet-cache').'</a>';
118
+ }
119
+ return $links;
120
+ };
121
+
122
+ /*
123
+ * Fills menu page inline SVG icon color.
124
+ *
125
+ * @since 150422 Rewrite.
126
+ *
127
+ * @param string $svg Inline SVG icon markup.
128
+ *
129
+ * @return string Inline SVG icon markup.
130
+ */
131
+ $self->colorSvgMenuIcon = function ($svg) use ($self) {
132
+ if (!($color = get_user_option('admin_color'))) {
133
+ $color = 'fresh'; // Default color scheme.
134
+ }
135
+ if (empty($self->wp_admin_icon_colors[$color])) {
136
+ return $svg; // Not possible.
137
+ }
138
+ $icon_colors = $self->wp_admin_icon_colors[$color];
139
+ $use_icon_fill_color = $icon_colors['base']; // Default base.
140
+
141
+ $current_pagenow = !empty($GLOBALS['pagenow']) ? $GLOBALS['pagenow'] : '';
142
+ $current_page = !empty($_REQUEST['page']) ? $_REQUEST['page'] : '';
143
+
144
+ if (strpos($current_pagenow, GLOBAL_NS) === 0 || strpos($current_page, GLOBAL_NS) === 0) {
145
+ $use_icon_fill_color = $icon_colors['current'];
146
+ }
147
+ return str_replace(' fill="currentColor"', ' fill="'.esc_attr($use_icon_fill_color).'"', $svg);
148
+ };
149
+
150
+ /*
151
+ * Loads the admin menu page options.
152
+ *
153
+ * @since 150422 Rewrite.
154
+ */
155
+ $self->menuPageOptions = function () use ($self) {
156
+ new MenuPage('options');
157
+ };
158
+
159
+
160
+
161
+
162
+
163
+ /*
164
+ * WordPress admin icon color schemes.
165
+ *
166
+ * @since 150422 Rewrite.
167
+ *
168
+ * @type array WP admin icon colors.
169
+ *
170
+ * @note These must be hard-coded, because they don't become available
171
+ * in core until `admin_init`; i.e., too late for `admin_menu`.
172
+ */
173
+ $self->wp_admin_icon_colors = array(
174
+ 'fresh' => array('base' => '#999999', 'focus' => '#2EA2CC', 'current' => '#FFFFFF'),
175
+ 'light' => array('base' => '#999999', 'focus' => '#CCCCCC', 'current' => '#CCCCCC'),
176
+ 'blue' => array('base' => '#E5F8FF', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'),
177
+ 'midnight' => array('base' => '#F1F2F3', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'),
178
+ 'sunrise' => array('base' => '#F3F1F1', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'),
179
+ 'ectoplasm' => array('base' => '#ECE6F6', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'),
180
+ 'ocean' => array('base' => '#F2FCFF', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'),
181
+ 'coffee' => array('base' => '#F3F2F1', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'),
182
+ );
183
+
184
+ /*
185
+ * On a specific menu page?
186
+ *
187
+ * @since 151002 Improving multisite compat.
188
+ *
189
+ * @param string $which Which page to check; may contain wildcards.
190
+ *
191
+ * @return boolean True if is the menu page.
192
+ */
193
+ $self->isMenuPage = function ($which) use ($self) {
194
+ if (!($which = trim((string) $which))) {
195
+ return false; // Empty.
196
+ }
197
+ if (!is_admin()) {
198
+ return false;
199
+ }
200
+ $page = $pagenow = ''; // Initialize.
201
+
202
+ if (!empty($_REQUEST['page'])) {
203
+ $page = (string) $_REQUEST['page'];
204
+ }
205
+ if (!empty($GLOBALS['pagenow'])) {
206
+ $pagenow = (string) $GLOBALS['pagenow'];
207
+ }
208
+ if ($page && fnmatch($which, $page, FNM_CASEFOLD)) {
209
+ return true; // Wildcard match.
210
+ }
211
+ if ($pagenow && fnmatch($which, $pagenow, FNM_CASEFOLD)) {
212
+ return true; // Wildcard match.
213
+ }
214
+ return false; // Nope.
215
+ };
src/includes/closures/Plugin/NoticeUtils.php ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Notice queue handlers.
6
+ */
7
+
8
+ /*
9
+ * Enqueue an administrative notice.
10
+ *
11
+ * @since 150422 Rewrite. Improved 151002.
12
+ *
13
+ * @param string $notice HTML markup containing the notice itself.
14
+ * @param string $args Any additional arguments supported by the notice API in this plugin.
15
+ * @param integer $blog_id Optional. Defaults to the current blog ID. Use any value `< 0` to indicate the main site.
16
+ *
17
+ * @return string A unique key generated for this notice.
18
+ */
19
+ $self->enqueueNotice = function ($notice, array $args = array(), $blog_id = 0) use ($self) {
20
+ $notice = trim((string) $notice);
21
+ $blog_id = (integer) $blog_id;
22
+
23
+ if (!$notice) {
24
+ return; // Nothing to do.
25
+ }
26
+ $notice = array('notice' => $notice);
27
+ $notice = $self->normalizeNotice($notice, $args);
28
+ $key = sha1(serialize($notice)); // Prevent dupes.
29
+
30
+ $notices = $self->getNotices($blog_id);
31
+
32
+ if ($notice['push_to_top']) {
33
+ $notices = array($key => $notice) + $notices;
34
+ } else {
35
+ $notices[$key] = $notice; // Default behavior.
36
+ }
37
+ $self->updateNotices($notices, $blog_id);
38
+
39
+ return $key; // For dismissals.
40
+ };
41
+
42
+ /*
43
+ * Dismiss an administrative notice.
44
+ *
45
+ * @since 151002 Improving multisite compat.
46
+ *
47
+ * @param string $key_to_dismiss A unique key which identifies a particular notice.
48
+ * Or, a persistent key which identifies one or more persistent notices.
49
+ *
50
+ * @param integer $blog_id The blog ID from which to dismiss the notice.
51
+ *
52
+ * @return array All remaining notices.
53
+ */
54
+ $self->dismissNotice = function ($key_to_dismiss, $blog_id = 0) use ($self) {
55
+ $key_to_dismiss = trim((string) $key_to_dismiss);
56
+ $blog_id = (integer) $blog_id; // For multisite compat.
57
+ $notices = $enqueued_notices = $self->getNotices($blog_id);
58
+
59
+ if (!$key_to_dismiss) {
60
+ return $notices; // Nothing to do.
61
+ }
62
+ foreach ($notices as $_key => $_notice) {
63
+ if ($_key === $key_to_dismiss) {
64
+ unset($notices[$_key]); // A specific key.
65
+ } elseif ($_notice['persistent_key'] === $key_to_dismiss) {
66
+ unset($notices[$_key]); // All matching keys.
67
+ }
68
+ } // ↑ Dismisses all matching keys.
69
+ unset($_key, $_notice); // Housekeeping.
70
+
71
+ if ($notices !== $enqueued_notices) { // Something changed?
72
+ $self->updateNotices($notices, $blog_id); // Update.
73
+ }
74
+ return $notices; // All remaining notices.
75
+ };
76
+
77
+ /*
78
+ * Enqueue an administrative error notice.
79
+ *
80
+ * @since 150422 Rewrite. Improved 151002.
81
+ */
82
+ $self->enqueueError = function ($notice, array $args = array(), $blog_id = 0) use ($self) {
83
+ return $self->enqueueNotice($notice, array_merge($args, array('class' => 'error')), $blog_id);
84
+ };
85
+
86
+ /*
87
+ * Enqueue an administrative notice (main site).
88
+ *
89
+ * @since 151002. Improving multisite compat.
90
+ */
91
+ $self->enqueueMainNotice = function ($notice, array $args = array()) use ($self) {
92
+ return $self->enqueueNotice($notice, $args, -1);
93
+ };
94
+
95
+ /*
96
+ * Enqueue an administrative error notice (main site).
97
+ *
98
+ * @since 151002. Improving multisite compat.
99
+ */
100
+ $self->enqueueMainError = function ($notice, array $args = array()) use ($self) {
101
+ return $self->enqueueNotice($notice, array_merge($args, array('class' => 'error')), -1);
102
+ };
103
+
104
+ /*
105
+ * Dismiss an administrative notice (main site).
106
+ *
107
+ * @since 151002 Improving multisite compat.
108
+ */
109
+ $self->dismissMainNotice = function ($key_to_dismiss) use ($self) {
110
+ return $self->dismissNotice($key_to_dismiss, -1);
111
+ };
112
+
113
+ /*
114
+ * Notice display handler.
115
+ */
116
+
117
+ /*
118
+ * Render admin notices.
119
+ *
120
+ * @since 150422 Rewrite. Improved 151002.
121
+ *
122
+ * @attaches-to `all_admin_notices` hook.
123
+ */
124
+ $self->allAdminNotices = function () use ($self) {
125
+ $notices = $enqueued_notices = $self->getNotices();
126
+
127
+ foreach ($notices as $_key => $_notice) {
128
+ # Always dismiss all non-persistent transients.
129
+
130
+ if ($_notice['is_transient'] && !$_notice['persistent_key']) {
131
+ unset($notices[$_key]); // Dismiss.
132
+ }
133
+ # Current user can see this notice?
134
+
135
+ if (!current_user_can($self->cap)) {
136
+ continue; // Current user unable to see.
137
+ }
138
+ if ($_notice['cap_required'] && !current_user_can($_notice['cap_required'])) {
139
+ continue; // Current user unable to see this notice.
140
+ }
141
+ # Current URI matches a limited scope/context for this notice?
142
+
143
+ if ($_notice['only_on_uris'] && !@preg_match($_notice['only_on_uris'], $_SERVER['REQUEST_URI'])) {
144
+ continue; // Not in the right context at the moment; i.e., does not regex.
145
+ }
146
+ # If persistent, allow a site owner to dismiss.
147
+
148
+ $_dismiss = ''; // Reset this to its default state.
149
+ if ($_notice['persistent_key'] && $_notice['dismissable']) { // See above. The `dismissNotice()` action requires `$self->cap` always.
150
+ $_dismiss = add_query_arg(urlencode_deep(array(GLOBAL_NS => array('dismissNotice' => array('key' => $_key)), '_wpnonce' => wp_create_nonce())));
151
+ $_dismiss = '<a style="display:inline-block; float:right; margin:0 0 0 15px; text-decoration:none; font-weight:bold;" href="'.esc_attr($_dismiss).'">'.__('dismiss &times;', 'comet-cache').'</a>';
152
+ }
153
+ # Display this notice. If not persistent, we can dismiss it too.
154
+
155
+ echo '<div class="'.esc_attr($_notice['class']).'"><p>'.$_notice['notice'].$_dismiss.'</p></div>';
156
+
157
+ if (!$_notice['persistent_key']) { // If not persistent, dismiss.
158
+ unset($notices[$_key]); // Dismiss; this notice has been displayed now.
159
+ }
160
+ }
161
+ unset($_key, $_notice, $_dismiss); // Housekeeping.
162
+
163
+ # Update notices if something changed above.
164
+
165
+ if ($notices !== $enqueued_notices) { // Something changed?
166
+ $self->updateNotices($notices); // Update.
167
+ }
168
+ };
169
+
170
+ /*
171
+ * Notice getter/setter.
172
+ */
173
+
174
+ /*
175
+ * Get admin notices.
176
+ *
177
+ * @since 151002 Improving multisite compat.
178
+ *
179
+ * @param integer $blog_id Optional. Defaults to the current blog ID.
180
+ * Use any value `< 0` to indicate the main site.
181
+ *
182
+ * @return array All notices.
183
+ */
184
+ $self->getNotices = function ($blog_id = 0) use ($self) {
185
+ if (is_multisite()) {
186
+ if (!($blog_id = (integer) $blog_id)) {
187
+ $blog_id = (integer) get_current_blog_id();
188
+ }
189
+ if ($blog_id < 0) { // Blog for main site.
190
+ $blog_id = (integer) get_current_site()->blog_id;
191
+ }
192
+ $blog_suffix = '_'.$blog_id; // Site option suffix.
193
+ $notices = get_site_option(GLOBAL_NS.$blog_suffix.'_notices');
194
+ } else {
195
+ $notices = get_site_option(GLOBAL_NS.'_notices');
196
+ }
197
+ if (!is_array($notices)) {
198
+ $notices = array(); // Force array.
199
+ // Prevent multiple DB queries by adding this key.
200
+ $self->updateNotices($notices, $blog_id);
201
+ }
202
+ foreach ($notices as $_key => &$_notice) {
203
+ if (!is_string($_key) || !is_array($_notice) || empty($_notice['notice'])) {
204
+ unset($notices[$_key]); // Old notice.
205
+ continue; // Bypass; i.e., do not normalize.
206
+ }
207
+ $_notice = $self->normalizeNotice($_notice);
208
+ } // ↑ Typecast/normalized each of the array elements.
209
+ unset($_key, $_notice); // Housekeeping.
210
+
211
+ return $notices;
212
+ };
213
+
214
+ /*
215
+ * Update admin notices.
216
+ *
217
+ * @since 151002 Improving multisite compat.
218
+ *
219
+ * @param array $notices New array of notices.
220
+ *
221
+ * @param integer $blog_id Optional. Defaults to the current blog ID.
222
+ * Use any value `< 0` to indicate the main site.
223
+ *
224
+ * @return array All notices.
225
+ */
226
+ $self->updateNotices = function (array $notices, $blog_id = 0) use ($self) {
227
+ if (is_multisite()) {
228
+ if (!($blog_id = (integer) $blog_id)) {
229
+ $blog_id = (integer) get_current_blog_id();
230
+ }
231
+ if ($blog_id < 0) { // Blog for main site.
232
+ $blog_id = (integer) get_current_site()->blog_id;
233
+ }
234
+ $blog_suffix = '_'.$blog_id; // Site option suffix.
235
+ update_site_option(GLOBAL_NS.$blog_suffix.'_notices', $notices);
236
+ } else {
237
+ update_site_option(GLOBAL_NS.'_notices', $notices);
238
+ }
239
+ return $notices;
240
+ };
241
+
242
+ /*
243
+ * Notice property utilities.
244
+ */
245
+
246
+ /*
247
+ * Normalize notice elements.
248
+ *
249
+ * @since 151002 Improving multisite compat.
250
+ *
251
+ * @param array $notice Notice array elements.
252
+ * @param array $args Any additional array elements.
253
+ *
254
+ * @return array Normalized notice array elements.
255
+ */
256
+ $self->normalizeNotice = function (array $notice, array $args = array()) use ($self) {
257
+ $notice_defaults = array(
258
+ 'notice' => '',
259
+ 'only_on_uris' => '',
260
+ 'persistent_key' => '',
261
+ 'dismissable' => true,
262
+ 'is_transient' => true,
263
+ 'push_to_top' => false,
264
+ 'class' => 'updated',
265
+ 'cap_required' => '', // `$self->cap` always.
266
+ // i.e., this cap is in addition to `$self->cap`.
267
+ );
268
+ $notice = array_merge($notice_defaults, $notice, $args);
269
+ $notice = array_intersect_key($notice, $notice_defaults);
270
+
271
+ foreach ($notice as $_key => &$_value) {
272
+ switch ($_key) {
273
+ case 'notice':
274
+ case 'only_on_uris':
275
+ case 'persistent_key':
276
+ $_value = trim((string) $_value);
277
+ break; // Stop here.
278
+
279
+ case 'is_transient':
280
+ case 'push_to_top':
281
+ case 'dismissable':
282
+ $_value = (boolean) $_value;
283
+ break; // Stop here.
284
+
285
+ case 'class':
286
+ case 'cap_required':
287
+ $_value = trim((string) $_value);
288
+ break; // Stop here.
289
+ }
290
+ } // ↑ Typecast each of the array elements.
291
+ unset($_key, $_value); // A little housekeeping.
292
+
293
+ ksort($notice); // For more accurate comparison in other routines.
294
+
295
+ return $notice; // Normalized.
296
+ };
src/includes/closures/Plugin/OptionUtils.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Get plugin options.
6
+ *
7
+ * @since 151002 Improving multisite compat.
8
+ *
9
+ * @return array Plugin options.
10
+ */
11
+ $self->getOptions = function () use ($self) {
12
+ if (!($options = $self->options)) { // Not defined yet?
13
+ if (!is_array($options = get_site_option(GLOBAL_NS.'_options'))) {
14
+ $options = array(); // Force array.
15
+ }
16
+ if (!$options && is_array($zencache_options = get_site_option('zencache_options'))) {
17
+ $options = $zencache_options; // Old ZenCache options.
18
+ $options['crons_setup'] = $this->default_options['crons_setup'];
19
+ }
20
+ }
21
+ $self->options = array_merge($self->default_options, $options);
22
+ $self->options = $self->applyWpFilters(GLOBAL_NS.'_options', $self->options);
23
+ $self->options = array_intersect_key($self->options, $self->default_options);
24
+
25
+ foreach ($self->options as $_key => &$_value) {
26
+ $_value = trim((string) $_value); // Force strings.
27
+ } unset($_key, $_value); // Housekeeping.
28
+
29
+ $self->options['base_dir'] = trim($self->options['base_dir'], '\\/'." \t\n\r\0\x0B");
30
+ if (!$self->options['base_dir'] || strpos(basename($self->options['base_dir']), 'wp-') === 0) {
31
+ $self->options['base_dir'] = $self->default_options['base_dir'];
32
+ }
33
+ return $self->options; // Plugin options.
34
+ };
35
+
36
+ /*
37
+ * Update plugin options.
38
+ *
39
+ * @since 151002 Improving multisite compat.
40
+ *
41
+ * @param array $options One or more new options.
42
+ *
43
+ * @return array Plugin options after update.
44
+ */
45
+ $self->updateOptions = function (array $options) use ($self) {
46
+ if (!IS_PRO) { // Do not save Pro option keys.
47
+ $options = array_diff_key($options, $self->pro_only_option_keys);
48
+ }
49
+ if (!empty($options['base_dir']) && $options['base_dir'] !== $self->options['base_dir']) {
50
+ $self->tryErasingAllFilesDirsIn($self->wpContentBaseDirTo(''));
51
+ }
52
+ $self->options = array_merge($self->default_options, $self->options, $options);
53
+ $self->options = array_intersect_key($self->options, $self->default_options);
54
+ update_site_option(GLOBAL_NS.'_options', $self->options);
55
+
56
+ return $self->getOptions();
57
+ };
58
+
59
+ /*
60
+ * Restore default plugin options.
61
+ *
62
+ * @since 151002 Improving multisite compat.
63
+ *
64
+ * @return array Plugin options after update.
65
+ */
66
+ $self->restoreDefaultOptions = function () use ($self) {
67
+ delete_site_option(GLOBAL_NS.'_options'); // Force restore.
68
+ $self->options = $self->default_options; // In real-time.
69
+ return $self->getOptions();
70
+ };
src/includes/closures/Plugin/PostUtils.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * All post statuses.
6
+ *
7
+ * @since 150821 Improving bbPress support.
8
+ *
9
+ * @return array All post statuses.
10
+ */
11
+ $self->postStatuses = function () use ($self) {
12
+ if (!is_null($statuses = &$self->cacheKey('postStatuses'))) {
13
+ return $statuses; // Already did this.
14
+ }
15
+ $statuses = get_post_stati();
16
+ $statuses = array_keys($statuses);
17
+
18
+ return $statuses;
19
+ };
20
+
21
+ /*
22
+ * All built-in post statuses.
23
+ *
24
+ * @since 150821 Improving bbPress support.
25
+ *
26
+ * @return array All built-in post statuses.
27
+ */
28
+ $self->builtInPostStatuses = function () use ($self) {
29
+ if (!is_null($statuses = &$self->cacheKey('builtInPostStatuses'))) {
30
+ return $statuses; // Already did this.
31
+ }
32
+ $statuses = array(); // Initialize.
33
+
34
+ foreach (get_post_stati(array(), 'objects') as $_key => $_status) {
35
+ if (!empty($_status->_builtin)) {
36
+ $statuses[] = $_status->name;
37
+ }
38
+ }
39
+ unset($_key, $_status); // Housekeeping.
40
+
41
+ return $statuses;
42
+ };
src/includes/closures/Plugin/UpdateUtils.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Checks for a new lite release.
6
+ *
7
+ * @since 151220 Show version number in plugin options.
8
+ *
9
+ * @attaches-to `admin_init` hook.
10
+ */
11
+ $self->maybeCheckLatestLiteVersion = function () use ($self) {
12
+ if (IS_PRO) {
13
+ return; // Not applicable.
14
+ }
15
+ if (!$self->options['lite_update_check']) {
16
+ return; // Nothing to do.
17
+ }
18
+ if (!current_user_can($self->update_cap)) {
19
+ return; // Nothing to do.
20
+ }
21
+ if (is_multisite() && !current_user_can($self->network_cap)) {
22
+ return; // Nothing to do.
23
+ }
24
+ if ($self->options['last_lite_update_check'] >= strtotime('-1 hour')) {
25
+ return; // No reason to keep checking on this.
26
+ }
27
+ $self->updateOptions(array('last_lite_update_check' => time()));
28
+
29
+ $product_api_url = 'https://'.urlencode(DOMAIN).'/';
30
+ $product_api_input_vars = array('product_api' => array('action' => 'latest_lite_version'));
31
+
32
+ $product_api_response = wp_remote_post($product_api_url, array('body' => $product_api_input_vars));
33
+ $product_api_response = json_decode(wp_remote_retrieve_body($product_api_response));
34
+
35
+ if (is_object($product_api_response) && !empty($product_api_response->lite_version)) {
36
+ $self->updateOptions(array('latest_lite_version' => $product_api_response->lite_version));
37
+ }
38
+ // Disabling the notice for now. We only run this check to collect the latest version number.
39
+ #if ($self->options['latest_lite_version'] && version_compare(VERSION, $self->options['latest_lite_version'], '<')) {
40
+ # $self->dismissMainNotice('new-lite-version-available'); // Dismiss any existing notices like this.
41
+ # $lite_updater_page = network_admin_url('/plugins.php'); // In a network this points to the master plugins list.
42
+ # $self->enqueueMainNotice(sprintf(__('<strong>%1$s:</strong> a new version is now available. Please <a href="%2$s">upgrade to v%3$s</a>.', 'comet-cache'), esc_html(NAME), esc_attr($lite_updater_page), esc_html($self->options['latest_lite_version'])), array('persistent_key' => 'new-lite-version-available'));
43
+ #}
44
+ };
45
+
46
+
src/includes/closures/Plugin/UrlUtils.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * URL to a Comet Cache plugin file.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param string $file Optional file path; relative to plugin directory.
10
+ * @param string $scheme Optional URL scheme; defaults to the current scheme.
11
+ *
12
+ * @return string URL to plugin directory; or to the specified `$file` if applicable.
13
+ */
14
+ $self->url = function ($file = '', $scheme = '') use ($self) {
15
+ $url = rtrim(plugin_dir_url(PLUGIN_FILE), '/');
16
+ $url .= (string) $file;
17
+
18
+ if ($scheme) {
19
+ $url = set_url_scheme($url, (string) $scheme);
20
+ }
21
+ return $url;
22
+ };
src/includes/closures/Plugin/UserUtils.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Current user can clear the cache?
6
+ *
7
+ * @since 151002 Enhancing user permissions.
8
+ *
9
+ * @return boolean Current user can clear the cache?
10
+ */
11
+ $self->currentUserCanClearCache = function () use ($self) {
12
+ if (!is_null($can = &$self->cacheKey('currentUserCanClearCache'))) {
13
+ return $can; // Already cached this.
14
+ }
15
+ $is_multisite = is_multisite();
16
+
17
+ if (!$is_multisite && current_user_can($self->cap)) {
18
+ return ($can = true); // Plugin admin.
19
+ }
20
+ if ($is_multisite && current_user_can($self->network_cap)) {
21
+ return ($can = true); // Plugin admin.
22
+ }
23
+
24
+ return ($can = false);
25
+ };
26
+ $self->currentUserCanWipeCache = $self->currentUserCanClearCache;
27
+
28
+ /*
29
+ * Current user can clear the opcache?
30
+ *
31
+ * @since 151114 Enhancing user permissions.
32
+ *
33
+ * @return boolean Current user can clear the opcache?
34
+ */
35
+ $self->currentUserCanClearOpCache = function () use ($self) {
36
+ if (!is_null($can = &$self->cacheKey('currentUserCanClearOpCache'))) {
37
+ return $can; // Already cached this.
38
+ }
39
+ $is_multisite = is_multisite();
40
+
41
+ if (!$is_multisite && current_user_can($self->cap)) {
42
+ return ($can = true); // Plugin admin.
43
+ }
44
+ if ($is_multisite && current_user_can($self->network_cap)) {
45
+ return ($can = true); // Plugin admin.
46
+ }
47
+ return ($can = false);
48
+ };
49
+ $self->currentUserCanWipeOpCache = $self->currentUserCanClearOpCache;
50
+
51
+ /*
52
+ * Current user can clear the CDN cache?
53
+ *
54
+ * @since 151114 Enhancing user permissions.
55
+ *
56
+ * @return boolean Current user can clear the CDN cache?
57
+ */
58
+ $self->currentUserCanClearCdnCache = function () use ($self) {
59
+ if (!is_null($can = &$self->cacheKey('currentUserCanClearCdnCache'))) {
60
+ return $can; // Already cached this.
61
+ }
62
+ $is_multisite = is_multisite();
63
+
64
+ if (!$is_multisite && current_user_can($self->cap)) {
65
+ return ($can = true); // Plugin admin.
66
+ }
67
+ if ($is_multisite && current_user_can($self->network_cap)) {
68
+ return ($can = true); // Plugin admin.
69
+ }
70
+ return ($can = false);
71
+ };
72
+ $self->currentUserCanWipeCdnCache = $self->currentUserCanClearCdnCache;
73
+
74
+ /*
75
+ * Current user can clear expired transients?
76
+ *
77
+ * @since 151220 Enhancing user permissions.
78
+ *
79
+ * @return boolean Current user can clear expired transients?
80
+ */
81
+ $self->currentUserCanClearExpiredTransients = function () use ($self) {
82
+ if (!is_null($can = &$self->cacheKey('currentUserCanClearExpiredTransients'))) {
83
+ return $can; // Already cached this.
84
+ }
85
+ $is_multisite = is_multisite();
86
+
87
+ if (!$is_multisite && current_user_can($self->cap)) {
88
+ return ($can = true); // Plugin admin.
89
+ }
90
+ if ($is_multisite && current_user_can($self->network_cap)) {
91
+ return ($can = true); // Plugin admin.
92
+ }
93
+ return ($can = false);
94
+ };
95
+ $self->currentUserCanWipeExpiredTransients = $self->currentUserCanClearExpiredTransients;
96
+
97
+ /*
98
+ * Current user can see stats?
99
+ *
100
+ * @since 151002 Enhancing user permissions.
101
+ *
102
+ * @return boolean Current user can see stats?
103
+ */
104
+ $self->currentUserCanSeeStats = function () use ($self) {
105
+ if (!is_null($can = &$self->cacheKey('currentUserCanSeeStats'))) {
106
+ return $can; // Already cached this.
107
+ }
108
+ $is_multisite = is_multisite();
109
+
110
+ if (!$is_multisite && current_user_can($self->cap)) {
111
+ return ($can = true); // Plugin admin.
112
+ }
113
+ if ($is_multisite && current_user_can($self->network_cap)) {
114
+ return ($can = true); // Plugin admin.
115
+ }
116
+
117
+ return ($can = false);
118
+ };
src/includes/closures/Plugin/WcpAuthorUtils.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache files for the author page(s).
6
+ *
7
+ * @attaches-to `post_updated` hook.
8
+ *
9
+ * @since 150422 Rewrite.
10
+ *
11
+ * @param int $post_id A WordPress post ID.
12
+ * @param \WP_Post $post_after WP_Post object following the update.
13
+ * @param \WP_Post $post_before WP_Post object before the update.
14
+ *
15
+ * @throws \Exception If a clear failure occurs.
16
+ *
17
+ * @return int Total files cleared by this routine (if any).
18
+ *
19
+ * @note If the author for the post is being changed, both the previous author
20
+ * and current author pages are cleared, if the post status is applicable.
21
+ */
22
+ $self->autoClearAuthorPageCache = function ($post_id, \WP_Post $post_after, \WP_Post $post_before) use ($self) {
23
+ $counter = 0; // Initialize.
24
+ $enqueued_notices = 0; // Initialize.
25
+ $authors = array(); // Initialize.
26
+ $authors_to_clear = array(); // Initialize.
27
+
28
+ if (!($post_id = (integer) $post_id)) {
29
+ return $counter; // Nothing to do.
30
+ }
31
+ if (!is_null($done = &$self->cacheKey('autoClearAuthorPageCache', array($post_id, $post_after->ID, $post_before->ID)))) {
32
+ return $counter; // Already did this.
33
+ }
34
+ $done = true; // Flag as having been done.
35
+
36
+ if (!$self->options['enable']) {
37
+ return $counter; // Nothing to do.
38
+ }
39
+ if (!$self->options['cache_clear_author_page_enable']) {
40
+ return $counter; // Nothing to do.
41
+ }
42
+ if (!is_dir($cache_dir = $self->cacheDir())) {
43
+ return $counter; // Nothing to do.
44
+ }
45
+ /*
46
+ * If we're changing the post author AND
47
+ * the previous post status was either 'published' or 'private'
48
+ * then clear the author page for both authors.
49
+ *
50
+ * Else if the old post status was 'published' or 'private' OR
51
+ * the new post status is 'published' or 'private'
52
+ * then clear the author page for the current author.
53
+ *
54
+ * Else return the counter; post status does not warrant clearing author page cache.
55
+ */
56
+ if ($post_after->post_author !== $post_before->post_author &&
57
+ ($post_before->post_status === 'publish' || $post_before->post_status === 'private')
58
+ ) {
59
+ $authors[] = (integer) $post_before->post_author;
60
+ $authors[] = (integer) $post_after->post_author;
61
+ } elseif (($post_before->post_status === 'publish' || $post_before->post_status === 'private') ||
62
+ ($post_after->post_status === 'publish' || $post_after->post_status === 'private')
63
+ ) {
64
+ $authors[] = (integer) $post_after->post_author;
65
+ }
66
+ if (!$authors) {
67
+ return $counter; // Nothing to do.
68
+ }
69
+ foreach ($authors as $_author_id) {
70
+ $authors_to_clear[$_author_id]['posts_url'] = get_author_posts_url($_author_id);
71
+ $authors_to_clear[$_author_id]['display_name'] = get_the_author_meta('display_name', $_author_id);
72
+ }
73
+ unset($_author_id); // Housekeeping.
74
+
75
+ foreach ($authors_to_clear as $_author) {
76
+ $_author_regex = $self->buildHostCachePathRegex($_author['posts_url']);
77
+ $_author_counter = $self->clearFilesFromHostCacheDir($_author_regex);
78
+ $counter += $_author_counter; // Add to overall counter.
79
+
80
+ if ($_author_counter && $enqueued_notices < 100 && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
81
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
82
+ sprintf(__('<strong>%1$s:</strong> detected changes. Found %2$s in the cache for Author Page: <code>%3$s</code>; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($_author_counter)), esc_html($_author['display_name'])));
83
+ $enqueued_notices++; // Increment enqueued notices counter.
84
+ }
85
+ }
86
+ unset($_author, $_author_regex, $_author_counter); // Housekeeping.
87
+
88
+ $counter += $self->autoClearXmlFeedsCache('blog');
89
+ $counter += $self->autoClearXmlFeedsCache('post-authors', $post_id);
90
+
91
+ return $counter;
92
+ };
src/includes/closures/Plugin/WcpCommentUtils.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache files for a post associated with a particular comment.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @attaches-to `trackback_post` hook.
10
+ * @attaches-to `pingback_post` hook.
11
+ * @attaches-to `comment_post` hook.
12
+ *
13
+ * @param int $comment_id A WordPress comment ID.
14
+ *
15
+ * @return int Total files cleared by this routine (if any).
16
+ */
17
+ $self->autoClearCommentPostCache = function ($comment_id) use ($self) {
18
+ $counter = 0; // Initialize.
19
+
20
+ if (!($comment_id = (integer) $comment_id)) {
21
+ return $counter; // Nothing to do.
22
+ }
23
+ if (!is_null($done = &$self->cacheKey('autoClearCommentPostCache', $comment_id))) {
24
+ return $counter; // Already did this.
25
+ }
26
+ $done = true; // Flag as having been done.
27
+
28
+ if (!$self->options['enable']) {
29
+ return $counter; // Nothing to do.
30
+ }
31
+ if (!is_object($comment = get_comment($comment_id))) {
32
+ return $counter; // Nothing we can do.
33
+ }
34
+ if (empty($comment->comment_post_ID)) {
35
+ return $counter; // Nothing we can do.
36
+ }
37
+ if ($comment->comment_approved === 'spam' || $comment->comment_approved === '0') {
38
+ // Don't allow next `autoClearPostCache()` call to clear post cache.
39
+ $allow = &$self->cacheKey('autoClearPostCache_allow');
40
+ $allow = false; // Flag as false; i.e., disallow.
41
+ return $counter;
42
+ }
43
+ $counter += $self->autoClearXmlFeedsCache('blog-comments');
44
+ $counter += $self->autoClearXmlFeedsCache('post-comments', $comment->comment_post_ID);
45
+ $counter += $self->autoClearPostCache($comment->comment_post_ID);
46
+
47
+ return $counter;
48
+ };
49
+
50
+ /*
51
+ * Automatically clears cache files for a post associated with a particular comment.
52
+ *
53
+ * @since 150422 Rewrite.
54
+ *
55
+ * @attaches-to `transition_comment_status` hook.
56
+ *
57
+ * @param string $new_status New comment status.
58
+ * @param string $old_status Old comment status.
59
+ * @param \stdClass $comment Comment object.
60
+ *
61
+ * @throws \Exception If a clear failure occurs.
62
+ *
63
+ * @return int Total files cleared by this routine (if any).
64
+ *
65
+ * @note This is also called upon by other routines which listen for
66
+ * events that are indirectly associated with a comment ID.
67
+ */
68
+ $self->autoClearCommentPostCacheTransition = function ($new_status, $old_status, $comment) use ($self) {
69
+ $counter = 0; // Initialize.
70
+
71
+ if (!is_object($comment)) {
72
+ return $counter; // Nothing we can do.
73
+ }
74
+ if (empty($comment->comment_post_ID)) {
75
+ return $counter; // Nothing we can do.
76
+ }
77
+ if (!is_null($done = &$self->cacheKey('autoClearCommentPostCacheTransition', array($new_status, $old_status, $comment->comment_post_ID)))) {
78
+ return $counter; // Already did this.
79
+ }
80
+ $done = true; // Flag as having been done.
81
+
82
+ if (!$self->options['enable']) {
83
+ return $counter; // Nothing to do.
84
+ }
85
+ if (!($old_status === 'approved' || ($old_status === 'unapproved' && $new_status === 'approved'))) {
86
+ // If excluded here, don't allow next `autoClearPostCache()` call to clear post cache.
87
+ $allow = &$self->cacheKey('autoClearPostCache_allow');
88
+ $allow = false; // Flag as false; i.e., disallow.
89
+ return $counter;
90
+ }
91
+ $counter += $self->autoClearXmlFeedsCache('blog-comments');
92
+ $counter += $self->autoClearXmlFeedsCache('post-comments', $comment->comment_post_ID);
93
+ $counter += $self->autoClearPostCache($comment->comment_post_ID);
94
+
95
+ return $counter;
96
+ };
src/includes/closures/Plugin/WcpFeedUtils.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache files related to XML feeds.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param string $type Type of feed(s) to auto-clear.
10
+ * @param int $post_id A Post ID (when applicable).
11
+ *
12
+ * @throws \Exception If a clear failure occurs.
13
+ *
14
+ * @return int Total files cleared by this routine (if any).
15
+ *
16
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently
17
+ * attached to any hooks. However, it is called upon by other routines attached to hooks.
18
+ */
19
+ $self->autoClearXmlFeedsCache = function ($type, $post_id = 0) use ($self) {
20
+ $counter = 0; // Initialize.
21
+
22
+ if (!($type = (string) $type)) {
23
+ return $counter; // Nothing we can do.
24
+ }
25
+ $post_id = (integer) $post_id; // Force integer.
26
+
27
+ if (!is_null($done = &$self->cacheKey('autoClearXmlFeedsCache', array($type, $post_id)))) {
28
+ return $counter; // Already did this.
29
+ }
30
+ $done = true; // Flag as having been done.
31
+
32
+ if (!$self->options['enable']) {
33
+ return $counter; // Nothing to do.
34
+ }
35
+ if (!$self->options['feeds_enable']) {
36
+ return $counter; // Nothing to do.
37
+ }
38
+ if (!$self->options['cache_clear_xml_feeds_enable']) {
39
+ return $counter; // Nothing to do.
40
+ }
41
+ if (!is_dir($cache_dir = $self->cacheDir())) {
42
+ return $counter; // Nothing to do.
43
+ }
44
+ $utils = new FeedUtils(); // Feed utilities.
45
+ $variations = $variation_regex_frags = array(); // Initialize.
46
+
47
+ switch ($type) { // Handle clearing based on the `$type`.
48
+
49
+ case 'blog': // The blog feed; i.e. `/feed/` on most WP installs.
50
+ $variations = array_merge($variations, $utils->feedLinkVariations());
51
+ break; // Break switch handler.
52
+
53
+ case 'blog-comments': // The blog comments feed; i.e. `/comments/feed/` on most WP installs.
54
+ $variations = array_merge($variations, $utils->feedLinkVariations('comments_'));
55
+ break; // Break switch handler.
56
+
57
+ case 'post-comments': // Feeds related to comments that a post has.
58
+ if (!$post_id) {
59
+ break; // Break switch handler.
60
+ }
61
+ if (!($post = get_post($post_id))) {
62
+ break; // Break switch handler.
63
+ }
64
+ $variations = array_merge($variations, $utils->postCommentsFeedLinkVariations($post));
65
+ break; // Break switch handler.
66
+
67
+ case 'post-authors': // Feeds related to authors that a post has.
68
+ if (!$post_id) {
69
+ break; // Break switch handler.
70
+ }
71
+ if (!($post = get_post($post_id))) {
72
+ break; // Break switch handler.
73
+ }
74
+ $variations = array_merge($variations, $utils->postAuthorFeedLinkVariations($post));
75
+ break; // Break switch handler.
76
+
77
+ case 'post-terms': // Feeds related to terms that a post has.
78
+ if (!$post_id) {
79
+ break; // Break switch handler.
80
+ }
81
+ if (!($post = get_post($post_id))) {
82
+ break; // Break switch handler.
83
+ }
84
+ $variations = array_merge($variations, $utils->postTermFeedLinkVariations($post, true));
85
+ break; // Break switch handler.
86
+
87
+ case 'custom-post-type': // Feeds related to a custom post type archive view.
88
+ if (!$post_id) {
89
+ break; // Break switch handler.
90
+ }
91
+ if (!($post = get_post($post_id))) {
92
+ break; // Break switch handler.
93
+ }
94
+ $variations = array_merge($variations, $utils->postTypeArchiveFeedLinkVariations($post));
95
+ break; // Break switch handler.
96
+
97
+ // @TODO Possibly consider search-related feeds in the future.
98
+ // See: <http://codex.wordpress.org/WordPress_Feeds#Categories_and_Tags>
99
+ }
100
+ if (!($variation_regex_frags = $utils->convertVariationsToHostCachePathRegexFrags($variations))) {
101
+ return $counter; // Nothing to do here.
102
+ }
103
+ $in_sets_of = $self->applyWpFilters(GLOBAL_NS.'_autoClearXmlFeedsCache_in_sets_of', 10, get_defined_vars());
104
+ for ($_i = 0; $_i < count($variation_regex_frags); $_i = $_i + $in_sets_of) {
105
+ $_variation_regex_frags = array_slice($variation_regex_frags, $_i, $in_sets_of);
106
+ $_regex = '/^\/(?:'.implode('|', $_variation_regex_frags).')\./i';
107
+ $counter += $self->clearFilesFromHostCacheDir($_regex);
108
+ }
109
+ unset($_i, $_variation_regex_frags, $_regex); // Housekeeping.
110
+
111
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
112
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
113
+ sprintf(__('<strong>%1$s:</strong> detected changes. Found %2$s in the cache, for XML feeds of type: <code>%3$s</code>; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)), esc_html($type)));
114
+ }
115
+ return $counter;
116
+ };
src/includes/closures/Plugin/WcpHomeBlogUtils.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache files for the home page.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @throws \Exception If a clear failure occurs.
10
+ *
11
+ * @return int Total files cleared by this routine (if any).
12
+ *
13
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently
14
+ * attached to any hooks. However, it is called upon by {@link autoClearPostCache()}.
15
+ */
16
+ $self->autoClearHomePageCache = function () use ($self) {
17
+ $counter = 0; // Initialize.
18
+
19
+ if (!is_null($done = &$self->cacheKey('autoClearHomePageCache'))) {
20
+ return $counter; // Already did this.
21
+ }
22
+ $done = true; // Flag as having been done.
23
+
24
+ if (!$self->options['enable']) {
25
+ return $counter; // Nothing to do.
26
+ }
27
+ if (!$self->options['cache_clear_home_page_enable']) {
28
+ return $counter; // Nothing to do.
29
+ }
30
+ if (!is_dir($cache_dir = $self->cacheDir())) {
31
+ return $counter; // Nothing to do.
32
+ }
33
+ $regex = $self->buildHostCachePathRegex(home_url('/'));
34
+ $counter += $self->clearFilesFromHostCacheDir($regex);
35
+
36
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
37
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
38
+ sprintf(__('<strong>%1$s:</strong> detected changes. Found %2$s in the cache for the designated "Home Page"; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter))));
39
+ }
40
+ $counter += $self->autoClearXmlFeedsCache('blog');
41
+
42
+ return $counter;
43
+ };
44
+
45
+ /*
46
+ * Automatically clears cache files for the posts page.
47
+ *
48
+ * @since 150422 Rewrite.
49
+ *
50
+ * @throws \Exception If a clear failure occurs.
51
+ *
52
+ * @return int Total files cleared by this routine (if any).
53
+ *
54
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently
55
+ * attached to any hooks. However, it is called upon by {@link autoClearPostCache()}.
56
+ */
57
+ $self->autoClearPostsPageCache = function () use ($self) {
58
+ $counter = 0; // Initialize.
59
+
60
+ if (!is_null($done = &$self->cacheKey('autoClearPostsPageCache'))) {
61
+ return $counter; // Already did this.
62
+ }
63
+ $done = true; // Flag as having been done.
64
+
65
+ if (!$self->options['enable']) {
66
+ return $counter; // Nothing to do.
67
+ }
68
+ if (!$self->options['cache_clear_posts_page_enable']) {
69
+ return $counter; // Nothing to do.
70
+ }
71
+ if (!is_dir($cache_dir = $self->cacheDir())) {
72
+ return $counter; // Nothing to do.
73
+ }
74
+ $show_on_front = get_option('show_on_front');
75
+ $page_for_posts = get_option('page_for_posts');
76
+
77
+ if (!in_array($show_on_front, array('posts', 'page'), true)) {
78
+ return $counter; // Nothing we can do in this case.
79
+ }
80
+ if ($show_on_front === 'page' && !$page_for_posts) {
81
+ return $counter; // Nothing we can do.
82
+ }
83
+ if ($show_on_front === 'posts') {
84
+ $posts_page = home_url('/');
85
+ } elseif ($show_on_front === 'page') {
86
+ $posts_page = get_permalink($page_for_posts);
87
+ }
88
+ if (empty($posts_page)) {
89
+ return $counter; // Nothing we can do.
90
+ }
91
+ $regex = $self->buildHostCachePathRegex($posts_page);
92
+ $counter += $self->clearFilesFromHostCacheDir($regex);
93
+
94
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
95
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
96
+ sprintf(__('<strong>%1$s:</strong> detected changes. Found %2$s in the cache for the designated "Posts Page"; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter))));
97
+ }
98
+ $counter += $self->autoClearXmlFeedsCache('blog');
99
+
100
+ return $counter;
101
+ };
src/includes/closures/Plugin/WcpJetpackUtils.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears all cache files for current blog when JetPack Custom CSS is saved.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @attaches-to `safecss_save_pre` hook.
10
+ *
11
+ * @param array $args Args passed in by hook.
12
+ */
13
+ $self->autoClearCacheOnJetpackCustomCss = function ($args) use ($self) {
14
+ $counter = 0; // Initialize.
15
+
16
+ if (!is_null($done = &$self->cacheKey('autoClearCacheOnJetpackCustomCss', $args))) {
17
+ return $counter; // Already did this.
18
+ }
19
+ $done = true; // Flag as having been done.
20
+
21
+ if (empty($args['is_preview']) && class_exists('\\Jetpack')) {
22
+ $counter += $self->autoClearCache();
23
+ }
24
+ };
src/includes/closures/Plugin/WcpOpcacheUtils.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Wipe (i.e., reset) OPCache.
6
+ *
7
+ * @since 151002 Adding OPCache support.
8
+ *
9
+ * @param bool $manually True if wiping is done manually.
10
+ * @param boolean $maybe Defaults to a true value.
11
+ * @param array $files Optional; wipe only specific files?
12
+ *
13
+ * @return integer Total keys wiped.
14
+ */
15
+ $self->wipeOpcache = function ($manually = false, $maybe = true, $files = array()) use ($self) {
16
+ $counter = 0; // Initialize counter.
17
+
18
+ if ($maybe && !$self->options['cache_clear_opcache_enable']) {
19
+ return $counter; // Not enabled at this time.
20
+ }
21
+ if (!$self->functionIsPossible('opcache_reset')) {
22
+ return $counter; // Not possible.
23
+ }
24
+ if (!($status = $self->sysOpcacheStatus())) {
25
+ return $counter; // Not possible.
26
+ }
27
+ if (empty($status->opcache_enabled)) {
28
+ return $counter; // Not necessary.
29
+ }
30
+ if (empty($status->opcache_statistics->num_cached_keys)) {
31
+ return $counter; // Not possible.
32
+ }
33
+ if ($files) { // Specific files?
34
+ foreach ($files as $_file) {
35
+ $counter += (int) opcache_invalidate($_file, true);
36
+ } // unset($_file); // Housekeeping.
37
+ } elseif (opcache_reset()) { // True if a reset occurs.
38
+ $counter += $status->opcache_statistics->num_cached_keys;
39
+ }
40
+ return $counter;
41
+ };
42
+
43
+ /*
44
+ * Clear (i.e., reset) OPCache.
45
+ *
46
+ * @since 151002 Adding OPCache support.
47
+ *
48
+ * @param bool $manually True if clearing is done manually.
49
+ * @param boolean $maybe Defaults to a true value.
50
+ *
51
+ * @return integer Total keys cleared.
52
+ */
53
+ $self->clearOpcache = function ($manually = false, $maybe = true) use ($self) {
54
+ if (!is_multisite() || is_main_site() || current_user_can($self->network_cap)) {
55
+ return $self->wipeOpcache($manually, $maybe);
56
+ }
57
+ return 0; // Not applicable.
58
+ };
59
+
60
+ /*
61
+ * Clear AC class file from Opcache (by force).
62
+ *
63
+ * @since 151215 Adding OPCache support.
64
+ *
65
+ * @return integer Total keys cleared.
66
+ */
67
+ $self->clearAcDropinFromOpcacheByForce = function () use ($self) {
68
+ return $self->wipeOpcache(false, false, array(WP_CONTENT_DIR.'/advanced-cache.php'));
69
+ };
src/includes/closures/Plugin/WcpPluginUtils.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically wipes/clears on plugin activation/deactivation.
6
+ *
7
+ * @since 151220 Adding auto-wipe|clear on plugin activations/deactivations.
8
+ *
9
+ * @attaches-to `activated_plugin` hook.
10
+ * @attaches-to `deactivated_plugin` hook.
11
+ *
12
+ * @param string $plugin Plugin basename.
13
+ * @param bool True if activating|deactivating network-wide. Defaults to boolean `FALSE` in case parameter is not passed to hook.
14
+ *
15
+ * @return int Total files wiped|cleared by this routine (if any).
16
+ */
17
+ $self->autoClearOnPluginActivationDeactivation = function ($plugin, $network_wide = false) use ($self) {
18
+ if (!$self->applyWpFilters(GLOBAL_NS.'_auto_clear_on_plugin_activation_deactivation', true)) {
19
+ return 0; // Nothing to do here.
20
+ }
21
+ return $self->{($network_wide ? 'autoWipeCache' : 'autoClearCache')}();
22
+ };
src/includes/closures/Plugin/WcpPostTypeUtils.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache files for a custom post type archive view.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param int $post_id A WordPress post ID.
10
+ *
11
+ * @throws \Exception If a clear failure occurs.
12
+ *
13
+ * @return int Total files cleared by this routine (if any).
14
+ *
15
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently
16
+ * attached to any hooks. However, it is called upon by {@link autoClearPostCache()}.
17
+ */
18
+ $self->autoClearCustomPostTypeArchiveCache = function ($post_id) use ($self) {
19
+ $counter = 0; // Initialize.
20
+
21
+ if (!($post_id = (integer) $post_id)) {
22
+ return $counter; // Nothing to do.
23
+ }
24
+ if (!is_null($done = &$self->cacheKey('autoClearCustomPostTypeArchiveCache', $post_id))) {
25
+ return $counter; // Already did this.
26
+ }
27
+ $done = true; // Flag as having been done.
28
+
29
+ if (!$self->options['enable']) {
30
+ return $counter; // Nothing to do.
31
+ }
32
+ if (!$self->options['cache_clear_custom_post_type_enable']) {
33
+ return $counter; // Nothing to do.
34
+ }
35
+ if (!is_dir($cache_dir = $self->cacheDir())) {
36
+ return $counter; // Nothing to do.
37
+ }
38
+ if (!($post_type = get_post_type($post_id))) {
39
+ return $counter; // Nothing to do.
40
+ }
41
+ if (!($all_custom_post_types = get_post_types(array('_builtin' => false)))) {
42
+ return $counter; // No custom post types.
43
+ }
44
+ if (!in_array($post_type, array_keys($all_custom_post_types), true)) {
45
+ return $counter; // This is NOT a custom post type.
46
+ }
47
+ if (!($custom_post_type = get_post_type_object($post_type))) {
48
+ return $counter; // Unable to retrieve post type.
49
+ }
50
+ if (empty($custom_post_type->labels->name) || !($custom_post_type_name = $custom_post_type->labels->name)) {
51
+ $custom_post_type_name = __('Untitled', 'comet-cache');
52
+ }
53
+ if (!($custom_post_type_archive_link = get_post_type_archive_link($post_type))) {
54
+ return $counter; // Nothing to do; no link to work from in this case.
55
+ }
56
+ $regex = $self->buildHostCachePathRegex($custom_post_type_archive_link);
57
+ $counter += $self->clearFilesFromHostCacheDir($regex);
58
+
59
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
60
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
61
+ sprintf(__('<strong>%1$s:</strong> detected changes. Found %2$s in the cache for Custom Post Type: <code>%3$s</code>; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)), esc_html($custom_post_type_name)));
62
+ }
63
+ $counter += $self->autoClearXmlFeedsCache('custom-post-type', $post_id);
64
+
65
+ return $counter;
66
+ };
src/includes/closures/Plugin/WcpPostUtils.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache files for a particular post.
6
+ *
7
+ * @attaches-to `save_post` hook.
8
+ * @attaches-to `delete_post` hook.
9
+ * @attaches-to `clean_post_cache` hook.
10
+ *
11
+ * @since 150422 Rewrite.
12
+ *
13
+ * @param int $post_id A WordPress post ID.
14
+ * @param bool $force Defaults to a `FALSE` value.
15
+ * Pass as TRUE if clearing should be done for `draft`, `pending`,
16
+ * `future`, or `trash` post statuses.
17
+ *
18
+ * @throws \Exception If a clear failure occurs.
19
+ *
20
+ * @return int Total files cleared by this routine (if any).
21
+ *
22
+ * @note This is also called upon by other routines which listen for
23
+ * events that are indirectly associated with a post ID.
24
+ */
25
+ $self->autoClearPostCache = function ($post_id, $force = false) use ($self) {
26
+ $counter = 0; // Initialize.
27
+
28
+ if (!is_null($allow = &$self->cacheKey('autoClearPostCache_allow'))) {
29
+ if ($allow === false) { // Disallow?
30
+ $allow = true; // Reset flag.
31
+ return $counter;
32
+ }
33
+ }
34
+ if (!($post_id = (integer) $post_id)) {
35
+ return $counter; // Nothing to do.
36
+ }
37
+ if (!is_null($done = &$self->cacheKey('autoClearPostCache', array($post_id, $force)))) {
38
+ return $counter; // Already did this.
39
+ }
40
+ $done = true; // Flag as having been done.
41
+
42
+ if (!$self->options['enable']) {
43
+ return $counter; // Nothing to do.
44
+ }
45
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
46
+ return $counter; // Nothing to do.
47
+ }
48
+ if (!is_dir($cache_dir = $self->cacheDir())) {
49
+ return $counter; // Nothing to do.
50
+ }
51
+ if (!($post_type = get_post_type($post_id))) {
52
+ return $counter; // Nothing to do.
53
+ }
54
+ $post_statuses = $self->postStatuses();
55
+ $unpublished_post_statuses = array_diff($post_statuses, array('publish'));
56
+ $is_bbpress_post_type = in_array($post_type, $self->bbPressPostTypes(), true);
57
+
58
+ if (!empty($self->pre_post_update_post_permalink[$post_id])) {
59
+ $permalink = $self->pre_post_update_post_permalink[$post_id];
60
+ $self->pre_post_update_post_permalink[$post_id] = ''; // Reset; only used for post status transitions.
61
+ } elseif (!($permalink = get_permalink($post_id))) {
62
+ return $counter; // Nothing we can do.
63
+ }
64
+ if (!($post_status = get_post_status($post_id))) {
65
+ return $counter; // Nothing to do.
66
+ }
67
+ if ($post_status === 'draft' && isset($GLOBALS['pagenow'], $_POST['publish'])
68
+ && is_admin() && $GLOBALS['pagenow'] === 'post.php' && current_user_can('publish_posts')
69
+ && strpos(wp_get_referer(), '/post-new.php') !== false
70
+ ) {
71
+ $post_status = 'publish'; // A new post being published now.
72
+ }
73
+ if (in_array($post_status, array('inherit', 'auto-draft'), true)) {
74
+ return $counter; // Nothing to do. Note: `inherit` = revision.
75
+ }
76
+ if (in_array($post_status, array('draft', 'pending', 'future', 'trash'), true) && !$force) {
77
+ return $counter; // Nothing to do; i.e., NOT forcing in this case.
78
+ }
79
+ if (($post_type_obj = get_post_type_object($post_type)) && !empty($post_type_obj->labels->singular_name)) {
80
+ $post_type_singular_name = $post_type_obj->labels->singular_name; // Singular name for the post type.
81
+ } else {
82
+ $post_type_singular_name = __('Post', 'comet-cache'); // Default value.
83
+ }
84
+ $regex = $self->buildHostCachePathRegex($permalink);
85
+ $counter += $self->clearFilesFromHostCacheDir($regex);
86
+
87
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
88
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
89
+ sprintf(__('<strong>%1$s:</strong> detected changes. Found %2$s in the cache for %3$s ID: <code>%4$s</code>; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)), esc_html($post_type_singular_name), esc_html($post_id)));
90
+ }
91
+ $counter += $self->autoClearXmlFeedsCache('blog');
92
+ $counter += $self->autoClearXmlFeedsCache('post-terms', $post_id);
93
+ $counter += $self->autoClearXmlFeedsCache('post-authors', $post_id);
94
+
95
+ $counter += $self->autoClearXmlSitemapsCache();
96
+ $counter += $self->autoClearHomePageCache();
97
+ $counter += $self->autoClearPostsPageCache();
98
+ $counter += $self->autoClearPostTermsCache($post_id, $force);
99
+ $counter += $self->autoClearCustomPostTypeArchiveCache($post_id);
100
+
101
+
102
+ if ($post_type !== 'page' && ($parent_post_id = wp_get_post_parent_id($post_id))) {
103
+ // Recursion: i.e., nested post types like bbPress forums/topic/replies.
104
+ $counter += $self->autoClearPostCache($parent_post_id, $force);
105
+ }
106
+ return $counter;
107
+ };
108
+ $self->auto_clear_post_cache = $self->autoClearPostCache; // Back compat.
109
+
110
+ /*
111
+ * Handles post status transitioning.
112
+ *
113
+ * @attaches-to `pre_post_update` hook.
114
+ *
115
+ * @since 150422 Rewrite.
116
+ *
117
+ * @param int $post_id Post ID.
118
+ * @param array $data Array of unslashed post data.
119
+ *
120
+ * @throws \Exception If a clear failure occurs.
121
+ *
122
+ * @return int Total files cleared by this routine (if any).
123
+ *
124
+ * @note This is also called upon by other routines which listen for
125
+ * events that are indirectly associated with a post ID.
126
+ */
127
+ $self->autoClearPostCacheTransition = function ($post_id, $data) use ($self) {
128
+ $counter = 0; // Initialize.
129
+
130
+ $old_status = (string) get_post_status($post_id);
131
+ $new_status = (string) $data['post_status'];
132
+ /*
133
+ * When a post has a status of `pending` or `draft`, the `get_permalink()` function
134
+ * does not return a friendly permalink and therefore `autoClearPostCache()` will
135
+ * have no way of building a path to the cache file that should be cleared as part of
136
+ * this post status transition. To get around this, we temporarily store the permalink
137
+ * in $self->pre_post_update_post_permalink for `autoClearPostCache()` to use.
138
+ *
139
+ * See also: <https://github.com/websharks/zencache/issues/441>
140
+ */
141
+ if (in_array($new_status, array('pending', 'draft'), true)) {
142
+ $self->pre_post_update_post_permalink[$post_id] = get_permalink($post_id);
143
+ }
144
+ // Begin post status transition sub-routine now.
145
+
146
+ if (!is_null($done = &$self->cacheKey('autoClearPostCacheTransition', array($old_status, $new_status, $post_id)))) {
147
+ return $counter; // Already did this.
148
+ }
149
+ $done = true; // Flag as having been done.
150
+
151
+ if (!$self->options['enable']) {
152
+ return $counter; // Nothing to do.
153
+ }
154
+ if ($new_status === $old_status) {
155
+ return $counter; // Nothing to do.
156
+ }
157
+ if (!($post_type = get_post_type($post_id))) {
158
+ return $counter; // Nothing to do.
159
+ }
160
+ $post_statuses = $self->postStatuses();
161
+ $unpublished_post_statuses = array_diff($post_statuses, array('publish'));
162
+ $is_bbpress_post_type = in_array($post_type, $self->bbPressPostTypes(), true);
163
+
164
+ if ($is_bbpress_post_type) {
165
+ if (in_array($old_status, array('publish', 'private', 'closed', 'spam', 'hidden'), true)) {
166
+ if (in_array($new_status, $unpublished_post_statuses, true)) {
167
+ $counter += $self->autoClearPostCache($post_id, true);
168
+ }
169
+ }
170
+ } elseif (in_array($old_status, array('publish', 'private'), true)) {
171
+ if (in_array($new_status, $unpublished_post_statuses, true)) {
172
+ $counter += $self->autoClearPostCache($post_id, true);
173
+ }
174
+ }
175
+ return $counter;
176
+ };
src/includes/closures/Plugin/WcpSettingUtils.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears all cache files for current blog under various conditions;
6
+ * used to check for conditions that don't have a hook that we can attach to.
7
+ *
8
+ * @since 150422 Rewrite.
9
+ *
10
+ * @attaches-to `admin_init` hook.
11
+ */
12
+ $self->autoClearCacheOnSettingChanges = function () use ($self) {
13
+ $counter = 0; // Initialize.
14
+ $pagenow = !empty($GLOBALS['pagenow']) ? $GLOBALS['pagenow'] : '';
15
+ $settings_updated = !empty($_REQUEST['settings-updated']);
16
+
17
+ if (!is_null($done = &$self->cacheKey('autoClearCacheOnSettingChanges', array($pagenow, $settings_updated)))) {
18
+ return $counter; // Already did this.
19
+ }
20
+ $done = true; // Flag as having been done.
21
+
22
+ if ($pagenow === 'options-general.php' && $settings_updated) {
23
+ $counter += $self->autoClearCache();
24
+ } elseif ($pagenow === 'options-reading.php' && $settings_updated) {
25
+ $counter += $self->autoClearCache();
26
+ } elseif ($pagenow === 'options-discussion.php' && $settings_updated) {
27
+ $counter += $self->autoClearCache();
28
+ } elseif ($pagenow === 'options-permalink.php' && $settings_updated) {
29
+ $counter += $self->autoClearCache();
30
+ }
31
+ };
src/includes/closures/Plugin/WcpSitemapUtils.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache files related to XML sitemaps.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @throws \Exception If a clear failure occurs.
10
+ *
11
+ * @return int Total files cleared by this routine (if any).
12
+ *
13
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently
14
+ * attached to any hooks. However, it is called upon by {@link autoClearPostCache()}.
15
+ */
16
+ $self->autoClearXmlSitemapsCache = function () use ($self) {
17
+ $counter = 0; // Initialize.
18
+
19
+ if (!is_null($done = &$self->cacheKey('autoClearXmlSitemapsCache'))) {
20
+ return $counter; // Already did this.
21
+ }
22
+ $done = true; // Flag as having been done.
23
+
24
+ if (!$self->options['enable']) {
25
+ return $counter; // Nothing to do.
26
+ }
27
+ if (!$self->options['cache_clear_xml_sitemaps_enable']) {
28
+ return $counter; // Nothing to do.
29
+ }
30
+ if (!$self->options['cache_clear_xml_sitemap_patterns']) {
31
+ return $counter; // Nothing to do.
32
+ }
33
+ if (!is_dir($cache_dir = $self->cacheDir())) {
34
+ return $counter; // Nothing to do.
35
+ }
36
+ if (!($regex_frags = $self->buildHostCachePathRegexFragsFromWcUris($self->options['cache_clear_xml_sitemap_patterns'], ''))) {
37
+ return $counter; // There are no patterns to look for.
38
+ }
39
+ $regex = $self->buildHostCachePathRegex('', '\/'.$regex_frags.'\.');
40
+ $counter += $self->clearFilesFromHostCacheDir($regex);
41
+
42
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
43
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
44
+ sprintf(__('<strong>%1$s:</strong> detected changes. Found %2$s in the cache for XML sitemaps; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter))));
45
+ }
46
+ return $counter;
47
+ };
src/includes/closures/Plugin/WcpTermUtils.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache files for terms associated with a post.
6
+ *
7
+ * @attaches-to `added_term_relationship` hook.
8
+ * @attaches-to `delete_term_relationships` hook.
9
+ *
10
+ * @since 150422 Rewrite.
11
+ *
12
+ * @param int $post_id A WordPress post ID.
13
+ * @param bool $force Defaults to a `FALSE` value.
14
+ * Pass as TRUE if clearing should be done for `draft`, `pending`,
15
+ * or `future` post statuses.
16
+ *
17
+ * @throws \Exception If a clear failure occurs.
18
+ *
19
+ * @return int Total files cleared by this routine (if any).
20
+ *
21
+ * @note In addition to the hooks this is attached to, it is also
22
+ * called upon by {@link autoClearPostCache()}.
23
+ */
24
+ $self->autoClearPostTermsCache = function ($post_id, $force = false) use ($self) {
25
+ $counter = 0; // Initialize.
26
+ $enqueued_notices = 0; // Initialize.
27
+
28
+ if (!($post_id = (integer) $post_id)) {
29
+ return $counter; // Nothing to do.
30
+ }
31
+ if (!is_null($done = &$self->cacheKey('autoClearPostTermsCache', array($post_id, $force)))) {
32
+ return $counter; // Already did this.
33
+ }
34
+ $done = true; // Flag as having been done.
35
+
36
+ if (!$self->options['enable']) {
37
+ return $counter; // Nothing to do.
38
+ }
39
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
40
+ return $counter; // Nothing to do.
41
+ }
42
+ if (!$self->options['cache_clear_term_category_enable'] && !$self->options['cache_clear_term_post_tag_enable'] && !$self->options['cache_clear_term_other_enable']) {
43
+ return $counter; // Nothing to do.
44
+ }
45
+ if (!is_dir($cache_dir = $self->cacheDir())) {
46
+ return $counter; // Nothing to do.
47
+ }
48
+ $post_status = get_post_status($post_id); // Cache this.
49
+
50
+ if ($post_status === 'draft' && isset($GLOBALS['pagenow'], $_POST['publish'])
51
+ && is_admin() && $GLOBALS['pagenow'] === 'post.php' && current_user_can('publish_posts')
52
+ && strpos(wp_get_referer(), '/post-new.php') !== false
53
+ ) {
54
+ $post_status = 'publish'; // A new post being published now.
55
+ }
56
+ if (in_array($post_status, array('inherit', 'auto-draft'), true)) {
57
+ return $counter; // Nothing to do. Note: `inherit` = revision.
58
+ }
59
+ if (in_array($post_status, array('draft', 'pending', 'future'), true) && !$force) {
60
+ return $counter; // Nothing to do; i.e., NOT forcing in this case.
61
+ }
62
+ /*
63
+ * Build an array of available taxonomies for this post (as taxonomy objects).
64
+ */
65
+ $taxonomies = get_object_taxonomies(get_post($post_id), 'objects');
66
+
67
+ if (!is_array($taxonomies)) {
68
+ return $counter; // Nothing to do.
69
+ }
70
+ /*
71
+ * Build an array of terms associated with this post for each taxonomy.
72
+ * Also save taxonomy label information for Dashboard messaging later.
73
+ */
74
+ $terms = array();
75
+ $taxonomy_labels = array();
76
+
77
+ foreach ($taxonomies as $_taxonomy) {
78
+ if (// Check if this is a taxonomy/term that we should clear.
79
+ ($_taxonomy->name === 'category' && !$self->options['cache_clear_term_category_enable'])
80
+ || ($_taxonomy->name === 'post_tag' && !$self->options['cache_clear_term_post_tag_enable'])
81
+ || ($_taxonomy->name !== 'category' && $_taxonomy->name !== 'post_tag' && !$self->options['cache_clear_term_other_enable'])
82
+ ) {
83
+ continue; // Continue; nothing to do for this taxonomy.
84
+ }
85
+ if (is_array($_terms = wp_get_post_terms($post_id, $_taxonomy->name))) {
86
+ $terms = array_merge($terms, $_terms);
87
+ if (empty($_taxonomy->labels->singular_name) || $_taxonomy->labels->singular_name === '') {
88
+ $taxonomy_labels[$_taxonomy->name] = $_taxonomy->name;
89
+ } else {
90
+ $taxonomy_labels[$_taxonomy->name] = $_taxonomy->labels->singular_name;
91
+ }
92
+ }
93
+ }
94
+ unset($_taxonomy, $_terms);
95
+
96
+ if (empty($terms)) {
97
+ return $counter; // Nothing to do.
98
+ }
99
+ /*
100
+ * Build an array of terms with term names,
101
+ * permalinks, and associated taxonomy labels.
102
+ */
103
+ $terms_to_clear = array();
104
+ $_i = 0;
105
+
106
+ foreach ($terms as $_term) {
107
+ if (($_link = get_term_link($_term))) {
108
+ $terms_to_clear[$_i]['permalink'] = $_link;
109
+ $terms_to_clear[$_i]['term_name'] = $_term->name;
110
+ if (!empty($taxonomy_labels[$_term->taxonomy])) {
111
+ $terms_to_clear[$_i]['taxonomy_label'] = $taxonomy_labels[$_term->taxonomy];
112
+ } else {
113
+ $terms_to_clear[$_i]['taxonomy_label'] = $_term->taxonomy;
114
+ }
115
+ }
116
+ ++$_i; // Array index counter.
117
+ }
118
+ unset($_term, $_link, $_i);
119
+
120
+ if (empty($terms_to_clear)) {
121
+ return $counter; // Nothing to do.
122
+ }
123
+ foreach ($terms_to_clear as $_term) {
124
+ $_term_regex = $self->buildHostCachePathRegex($_term['permalink']);
125
+ $_term_counter = $self->clearFilesFromHostCacheDir($_term_regex);
126
+ $counter += $_term_counter; // Add to overall counter.
127
+
128
+ if ($_term_counter && $enqueued_notices < 100 && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
129
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
130
+ sprintf(__('<strong>%1$s:</strong> detected changes. Found %2$s in the cache for %3$s: <code>%4$s</code>; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($_term_counter)), esc_html($_term['taxonomy_label']), esc_html($_term['term_name'])));
131
+ ++$enqueued_notices; // Increment enqueued notices counter.
132
+ }
133
+ }
134
+ unset($_term, $_term_regex, $_term_counter); // Housekeeping.
135
+
136
+ $counter += $self->autoClearXmlFeedsCache('post-terms', $post_id);
137
+
138
+ return $counter;
139
+ };
src/includes/closures/Plugin/WcpUpdaterUtils.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears all cache files for current blog when WordPress core, or an active component, is upgraded.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @attaches-to `upgrader_process_complete` hook.
10
+ *
11
+ * @param \WP_Upgrader $upgrader_instance An instance of \WP_Upgrader.
12
+ * Or, any class that extends \WP_Upgrader.
13
+ * @param array $data Array of bulk item update data.
14
+ *
15
+ * This array may include one or more of the following keys:
16
+ *
17
+ * - `string` `$action` Type of action. Default 'update'.
18
+ * - `string` `$type` Type of update process; e.g. 'plugin', 'theme', 'core'.
19
+ * - `boolean` `$bulk` Whether the update process is a bulk update. Default true.
20
+ * - `array` `$packages` Array of plugin, theme, or core packages to update.
21
+ */
22
+ $self->autoClearOnUpgraderProcessComplete = function (\WP_Upgrader $upgrader_instance, array $data) use ($self) {
23
+ $counter = 0; // Initialize.
24
+
25
+ switch (!empty($data['type']) ? $data['type'] : '') {
26
+ case 'plugin': // Plugin upgrade.
27
+
28
+ $multi_plugin_update = $single_plugin_update = false;
29
+ $upgrading_active_plugin = false; // Initialize.
30
+
31
+ if (!empty($data['bulk']) && !empty($data['plugins']) && is_array($data['plugins'])) {
32
+ $multi_plugin_update = true;
33
+ } elseif (!empty($data['plugin']) && is_string($data['plugin'])) {
34
+ $single_plugin_update = true;
35
+ }
36
+ if ($multi_plugin_update) {
37
+ foreach ($data['plugins'] as $_plugin) {
38
+ if ($_plugin && is_string($_plugin) && is_plugin_active($_plugin)) {
39
+ $upgrading_active_plugin = true;
40
+ break; // Got what we need here.
41
+ }
42
+ }
43
+ unset($_plugin); // Housekeeping.
44
+ } elseif ($single_plugin_update && is_plugin_active($data['plugin'])) {
45
+ $upgrading_active_plugin = true;
46
+ }
47
+ if ($upgrading_active_plugin) {
48
+ $counter += $self->autoClearCache();
49
+ }
50
+ break; // Break switch.
51
+
52
+ case 'theme': // Theme upgrade.
53
+
54
+ $current_active_theme = wp_get_theme();
55
+ $current_active_theme_parent = $current_active_theme->parent();
56
+ $multi_theme_update = $single_theme_update = false;
57
+ $upgrading_active_parent_theme = $upgrading_active_theme = false;
58
+
59
+ if (!empty($data['bulk']) && !empty($data['themes']) && is_array($data['themes'])) {
60
+ $multi_theme_update = true;
61
+ } elseif (!empty($data['theme']) && is_string($data['theme'])) {
62
+ $single_theme_update = true;
63
+ }
64
+ if ($multi_theme_update) {
65
+ foreach ($data['themes'] as $_theme) {
66
+ if (!$_theme || !is_string($_theme) || !($_theme_obj = wp_get_theme($_theme))) {
67
+ continue; // Unable to acquire theme object instance.
68
+ }
69
+ if ($current_active_theme_parent && $current_active_theme_parent->get_stylesheet() === $_theme_obj->get_stylesheet()) {
70
+ $upgrading_active_parent_theme = true;
71
+ break; // Got what we needed here.
72
+ } elseif ($current_active_theme->get_stylesheet() === $_theme_obj->get_stylesheet()) {
73
+ $upgrading_active_theme = true;
74
+ break; // Got what we needed here.
75
+ }
76
+ }
77
+ unset($_theme, $_theme_obj); // Housekeeping.
78
+ } elseif ($single_theme_update && ($_theme_obj = wp_get_theme($data['theme']))) {
79
+ if ($current_active_theme_parent && $current_active_theme_parent->get_stylesheet() === $_theme_obj->get_stylesheet()) {
80
+ $upgrading_active_parent_theme = true;
81
+ } elseif ($current_active_theme->get_stylesheet() === $_theme_obj->get_stylesheet()) {
82
+ $upgrading_active_theme = true;
83
+ }
84
+ }
85
+ unset($_theme_obj); // Housekeeping.
86
+
87
+ if ($upgrading_active_theme || $upgrading_active_parent_theme) {
88
+ $counter += $self->autoClearCache();
89
+ }
90
+ break; // Break switch.
91
+
92
+ case 'core': // Core upgrade.
93
+ default: // Or any other sort of upgrade.
94
+
95
+ $counter += $self->autoClearCache();
96
+
97
+ break; // Break switch.
98
+ }
99
+ };
src/includes/closures/Plugin/WcpUtils.php ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Used for temporarily storing the permalink for posts transitioning from
6
+ * `publish` or `private` post status to `pending` or `draft` post status.
7
+ *
8
+ * @since 150422 Rewrite.
9
+ *
10
+ * @type array An associative array with the Post ID as the named key containing
11
+ * the post permalink before the post has been transitioned.
12
+ */
13
+ $self->pre_post_update_post_permalink = array();
14
+
15
+ /*
16
+ * Wipes out all cache files.
17
+ *
18
+ * @since 150422 Rewrite.
19
+ *
20
+ * @param bool $manually TRUE if wiping is done manually.
21
+ *
22
+ * @throws \Exception If a wipe failure occurs.
23
+ *
24
+ * @return int Total files wiped by this routine.
25
+ */
26
+ $self->wipeCache = function ($manually = false) use ($self) {
27
+ $counter = 0; // Initialize.
28
+
29
+ if (!$manually && $self->disableAutoWipeCacheRoutines()) {
30
+ return $counter; // Nothing to do.
31
+ }
32
+ @set_time_limit(1800); // @TODO Display a warning.
33
+
34
+ if (is_dir($cache_dir = $self->cacheDir())) {
35
+ $regex = $self->assembleCachePathRegex('', '.+');
36
+ $counter += $self->wipeFilesFromCacheDir($regex);
37
+ }
38
+
39
+
40
+
41
+
42
+
43
+ return $counter;
44
+ };
45
+ $self->wipe_cache = $self->wipeCache; // Back compat.
46
+
47
+ /*
48
+ * Clears cache files (current blog).
49
+ *
50
+ * @since 150422 Rewrite.
51
+ *
52
+ * @param bool $manually TRUE if clearing is done manually.
53
+ *
54
+ * @throws \Exception If a clearing failure occurs.
55
+ *
56
+ * @return int Total files cleared by this routine.
57
+ */
58
+ $self->clearCache = function ($manually = false) use ($self) {
59
+ $counter = 0; // Initialize.
60
+
61
+ if (!$manually && $self->disableAutoClearCacheRoutines()) {
62
+ return $counter; // Nothing to do.
63
+ }
64
+ @set_time_limit(1800); // @TODO Display a warning.
65
+
66
+ if (is_dir($cache_dir = $self->cacheDir())) {
67
+ $regex = $self->buildHostCachePathRegex('', '.+');
68
+ $counter += $self->clearFilesFromHostCacheDir($regex);
69
+ }
70
+
71
+
72
+
73
+
74
+
75
+ return $counter;
76
+ };
77
+ $self->clear_cache = $self->clearCache; // Back compat.
78
+
79
+ /*
80
+ * Purges expired cache files (current blog).
81
+ *
82
+ * @since 150422 Rewrite.
83
+ *
84
+ * @param bool $manually TRUE if purging is done manually.
85
+ *
86
+ * @throws \Exception If a purge failure occurs.
87
+ *
88
+ * @return int Total files purged by this routine.
89
+ */
90
+ $self->purgeCache = function ($manually = false) use ($self) {
91
+ $counter = 0; // Initialize.
92
+
93
+ if (!$manually && $self->disableAutoPurgeCacheRoutines()) {
94
+ return $counter; // Nothing to do.
95
+ }
96
+ @set_time_limit(1800); // @TODO Display a warning.
97
+
98
+ if (is_dir($cache_dir = $self->cacheDir())) {
99
+ $regex = $self->buildHostCachePathRegex('', '.+');
100
+ $counter += $self->purgeFilesFromHostCacheDir($regex);
101
+ }
102
+
103
+ return $counter;
104
+ };
105
+ $self->purge_cache = $self->purgeCache; // Back compat.
106
+
107
+ /*
108
+ * Wurges (purges) all expired cache files; like wipe, but expired files only.
109
+ *
110
+ * @since 151002 Look at entire cache directory.
111
+ *
112
+ * @param bool $manually TRUE if wurging is done manually.
113
+ *
114
+ * @throws \Exception If a wurge failure occurs.
115
+ *
116
+ * @return int Total files wurged by this routine.
117
+ */
118
+ $self->wurgeCache = function ($manually = false) use ($self) {
119
+ $counter = 0; // Initialize.
120
+
121
+ if (!$manually && $self->disableAutoPurgeCacheRoutines()) {
122
+ return $counter; // Nothing to do.
123
+ }
124
+ @set_time_limit(1800); // @TODO Display a warning.
125
+
126
+ if (is_dir($cache_dir = $self->cacheDir())) {
127
+ $regex = $self->assembleCachePathRegex('', '.+');
128
+ $counter += $self->wurgeFilesFromCacheDir($regex);
129
+ }
130
+
131
+ return $counter;
132
+ };
133
+
134
+ /*
135
+ * Automatically wipes out all cache files.
136
+ *
137
+ * @attaches-to Nothing at this time.
138
+ *
139
+ * @since 150422 Rewrite.
140
+ *
141
+ * @return int Total files wiped by this routine (if any).
142
+ *
143
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks.
144
+ * This is called upon whenever options are saved and/or restored though.
145
+ */
146
+ $self->autoWipeCache = function () use ($self) {
147
+ $counter = 0; // Initialize.
148
+
149
+ if (!is_null($done = &$self->cacheKey('autoWipeCache'))) {
150
+ return $counter; // Already did this.
151
+ }
152
+ $done = true; // Flag as having been done.
153
+
154
+ if (!$self->options['enable']) {
155
+ return $counter; // Nothing to do.
156
+ }
157
+ if ($self->disableAutoWipeCacheRoutines()) {
158
+ return $counter; // Nothing to do.
159
+ }
160
+ $counter += $self->wipeCache();
161
+
162
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
163
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/wipe.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
164
+ sprintf(__('<strong>%1$s:</strong> detected significant changes. Found %2$s in the cache; auto-wiping.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter))));
165
+ }
166
+ return $counter;
167
+ };
168
+
169
+ /*
170
+ * Automatically clears all cache files (current host).
171
+ *
172
+ * @attaches-to `switch_theme` hook.
173
+ *
174
+ * @attaches-to `wp_create_nav_menu` hook.
175
+ * @attaches-to `wp_update_nav_menu` hook.
176
+ * @attaches-to `wp_delete_nav_menu` hook.
177
+ *
178
+ * @attaches-to `create_term` hook.
179
+ * @attaches-to `edit_terms` hook.
180
+ * @attaches-to `delete_term` hook.
181
+ *
182
+ * @attaches-to `add_link` hook.
183
+ * @attaches-to `edit_link` hook.
184
+ * @attaches-to `delete_link` hook.
185
+ *
186
+ * @since 150422 Rewrite.
187
+ *
188
+ * @return int Total files cleared by this routine (if any).
189
+ *
190
+ * @note This is also called upon during plugin activation.
191
+ */
192
+ $self->autoClearCache = function () use ($self) {
193
+ $counter = 0; // Initialize.
194
+
195
+ if (!is_null($done = &$self->cacheKey('autoClearCache'))) {
196
+ return $counter; // Already did this.
197
+ }
198
+ $done = true; // Flag as having been done.
199
+
200
+ if (!$self->options['enable']) {
201
+ return $counter; // Nothing to do.
202
+ }
203
+ if ($self->disableAutoClearCacheRoutines()) {
204
+ return $counter; // Nothing to do.
205
+ }
206
+ $counter += $self->clearCache();
207
+
208
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
209
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
210
+ sprintf(__('<strong>%1$s:</strong> detected important site changes. Found %2$s in the cache for this site; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter))));
211
+ }
212
+ return $counter;
213
+ };
214
+
215
+ /*
216
+ * Automatically purges all cache files (current host).
217
+ *
218
+ * @attaches-to Nothing at this time.
219
+ *
220
+ * @since 151002 While working on directory stats.
221
+ *
222
+ * @return int Total files purged by this routine.
223
+ *
224
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks.
225
+ */
226
+ $self->autoPurgeCache = function () use ($self) {
227
+ $counter = 0; // Initialize.
228
+
229
+ if (!is_null($done = &$self->cacheKey('autoPurgeCache'))) {
230
+ return $counter; // Already did this.
231
+ }
232
+ $done = true; // Flag as having been done.
233
+
234
+ if (!$self->options['enable']) {
235
+ return $counter; // Nothing to do.
236
+ }
237
+ if ($self->disableAutoPurgeCacheRoutines()) {
238
+ return $counter; // Nothing to do.
239
+ }
240
+ $counter += $self->purgeCache();
241
+
242
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
243
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
244
+ sprintf(__('<strong>%1$s:</strong> detected important site changes. Found %2$s in the cache for this site that were expired; auto-purging.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter))));
245
+ }
246
+ return $counter;
247
+ };
248
+
249
+ /*
250
+ * Automatically wurges all cache files.
251
+ *
252
+ * @attaches-to Nothing at this time.
253
+ *
254
+ * @since 151002 While working on directory stats.
255
+ *
256
+ * @return int Total files wurged by this routine.
257
+ *
258
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks.
259
+ */
260
+ $self->autoWurgeCache = function () use ($self) {
261
+ $counter = 0; // Initialize.
262
+
263
+ if (!is_null($done = &$self->cacheKey('autoWurgeCache'))) {
264
+ return $counter; // Already did this.
265
+ }
266
+ $done = true; // Flag as having been done.
267
+
268
+ if (!$self->options['enable']) {
269
+ return $counter; // Nothing to do.
270
+ }
271
+ if ($self->disableAutoPurgeCacheRoutines()) {
272
+ return $counter; // Nothing to do.
273
+ }
274
+ $counter += $self->wurgeCache();
275
+
276
+ if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
277
+ $self->enqueueNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
278
+ sprintf(__('<strong>%1$s:</strong> detected important site changes. Found %2$s in the cache that were expired; auto-purging.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter))));
279
+ }
280
+ return $counter;
281
+ };
282
+
283
+ /*
284
+ * Allows a site owner to disable the automatic cache wiping routines.
285
+ *
286
+ * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_wipe_cache_routines` to return TRUE,
287
+ * in which case this method returns TRUE, otherwise it returns FALSE.
288
+ *
289
+ * @since 150422 Rewrite.
290
+ *
291
+ * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases.
292
+ */
293
+ $self->disableAutoWipeCacheRoutines = function () use ($self) {
294
+ $is_disabled = (boolean) $self->applyWpFilters(GLOBAL_NS.'_disable_auto_wipe_cache_routines', false);
295
+
296
+ if ($is_disabled && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
297
+ $self->enqueueMainNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
298
+ sprintf(__('<strong>%1$s:</strong> detected significant changes that would normally trigger cache wiping routines. However, cache wiping routines have been disabled by a site administrator. [<a href="http://cometcache.com/r/kb-clear-and-wipe-cache-routines/" target="_blank">?</a>]', 'comet-cache'), esc_html(NAME)));
299
+ }
300
+ return $is_disabled;
301
+ };
302
+
303
+ /*
304
+ * Allows a site owner to disable the automatic cache clearing routines.
305
+ *
306
+ * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_clear_cache_routines` to return TRUE,
307
+ * in which case this method returns TRUE, otherwise it returns FALSE.
308
+ *
309
+ * @since 150422 Rewrite.
310
+ *
311
+ * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases.
312
+ */
313
+ $self->disableAutoClearCacheRoutines = function () use ($self) {
314
+ $is_disabled = (boolean) $self->applyWpFilters(GLOBAL_NS.'_disable_auto_clear_cache_routines', false);
315
+
316
+ if ($is_disabled && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
317
+ $self->enqueueMainNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
318
+ sprintf(__('<strong>%1$s:</strong> detected important site changes that would normally trigger cache clearing routines. However, cache clearing routines have been disabled by a site administrator. [<a href="http://cometcache.com/r/kb-clear-and-wipe-cache-routines/" target="_blank">?</a>]', 'comet-cache'), esc_html(NAME)));
319
+ }
320
+ return $is_disabled;
321
+ };
322
+
323
+ /*
324
+ * Allows a site owner to disable the automatic cache purging routines.
325
+ *
326
+ * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_purge_cache_routines` to return TRUE,
327
+ * in which case this method returns TRUE, otherwise it returns FALSE.
328
+ *
329
+ * @since 151002 While working on directory stats.
330
+ *
331
+ * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases.
332
+ */
333
+ $self->disableAutoPurgeCacheRoutines = function () use ($self) {
334
+ $is_disabled = (boolean) $self->applyWpFilters(GLOBAL_NS.'_disable_auto_purge_cache_routines', false);
335
+
336
+ if ($is_disabled && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) {
337
+ $self->enqueueMainNotice('<img src="'.esc_attr($self->url('/src/client-s/images/clear.png')).'" style="float:left; margin:0 10px 0 0; border:0;" />'.
338
+ sprintf(__('<strong>%1$s:</strong> detected important site changes that would normally trigger cache purging routines. However, cache purging routines have been disabled by a site administrator. [<a href="http://cometcache.com/r/kb-clear-and-wipe-cache-routines/" target="_blank">?</a>]', 'comet-cache'), esc_html(NAME)));
339
+ }
340
+ return $is_disabled;
341
+ };
src/includes/closures/Plugin/WcpWooCommerceUtils.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Automatically clears cache file for a WooCommerce Product when its stock is changed.
6
+ *
7
+ * @since 151220 Improving WooCommerce Compatibility.
8
+ *
9
+ * @attaches-to `woocommerce_product_set_stock` hook.
10
+ *
11
+ * @param \WC_Product $product A WooCommerce WC_Product object
12
+ */
13
+ $self->autoClearPostCacheOnWooCommerceSetStock = function ($product) use ($self) {
14
+ $counter = 0; // Initialize.
15
+
16
+ if (!is_null($done = &$self->cacheKey('autoClearPostCacheOnWooCommerceSetStock'))) {
17
+ return $counter; // Already did this.
18
+ }
19
+ $done = true; // Flag as having been done.
20
+
21
+ if(class_exists('\\WooCommerce')) {
22
+ $counter += $self->autoClearPostCache($product->id);
23
+ }
24
+ };
src/includes/closures/Shared/BlogUtils.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Get blog details.
6
+ *
7
+ * @since 150821 Improving multisite compat.
8
+ *
9
+ * @param integer $blog_id For which blog ID?
10
+ *
11
+ * @return \stdClass|null Blog details if possible.
12
+ *
13
+ * @note The return value of this function is NOT cached in support of `switch_to_blog()`.
14
+ */
15
+ $self->blogDetails = function ($blog_id = 0) use ($self) {
16
+ if (!is_multisite() || $self->isAdvancedCache()) {
17
+ return null; // Not possible.
18
+ }
19
+ if (($blog_id = (integer) $blog_id) < 0) {
20
+ $blog_id = (integer) get_current_site()->blog_id;
21
+ }
22
+ if (!$blog_id) {
23
+ $blog_id = (integer) get_current_blog_id();
24
+ }
25
+ if (!$blog_id || $blog_id < 0) {
26
+ return null; // Not possible.
27
+ }
28
+ $details = get_blog_details($blog_id);
29
+
30
+ return is_object($details) ? $details : null;
31
+ };
src/includes/closures/Shared/CacheDirUtils.php ADDED
@@ -0,0 +1,643 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Cache directory path.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param string $rel_path Relative path inside cache directory.
10
+ *
11
+ * @throws \Exception If unable to get cache directory.
12
+ *
13
+ * @return string Absolute path to cache directory.
14
+ */
15
+ $self->cacheDir = function ($rel_path = '') use ($self) {
16
+ $rel_path = (string) $rel_path;
17
+
18
+ if ($self->isAdvancedCache()) {
19
+ $cache_dir = defined('COMET_CACHE_DIR') ? COMET_CACHE_DIR : '';
20
+ } elseif (!empty($self->cache_sub_dir)) {
21
+ $cache_dir = $self->wpContentBaseDirTo($self->cache_sub_dir);
22
+ }
23
+ if (empty($cache_dir)) {
24
+ throw new \Exception(__('Unable to determine cache directory location.', 'comet-cache'));
25
+ }
26
+ return rtrim($cache_dir, '/').($rel_path ? '/'.ltrim($rel_path) : '');
27
+ };
28
+
29
+ /*
30
+ * Wipe files from the cache directory (for all hosts/blogs);
31
+ * i.e., those that match a specific regex pattern.
32
+ *
33
+ * @since 151002 While working on directory stats.
34
+ *
35
+ * @param string $regex A regex pattern; see {@link deleteFilesFromCacheDir()}.
36
+ *
37
+ * @return integer Total files wiped by this routine.
38
+ */
39
+ $self->wipeFilesFromCacheDir = function ($regex) use ($self) {
40
+ return $self->deleteFilesFromCacheDir($regex);
41
+ };
42
+
43
+ /*
44
+ * Clear files from the cache directory (for the current host);
45
+ * i.e., those that match a specific regex pattern.
46
+ *
47
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
48
+ *
49
+ * @param string $regex A regex pattern; see {@link deleteFilesFromHostCacheDir()}.
50
+ *
51
+ * @return integer Total files cleared by this routine (if any).
52
+ */
53
+ $self->clearFilesFromHostCacheDir = function ($regex) use ($self) {
54
+ return $self->deleteFilesFromHostCacheDir($regex);
55
+ };
56
+
57
+ /*
58
+ * Wurge (purge) files from the cache directory (for all hosts/blogs);
59
+ * i.e., those that match a specific regex pattern.
60
+ *
61
+ * @since 151002 While working on directory stats.
62
+ *
63
+ * @param string $regex A regex pattern; see {@link deleteFilesFromCacheDir()}.
64
+ *
65
+ * @return integer Total files wurged by this routine.
66
+ */
67
+ $self->wurgeFilesFromCacheDir = function ($regex) use ($self) {
68
+ return $self->deleteFilesFromCacheDir($regex, true);
69
+ };
70
+
71
+ /*
72
+ * Purge files from the cache directory (for the current host);
73
+ * i.e., those that match a specific regex pattern.
74
+ *
75
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
76
+ *
77
+ * @param string $regex A regex pattern; see {@link deleteFilesFromHostCacheDir()}.
78
+ *
79
+ * @return integer Total files purged by this routine (if any).
80
+ */
81
+ $self->purgeFilesFromHostCacheDir = function ($regex) use ($self) {
82
+ return $self->deleteFilesFromHostCacheDir($regex, true);
83
+ };
84
+
85
+ /*
86
+ * Delete files from the cache directory (for all hosts/blogs);
87
+ * i.e., those that match a specific regex pattern.
88
+ *
89
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
90
+ *
91
+ * @param string $regex A `/[regex pattern]/`; relative to the cache directory.
92
+ * e.g. `/^http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/`
93
+ *
94
+ * Or, this can also be a full/absolute regex pattern against an absolute path;
95
+ * provided that it always starts with `/^`; including the full absolute cache/host directory path.
96
+ * e.g. `/^\/cache\/dir\/http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/`
97
+ *
98
+ * @param boolean $check_max_age Check max age? i.e., use purge behavior?
99
+ *
100
+ * @return integer Total files deleted by this routine (if any).
101
+ *
102
+ * @throws \Exception If unable to delete a file for any reason.
103
+ *
104
+ * @TODO Optimize this for multisite networks w/ a LOT of child blogs.
105
+ * @TODO Optimize this for extremely large sites. A LOT of files here could slow things down.
106
+ * This class member is currently used in wiping and purging for a network. So there is the potential for a LOT of files in a single scan.
107
+ * See also: <https://codex.wordpress.org/Function_Reference/wp_is_large_network>
108
+ */
109
+ $self->deleteFilesFromCacheDir = function ($regex, $check_max_age = false) use ($self) {
110
+ $counter = 0; // Initialize.
111
+
112
+ if (!($regex = (string) $regex)) {
113
+ return $counter; // Nothing to do.
114
+ }
115
+ if (!is_dir($cache_dir = $self->cacheDir())) {
116
+ return $counter; // Nothing to do.
117
+ }
118
+ $cache_dir = $self->nDirSeps($cache_dir);
119
+
120
+ if ($check_max_age && $self->isAdvancedCache()) {
121
+ throw new \Exception(__('Invalid argument; isAdvancedCache!', 'comet-cache'));
122
+ }
123
+ if ($check_max_age && !($max_age = strtotime('-'.$self->options['cache_max_age']))) {
124
+ return $counter; // Invalid cache expiration time.
125
+ }
126
+ /* ------- Begin lock state... ----------- */
127
+
128
+ $cache_lock = $self->cacheLock(); // Lock cache writes.
129
+
130
+ clearstatcache(); // Clear stat cache to be sure we have a fresh start below.
131
+
132
+ $cache_dir_tmp = $self->addTmpSuffix($cache_dir); // Temporary directory.
133
+
134
+ $cache_dir_tmp_regex = $regex; // Initialize host-specific regex pattern for the tmp directory.
135
+ $cache_dir_tmp_regex = '\\/'.ltrim($cache_dir_tmp_regex, '^\\/'); // Make sure it begins with an escaped `/`.
136
+ $cache_dir_tmp_regex = $self->strIreplaceOnce(preg_quote($cache_dir.'/', '/'), '', $cache_dir_tmp_regex);
137
+
138
+ $cache_dir_tmp_regex = ltrim($cache_dir_tmp_regex, '^\\/');
139
+ if (strpos($cache_dir_tmp_regex, '(?:\/') === 0 || strpos($cache_dir_tmp_regex, '(\/') === 0) {
140
+ $cache_dir_tmp_regex = '/^'.preg_quote($cache_dir_tmp, '/').$cache_dir_tmp_regex;
141
+ } else {
142
+ $cache_dir_tmp_regex = '/^'.preg_quote($cache_dir_tmp.'/', '/').$cache_dir_tmp_regex;
143
+ }
144
+ # if(WP_DEBUG) file_put_contents(WP_CONTENT_DIR.'/'.strtolower(SHORT_NAME).'-debug.log', print_r($regex, TRUE)."\n".print_r($cache_dir_tmp_regex, TRUE)."\n\n", FILE_APPEND);
145
+ // Uncomment the above line to debug regex pattern matching used by this routine; and others that call upon it.
146
+
147
+ if (!rename($cache_dir, $cache_dir_tmp)) {
148
+ throw new \Exception(sprintf(__('Unable to delete files. Rename failure on directory: `%1$s`.', 'comet-cache'), $cache_dir));
149
+ }
150
+ foreach (($_dir_regex_iteration = $self->dirRegexIteration($cache_dir_tmp, $cache_dir_tmp_regex)) as $_resource) {
151
+ $_resource_type = $_resource->getType();
152
+ $_sub_path_name = $_resource->getSubpathname();
153
+ $_path_name = $_resource->getPathname();
154
+
155
+ if ($_resource_type !== 'dir' && strpos($_sub_path_name, '/') === false) {
156
+ continue; // Don't delete links/files in the immediate directory; e.g. `[SHORT_NAME]-advanced-cache` or `.htaccess`, etc.
157
+ // Actual `http|https/...` cache links/files are nested. Links/files in the immediate directory are for other purposes.
158
+ }
159
+ switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`.
160
+
161
+ case 'link': // Symbolic links; i.e., 404 errors.
162
+
163
+ if ($check_max_age && !empty($max_age) && is_file($_resource->getLinkTarget())) {
164
+ if (($_lstat = lstat($_path_name)) && !empty($_lstat['mtime'])) {
165
+ if ($_lstat['mtime'] >= $max_age) {
166
+ break; // Break switch.
167
+ }
168
+ }
169
+ }
170
+ if (!unlink($_path_name)) {
171
+ $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible.
172
+ throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name));
173
+ }
174
+ ++$counter; // Increment counter for each link we delete.
175
+
176
+ break; // Break switch handler.
177
+
178
+ case 'file': // Regular files; i.e., not symlinks.
179
+
180
+ if ($check_max_age && !empty($max_age)) {
181
+ if ($_resource->getMTime() >= $max_age) {
182
+ break; // Break switch.
183
+ }
184
+ }
185
+ if (!unlink($_path_name)) {
186
+ $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible.
187
+ throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name));
188
+ }
189
+ ++$counter; // Increment counter for each file we delete.
190
+
191
+ break; // Break switch handler.
192
+
193
+ case 'dir': // A regular directory; i.e., not a symlink.
194
+
195
+ if ($regex !== '/^.+/i') {
196
+ break; // Not deleting everything.
197
+ }
198
+ if ($check_max_age && !empty($max_age)) {
199
+ break; // Not deleting everything.
200
+ }
201
+ if (!rmdir($_path_name)) {
202
+ $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible.
203
+ throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name));
204
+ }
205
+ # $counter++; // Increment counter for each directory we delete. ~ NO don't do that here.
206
+
207
+ break; // Break switch handler.
208
+
209
+ default: // Something else that is totally unexpected here.
210
+ $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible.
211
+ throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type));
212
+ }
213
+ }
214
+ unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name, $_lstat); // Housekeeping.
215
+
216
+ if (!rename($cache_dir_tmp, $cache_dir)) {
217
+ $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible.
218
+ throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $cache_dir_tmp));
219
+ }
220
+ /* ------- End lock state... ------------- */
221
+
222
+ $self->cacheUnlock($cache_lock); // Release.
223
+
224
+ return $counter;
225
+ };
226
+
227
+ /*
228
+ * Delete files from the cache directory (for the current host);
229
+ * i.e., those that match a specific regex pattern.
230
+ *
231
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
232
+ *
233
+ * @param string $regex A `/[regex pattern]/`; relative to the host cache directory.
234
+ * e.g. `/^my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/`
235
+ *
236
+ * Or, this can also be a full/absolute regex pattern against an absolute path;
237
+ * provided that it always starts with `/^`; including the full absolute cache/host directory path.
238
+ * e.g. `/^\/cache\/dir\/http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/`
239
+ *
240
+ * @param boolean $check_max_age Check max age? i.e., use purge behavior?
241
+ *
242
+ * @param boolean $___considering_domain_mapping For internal use only.
243
+ * @param boolean $___consider_domain_mapping_host_token For internal use only.
244
+ * @param boolean $___consider_domain_mapping_host_base_dir_tokens For internal use only.
245
+ *
246
+ * @return integer Total files deleted by this routine (if any).
247
+ *
248
+ * @throws \Exception If unable to delete a file for any reason.
249
+ */
250
+ $self->deleteFilesFromHostCacheDir = function (
251
+ $regex,
252
+ $check_max_age = false,
253
+ $___considering_domain_mapping = false,
254
+ $___consider_domain_mapping_host_token = null,
255
+ $___consider_domain_mapping_host_base_dir_tokens = null
256
+ ) use ($self) {
257
+ $counter = 0; // Initialize.
258
+
259
+ if (!($regex = (string) $regex)) {
260
+ return $counter; // Nothing to do.
261
+ }
262
+ if (!is_dir($cache_dir = $self->cacheDir())) {
263
+ return $counter; // Nothing to do.
264
+ }
265
+ $cache_dir = $self->nDirSeps($cache_dir); // Normalize.
266
+ $host_token = $current_host_token = $self->hostToken();
267
+ $host_base_dir_tokens = $current_host_base_dir_tokens = $self->hostBaseDirTokens();
268
+
269
+ if ($___considering_domain_mapping && isset($___consider_domain_mapping_host_token, $___consider_domain_mapping_host_base_dir_tokens)) {
270
+ $host_token = (string) $___consider_domain_mapping_host_token;
271
+ $host_base_dir_tokens = (string) $___consider_domain_mapping_host_base_dir_tokens;
272
+ }
273
+ if (!$host_token) { // Must have a host in the sub-routine below.
274
+ throw new \Exception(__('Invalid argument; host token empty!', 'comet-cache'));
275
+ }
276
+ if ($check_max_age && $self->isAdvancedCache()) {
277
+ throw new \Exception(__('Invalid argument; isAdvancedCache!', 'comet-cache'));
278
+ }
279
+ if ($check_max_age && !($max_age = strtotime('-'.$self->options['cache_max_age']))) {
280
+ return $counter; // Invalid cache expiration time.
281
+ }
282
+ /* ------- Begin lock state... ----------- */
283
+
284
+ $cache_lock = $self->cacheLock(); // Lock cache writes.
285
+
286
+ clearstatcache(); // Clear stat cache to be sure we have a fresh start below.
287
+
288
+ foreach (array('http', 'https') as $_host_scheme) {
289
+ $_host_url = $_host_scheme.'://'.$host_token.$host_base_dir_tokens;
290
+ $_host_cache_path_flags = CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT;
291
+ $_host_cache_path = $self->buildCachePath($_host_url, '', '', $_host_cache_path_flags);
292
+ $_host_cache_dir = $self->nDirSeps($cache_dir.'/'.$_host_cache_path); // Normalize.
293
+
294
+ if (!$_host_cache_dir || !is_dir($_host_cache_dir)) {
295
+ // On a multisite install this may have a cache sub-directory.
296
+ // e.g., `http/example-com[[-base]-child1][[/base]/child1]` instead of `http/example-com`.
297
+ continue; // Nothing to do.
298
+ }
299
+ $_host_cache_dir_tmp = $self->addTmpSuffix($_host_cache_dir); // Temporary directory.
300
+
301
+ $_host_cache_dir_tmp_regex = $regex; // Initialize host-specific regex pattern for the tmp directory.
302
+ $_host_cache_dir_tmp_regex = '\\/'.ltrim($_host_cache_dir_tmp_regex, '^\\/'); // Make sure it begins with an escaped `/`.
303
+ $_host_cache_dir_tmp_regex = $self->strIreplaceOnce(preg_quote($_host_cache_path.'/', '/'), '', $_host_cache_dir_tmp_regex);
304
+ $_host_cache_dir_tmp_regex = $self->strIreplaceOnce(preg_quote($_host_cache_dir.'/', '/'), '', $_host_cache_dir_tmp_regex);
305
+
306
+ $_host_cache_dir_tmp_regex = ltrim($_host_cache_dir_tmp_regex, '^\\/');
307
+ if (strpos($_host_cache_dir_tmp_regex, '(?:\/') === 0 || strpos($_host_cache_dir_tmp_regex, '(\/') === 0) {
308
+ $_host_cache_dir_tmp_regex = '/^'.preg_quote($_host_cache_dir_tmp, '/').$_host_cache_dir_tmp_regex;
309
+ } else {
310
+ $_host_cache_dir_tmp_regex = '/^'.preg_quote($_host_cache_dir_tmp.'/', '/').$_host_cache_dir_tmp_regex;
311
+ }
312
+ #if(WP_DEBUG) file_put_contents(WP_CONTENT_DIR.'/'.strtolower(SHORT_NAME).'-debug.log', print_r($regex, TRUE)."\n".print_r($_host_cache_dir_tmp_regex, TRUE)."\n\n", FILE_APPEND);
313
+ // Uncomment the above line to debug regex pattern matching used by this routine; and others that call upon it.
314
+
315
+ if (!rename($_host_cache_dir, $_host_cache_dir_tmp)) {
316
+ throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $_host_cache_dir));
317
+ }
318
+ foreach (($_dir_regex_iteration = $self->dirRegexIteration($_host_cache_dir_tmp, $_host_cache_dir_tmp_regex)) as $_resource) {
319
+ $_resource_type = $_resource->getType();
320
+ $_sub_path_name = $_resource->getSubpathname();
321
+ $_path_name = $_resource->getPathname();
322
+
323
+ if ($_host_cache_dir === $cache_dir && $_resource_type !== 'dir' && strpos($_sub_path_name, '/') === false) {
324
+ continue; // Don't delete links/files in the immediate directory; e.g. `[SHORT_NAME]-advanced-cache` or `.htaccess`, etc.
325
+ // Actual `http|https/...` cache links/files are nested. Links/files in the immediate directory are for other purposes.
326
+ }
327
+ switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`.
328
+
329
+ case 'link': // Symbolic links; i.e., 404 errors.
330
+
331
+ if ($check_max_age && !empty($max_age) && is_file($_resource->getLinkTarget())) {
332
+ if (($_lstat = lstat($_path_name)) && !empty($_lstat['mtime'])) {
333
+ if ($_lstat['mtime'] >= $max_age) {
334
+ break; // Break switch.
335
+ }
336
+ }
337
+ }
338
+ if (!unlink($_path_name)) {
339
+ $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible.
340
+ throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name));
341
+ }
342
+ ++$counter; // Increment counter for each link we delete.
343
+
344
+ break; // Break switch handler.
345
+
346
+ case 'file': // Regular files; i.e., not symlinks.
347
+
348
+ if ($check_max_age && !empty($max_age)) {
349
+ if ($_resource->getMTime() >= $max_age) {
350
+ break; // Break switch handler.
351
+ }
352
+ }
353
+ if (!unlink($_path_name)) {
354
+ $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible.
355
+ throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name));
356
+ }
357
+ ++$counter; // Increment counter for each file we delete.
358
+
359
+ break; // Break switch handler.
360
+
361
+ case 'dir': // A regular directory; i.e., not a symlink.
362
+
363
+ if ($regex !== '/^.+/i') {
364
+ break; // Not deleting everything.
365
+ }
366
+ if ($check_max_age && !empty($max_age)) {
367
+ break; // Not deleting everything.
368
+ }
369
+ if (!rmdir($_path_name)) {
370
+ $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible.
371
+ throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name));
372
+ }
373
+ # $counter++; // Increment counter for each directory we delete. ~ NO don't do that here.
374
+
375
+ break; // Break switch handler.
376
+
377
+ default: // Something else that is totally unexpected here.
378
+ $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible.
379
+ throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type));
380
+ }
381
+ }
382
+ unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name, $_lstat); // Housekeeping.
383
+
384
+ if (!rename($_host_cache_dir_tmp, $_host_cache_dir)) {
385
+ $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible.
386
+ throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $_host_cache_dir_tmp));
387
+ }
388
+ }
389
+ unset($_host_scheme, $_host_url, $_host_cache_path_flags, $_host_cache_path, $_host_cache_dir, $_host_cache_dir_tmp, $_host_cache_dir_tmp_regex);
390
+
391
+ /* ------- End lock state... ------------- */
392
+
393
+ $self->cacheUnlock($cache_lock); // Release.
394
+
395
+ /* ------- Include domain mapping variations also. ------- */
396
+
397
+ if (!$___considering_domain_mapping && is_multisite() && $self->canConsiderDomainMapping()) {
398
+ $domain_mapping_variations = array(); // Initialize array of domain variations.
399
+
400
+ if (($_host_token_for_blog = $self->hostTokenForBlog())) {
401
+ $_host_base_dir_tokens_for_blog = $self->hostBaseDirTokensForBlog();
402
+ $domain_mapping_variations[] = array('host_token' => $_host_token_for_blog, 'host_base_dir_tokens' => $_host_base_dir_tokens_for_blog);
403
+ } // The original blog host; i.e., without domain mapping.
404
+ unset($_host_token_for_blog, $_host_base_dir_tokens_for_blog); // Housekeeping.
405
+
406
+ foreach ($self->domainMappingBlogDomains() as $_domain_mapping_blog_domain) {
407
+ if (($_domain_host_token_for_blog = $self->hostTokenForBlog(false, true, $_domain_mapping_blog_domain))) {
408
+ $_domain_host_base_dir_tokens_for_blog = $self->hostBaseDirTokensForBlog(false, true); // This is only a formality.
409
+ $domain_mapping_variations[] = array('host_token' => $_domain_host_token_for_blog, 'host_base_dir_tokens' => $_domain_host_base_dir_tokens_for_blog);
410
+ }
411
+ } // This includes all of the domain mappings configured for the current blog ID.
412
+ unset($_domain_mapping_blog_domain, $_domain_host_token_for_blog, $_domain_host_base_dir_tokens_for_blog); // Housekeeping.
413
+
414
+ foreach ($domain_mapping_variations as $_domain_mapping_variation) {
415
+ if ($_domain_mapping_variation['host_token'] === $current_host_token && $_domain_mapping_variation['host_base_dir_tokens'] === $current_host_base_dir_tokens) {
416
+ continue; // Exclude current tokens. They were already iterated above.
417
+ }
418
+ $counter += $self->deleteFilesFromHostCacheDir($regex, $check_max_age, true, $_domain_mapping_variation['host_token'], $_domain_mapping_variation['host_base_dir_tokens']);
419
+ }
420
+ unset($_domain_mapping_variation); // Housekeeping.
421
+ }
422
+ return $counter;
423
+ };
424
+
425
+ /*
426
+ * Delete all files/dirs from a directory (for all schemes/hosts);
427
+ * including `[SHORT_NAME]-` prefixed files; or anything else for that matter.
428
+ *
429
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
430
+ *
431
+ * @param string $dir The directory from which to delete files/dirs.
432
+ *
433
+ * SECURITY: This directory MUST be located inside the `/wp-content/` directory.
434
+ * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself.
435
+ * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`.
436
+ *
437
+ * @param boolean $delete_dir_too Delete parent? i.e., delete the `$dir` itself also?
438
+ *
439
+ * @return integer Total files/directories deleted by this routine (if any).
440
+ *
441
+ * @throws \Exception If unable to delete a file/directory for any reason.
442
+ */
443
+ $self->deleteAllFilesDirsIn = function ($dir, $delete_dir_too = false) use ($self) {
444
+ $counter = 0; // Initialize.
445
+
446
+ if (!($dir = trim((string) $dir)) || !is_dir($dir)) {
447
+ return $counter; // Nothing to do.
448
+ }
449
+ $dir = $self->nDirSeps($dir);
450
+ $dir_temp = $self->addTmpSuffix($dir);
451
+ $wp_content_dir = $self->nDirSeps(WP_CONTENT_DIR);
452
+ $wp_content_dir_regex = preg_quote($wp_content_dir, '/');
453
+
454
+ if (!preg_match('/^'.$wp_content_dir_regex.'\/[^\/]+/i', $dir)) {
455
+ return $counter; // Security flag; do nothing in this case.
456
+ }
457
+ if (preg_match('/^'.$wp_content_dir_regex.'\/(?:mu\-plugins|themes|plugins)(?:\/|$)/i', $dir)) {
458
+ return $counter; // Security flag; do nothing in this case.
459
+ }
460
+ /* ------- Begin lock state... ----------- */
461
+
462
+ $cache_lock = $self->cacheLock(); // Lock cache writes.
463
+
464
+ clearstatcache(); // Clear stat cache to be sure we have a fresh start below.
465
+
466
+ if (!rename($dir, $dir_temp)) {
467
+ throw new \Exception(sprintf(__('Unable to delete all files/dirs. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $dir));
468
+ }
469
+ foreach (($_dir_regex_iteration = $self->dirRegexIteration($dir_temp, '/.+/')) as $_resource) {
470
+ $_resource_type = $_resource->getType();
471
+ $_sub_path_name = $_resource->getSubpathname();
472
+ $_path_name = $_resource->getPathname();
473
+
474
+ switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`.
475
+
476
+ case 'link': // Symbolic links; i.e., 404 errors.
477
+
478
+ if (!unlink($_path_name)) {
479
+ $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible.
480
+ throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name));
481
+ }
482
+ ++$counter; // Increment counter for each link we delete.
483
+
484
+ break; // Break switch handler.
485
+
486
+ case 'file': // Regular files; i.e., not symlinks.
487
+
488
+ if (!unlink($_path_name)) {
489
+ $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible.
490
+ throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name));
491
+ }
492
+ ++$counter; // Increment counter for each file we delete.
493
+
494
+ break; // Break switch handler.
495
+
496
+ case 'dir': // A regular directory; i.e., not a symlink.
497
+
498
+ if (!rmdir($_path_name)) {
499
+ $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible.
500
+ throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name));
501
+ }
502
+ # ++$counter; // Increment counter for each directory we delete. ~ NO don't do that here.
503
+
504
+ break; // Break switch handler.
505
+
506
+ default: // Something else that is totally unexpected here.
507
+ $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible.
508
+ throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type));
509
+ }
510
+ }
511
+ unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name); // Housekeeping.
512
+
513
+ if (!rename($dir_temp, $dir)) {
514
+ $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible.
515
+ throw new \Exception(sprintf(__('Unable to delete all files/dirs. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $dir_temp));
516
+ }
517
+ if ($delete_dir_too) {
518
+ if (!rmdir($dir)) {
519
+ throw new \Exception(sprintf(__('Unable to delete directory: `%1$s`.', 'comet-cache'), $dir));
520
+ }
521
+ ++$counter; // Increment counter for each directory we delete.
522
+ }
523
+ /* ------- End lock state... ------------- */
524
+
525
+ $self->cacheUnlock($cache_lock); // Release.
526
+
527
+ return $counter;
528
+ };
529
+
530
+ /*
531
+ * Erase all files/dirs from a directory (for all schemes/hosts);
532
+ * including `[SHORT_NAME]-` prefixed files; or anything else for that matter.
533
+ *
534
+ * WARNING: This does NO LOCKING and NO ATOMIC deletions.
535
+ *
536
+ * @since 150821 Improving recovery under stress.
537
+ *
538
+ * @param string $dir The directory from which to erase files/dirs.
539
+ *
540
+ * SECURITY: This directory MUST be located inside the `/wp-content/` directory.
541
+ * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself.
542
+ * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`.
543
+ *
544
+ * @param boolean $erase_dir_too Erase parent? i.e., erase the `$dir` itself also?
545
+ *
546
+ * @return integer Total files/directories erased by this routine (if any).
547
+ *
548
+ * @throws \Exception If unable to erase a file/directory for any reason.
549
+ */
550
+ $self->eraseAllFilesDirsIn = function ($dir, $erase_dir_too = false) use ($self) {
551
+ $counter = 0; // Initialize.
552
+
553
+ if (!($dir = trim((string) $dir)) || !is_dir($dir)) {
554
+ return $counter; // Nothing to do.
555
+ }
556
+ $dir = $self->nDirSeps($dir);
557
+ $wp_content_dir = $self->nDirSeps(WP_CONTENT_DIR);
558
+ $wp_content_dir_regex = preg_quote($wp_content_dir, '/');
559
+
560
+ if (!preg_match('/^'.$wp_content_dir_regex.'\/[^\/]+/i', $dir)) {
561
+ return $counter; // Security flag; do nothing in this case.
562
+ }
563
+ if (preg_match('/^'.$wp_content_dir_regex.'\/(?:mu\-plugins|themes|plugins)(?:\/|$)/i', $dir)) {
564
+ return $counter; // Security flag; do nothing in this case.
565
+ }
566
+ clearstatcache(); // Clear stat cache to be sure we have a fresh start below.
567
+
568
+ foreach (($_dir_regex_iteration = $self->dirRegexIteration($dir, '/.+/')) as $_resource) {
569
+ $_resource_type = $_resource->getType();
570
+ $_sub_path_name = $_resource->getSubpathname();
571
+ $_path_name = $_resource->getPathname();
572
+
573
+ switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`.
574
+
575
+ case 'link': // Symbolic links; i.e., 404 errors.
576
+
577
+ if (!unlink($_path_name)) {
578
+ throw new \Exception(sprintf(__('Unable to erase symlink: `%1$s`.', 'comet-cache'), $_path_name));
579
+ }
580
+ ++$counter; // Increment counter for each link we erase.
581
+
582
+ break; // Break switch handler.
583
+
584
+ case 'file': // Regular files; i.e., not symlinks.
585
+
586
+ if (!unlink($_path_name)) {
587
+ throw new \Exception(sprintf(__('Unable to erase file: `%1$s`.', 'comet-cache'), $_path_name));
588
+ }
589
+ ++$counter; // Increment counter for each file we erase.
590
+
591
+ break; // Break switch handler.
592
+
593
+ case 'dir': // A regular directory; i.e., not a symlink.
594
+
595
+ if (!rmdir($_path_name)) {
596
+ throw new \Exception(sprintf(__('Unable to erase dir: `%1$s`.', 'comet-cache'), $_path_name));
597
+ }
598
+ # ++$counter; // Increment counter for each directory we erase. ~ NO don't do that here.
599
+
600
+ break; // Break switch handler.
601
+
602
+ default: // Something else that is totally unexpected here.
603
+ throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type));
604
+ }
605
+ }
606
+ unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name); // Housekeeping.
607
+
608
+ if ($erase_dir_too) {
609
+ if (!rmdir($dir)) {
610
+ throw new \Exception(sprintf(__('Unable to erase directory: `%1$s`.', 'comet-cache'), $dir));
611
+ }
612
+ ++$counter; // Increment counter for each directory we erase.
613
+ }
614
+ return $counter;
615
+ };
616
+
617
+ /*
618
+ * Try to erase all files/dirs from a directory (for all schemes/hosts);
619
+ * including `[SHORT_NAME]-` prefixed files; or anything else for that matter.
620
+ *
621
+ * WARNING: This does NO LOCKING and NO ATOMIC deletions.
622
+ *
623
+ * @since 150821 Improving recovery under stress.
624
+ *
625
+ * @param string $dir The directory from which to erase files/dirs.
626
+ *
627
+ * SECURITY: This directory MUST be located inside the `/wp-content/` directory.
628
+ * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself.
629
+ * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`.
630
+ *
631
+ * @param boolean $erase_dir_too Erase parent? i.e., erase the `$dir` itself also?
632
+ *
633
+ * @return integer Total files/directories erased by this routine (if any).
634
+ */
635
+ $self->tryErasingAllFilesDirsIn = function ($dir, $erase_dir_too = false) use ($self) {
636
+ $counter = 0; // Initialize counter.
637
+ try {
638
+ $counter += $self->eraseAllFilesDirsIn($dir, $erase_dir_too);
639
+ } catch (\Exception $exception) {
640
+ // Fail softly.
641
+ }
642
+ return $counter;
643
+ };
src/includes/closures/Shared/CacheLockUtils.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Get an exclusive lock on the cache directory.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @return array Lock type & resource handle needed to unlock later or FALSE if disabled by filter.
10
+ *
11
+ * @throws \Exception If {@link \sem_get()} not available and there's
12
+ * no writable tmp directory for {@link \flock()} either.
13
+ *
14
+ * @throws \Exception If unable to obtain an exclusive lock by any available means.
15
+ *
16
+ * @note This call is blocking; i.e. it will not return a lock until a lock becomes possible.
17
+ * In short, this will block the caller until such time as write access becomes possible.
18
+ */
19
+ $self->cacheLock = function () use ($self) {
20
+ if ($self->applyWpFilters(GLOBAL_NS.'\\share::disable_cache_locking', false)
21
+ || $self->applyWpFilters(GLOBAL_NS.'_disable_cache_locking', false)) {
22
+ return false; // Disabled cache locking.
23
+ }
24
+ if (!($wp_config_file = $self->findWpConfigFile())) {
25
+ throw new \Exception(__('Unable to find the wp-config.php file.', 'comet-cache'));
26
+ }
27
+ $lock_type = 'flock'; // Default lock type.
28
+ $lock_type = $self->applyWpFilters(GLOBAL_NS.'\\share::cache_lock_lock_type', $lock_type);
29
+ $lock_type = $self->applyWpFilters(GLOBAL_NS.'_cache_lock_type', $lock_type);
30
+
31
+ if (!in_array($lock_type, array('flock', 'sem'), true)) {
32
+ $lock_type = 'flock'; // Default lock type.
33
+ }
34
+ if ($lock_type === 'sem' && $self->functionIsPossible('sem_get')) {
35
+ if (($ipc_key = ftok($wp_config_file, 'w'))) {
36
+ if (($resource = sem_get($ipc_key, 1)) && sem_acquire($resource)) {
37
+ return array('type' => 'sem', 'resource' => $resource);
38
+ }
39
+ }
40
+ }
41
+ if (!($tmp_dir = $self->getTmpDir())) {
42
+ throw new \Exception(__('No writable tmp directory.', 'comet-cache'));
43
+ }
44
+ $inode_key = fileinode($wp_config_file);
45
+ $mutex = $tmp_dir.'/'.SLUG_TD.'-'.$inode_key.'.lock';
46
+
47
+ if (!($resource = fopen($mutex, 'w')) || !flock($resource, LOCK_EX)) {
48
+ throw new \Exception(__('Unable to obtain an exclusive lock.', 'comet-cache'));
49
+ }
50
+ return array('type' => 'flock', 'resource' => $resource);
51
+ };
52
+
53
+ /*
54
+ * Release an exclusive lock on the cache directory.
55
+ *
56
+ * @since 150422 Rewrite. Updated 151002 to remove the `array` typecast.
57
+ *
58
+ * @param array|mixed $lock Type & resource.
59
+ */
60
+ $self->cacheUnlock = function ($lock) use ($self) {
61
+ if (!is_array($lock)) {
62
+ return; // Not possible.
63
+ // Or, they disabled cache locking.
64
+ }
65
+ if (empty($lock['type']) || empty($lock['resource'])) {
66
+ return; // Not possible.
67
+ }
68
+ if (!is_resource($lock['resource'])) {
69
+ return; // Not possible.
70
+ }
71
+ if ($lock['type'] === 'sem') {
72
+ sem_release($lock['resource']);
73
+ } elseif ($lock['type'] === 'flock') {
74
+ flock($lock['resource'], LOCK_UN);
75
+ fclose($lock['resource']);
76
+ }
77
+ };
src/includes/closures/Shared/CachePathConsts.php ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ if (defined(__NAMESPACE__.'\\CACHE_PATH_DEFAULT')) {
5
+ return; // Already defined these.
6
+ }
7
+ /**
8
+ * Default cache path flags.
9
+ *
10
+ * @since 150422 Rewrite.
11
+ *
12
+ * @type int A bitmask.
13
+ */
14
+ const CACHE_PATH_DEFAULT = 0;
15
+
16
+ /**
17
+ * Use a domain-mapped cache path.
18
+ *
19
+ * @since 150821 Improving multisite compat.
20
+ *
21
+ * @type int Part of a bitmask.
22
+ */
23
+ const CACHE_PATH_CONSIDER_DOMAIN_MAPPING = 1;
24
+
25
+ /**
26
+ * Generate an unmapped cache path?
27
+ *
28
+ * @since 150821 Improving multisite compat.
29
+ *
30
+ * @type int Part of a bitmask.
31
+ */
32
+ const CACHE_PATH_REVERSE_DOMAIN_MAPPING = 2;
33
+
34
+ /**
35
+ * Exclude scheme from cache path.
36
+ *
37
+ * @since 150422 Rewrite.
38
+ *
39
+ * @type int Part of a bitmask.
40
+ */
41
+ const CACHE_PATH_NO_SCHEME = 4;
42
+
43
+ /**
44
+ * Exclude host (i.e. domain name) from cache path.
45
+ *
46
+ * @since 150422 Rewrite.
47
+ *
48
+ * @type int Part of a bitmask.
49
+ */
50
+ const CACHE_PATH_NO_HOST = 8;
51
+
52
+ /**
53
+ * Exclude path from cache path.
54
+ *
55
+ * @since 150422 Rewrite.
56
+ *
57
+ * @type int Part of a bitmask.
58
+ */
59
+ const CACHE_PATH_NO_PATH = 16;
60
+
61
+ /**
62
+ * Exclude path index (i.e. no default `index`) from cache path.
63
+ *
64
+ * @since 150422 Rewrite.
65
+ *
66
+ * @type int Part of a bitmask.
67
+ */
68
+ const CACHE_PATH_NO_PATH_INDEX = 32;
69
+
70
+ /**
71
+ * Exclude query, user & version salt from cache path.
72
+ *
73
+ * @since 150422 Rewrite.
74
+ *
75
+ * @type int Part of a bitmask.
76
+ */
77
+ const CACHE_PATH_NO_QUV = 64;
78
+
79
+ /**
80
+ * Exclude query string from cache path.
81
+ *
82
+ * @since 150422 Rewrite.
83
+ *
84
+ * @type int Part of a bitmask.
85
+ */
86
+ const CACHE_PATH_NO_QUERY = 128;
87
+
88
+ /**
89
+ * Exclude user token from cache path.
90
+ *
91
+ * @since 150422 Rewrite.
92
+ *
93
+ * @type int Part of a bitmask.
94
+ */
95
+ const CACHE_PATH_NO_USER = 256;
96
+
97
+ /**
98
+ * Exclude version salt from cache path.
99
+ *
100
+ * @since 150422 Rewrite.
101
+ *
102
+ * @type int Part of a bitmask.
103
+ */
104
+ const CACHE_PATH_NO_VSALT = 512;
105
+
106
+ /**
107
+ * Exclude extension from cache path.
108
+ *
109
+ * @since 150422 Rewrite.
110
+ *
111
+ * @type int Part of a bitmask.
112
+ */
113
+ const CACHE_PATH_NO_EXT = 1024;
114
+
115
+ /**
116
+ * Allow wildcards in the cache path.
117
+ *
118
+ * @since 150422 Rewrite.
119
+ *
120
+ * @type int Part of a bitmask.
121
+ */
122
+ const CACHE_PATH_ALLOW_WILDCARDS = 2048;
123
+
124
+ /**
125
+ * Allow watered-down regex in the cache path.
126
+ *
127
+ * @since 151114 Improving regex syntax.
128
+ *
129
+ * @type int Part of a bitmask.
130
+ */
131
+ const CACHE_PATH_ALLOW_WD_REGEX = 4096;
132
+
133
+ /**
134
+ * Default cache path regex suffix frag.
135
+ *
136
+ * @since 150422 Rewrite.
137
+ *
138
+ * @type string Default regex suffix frag used in cache path patterns.
139
+ */
140
+ const CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG = null;
src/includes/closures/Shared/CachePathUtils.php ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Cache-path suffix frag (regex).
6
+ *
7
+ * @since 151220 Enhancing translation support.
8
+ *
9
+ * @param string $regex_suffix_frag Existing regex suffix frag?
10
+ *
11
+ * @return string Cache-path suffix frag (regex).
12
+ */
13
+ $self->cachePathRegexSuffixFrag = function ($regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) {
14
+ if ($regex_suffix_frag === CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) {
15
+ return $self->cachePathRegexDefaultSuffixFrag();
16
+ }
17
+ return (string) $regex_suffix_frag;
18
+ };
19
+
20
+ /*
21
+ * Default cache-path suffix frag (regex).
22
+ *
23
+ * @since 151220 Enhancing translation support.
24
+ *
25
+ * @return string Default cache-path suffix frag (regex).
26
+ */
27
+ $self->cachePathRegexDefaultSuffixFrag = function () use ($self) {
28
+ if ($self->isPlugin() && !empty($GLOBALS['wp_rewrite'])){
29
+ $pagination_base = $GLOBALS['wp_rewrite']->pagination_base;
30
+ $comments_pagination_base = $GLOBALS['wp_rewrite']->comments_pagination_base;
31
+ return '(?:\/index)?(?:\.|\/(?:'.preg_quote($pagination_base, '/').'\/[0-9]+|'.preg_quote($comments_pagination_base, '/').'\-[0-9]+)[.\/])';
32
+ } else {
33
+ return '(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])';
34
+ }
35
+ };
36
+
37
+ /*
38
+ * Converts a URL into a `cache/path` based on input `$flags`.
39
+ *
40
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
41
+ *
42
+ * @param string $url The input URL to convert.
43
+ * @param string $with_user_token Optional user token (if applicable).
44
+ * @param string $with_version_salt Optional version salt (if applicable).
45
+ * @param int $flags Optional flags. A bitmask via `CACHE_PATH_*` constants.
46
+ *
47
+ * @return string The resulting `cache/path` based on the input `$url` & `$flags`.
48
+ */
49
+ $self->buildCachePath = function ($url, $with_user_token = '', $with_version_salt = '', $flags = CACHE_PATH_DEFAULT) use ($self) {
50
+ # Force parameter types.
51
+
52
+ $url = trim((string) $url);
53
+ $with_user_token = trim((string) $with_user_token);
54
+ $with_version_salt = trim((string) $with_version_salt);
55
+
56
+ # Initialize variables.
57
+
58
+ $is_multisite = is_multisite();
59
+ $can_consider_domain_mapping = $is_multisite && $self->canConsiderDomainMapping();
60
+ $cache_path = ''; // Initialize cache path being built here.
61
+
62
+ # Deal w/ domain mapping considerations.
63
+
64
+ if ($flags & CACHE_PATH_CONSIDER_DOMAIN_MAPPING && $is_multisite && $can_consider_domain_mapping) {
65
+ if ($flags & CACHE_PATH_REVERSE_DOMAIN_MAPPING) {
66
+ $url = $self->domainMappingReverseUrlFilter($url);
67
+ } else {
68
+ $url = $self->domainMappingUrlFilter($url);
69
+ }
70
+ }
71
+ # Validate the URL we have now.
72
+
73
+ if (!$url || !($url_parts = $self->parseUrl($url))) {
74
+ return ($cache_path = ''); // Not possible.
75
+ }
76
+ if (empty($url_parts['scheme']) || $url_parts['scheme'] === '//') {
77
+ return ($cache_path = ''); // Not possible.
78
+ }
79
+ if (empty($url_parts['host'])) {
80
+ return ($cache_path = ''); // Not possible.
81
+ }
82
+ # Initialize additional variables; based on the parsed URL.
83
+
84
+ $is_url_domain_mapped = $is_multisite && $can_consider_domain_mapping && $self->domainMappingBlogId($url);
85
+ $host_base_dir_tokens = $self->hostBaseDirTokens(false, $is_url_domain_mapped, !empty($url_parts['path']) ? $url_parts['path'] : '/');
86
+
87
+ $is_a_multisite_base_dir = $is_multisite && $host_base_dir_tokens && $host_base_dir_tokens !== '/' // Check?
88
+ && stripos(!empty($url_parts['path']) ? rtrim($url_parts['path'], '/').'/' : '/', $host_base_dir_tokens) === 0;
89
+
90
+ $is_a_multisite_base_dir_root = $is_multisite && $is_a_multisite_base_dir // Save time by using the previous check here.
91
+ && strcasecmp(trim($host_base_dir_tokens, '/'), trim(!empty($url_parts['path']) ? $url_parts['path'] : '/', '/')) === 0;
92
+
93
+ # Build and return the cache path.
94
+
95
+ if (!($flags & CACHE_PATH_NO_SCHEME)) {
96
+ $cache_path .= $url_parts['scheme'].'/';
97
+ }
98
+ if (!($flags & CACHE_PATH_NO_HOST)) {
99
+ $cache_path .= $url_parts['host'].'/';
100
+
101
+ // Put multisite sub-roots into a host directory of their own.
102
+ // e.g., `example-com[[-base]-child1]` instead of `example-com`.
103
+ if ($is_a_multisite_base_dir && $host_base_dir_tokens && $host_base_dir_tokens !== '/') {
104
+ $host_base_dir_suffix = preg_replace('/[^a-z0-9.]/i', '-', rtrim($host_base_dir_tokens, '/'));
105
+ $cache_path = rtrim($cache_path, '/').$host_base_dir_suffix.'/';
106
+ }
107
+ }
108
+ if (!($flags & CACHE_PATH_NO_PATH)) {
109
+ if (isset($url_parts['path'][201])) {
110
+ $_path_tmp = '/'; // Initialize tmp path.
111
+ foreach (explode('/', $url_parts['path']) as $_path_component) {
112
+ if (!isset($_path_component[0])) {
113
+ continue; // Empty.
114
+ }
115
+ if (isset($_path_component[201])) {
116
+ $_path_component = 'lpc-'.sha1($_path_component);
117
+ }
118
+ $_path_tmp .= $_path_component.'/';
119
+ }
120
+ $url_parts['path'] = $_path_tmp; // Shorter components.
121
+ unset($_path_component, $_path_tmp); // Housekeeping.
122
+
123
+ if (isset($url_parts['path'][2001])) {
124
+ $url_parts['path'] = '/lp-'.sha1($url_parts['path']).'/';
125
+ }
126
+ } // Now add the path and check for a possible root `index/` suffix.
127
+ if (!empty($url_parts['path']) && strlen($url_parts['path'] = trim($url_parts['path'], '\\/'." \t\n\r\0\x0B"))) {
128
+ $cache_path .= $url_parts['path'].'/'; // Add the path as it exists.
129
+
130
+ if (!($flags & CACHE_PATH_NO_PATH_INDEX) && $is_multisite && $is_a_multisite_base_dir_root) {
131
+ // We should build an `index/` when this ends with a multisite [[/base]/child1] root.
132
+ // e.g., `http/example-com[[-base]-child1][[/base]/child1]` is a root directory.
133
+ $cache_path .= 'index/'; // Use an index suffix.
134
+ }
135
+ } elseif (!($flags & CACHE_PATH_NO_PATH_INDEX)) {
136
+ $cache_path .= 'index/';
137
+ }
138
+ }
139
+ if ($self->isExtensionLoaded('mbstring') && mb_check_encoding($cache_path, 'UTF-8')) {
140
+ $cache_path = mb_strtolower($cache_path, 'UTF-8');
141
+ }
142
+ $cache_path = str_replace('.', '-', strtolower($cache_path));
143
+
144
+ if (!($flags & CACHE_PATH_NO_QUV)) {
145
+ if (!($flags & CACHE_PATH_NO_QUERY)) {
146
+ if (isset($url_parts['query']) && $url_parts['query'] !== '') {
147
+ $cache_path = rtrim($cache_path, '/').'.q/'.md5($url_parts['query']).'/';
148
+ }
149
+ }
150
+ if (!($flags & CACHE_PATH_NO_USER)) {
151
+ if ($with_user_token !== '') {
152
+ $cache_path = rtrim($cache_path, '/').'.u/'.str_replace(array('/', '\\'), '-', $with_user_token).'/';
153
+ }
154
+ }
155
+ if (!($flags & CACHE_PATH_NO_VSALT)) {
156
+ if ($with_version_salt !== '') {
157
+ $cache_path = rtrim($cache_path, '/').'.v/'.str_replace(array('/', '\\'), '-', $with_version_salt).'/';
158
+ }
159
+ }
160
+ }
161
+ $cache_path = trim(preg_replace(array('/\/+/', '/\.+/'), array('/', '.'), $cache_path), '/');
162
+
163
+ if ($flags & CACHE_PATH_ALLOW_WD_REGEX) {
164
+ $cache_path = preg_replace('/[^a-z0-9\/.*\^$]/i', '-', $cache_path);
165
+ } elseif ($flags & CACHE_PATH_ALLOW_WILDCARDS) {
166
+ $cache_path = preg_replace('/[^a-z0-9\/.*]/i', '-', $cache_path);
167
+ } else {
168
+ $cache_path = preg_replace('/[^a-z0-9\/.]/i', '-', $cache_path);
169
+ }
170
+ if (!($flags & CACHE_PATH_NO_EXT)) {
171
+ $cache_path .= '.html';
172
+ }
173
+ return $cache_path;
174
+ };
175
+
176
+ /*
177
+ * Regex pattern for a call to `deleteFilesFromCacheDir()`.
178
+ *
179
+ * @since 151114 Updated to support an arbitrary URL instead of a regex frag.
180
+ *
181
+ * @param string $url The input URL to convert. This CAN be left empty when necessary.
182
+ * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`.
183
+ * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`.
184
+ *
185
+ * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`.
186
+ * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`.
187
+ * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`.
188
+ * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}.
189
+ *
190
+ * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`.
191
+ */
192
+ $self->buildCachePathRegex = function ($url, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) {
193
+ $url = trim((string) $url);
194
+ $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag);
195
+ $cache_path_regex = ''; // Initialize regex.
196
+
197
+ if ($url) {
198
+ $flags = CACHE_PATH_NO_SCHEME // Scheme added below.
199
+ | CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT;
200
+ $cache_path = $self->buildCachePath($url, '', '', $flags); // Without the scheme.
201
+ $cache_path_regex = isset($cache_path[0]) ? '\/https?\/'.preg_quote($cache_path, '/') : '';
202
+ }
203
+ return '/^'.$cache_path_regex.$regex_suffix_frag.'/i';
204
+ };
205
+
206
+ /*
207
+ * Regex pattern for a call to `deleteFilesFromHostCacheDir()`.
208
+ *
209
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
210
+ *
211
+ * @param string $url The input URL to convert. This CAN be left empty when necessary.
212
+ * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`.
213
+ * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`.
214
+ *
215
+ * @param string $regex_suffix_frag Regex fragment to come after the relative cache/path regex frag.
216
+ * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`.
217
+ * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`.
218
+ * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}.
219
+ *
220
+ * @return string Regex pattern for a call to `deleteFilesFromHostCacheDir()`.
221
+ */
222
+ $self->buildHostCachePathRegex = function ($url, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) {
223
+ $url = trim((string) $url);
224
+ $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag);
225
+ $abs_relative_cache_path_regex = ''; // Initialize regex.
226
+ $is_url_domain_mapped = false; // Initialize.
227
+
228
+ if ($url) {
229
+ if (is_multisite() && $self->canConsiderDomainMapping()) {
230
+ // Shortest possible URI; i.e., consider domain mapping.
231
+ $url = $self->domainMappingUrlFilter($url);
232
+ $is_url_domain_mapped = $url && $self->domainMappingBlogId($url);
233
+ }
234
+ if ($url && ($url_parts = $self->parseUrl($url)) && !empty($url_parts['host'])) {
235
+ $flags = CACHE_PATH_NO_SCHEME | CACHE_PATH_NO_HOST
236
+ | CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT;
237
+
238
+ $host_base_dir_tokens = $self->hostBaseDirTokens(false, $is_url_domain_mapped, !empty($url_parts['path']) ? $url_parts['path'] : '/');
239
+ $host_url = rtrim('http://'.$url_parts['host'].$host_base_dir_tokens, '/');
240
+ $host_cache_path = $self->buildCachePath($host_url, '', '', $flags);
241
+
242
+ $cache_path = $self->buildCachePath($url, '', '', $flags);
243
+ $relative_cache_path = preg_replace('/^'.preg_quote($host_cache_path, '/').'(?:\/|$)/i', '', $cache_path);
244
+
245
+ $abs_relative_cache_path = isset($relative_cache_path[0]) ? '/'.$relative_cache_path : '';
246
+ $abs_relative_cache_path_regex = isset($abs_relative_cache_path[0]) ? preg_quote($abs_relative_cache_path, '/') : '';
247
+ }
248
+ }
249
+ return '/^'.$abs_relative_cache_path_regex.$regex_suffix_frag.'/i';
250
+ };
251
+
252
+ /*
253
+ * Regex pattern for a call to `deleteFilesFromCacheDir()`.
254
+ *
255
+ * @since 151114 Improving watered-down regex syntax.
256
+ *
257
+ * @param string $url The input URL to convert. This CAN be left empty when necessary.
258
+ * This may also contain watered-down regex; i.e., `*^$` characters are OK here.
259
+ * However, `^$` are discarded, as they are unnecessary in this context.
260
+ *
261
+ * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`.
262
+ * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`.
263
+ *
264
+ * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`.
265
+ * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`.
266
+ * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`.
267
+ * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}.
268
+ *
269
+ * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`.
270
+ */
271
+ $self->buildCachePathRegexFromWcUrl = function ($url, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) {
272
+ $url = trim((string) $url, '^$');
273
+ $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag);
274
+ $cache_path_regex = ''; // Initialize regex.
275
+
276
+ if ($url) { // After `^$` trimming above.
277
+ $flags = CACHE_PATH_ALLOW_WILDCARDS | CACHE_PATH_NO_SCHEME
278
+ | CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT;
279
+ $cache_path = $self->buildCachePath($url, '', '', $flags); // Without the scheme.
280
+ $cache_path_regex = isset($cache_path[0]) ? '\/https?\/'.$self->wdRegexToActualRegexFrag($cache_path) : '';
281
+ }
282
+ return '/^'.$cache_path_regex.$regex_suffix_frag.'/i';
283
+ };
284
+
285
+ /*
286
+ * Regex pattern for a call to `deleteFilesFromHostCacheDir()`.
287
+ *
288
+ * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements.
289
+ *
290
+ * @param string $uris A line-delimited list of URIs. These may contain `*^$` also.
291
+ * However, `^$` are discarded, as they are unnecessary in this context.
292
+ *
293
+ * @param string $regex_suffix_frag Regex fragment to come after each relative cache/path.
294
+ * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`.
295
+ * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`.
296
+ * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}.
297
+ *
298
+ * @return string Regex pattern for a call to `deleteFilesFromHostCacheDir()`.
299
+ */
300
+ $self->buildHostCachePathRegexFragsFromWcUris = function ($uris, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) {
301
+ $uris = trim((string) $uris);
302
+ $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag);
303
+
304
+ $_self = $self; // Reference for the closure below.
305
+ $flags = CACHE_PATH_ALLOW_WILDCARDS | CACHE_PATH_NO_SCHEME | CACHE_PATH_NO_HOST
306
+ | CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT;
307
+
308
+ $host = 'doesnt-matter.foo.bar';
309
+ $host_url = rtrim('http://'.$host, '/');
310
+ $host_cache_path = $self->buildCachePath($host_url, '', '', $flags);
311
+ $uri_patterns = array_unique(preg_split('/['."\r\n".']+/', $uris, -1, PREG_SPLIT_NO_EMPTY));
312
+
313
+ foreach ($uri_patterns as $_key => &$_uri_pattern) {
314
+ if (($_uri_pattern = trim($_uri_pattern, '^$'))) {
315
+ $_cache_path = $_self->buildCachePath($host_url.'/'.trim($_uri_pattern, '/'), '', '', $flags);
316
+ $_relative_cache_path = preg_replace('/^'.preg_quote($host_cache_path, '/').'(?:\/|$)/i', '', $_cache_path);
317
+ $_uri_pattern = $self->wdRegexToActualRegexFrag($_relative_cache_path);
318
+ }
319
+ if (!$_uri_pattern) {
320
+ unset($uri_patterns[$_key]); // Remove it.
321
+ }
322
+ }
323
+ unset($_key, $_uri_pattern, $_cache_path, $_relative_cache_path); // Housekeeping.
324
+
325
+ return $uri_patterns ? '(?:'.implode('|', array_unique($uri_patterns)).')'.$regex_suffix_frag : '';
326
+ };
327
+
328
+ /*
329
+ * Regex pattern for a call to `deleteFilesFromCacheDir()`.
330
+ *
331
+ * @since 151114 Moving this low-level routine into a method of a different name.
332
+ *
333
+ * @param string $regex_frag A regex fragment. This CAN be left empty when necessary.
334
+ * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`.
335
+ * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`.
336
+ *
337
+ * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`.
338
+ * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`.
339
+ * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`.
340
+ * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}.
341
+ *
342
+ * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`.
343
+ */
344
+ $self->assembleCachePathRegex = function ($regex_frag, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) {
345
+ $regex_frag = (string) $regex_frag;
346
+ $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag);
347
+
348
+ return '/^'.$regex_frag.$regex_suffix_frag.'/i';
349
+ };
src/includes/closures/Shared/ConditionalUtils.php ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Is AdvancedCache class?
6
+ *
7
+ * @since 150821 Improving multisite compat.
8
+ *
9
+ * @return bool `TRUE` if this is the AdvancedCache class.
10
+ */
11
+ $self->isAdvancedCache = function () use ($self) {
12
+ return $self instanceof AdvancedCache;
13
+ };
14
+
15
+ /*
16
+ * Is Plugin class?
17
+ *
18
+ * @since 150821 Improving multisite compat.
19
+ *
20
+ * @return bool `TRUE` if this is the Plugin class.
21
+ */
22
+ $self->isPlugin = function () use ($self) {
23
+ return $self instanceof Plugin;
24
+ };
25
+
26
+ /*
27
+ * Is the current request method `POST`, `PUT` or `DELETE`?
28
+ *
29
+ * @since 150422 Rewrite.
30
+ *
31
+ * @return boolean `TRUE` if current request method is `POST`, `PUT` or `DELETE`.
32
+ *
33
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
34
+ */
35
+ $self->isPostPutDeleteRequest = function () use ($self) {
36
+ if (!is_null($is = &$self->staticKey('isPostPutDeleteRequest'))) {
37
+ return $is; // Already cached this.
38
+ }
39
+ if (!empty($_POST)) {
40
+ return ($is = true);
41
+ }
42
+ if (!empty($_SERVER['REQUEST_METHOD']) && is_string($_SERVER['REQUEST_METHOD'])) {
43
+ if (in_array(strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT', 'DELETE'), true)) {
44
+ return ($is = true);
45
+ }
46
+ }
47
+ return ($is = false);
48
+ };
49
+
50
+ /*
51
+ * Does the current request include an uncacheable query string?
52
+ *
53
+ * @since 151002 Improving Nginx support.
54
+ *
55
+ * @return boolean True if request includes an uncacheable query string.
56
+ *
57
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
58
+ */
59
+ $self->requestContainsUncacheableQueryVars = function () use ($self) {
60
+ if (!is_null($is = &$self->staticKey('requestContainsUncacheableQueryVars'))) {
61
+ return $is; // Already cached this.
62
+ }
63
+ if (!empty($_GET) || !empty($_SERVER['QUERY_STRING'])) {
64
+ $_get_count = !empty($_GET) ? count($_GET) : 0;
65
+ $is_abc_only = $_get_count === 1 && isset($_GET[strtolower(SHORT_NAME).'ABC']);
66
+ $is_nginx_q_only = $_get_count === 1 && isset($_GET['q']) && $self->isNginx();
67
+ $is_ac_get_var_true = isset($_GET[strtolower(SHORT_NAME).'AC']) && filter_var($_GET[strtolower(SHORT_NAME).'AC'], FILTER_VALIDATE_BOOLEAN);
68
+
69
+ if (!$is_abc_only && !$is_nginx_q_only && !$is_ac_get_var_true) {
70
+ return ($is = true);
71
+ }
72
+ }
73
+ return ($is = false);
74
+ };
75
+
76
+ /*
77
+ * Is the current request method is uncacheable?
78
+ *
79
+ * @since 150422 Rewrite.
80
+ *
81
+ * @return boolean `TRUE` if current request method is uncacheable.
82
+ *
83
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
84
+ */
85
+ $self->isUncacheableRequestMethod = function () use ($self) {
86
+ if (!is_null($is = &$self->staticKey('isUncacheableRequestMethod'))) {
87
+ return $is; // Already cached this.
88
+ }
89
+ if (!empty($_POST)) {
90
+ return ($is = true);
91
+ }
92
+ if (!empty($_SERVER['REQUEST_METHOD']) && is_string($_SERVER['REQUEST_METHOD'])) {
93
+ if (!in_array(strtoupper($_SERVER['REQUEST_METHOD']), array('GET'), true)) {
94
+ return ($is = true);
95
+ }
96
+ }
97
+ return ($is = false);
98
+ };
99
+
100
+ /*
101
+ * Should the current user should be considered a logged-in user?
102
+ *
103
+ * @since 150422 Rewrite.
104
+ *
105
+ * @return boolean `TRUE` if current user should be considered a logged-in user.
106
+ *
107
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
108
+ */
109
+ $self->isLikeUserLoggedIn = function () use ($self) {
110
+ if (!is_null($is = &$self->staticKey('isLikeUserLoggedIn'))) {
111
+ return $is; // Already cached this.
112
+ }
113
+ if (defined('SID') && SID) {
114
+ return ($is = true); // Session ID.
115
+ }
116
+ if (empty($_COOKIE)) {
117
+ return ($is = false); // No cookies.
118
+ }
119
+ $regex_logged_in_cookies = '/^'; // Initialize.
120
+
121
+ if (defined('LOGGED_IN_COOKIE') && LOGGED_IN_COOKIE) {
122
+ $regex_logged_in_cookies .= preg_quote(LOGGED_IN_COOKIE, '/');
123
+ } else { // Use the default hard-coded cookie prefix.
124
+ $regex_logged_in_cookies .= 'wordpress_logged_in_';
125
+ }
126
+ $regex_logged_in_cookies .= '|comment_author_';
127
+ $regex_logged_in_cookies .= '|wp[_\-]postpass_';
128
+
129
+ $regex_logged_in_cookies .= '/'; // Close regex.
130
+
131
+ foreach ($_COOKIE as $_key => $_value) {
132
+ if ($_value && preg_match($regex_logged_in_cookies, $_key)) {
133
+ return ($is = true); // Like a logged-in user.
134
+ }
135
+ } unset($_key, $_value); // Housekeeping.
136
+
137
+ return ($is = false);
138
+ };
139
+
140
+ /*
141
+ * Are we in a LOCALHOST environment?
142
+ *
143
+ * @since 150422 Rewrite.
144
+ *
145
+ * @return boolean `TRUE` if we are in a LOCALHOST environment.
146
+ *
147
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
148
+ */
149
+ $self->isLocalhost = function () use ($self) {
150
+ if (!is_null($is = &$self->staticKey('isLocalhost'))) {
151
+ return $is; // Already cached this.
152
+ }
153
+ if (defined('LOCALHOST')) {
154
+ return ($is = (boolean) LOCALHOST);
155
+ }
156
+ if (preg_match('/\b(?:localhost|127\.0\.0\.1)\b/i', $self->hostToken())) {
157
+ return ($is = true);
158
+ }
159
+ return ($is = false);
160
+ };
161
+
162
+
163
+
164
+ /*
165
+ * Is the current request for a feed?
166
+ *
167
+ * @since 150422 Rewrite.
168
+ *
169
+ * @return boolean `TRUE` if the current request is for a feed.
170
+ *
171
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
172
+ */
173
+ $self->isFeed = function () use ($self) {
174
+ if (!is_null($is = &$self->staticKey('isFeed'))) {
175
+ return $is; // Already cached this.
176
+ }
177
+ if (isset($_REQUEST['feed'])) {
178
+ return ($is = true);
179
+ }
180
+ if (!empty($_SERVER['REQUEST_URI']) && is_string($_SERVER['REQUEST_URI'])) {
181
+ if (preg_match('/\/feed(?:[\/?]|$)/', $_SERVER['REQUEST_URI'])) {
182
+ return ($is = true);
183
+ }
184
+ }
185
+ return ($is = false);
186
+ };
187
+
188
+ /*
189
+ * Is a document/string an HTML/XML doc; or no?
190
+ *
191
+ * @since 150422 Rewrite.
192
+ *
193
+ * @param string $doc Input string/document to check.
194
+ *
195
+ * @return boolean True if `$doc` is an HTML/XML doc type.
196
+ */
197
+ $self->isHtmlXmlDoc = function ($doc) use ($self) {
198
+ $doc = trim((string) $doc);
199
+ $doc_hash = sha1($doc);
200
+
201
+ if (!is_null($is = &$self->staticKey('isHtmlXmlDoc', $doc_hash))) {
202
+ return $is; // Already cached this.
203
+ }
204
+ if (stripos($doc, '</html>') !== false) {
205
+ return ($is = true);
206
+ }
207
+ if (stripos($doc, '<?xml') === 0) {
208
+ return ($is = true);
209
+ }
210
+ return ($is = false);
211
+ };
212
+
213
+ /*
214
+ * Does the current request have a cacheable content type?
215
+ *
216
+ * @since 150422 Rewrite.
217
+ *
218
+ * @return boolean `TRUE` if the current request has a cacheable content type.
219
+ *
220
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
221
+ *
222
+ * @warning Do NOT call upon this method until the end of a script execution.
223
+ */
224
+ $self->hasACacheableContentType = function () use ($self) {
225
+ if (!is_null($is = &$self->staticKey('hasACacheableContentType'))) {
226
+ return $is; // Already cached this.
227
+ }
228
+ foreach ($self->headersList() as $_key => $_header) {
229
+ if (stripos($_header, 'Content-Type:') === 0) {
230
+ $content_type = $_header; // Last one.
231
+ }
232
+ } unset($_key, $_header); // Housekeeping.
233
+
234
+ if (isset($content_type[0]) && stripos($content_type, 'html') === false
235
+ && stripos($content_type, 'xml') === false && stripos($content_type, GLOBAL_NS) === false) {
236
+ return ($is = false); // Do NOT cache data sent by scripts serving other MIME types.
237
+ }
238
+ return ($is = true);
239
+ };
240
+
241
+ /*
242
+ * Does the current request have a cacheable HTTP status code?
243
+ *
244
+ * @since 150422 Rewrite.
245
+ *
246
+ * @return boolean `TRUE` if the current request has a cacheable HTTP status code.
247
+ *
248
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
249
+ *
250
+ * @warning Do NOT call upon this method until the end of a script execution.
251
+ */
252
+ $self->hasACacheableStatus = function () use ($self) {
253
+ if (!is_null($is = &$self->staticKey('hasACacheableStatus'))) {
254
+ return $is; // Already cached this.
255
+ }
256
+ if (($http_status = (string) $self->httpStatus()) && $http_status[0] !== '2' && $http_status !== '404') {
257
+ return ($is = false); // A non-2xx & non-404 status code.
258
+ }
259
+ foreach ($self->headersList() as $_key => $_header) {
260
+ if (preg_match('/^(?:Retry\-After\:\s+(?P<retry>.+)|Status\:\s+(?P<status>[0-9]+)|HTTP\/[0-9]+(?:\.[0-9]+)?\s+(?P<http_status>[0-9]+))/i', $_header, $_m)) {
261
+ if (!empty($_m['retry']) || (!empty($_m['status']) && $_m['status'][0] !== '2' && $_m['status'] !== '404')
262
+ || (!empty($_m['http_status']) && $_m['http_status'][0] !== '2' && $_m['http_status'] !== '404')
263
+ ) {
264
+ return ($is = false); // Not a cacheable status.
265
+ }
266
+ }
267
+ } unset($_key, $_header); // Housekeeping.
268
+
269
+ return ($is = true);
270
+ };
271
+
272
+ /*
273
+ * Checks if a PHP extension is loaded up.
274
+ *
275
+ * @since 150422 Rewrite.
276
+ *
277
+ * @param string $extension A PHP extension slug (i.e. extension name).
278
+ *
279
+ * @return boolean `TRUE` if the extension is loaded.
280
+ *
281
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
282
+ */
283
+ $self->isExtensionLoaded = function ($extension) use ($self) {
284
+ $extension = (string) $extension;
285
+
286
+ if (!is_null($is = &$self->staticKey('isExtensionLoaded', $extension))) {
287
+ return $is; // Already cached this.
288
+ }
289
+ return ($is = (boolean) extension_loaded($extension));
290
+ };
291
+
292
+ /*
293
+ * Is a particular function possible in every way?
294
+ *
295
+ * @since 150422 Rewrite.
296
+ *
297
+ * @param string $function A PHP function (or user function) to check.
298
+ *
299
+ * @return string `TRUE` if the function is possible.
300
+ *
301
+ * @note This checks (among other things) if the function exists and that it's callable.
302
+ * It also checks the currently configured `disable_functions` and `suhosin.executor.func.blacklist`.
303
+ */
304
+ $self->functionIsPossible = function ($function) use ($self) {
305
+ $function = (string) $function;
306
+
307
+ if (!is_null($is = &$self->staticKey('functionIsPossible', $function))) {
308
+ return $is; // Already cached this.
309
+ }
310
+ if (is_null($disabled_functions = &$self->staticKey('functionIsPossible_disabled_functions'))) {
311
+ $disabled_functions = array(); // Initialize disabled/blacklisted functions.
312
+
313
+ if (($disable_functions = trim(ini_get('disable_functions')))) {
314
+ $disabled_functions = array_merge($disabled_functions, preg_split('/[\s;,]+/', strtolower($disable_functions), -1, PREG_SPLIT_NO_EMPTY));
315
+ }
316
+ if (($blacklist_functions = trim(ini_get('suhosin.executor.func.blacklist')))) {
317
+ $disabled_functions = array_merge($disabled_functions, preg_split('/[\s;,]+/', strtolower($blacklist_functions), -1, PREG_SPLIT_NO_EMPTY));
318
+ }
319
+ }
320
+ if (!function_exists($function) || !is_callable($function)) {
321
+ return ($is = false); // Not possible.
322
+ }
323
+ if ($disabled_functions && in_array(strtolower($function), $disabled_functions, true)) {
324
+ return ($is = false); // Not possible.
325
+ }
326
+ return ($is = true);
327
+ };
src/includes/closures/Shared/DomainMappingUtils.php ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Can consider domain mapping?
6
+ *
7
+ * @since 150821 Improving multisite compat.
8
+ *
9
+ * @return bool `TRUE` if we can consider domain mapping.
10
+ *
11
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
12
+ */
13
+ $self->canConsiderDomainMapping = function () use ($self) {
14
+ if (!is_null($can = &$self->staticKey('canConsiderDomainMapping'))) {
15
+ return $can; // Already cached this.
16
+ }
17
+ if (!$self->isAdvancedCache() && is_multisite() && $self->hostBaseToken() === '/'
18
+ && defined('SUNRISE_LOADED') && SUNRISE_LOADED && !empty($GLOBALS['dm_domain'])) {
19
+ return ($can = true); // Can consider.
20
+ }
21
+ return ($can = false); // Cannot consider.
22
+ };
23
+
24
+ /*
25
+ * Domain mapping?
26
+ *
27
+ * @since 150821 Improving multisite compat.
28
+ *
29
+ * @return integer Domain mapping ID; else `0` (false).
30
+ *
31
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
32
+ */
33
+ $self->isDomainMapping = function () use ($self) {
34
+ if (!is_null($is = &$self->staticKey('isDomainMapping'))) {
35
+ return $is; // Already cached this.
36
+ }
37
+ if (!$self->isAdvancedCache() && is_multisite() && $self->canConsiderDomainMapping()
38
+ && defined('DOMAIN_MAPPING') && DOMAIN_MAPPING && !empty($GLOBALS['domain_mapping_id'])) {
39
+ return ($is = (integer) $GLOBALS['domain_mapping_id']); // Blog ID.
40
+ }
41
+ return ($is = 0); // Not domain mapping.
42
+ };
43
+
44
+ /*
45
+ * Filters a URL in order to apply domain mapping.
46
+ *
47
+ * @since 150821 Improving multisite compat.
48
+ *
49
+ * @param string $url The input URL to filter.
50
+ *
51
+ * @return string The filtered URL; else the original URL.
52
+ *
53
+ * @note The return value of this function is NOT cached, but inner portions are.
54
+ */
55
+ $self->domainMappingUrlFilter = function ($url) use ($self) {
56
+ $original_url = (string) $url; // Preserve.
57
+ $url = trim((string) $url);
58
+
59
+ if (!is_multisite() || !$self->canConsiderDomainMapping()) {
60
+ return $original_url; // Not possible.
61
+ }
62
+ if (!$url || !($url_parts = $self->parseUrl($url))) {
63
+ return $original_url; // Not possible.
64
+ }
65
+ if (empty($url_parts['host'])) {
66
+ return $original_url; // Not possible.
67
+ }
68
+ $blog_domain = strtolower($url_parts['host']); // In the unfiltered URL.
69
+ $blog_path = $self->hostDirToken(false, false, !empty($url_parts['path']) ? $url_parts['path'] : '/');
70
+
71
+ if (!($blog_id = (integer) get_blog_id_from_url($blog_domain, $blog_path))) {
72
+ return $original_url; // Not possible.
73
+ }
74
+ if (!($domain = $self->domainMappingBlogDomain($blog_id)) || $domain === $blog_domain) {
75
+ return $original_url; // Not applicable.
76
+ }
77
+ $url_parts['host'] = $domain; // Filter the URL now.
78
+ if (!empty($url_parts['path']) && $url_parts['path'] !== '/') {
79
+ if (($host_base_dir_tokens = trim($self->hostBaseDirTokens(false, false, $url_parts['path']), '/'))) {
80
+ $url_parts['path'] = preg_replace('/^\/'.preg_quote($host_base_dir_tokens, '/').'(\/|$)/i', '${1}', $url_parts['path']);
81
+ }
82
+ }
83
+ return ($url = $self->unParseUrl($url_parts));
84
+ };
85
+
86
+ /*
87
+ * Filters a URL in order to remove domain mapping.
88
+ *
89
+ * @since 150821 Improving multisite compat.
90
+ *
91
+ * @param string $url The input URL to filter.
92
+ *
93
+ * @return string The filtered URL; else the original URL.
94
+ *
95
+ * @note The return value of this function is NOT cached, but inner portions are.
96
+ */
97
+ $self->domainMappingReverseUrlFilter = function ($url) use ($self) {
98
+ $original_url = (string) $url; // Preserve.
99
+ $url = trim((string) $url);
100
+
101
+ if (!is_multisite() || !$self->canConsiderDomainMapping()) {
102
+ return $original_url; // Not possible.
103
+ }
104
+ if (!$url || !($url_parts = $self->parseUrl($url))) {
105
+ return $original_url; // Not possible.
106
+ }
107
+ if (empty($url_parts['host'])) {
108
+ return $original_url; // Not possible.
109
+ }
110
+ if (!($blog_id = $self->domainMappingBlogId('', $url_parts['host']))) {
111
+ return $original_url; // No a domain in the map.
112
+ }
113
+ if (!($blog_details = $self->blogDetails($blog_id))) {
114
+ return $original_url; // Not possible.
115
+ }
116
+ $url_parts['host'] = $blog_details->domain; // Filter the URL now.
117
+ if (($host_base_dir_tokens = trim($self->hostBaseDirTokens(false, false, $blog_details->path), '/'))) {
118
+ $url_parts['path'] = '/'.$host_base_dir_tokens.'/'.ltrim(@$url_parts['path'], '/');
119
+ }
120
+ return ($url = $self->unParseUrl($url_parts));
121
+ };
122
+
123
+ /*
124
+ * Converts a host into a mapped blog ID.
125
+ *
126
+ * @since 150821 Improving multisite compat.
127
+ *
128
+ * @param string $url URL containing the domain to convert.
129
+ * @param string $domain The domain to convert. Override URL is provided.
130
+ *
131
+ * @return integer The mapped blog ID; else `0` on failure.
132
+ *
133
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
134
+ */
135
+ $self->domainMappingBlogId = function ($url = '', $domain = '') use ($self) {
136
+ $domain = (string) $domain;
137
+ $url = $domain ? '' : (string) $url;
138
+
139
+ if (!is_multisite() || !$self->canConsiderDomainMapping()) {
140
+ return 0; // Not possible/applicable.
141
+ }
142
+ if ($url === 'network' || $domain === 'network') {
143
+ $domain = (string) get_current_site()->domain;
144
+ }
145
+ if (!$domain && $url && $url !== 'network') {
146
+ $domain = $self->parseUrl($url, PHP_URL_HOST);
147
+ }
148
+ if (!$url && !$domain && ($blog_details = $self->blogDetails())) {
149
+ $domain = $blog_details->domain;
150
+ }
151
+ $domain = strtolower(preg_replace('/^www\./i', '', $domain));
152
+
153
+ if (!$domain || strpos($domain, '.') === false) {
154
+ return 0; // Not possible.
155
+ }
156
+ if (!is_null($blog_id = &$self->staticKey('domainMappingBlogId', $domain))) {
157
+ return $blog_id; // Already cached this.
158
+ }
159
+ $wpdb = $self->wpdb(); // WordPress database class.
160
+ $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet.
161
+ $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain?
162
+
163
+ if (!$enforcing_primary_domain) {
164
+ $blog_id = (integer) $wpdb->get_var('SELECT `blog_id` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') ORDER BY CHAR_LENGTH(`domain`) DESC, `active` DESC LIMIT 1');
165
+ } else {
166
+ $blog_id = (integer) $wpdb->get_var('SELECT `blog_id` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') AND `active` = \'1\' ORDER BY CHAR_LENGTH(`domain`) DESC LIMIT 1');
167
+ }
168
+ $wpdb->suppress_errors($suppressing_errors); // Restore.
169
+
170
+ return ($blog_id = (integer) $blog_id);
171
+ };
172
+
173
+ /*
174
+ * Converts a blog ID into a mapped domain.
175
+ *
176
+ * @since 150821 Improving multisite compat.
177
+ *
178
+ * @param integer $blog_id The blog ID.
179
+ *
180
+ * @param boolean $fallback Fallback on blog's domain?
181
+ *
182
+ * @return string The mapped domain, else an empty string.
183
+ *
184
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
185
+ */
186
+ $self->domainMappingBlogDomain = function ($blog_id = 0, $fallback = false) use ($self) {
187
+ if (!is_multisite() || !$self->canConsiderDomainMapping()) {
188
+ return ''; // Not possible/applicable.
189
+ }
190
+ if (($blog_id = (integer) $blog_id) < 0) {
191
+ $blog_id = (integer) get_current_site()->blog_id;
192
+ }
193
+ if (!$blog_id) {
194
+ $blog_id = (integer) get_current_blog_id();
195
+ }
196
+ if (!$blog_id || $blog_id < 0) {
197
+ return ''; // Not possible.
198
+ }
199
+ if (!is_null($domain = &$self->staticKey('domainMappingBlogDomain', $blog_id))) {
200
+ return $domain; // Already cached this.
201
+ }
202
+ $wpdb = $self->wpdb(); // WordPress database class.
203
+ $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet.
204
+ $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain?
205
+
206
+ if (!$enforcing_primary_domain) {
207
+ if ($self->isDomainMapping() === $blog_id) {
208
+ $domain = $self->hostToken();
209
+ $domain = preg_replace('/^www\./i', '', $domain);
210
+ $domain = (string) $wpdb->get_var('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') ORDER BY CHAR_LENGTH(`domain`) DESC LIMIT 1');
211
+ } elseif (($domains = $self->domainMappingBlogDomains($blog_id))) {
212
+ $domain = $domains[0]; // Use the first of all possible domains.
213
+ }
214
+ } else { // A single primary domain in this case; i.e., `active` = primary.
215
+ $domain = (string) $wpdb->get_var('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' AND `active` = \'1\' LIMIT 1');
216
+ }
217
+ if (!$domain && $fallback && ($blog_details = $self->blogDetails($blog_id))) {
218
+ $domain = $blog_details->domain; // Use original domain.
219
+ }
220
+ $wpdb->suppress_errors($suppressing_errors); // Restore.
221
+
222
+ return ($domain = strtolower((string) $domain));
223
+ };
224
+
225
+ /*
226
+ * Converts a blog ID into mapped domains (plural).
227
+ *
228
+ * @since 150821 Improving multisite compat.
229
+ *
230
+ * @param integer $blog_id The blog ID.
231
+ *
232
+ * @return array Mapped domains; else an empty array.
233
+ *
234
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
235
+ */
236
+ $self->domainMappingBlogDomains = function ($blog_id = 0) use ($self) {
237
+ if (!is_multisite() || !$self->canConsiderDomainMapping()) {
238
+ return array(); // Not possible/applicable.
239
+ }
240
+ if (($blog_id = (integer) $blog_id) < 0) {
241
+ $blog_id = (integer) get_current_site()->blog_id;
242
+ }
243
+ if (!$blog_id) {
244
+ $blog_id = (integer) get_current_blog_id();
245
+ }
246
+ if (!$blog_id || $blog_id < 0) {
247
+ return array(); // Not possible.
248
+ }
249
+ if (!is_null($domains = &$self->staticKey('domainMappingBlogDomains', $blog_id))) {
250
+ return $domains; // Already cached this.
251
+ }
252
+ $wpdb = $self->wpdb(); // WordPress database class.
253
+ $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet.
254
+ $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain?
255
+
256
+ if (!$enforcing_primary_domain) { // Not enforcing a primary domain, so let's pull all of the domains.
257
+ $domains = $wpdb->get_col('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' ORDER BY `active` DESC');
258
+ } else { // Primary domains in this case; i.e., `active` = primary.
259
+ $domains = $wpdb->get_col('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' AND `active` = \'1\'');
260
+ }
261
+ $wpdb->suppress_errors($suppressing_errors); // Restore.
262
+
263
+ return ($domains = array_unique(array_map('strtolower', (array) $domains)));
264
+ };
src/includes/closures/Shared/EscapeUtils.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Escape single quotes.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param string $string Input string to escape.
10
+ * @param integer $times Optional. Defaults to one escape char; e.g. `\'`.
11
+ * If you need to escape more than once, set this to something > `1`.
12
+ *
13
+ * @return string Escaped string; e.g. `Raam\'s the lead developer`.
14
+ */
15
+ $self->escSq = function ($string, $times = 1) use ($self) {
16
+ return str_replace("'", str_repeat('\\', abs($times))."'", (string) $string);
17
+ };
src/includes/closures/Shared/FsUtils.php ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Normalizes directory/file separators.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param string $dir_file Directory/file path.
10
+ *
11
+ * @param boolean $allow_trailing_slash Defaults to FALSE.
12
+ * If TRUE; and `$dir_file` contains a trailing slash; we'll leave it there.
13
+ *
14
+ * @return string Normalized directory/file path.
15
+ */
16
+ $self->nDirSeps = function ($dir_file, $allow_trailing_slash = false) use ($self) {
17
+ $dir_file = (string) $dir_file;
18
+
19
+ if (!isset($dir_file[0])) {
20
+ return ''; // Catch empty string.
21
+ }
22
+ if (strpos($dir_file, '://' !== false)) {
23
+ if (preg_match('/^(?P<stream_wrapper>[a-zA-Z0-9]+)\:\/\//', $dir_file, $stream_wrapper)) {
24
+ $dir_file = preg_replace('/^(?P<stream_wrapper>[a-zA-Z0-9]+)\:\/\//', '', $dir_file);
25
+ }
26
+ }
27
+ if (strpos($dir_file, ':' !== false)) {
28
+ if (preg_match('/^(?P<drive_letter>[a-zA-Z])\:[\/\\\\]/', $dir_file)) {
29
+ $dir_file = preg_replace_callback('/^(?P<drive_letter>[a-zA-Z])\:[\/\\\\]/', create_function('$m', 'return strtoupper($m[0]);'), $dir_file);
30
+ }
31
+ }
32
+ $dir_file = preg_replace('/\/+/', '/', str_replace(array(DIRECTORY_SEPARATOR, '\\', '/'), '/', $dir_file));
33
+ $dir_file = ($allow_trailing_slash) ? $dir_file : rtrim($dir_file, '/'); // Strip trailing slashes.
34
+
35
+ if (!empty($stream_wrapper[0])) {
36
+ $dir_file = strtolower($stream_wrapper[0]).$dir_file;
37
+ }
38
+ return $dir_file; // Normalized now.
39
+ };
40
+
41
+ /*
42
+ * Acquires system tmp directory path.
43
+ *
44
+ * @since 150422 Rewrite.
45
+ *
46
+ * @return string System tmp directory path; else an empty string.
47
+ */
48
+ $self->getTmpDir = function () use ($self) {
49
+ if (!is_null($dir = &$self->staticKey('getTmpDir'))) {
50
+ return $dir; // Already cached this.
51
+ }
52
+ $possible_dirs = array(); // Initialize.
53
+
54
+ if (defined('WP_TEMP_DIR')) {
55
+ $possible_dirs[] = (string) WP_TEMP_DIR;
56
+ }
57
+ if ($self->functionIsPossible('sys_get_temp_dir')) {
58
+ $possible_dirs[] = (string) sys_get_temp_dir();
59
+ }
60
+ $possible_dirs[] = (string) ini_get('upload_tmp_dir');
61
+
62
+ if (!empty($_SERVER['TEMP'])) {
63
+ $possible_dirs[] = (string) $_SERVER['TEMP'];
64
+ }
65
+ if (!empty($_SERVER['TMPDIR'])) {
66
+ $possible_dirs[] = (string) $_SERVER['TMPDIR'];
67
+ }
68
+ if (!empty($_SERVER['TMP'])) {
69
+ $possible_dirs[] = (string) $_SERVER['TMP'];
70
+ }
71
+ if (stripos(PHP_OS, 'win') === 0) {
72
+ $possible_dirs[] = 'C:/Temp';
73
+ }
74
+ if (stripos(PHP_OS, 'win') !== 0) {
75
+ $possible_dirs[] = '/tmp';
76
+ }
77
+ if (defined('WP_CONTENT_DIR')) {
78
+ $possible_dirs[] = (string) WP_CONTENT_DIR;
79
+ }
80
+ foreach ($possible_dirs as $_key => $_dir) {
81
+ if (($_dir = trim((string) $_dir)) && @is_dir($_dir) && @is_writable($_dir)) {
82
+ return ($dir = $self->nDirSeps($_dir));
83
+ }
84
+ }
85
+ unset($_key, $_dir); // Housekeeping.
86
+
87
+ return ($dir = '');
88
+ };
89
+
90
+ /*
91
+ * Finds absolute server path to `/wp-config.php` file.
92
+ *
93
+ * @since 150422 Rewrite.
94
+ *
95
+ * @return string Absolute server path to `/wp-config.php` file;
96
+ * else an empty string if unable to locate the file.
97
+ */
98
+ $self->findWpConfigFile = function () use ($self) {
99
+ if (!is_null($file = &$self->staticKey('findWpConfigFile'))) {
100
+ return $file; // Already cached this.
101
+ }
102
+ $file = ''; // Initialize.
103
+
104
+ if (is_file($abspath_wp_config = ABSPATH.'wp-config.php')) {
105
+ $file = $abspath_wp_config;
106
+ } elseif (is_file($dirname_abspath_wp_config = dirname(ABSPATH).'/wp-config.php')) {
107
+ $file = $dirname_abspath_wp_config;
108
+ }
109
+ return $file;
110
+ };
111
+
112
+ /*
113
+ * Adds a tmp name suffix to a directory/file path.
114
+ *
115
+ * @since 150422 Rewrite.
116
+ *
117
+ * @param string $dir_file An input directory or file path.
118
+ *
119
+ * @return string The original `$dir_file` with a tmp name suffix.
120
+ */
121
+ $self->addTmpSuffix = function ($dir_file) use ($self) {
122
+ $dir_file = (string) $dir_file;
123
+ $dir_file = rtrim($dir_file, DIRECTORY_SEPARATOR.'\\/');
124
+
125
+ return $dir_file.'-'.str_replace('.', '', uniqid('', true)).'-tmp';
126
+ };
127
+
128
+ /*
129
+ * Recursive directory iterator based on a regex pattern.
130
+ *
131
+ * @since 150422 Rewrite.
132
+ *
133
+ * @param string $dir An absolute server directory path.
134
+ * @param string $regex A regex pattern; compares to each full file path.
135
+ *
136
+ * @return \RegexIterator Navigable with {@link \foreach()}; where each item
137
+ * is a {@link \RecursiveDirectoryIterator}.
138
+ */
139
+ $self->dirRegexIteration = function ($dir, $regex = '') use ($self) {
140
+ $dir = (string) $dir;
141
+ $regex = (string) $regex;
142
+
143
+ $dir_iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_SELF | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS);
144
+ $iterator_iterator = new \RecursiveIteratorIterator($dir_iterator, \RecursiveIteratorIterator::CHILD_FIRST);
145
+
146
+ if ($regex && $regex !== '/.*/' && $regex !== '/.+/') { // Apply regex filter?
147
+ // @TODO Optimize calls to this method in order to avoid the regex iterator when not necessary.
148
+ return new \RegexIterator($iterator_iterator, $regex, \RegexIterator::MATCH, \RegexIterator::USE_KEY);
149
+ }
150
+ return $iterator_iterator; // Iterate everything.
151
+ };
152
+
153
+ /*
154
+ * Abbreviated byte notation for file sizes.
155
+ *
156
+ * @since 151002 Adding a few statistics.
157
+ *
158
+ * @param float $bytes File size in bytes. A (float) value.
159
+ * @param integer $precision Number of decimals to use.
160
+ *
161
+ * @return string Byte notation.
162
+ */
163
+ $self->bytesAbbr = function ($bytes, $precision = 2) use ($self) {
164
+ $bytes = max(0.0, (float) $bytes);
165
+ $precision = max(0, (integer) $precision);
166
+ $units = array('bytes', 'kbs', 'MB', 'GB', 'TB');
167
+
168
+ $power = floor(($bytes ? log($bytes) : 0) / log(1024));
169
+ $abbr_bytes = round($bytes / pow(1024, $power), $precision);
170
+ $abbr = $units[min($power, count($units) - 1)];
171
+
172
+ if ($abbr_bytes === (float) 1 && $abbr === 'bytes') {
173
+ $abbr = 'byte'; // Quick fix.
174
+ } elseif ($abbr_bytes === (float) 1 && $abbr === 'kbs') {
175
+ $abbr = 'kb'; // Quick fix.
176
+ }
177
+ return $abbr_bytes.' '.$abbr;
178
+ };
179
+
180
+ /*
181
+ * Converts an abbreviated byte notation into bytes.
182
+ *
183
+ * @since 151002 Adding a few statistics.
184
+ *
185
+ * @param string $string A string value in byte notation.
186
+ *
187
+ * @return float A float indicating the number of bytes.
188
+ */
189
+ $self->abbrBytes = function ($string) use ($self) {
190
+ $string = (string) $string;
191
+ $regex = '/^(?P<value>[0-9\.]+)\s*(?P<modifier>bytes|byte|kbs|kb|k|mb|m|gb|g|tb|t)$/i';
192
+
193
+ if (!preg_match($regex, $string, $_m)) {
194
+ return (float) 0;
195
+ }
196
+ $value = (float) $_m['value'];
197
+ $modifier = strtolower($_m['modifier']);
198
+ unset($_m); // Housekeeping.
199
+
200
+ switch ($modifier) {
201
+ case 't':
202
+ case 'tb':
203
+ $value *= 1024;
204
+ // Fall through.
205
+ case 'g':
206
+ case 'gb':
207
+ $value *= 1024;
208
+ // Fall through.
209
+ case 'm':
210
+ case 'mb':
211
+ $value *= 1024;
212
+ // Fall through.
213
+ case 'k':
214
+ case 'kb':
215
+ case 'kbs':
216
+ $value *= 1024;
217
+ }
218
+ return (float) $value;
219
+ };
220
+
221
+ /*
222
+ * Directory stats.
223
+ *
224
+ * @since 151002 Adding a few statistics.
225
+ *
226
+ * @param string $dir An absolute server directory path.
227
+ * @param string $regex A regex pattern; compares to each full file path.
228
+ * @param boolean $include_paths Include array of all scanned file paths?
229
+ * @param boolean $check_disk Also check disk statistics?
230
+ * @param boolean $no_cache Do not read/write cache?
231
+ *
232
+ * @return array Directory stats.
233
+ */
234
+ $self->getDirRegexStats = function ($dir, $regex = '', $include_paths = false, $check_disk = true, $no_cache = false) use ($self) {
235
+ $dir = (string) $dir; // Force string.
236
+ $cache_keys = array($dir, $regex, $include_paths, $check_disk);
237
+ if (!$no_cache && !is_null($stats = &$self->staticKey('getDirRegexStats', $cache_keys))) {
238
+ return $stats; // Already cached this.
239
+ }
240
+ $stats = array(
241
+ 'total_size' => 0,
242
+ 'total_resources' => 0,
243
+ 'total_links_files' => 0,
244
+
245
+ 'total_links' => 0,
246
+ 'link_subpaths' => array(),
247
+
248
+ 'total_files' => 0,
249
+ 'file_subpaths' => array(),
250
+
251
+ 'total_dirs' => 0,
252
+ 'dir_subpaths' => array(),
253
+
254
+ 'disk_total_space' => 0,
255
+ 'disk_free_space' => 0,
256
+ );
257
+ if (!$dir || !is_dir($dir)) {
258
+ return $stats; // Not possible.
259
+ }
260
+ $short_name_lc = strtolower(SHORT_NAME); // Once only.
261
+
262
+ foreach ($self->dirRegexIteration($dir, $regex) as $_resource) {
263
+ $_resource_sub_path = $_resource->getSubpathname();
264
+ $_resource_basename = basename($_resource_sub_path);
265
+
266
+ if ($_resource_basename === '.DS_Store') {
267
+ continue; // Ignore `.htaccess`.
268
+ }
269
+ if ($_resource_basename === '.htaccess') {
270
+ continue; // Ignore `.htaccess`.
271
+ }
272
+ if (stripos($_resource_sub_path, $short_name_lc.'-') === 0) {
273
+ continue; // Ignore [SHORT_NAME] files in base.
274
+ }
275
+ switch ($_resource->getType()) { // `link`, `file`, `dir`.
276
+ case 'link':
277
+ if ($include_paths) {
278
+ $stats['link_subpaths'][] = $_sub_path;
279
+ }
280
+ ++$stats['total_resources'];
281
+ ++$stats['total_links_files'];
282
+ ++$stats['total_links'];
283
+
284
+ break; // Break switch.
285
+
286
+ case 'file':
287
+ if ($include_paths) {
288
+ $stats['file_subpaths'][] = $_sub_path;
289
+ }
290
+ $stats['total_size'] += $_resource->getSize();
291
+ ++$stats['total_resources'];
292
+ ++$stats['total_links_files'];
293
+ ++$stats['total_files'];
294
+
295
+ break; // Break switch.
296
+
297
+ case 'dir':
298
+ if ($include_paths) {
299
+ $stats['dir_subpaths'][] = $_sub_path;
300
+ }
301
+ ++$stats['total_resources'];
302
+ ++$stats['total_dirs'];
303
+
304
+ break; // Break switch.
305
+ }
306
+ }
307
+ unset($_resource, $_resource_sub_path, $_resource_basename); // Housekeeping.
308
+
309
+ if ($check_disk) { // Check disk also?
310
+ $stats['disk_total_space'] = disk_total_space($dir);
311
+ $stats['disk_free_space'] = disk_free_space($dir);
312
+ }
313
+ return $stats;
314
+ };
315
+
316
+ /*
317
+ * Apache `.htaccess` rules that deny public access to the contents of a directory.
318
+ *
319
+ * @since 150422 Rewrite.
320
+ *
321
+ * @var string `.htaccess` fules.
322
+ */
323
+ $self->htaccess_deny = "<IfModule authz_core_module>\n\tRequire all denied\n</IfModule>\n<IfModule !authz_core_module>\n\tdeny from all\n</IfModule>";
src/includes/closures/Shared/HookUtils.php ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Array of hooks.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @type array An array of hooks.
10
+ */
11
+ $self->hooks = array();
12
+
13
+ /*
14
+ * Assigns an ID to each callable attached to a hook/filter.
15
+ *
16
+ * @since 150422 Rewrite.
17
+ *
18
+ * @param string|callable|mixed $function A string or a callable.
19
+ *
20
+ * @return string Hook ID for the given `$function`.
21
+ *
22
+ * @throws \Exception If the hook/function is invalid (i.e. it's not possible to generate an ID).
23
+ */
24
+ $self->hookId = function ($function) use ($self) {
25
+ if (is_string($function)) {
26
+ return $function;
27
+ }
28
+ if (is_object($function)) {
29
+ $function = array($function, '');
30
+ } else {
31
+ $function = (array) $function;
32
+ }
33
+ if (isset($function[0], $function[1])) {
34
+ if (is_object($function[0])) {
35
+ return spl_object_hash($function[0]).$function[1];
36
+ } elseif (is_string($function[0])) {
37
+ return $function[0].'::'.$function[1];
38
+ }
39
+ }
40
+ throw new \Exception(__('Invalid hook.', 'comet-cache'));
41
+ };
42
+
43
+ /*
44
+ * Adds a new hook (works with both actions & filters).
45
+ *
46
+ * @since 150422 Rewrite.
47
+ *
48
+ * @param string $hook The name of a hook to attach to.
49
+ * @param string|callable|mixed $function A string or a callable.
50
+ * @param integer $priority Hook priority; defaults to `10`.
51
+ * @param integer $accepted_args Max number of args that should be passed to the `$function`.
52
+ *
53
+ * @return boolean This always returns a `TRUE` value.
54
+ */
55
+ $self->addHook = function ($hook, $function, $priority = 10, $accepted_args = 1) use ($self) {
56
+ $hook = (string) $hook;
57
+ if (stripos($hook, 'zencache') === 0) {
58
+ $hook = GLOBAL_NS.substr($hook, strlen('zencache'));
59
+ }
60
+ $priority = (integer) $priority;
61
+ $accepted_args = max(0, (integer) $accepted_args);
62
+ $hook_id = $self->hookId($function);
63
+
64
+ $self->hooks[$hook][$priority][$hook_id] = array(
65
+ 'function' => $function,
66
+ 'accepted_args' => $accepted_args,
67
+ );
68
+ return true; // Always returns true.
69
+ };
70
+
71
+ /*
72
+ * Adds a new action hook.
73
+ *
74
+ * @since 150422 Rewrite.
75
+ *
76
+ * @return boolean This always returns a `TRUE` value.
77
+ */
78
+ $self->addAction = function () use ($self) {
79
+ return call_user_func_array(array($self, 'addHook'), func_get_args());
80
+ };
81
+ $self->add_action = $self->addAction; // Back compat.
82
+
83
+ /*
84
+ * Adds a new filter.
85
+ *
86
+ * @since 150422 Rewrite.
87
+ *
88
+ * @return boolean This always returns a `TRUE` value.
89
+ */
90
+ $self->addFilter = function () use ($self) {
91
+ return call_user_func_array(array($self, 'addHook'), func_get_args());
92
+ };
93
+ $self->add_filter = $self->addFilter; // Back compat.
94
+
95
+ /*
96
+ * Removes a hook (works with both actions & filters).
97
+ *
98
+ * @since 150422 Rewrite.
99
+ *
100
+ * @param string $hook The name of a hook to remove.
101
+ * @param string|callable|mixed $function A string or a callable.
102
+ * @param integer $priority Hook priority; defaults to `10`.
103
+ *
104
+ * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason.
105
+ */
106
+ $self->removeHook = function ($hook, $function, $priority = 10) use ($self) {
107
+ $hook = (string) $hook;
108
+ if (stripos($hook, 'zencache') === 0) {
109
+ $hook = GLOBAL_NS.substr($hook, strlen('zencache'));
110
+ }
111
+ $priority = (integer) $priority;
112
+ $hook_id = $self->hookId($function);
113
+
114
+ if (!isset($self->hooks[$hook][$priority][$hook_id])) {
115
+ return false; // Nothing to remove.
116
+ }
117
+ unset($self->hooks[$hook][$priority][$hook_id]);
118
+
119
+ if (!$self->hooks[$hook][$priority]) {
120
+ unset($self->hooks[$hook][$priority]);
121
+ }
122
+ return true; // Existed before it was removed.
123
+ };
124
+
125
+ /*
126
+ * Removes an action.
127
+ *
128
+ * @since 150422 Rewrite.
129
+ *
130
+ * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason.
131
+ */
132
+ $self->removeAction = function () use ($self) {
133
+ return call_user_func_array(array($self, 'removeHook'), func_get_args());
134
+ };
135
+
136
+ /*
137
+ * Removes a filter.
138
+ *
139
+ * @since 150422 Rewrite.
140
+ *
141
+ * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason.
142
+ */
143
+ $self->removeFilter = function () use ($self) {
144
+ return call_user_func_array(array($self, 'removeHook'), func_get_args());
145
+ };
146
+
147
+ /*
148
+ * Runs any callables attached to an action.
149
+ *
150
+ * @since 150422 Rewrite.
151
+ *
152
+ * @param string $hook The name of an action hook.
153
+ */
154
+ $self->doAction = function ($hook) use ($self) {
155
+ $hook = (string) $hook;
156
+ if (empty($self->hooks[$hook])) {
157
+ return; // No hooks.
158
+ }
159
+ $hook_actions = $self->hooks[$hook];
160
+ $args = func_get_args();
161
+ ksort($hook_actions);
162
+
163
+ foreach ($hook_actions as $_hook_action) {
164
+ foreach ($_hook_action as $_action) {
165
+ if (!isset($_action['function'], $_action['accepted_args'])) {
166
+ continue; // Not a valid filter in this case.
167
+ }
168
+ call_user_func_array($_action['function'], array_slice($args, 1, $_action['accepted_args']));
169
+ }
170
+ }
171
+ unset($_hook_action, $_action); // Housekeeping.
172
+ };
173
+
174
+ /*
175
+ * Runs any callables attached to a filter.
176
+ *
177
+ * @since 150422 Rewrite.
178
+ *
179
+ * @param string $hook The name of a filter hook.
180
+ * @param mixed $value The value to filter.
181
+ *
182
+ * @return mixed The filtered `$value`.
183
+ */
184
+ $self->applyFilters = function ($hook, $value) use ($self) {
185
+ $hook = (string) $hook;
186
+ if (empty($self->hooks[$hook])) {
187
+ return $value; // No hooks.
188
+ }
189
+ $hook_filters = $self->hooks[$hook];
190
+ $args = func_get_args();
191
+ ksort($hook_filters);
192
+
193
+ foreach ($hook_filters as $_hook_filter) {
194
+ foreach ($_hook_filter as $_filter) {
195
+ if (!isset($_filter['function'], $_filter['accepted_args'])) {
196
+ continue; // Not a valid filter in this case.
197
+ }
198
+ $args[1] = $value; // Continously update the argument `$value`.
199
+ $value = call_user_func_array($_filter['function'], array_slice($args, 1, $_filter['accepted_args']));
200
+ }
201
+ }
202
+ unset($_hook_filter, $_filter); // Housekeeping.
203
+
204
+ return $value; // With applied filters.
205
+ };
206
+
207
+ /*
208
+ * Does an action w/ back compat. for ZenCache.
209
+ *
210
+ * @since 150422 Rewrite.
211
+ *
212
+ * @param string $hook The hook to apply.
213
+ */
214
+ $self->doWpAction = function ($hook) use ($self) {
215
+ $hook = (string) $hook;
216
+ $args = func_get_args();
217
+ call_user_func_array('do_action', $args);
218
+
219
+ if (stripos($hook, GLOBAL_NS) === 0) {
220
+ $zencache_filter = 'zencache'.substr($hook, strlen(GLOBAL_NS));
221
+ $zencache_args = $args; // Use a copy of the args.
222
+ $zencache_args[0] = $zencache_filter;
223
+ call_user_func_array('do_action', $zencache_args);
224
+ }
225
+ };
226
+
227
+ /*
228
+ * Applies filters w/ back compat. for ZenCache.
229
+ *
230
+ * @since 150422 Rewrite.
231
+ *
232
+ * @param string $hook The hook to apply.
233
+ *
234
+ * @return mixed The filtered value.
235
+ */
236
+ $self->applyWpFilters = function ($hook) use ($self) {
237
+ $hook = (string) $hook;
238
+ $args = func_get_args();
239
+ $value = call_user_func_array('apply_filters', $args);
240
+
241
+ if (stripos($hook, GLOBAL_NS) === 0) {
242
+ $zencache_hook = 'zencache'.substr($hook, strlen(GLOBAL_NS));
243
+ $zencache_args = $args; // Use a copy of the args.
244
+ $zencache_args[0] = $zencache_hook;
245
+ $zencache_args[1] = $value; // Filtered value.
246
+ $value = call_user_func_array('apply_filters', $zencache_args);
247
+ }
248
+ return $value; // Filtered value.
249
+ };
src/includes/closures/Shared/HttpUtils.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Current HTTP protocol.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @return string Current HTTP protocol.
10
+ */
11
+ $self->httpProtocol = function () use ($self) {
12
+ if (!is_null($protocol = &$self->staticKey('httpProtocol'))) {
13
+ return $protocol; // Already cached this.
14
+ }
15
+ if (!empty($_SERVER['SERVER_PROTOCOL']) && is_string($_SERVER['SERVER_PROTOCOL'])) {
16
+ $protocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
17
+ }
18
+ if (!$protocol || stripos($protocol, 'HTTP/') !== 0) {
19
+ $protocol = 'HTTP/1.0'; // Default value.
20
+ }
21
+ return $protocol;
22
+ };
23
+
24
+ /*
25
+ * PHP {@link headers_list()} + HTTP status.
26
+ *
27
+ * @since 150422 Rewrite.
28
+ *
29
+ * @return array PHP {@link headers_list()} + HTTP status.
30
+ *
31
+ * @warning Do NOT call until end of script execution.
32
+ */
33
+ $self->headersList = function () use ($self) {
34
+ if (!is_null($headers = &$self->staticKey('headersList'))) {
35
+ return $headers; // Already cached this.
36
+ }
37
+ $headers = headers_list(); // Lacks status.
38
+
39
+ if (($status = (string) $self->httpStatus())) {
40
+ array_unshift($headers, $self->httpProtocol().' '.$status);
41
+ }
42
+ return $headers;
43
+ };
44
+
45
+ /*
46
+ * PHP {@link headers_list()} + HTTP status.
47
+ *
48
+ * @since 150422 Rewrite.
49
+ *
50
+ * @return array PHP {@link headers_list()} + HTTP status.
51
+ *
52
+ * @warning Do NOT call until end of script execution.
53
+ */
54
+ $self->cacheableHeadersList = function () use ($self) {
55
+ if (!is_null($headers = &$self->staticKey('cacheableHeadersList'))) {
56
+ return $headers; // Already cached this.
57
+ }
58
+ $headers = headers_list(); // Lacks status.
59
+
60
+ $cacheable_headers = array(
61
+ 'Access-Control-Allow-Origin',
62
+ 'Accept-Ranges',
63
+ 'Age',
64
+ 'Allow',
65
+ 'Cache-Control',
66
+ 'Connection',
67
+ 'Content-Encoding',
68
+ 'Content-Language',
69
+ 'Content-Length',
70
+ 'Content-Location',
71
+ 'Content-MD5',
72
+ 'Content-Disposition',
73
+ 'Content-Range',
74
+ 'Content-Type',
75
+ 'Date',
76
+ 'ETag',
77
+ 'Expires',
78
+ 'Last-Modified',
79
+ 'Link',
80
+ 'Location',
81
+ 'P3P',
82
+ 'Pragma',
83
+ 'Proxy-Authenticate',
84
+ 'Refresh',
85
+ 'Retry-After',
86
+ 'Server',
87
+ 'Status',
88
+ 'Strict-Transport-Security',
89
+ 'Trailer',
90
+ 'Transfer-Encoding',
91
+ 'Upgrade',
92
+ 'Vary',
93
+ 'Via',
94
+ 'Warning',
95
+ 'WWW-Authenticate',
96
+ 'X-Frame-Options',
97
+ 'Public-Key-Pins',
98
+ 'X-XSS-Protection',
99
+ 'Content-Security-Policy',
100
+ 'X-Content-Security-Policy',
101
+ 'X-WebKit-CSP',
102
+ 'X-Content-Type-Options',
103
+ 'X-Powered-By',
104
+ 'X-UA-Compatible',
105
+ );
106
+ $cacheable_headers = array_map('strtolower', $cacheable_headers);
107
+
108
+ foreach ($headers as $_key => $_header) {
109
+ $_header = strtolower((string) strstr($_header, ':', true));
110
+ if (!$_header || !in_array($_header, $cacheable_headers, true)) {
111
+ unset($headers[$_key]);
112
+ }
113
+ }
114
+ unset($_key, $_header); // Housekeeping.
115
+
116
+ if (($status = (string) $self->httpStatus())) {
117
+ array_unshift($headers, $self->httpProtocol().' '.$status);
118
+ }
119
+ return $headers;
120
+ };
121
+
122
+ /*
123
+ * HTTP status code.
124
+ *
125
+ * @since 150422 Rewrite.
126
+ *
127
+ * @return integer HTTP status code.
128
+ *
129
+ * @warning Do NOT call until end of script execution.
130
+ *
131
+ * @note Automatically updates HTTP status-related flags.
132
+ */
133
+ $self->httpStatus = function () use ($self) {
134
+ if (!is_null($status = &$self->staticKey('httpStatus'))) {
135
+ return $status; // Already cached this.
136
+ }
137
+ $status = 0; // Initialize.
138
+ $has_property_is_404 = property_exists($self, 'is_404');
139
+ $has_property_http_status = property_exists($self, 'http_status');
140
+
141
+ if ($has_property_is_404 && $self->{'is_404'}) {
142
+ $status = 404; // WordPress said so.
143
+ } elseif ($self->functionIsPossible('http_response_code') && ($code = (integer) http_response_code())) {
144
+ $status = (integer) $code; // {@link \http_response_code()} available since PHP v5.4.
145
+ } elseif ($has_property_http_status && (integer) $self->{'http_status'}) {
146
+ $status = (integer) $self->{'http_status'}; // {@link \status_header()} filter.
147
+ }
148
+ if ($status && $has_property_http_status) {
149
+ $self->{'http_status'} = $status; // Prefer over {@link status_header()}.
150
+ }
151
+ if ($status === 404 && $has_property_is_404) {
152
+ $self->{'is_404'} = true; // Prefer over {@link is_404()}.
153
+ }
154
+ return $status;
155
+ };
156
+ /*
157
+ * Sends no-cache headers.
158
+ *
159
+ * @since 151220 Enhancing no-cache headers.
160
+ */
161
+ $self->sendNoCacheHeaders = function() use($self) {
162
+ header_remove('Last-Modified');
163
+ header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
164
+ header('Cache-Control: no-cache, must-revalidate, max-age=0');
165
+ header('Pragma: no-cache');
166
+ };
src/includes/closures/Shared/I18nUtils.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * `X file` or `X files`, translated w/ singlular/plural context.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param integer $counter Total files; i.e. the counter.
10
+ *
11
+ * @return string The phrase `X file` or `X files`.
12
+ */
13
+ $self->i18nFiles = function ($counter) use ($self) {
14
+ $counter = (integer) $counter;
15
+ return sprintf(_n('%1$s file', '%1$s files', $counter, 'comet-cache'), $counter);
16
+ };
17
+
18
+ /*
19
+ * `X directory` or `X directories`, translated w/ singlular/plural context.
20
+ *
21
+ * @since 150422 Rewrite.
22
+ *
23
+ * @param integer $counter Total directories; i.e. the counter.
24
+ *
25
+ * @return string The phrase `X directory` or `X directories`.
26
+ */
27
+ $self->i18nDirs = function ($counter) use ($self) {
28
+ $counter = (integer) $counter;
29
+ return sprintf(_n('%1$s directory', '%1$s directories', $counter, 'comet-cache'), $counter);
30
+ };
31
+
32
+ /*
33
+ * `X file/directory` or `X files/directories`, translated w/ singlular/plural context.
34
+ *
35
+ * @since 150422 Rewrite.
36
+ *
37
+ * @param integer $counter Total files/directories; i.e. the counter.
38
+ *
39
+ * @return string The phrase `X file/directory` or `X files/directories`.
40
+ */
41
+ $self->i18nFilesDirs = function ($counter) use ($self) {
42
+ $counter = (integer) $counter;
43
+ return sprintf(_n('%1$s file/directory', '%1$s files/directories', $counter, 'comet-cache'), $counter);
44
+ };
src/includes/closures/Shared/IpAddrUtils.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Get the current visitor's real IP address.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @return string Real IP address, else `unknown` on failure.
10
+ *
11
+ * @note This supports both IPv4 and IPv6 addresses.
12
+ * @note See my tests against this here: http://3v4l.org/fVWUp
13
+ */
14
+ $self->currentIp = function () use ($self) {
15
+ if (!is_null($ip = &$self->staticKey('currentIp'))) {
16
+ return $ip; // Already cached this.
17
+ }
18
+ $sources = array(
19
+ 'HTTP_CF_CONNECTING_IP',
20
+ 'HTTP_CLIENT_IP',
21
+ 'HTTP_X_FORWARDED_FOR',
22
+ 'HTTP_X_FORWARDED',
23
+ 'HTTP_X_CLUSTER_CLIENT_IP',
24
+ 'HTTP_FORWARDED_FOR',
25
+ 'HTTP_FORWARDED',
26
+ 'HTTP_VIA',
27
+ 'REMOTE_ADDR',
28
+ );
29
+ $sources = $self->applyFilters(GLOBAL_NS.'\\share::current_ip_sources', $sources);
30
+ $sources = $self->applyFilters(GLOBAL_NS.'_current_ip_sources', $sources);
31
+
32
+ $prioritize_remote_addr = false; // Off by default; can be filtered however.
33
+ $prioritize_remote_addr = $self->applyFilters(GLOBAL_NS.'\\share::current_ip_prioritize_remote_addr', $prioritize_remote_addr);
34
+ $prioritize_remote_addr = $self->applyFilters(GLOBAL_NS.'_current_ip_prioritize_remote_addr', $prioritize_remote_addr);
35
+
36
+ if (!empty($_SERVER['REMOTE_ADDR']) && $prioritize_remote_addr) {
37
+ if (($_valid_public_ip = $self->validPublicIp((string) $_SERVER['REMOTE_ADDR']))) {
38
+ return ($ip = $_valid_public_ip);
39
+ }
40
+ unset($_valid_public_ip); // Housekeeping.
41
+ }
42
+ foreach ($sources as $_key => $_source) {
43
+ if (!empty($_SERVER[$_source])) {
44
+ if (($_valid_public_ip = $self->validPublicIp((string) $_SERVER[$_source]))) {
45
+ return ($ip = $_valid_public_ip);
46
+ }
47
+ }
48
+ unset($_key, $_source, $_valid_public_ip); // Housekeeping.
49
+ }
50
+ if (!empty($_SERVER['REMOTE_ADDR'])) {
51
+ return ($ip = strtolower((string) $_SERVER['REMOTE_ADDR']));
52
+ }
53
+ return ($ip = 'unknown'); // Not possible.
54
+ };
55
+
56
+ /*
57
+ * Gets a valid/public IP address.
58
+ *
59
+ * @since 150422 Rewrite.
60
+ *
61
+ * @param string $list_of_possible_ips A single IP, or a comma-delimited list of IPs.
62
+ *
63
+ * @return string A valid/public IP address (if one is found), else an empty string.
64
+ *
65
+ * @note This supports both IPv4 and IPv6 addresses.
66
+ * @note See my tests against this here: http://3v4l.org/fVWUp
67
+ */
68
+ $self->validPublicIp = function ($list_of_possible_ips) use ($self) {
69
+ if (!$list_of_possible_ips || !is_string($list_of_possible_ips)) {
70
+ return ''; // Empty or invalid data.
71
+ }
72
+ if (!($list_of_possible_ips = trim($list_of_possible_ips))) {
73
+ return ''; // Not possible; i.e., empty string.
74
+ }
75
+ foreach (preg_split('/[\s;,]+/', $list_of_possible_ips, -1, PREG_SPLIT_NO_EMPTY) as $_key => $_possible_ip) {
76
+ if (($_valid_public_ip = filter_var(strtolower($_possible_ip), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))) {
77
+ return $_valid_public_ip; // A valid public IPv4 or IPv6 address.
78
+ }
79
+ }
80
+ unset($_key, $_possible_ip, $_valid_public_ip); // Housekeeping.
81
+
82
+ return ''; // Default return value.
83
+ };
src/includes/closures/Shared/PatternUtils.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Convert line-delimited patterns to a regex.
6
+ *
7
+ * @since 151114 Enhancing exclusion pattern support.
8
+ *
9
+ * @param string $patterns Line-delimited list of patterns.
10
+ *
11
+ * @return string A `/(?:list|of|regex)/i` patterns.
12
+ */
13
+ $self->lineDelimitedPatternsToRegex = function ($patterns) use ($self) {
14
+ $regex = ''; // Initialize list of regex patterns.
15
+ $patterns = (string) $patterns;
16
+
17
+ if (($patterns = preg_split('/['."\r\n".']+/', $patterns, -1, PREG_SPLIT_NO_EMPTY))) {
18
+ $regex = '/(?:'.implode('|', array_map($self->wdRegexToActualRegexFrag, $patterns)).')/i';
19
+ }
20
+ return $regex;
21
+ };
22
+
23
+ /*
24
+ * Convert watered-down regex to actual regex.
25
+ *
26
+ * @since 151114 Enhancing exclusion pattern support.
27
+ *
28
+ * @param string $string Input watered-down regex to convert.
29
+ *
30
+ * @return string Actual regex pattern after conversion.
31
+ */
32
+ $self->wdRegexToActualRegexFrag = function ($string) use ($self) {
33
+ return preg_replace(
34
+ array(
35
+ '/\\\\\^/',
36
+ '/\\\\\*\\\\\*/',
37
+ '/\\\\\*/',
38
+ '/\\\\\$/',
39
+ ),
40
+ array(
41
+ '^', // Beginning of line.
42
+ '.*?', // Zero or more chars.
43
+ '[^\/]*?', // Zero or more chars != /.
44
+ '$', // End of line.
45
+ ),
46
+ preg_quote((string) $string, '/')
47
+ );
48
+ };
src/includes/closures/Shared/ReplaceUtils.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * String replace ONE time.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param string $needle A string to search/replace.
10
+ * @param string $replace What to replace `$needle` with.
11
+ * @param string $haystack The string/haystack to search in.
12
+ *
13
+ * @param boolean $caSe_insensitive Defaults to a `FALSE` value.
14
+ * Pass this as `TRUE` to a caSe-insensitive search/replace.
15
+ *
16
+ * @return string The `$haystack`, with `$needle` replaced with `$replace` ONE time only.
17
+ */
18
+ $self->strReplaceOnce = function ($needle, $replace, $haystack, $caSe_insensitive = false) use ($self) {
19
+ $needle = (string) $needle;
20
+ $replace = (string) $replace;
21
+ $haystack = (string) $haystack;
22
+ $caSe_strpos = $caSe_insensitive ? 'stripos' : 'strpos';
23
+
24
+ if (($needle_strpos = $caSe_strpos($haystack, $needle)) === false) {
25
+ return $haystack; // Nothing to replace.
26
+ }
27
+ return (string) substr_replace($haystack, $replace, $needle_strpos, strlen($needle));
28
+ };
29
+
30
+ /*
31
+ * String replace ONE time (caSe-insensitive).
32
+ *
33
+ * @since 150422 Rewrite.
34
+ *
35
+ * @param string $needle A string to search/replace.
36
+ * @param string $replace What to replace `$needle` with.
37
+ * @param string $haystack The string/haystack to search in.
38
+ *
39
+ * @return string The `$haystack`, with `$needle` replaced with `$replace` ONE time only.
40
+ */
41
+ $self->strIreplaceOnce = function ($needle, $replace, $haystack) use ($self) {
42
+ return $self->strReplaceOnce($needle, $replace, $haystack, true);
43
+ };
src/includes/closures/Shared/ServerUtils.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Is running on Apache?
6
+ *
7
+ * @since 151002 This is Apache?
8
+ *
9
+ * @return bool True if running Apache.
10
+ */
11
+ $self->isApache = function () use ($self) {
12
+ if (!is_null($is = &$self->staticKey('isApache'))) {
13
+ return $is; // Already cached this.
14
+ }
15
+ if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) {
16
+ if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) {
17
+ return ($is = true);
18
+ }
19
+ if (stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false) {
20
+ return ($is = true);
21
+ }
22
+ }
23
+ return ($is = false);
24
+ };
25
+
26
+ /*
27
+ * Is running on Nginx?
28
+ *
29
+ * @since 151002 This is Nginx?
30
+ *
31
+ * @return bool True if running Nginx.
32
+ */
33
+ $self->isNginx = function () use ($self) {
34
+ if (!is_null($is = &$self->staticKey('isNginx'))) {
35
+ return $is; // Already cached this.
36
+ }
37
+ if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) {
38
+ if (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
39
+ return ($is = true);
40
+ }
41
+ }
42
+ return ($is = false);
43
+ };
44
+
45
+ /*
46
+ * Is running on Windows IIS?
47
+ *
48
+ * @since 151002 This is Windows IIS?
49
+ *
50
+ * @return bool True if running Windows IIS.
51
+ */
52
+ $self->isIis = function () use ($self) {
53
+ if (!is_null($is = &$self->staticKey('isIis'))) {
54
+ return $is; // Already cached this.
55
+ }
56
+ if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) {
57
+ if (stripos($_SERVER['SERVER_SOFTWARE'], 'microsoft-iis') !== false) {
58
+ return ($is = true);
59
+ }
60
+ if (stripos($_SERVER['SERVER_SOFTWARE'], 'expressiondevserver') !== false) {
61
+ return ($is = true);
62
+ }
63
+ }
64
+ return ($is = false);
65
+ };
src/includes/closures/Shared/StringUtils.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Clips string(s) to X chars deeply.
6
+ *
7
+ * @since 151114 Adding string utils.
8
+ *
9
+ * @param mixed $value Any input value.
10
+ * @param int $max_length Defaults to a value of `80`.
11
+ * @param bool $force_ellipsis Defaults to a value of `FALSE`.
12
+ *
13
+ * @return string|array|object Clipped value.
14
+ */
15
+ $self->clip = function ($value, $max_length = 80, $force_ellipsis = false) use ($self) {
16
+ if (is_array($value) || is_object($value)) {
17
+ foreach ($value as $_key => &$_value) {
18
+ $_value = $self->clip($_value, $max_length, $force_ellipsis);
19
+ }
20
+ unset($_key, $_value); // Housekeeping.
21
+
22
+ return $value;
23
+ }
24
+ if (!($string = (string) $value)) {
25
+ return $string; // Empty.
26
+ }
27
+ $max_length = max(4, $max_length);
28
+
29
+ $string = strip_tags($string);
30
+ $string = preg_replace('/\s+/', ' ', strip_tags($string));
31
+ $string = trim($string); // Trim it up now.
32
+
33
+ if (strlen($string) > $max_length) {
34
+ $string = (string) substr($string, 0, $max_length - 3).'...';
35
+ } elseif ($force_ellipsis && strlen($string) + 3 > $max_length) {
36
+ $string = (string) substr($string, 0, $max_length - 3).'...';
37
+ } else {
38
+ $string .= $force_ellipsis ? '...' : '';
39
+ }
40
+ return $string;
41
+ };
42
+
43
+ /*
44
+ * Mid-clips string(s) to X chars deeply.
45
+ *
46
+ * @since 151114 Adding string utils.
47
+ *
48
+ * @param mixed $value Any input value.
49
+ * @param int $max_length Defaults to a value of `80`.
50
+ *
51
+ * @return string|array|object Mid-clipped value.
52
+ */
53
+ $self->midClip = function ($value, $max_length = 80) use ($self) {
54
+ if (is_array($value) || is_object($value)) {
55
+ foreach ($value as $_key => &$_value) {
56
+ $_value = $self->midClip($_value, $max_length);
57
+ }
58
+ unset($_key, $_value); // Housekeeping.
59
+
60
+ return $value;
61
+ }
62
+ if (!($string = (string) $value)) {
63
+ return $string; // Empty.
64
+ }
65
+ $max_length = max(4, $max_length);
66
+
67
+ $string = strip_tags($string);
68
+ $string = preg_replace('/\s+/', ' ', strip_tags($string));
69
+ $string = trim($string); // Trim it up now.
70
+
71
+ if (strlen($string) <= $max_length) {
72
+ return $string; // Nothing to do.
73
+ }
74
+ $full_string = $string;
75
+ $half_max_length = floor($max_length / 2);
76
+
77
+ $first_clip = $half_max_length - 3;
78
+ $string = $first_clip >= 1 // Something?
79
+ ? substr($full_string, 0, $first_clip).'...'
80
+ : '...'; // Ellipsis only.
81
+
82
+ $second_clip = strlen($full_string) - ($max_length - strlen($string));
83
+ $string .= $second_clip >= 0 && $second_clip >= $first_clip
84
+ ? substr($full_string, $second_clip) : '';
85
+
86
+ return $string;
87
+ };
src/includes/closures/Shared/SysUtils.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * System load averages.
6
+ *
7
+ * @since 151002 Adding cache directory statistics.
8
+ *
9
+ * @return array System load averages.
10
+ */
11
+ $self->sysLoadAverages = function () use ($self) {
12
+ if (!is_null($averages = &$self->cacheKey('sysLoadAverages'))) {
13
+ return $averages; // Already cached these.
14
+ }
15
+ if (!$self->functionIsPossible('sys_getloadavg')) {
16
+ return ($averages = array());
17
+ }
18
+ if (!is_array($averages = sys_getloadavg()) || !$averages) {
19
+ return ($averages = array());
20
+ }
21
+ $averages = array_map('floatval', $averages);
22
+ $averages = array_slice($averages, 0, 3);
23
+ // i.e., 1m, 5m, 15m; see: <http://jas.xyz/1gWyJLt>
24
+
25
+ return $averages;
26
+ };
27
+
28
+ /*
29
+ * System memory info.
30
+ *
31
+ * @since 151002 Adding cache directory statistics.
32
+ *
33
+ * @return \stdClass|boolean System memory info.
34
+ */
35
+ $self->sysMemoryStatus = function () use ($self) {
36
+ if (!is_null($status = &$self->cacheKey('sysMemoryStatus'))) {
37
+ return $status; // Already cached this.
38
+ }
39
+ if (!$self->functionIsPossible('shell_exec')) {
40
+ return ($status = false);
41
+ }
42
+ if (!($free = trim((string) @shell_exec('free')))) {
43
+ return ($status = false);
44
+ }
45
+ if (!($free_lines = explode("\n", $free))) {
46
+ return ($status = false);
47
+ }
48
+ if (empty($free_lines[1])) {
49
+ return ($status = false);
50
+ }
51
+ if (!($memory = explode(' ', $free_lines[1]))) {
52
+ return ($status = false);
53
+ }
54
+ if (!($memory = array_merge(array_filter($memory)))) {
55
+ return ($status = false);
56
+ }
57
+ if (!isset($memory[1], $memory[2])) {
58
+ return ($status = false);
59
+ }
60
+ if (($total = (integer) $memory[1]) <= 0) {
61
+ return ($status = false);
62
+ }
63
+ $used = (integer) $memory[2];
64
+ $percent = $used / $total * 100;
65
+ $percentage = sprintf(__('%s%%', 'comet-cache'), number_format($percent, 2, '.', ''));
66
+ $status = (object) compact('total', 'used', 'percent', 'percentage');
67
+
68
+ return $status;
69
+ };
70
+
71
+ /*
72
+ * System opcache status/details.
73
+ *
74
+ * @since 151002 Adding cache directory statistics.
75
+ *
76
+ * @return \stdClass|boolean System opcache status/details.
77
+ */
78
+ $self->sysOpcacheStatus = function () use ($self) {
79
+ if (!is_null($status = &$self->cacheKey('sysOpcacheStatus'))) {
80
+ return $status; // Already cached this.
81
+ }
82
+ if (!$self->functionIsPossible('opcache_get_status')) {
83
+ return ($status = false);
84
+ }
85
+ if (!is_array($status = opcache_get_status(false)) || !$status) {
86
+ return ($status = false);
87
+ }
88
+ if (empty($status['opcache_enabled'])) {
89
+ return ($status = false);
90
+ }
91
+ return json_decode(json_encode($status));
92
+ };
src/includes/closures/Shared/TokenUtils.php ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+
5
+
6
+ /*
7
+ * Current host.
8
+ *
9
+ * @since 150422 Rewrite.
10
+ *
11
+ * @param boolean $dashify Optional, defaults to a `FALSE` value.
12
+ * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9]`.
13
+ *
14
+ * @param boolean $consider_domain_mapping Consider?
15
+ *
16
+ * @param string $consider_domain_mapping_domain A specific domain?
17
+ *
18
+ * @return string Current host.
19
+ *
20
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
21
+ */
22
+ $self->hostToken = function ($dashify = false, $consider_domain_mapping = false, $consider_domain_mapping_domain = '') use ($self) {
23
+ if (!is_null($token = &$self->staticKey('hostToken', array($dashify, $consider_domain_mapping, $consider_domain_mapping_domain)))) {
24
+ return $token; // Already cached this.
25
+ }
26
+ $token = ''; // Initialize token value.
27
+
28
+ if (!is_multisite() || $self->isAdvancedCache()) {
29
+ $token = (string) $_SERVER['HTTP_HOST'];
30
+ } elseif ($consider_domain_mapping && $self->canConsiderDomainMapping()) {
31
+ if (($consider_domain_mapping_domain = trim((string) $consider_domain_mapping_domain))) {
32
+ $token = $consider_domain_mapping_domain;
33
+ } elseif ($self->isDomainMapping()) {
34
+ $token = (string) $_SERVER['HTTP_HOST'];
35
+ } else { // For the current blog ID.
36
+ $token = $self->domainMappingUrlFilter($self->currentUrl());
37
+ $token = $self->parseUrl($token, PHP_URL_HOST);
38
+ }
39
+ }
40
+ if (!$token) { // Use default?
41
+ $token = (string) $_SERVER['HTTP_HOST'];
42
+ }
43
+ if ($token) { // Have token?
44
+ $token = strtolower($token);
45
+ if ($dashify) { // Dashify it?
46
+ $token = preg_replace('/[^a-z0-9]/i', '-', $token);
47
+ $token = trim($token, '-');
48
+ }
49
+ }
50
+ return $token;
51
+ };
52
+
53
+ /*
54
+ * Host for a specific blog.
55
+ *
56
+ * @since 150821 Improving multisite compat.
57
+ *
58
+ * @param boolean $dashify Optional, defaults to a `FALSE` value.
59
+ * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9]`.
60
+ *
61
+ * @param boolean $consider_domain_mapping Consider?
62
+ *
63
+ * @param string $consider_domain_mapping_domain A specific domain?
64
+ *
65
+ * @param boolean $fallback Fallback on blog's domain when mapping?
66
+ *
67
+ * @param integer $blog_id For which blog ID?
68
+ *
69
+ * @return string Host for a specific blog.
70
+ *
71
+ * @note The return value of this function is NOT cached in support of `switch_to_blog()`.
72
+ */
73
+ $self->hostTokenForBlog = function ($dashify = false, $consider_domain_mapping = false, $consider_domain_mapping_domain = '', $fallback = false, $blog_id = 0) use ($self) {
74
+ if (!is_multisite() || $self->isAdvancedCache()) {
75
+ return $self->hostToken($dashify, $consider_domain_mapping, $consider_domain_mapping_domain);
76
+ }
77
+ $token = ''; // Initialize token value.
78
+
79
+ if ($consider_domain_mapping && $self->canConsiderDomainMapping()) {
80
+ if (($consider_domain_mapping_domain = trim((string) $consider_domain_mapping_domain))) {
81
+ $token = $consider_domain_mapping_domain; // Force this value.
82
+ } else {
83
+ $token = $self->domainMappingBlogDomain($blog_id, $fallback);
84
+ }
85
+ } elseif (($blog_details = $self->blogDetails($blog_id))) {
86
+ $token = $blog_details->domain; // Unmapped domain.
87
+ }
88
+ if ($token) { // Have token?
89
+ $token = strtolower($token);
90
+ if ($dashify) { // Dashify it?
91
+ $token = preg_replace('/[^a-z0-9]/i', '-', $token);
92
+ $token = trim($token, '-');
93
+ }
94
+ }
95
+ return $token;
96
+ };
97
+
98
+ /*
99
+ * Current site's base directory.
100
+ *
101
+ * @since 150422 Rewrite.
102
+ *
103
+ * @param boolean $dashify Optional, defaults to a `FALSE` value.
104
+ * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`.
105
+ *
106
+ * @param boolean $consider_domain_mapping Consider?
107
+ *
108
+ * @return string Current site's base directory.
109
+ *
110
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
111
+ */
112
+ $self->hostBaseToken = function ($dashify = false, $consider_domain_mapping = false) use ($self) {
113
+ if (!is_null($token = &$self->staticKey('hostBaseToken', array($dashify, $consider_domain_mapping)))) {
114
+ return $token; // Already cached this.
115
+ }
116
+ $token = '/'; // Assume NOT multisite; or own domain.
117
+
118
+ if (!is_multisite()) {
119
+ return $token; // Not applicable.
120
+ }
121
+ if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) {
122
+ return $token; // Not applicable.
123
+ }
124
+ if ($consider_domain_mapping && $self->canConsiderDomainMapping()) {
125
+ return $token; // Not applicable.
126
+ }
127
+ if (defined('PATH_CURRENT_SITE')) {
128
+ $token = (string) PATH_CURRENT_SITE;
129
+ }
130
+ $token = trim($token, '\\/'." \t\n\r\0\x0B");
131
+ $token = isset($token[0]) ? '/'.$token.'/' : '/';
132
+
133
+ if ($token !== '/' && $dashify) {
134
+ $token = preg_replace('/[^a-z0-9\/]/i', '-', $token);
135
+ $token = trim($token, '-');
136
+ }
137
+ return $token;
138
+ };
139
+
140
+ /*
141
+ * Current blog's sub-directory.
142
+ *
143
+ * @since 150422 Rewrite.
144
+ *
145
+ * @param boolean $dashify Optional, defaults to a `FALSE` value.
146
+ * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`.
147
+ *
148
+ * @param boolean $consider_domain_mapping Consider?
149
+ *
150
+ * @param string $path Defaults to the current URI path.
151
+ *
152
+ * @return string Current blog's sub-directory.
153
+ *
154
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
155
+ */
156
+ $self->hostDirToken = function ($dashify = false, $consider_domain_mapping = false, $path = null) use ($self) {
157
+ if (!isset($path)) { // Use current/default path?
158
+ $path = (string) $self->parseUrl($_SERVER['REQUEST_URI'], PHP_URL_PATH);
159
+ }
160
+ $path = '/'.ltrim((string) $path, '/'); // Force leading slash.
161
+
162
+ if (!is_null($token = &$self->staticKey('hostDirToken', array($dashify, $consider_domain_mapping, $path)))) {
163
+ return $token; // Already cached this.
164
+ }
165
+ $token = '/'; // Assume NOT multisite; or own domain.
166
+
167
+ if (!is_multisite()) {
168
+ return $token; // Not applicable.
169
+ }
170
+ if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) {
171
+ return $token; // Not applicable.
172
+ }
173
+ if ($consider_domain_mapping && $self->canConsiderDomainMapping()) {
174
+ return $token; // Not applicable.
175
+ }
176
+ if ($path && $path !== '/' && ($host_base_token = trim($self->hostBaseToken(), '/'))) {
177
+ $path_minus_base = preg_replace('/^\/'.preg_quote($host_base_token, '/').'(\/|$)/i', '${1}', $path);
178
+ } else {
179
+ $path_minus_base = $path; // Default value.
180
+ }
181
+ list($token) = explode('/', trim($path_minus_base, '/'));
182
+ $token = trim($token, '\\/'." \t\n\r\0\x0B");
183
+ $token = isset($token[0]) ? '/'.$token.'/' : '/';
184
+
185
+ if ($token !== '/') { // Perhaps NOT the main site?
186
+ $blog_paths_file = $self->cacheDir().'/'.strtolower(SHORT_NAME).'-blog-paths';
187
+ if (!is_file($blog_paths_file) || !in_array($token, unserialize(file_get_contents($blog_paths_file)), true)) {
188
+ $token = '/'; // NOT a real/valid child blog path.
189
+ }
190
+ }
191
+ if ($token !== '/' && $dashify) {
192
+ $token = preg_replace('/[^a-z0-9\/]/i', '-', $token);
193
+ $token = trim($token, '-');
194
+ }
195
+ return $token;
196
+ };
197
+
198
+ /*
199
+ * A blog's sub-directory.
200
+ *
201
+ * @since 150821 Improving multisite compat.
202
+ *
203
+ * @param boolean $dashify Optional, defaults to a `FALSE` value.
204
+ * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9]`.
205
+ *
206
+ * @param boolean $consider_domain_mapping Consider?
207
+ *
208
+ * @param integer $blog_id For which blog ID?
209
+ *
210
+ * @return string A blog's sub-directory.
211
+ *
212
+ * @note The return value of this function is NOT cached in support of `switch_to_blog()`.
213
+ */
214
+ $self->hostDirTokenForBlog = function ($dashify = false, $consider_domain_mapping = false, $blog_id = 0) use ($self) {
215
+ if (!is_multisite() || $self->isAdvancedCache()) {
216
+ return $self->hostDirToken($dashify, $consider_domain_mapping);
217
+ }
218
+ $token = '/'; // Initialize token value.
219
+
220
+ if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) {
221
+ return $token; // Not applicable.
222
+ }
223
+ if ($consider_domain_mapping && $self->canConsiderDomainMapping()) {
224
+ return $token; // Not applicable.
225
+ }
226
+ if (($blog_details = $self->blogDetails($blog_id))) {
227
+ $path = $blog_details->path; // e.g., `[/base]/path/` (includes base).
228
+ if ($path && $path !== '/' && ($host_base_token = trim($self->hostBaseToken(), '/'))) {
229
+ $path_minus_base = preg_replace('/^\/'.preg_quote($host_base_token, '/').'(\/|$)/i', '${1}', $path);
230
+ } else {
231
+ $path_minus_base = $path; // Default value.
232
+ }
233
+ list($token) = explode('/', trim($path_minus_base, '/'));
234
+ }
235
+ $token = trim($token, '\\/'." \t\n\r\0\x0B");
236
+ $token = isset($token[0]) ? '/'.$token.'/' : '/';
237
+
238
+ if ($token !== '/') { // Perhaps NOT the main site?
239
+ $blog_paths_file = $self->cacheDir().'/'.strtolower(SHORT_NAME).'-blog-paths';
240
+ if (!is_file($blog_paths_file) || !in_array($token, unserialize(file_get_contents($blog_paths_file)), true)) {
241
+ $token = '/'; // NOT a real/valid child blog path.
242
+ }
243
+ }
244
+ if ($token !== '/' && $dashify) {
245
+ $token = preg_replace('/[^a-z0-9\/]/i', '-', $token);
246
+ $token = trim($token, '-');
247
+ }
248
+ return $token;
249
+ };
250
+
251
+ /*
252
+ * Current site's base directory & current blog's sub-directory.
253
+ *
254
+ * @since 150422 Rewrite.
255
+ *
256
+ * @param boolean $dashify Optional, defaults to a `FALSE` value.
257
+ * If `TRUE`, the tokens are returned with dashes in place of `[^a-z0-9\/]`.
258
+ *
259
+ * @param boolean $consider_domain_mapping Consider?
260
+ *
261
+ * @param string $path Defaults to the current URI path.
262
+ *
263
+ * @return string Current site's base directory & current blog's sub-directory.
264
+ *
265
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
266
+ */
267
+ $self->hostBaseDirTokens = function ($dashify = false, $consider_domain_mapping = false, $path = null) use ($self) {
268
+ if (!is_null($tokens = &$self->staticKey('hostBaseDirTokens', array($dashify, $consider_domain_mapping, $path)))) {
269
+ return $tokens; // Already cached this.
270
+ }
271
+ $tokens = $self->hostBaseToken($dashify, $consider_domain_mapping);
272
+ $tokens .= $self->hostDirToken($dashify, $consider_domain_mapping, $path);
273
+
274
+ return ($tokens = preg_replace('/\/+/', '/', $tokens));
275
+ };
276
+
277
+ /*
278
+ * A site's base directory & a blog's sub-directory.
279
+ *
280
+ * @since 150821 Improving multisite compat.
281
+ *
282
+ * @param boolean $dashify Optional, defaults to a `FALSE` value.
283
+ * If `TRUE`, the tokens are returned with dashes in place of `[^a-z0-9\/]`.
284
+ *
285
+ * @param boolean $consider_domain_mapping Consider?
286
+ *
287
+ * @param integer $blog_id For which blog ID?
288
+ *
289
+ * @return string A site's base directory & a blog's sub-directory.
290
+ *
291
+ * @note The return value of this function is NOT cached in support of `switch_to_blog()`.
292
+ */
293
+ $self->hostBaseDirTokensForBlog = function ($dashify = false, $consider_domain_mapping = false, $blog_id = 0) use ($self) {
294
+ $tokens = $self->hostBaseToken($dashify, $consider_domain_mapping);
295
+ $tokens .= $self->hostDirTokenForBlog($dashify, $consider_domain_mapping, $blog_id);
296
+
297
+ return ($tokens = preg_replace('/\/+/', '/', $tokens));
298
+ };
299
+
300
+
src/includes/closures/Shared/TrimUtils.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Trims strings deeply.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param mixed $values Any value can be converted into a trimmed string.
10
+ * Actually, objects can't, but this recurses into objects.
11
+ *
12
+ * @param string $chars Specific chars to trim.
13
+ * Defaults to PHP's trim: " \r\n\t\0\x0B". Use an empty string to bypass.
14
+ *
15
+ * @param string $extra_chars Additional chars to trim.
16
+ *
17
+ * @return string|array|object Trimmed string, array, object.
18
+ */
19
+ $self->trimDeep = function ($values, $chars = '', $extra_chars = '') use ($self) {
20
+ if (is_array($values) || is_object($values)) {
21
+ foreach ($values as $_key => &$_values) {
22
+ $_values = $self->trimDeep($_values, $chars, $extra_chars);
23
+ }
24
+ unset($_key, $_values); // Housekeeping.
25
+
26
+ return $values;
27
+ }
28
+ $string = (string) $values;
29
+ $chars = (string) $chars;
30
+ $extra_chars = (string) $extra_chars;
31
+
32
+ $chars = isset($chars[0]) ? $chars : " \r\n\t\0\x0B";
33
+ $chars = $chars.$extra_chars; // Concatenate.
34
+
35
+ return trim($string, $chars);
36
+ };
src/includes/closures/Shared/UrlUtils.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /*
5
+ * Parses a URL.
6
+ *
7
+ * @since 150821 Improving multisite compat.
8
+ *
9
+ * @param string $url_uri_qsl Input URL, URI, or query string w/ a leading `?`.
10
+ * @param int $component Optional component to retrieve.
11
+ *
12
+ * @return array|string|int|null Array, else `string|int|null` component value.
13
+ */
14
+ $self->parseUrl = function ($url_uri_qsl, $component = -1) use ($self) {
15
+ $url_uri_qsl = (string) $url_uri_qsl;
16
+ $component = (integer) $component;
17
+ ${'//'} = strpos($url_uri_qsl, '//') === 0;
18
+
19
+ if ($url_uri_qsl && strpos($url_uri_qsl, '&amp;') !== false) {
20
+ $url_uri_qsl = str_replace('&amp;', '&', $url_uri_qsl);
21
+ }
22
+ if ($component > -1) {
23
+ if (${'//'} && $component === PHP_URL_SCHEME) {
24
+ return ($part = '//');
25
+ }
26
+ return ($part = parse_url($url_uri_qsl, $component));
27
+ } else {
28
+ if (!is_array($parts = parse_url($url_uri_qsl))) {
29
+ return ($parts = array());
30
+ }
31
+ if (${'//'}) {
32
+ $parts['scheme'] = '//';
33
+ }
34
+ return $parts;
35
+ }
36
+ };
37
+
38
+ /*
39
+ * Unparses a URL.
40
+ *
41
+ * @since 150821 Improving multisite compat.
42
+ *
43
+ * @param array $parts Input URL parts.
44
+ *
45
+ * @return string Unparsed URL in string format.
46
+ */
47
+ $self->unParseUrl = function (array $parts) use ($self) {
48
+ $scheme = '';
49
+ $host = '';
50
+ $port = '';
51
+ $user = '';
52
+ $pass = '';
53
+ $path = '';
54
+ $query = '';
55
+ $fragment = '';
56
+
57
+ if (!empty($parts['scheme'])) {
58
+ if ($parts['scheme'] === '//') {
59
+ $scheme = $parts['scheme'];
60
+ } else {
61
+ $scheme = $parts['scheme'].'://';
62
+ }
63
+ }
64
+ if (!empty($parts['host'])) {
65
+ $host = $parts['host'];
66
+ }
67
+ if (!empty($parts['port'])) {
68
+ $port = ':'.$parts['port'];
69
+ }
70
+ if (!empty($parts['user'])) {
71
+ $user = $parts['user'];
72
+ }
73
+ if (!empty($parts['pass'])) {
74
+ $pass = $parts['pass'];
75
+ }
76
+ if ($user || $pass) {
77
+ $pass .= '@';
78
+ }
79
+ if (!empty($parts['path'])) {
80
+ $path = '/'.ltrim($parts['path'], '/');
81
+ }
82
+ if (!empty($parts['query'])) {
83
+ $query = '?'.$parts['query'];
84
+ }
85
+ if (!empty($parts['fragment'])) {
86
+ $fragment = '#'.$parts['fragment'];
87
+ }
88
+ return $scheme.$user.$pass.$host.$port.$path.$query.$fragment;
89
+ };
90
+
91
+ /*
92
+ * Is the current request over SSL?
93
+ *
94
+ * @since 150422 Rewrite.
95
+ *
96
+ * @return boolean `TRUE` if the current request is over SSL.
97
+ *
98
+ * @note The return value of this function is cached to reduce overhead on repeat calls.
99
+ */
100
+ $self->isSsl = function () use ($self) {
101
+ if (!is_null($is = &$self->staticKey('isSsl'))) {
102
+ return $is; // Already cached this.
103
+ }
104
+ if (!empty($_SERVER['SERVER_PORT'])) {
105
+ if ((integer) $_SERVER['SERVER_PORT'] === 443) {
106
+ return ($is = true);
107
+ }
108
+ }
109
+ if (!empty($_SERVER['HTTPS'])) {
110
+ if (filter_var($_SERVER['HTTPS'], FILTER_VALIDATE_BOOLEAN)) {
111
+ return ($is = true);
112
+ }
113
+ }
114
+ if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
115
+ if (strcasecmp((string) $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0) {
116
+ return ($is = true);
117
+ }
118
+ }
119
+ return ($is = false);
120
+ };
121
+
122
+ /*
123
+ * Current URL.
124
+ *
125
+ * @since 150821 Improving multisite compat.
126
+ *
127
+ * @return string Current URL.
128
+ */
129
+ $self->currentUrl = function () use ($self) {
130
+ return ($self->isSsl() ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
131
+ };
src/includes/functions/i18n-utils.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WebSharks\Comet_Cache;
3
+
4
+ /**
5
+ * Polyfill for {@link \__()}.
6
+ *
7
+ * @since 150422 Rewrite.
8
+ *
9
+ * @param string $string String to translate.
10
+ * @param string $text_domain Plugin text domain.
11
+ *
12
+ * @return string Possibly translated string.
13
+ */
14
+ function __($string, $text_domain)
15
+ {
16
+ static $exists; // Cache.
17
+
18
+ if ($exists || ($exists = function_exists('__'))) {
19
+ return \__($string, $text_domain);
20
+ }
21
+ return $string; // Not possible (yet).
22
+ }
src/includes/functions/wp-cache-postload.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use WebSharks\Comet_Cache as Plugin;
3
+
4
+ /**
5
+ * Postload event handler; overrides core WP function.
6
+ *
7
+ * @since 140422 First documented version.
8
+ *
9
+ * @note See `/wp-settings.php` around line #226.
10
+ */
11
+ function wp_cache_postload()
12
+ {
13
+ $GLOBAL_NS = Plugin\GLOBAL_NS;
14
+ $advanced_cache = $GLOBALS[$GLOBAL_NS.'_advanced_cache'];
15
+
16
+ if (!$advanced_cache->is_running) {
17
+ return; // Not applicable.
18
+ }
19
+ $advanced_cache->doWpAction('before_'.$GLOBAL_NS.'_'.__FUNCTION__, get_defined_vars());
20
+
21
+
22
+ if (!empty($advanced_cache->postload['filter_status_header'])) {
23
+ $advanced_cache->maybeFilterStatusHeaderPostload();
24
+ }
25
+ if (!empty($advanced_cache->postload['set_debug_info'])) {
26
+ $advanced_cache->maybeSetDebugInfoPostload();
27
+ }
28
+ if (!empty($advanced_cache->postload['wp_main_query'])) {
29
+ add_action('wp', array($advanced_cache, 'wpMainQueryPostload'), PHP_INT_MAX);
30
+ }
31
+ $advanced_cache->doWpAction('after_'.$GLOBAL_NS.'_'.__FUNCTION__, get_defined_vars());
32
+ $advanced_cache->doWpAction($GLOBAL_NS.'_'.__FUNCTION__.'_complete', get_defined_vars());
33
+ }
src/includes/plugin.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin.
4
+ *
5
+ * @since 150422 Rewrite.
6
+ */
7
+ namespace WebSharks\Comet_Cache;
8
+
9
+ if (!defined('WPINC')) {
10
+ exit('Do NOT access this file directly: '.basename(__FILE__));
11
+ }
12
+ require_once dirname(__FILE__).'/stub.php';
13
+
14
+ if (!Conflicts::check()) {
15
+ $GLOBALS[GLOBAL_NS] = new Plugin();
16
+ require_once dirname(__FILE__).'/api.php';
17
+ }
src/includes/stub.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Stub.
4
+ *
5
+ * @since 150422 Rewrite.
6
+ */
7
+ namespace WebSharks\Comet_Cache;
8
+
9
+ if (!defined('WPINC')) {
10
+ exit('Do NOT access this file directly: '.basename(__FILE__));
11
+ }
12
+ require_once dirname(dirname(__FILE__)).'/vendor/autoload.php';
13
+ require_once dirname(__FILE__).'/functions/i18n-utils.php';
14
+
15
+ ${__FILE__}['version'] = '160211'; //version//
16
+ ${__FILE__}['plugin'] = dirname(dirname(dirname(__FILE__)));
17
+ ${__FILE__}['plugin'] .= '/'.basename(${__FILE__}['plugin']).'.php';
18
+ ${__FILE__}['ns_path'] = str_replace('\\', '/', __NAMESPACE__); // To dir/path.
19
+ ${__FILE__}['is_pro'] = strtolower(basename(${__FILE__}['ns_path'])) === 'pro';
20
+
21
+ define(__NAMESPACE__.'\\SHORT_NAME', 'CC');
22
+ define(__NAMESPACE__.'\\NAME', 'Comet Cache');
23
+ define(__NAMESPACE__.'\\DOMAIN', 'cometcache.com');
24
+ define(__NAMESPACE__.'\\GLOBAL_NS', 'comet_cache');
25
+ define(__NAMESPACE__.'\\SLUG_TD', 'comet-cache');
26
+ define(__NAMESPACE__.'\\VERSION', ${__FILE__}['version']);
27
+ define(__NAMESPACE__.'\\PLUGIN_FILE', ${__FILE__}['plugin']);
28
+ define(__NAMESPACE__.'\\IS_PRO', ${__FILE__}['is_pro']);
29
+
30
+ $GLOBALS[GLOBAL_NS] = null;
31
+
32
+ unset(${__FILE__}); // Housekeeping.
src/includes/templates/ac-plugin.txt ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Example AC (Advanced Cache) Plugin File.
4
+ *
5
+ * If implemented; this file should go in this special directory:
6
+ * `/wp-content/ac-plugins/my-ac-plugin.php`
7
+ */
8
+ if (!defined('WPINC')) {
9
+ exit('Do NOT access this file directly: '.basename(__FILE__));
10
+ }
11
+ function my_ac_plugin() // Example plugin.
12
+ {
13
+ $ac = $GLOBALS['comet_cache_advanced_cache']; // Comet Cache instance.
14
+ $ac->addFilter('comet_cache_version_salt', 'my_ac_version_salt_shaker');
15
+ }
16
+ function my_ac_version_salt_shaker($version_salt)
17
+ {
18
+ if (stripos($_SERVER['HTTP_USER_AGENT'], 'iphone') !== false) {
19
+ $version_salt .= 'iphones'; // Give iPhones their own variation of the cache.
20
+ } elseif (stripos($_SERVER['HTTP_USER_AGENT'], 'android') !== false) {
21
+ $version_salt .= 'androids'; // Androic variation.
22
+ } else {
23
+ $version_salt .= 'other'; // A default group.
24
+ }
25
+ return $version_salt;
26
+ }
27
+ my_ac_plugin(); // Run this plugin.
src/includes/templates/advanced-cache.txt ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Advanced cache stub.
4
+ *
5
+ * @since 150422 Rewrite.
6
+ */
7
+ namespace WebSharks\Comet_Cache;
8
+
9
+ if (!defined('WPINC')) {
10
+ exit('Do NOT access this file directly: '.basename(__FILE__));
11
+ }
12
+ if (!defined('COMET_CACHE_PLUGIN_FILE')) {
13
+ /**
14
+ * Plugin file path.
15
+ *
16
+ * @since 140725 Reorganizing class members.
17
+ *
18
+ * @var string Absolute server path to CC plugin file.
19
+ */
20
+ define('COMET_CACHE_PLUGIN_FILE', '%%COMET_CACHE_PLUGIN_FILE%%');
21
+ }
22
+ if (defined('WP_DEBUG') && WP_DEBUG) {
23
+ if ((include_once(dirname(COMET_CACHE_PLUGIN_FILE).'/src/includes/stub.php')) === false) {
24
+ return; // Unable to find stub. Fail softly w/ PHP warning.
25
+ }
26
+ } elseif ((@include_once(dirname(COMET_CACHE_PLUGIN_FILE).'/src/includes/stub.php')) === false) {
27
+ return; // Unable to find stub. Fail softly.
28
+ }
29
+ if (defined('WP_DEBUG') && WP_DEBUG) {
30
+ if ((@include_once(dirname(COMET_CACHE_PLUGIN_FILE).'/src/includes/functions/wp-cache-postload.php')) === false) {
31
+ return; // Unable to find postload function(s). Fail softly w/ PHP warning.
32
+ }
33
+ } elseif ((@include_once(dirname(COMET_CACHE_PLUGIN_FILE).'/src/includes/functions/wp-cache-postload.php')) === false) {
34
+ return; // Unable to find postload function(s). Fail softly.
35
+ }
36
+ AdvCacheBackCompat::zenCacheConstants();
37
+ AdvCacheBackCompat::zcRequestVars();
38
+
39
+ if (!defined('COMET_CACHE_PRO')) {
40
+ /**
41
+ * Comet Cache Pro flag.
42
+ *
43
+ * @since 140422 First documented version.
44
+ *
45
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
46
+ */
47
+ define('COMET_CACHE_PRO', IS_PRO);
48
+ }
49
+ if (!defined('COMET_CACHE_ENABLE')) {
50
+ /**
51
+ * Is Comet Cache enabled?
52
+ *
53
+ * @since 140422 First documented version.
54
+ *
55
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
56
+ */
57
+ define('COMET_CACHE_ENABLE', '%%COMET_CACHE_ENABLE%%');
58
+ }
59
+ if (!defined('COMET_CACHE_DEBUGGING_ENABLE')) {
60
+ /**
61
+ * Is Comet Cache debugging enabled?
62
+ *
63
+ * @since 140422 First documented version.
64
+ *
65
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
66
+ */
67
+ define('COMET_CACHE_DEBUGGING_ENABLE', '%%COMET_CACHE_DEBUGGING_ENABLE%%');
68
+ }
69
+ if (!defined('COMET_CACHE_ALLOW_BROWSER_CACHE')) {
70
+ /**
71
+ * Allow browsers to cache each document?
72
+ *
73
+ * @since 140422 First documented version.
74
+ *
75
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
76
+ *
77
+ * @note If this is a `FALSE` (or an empty) value; Comet Cache will send no-cache headers.
78
+ * If `TRUE`, Comet Cache will NOT send no-cache headers.
79
+ */
80
+ define('COMET_CACHE_ALLOW_BROWSER_CACHE', '%%COMET_CACHE_ALLOW_BROWSER_CACHE%%');
81
+ }
82
+ if (!defined('COMET_CACHE_GET_REQUESTS')) {
83
+ /**
84
+ * Cache `$_GET` requests w/ a query string?
85
+ *
86
+ * @since 140422 First documented version.
87
+ *
88
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
89
+ */
90
+ define('COMET_CACHE_GET_REQUESTS', '%%COMET_CACHE_GET_REQUESTS%%');
91
+ }
92
+ if (!defined('COMET_CACHE_CACHE_404_REQUESTS')) {
93
+ /**
94
+ * Cache 404 errors?
95
+ *
96
+ * @since 140422 First documented version.
97
+ *
98
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
99
+ */
100
+ define('COMET_CACHE_CACHE_404_REQUESTS', '%%COMET_CACHE_CACHE_404_REQUESTS%%');
101
+ }
102
+ if (!defined('COMET_CACHE_CACHE_NONCE_VALUES')) {
103
+ /**
104
+ * Cache HTML containing nonce values?
105
+ *
106
+ * @since 160103 First documented version.
107
+ *
108
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
109
+ */
110
+ define('COMET_CACHE_CACHE_NONCE_VALUES', '%%COMET_CACHE_CACHE_NONCE_VALUES%%');
111
+ }
112
+ if (!defined('COMET_CACHE_CACHE_NONCE_VALUES_WHEN_LOGGED_IN')) {
113
+ /**
114
+ * Cache HTML containing nonce values for Logged-In Users?
115
+ *
116
+ * @since 160103 First documented version.
117
+ *
118
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
119
+ */
120
+ define('COMET_CACHE_CACHE_NONCE_VALUES_WHEN_LOGGED_IN', '%%COMET_CACHE_CACHE_NONCE_VALUES_WHEN_LOGGED_IN%%');
121
+ }
122
+ if (!defined('COMET_CACHE_FEEDS_ENABLE')) {
123
+ /**
124
+ * Cache XML/RSS/Atom feeds?
125
+ *
126
+ * @since 140422 First documented version.
127
+ *
128
+ * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
129
+ */
130
+ define('COMET_CACHE_FEEDS_ENABLE', '%%COMET_CACHE_FEEDS_ENABLE%%');
131
+ }
132
+
133
+ if (!defined('COMET_CACHE_DIR')) {
134
+ /**
135
+ * Directory used to store cache files; relative to `WP_CONTENT_DIR`.
136
+ *
137
+ * @since 140422 First documented version.
138
+ *
139
+ * @var string Absolute server directory path.
140
+ */
141
+ define('COMET_CACHE_DIR', WP_CONTENT_DIR.'/'.'%%COMET_CACHE_DIR%%');
142
+ }
143
+ if (!defined('COMET_CACHE_MAX_AGE')) {
144
+ /**
145
+ * Cache expiration time.
146
+ *
147
+ * @since 140422 First documented version.
148
+ *
149
+ * @var string Anything compatible with PHP's {@link \strtotime()}.
150
+ */
151
+ define('COMET_CACHE_MAX_AGE', '%%COMET_CACHE_MAX_AGE%%');
152
+ }
153
+
154
+ if (!defined('COMET_CACHE_EXCLUDE_URIS')) {
155
+ /**
156
+ * URI exclusions.
157
+ *
158
+ * @since 140422 First documented version.
159
+ *
160
+ * @var string A regular expression; else an empty string.
161
+ */
162
+ define('COMET_CACHE_EXCLUDE_URIS', '%%COMET_CACHE_EXCLUDE_URIS%%');
163
+ }
164
+ if (!defined('COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS')) {
165
+ /**
166
+ * Client-side URI exclusions.
167
+ *
168
+ * @since 151220 Adding support for client-side URI exclusions.
169
+ *
170
+ * @var string A regular expression; else an empty string.
171
+ */
172
+ define('COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS', '%%COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS%%');
173
+ }
174
+ if (!defined('COMET_CACHE_EXCLUDE_REFS')) {
175
+ /**
176
+ * HTTP referrer exclusions.
177
+ *
178
+ * @since 140422 First documented version.
179
+ *
180
+ * @var string A regular expression; else an empty string.
181
+ */
182
+ define('COMET_CACHE_EXCLUDE_REFS', '%%COMET_CACHE_EXCLUDE_REFS%%');
183
+ }
184
+ if (!defined('COMET_CACHE_EXCLUDE_AGENTS')) {
185
+ /**
186
+ * HTTP user-agent exclusions.
187
+ *
188
+ * @since 140422 First documented version.
189
+ *
190
+ * @var string A regular expression; else an empty string.
191
+ */
192
+ define('COMET_CACHE_EXCLUDE_AGENTS', '%%COMET_CACHE_EXCLUDE_AGENTS%%');
193
+ }
194
+ if (!defined('COMET_CACHE_404_CACHE_FILENAME')) {
195
+ /**
196
+ * 404 file name (if applicable).
197
+ *
198
+ * @since 140422 First documented version.
199
+ *
200
+ * @var string A unique file name that will not conflict with real paths.
201
+ * This should NOT include the extension; basename only please.
202
+ */
203
+ define('COMET_CACHE_404_CACHE_FILENAME', '----404----');
204
+ }
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+ $GLOBALS[GLOBAL_NS.'_advanced_cache'] = new AdvancedCache();
221
+ $GLOBALS[GLOBAL_NS.'__advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache'];
222
+ if (!isset($GLOBALS['zencache__advanced_cache'])) {
223
+ $GLOBALS['zencache_advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache'];
224
+ $GLOBALS['zencache__advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache'];
225
+ }
src/includes/templates/gzip-htaccess.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <IfModule deflate_module>
2
+ <IfModule filter_module>
3
+ AddOutputFilterByType DEFLATE text/plain text/html
4
+ AddOutputFilterByType DEFLATE text/xml application/xml application/xhtml+xml application/xml-dtd
5
+ AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml image/svg+xml
6
+ AddOutputFilterByType DEFLATE text/css text/javascript application/javascript application/x-javascript
7
+ AddOutputFilterByType DEFLATE font/otf font/opentype application/font-otf application/x-font-otf
8
+ AddOutputFilterByType DEFLATE font/ttf font/truetype application/font-ttf application/x-font-ttf
9
+ </IfModule>
10
+ </IfModule>
src/includes/templates/htaccess/back-compat/v151114-2.txt ADDED
@@ -0,0 +1,2 @@
 
 
1
+ # BEGIN ZenCache
2
+ # END ZenCache
src/includes/templates/htaccess/back-compat/v151114.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ # BEGIN ZenCache
2
+ <IfModule headers_module>
3
+ <FilesMatch "\.(ttf|ttc|otf|eot|woff|woff2|font.css|css|js)$">
4
+ Header set Access-Control-Allow-Origin "*"
5
+ </FilesMatch>
6
+ </IfModule>
7
+ # END ZenCache
src/includes/translations/comet-cache.pot ADDED
@@ -0,0 +1,1881 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2016 Comet Cache
2
+ # This file is distributed under the same license as the Comet Cache package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: Comet Cache 160211\n"
6
+ "Report-Msgid-Bugs-To: http://wordpress.org/tag/comet-cache\n"
7
+ "POT-Creation-Date: 2016-02-12 01:29:17+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=UTF-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+
15
+ #: plugin.php:13
16
+ msgid "<strong>NOTICE: Comet Cache Minimum PHP Version</strong></h3>"
17
+ msgstr ""
18
+
19
+ #: plugin.php:14
20
+ msgid "<strong>As of December 1st, 2015 Comet Cache requires PHP 5.4 or higher.</strong> Your server is currently running PHP v%1$s. You will need to upgrade to PHP 5.4 or higher to run this version of Comet Cache."
21
+ msgstr ""
22
+
23
+ #: plugin.php:15
24
+ msgid "Learn more about this change here: <a href=\"http://cometcache.com/r/new-minimum-php-version-php-5-4/\" target=\"_blank\">New Minimum PHP Version: PHP 5.4</a>"
25
+ msgstr ""
26
+
27
+ #: plugin.php:17
28
+ msgid "Your server is also running the <strong>outdated PHP APC extension</strong>. Please see: <a href=\"http://cometcache.com/r/php-apc-extension-no-longer-supported/\" target=\"_blank\">PHP APC Extension No Longer Supported</a>"
29
+ msgstr ""
30
+
31
+ #: plugin.php:35
32
+ msgid "<strong>NOTICE: Comet Cache + PHP APC Extension</strong></h3>"
33
+ msgstr ""
34
+
35
+ #: plugin.php:36
36
+ msgid "<strong>As of December 1st, 2015 Comet Cache no longer runs with the outdated PHP APC extension.</strong> It appears that you're currently running PHP v%1$s with APC enabled. You will need to follow one of the actions below to run this version of Comet Cache."
37
+ msgstr ""
38
+
39
+ #: plugin.php:37
40
+ msgid "<h4 style=\"margin:0 0 .5em 0; font-size:1.25em;\"><span class=\"dashicons dashicons-lightbulb\"></span> Options Available (Action Required):</h4>"
41
+ msgstr ""
42
+
43
+ #: plugin.php:39
44
+ msgid "Please add <code>ini_set('apc.cache_by_default', false);</code> to the top of your <code>/wp-config.php</code> file. That will get rid of this message and allow Comet Cache to run without issue."
45
+ msgstr ""
46
+
47
+ #: plugin.php:40
48
+ msgid "Or, contact your web hosting provider and ask about upgrading to PHP v5.5+; which includes the new <a href=\"http://cometcache.com/r/php-opcache-extension/\" target=\"_blank\">OPcache extension for PHP</a>. The new OPcache extension replaces APC in modern versions of PHP."
49
+ msgstr ""
50
+
51
+ #: plugin.php:42
52
+ msgid "To learn more about this change, please see the announcement: <a href=\"http://cometcache.com/r/php-apc-extension-no-longer-supported/\" target=\"_blank\">PHP APC Extension No Longer Supported</a>"
53
+ msgstr ""
54
+
55
+ #: src/includes/classes/AbsBase.php:116
56
+ msgid "Undefined overload property: `%1$s`."
57
+ msgstr ""
58
+
59
+ #: src/includes/classes/AbsBase.php:134
60
+ msgid "Refused to set overload property: `%1$s`."
61
+ msgstr ""
62
+
63
+ #: src/includes/classes/AbsBase.php:151
64
+ msgid "Refused to unset overload property: `%1$s`."
65
+ msgstr ""
66
+
67
+ #: src/includes/classes/AbsBaseAp.php:57
68
+ msgid "Undefined method/closure: `%1$s`."
69
+ msgstr ""
70
+
71
+ #: src/includes/classes/Conflicts.php:91
72
+ msgid "Pro"
73
+ msgstr ""
74
+
75
+ #: src/includes/classes/Conflicts.php:92
76
+ msgid "Lite"
77
+ msgstr ""
78
+
79
+ #: src/includes/classes/Conflicts.php:97
80
+ msgid "<strong>%1$s</strong> is NOT running. A conflicting plugin, <strong>%2$s</strong>, is currently active. Please deactivate the %2$s plugin to clear this message."
81
+ msgstr ""
82
+
83
+ #: src/includes/classes/MenuPageOptions.php:30
84
+ msgid "Wipe Cache (Start Fresh); clears the cache for all sites in this network at once!"
85
+ msgstr ""
86
+
87
+ #: src/includes/classes/MenuPageOptions.php:32
88
+ msgid "Wipe"
89
+ msgstr ""
90
+
91
+ #: src/includes/classes/MenuPageOptions.php:34
92
+ msgid "Clear Cache (Start Fresh)"
93
+ msgstr ""
94
+
95
+ #: src/includes/classes/MenuPageOptions.php:34
96
+ msgid "; affects the current site only."
97
+ msgstr ""
98
+
99
+ #: src/includes/classes/MenuPageOptions.php:36
100
+ msgid "Clear"
101
+ msgstr ""
102
+
103
+ #: src/includes/classes/MenuPageOptions.php:39
104
+ msgid "Restore default plugin options? You will lose all of your current settings! Are you absolutely sure about this?"
105
+ msgstr ""
106
+
107
+ #: src/includes/classes/MenuPageOptions.php:41
108
+ msgid "Restore"
109
+ msgstr ""
110
+
111
+ #: src/includes/classes/MenuPageOptions.php:43
112
+ msgid "All Panels"
113
+ msgstr ""
114
+
115
+ #: src/includes/classes/MenuPageOptions.php:50
116
+ msgid "Pro Updater"
117
+ msgstr ""
118
+
119
+ #: src/includes/classes/MenuPageOptions.php:53
120
+ #: src/includes/closures/Plugin/MenuPageUtils.php:116
121
+ msgid "Preview Pro Features"
122
+ msgstr ""
123
+
124
+ #: src/includes/classes/MenuPageOptions.php:54
125
+ msgid "Pro Upgrade"
126
+ msgstr ""
127
+
128
+ #: src/includes/classes/MenuPageOptions.php:56
129
+ msgid "Newsletter"
130
+ msgstr ""
131
+
132
+ #: src/includes/classes/MenuPageOptions.php:57
133
+ msgid "Beta Testers"
134
+ msgstr ""
135
+
136
+ #: src/includes/classes/MenuPageOptions.php:62
137
+ msgid "%1$s&trade; Pro v%2$s"
138
+ msgstr ""
139
+
140
+ #: src/includes/classes/MenuPageOptions.php:65
141
+ #: src/includes/classes/MenuPageOptions.php:75
142
+ msgid "update available"
143
+ msgstr ""
144
+
145
+ #: src/includes/classes/MenuPageOptions.php:67
146
+ #: src/includes/classes/MenuPageOptions.php:77
147
+ msgid "changelog"
148
+ msgstr ""
149
+
150
+ #: src/includes/classes/MenuPageOptions.php:72
151
+ msgid "%1$s&trade; v%2$s"
152
+ msgstr ""
153
+
154
+ #: src/includes/classes/MenuPageOptions.php:81
155
+ #: src/includes/closures/Plugin/MenuPageUtils.php:73
156
+ #: src/includes/closures/Plugin/MenuPageUtils.php:95
157
+ msgid "Plugin Options"
158
+ msgstr ""
159
+
160
+ #: src/includes/classes/MenuPageOptions.php:93
161
+ msgid "Options updated successfully."
162
+ msgstr ""
163
+
164
+ #: src/includes/classes/MenuPageOptions.php:98
165
+ msgid "Default options successfully restored."
166
+ msgstr ""
167
+
168
+ #: src/includes/classes/MenuPageOptions.php:103
169
+ msgid "Cache wiped across all sites; recreation will occur automatically over time."
170
+ msgstr ""
171
+
172
+ #: src/includes/classes/MenuPageOptions.php:109
173
+ msgid "Cache cleared for main site; recreation will occur automatically over time."
174
+ msgstr ""
175
+
176
+ #: src/includes/classes/MenuPageOptions.php:111
177
+ msgid "Cache cleared for this site; recreation will occur automatically over time."
178
+ msgstr ""
179
+
180
+ #: src/includes/classes/MenuPageOptions.php:117
181
+ #: src/includes/classes/MenuPageOptions.php:122
182
+ msgid "Failed to update your <code>/.htaccess</code> file automatically. Most likely a permissions error. Please make sure it has permissions <code>644</code> or higher (perhaps <code>666</code>). Once you've done this, please try saving the %1$s options again."
183
+ msgstr ""
184
+
185
+ #: src/includes/classes/MenuPageOptions.php:127
186
+ msgid "It appears that your server is running NGINX and does not support <code>.htaccess</code> rules. Please <a href=\"http://cometcache.com/r/kb-article-recommended-nginx-server-configuration/\" target=\"_new\">update your server configuration manually</a>. If you've already updated your NGINX configuration, you can safely <a href=\"http://cometcache.com/r/kb-article-how-do-i-disable-the-nginx-htaccess-notice/\" target=\"_new\">ignore this message</a>."
187
+ msgstr ""
188
+
189
+ #: src/includes/classes/MenuPageOptions.php:132
190
+ msgid "Failed to update your <code>/wp-config.php</code> file automatically. Please add the following line to your <code>/wp-config.php</code> file (right after the opening <code>&lt;?php</code> tag; on it's own line). <pre class=\"code\"><code>&lt;?php<br />define('WP_CACHE', TRUE);</code></pre>"
191
+ msgstr ""
192
+
193
+ #: src/includes/classes/MenuPageOptions.php:137
194
+ msgid "Failed to update your <code>/wp-config.php</code> file automatically. Please remove the following line from your <code>/wp-config.php</code> file, or set <code>WP_CACHE</code> to a <code>FALSE</code> value. <pre class=\"code\"><code>define('WP_CACHE', TRUE);</code></pre>"
195
+ msgstr ""
196
+
197
+ #: src/includes/classes/MenuPageOptions.php:143
198
+ msgid "Failed to update your <code>/wp-content/advanced-cache.php</code> file. Cannot write stat file: <code>%1$s/%2$s-advanced-cache</code>. Please be sure this directory exists (and that it's writable): <code>%1$s</code>. Please use directory permissions <code>755</code> or higher (perhaps <code>777</code>). Once you've done this, please try again."
199
+ msgstr ""
200
+
201
+ #: src/includes/classes/MenuPageOptions.php:145
202
+ msgid "Failed to update your <code>/wp-content/advanced-cache.php</code> file. Most likely a permissions error. Please create an empty file here: <code>/wp-content/advanced-cache.php</code> (just an empty PHP file, with nothing in it); give it permissions <code>644</code> or higher (perhaps <code>666</code>). Once you've done this, please try again."
203
+ msgstr ""
204
+
205
+ #: src/includes/classes/MenuPageOptions.php:151
206
+ msgid "Failed to remove your <code>/wp-content/advanced-cache.php</code> file. Most likely a permissions error. Please delete (or empty the contents of) this file: <code>/wp-content/advanced-cache.php</code>."
207
+ msgstr ""
208
+
209
+ #: src/includes/classes/MenuPageOptions.php:156
210
+ msgid "close"
211
+ msgstr ""
212
+
213
+ #: src/includes/classes/MenuPageOptions.php:157
214
+ msgid "<strong>Pro Features (Preview)</strong> ~ New option panels below. Please explore before <a href=\"http://cometcache.com/prices/\" target=\"_blank\">upgrading <i class=\"si si-heart-o\"></i></a>.<br /><small>NOTE: the free version of %1$s (this lite version) is more-than-adequate for most sites. Please upgrade only if you desire advanced features or would like to support the developer.</small>"
215
+ msgstr ""
216
+
217
+ #: src/includes/classes/MenuPageOptions.php:162
218
+ msgid "%1$s is currently disabled; please review options below."
219
+ msgstr ""
220
+
221
+ #: src/includes/classes/MenuPageOptions.php:172
222
+ msgid "Basic Configuration (Required)"
223
+ msgstr ""
224
+
225
+ #: src/includes/classes/MenuPageOptions.php:173
226
+ msgid "Review these basic options and %1$s&trade; will be ready-to-go!"
227
+ msgstr ""
228
+
229
+ #: src/includes/classes/MenuPageOptions.php:181
230
+ msgid "Enable/Disable"
231
+ msgstr ""
232
+
233
+ #: src/includes/classes/MenuPageOptions.php:186
234
+ msgid "%1$s&trade; = SPEED<em>!!</em>"
235
+ msgstr ""
236
+
237
+ #: src/includes/classes/MenuPageOptions.php:187
238
+ msgid "Yes, enable %1$s&trade;"
239
+ msgstr ""
240
+
241
+ #: src/includes/classes/MenuPageOptions.php:187
242
+ msgid "No, disable."
243
+ msgstr ""
244
+
245
+ #: src/includes/classes/MenuPageOptions.php:188
246
+ msgid "<strong>HUGE Time-Saver:</strong> Approx. 95%% of all WordPress sites running %1$s, simply enable it here; and that's it :-) <strong>No further configuration is necessary (really).</strong> All of the other options (down below) are already tuned for the BEST performance on a typical WordPress installation. Simply enable %1$s here and click \"Save All Changes\". If you get any warnings please follow the instructions given. Otherwise, you're good <i class=\"si si-smile-o\"></i>. This plugin is designed to run just fine like it is. Take it for a spin right away; you can always fine-tune things later if you deem necessary."
247
+ msgstr ""
248
+
249
+ #: src/includes/classes/MenuPageOptions.php:191
250
+ msgid "How Can I Tell %1$s is Working?"
251
+ msgstr ""
252
+
253
+ #: src/includes/classes/MenuPageOptions.php:192
254
+ msgid "First of all, please make sure that you've enabled %1$s here; then scroll down to the bottom of this page and click \"Save All Changes\". All of the other options (below) are already pre-configured for typical usage. Feel free to skip them all for now. You can go back through all of these later and fine-tune things the way you like them."
255
+ msgstr ""
256
+
257
+ #: src/includes/classes/MenuPageOptions.php:193
258
+ msgid "Once %1$s has been enabled, <strong>you'll need to log out (and/or clear browser cookies)</strong>. By default, cache files are NOT served to visitors who are logged-in, and that includes you too ;-) Cache files are NOT served to recent comment authors either. If you've commented (or replied to a comment lately); please clear your browser cookies before testing."
259
+ msgstr ""
260
+
261
+ #: src/includes/classes/MenuPageOptions.php:194
262
+ msgid "<strong>To verify that %1$s is working</strong>, navigate your site like a normal visitor would. Right-click on any page (choose View Source), then scroll to the very bottom of the document. At the bottom, you'll find comments that show %1$s stats and information. You should also notice that page-to-page navigation is <i class=\"si si-flash\"></i> <strong>lightning fast</strong> now that %1$s is running; and it gets faster over time!"
263
+ msgstr ""
264
+
265
+ #: src/includes/classes/MenuPageOptions.php:196
266
+ msgid "Yes, enable notes in the source code so I can see it's working (recommended)."
267
+ msgstr ""
268
+
269
+ #: src/includes/classes/MenuPageOptions.php:197
270
+ msgid "Yes, enable notes in the source code AND show debugging details (not recommended for production)."
271
+ msgstr ""
272
+
273
+ #: src/includes/classes/MenuPageOptions.php:198
274
+ msgid "No, I don't want my source code to contain any of these notes."
275
+ msgstr ""
276
+
277
+ #: src/includes/classes/MenuPageOptions.php:209
278
+ msgid "Plugin Deletion Safeguards"
279
+ msgstr ""
280
+
281
+ #: src/includes/classes/MenuPageOptions.php:214
282
+ msgid "Uninstall on Plugin Deletion; or Safeguard Options?"
283
+ msgstr ""
284
+
285
+ #: src/includes/classes/MenuPageOptions.php:215
286
+ msgid "<strong>Tip:</strong> By default, if you delete %1$s using the plugins menu in WordPress, nothing is lost. However, if you want to completely uninstall %1$s you should set this to <code>Yes</code> and <strong>THEN</strong> deactivate &amp; delete %1$s from the plugins menu in WordPress. This way %1$s will erase your options for the plugin, erase directories/files created by the plugin, remove the <code>advanced-cache.php</code> file, terminate CRON jobs, etc. It erases itself from existence completely."
287
+ msgstr ""
288
+
289
+ #: src/includes/classes/MenuPageOptions.php:217
290
+ msgid "Safeguard my options and the cache (recommended)."
291
+ msgstr ""
292
+
293
+ #: src/includes/classes/MenuPageOptions.php:218
294
+ msgid "Yes, uninstall (completely erase) %1$s on plugin deletion."
295
+ msgstr ""
296
+
297
+ #: src/includes/classes/MenuPageOptions.php:227
298
+ msgid "Advanced Configuration (All Optional)"
299
+ msgstr ""
300
+
301
+ #: src/includes/classes/MenuPageOptions.php:228
302
+ msgid "Recommended for advanced site owners only; already pre-configured for most WP installs."
303
+ msgstr ""
304
+
305
+ #: src/includes/classes/MenuPageOptions.php:237
306
+ msgid "Manual Cache Clearing"
307
+ msgstr ""
308
+
309
+ #: src/includes/classes/MenuPageOptions.php:241
310
+ msgid "Clearing the Cache Manually"
311
+ msgstr ""
312
+
313
+ #: src/includes/classes/MenuPageOptions.php:243
314
+ msgid "Once %1$s is enabled, you will find this new option in your WordPress Admin Bar (screenshot on right). Clicking this button will clear the cache and you can start fresh at anytime (e.g., you can do this manually; and as often as you wish)."
315
+ msgstr ""
316
+
317
+ #: src/includes/classes/MenuPageOptions.php:244
318
+ msgid "Depending on the structure of your site, there could be many reasons to clear the cache. However, the most common reasons are related to Post/Page edits or deletions, Category/Tag edits or deletions, and Theme changes. %1$s handles most scenarios all by itself. However, many site owners like to clear the cache manually; for a variety of reasons (just to force a refresh)."
319
+ msgstr ""
320
+
321
+ #: src/includes/classes/MenuPageOptions.php:246
322
+ msgid "Yes, enable &quot;Clear Cache&quot; button in admin bar"
323
+ msgstr ""
324
+
325
+ #: src/includes/classes/MenuPageOptions.php:247
326
+ msgid "No, I don't intend to clear the cache manually."
327
+ msgstr ""
328
+
329
+ #: src/includes/classes/MenuPageOptions.php:250
330
+ msgid "w/ dropdown options."
331
+ msgstr ""
332
+
333
+ #: src/includes/classes/MenuPageOptions.php:251
334
+ msgid "w/ dropdown options in split menu."
335
+ msgstr ""
336
+
337
+ #: src/includes/classes/MenuPageOptions.php:252
338
+ msgid "w/o dropdown options."
339
+ msgstr ""
340
+
341
+ #: src/includes/classes/MenuPageOptions.php:257
342
+ msgid "Also allow Child Sites in a Network to clear the cache from their Admin Bar?"
343
+ msgstr ""
344
+
345
+ #: src/includes/classes/MenuPageOptions.php:258
346
+ msgid "In a Multisite Network, each child site can clear its own cache. If you want child sites to see the \"Clear Cache\" button in their WordPress Admin Bar, you can specify a comma-delimited list of <a href=\"http://cometcache.com/r/wp-roles-caps/\" target=\"_blank\">Roles and/or Capabilities</a> that are allowed. For example, if I want Administrators to be capable of clearing the cache from their Admin Bar, I could enter <code>administrator</code> here. If I also want to allow Editors, I can use a comma-delimited list: <code>administrator,editor</code>. Or, I could use a single Capability of: <code>edit_others_posts</code>; which covers both Administrators &amp; Editors at the same time."
347
+ msgstr ""
348
+
349
+ #: src/includes/classes/MenuPageOptions.php:260
350
+ #: src/includes/classes/MenuPageOptions.php:451
351
+ msgid "<strong>Note:</strong> As a security measure, in addition to the Role(s) and/or Capabilities that you list here, each child site owner must also have the ability to <code>%1$s</code>."
352
+ msgstr ""
353
+
354
+ #: src/includes/classes/MenuPageOptions.php:265
355
+ msgid "Also allow others to clear the cache from their Admin Bar?"
356
+ msgstr ""
357
+
358
+ #: src/includes/classes/MenuPageOptions.php:266
359
+ msgid "If you want others to see the \"Clear Cache\" button in their WordPress Admin Bar, you can specify a comma-delimited list of <a href=\"http://cometcache.com/r/wp-roles-caps/\" target=\"_blank\">Roles and/or Capabilities</a> that are allowed. For example, if I want Editors to be capable of clearing the cache from their Admin Bar, I could enter <code>editor</code> here. If I also want to allow Authors, I can use a comma-delimited list: <code>editor,author</code>. Or, I could use a single Capability of: <code>publish_posts</code>; which covers both Editors &amp; Authors at the same time."
360
+ msgstr ""
361
+
362
+ #: src/includes/classes/MenuPageOptions.php:268
363
+ #: src/includes/classes/MenuPageOptions.php:459
364
+ msgid "<strong>Note:</strong> As a security measure, in addition to the Role(s) and/or Capabilities that you list here, each user must also have the ability to <code>%1$s</code>."
365
+ msgstr ""
366
+
367
+ #: src/includes/classes/MenuPageOptions.php:274
368
+ msgid "Clear the <a href=\"http://cometcache.com/r/php-opcache/\" target=\"_blank\">PHP OPcache</a> Too?"
369
+ msgstr ""
370
+
371
+ #: src/includes/classes/MenuPageOptions.php:275
372
+ msgid "If you clear the cache manually, do you want %1$s to clear the PHP OPcache too? This is not necessary, but if you want a truly clean start, this will clear all PHP files in the server's opcode cache also. Note: If you don't already know what the PHP OPcache is, it is suggested that you leave this disabled. It really is not necessary. This is just an added feature for advanced users."
373
+ msgstr ""
374
+
375
+ #: src/includes/classes/MenuPageOptions.php:277
376
+ msgid "No, I don't use the PHP OPcache extension; or, I don't want the opcode cache cleared."
377
+ msgstr ""
378
+
379
+ #: src/includes/classes/MenuPageOptions.php:278
380
+ msgid "Yes, if the PHP OPcache extension is enabled, also clear the entire PHP opcode cache."
381
+ msgstr ""
382
+
383
+ #: src/includes/classes/MenuPageOptions.php:283
384
+ msgid "Clear the <a href=\"http://websharks-inc.com/product/s2clean/\" target=\"_blank\">s2Clean</a> Cache Too?"
385
+ msgstr ""
386
+
387
+ #: src/includes/classes/MenuPageOptions.php:284
388
+ msgid "If the s2Clean theme is installed, and you clear the cache manually, %1$s can clear the s2Clean Markdown cache too (if you've enabled Markdown processing with s2Clean)."
389
+ msgstr ""
390
+
391
+ #: src/includes/classes/MenuPageOptions.php:286
392
+ msgid "Yes, if the s2Clean theme is installed, also clear s2Clean-related caches."
393
+ msgstr ""
394
+
395
+ #: src/includes/classes/MenuPageOptions.php:287
396
+ msgid "No, I don't use s2Clean; or, I don't want s2Clean-related caches cleared."
397
+ msgstr ""
398
+
399
+ #: src/includes/classes/MenuPageOptions.php:291
400
+ msgid "Evaluate Custom PHP Code when Clearing the Cache?"
401
+ msgstr ""
402
+
403
+ #: src/includes/classes/MenuPageOptions.php:292
404
+ msgid "If you have any custom routines you'd like to process when the cache is cleared manually, please enter PHP code here. If your PHP code outputs a message, it will be displayed along with any other notes from %1$s itself. This feature is intended for developers, and it may come in handy if you need to clear any system caches not already covered by %1$s configuration options."
405
+ msgstr ""
406
+
407
+ #: src/includes/classes/MenuPageOptions.php:294
408
+ msgid "<strong>Example:</strong> <code>&lt;?php apc_clear_cache(); echo '&lt;p&gt;Also cleared APC cache.&lt;/p&gt;'; ?&gt;</code>"
409
+ msgstr ""
410
+
411
+ #: src/includes/classes/MenuPageOptions.php:297
412
+ msgid "Clear the CDN Cache Too?"
413
+ msgstr ""
414
+
415
+ #: src/includes/classes/MenuPageOptions.php:298
416
+ msgid "If you clear the cache manually, do you want %1$s to automatically bump the CDN invalidation counter too? i.e., automatically increment the <code>?%2$s=[counter]</code> in all static CDN URLs?"
417
+ msgstr ""
418
+
419
+ #: src/includes/classes/MenuPageOptions.php:300
420
+ msgid "No, I don't use Static CDN Filters; or, I don't want the CDN cache cleared."
421
+ msgstr ""
422
+
423
+ #: src/includes/classes/MenuPageOptions.php:301
424
+ msgid "Yes, if Static CDN Filters are enabled, also clear the CDN cache."
425
+ msgstr ""
426
+
427
+ #: src/includes/classes/MenuPageOptions.php:312
428
+ msgid "Automatic Cache Clearing"
429
+ msgstr ""
430
+
431
+ #: src/includes/classes/MenuPageOptions.php:317
432
+ msgid "Clearing the Cache Automatically"
433
+ msgstr ""
434
+
435
+ #: src/includes/classes/MenuPageOptions.php:319
436
+ msgid "This is built into the %1$s plugin; i.e., this functionality is \"always on\". If you edit a Post/Page (or delete one), %1$s will automatically clear the cache file(s) associated with that content. This way a new updated version of the cache will be created automatically the next time this content is accessed. Simple updates like this occur each time you make changes in the Dashboard, and %1$s will notify you of these as they occur. %1$s monitors changes to Posts (of any kind, including Pages), Categories, Tags, Links, Themes (even Users), and more."
437
+ msgstr ""
438
+
439
+ #: src/includes/classes/MenuPageOptions.php:323
440
+ msgid "Yes, enable %1$s notifications in the Dashboard when changes are detected &amp; one or more cache files are cleared automatically."
441
+ msgstr ""
442
+
443
+ #: src/includes/classes/MenuPageOptions.php:324
444
+ msgid "No, I don't want to know (don't really care) what %1$s is doing behind-the-scene."
445
+ msgstr ""
446
+
447
+ #: src/includes/classes/MenuPageOptions.php:329
448
+ msgid "Primary Page Options"
449
+ msgstr ""
450
+
451
+ #: src/includes/classes/MenuPageOptions.php:331
452
+ msgid "Auto-Clear Designated \"Home Page\" Too?"
453
+ msgstr ""
454
+
455
+ #: src/includes/classes/MenuPageOptions.php:332
456
+ msgid "On many sites, the Home Page (aka: the Front Page) offers an archive view of all Posts (or even Pages). Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the \"Home Page\"?"
457
+ msgstr ""
458
+
459
+ #: src/includes/classes/MenuPageOptions.php:334
460
+ msgid "Yes, if any single Post/Page is cleared/reset; also clear the \"Home Page\"."
461
+ msgstr ""
462
+
463
+ #: src/includes/classes/MenuPageOptions.php:335
464
+ msgid "No, my Home Page does not provide a list of Posts/Pages; e.g., this is not necessary."
465
+ msgstr ""
466
+
467
+ #: src/includes/classes/MenuPageOptions.php:337
468
+ msgid "Auto-Clear Designated \"Posts Page\" Too?"
469
+ msgstr ""
470
+
471
+ #: src/includes/classes/MenuPageOptions.php:338
472
+ msgid "On many sites, the Posts Page (aka: the Blog Page) offers an archive view of all Posts (or even Pages). Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the \"Posts Page\"?"
473
+ msgstr ""
474
+
475
+ #: src/includes/classes/MenuPageOptions.php:340
476
+ msgid "Yes, if any single Post/Page is cleared/reset; also clear the \"Posts Page\"."
477
+ msgstr ""
478
+
479
+ #: src/includes/classes/MenuPageOptions.php:341
480
+ msgid "No, I don't use a separate Posts Page; e.g., my Home Page IS my Posts Page."
481
+ msgstr ""
482
+
483
+ #: src/includes/classes/MenuPageOptions.php:345
484
+ msgid "Author, Archive, and Tag/Term Options"
485
+ msgstr ""
486
+
487
+ #: src/includes/classes/MenuPageOptions.php:347
488
+ msgid "Auto-Clear \"Author Page\" Too?"
489
+ msgstr ""
490
+
491
+ #: src/includes/classes/MenuPageOptions.php:348
492
+ msgid "On many sites, each author has a related \"Author Page\" that offers an archive view of all posts associated with that author. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the related \"Author Page\"?"
493
+ msgstr ""
494
+
495
+ #: src/includes/classes/MenuPageOptions.php:350
496
+ msgid "Yes, if any single Post/Page is cleared/reset; also clear the \"Author Page\"."
497
+ msgstr ""
498
+
499
+ #: src/includes/classes/MenuPageOptions.php:351
500
+ msgid "No, my site doesn't use multiple authors and/or I don't have any \"Author Page\" archive views."
501
+ msgstr ""
502
+
503
+ #: src/includes/classes/MenuPageOptions.php:354
504
+ msgid "Auto-Clear \"Category Archives\" Too?"
505
+ msgstr ""
506
+
507
+ #: src/includes/classes/MenuPageOptions.php:355
508
+ msgid "On many sites, each post is associated with at least one Category. Each category then has an archive view that contains all the posts within that category. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Category archive views?"
509
+ msgstr ""
510
+
511
+ #: src/includes/classes/MenuPageOptions.php:357
512
+ msgid "Yes, if any single Post/Page is cleared/reset; also clear the associated Category archive views."
513
+ msgstr ""
514
+
515
+ #: src/includes/classes/MenuPageOptions.php:358
516
+ msgid "No, my site doesn't use Categories and/or I don't have any Category archive views."
517
+ msgstr ""
518
+
519
+ #: src/includes/classes/MenuPageOptions.php:361
520
+ msgid "Auto-Clear \"Tag Archives\" Too?"
521
+ msgstr ""
522
+
523
+ #: src/includes/classes/MenuPageOptions.php:362
524
+ msgid "On many sites, each post may be associated with at least one Tag. Each tag then has an archive view that contains all the posts assigned that tag. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Tag archive views?"
525
+ msgstr ""
526
+
527
+ #: src/includes/classes/MenuPageOptions.php:364
528
+ msgid "Yes, if any single Post/Page is cleared/reset; also clear the associated Tag archive views."
529
+ msgstr ""
530
+
531
+ #: src/includes/classes/MenuPageOptions.php:365
532
+ msgid "No, my site doesn't use Tags and/or I don't have any Tag archive views."
533
+ msgstr ""
534
+
535
+ #: src/includes/classes/MenuPageOptions.php:368
536
+ msgid "Auto-Clear \"Custom Term Archives\" Too?"
537
+ msgstr ""
538
+
539
+ #: src/includes/classes/MenuPageOptions.php:369
540
+ msgid "Most sites do not use any custom Terms so it should be safe to leave this disabled. However, if your site uses custom Terms and they have their own Term archive views, you may want to clear those when the associated post is cleared. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Tag archive views?"
541
+ msgstr ""
542
+
543
+ #: src/includes/classes/MenuPageOptions.php:371
544
+ msgid "Yes, if any single Post/Page is cleared/reset; also clear any associated custom Term archive views."
545
+ msgstr ""
546
+
547
+ #: src/includes/classes/MenuPageOptions.php:372
548
+ msgid "No, my site doesn't use any custom Terms and/or I don't have any custom Term archive views."
549
+ msgstr ""
550
+
551
+ #: src/includes/classes/MenuPageOptions.php:375
552
+ msgid "Auto-Clear \"Custom Post Type Archives\" Too?"
553
+ msgstr ""
554
+
555
+ #: src/includes/classes/MenuPageOptions.php:376
556
+ msgid "Most sites do not use any Custom Post Types so it should be safe to disable this option. However, if your site uses Custom Post Types and they have their own Custom Post Type archive views, you may want to clear those when any associated post is cleared. Therefore, if a single Post with a Custom Post Type is changed in some way; and %1$s clears/resets the cache for that post, would you like %1$s to also clear any existing cache files for the associated Custom Post Type archive views?"
557
+ msgstr ""
558
+
559
+ #: src/includes/classes/MenuPageOptions.php:378
560
+ msgid "Yes, if any single Post with a Custom Post Type is cleared/reset; also clear any associated Custom Post Type archive views."
561
+ msgstr ""
562
+
563
+ #: src/includes/classes/MenuPageOptions.php:379
564
+ msgid "No, my site doesn't use any Custom Post Types and/or I don't have any Custom Post Type archive views."
565
+ msgstr ""
566
+
567
+ #: src/includes/classes/MenuPageOptions.php:383
568
+ msgid "Feed-Related Options"
569
+ msgstr ""
570
+
571
+ #: src/includes/classes/MenuPageOptions.php:385
572
+ msgid "Auto-Clear \"RSS/RDF/ATOM Feeds\" Too?"
573
+ msgstr ""
574
+
575
+ #: src/includes/classes/MenuPageOptions.php:386
576
+ msgid "If you enable Feed Caching (below), this can be quite handy. If enabled, when you update a Post/Page, approve a Comment, or make other changes where %1$s can detect that certain types of Feeds should be cleared to keep your site up-to-date, then %1$s will do this for you automatically. For instance, the blog's master feed, the blog's master comments feed, feeds associated with comments on a Post/Page, term-related feeds (including mixed term-related feeds), author-related feeds, etc. Under various circumstances (i.e., as you work in the Dashboard) these can be cleared automatically to keep your site up-to-date."
577
+ msgstr ""
578
+
579
+ #: src/includes/classes/MenuPageOptions.php:388
580
+ msgid "Yes, automatically clear RSS/RDF/ATOM Feeds from the cache when certain changes occur."
581
+ msgstr ""
582
+
583
+ #: src/includes/classes/MenuPageOptions.php:389
584
+ msgid "No, I don't have Feed Caching enabled, or I prefer not to automatically clear Feeds."
585
+ msgstr ""
586
+
587
+ #: src/includes/classes/MenuPageOptions.php:393
588
+ msgid "Sitemap-Related Options"
589
+ msgstr ""
590
+
591
+ #: src/includes/classes/MenuPageOptions.php:395
592
+ msgid "Auto-Clear \"XML Sitemaps\" Too?"
593
+ msgstr ""
594
+
595
+ #: src/includes/classes/MenuPageOptions.php:396
596
+ msgid "If you're generating XML Sitemaps with a plugin like <a href=\"http://wordpress.org/plugins/google-sitemap-generator/\" target=\"_blank\">Google XML Sitemaps</a>, you can tell %1$s to automatically clear the cache of any XML Sitemaps whenever it clears a Post/Page. Note: This does NOT clear the XML Sitemap itself of course, only the cache. The point being, to clear the cache and allow changes to a Post/Page to be reflected by a fresh copy of your XML Sitemap; sooner rather than later."
597
+ msgstr ""
598
+
599
+ #: src/includes/classes/MenuPageOptions.php:398
600
+ msgid "Yes, if any single Post/Page is cleared/reset; also clear the cache for any XML Sitemaps."
601
+ msgstr ""
602
+
603
+ #: src/includes/classes/MenuPageOptions.php:399
604
+ msgid "No, my site doesn't use any XML Sitemaps and/or I prefer NOT to clear the cache for XML Sitemaps."
605
+ msgstr ""
606
+
607
+ #: src/includes/classes/MenuPageOptions.php:402
608
+ msgid "<strong style=\"font-size:110%;\">XML Sitemap Patterns...</strong> A default value of <code>/sitemap**.xml</code> covers all XML Sitemaps for most installations. However, you may customize this further if you deem necessary. <strong>Please list one pattern per line.</strong> These XML Sitemap Pattern searches are performed against the <a href=\"https://gist.github.com/jaswsinc/338b6eb03a36c048c26f\" target=\"_blank\">REQUEST_URI</a>. A wildcard <code>**</code> character can also be used when necessary; e.g., <code>/sitemap**.xml</code> (where <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes). Other special characters include: <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href=\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
609
+ msgstr ""
610
+
611
+ #: src/includes/classes/MenuPageOptions.php:405
612
+ msgid "In a Multisite Network, each child blog (whether it be a sub-domain, a sub-directory, or a mapped domain); will automatically change the leading <code>http://[sub.]domain/[sub-directory]</code> used in pattern matching. In short, there is no need to add sub-domains or sub-directories for each child blog in these patterns. Please include only the <a href=\"https://gist.github.com/jaswsinc/338b6eb03a36c048c26f\" target=\"_blank\">REQUEST_URI</a> (i.e., the path) which leads to the XML Sitemap on all child blogs in the network."
613
+ msgstr ""
614
+
615
+ #: src/includes/classes/MenuPageOptions.php:411
616
+ msgid "Misc. Auto-Clear Options"
617
+ msgstr ""
618
+
619
+ #: src/includes/classes/MenuPageOptions.php:412
620
+ msgid "Auto-Clear a List of Custom URLs Too?"
621
+ msgstr ""
622
+
623
+ #: src/includes/classes/MenuPageOptions.php:413
624
+ msgid "When you update a Post/Page, approve a Comment, or make other changes where %1$s can detect that a Post/Page cache should be cleared to keep your site up-to-date; then %1$s will also clear a list of custom URLs that you list here. <strong>Please list one URL per line.</strong> A wildcard <code>*</code> character can also be used when necessary; e.g., <code>/category/abc-followed-by-*</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href =\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
625
+ msgstr ""
626
+
627
+ #: src/includes/classes/MenuPageOptions.php:426
628
+ msgid "Cache-Related Statistics"
629
+ msgstr ""
630
+
631
+ #: src/includes/classes/MenuPageOptions.php:431
632
+ msgid "Enable Cache-Related Stats &amp; Charts?"
633
+ msgstr ""
634
+
635
+ #: src/includes/classes/MenuPageOptions.php:432
636
+ msgid "%1$s can collect and display cache-related statistics (including charts). Stats are displayed in the WordPress Admin Bar, and also in your Dashboard under: <strong>%1$s → Stats/Charts</strong>. Cache-related stats provide you with a quick look at what's happening behind-the-scenes. Your site grows faster and faster as the cache grows larger in size."
637
+ msgstr ""
638
+
639
+ #: src/includes/classes/MenuPageOptions.php:434
640
+ msgid "Yes, enable stats collection &amp; the menu page in WordPress for viewing stats."
641
+ msgstr ""
642
+
643
+ #: src/includes/classes/MenuPageOptions.php:435
644
+ msgid "No, I have a VERY large site and I want to avoid any unnecessary directory scans."
645
+ msgstr ""
646
+
647
+ #: src/includes/classes/MenuPageOptions.php:437
648
+ msgid "<strong>Note:</strong> %1$s does a great job of collecting stats, in ways that don't cause a performance issue. In addition, as your cache grows larger than several hundred files in total size, statistics are collected less often and at longer intervals. All of that being said, if you run a VERY large site (e.g., more than 20K posts), you might want to disable stats collection in favor of blazing fast speeds not impeded by any directory scans needed to collect stats."
649
+ msgstr ""
650
+
651
+ #: src/includes/classes/MenuPageOptions.php:441
652
+ msgid "Show Stats in the WordPress Admin Bar?"
653
+ msgstr ""
654
+
655
+ #: src/includes/classes/MenuPageOptions.php:443
656
+ msgid "Yes, enable stats in the WordPress admin bar."
657
+ msgstr ""
658
+
659
+ #: src/includes/classes/MenuPageOptions.php:444
660
+ msgid "No, I'll review stats from the menu page in WordPress if I need to."
661
+ msgstr ""
662
+
663
+ #: src/includes/classes/MenuPageOptions.php:448
664
+ msgid "Allow Child Sites in a Network to See Stats in Admin Bar?"
665
+ msgstr ""
666
+
667
+ #: src/includes/classes/MenuPageOptions.php:449
668
+ msgid "In a Multisite Network, each child site has stats of its own. If you want child sites to see cache-related stats in their WordPress Admin Bar, you can specify a comma-delimited list of <a href=\"http://cometcache.com/r/wp-roles-caps/\" target=\"_blank\">Roles and/or Capabilities</a> that are allowed to see stats. For example, if I want the Administrator to see stats in their Admin Bar, I could enter <code>administrator</code> here. If I also want to show stats to Editors, I can use a comma-delimited list: <code>administrator,editor</code>. Or, I could use a single Capability of: <code>edit_others_posts</code>; which covers both Administrators &amp; Editors at the same time."
669
+ msgstr ""
670
+
671
+ #: src/includes/classes/MenuPageOptions.php:456
672
+ msgid "Allow Others to See Stats in Admin Bar?"
673
+ msgstr ""
674
+
675
+ #: src/includes/classes/MenuPageOptions.php:457
676
+ msgid "If you want others to see cache-related stats in their WordPress Admin Bar, you can specify a comma-delimited list of <a href=\"http://cometcache.com/r/wp-roles-caps/\" target=\"_blank\">Roles and/or Capabilities</a> that are allowed to see stats. For example, if I want Editors to see stats in their Admin Bar, I could enter <code>editor</code> here. If I also want to show stats to Authors, I can use a comma-delimited list: <code>editor,author</code>. Or, I could use a single Capability of: <code>publish_posts</code>; which covers both Editors &amp; Authors at the same time."
677
+ msgstr ""
678
+
679
+ #: src/includes/classes/MenuPageOptions.php:473
680
+ msgid "Cache Directory"
681
+ msgstr ""
682
+
683
+ #: src/includes/classes/MenuPageOptions.php:477
684
+ msgid "Base Cache Directory (Must be Writable; i.e., <a href=\"http://cometcache.com/r/wp-file-permissions/\" target=\"_blank\">Permissions</a> <code>755</code> or Higher)"
685
+ msgstr ""
686
+
687
+ #: src/includes/classes/MenuPageOptions.php:478
688
+ msgid "This is where %1$s will store the cached version of your site. If you're not sure how to deal with directory permissions, don't worry too much about this. If there is a problem, %1$s will let you know about it. By default, this directory is created by %1$s and the permissions are setup automatically. In most cases there is nothing more you need to do."
689
+ msgstr ""
690
+
691
+ #: src/includes/classes/MenuPageOptions.php:489
692
+ msgid "Cache Expiration Time"
693
+ msgstr ""
694
+
695
+ #: src/includes/classes/MenuPageOptions.php:494
696
+ msgid "Automatic Expiration Time (Max Age)"
697
+ msgstr ""
698
+
699
+ #: src/includes/classes/MenuPageOptions.php:495
700
+ msgid "If you don't update your site much, you could set this to <code>6 months</code> and optimize everything even further. The longer the Cache Expiration Time is, the greater your performance gain. Alternatively, the shorter the Expiration Time, the fresher everything will remain on your site. A default value of <code>7 days</code> (recommended); is a good conservative middle-ground."
701
+ msgstr ""
702
+
703
+ #: src/includes/classes/MenuPageOptions.php:496
704
+ msgid "Keep in mind that your Expiration Time is only one part of the big picture. %1$s will also clear the cache automatically as changes are made to the site (i.e., you edit a post, someone comments on a post, you change your theme, you add a new navigation menu item, etc., etc.). Thus, your Expiration Time is really just a fallback; e.g., the maximum amount of time that a cache file could ever possibly live."
705
+ msgstr ""
706
+
707
+ #: src/includes/classes/MenuPageOptions.php:497
708
+ msgid "All of that being said, you could set this to just <code>60 seconds</code> and you would still see huge differences in speed and performance. If you're just starting out with %1$s (perhaps a bit nervous about old cache files being served to your visitors); you could set this to something like <code>30 minutes</code> and experiment with it while you build confidence in %1$s. It's not necessary to do so, but many site owners have reported this makes them feel like they're more-in-control when the cache has a short expiration time. All-in-all, it's a matter of preference <i class=\"si si-smile-o\"></i>."
709
+ msgstr ""
710
+
711
+ #: src/includes/classes/MenuPageOptions.php:499
712
+ msgid "<strong>Tip:</strong> the value that you specify here MUST be compatible with PHP's <a href=\"http://php.net/manual/en/function.strtotime.php\" target=\"_blank\" style=\"text-decoration:none;\"><code>strtotime()</code></a> function. Examples: <code>30 seconds</code>, <code>2 hours</code>, <code>7 days</code>, <code>6 months</code>, <code>1 year</code>."
713
+ msgstr ""
714
+
715
+ #: src/includes/classes/MenuPageOptions.php:500
716
+ msgid "<strong>Note:</strong> %1$s will never serve a cache file that is older than what you specify here (even if one exists in your cache directory; stale cache files are never used). In addition, a WP Cron job will automatically cleanup your cache directory (once per hour); purging expired cache files periodically. This prevents a HUGE cache from building up over time, creating a potential storage issue."
717
+ msgstr ""
718
+
719
+ #: src/includes/classes/MenuPageOptions.php:504
720
+ msgid "Cache Cleanup Schedule"
721
+ msgstr ""
722
+
723
+ #: src/includes/classes/MenuPageOptions.php:505
724
+ msgid "If you have an extremely large site and you lower the default Cache Expiration Time of <code>7 days</code>, expired cache files can build up more quickly. By default, %1$s cleans up expired cache files via <a href=\"http://cometcache.com/r/wp_cron-functions/\" target=\"_blank\">WP Cron</a> at an <code>hourly</code> interval, but you can tell %1$s to use a custom Cache Cleanup Schedule below to run the cleanup process more or less frequently, depending on your specific needs."
725
+ msgstr ""
726
+
727
+ #: src/includes/classes/MenuPageOptions.php:517
728
+ msgid "Disable Cache Expiration If Server Load Average is High?"
729
+ msgstr ""
730
+
731
+ #: src/includes/classes/MenuPageOptions.php:518
732
+ msgid "If you have high traffic at certain times of the day, %1$s can be told to check the current load average via <a href=\"http://cometcache.com/r/system-load-average-via-php/\" target=\"_blank\"><code>sys_getloadavg()</code></a>. If your server's load average has been high in the last 15 minutes or so, cache expiration is disabled automatically to help reduce stress on the server; i.e., to avoid generating a new version of the cache while the server is very busy."
733
+ msgstr ""
734
+
735
+ #: src/includes/classes/MenuPageOptions.php:519
736
+ msgid "To enable this functionality you should first determine what a high load average is for your server. If you log into your machine via SSH you can run the <code>top</code> command to get a feel for what a high load average looks like. Once you know the number, you can enter it in the field below; e.g., <code>1.05</code> might be a high load average for a server with one CPU. See also: <a href=\"http://cometcache.com/r/understanding-load-average/\" target=\"_blank\">Understanding Load Average</a>"
737
+ msgstr ""
738
+
739
+ #: src/includes/classes/MenuPageOptions.php:522
740
+ msgid "<strong>Note:</strong> It appears that your server is running Windows. The <code>sys_getloadavg()</code> function has not been implemented in PHP for Windows servers yet."
741
+ msgstr ""
742
+
743
+ #: src/includes/classes/MenuPageOptions.php:524
744
+ msgid "<strong>Note:</strong> <code>sys_getloadavg()</code> has been disabled by your web hosting company or is not available on your server."
745
+ msgstr ""
746
+
747
+ #: src/includes/classes/MenuPageOptions.php:537
748
+ msgid "Client-Side Cache"
749
+ msgstr ""
750
+
751
+ #: src/includes/classes/MenuPageOptions.php:542
752
+ msgid "Allow Double-Caching In The Client-Side Browser?"
753
+ msgstr ""
754
+
755
+ #: src/includes/classes/MenuPageOptions.php:543
756
+ msgid "Recommended setting: <code>No</code> (for membership sites, very important). Otherwise, <code>Yes</code> would be better (if users do NOT log in/out of your site)."
757
+ msgstr ""
758
+
759
+ #: src/includes/classes/MenuPageOptions.php:544
760
+ msgid "%1$s handles content delivery through its ability to communicate with a browser using PHP. If you allow a browser to (cache) the caching system itself, you are momentarily losing some control; and this can have a negative impact on users that see more than one version of your site; e.g., one version while logged-in, and another while NOT logged-in. For instance, a user may log out of your site, but upon logging out they report seeing pages on the site which indicate they are STILL logged in (even though they're not — that's bad). This can happen if you allow a client-side cache, because their browser may cache web pages they visited while logged into your site which persist even after logging out. Sending no-cache headers will work to prevent this issue."
761
+ msgstr ""
762
+
763
+ #: src/includes/classes/MenuPageOptions.php:545
764
+ msgid "All of that being said, if all you care about is blazing fast speed and users don't log in/out of your site (only you do); you can safely set this to <code>Yes</code> (recommended in this case). Allowing a client-side browser cache will improve speed and reduce outgoing bandwidth when this option is feasible."
765
+ msgstr ""
766
+
767
+ #: src/includes/classes/MenuPageOptions.php:547
768
+ msgid "No, prevent a client-side browser cache (safest option)."
769
+ msgstr ""
770
+
771
+ #: src/includes/classes/MenuPageOptions.php:548
772
+ msgid "Yes, I will allow a client-side browser cache of pages on the site."
773
+ msgstr ""
774
+
775
+ #: src/includes/classes/MenuPageOptions.php:550
776
+ msgid "<strong>Tip:</strong> Setting this to <code>No</code> is highly recommended when running a membership plugin like <a href=\"http://wordpress.org/plugins/s2member/\" target=\"_blank\">s2Member</a> (as one example). In fact, many plugins like s2Member will send <a href=\"http://codex.wordpress.org/Function_Reference/nocache_headers\" target=\"_blank\">nocache_headers()</a> on their own, so your configuration here will likely be overwritten when you run such plugins (which is better anyway). In short, if you run a membership plugin, you should NOT allow a client-side browser cache."
777
+ msgstr ""
778
+
779
+ #: src/includes/classes/MenuPageOptions.php:551
780
+ msgid "<strong>Tip:</strong> Setting this to <code>No</code> will NOT impact static content; e.g., CSS, JS, images, or other media. This setting pertains only to dynamic PHP scripts which produce content generated by WordPress."
781
+ msgstr ""
782
+
783
+ #: src/includes/classes/MenuPageOptions.php:552
784
+ msgid "<strong>Advanced Tip:</strong> if you have this set to <code>No</code>, but you DO want to allow a few special URLs to be cached by the browser; you can add this parameter to your URL <code>?%2$sABC=1</code>. This tells %1$s that it's OK for the browser to cache that particular URL. In other words, the <code>%2$sABC=1</code> parameter tells %1$s NOT to send no-cache headers to the browser."
785
+ msgstr ""
786
+
787
+ #: src/includes/classes/MenuPageOptions.php:553
788
+ msgid "Exclusion Patterns for Client-Side Caching"
789
+ msgstr ""
790
+
791
+ #: src/includes/classes/MenuPageOptions.php:554
792
+ msgid "When you enable Client-Side Caching above, you may want to prevent certain pages on your site from being cached by a client-side browser. This is where you will enter those if you need to (one per line). Searches are performed against the <a href=\"https://gist.github.com/jaswsinc/338b6eb03a36c048c26f\" target=\"_blank\" style=\"text-decoration:none;\"><code>REQUEST_URI</code></a>; i.e., <code>/path/?query</code> (caSe insensitive). So, don't put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard <code>*</code> character can also be used when necessary; e.g., <code>/category/abc-followed-by-*</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href =\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
793
+ msgstr ""
794
+
795
+ #: src/includes/classes/MenuPageOptions.php:556
796
+ #: src/includes/classes/MenuPageOptions.php:680
797
+ #: src/includes/classes/MenuPageOptions.php:841
798
+ msgid "<strong>Tip:</strong> let's use this example URL: <code>http://www.example.com/post/example-post-123</code>. To exclude this URL, you would put this line into the field above: <code>/post/example-post-123</code>. Or, you could also just put in a small fragment, like: <code>example</code> or <code>example-*-123</code> and that would exclude any URI containing that word fragment."
799
+ msgstr ""
800
+
801
+ #: src/includes/classes/MenuPageOptions.php:557
802
+ #: src/includes/classes/MenuPageOptions.php:681
803
+ #: src/includes/classes/MenuPageOptions.php:699
804
+ #: src/includes/classes/MenuPageOptions.php:717
805
+ #: src/includes/classes/MenuPageOptions.php:833
806
+ #: src/includes/classes/MenuPageOptions.php:837
807
+ #: src/includes/classes/MenuPageOptions.php:842
808
+ #: src/includes/classes/MenuPageOptions.php:959
809
+ msgid "<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line."
810
+ msgstr ""
811
+
812
+ #: src/includes/classes/MenuPageOptions.php:568
813
+ msgid "Logged-In Users"
814
+ msgstr ""
815
+
816
+ #: src/includes/classes/MenuPageOptions.php:573
817
+ msgid "Caching Enabled for Logged-In Users &amp; Comment Authors?"
818
+ msgstr ""
819
+
820
+ #: src/includes/classes/MenuPageOptions.php:574
821
+ msgid "This should almost ALWAYS be set to <code>No</code>. Most sites will NOT want to cache content generated while a user is logged-in. Doing so could result in a cache of dynamic content generated specifically for a particular user, where the content being cached may contain details that pertain only to the user that was logged-in when the cache was generated. Imagine visiting a website that says you're logged-in as Billy Bob (but you're not Billy Bob; NOT good). In short, do NOT turn this on unless you know what you're doing."
822
+ msgstr ""
823
+
824
+ #: src/includes/classes/MenuPageOptions.php:576
825
+ msgid "<strong>Exception (Membership Sites):</strong> If you run a site with many users and the majority of your traffic comes from users who ARE logged-in, please choose: <code>Yes (maintain separate cache)</code>. %1$s will operate normally; but when a user is logged-in, the cache is user-specific. %1$s will intelligently refresh the cache when/if a user submits a form on your site with the GET or POST method. Or, if you make changes to their account (or another plugin makes changes to their account); including user <a href=\"http://codex.wordpress.org/Function_Reference/update_user_option\" target=\"_blank\">option</a>|<a href=\"http://codex.wordpress.org/Function_Reference/update_user_meta\" target=\"_blank\">meta</a> additions, updates &amp; deletions too. However, please note that enabling this feature (e.g., user-specific cache entries); will eat up MUCH more disk space. That being said, the benefits of this feature for most sites will outweigh the disk overhead (e.g., it's NOT an issue in most cases). Unless you are short on disk space (or you have MANY thousands of users), the disk overhead is neglible."
826
+ msgstr ""
827
+
828
+ #: src/includes/classes/MenuPageOptions.php:578
829
+ msgid "No, do NOT cache; or serve a cache file when a user is logged-in (safest option)."
830
+ msgstr ""
831
+
832
+ #: src/includes/classes/MenuPageOptions.php:579
833
+ msgid "Yes, and maintain a separate cache for each user (recommended for membership sites)."
834
+ msgstr ""
835
+
836
+ #: src/includes/classes/MenuPageOptions.php:582
837
+ msgid "Yes, but DON'T maintain a separate cache for each user (I know what I'm doing)."
838
+ msgstr ""
839
+
840
+ #: src/includes/classes/MenuPageOptions.php:586
841
+ msgid "<strong>Warning:</strong> Whenever you enable caching for logged-in users (without a separate cache for each user), the WordPress Admin Bar <em>must</em> be disabled to prevent one user from seeing another user's details in the Admin Bar. <strong>Given your current configuration, %1$s will automatically hide the WordPress Admin Bar on the front-end of your site.</strong>"
842
+ msgstr ""
843
+
844
+ #: src/includes/classes/MenuPageOptions.php:588
845
+ msgid "<strong>Note:</strong> For most sites, the majority of their traffic (if not all of their traffic) comes from visitors who are not logged in, so disabling the cache for logged-in users is NOT ordinarily a performance issue. When a user IS logged-in, disabling the cache is considered ideal, because a logged-in user has a session open with your site; and the content they view should remain very dynamic in this scenario."
846
+ msgstr ""
847
+
848
+ #: src/includes/classes/MenuPageOptions.php:589
849
+ msgid "<strong>Note:</strong> This setting includes some users who AREN'T actually logged into the system, but who HAVE authored comments recently. %1$s includes comment authors as part of it's logged-in user check. This way comment authors will be able to see updates to the comment thread immediately; and, so that any dynamically-generated messages displayed by your theme will work as intended. In short, %1$s thinks of a comment author as a logged-in user, even though technically they are not. ~ Users who gain access to password-protected Posts/Pages are also included."
850
+ msgstr ""
851
+
852
+ #: src/includes/classes/MenuPageOptions.php:591
853
+ msgid "Static CDN Filters Enabled for Logged-In Users &amp; Comment Authors?"
854
+ msgstr ""
855
+
856
+ #: src/includes/classes/MenuPageOptions.php:592
857
+ msgid "While this defaults to a value of <code>No</code>, it should almost always be set to <code>Yes</code>. This value defaults to <code>No</code> only because Logged-In User caching (see above) defaults to <code>No</code> and setting this value to <code>Yes</code> by default can cause confusion for some users. Once you understand that Static CDN Filters can be applied safely for all visitors (logged-in or not logged-in), please choose <code>Yes</code> in the dropdown below. If you are not using Static CDN Filters, the value below is ignored."
858
+ msgstr ""
859
+
860
+ #: src/includes/classes/MenuPageOptions.php:594
861
+ msgid "No, disable Static CDN Filters when a user is logged-in."
862
+ msgstr ""
863
+
864
+ #: src/includes/classes/MenuPageOptions.php:595
865
+ msgid "Yes, enable Static CDN Filters for logged-in users (recommended) ."
866
+ msgstr ""
867
+
868
+ #: src/includes/classes/MenuPageOptions.php:597
869
+ msgid "<strong>Note:</strong> Static CDN Filters serve <em>static</em> resources. Static resources, are, simply put, static. Thus, it is not a problem to cache these resources for any visitor (logged-in or not logged-in). To avoid confusion, this defaults to a value of <code>No</code>, and we ask that you set it to <code>Yes</code> on your own so that you'll know to expect this behavior; i.e., that static resources will always be served from the CDN (logged-in or not logged-in) even though Logged-In User caching may be disabled above."
870
+ msgstr ""
871
+
872
+ #: src/includes/classes/MenuPageOptions.php:607
873
+ msgid "GET Requests"
874
+ msgstr ""
875
+
876
+ #: src/includes/classes/MenuPageOptions.php:612
877
+ msgid "Caching Enabled for GET (Query String) Requests?"
878
+ msgstr ""
879
+
880
+ #: src/includes/classes/MenuPageOptions.php:613
881
+ msgid "This should almost ALWAYS be set to <code>No</code>. UNLESS, you're using unfriendly Permalinks. In other words, if all of your URLs contain a query string (e.g., <code>/?key=value</code>); you're using unfriendly Permalinks. Ideally, you would refrain from doing this; and instead, update your Permalink options immediately; which also optimizes your site for search engines. That being said, if you really want to use unfriendly Permalinks, and ONLY if you're using unfriendly Permalinks, you should set this to <code>Yes</code>; and don't worry too much, the sky won't fall on your head :-)"
882
+ msgstr ""
883
+
884
+ #: src/includes/classes/MenuPageOptions.php:615
885
+ msgid "No, do NOT cache (or serve a cache file) when a query string is present."
886
+ msgstr ""
887
+
888
+ #: src/includes/classes/MenuPageOptions.php:616
889
+ msgid "Yes, I would like to cache URLs that contain a query string."
890
+ msgstr ""
891
+
892
+ #: src/includes/classes/MenuPageOptions.php:618
893
+ msgid "<strong>Note:</strong> POST requests (i.e., forms with <code>method=&quot;post&quot;</code>) are always excluded from the cache, which is the way it should be. Any <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html\" target=\"_blank\">POST/PUT/DELETE</a> request should NEVER (ever) be cached. CLI (and self-serve) requests are also excluded from the cache (always). A CLI request is one that comes from the command line; commonly used by CRON jobs and other automated routines. A self-serve request is an HTTP connection established from your site -› to your site. For instance, a WP Cron job, or any other HTTP request that is spawned not by a user, but by the server itself."
894
+ msgstr ""
895
+
896
+ #: src/includes/classes/MenuPageOptions.php:619
897
+ msgid "<strong>Advanced Tip:</strong> If you are NOT caching GET requests (recommended), but you DO want to allow some special URLs that include query string parameters to be cached; you can add this special parameter to any URL <code>?%2$sAC=1</code>. This tells %1$s that it's OK to cache that particular URL, even though it contains query string arguments. If you ARE caching GET requests and you want to force %1$s to NOT cache a specific request, you can add this special parameter to any URL <code>?%2$sAC=0</code>."
898
+ msgstr ""
899
+
900
+ #: src/includes/classes/MenuPageOptions.php:629
901
+ msgid "404 Requests"
902
+ msgstr ""
903
+
904
+ #: src/includes/classes/MenuPageOptions.php:634
905
+ msgid "Caching Enabled for 404 Requests?"
906
+ msgstr ""
907
+
908
+ #: src/includes/classes/MenuPageOptions.php:635
909
+ msgid "When this is set to <code>No</code>, %1$s will ignore all 404 requests and no cache file will be served. While this is fine for most site owners, caching the 404 page on a high-traffic site may further reduce server load. When this is set to <code>Yes</code>, %1$s will cache the 404 page (see <a href=\"https://codex.wordpress.org/Creating_an_Error_404_Page\" target=\"_blank\">Creating an Error 404 Page</a>) and then serve that single cache file to all future 404 requests."
910
+ msgstr ""
911
+
912
+ #: src/includes/classes/MenuPageOptions.php:637
913
+ msgid "No, do NOT cache (or serve a cache file) for 404 requests."
914
+ msgstr ""
915
+
916
+ #: src/includes/classes/MenuPageOptions.php:638
917
+ msgid "Yes, I would like to cache the 404 page and serve the cached file for 404 requests."
918
+ msgstr ""
919
+
920
+ #: src/includes/classes/MenuPageOptions.php:640
921
+ msgid "<strong>How does %1$s cache 404 requests?</strong> %1$s will create a special cache file (<code>----404----.html</code>, see Advanced Tip below) for the first 404 request and then <a href=\"http://www.php.net/manual/en/function.symlink.php\" target=\"_blank\">symlink</a> future 404 requests to this special cache file. That way you don't end up with lots of 404 cache files that all contain the same thing (the contents of the 404 page). Instead, you'll have one 404 cache file and then several symlinks (i.e., references) to that 404 cache file."
922
+ msgstr ""
923
+
924
+ #: src/includes/classes/MenuPageOptions.php:641
925
+ msgid "<strong>Advanced Tip:</strong> The default 404 cache filename (<code>----404----.html</code>) is designed to minimize the chance of a collision with a cache file for a real page with the same name. However, if you want to override this default and define your own 404 cache filename, you can do so by adding <code>define('COMET_CACHE_404_CACHE_FILENAME', 'your-404-cache-filename');</code> to your <code>wp-config.php</code> file (note that the <code>.html</code> extension should be excluded when defining a new filename)."
926
+ msgstr ""
927
+
928
+ #: src/includes/classes/MenuPageOptions.php:651
929
+ msgid "RSS, RDF, and Atom Feeds"
930
+ msgstr ""
931
+
932
+ #: src/includes/classes/MenuPageOptions.php:656
933
+ msgid "Caching Enabled for RSS, RDF, Atom Feeds?"
934
+ msgstr ""
935
+
936
+ #: src/includes/classes/MenuPageOptions.php:657
937
+ msgid "This should almost ALWAYS be set to <code>No</code>. UNLESS, you're sure that you want to cache your feeds. If you use a web feed management provider like Google® Feedburner and you set this option to <code>Yes</code>, you may experience delays in the detection of new posts. <strong>NOTE:</strong> If you do enable this, it is highly recommended that you also enable automatic Feed Clearing too. Please see the section above: \"Automatic Cache Clearing\". Find the sub-section titled: \"Auto-Clear RSS/RDF/ATOM Feeds\"."
938
+ msgstr ""
939
+
940
+ #: src/includes/classes/MenuPageOptions.php:659
941
+ msgid "No, do NOT cache (or serve a cache file) when displaying a feed."
942
+ msgstr ""
943
+
944
+ #: src/includes/classes/MenuPageOptions.php:660
945
+ msgid "Yes, I would like to cache feed URLs."
946
+ msgstr ""
947
+
948
+ #: src/includes/classes/MenuPageOptions.php:662
949
+ msgid "<strong>Note:</strong> This option affects all feeds served by WordPress, including the site feed, the site comment feed, post-specific comment feeds, author feeds, search feeds, and category and tag feeds. See also: <a href=\"http://codex.wordpress.org/WordPress_Feeds\" target=\"_blank\">WordPress Feeds</a>."
950
+ msgstr ""
951
+
952
+ #: src/includes/classes/MenuPageOptions.php:672
953
+ msgid "URI Exclusion Patterns"
954
+ msgstr ""
955
+
956
+ #: src/includes/classes/MenuPageOptions.php:676
957
+ msgid "Don't Cache These Special URI Exclusion Patterns?"
958
+ msgstr ""
959
+
960
+ #: src/includes/classes/MenuPageOptions.php:677
961
+ msgid "Sometimes there are certain cases where a particular file, or a particular group of files, should never be cached. This is where you will enter those if you need to (one per line). Searches are performed against the <a href=\"https://gist.github.com/jaswsinc/338b6eb03a36c048c26f\" target=\"_blank\" style=\"text-decoration:none;\"><code>REQUEST_URI</code></a>; i.e., <code>/path/?query</code> (caSe insensitive). So, don't put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard <code>*</code> character can also be used when necessary; e.g., <code>/category/abc-followed-by-*</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href =\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
962
+ msgstr ""
963
+
964
+ #: src/includes/classes/MenuPageOptions.php:691
965
+ msgid "HTTP Referrer Exclusion Patterns"
966
+ msgstr ""
967
+
968
+ #: src/includes/classes/MenuPageOptions.php:695
969
+ msgid "Don't Cache These Special HTTP Referrer Exclusion Patterns?"
970
+ msgstr ""
971
+
972
+ #: src/includes/classes/MenuPageOptions.php:696
973
+ msgid "Sometimes there are special cases where a particular referring URL (or referring domain) that sends you traffic; or even a particular group of referring URLs or domains that send you traffic; should result in a page being loaded on your site that is NOT from the cache (and that resulting page should never be cached). This is where you will enter those if you need to (one per line). Searches are performed against the <a href=\"http://www.php.net//manual/en/reserved.variables.server.php\" target=\"_blank\" style=\"text-decoration:none;\"><code>HTTP_REFERER</code></a> (caSe insensitive). A wildcard <code>*</code> character can also be used when necessary; e.g., <code>*.domain.com</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href =\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
974
+ msgstr ""
975
+
976
+ #: src/includes/classes/MenuPageOptions.php:698
977
+ msgid "<strong>Tip:</strong> let's use this example URL: <code>http://www.referring-domain.com/search/?q=search+terms</code>. To exclude this referring URL, you could put this line into the field above: <code>www.referring-domain.com</code>. Or, you could also just put in a small fragment, like: <code>/search/</code> or <code>q=*</code>; and that would exclude any referrer containing that word fragment."
978
+ msgstr ""
979
+
980
+ #: src/includes/classes/MenuPageOptions.php:709
981
+ msgid "User-Agent Exclusion Patterns"
982
+ msgstr ""
983
+
984
+ #: src/includes/classes/MenuPageOptions.php:713
985
+ msgid "Don't Cache These Special User-Agent Exclusion Patterns?"
986
+ msgstr ""
987
+
988
+ #: src/includes/classes/MenuPageOptions.php:714
989
+ msgid "Sometimes there are special cases when a particular user-agent (e.g., a specific browser or a specific type of device); should be shown a page on your site that is NOT from the cache (and that resulting page should never be cached). This is where you will enter those if you need to (one per line). Searches are performed against the <a href=\"http://www.php.net//manual/en/reserved.variables.server.php\" target=\"_blank\" style=\"text-decoration:none;\"><code>HTTP_USER_AGENT</code></a> (caSe insensitive). A wildcard <code>*</code> character can also be used when necessary; e.g., <code>Android *; Chrome/* Mobile</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href =\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
990
+ msgstr ""
991
+
992
+ #: src/includes/classes/MenuPageOptions.php:716
993
+ msgid "<strong>Tip:</strong> if you wanted to exclude iPhones put this line into the field above: <code>iPhone;*AppleWebKit</code>. Or, you could also just put in a small fragment, like: <code>iphone</code>; and that would exclude any user-agent containing that word fragment. Note, this is just an example. With a default installation of %1$s, there is no compelling reason to exclude iOS devices (or any mobile device for that matter)."
994
+ msgstr ""
995
+
996
+ #: src/includes/classes/MenuPageOptions.php:728
997
+ msgid "Auto-Cache Engine"
998
+ msgstr ""
999
+
1000
+ #: src/includes/classes/MenuPageOptions.php:733
1001
+ msgid "Enable the Auto-Cache Engine?"
1002
+ msgstr ""
1003
+
1004
+ #: src/includes/classes/MenuPageOptions.php:734
1005
+ msgid "After using %1$s for awhile (or any other page caching plugin, for that matter); it becomes obvious that at some point (based on your configured Expiration Time) %1$s has to refresh itself. It does this by ditching its cached version of a page, reloading the database-driven content, and then recreating the cache with the latest data. This is a never ending regeneration cycle that is based entirely on your configured Expiration Time."
1006
+ msgstr ""
1007
+
1008
+ #: src/includes/classes/MenuPageOptions.php:735
1009
+ msgid "Understanding this, you can see that 99% of your visitors are going to receive a lightning fast response from your server. However, there will always be around 1% of your visitors that land on a page for the very first time (before it's been cached), or land on a page that needs to have its cache regenerated, because the existing cache has become outdated. We refer to this as a <em>First-Come Slow-Load Issue</em>. Not a huge problem, but if you're optimizing your site for every ounce of speed possible, the Auto-Cache Engine can help with this. The Auto-Cache Engine has been designed to combat this issue by taking on the responsibility of being that first visitor to a page that has not yet been cached, or has an expired cache. The Auto-Cache Engine is powered, in part, by <a href=\"http://codex.wordpress.org/Category:WP-Cron_Functions\" target=\"_blank\">WP-Cron</a> (already built into WordPress). The Auto-Cache Engine runs at 15-minute intervals via WP-Cron. It also uses the <a href=\"http://core.trac.wordpress.org/browser/trunk/wp-includes/http.php\" target=\"_blank\">WP_Http</a> class, which is also built into WordPress already."
1010
+ msgstr ""
1011
+
1012
+ #: src/includes/classes/MenuPageOptions.php:736
1013
+ msgid "The Auto-Cache Engine obtains its list of URLs to auto-cache, from two different sources. It can read an <a href=\"http://wordpress.org/extend/plugins/google-sitemap-generator/\" target=\"_blank\">XML Sitemap</a> and/or a list of specific URLs that you supply. If you supply both sources, it will use both sources collectively. The Auto-Cache Engine takes ALL of your other configuration options into consideration too, including your Expiration Time, as well as any cache exclusion rules."
1014
+ msgstr ""
1015
+
1016
+ #: src/includes/classes/MenuPageOptions.php:738
1017
+ msgid "No, leave the Auto-Cache Engine disabled please."
1018
+ msgstr ""
1019
+
1020
+ #: src/includes/classes/MenuPageOptions.php:739
1021
+ msgid "Yes, I want the Auto-Cache Engine to keep pages cached automatically."
1022
+ msgstr ""
1023
+
1024
+ #: src/includes/classes/MenuPageOptions.php:745
1025
+ msgid "XML Sitemap URL (or an XML Sitemap Index)"
1026
+ msgstr ""
1027
+
1028
+ #: src/includes/classes/MenuPageOptions.php:749
1029
+ msgid "All URLs in this network are in the sitemap for the main site."
1030
+ msgstr ""
1031
+
1032
+ #: src/includes/classes/MenuPageOptions.php:750
1033
+ msgid "Using the path I've given, look for blog-specific sitemaps in each child blog also."
1034
+ msgstr ""
1035
+
1036
+ #: src/includes/classes/MenuPageOptions.php:752
1037
+ msgid "<strong>↑</strong> If enabled here, each child blog can be auto-cached too. %1$s will dynamically change the leading <code>%2$s</code> as necessary; for each child blog in the network. %1$s supports both sub-directory &amp; sub-domain networks, including domain mapping plugins. For more information about how the Auto-Cache Engine caches child blogs, see <a href=\"http://cometcache.com/r/kb-article-how-does-the-auto-cache-engine-cache-child-blogs-in-a-multisite-network/\" target=\"_blank\">this article</a>."
1038
+ msgstr ""
1039
+
1040
+ #: src/includes/classes/MenuPageOptions.php:756
1041
+ msgid "And/Or; a List of URLs to Auto-Cache (One Per Line)"
1042
+ msgstr ""
1043
+
1044
+ #: src/includes/classes/MenuPageOptions.php:758
1045
+ msgid "<strong>Note:</strong> Wildcards are NOT supported here. If you are going to supply a list of URLs above, each line must contain one full URL for the Auto-Cache Engine to auto-cache. If you have many URLs, we recommend using an <a href=\"https://en.wikipedia.org/wiki/Sitemaps\" target=\"_blank\">XML Sitemap</a>."
1046
+ msgstr ""
1047
+
1048
+ #: src/includes/classes/MenuPageOptions.php:762
1049
+ msgid "Auto-Cache Delay Timer (in Milliseconds)"
1050
+ msgstr ""
1051
+
1052
+ #: src/includes/classes/MenuPageOptions.php:763
1053
+ msgid "As the Auto-Cache Engine runs through each URL, you can tell it to wait X number of milliseconds between each connection that it makes. It is strongly suggested that you DO have some small delay here. Otherwise, you run the risk of hammering your own web server with multiple repeated connections whenever the Auto-Cache Engine is running. This is especially true on very large sites; where there is the potential for hundreds of repeated connections as the Auto-Cache Engine goes through a long list of URLs. Adding a delay between each connection will prevent the Auto-Cache Engine from placing a heavy load on the processor that powers your web server. A value of <code>500</code> milliseconds is suggested here (half a second). If you experience problems, you can bump this up a little at a time, in increments of <code>500</code> milliseconds; until you find a happy place for your server. <em>Please note that <code>1000</code> milliseconds = <code>1</code> full second.</em>"
1054
+ msgstr ""
1055
+
1056
+ #: src/includes/classes/MenuPageOptions.php:768
1057
+ msgid "Auto-Cache User-Agent String"
1058
+ msgstr ""
1059
+
1060
+ #: src/includes/classes/MenuPageOptions.php:770
1061
+ msgid "This is how the Auto-Cache Engine identifies itself when connecting to URLs. See <a href=\"http://en.wikipedia.org/wiki/User_agent\" target=\"_blank\">User Agent</a> in the Wikipedia."
1062
+ msgstr ""
1063
+
1064
+ #: src/includes/classes/MenuPageOptions.php:782
1065
+ msgid "HTML Compression"
1066
+ msgstr ""
1067
+
1068
+ #: src/includes/classes/MenuPageOptions.php:787
1069
+ msgid "Enable WebSharks™ HTML Compression?"
1070
+ msgstr ""
1071
+
1072
+ #: src/includes/classes/MenuPageOptions.php:789
1073
+ msgid "No, do NOT compress HTML/CSS/JS code at runtime."
1074
+ msgstr ""
1075
+
1076
+ #: src/includes/classes/MenuPageOptions.php:790
1077
+ msgid "Yes, I want to compress HTML/CSS/JS for blazing fast speeds."
1078
+ msgstr ""
1079
+
1080
+ #: src/includes/classes/MenuPageOptions.php:792
1081
+ msgid "<strong>Note:</strong> This is experimental. Please <a href=\"https://github.com/websharks/comet-cache/issues\" target=\"_blank\">report issues here</a>."
1082
+ msgstr ""
1083
+
1084
+ #: src/includes/classes/MenuPageOptions.php:795
1085
+ msgid "HTML Compression Options"
1086
+ msgstr ""
1087
+
1088
+ #: src/includes/classes/MenuPageOptions.php:796
1089
+ msgid "You can <a href=\"https://github.com/websharks/html-compressor\" target=\"_blank\">learn more about all of these options here</a>."
1090
+ msgstr ""
1091
+
1092
+ #: src/includes/classes/MenuPageOptions.php:798
1093
+ msgid "Yes, combine CSS from &lt;head&gt; and &lt;body&gt; into fewer files."
1094
+ msgstr ""
1095
+
1096
+ #: src/includes/classes/MenuPageOptions.php:799
1097
+ msgid "No, do not combine CSS from &lt;head&gt; and &lt;body&gt; into fewer files."
1098
+ msgstr ""
1099
+
1100
+ #: src/includes/classes/MenuPageOptions.php:802
1101
+ msgid "Yes, compress the code in any unified CSS files."
1102
+ msgstr ""
1103
+
1104
+ #: src/includes/classes/MenuPageOptions.php:803
1105
+ msgid "No, do not compress the code in any unified CSS files."
1106
+ msgstr ""
1107
+
1108
+ #: src/includes/classes/MenuPageOptions.php:806
1109
+ msgid "Yes, combine JS from &lt;head&gt; into fewer files."
1110
+ msgstr ""
1111
+
1112
+ #: src/includes/classes/MenuPageOptions.php:807
1113
+ msgid "No, do not combine JS from &lt;head&gt; into fewer files."
1114
+ msgstr ""
1115
+
1116
+ #: src/includes/classes/MenuPageOptions.php:810
1117
+ msgid "Yes, combine JS footer scripts into fewer files."
1118
+ msgstr ""
1119
+
1120
+ #: src/includes/classes/MenuPageOptions.php:811
1121
+ msgid "No, do not combine JS footer scripts into fewer files."
1122
+ msgstr ""
1123
+
1124
+ #: src/includes/classes/MenuPageOptions.php:814
1125
+ msgid "Yes, combine CSS/JS from remote resources too."
1126
+ msgstr ""
1127
+
1128
+ #: src/includes/classes/MenuPageOptions.php:815
1129
+ msgid "No, do not combine CSS/JS from remote resources."
1130
+ msgstr ""
1131
+
1132
+ #: src/includes/classes/MenuPageOptions.php:818
1133
+ msgid "Yes, compress the code in any unified JS files."
1134
+ msgstr ""
1135
+
1136
+ #: src/includes/classes/MenuPageOptions.php:819
1137
+ msgid "No, do not compress the code in any unified JS files."
1138
+ msgstr ""
1139
+
1140
+ #: src/includes/classes/MenuPageOptions.php:822
1141
+ msgid "Yes, compress inline JavaScript snippets."
1142
+ msgstr ""
1143
+
1144
+ #: src/includes/classes/MenuPageOptions.php:823
1145
+ msgid "No, do not compress inline JavaScript snippets."
1146
+ msgstr ""
1147
+
1148
+ #: src/includes/classes/MenuPageOptions.php:826
1149
+ msgid "Yes, compress (remove extra whitespace) in the final HTML code too."
1150
+ msgstr ""
1151
+
1152
+ #: src/includes/classes/MenuPageOptions.php:827
1153
+ msgid "No, do not compress the final HTML code."
1154
+ msgstr ""
1155
+
1156
+ #: src/includes/classes/MenuPageOptions.php:830
1157
+ msgid "CSS Exclusion Patterns?"
1158
+ msgstr ""
1159
+
1160
+ #: src/includes/classes/MenuPageOptions.php:831
1161
+ msgid "Sometimes there are special cases when a particular CSS file should NOT be consolidated or compressed in any way. This is where you will enter those if you need to (one per line). Searches are performed against the <code>&lt;link href=&quot;&quot;&gt;</code> value, and also against the contents of any inline <code>&lt;style&gt;</code> tags (caSe insensitive). A wildcard <code>*</code> character can also be used when necessary; e.g., <code>xy*-framework</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href =\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
1162
+ msgstr ""
1163
+
1164
+ #: src/includes/classes/MenuPageOptions.php:834
1165
+ msgid "JavaScript Exclusion Patterns?"
1166
+ msgstr ""
1167
+
1168
+ #: src/includes/classes/MenuPageOptions.php:835
1169
+ msgid "Sometimes there are special cases when a particular JS file should NOT be consolidated or compressed in any way. This is where you will enter those if you need to (one per line). Searches are performed against the <code>&lt;script src=&quot;&quot;&gt;</code> value, and also against the contents of any inline <code>&lt;script&gt;</code> tags (caSe insensitive). A wildcard <code>*</code> character can also be used when necessary; e.g., <code>xy*-framework</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href =\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
1170
+ msgstr ""
1171
+
1172
+ #: src/includes/classes/MenuPageOptions.php:838
1173
+ msgid "URI Exclusions for HTML Compressor?"
1174
+ msgstr ""
1175
+
1176
+ #: src/includes/classes/MenuPageOptions.php:839
1177
+ msgid "When you enable HTML Compression above, you may want to prevent certain pages on your site from being cached by the HTML Compressor. This is where you will enter those if you need to (one per line). Searches are performed against the <a href=\"https://gist.github.com/jaswsinc/338b6eb03a36c048c26f\" target=\"_blank\" style=\"text-decoration:none;\"><code>REQUEST_URI</code></a>; i.e., <code>/path/?query</code> (caSe insensitive). So, don't put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard <code>*</code> character can also be used when necessary; e.g., <code>/category/abc-followed-by-*</code> (where <code>*</code> = 0 or more characters that are NOT a slash <code>/</code>). Other special characters include: <code>**</code> = 0 or more characters of any kind, including <code>/</code> slashes; <code>^</code> = beginning of the string; <code>$</code> = end of the string. To learn more about this syntax, please see <a href =\"http://cometcache.com/r/watered-down-regex-syntax/\" target=\"_blank\">this KB article</a>."
1178
+ msgstr ""
1179
+
1180
+ #: src/includes/classes/MenuPageOptions.php:844
1181
+ msgid "HTML Compression Cache Expiration"
1182
+ msgstr ""
1183
+
1184
+ #: src/includes/classes/MenuPageOptions.php:846
1185
+ msgid "<strong>Tip:</strong> the value that you specify here MUST be compatible with PHP's <a href=\"http://php.net/manual/en/function.strtotime.php\" target=\"_blank\" style=\"text-decoration:none;\"><code>strtotime()</code></a> function. Examples: <code>2 hours</code>, <code>7 days</code>, <code>6 months</code>, <code>1 year</code>."
1186
+ msgstr ""
1187
+
1188
+ #: src/includes/classes/MenuPageOptions.php:847
1189
+ msgid "<strong>Note:</strong> This does NOT impact the overall cache expiration time that you configure with %1$s. It only impacts the sub-routines provided by the HTML Compressor. In fact, this expiration time is mostly irrelevant. The HTML Compressor uses an internal checksum, and it also checks <code>filemtime()</code> before using an existing cache file. The HTML Compressor class also handles the automatic cleanup of your cache directories to keep it from growing too large over time. Therefore, unless you have VERY little disk space there is no reason to set this to a lower value (even if your site changes dynamically quite often). If anything, you might like to increase this value which could help to further reduce server load. You can <a href=\"https://github.com/websharks/HTML-Compressor\" target=\"_blank\">learn more here</a>. We recommend setting this value to at least double that of your overall %1$s expiration time."
1190
+ msgstr ""
1191
+
1192
+ #: src/includes/classes/MenuPageOptions.php:858
1193
+ msgid "GZIP Compression"
1194
+ msgstr ""
1195
+
1196
+ #: src/includes/classes/MenuPageOptions.php:863
1197
+ msgid "<a href=\"https://developers.google.com/speed/articles/gzip\" target=\"_blank\">GZIP Compression</a> (Optional; Highly Recommended)"
1198
+ msgstr ""
1199
+
1200
+ #: src/includes/classes/MenuPageOptions.php:864
1201
+ msgid "You don't have to use an <code>.htaccess</code> file to enjoy the performance enhancements provided by this plugin; caching is handled automatically by WordPress/PHP alone. That being said, if you want to take advantage of the additional speed enhancements associated w/ GZIP compression (and we do recommend this), then you WILL need an <code>.htaccess</code> file to accomplish that part."
1202
+ msgstr ""
1203
+
1204
+ #: src/includes/classes/MenuPageOptions.php:865
1205
+ msgid "%1$s fully supports GZIP compression on its output. However, it does not handle GZIP compression directly. We purposely left GZIP compression out of this plugin, because GZIP compression is something that should really be enabled at the Apache level or inside your <code>php.ini</code> file. GZIP compression can be used for things like JavaScript and CSS files as well, so why bother turning it on for only WordPress-generated pages when you can enable GZIP at the server level and cover all the bases!"
1206
+ msgstr ""
1207
+
1208
+ #: src/includes/classes/MenuPageOptions.php:866
1209
+ msgid "If you want to enable GZIP, create an <code>.htaccess</code> file in your WordPress® installation directory, and put the following few lines in it. Alternatively, if you already have an <code>.htaccess</code> file, just add these lines to it, and that is all there is to it. GZIP is now enabled in the recommended way! See also: <a href=\"https://developers.google.com/speed/articles/gzip\" target=\"_blank\"><i class=\"si si-youtube-play\"></i> video about GZIP Compression</a>."
1210
+ msgstr ""
1211
+
1212
+ #: src/includes/classes/MenuPageOptions.php:880
1213
+ msgid "Static CDN Filters"
1214
+ msgstr ""
1215
+
1216
+ #: src/includes/classes/MenuPageOptions.php:884
1217
+ msgid "Clear CDN Cache (Bump CDN Invalidation Counter)"
1218
+ msgstr ""
1219
+
1220
+ #: src/includes/classes/MenuPageOptions.php:884
1221
+ msgid "Clear CDN Cache"
1222
+ msgstr ""
1223
+
1224
+ #: src/includes/classes/MenuPageOptions.php:885
1225
+ msgid "Enable Static CDN Filters (e.g., MaxCDN/CloudFront)?"
1226
+ msgstr ""
1227
+
1228
+ #: src/includes/classes/MenuPageOptions.php:886
1229
+ msgid "This feature allows you to serve some and/or ALL static files on your site from a CDN of your choosing. This is made possible through content/URL filters exposed by WordPress and implemented by %1$s. All it requires is that you setup a CDN host name sourced by your WordPress installation domain. You enter that CDN host name below and %1$s will do the rest! Super easy, and it doesn't require any DNS changes either. :-) Please <a href=\"http://cometcache.com/r/static-cdn-filters-general-instructions/\" target=\"_blank\">click here</a> for a general set of instructions."
1230
+ msgstr ""
1231
+
1232
+ #: src/includes/classes/MenuPageOptions.php:887
1233
+ msgid "<strong>What's a CDN?</strong> It's a Content Delivery Network (i.e., a network of optimized servers) designed to cache static resources served from your site (e.g., JS/CSS/images and other static files) onto it's own servers, which are located strategically in various geographic areas around the world. Integrating a CDN for static files can dramatically improve the speed and performance of your site, lower the burden on your own server, and reduce latency associated with visitors attempting to access your site from geographic areas of the world that might be very far away from the primary location of your own web servers."
1234
+ msgstr ""
1235
+
1236
+ #: src/includes/classes/MenuPageOptions.php:890
1237
+ msgid "It appears that your server is running NGINX and does not support <code>.htaccess</code> rules. Please <a href=\"http://cometcache.com/r/kb-article-recommended-nginx-server-configuration/\" target=\"_new\">update your server configuration manually</a>. Note that updating your NGINX server configuration <em>before</em> enabling Static CDN Filters is recommended to prevent any <a href=\"http://cometcache.com/r/kb-article-what-are-cross-origin-request-blocked-cors-errors/\" target=\"_new\">CORS errors</a> with your CDN. If you've already updated your NGINX configuration, you can safely <a href=\"http://cometcache.com/r/kb-article-how-do-i-disable-the-nginx-htaccess-notice/\" target=\"_new\">ignore this message</a>."
1238
+ msgstr ""
1239
+
1240
+ #: src/includes/classes/MenuPageOptions.php:894
1241
+ msgid "No, I do NOT want CDN filters applied at runtime."
1242
+ msgstr ""
1243
+
1244
+ #: src/includes/classes/MenuPageOptions.php:895
1245
+ msgid "Yes, I want CDN filters applied w/ my configuration below."
1246
+ msgstr ""
1247
+
1248
+ #: src/includes/classes/MenuPageOptions.php:902
1249
+ msgid "CDN Host Name (Required)"
1250
+ msgstr ""
1251
+
1252
+ #: src/includes/classes/MenuPageOptions.php:908
1253
+ msgid "This field is really all that's necessary to get Static CDN Filters working! However, it does requires a little bit of work on your part. You need to setup and configure a CDN before you can fill in this field. Once you configure a CDN, you'll receive a host name (provided by your CDN), which you'll enter here; e.g., <code>js9dgjsl4llqpp.cloudfront.net</code>. We recommend <a href=\"http://cometcache.com/r/maxcdn/\" target=\"_blank\">MaxCDN</a>, <a href=\"http://cometcache.com/r/amazon-cloudfront/\" target=\"_blank\">Amazon CloudFront</a>, <a href=\"http://cometcache.com/r/keycdn/\" target=\"_blank\">KeyCDN</a>, and/or <a href=\"http://cometcache.com/r/cdn77/\" target=\"_blank\">CDN77</a> but this should work with many of the most popular CDNs. Please read <a href=\"http://cometcache.com/r/static-cdn-filters-general-instructions/\" target=\"_blank\">this article</a> for a general set of instructions. We also have a <a href=\"http://cometcache.com/r/static-cdn-filters-maxcdn/\" target=\"_blank\">MaxCDN tutorial</a>, <a href=\"http://cometcache.com/r/static-cdn-filters-cloudfront/\" target=\"_blank\">CloudFront tutorial</a>, <a href=\"http://cometcache.com/r/static-cdn-filters-keycdn/\" target=\"_blank\">KeyCDN tutorial</a>, and a <a href=\"http://cometcache.com/r/static-cdn-filters-cdn77/\" target=\"_blank\">CDN77 tutorial</a> to walk you through the process."
1254
+ msgstr ""
1255
+
1256
+ #: src/includes/classes/MenuPageOptions.php:913
1257
+ msgid "Multiple CDN Host Names for Domain Sharding and Multisite Networks (Optional)"
1258
+ msgstr ""
1259
+
1260
+ #: src/includes/classes/MenuPageOptions.php:914
1261
+ msgid "%1$s also supports multiple CDN Host Names for any given domain. Using multiple CDN Host Names (instead of just one, as seen above) is referred to as <strong><a href=\"http://cometcache.com/r/domain-sharding/\" target=\"_blank\">Domain Sharding</a></strong> (<a href=\"http://cometcache.com/r/domain-sharding/\" target=\"_blank\">click here to learn more</a>). If you configure multiple CDN Host Names (i.e., if you implement Domain Sharding), %1$s will use the first one that you list for static resources loaded in the HTML <code>&lt;head&gt;</code> section, the last one for static resources loaded in the footer, and it will choose one at random for all other static resource locations. Configuring multiple CDN Host Names can improve speed! This is a way for advanced site owners to work around concurrency limits in popular browsers; i.e., making it possible for browsers to download many more resources simultaneously, resulting in a faster overall completion time. In short, this tells the browser that your website will not be overloaded by concurrent requests, because static resources are in fact being served by a content-delivery network (i.e., multiple CDN host names). If you use this functionality for Domain Sharding, we suggest that you setup one CDN Distribution (aka: Pull Zone), and then create multiple CNAME records pointing to that distribution. You can enter each of your CNAMES in the field below, as instructed."
1262
+ msgstr ""
1263
+
1264
+ #: src/includes/classes/MenuPageOptions.php:915
1265
+ msgid "<strong>On WordPress Multisite Network installations</strong>, this field also allows you to configure different CDN Host Names for each domain (or sub-domain) that you run from a single installation of WordPress. For more information about configuring Static CDN Filters on a WordPress Multisite Network, see this tutorial: <a href=\"http://cometcache.com/r/static-cdn-filters-for-wordpress-multisite-networks/\" target=\"_blank\">Static CDN Filters for WordPress Multisite Networks</a>."
1266
+ msgstr ""
1267
+
1268
+ #: src/includes/classes/MenuPageOptions.php:917
1269
+ msgid "<strong>↑ Syntax:</strong> This is a line-delimited list of domain mappings. Each line should start with your WordPress domain name (e.g., <code>%1$s</code>), followed by an <code>=</code> sign, followed by a comma-delimited list of CDN Host Names associated with the domain in that line. If you're running a Multisite Network installation of WordPress, you might have multiple configuration lines. Otherwise, you should only need one line to configure multiple CDN Host Names for a standard WordPress installation."
1270
+ msgstr ""
1271
+
1272
+ #: src/includes/classes/MenuPageOptions.php:921
1273
+ msgid "CDN Supports HTTPS Connections?"
1274
+ msgstr ""
1275
+
1276
+ #: src/includes/classes/MenuPageOptions.php:923
1277
+ msgid "No, I don't serve content over https://; or I haven't configured my CDN w/ an SSL certificate."
1278
+ msgstr ""
1279
+
1280
+ #: src/includes/classes/MenuPageOptions.php:924
1281
+ msgid "Yes, I've configured my CDN w/ an SSL certificate; I need https:// enabled."
1282
+ msgstr ""
1283
+
1284
+ #: src/includes/classes/MenuPageOptions.php:931
1285
+ msgid "Additional Options (For Advanced Users)"
1286
+ msgstr ""
1287
+
1288
+ #: src/includes/classes/MenuPageOptions.php:936
1289
+ msgid "Everything else below is 100% completely optional; i.e., not required to enjoy the benefits of Static CDN Filters."
1290
+ msgstr ""
1291
+
1292
+ #: src/includes/classes/MenuPageOptions.php:940
1293
+ msgid "Whitelisted File Extensions (Optional; Comma-Delimited)"
1294
+ msgstr ""
1295
+
1296
+ #: src/includes/classes/MenuPageOptions.php:942
1297
+ msgid "If you leave this empty a default set of extensions are taken from WordPress itself. The default set of whitelisted file extensions includes everything supported by the WordPress media library."
1298
+ msgstr ""
1299
+
1300
+ #: src/includes/classes/MenuPageOptions.php:944
1301
+ msgid "Blacklisted File Extensions (Optional; Comma-Delimited)"
1302
+ msgstr ""
1303
+
1304
+ #: src/includes/classes/MenuPageOptions.php:946
1305
+ msgid "With or without a whitelist, you can force exclusions by explicitly blacklisting certain file extensions of your choosing. Please note, the <code>php</code> extension will never be considered a static resource; i.e., it is automatically blacklisted at all times."
1306
+ msgstr ""
1307
+
1308
+ #: src/includes/classes/MenuPageOptions.php:950
1309
+ msgid "Whitelisted URI Inclusion Patterns (Optional; One Per Line)"
1310
+ msgstr ""
1311
+
1312
+ #: src/includes/classes/MenuPageOptions.php:952
1313
+ msgid "<strong>Note:</strong> please remember that your entries here should be formatted as a line-delimited list; e.g., one inclusion pattern per line."
1314
+ msgstr ""
1315
+
1316
+ #: src/includes/classes/MenuPageOptions.php:953
1317
+ msgid "If provided, only local URIs matching one of the patterns you list here will be served from your CDN Host Name. URI patterns are caSe-insensitive. A wildcard <code>*</code> will match zero or more characters in any of your patterns. A caret <code>^</code> symbol will match zero or more characters that are NOT the <code>/</code> character. For instance, <code>*/wp-content/*</code> here would indicate that you only want to filter URLs that lead to files located inside the <code>wp-content</code> directory. Adding an additional line with <code>*/wp-includes/*</code> would filter URLs in the <code>wp-includes</code> directory also. <strong>If you leave this empty</strong>, ALL files matching a static file extension will be served from your CDN; i.e., the default behavior."
1318
+ msgstr ""
1319
+
1320
+ #: src/includes/classes/MenuPageOptions.php:954
1321
+ msgid "Please note that URI patterns are tested against a file's path (i.e., a file's URI, and NOT its full URL). A URI always starts with a leading <code>/</code>. To clarify, a URI is the portion of the URL which comes after the host name. For instance, given the following URL: <code>http://example.com/path/to/style.css?ver=3</code>, the URI you are matching against would be: <code>/path/to/style.css?ver=3</code>. To whitelist this URI, you could use a line that contains something like this: <code>/path/to/*.css*</code>"
1322
+ msgstr ""
1323
+
1324
+ #: src/includes/classes/MenuPageOptions.php:956
1325
+ msgid "Blacklisted URI Exclusion Patterns (Optional; One Per Line)"
1326
+ msgstr ""
1327
+
1328
+ #: src/includes/classes/MenuPageOptions.php:958
1329
+ msgid "With or without a whitelist, you can force exclusions by explicitly blacklisting certain URI patterns. URI patterns are caSe-insensitive. A wildcard <code>*</code> will match zero or more characters in any of your patterns. A caret <code>^</code> symbol will match zero or more characters that are NOT the <code>/</code> character. For instance, <code>*/wp-content/*/dynamic.pdf*</code> would exclude a file with the name <code>dynamic.pdf</code> located anywhere inside a sub-directory of <code>wp-content</code>."
1330
+ msgstr ""
1331
+
1332
+ #: src/includes/classes/MenuPageOptions.php:963
1333
+ msgid "Query String Invalidation Variable Name"
1334
+ msgstr ""
1335
+
1336
+ #: src/includes/classes/MenuPageOptions.php:965
1337
+ msgid "Each filtered URL (which then leads to your CDN) will include this query string variable as an easy way to invalidate the CDN cache at any time. Invalidating the CDN cache is simply a matter of changing the global invalidation counter (i.e., the value assigned to this query string variable). %1$s manages invalidations automatically; i.e., %1$s will automatically bump an internal counter each time you upgrade a WordPress component (e.g., a plugin, theme, or WP itself). Or, if you ask %1$s to invalidate the CDN cache (e.g., a manual clearing of the CDN cache); the internal counter is bumped then too. In short, %1$s handles cache invalidations for you reliably. This option simply allows you to customize the query string variable name which makes cache invalidations possible. <strong>Please note, the default value is adequate for most sites. You can change this if you like, but it's not necessary.</strong>"
1338
+ msgstr ""
1339
+
1340
+ #: src/includes/classes/MenuPageOptions.php:966
1341
+ msgid "<strong>Tip:</strong> You can also tell %1$s to automatically bump the CDN Invalidation Counter whenever you clear the cache manually. See: <strong>%1$s → Manual Cache Clearing → Clear the CDN Cache Too?</strong>"
1342
+ msgstr ""
1343
+
1344
+ #: src/includes/classes/MenuPageOptions.php:967
1345
+ msgid "<strong>Note:</strong> If you empty this field, it will effectively disable the %1$s invalidation system for Static CDN Filters; i.e., the query string variable will NOT be included if you do not supply a variable name."
1346
+ msgstr ""
1347
+
1348
+ #: src/includes/classes/MenuPageOptions.php:980
1349
+ msgid "Dynamic Version Salt"
1350
+ msgstr ""
1351
+
1352
+ #: src/includes/classes/MenuPageOptions.php:985
1353
+ msgid "<i class=\"si si-flask\"></i> <span style=\"display:inline-block; padding:5px; border-radius:3px; background:#FFFFFF; color:#354913;\"><span style=\"font-weight:bold; font-size:80%;\">GEEK ALERT</span></span> This is for VERY advanced users only..."
1354
+ msgstr ""
1355
+
1356
+ #: src/includes/classes/MenuPageOptions.php:986
1357
+ msgid "<em>Note: Understanding the %1$s <a href=\"http://cometcache.com/r/kb-branched-cache-structure/\" target=\"_blank\">Branched Cache Structure</a> is a prerequisite to understanding how Dynamic Version Salts are added to the mix.</em>"
1358
+ msgstr ""
1359
+
1360
+ #: src/includes/classes/MenuPageOptions.php:987
1361
+ msgid "A Version Salt gives you the ability to dynamically create multiple variations of the cache, and those dynamic variations will be served on subsequent visits; e.g., if a visitor has a specific cookie (of a certain value) they will see pages which were cached with that version (i.e., w/ that Version Salt: the value of the cookie). A Version Salt can really be anything."
1362
+ msgstr ""
1363
+
1364
+ #: src/includes/classes/MenuPageOptions.php:988
1365
+ msgid "A Version Salt can be a single variable like <code>$_COOKIE['my_cookie']</code>, or it can be a combination of multiple variables, like <code>$_COOKIE['my_cookie'].$_COOKIE['my_other_cookie']</code>. (When using multiple variables, please separate them with a dot, as shown in the example.)"
1366
+ msgstr ""
1367
+
1368
+ #: src/includes/classes/MenuPageOptions.php:989
1369
+ msgid "Experts could even use PHP ternary expressions that evaluate into something. For example: <code>((preg_match('/iPhone/i', $_SERVER['HTTP_USER_AGENT'])) ? 'iPhones' : '')</code>. This would force a separate version of the cache to be created for iPhones (e.g., <code>/cache/PROTOCOL/HOST/REQUEST-URI.v/iPhones.html</code>)."
1370
+ msgstr ""
1371
+
1372
+ #: src/includes/classes/MenuPageOptions.php:990
1373
+ msgid "For more documentation, please see <a href=\"http://cometcache.com/r/kb-dynamic-version-salts/\" target=\"_blank\">Dynamic Version Salts</a>."
1374
+ msgstr ""
1375
+
1376
+ #: src/includes/classes/MenuPageOptions.php:992
1377
+ msgid "Create a Dynamic Version Salt For %1$s? &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size:90%%; opacity:0.5;\">150%% OPTIONAL</span>"
1378
+ msgstr ""
1379
+
1380
+ #: src/includes/classes/MenuPageOptions.php:994
1381
+ msgid "<a href=\"http://php.net/manual/en/language.variables.superglobals.php\" target=\"_blank\">Super Globals</a> work here; <a href=\"http://codex.wordpress.org/Editing_wp-config.php#table_prefix\" target=\"_blank\"><code>$GLOBALS['table_prefix']</code></a> is a popular one.<br />Or, perhaps a PHP Constant defined in <code>/wp-config.php</code>; such as <code>WPLANG</code> or <code>DB_HOST</code>."
1382
+ msgstr ""
1383
+
1384
+ #: src/includes/classes/MenuPageOptions.php:995
1385
+ msgid "<strong>Important:</strong> your Version Salt is scanned for PHP syntax errors via <a href=\"http://phpcodechecker.com/\" target=\"_blank\"><code>phpCodeChecker.com</code></a>. If errors are found, you'll receive a notice in the Dashboard."
1386
+ msgstr ""
1387
+
1388
+ #: src/includes/classes/MenuPageOptions.php:996
1389
+ msgid "If you've enabled a separate cache for each user (optional) that's perfectly OK. A Version Salt works with user caching too."
1390
+ msgstr ""
1391
+
1392
+ #: src/includes/classes/MenuPageOptions.php:1006
1393
+ msgid "Theme/Plugin Developers"
1394
+ msgstr ""
1395
+
1396
+ #: src/includes/classes/MenuPageOptions.php:1011
1397
+ msgid "Developing a Theme or Plugin for WordPress?"
1398
+ msgstr ""
1399
+
1400
+ #: src/includes/classes/MenuPageOptions.php:1012
1401
+ msgid "<strong>Tip:</strong> %1$s can be disabled temporarily. If you're a theme/plugin developer, you can set a flag within your PHP code to disable the cache engine at runtime. Perhaps on a specific page, or in a specific scenario. In your PHP script, set: <code>$_SERVER['COMET_CACHE_ALLOWED'] = FALSE;</code> or <code>define('COMET_CACHE_ALLOWED', FALSE)</code>. %1$s is also compatible with: <code>define('DONOTCACHEPAGE', TRUE)</code>. It does't matter where or when you define one of these, because %1$s is the last thing to run before script execution ends."
1402
+ msgstr ""
1403
+
1404
+ #: src/includes/classes/MenuPageOptions.php:1014
1405
+ msgid "Writing \"Advanced Cache\" Plugins Specifically for %1$s"
1406
+ msgstr ""
1407
+
1408
+ #: src/includes/classes/MenuPageOptions.php:1015
1409
+ msgid "Theme/plugin developers can take advantage of the %1$s plugin architecture by creating PHP files inside this special directory: <code>/wp-content/ac-plugins/</code>. There is an <a href=\"http://cometcache.com/r/ac-plugin-example/\" target=\"_blank\">example plugin file @ GitHub</a> (please review it carefully and ask questions). If you develop a plugin for %1$s, please share it with the community by publishing it in the plugins respository at WordPress.org."
1410
+ msgstr ""
1411
+
1412
+ #: src/includes/classes/MenuPageOptions.php:1016
1413
+ msgid "<strong>Why does %1$s have it's own plugin architecture?</strong> WordPress loads the <code>advanced-cache.php</code> drop-in file (for caching purposes) very early-on; before any other plugins or a theme. For this reason, %1$s implements it's own watered-down version of functions like <code>add_action()</code>, <code>do_action()</code>, <code>add_filter()</code>, <code>apply_filters()</code>."
1414
+ msgstr ""
1415
+
1416
+ #: src/includes/classes/MenuPageOptions.php:1027
1417
+ msgid "Import/Export Options"
1418
+ msgstr ""
1419
+
1420
+ #: src/includes/classes/MenuPageOptions.php:1032
1421
+ msgid "Import Options from Another %1$s Installation?"
1422
+ msgstr ""
1423
+
1424
+ #: src/includes/classes/MenuPageOptions.php:1033
1425
+ msgid "Upload your <code>%1$s-options.json</code> file and click \"Save All Changes\" below. The options provided by your import file will override any that exist currently."
1426
+ msgstr ""
1427
+
1428
+ #: src/includes/classes/MenuPageOptions.php:1036
1429
+ msgid "Export Existing Options from this %1$s Installation?"
1430
+ msgstr ""
1431
+
1432
+ #: src/includes/classes/MenuPageOptions.php:1039
1433
+ msgid "%1$s-options.json"
1434
+ msgstr ""
1435
+
1436
+ #: src/includes/classes/MenuPageOptions.php:1040
1437
+ msgid "Download your existing options and import them all into another %1$s installation; saves time on future installs."
1438
+ msgstr ""
1439
+
1440
+ #: src/includes/classes/MenuPageOptions.php:1048
1441
+ msgid "Save All Changes"
1442
+ msgstr ""
1443
+
1444
+ #: src/includes/classes/VsUpgrades.php:193
1445
+ msgid "<strong>Woohoo! %1$s activated.</strong> :-)"
1446
+ msgstr ""
1447
+
1448
+ #: src/includes/closures/Ac/NcDebugUtils.php:75
1449
+ msgid "because `PHP_SAPI` reports that you are currently running from the command line."
1450
+ msgstr ""
1451
+
1452
+ #: src/includes/closures/Ac/NcDebugUtils.php:79
1453
+ msgid "because `$_SERVER['HTTP_HOST']` is missing from your server configuration."
1454
+ msgstr ""
1455
+
1456
+ #: src/includes/closures/Ac/NcDebugUtils.php:83
1457
+ msgid "because `$_SERVER['REQUEST_URI']` is missing from your server configuration."
1458
+ msgstr ""
1459
+
1460
+ #: src/includes/closures/Ac/NcDebugUtils.php:88
1461
+ msgid "because the s2Member plugin set the PHP constant `COMET_CACHE_ALLOWED` to a boolean-ish `FALSE` value at runtime. The s2Member plugin is serving content that must remain dynamic on this particular page, and therefore this page was intentionally not cached for a very good reason."
1462
+ msgstr ""
1463
+
1464
+ #: src/includes/closures/Ac/NcDebugUtils.php:90
1465
+ msgid "because the PHP constant `COMET_CACHE_ALLOWED` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it's usually for a very good reason."
1466
+ msgstr ""
1467
+
1468
+ #: src/includes/closures/Ac/NcDebugUtils.php:95
1469
+ msgid "because the environment variable `$_SERVER['COMET_CACHE_ALLOWED']` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it's usually for a very good reason."
1470
+ msgstr ""
1471
+
1472
+ #: src/includes/closures/Ac/NcDebugUtils.php:99
1473
+ msgid "because the PHP constant `DONOTCACHEPAGE` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it's usually for a very good reason."
1474
+ msgstr ""
1475
+
1476
+ #: src/includes/closures/Ac/NcDebugUtils.php:103
1477
+ msgid "because the environment variable `$_SERVER['DONOTCACHEPAGE']` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it's usually for a very good reason."
1478
+ msgstr ""
1479
+
1480
+ #: src/includes/closures/Ac/NcDebugUtils.php:107
1481
+ msgid "because `$_GET['%1$sAC']` is set to a boolean-ish FALSE value."
1482
+ msgstr ""
1483
+
1484
+ #: src/includes/closures/Ac/NcDebugUtils.php:111
1485
+ msgid "because `$_SERVER['REQUEST_METHOD']` is `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `TRACE` or `CONNECT`. These request methods should never (ever) be cached in any way."
1486
+ msgstr ""
1487
+
1488
+ #: src/includes/closures/Ac/NcDebugUtils.php:115
1489
+ msgid "because `[current IP address]` === `$_SERVER['SERVER_ADDR']`; i.e. a self-serve request. DEVELOPER TIP: if you are testing on a localhost installation, please add `define('LOCALHOST', TRUE);` to your `/wp-config.php` file while you run tests :-) Remove it (or set it to a `FALSE` value) once you go live on the web."
1490
+ msgstr ""
1491
+
1492
+ #: src/includes/closures/Ac/NcDebugUtils.php:119
1493
+ msgid "because `$_SERVER['REQUEST_URI']` indicates this is a `/feed`; and the configuration of this site says not to cache XML-based feeds."
1494
+ msgstr ""
1495
+
1496
+ #: src/includes/closures/Ac/NcDebugUtils.php:123
1497
+ msgid "because `$_SERVER['REQUEST_URI']` indicates this is a `wp-` or `xmlrpc` file; i.e. a WordPress systematic file. WordPress systematics are never (ever) cached in any way."
1498
+ msgstr ""
1499
+
1500
+ #: src/includes/closures/Ac/NcDebugUtils.php:127
1501
+ msgid "because `$_SERVER['REQUEST_URI']` or the `is_admin()` function indicates this is an administrative area of the site."
1502
+ msgstr ""
1503
+
1504
+ #: src/includes/closures/Ac/NcDebugUtils.php:131
1505
+ msgid "because `$_SERVER['REQUEST_URI']` indicates this is a Multisite Network; and this was a request for `/files/*`, not a page."
1506
+ msgstr ""
1507
+
1508
+ #: src/includes/closures/Ac/NcDebugUtils.php:136
1509
+ msgid "because the current user visiting this page (usually YOU), appears to be logged-in. The current configuration says NOT to cache pages for logged-in visitors. This message may also appear if you have an active PHP session on this site, or if you've left (or replied to) a comment recently. If this message continues, please clear your cookies and try again."
1510
+ msgstr ""
1511
+
1512
+ #: src/includes/closures/Ac/NcDebugUtils.php:140
1513
+ msgid "because the current page contains `_wpnonce` or `akismet_comment_nonce`. While your current configuration states that pages SHOULD be cache for logged-in visitors, `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details."
1514
+ msgstr ""
1515
+
1516
+ #: src/includes/closures/Ac/NcDebugUtils.php:144
1517
+ msgid "because the current page contains `_wpnonce` or `akismet_comment_nonce`. Note that `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details."
1518
+ msgstr ""
1519
+
1520
+ #: src/includes/closures/Ac/NcDebugUtils.php:148
1521
+ msgid "because the current user appeared to be logged into the site (in one way or another); but %1$s was unable to formulate a User Token for them. Please report this as a possible bug."
1522
+ msgstr ""
1523
+
1524
+ #: src/includes/closures/Ac/NcDebugUtils.php:152
1525
+ msgid "because `$_GET` contains query string data. The current configuration says NOT to cache GET requests with a query string."
1526
+ msgstr ""
1527
+
1528
+ #: src/includes/closures/Ac/NcDebugUtils.php:156
1529
+ msgid "because `$_REQUEST` indicates this is simply a preview of something to come."
1530
+ msgstr ""
1531
+
1532
+ #: src/includes/closures/Ac/NcDebugUtils.php:160
1533
+ msgid "because `$_SERVER['REQUEST_URI']` matches a configured URI Exclusion Pattern on this installation."
1534
+ msgstr ""
1535
+
1536
+ #: src/includes/closures/Ac/NcDebugUtils.php:164
1537
+ msgid "because `$_SERVER['HTTP_USER_AGENT']` matches a configured User-Agent Exclusion Pattern on this installation."
1538
+ msgstr ""
1539
+
1540
+ #: src/includes/closures/Ac/NcDebugUtils.php:168
1541
+ msgid "because `$_SERVER['HTTP_REFERER']` and/or `$_GET['_wp_http_referer']` matches a configured HTTP Referrer Exclusion Pattern on this installation."
1542
+ msgstr ""
1543
+
1544
+ #: src/includes/closures/Ac/NcDebugUtils.php:172
1545
+ msgid "because the WordPress `is_404()` Conditional Tag says the current page is a 404 error. The current configuration says NOT to cache 404 errors."
1546
+ msgstr ""
1547
+
1548
+ #: src/includes/closures/Ac/NcDebugUtils.php:176
1549
+ msgid "because a plugin running on this installation says this page is in Maintenance Mode; i.e. is not available publicly at this time."
1550
+ msgstr ""
1551
+
1552
+ #: src/includes/closures/Ac/NcDebugUtils.php:180
1553
+ msgid "because %1$s is unable to cache already-compressed output. Please use `mod_deflate` w/ Apache; or use `zlib.output_compression` in your `php.ini` file. %1$s is NOT compatible with `ob_gzhandler()` and others like this."
1554
+ msgstr ""
1555
+
1556
+ #: src/includes/closures/Ac/NcDebugUtils.php:184
1557
+ msgid "because the contents of this document contain `<body id=\"error-page\">`, which indicates this is an auto-generated WordPress error message."
1558
+ msgstr ""
1559
+
1560
+ #: src/includes/closures/Ac/NcDebugUtils.php:188
1561
+ msgid "because a `Content-Type:` header was set via PHP at runtime. The header contains a MIME type which is NOT a variation of HTML or XML. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins."
1562
+ msgstr ""
1563
+
1564
+ #: src/includes/closures/Ac/NcDebugUtils.php:192
1565
+ msgid "because a `Status:` header (or an `HTTP/` header) was set via PHP at runtime. The header contains a non-`2xx` status code. This indicates the current page was not loaded successfully. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins."
1566
+ msgstr ""
1567
+
1568
+ #: src/includes/closures/Ac/NcDebugUtils.php:196
1569
+ msgid "because the WordPress `is_404()` Conditional Tag says the current page is a 404 error; and this is the first time it's happened on this page. Your current configuration says that 404 errors SHOULD be cached, so %1$s built a cached symlink which points future requests for this location to your already-cached 404 error document. If you reload this page (assuming you don't clear the cache before you do so); you should get a cached version of your 404 error document. This message occurs ONCE for each new/unique 404 error request."
1570
+ msgstr ""
1571
+
1572
+ #: src/includes/closures/Ac/NcDebugUtils.php:200
1573
+ msgid "because %1$s detected an early output buffer termination. This may happen when a theme/plugin ends, cleans, or flushes all output buffers before reaching the PHP shutdown phase. It's not always a bad thing. Sometimes it is necessary for a theme/plugin to do this. However, in this scenario it is NOT possible to cache the output; since %1$s is effectively disabled at runtime when this occurs."
1574
+ msgstr ""
1575
+
1576
+ #: src/includes/closures/Ac/NcDebugUtils.php:204
1577
+ msgid "due to an unexpected behavior in the application. Please report this as a bug!"
1578
+ msgstr ""
1579
+
1580
+ #: src/includes/closures/Ac/NcDebugUtils.php:208
1581
+ msgid "%1$s is NOT caching this page, %2$s"
1582
+ msgstr ""
1583
+
1584
+ #. translators: This string is actually NOT translatable because the `__()`
1585
+ #. function is not available at this point in the processing.
1586
+ #: src/includes/closures/Ac/ObUtils.php:210
1587
+ msgid "%1$s fully functional :-) Cache file served for (%2$s) in %3$s seconds, on: %4$s."
1588
+ msgstr ""
1589
+
1590
+ #: src/includes/closures/Ac/ObUtils.php:238
1591
+ msgid "Unexpected OB phase: `%1$s`."
1592
+ msgstr ""
1593
+
1594
+ #: src/includes/closures/Ac/ObUtils.php:318
1595
+ msgid "Cache directory not writable. %1$s needs this directory please: `%2$s`. Set permissions to `755` or higher; `777` might be needed in some cases."
1596
+ msgstr ""
1597
+
1598
+ #: src/includes/closures/Ac/ObUtils.php:324
1599
+ #: src/includes/closures/Ac/ObUtils.php:342
1600
+ msgid "Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`."
1601
+ msgstr ""
1602
+
1603
+ #: src/includes/closures/Ac/ObUtils.php:335
1604
+ msgid "%1$s file path: %2$s"
1605
+ msgstr ""
1606
+
1607
+ #: src/includes/closures/Ac/ObUtils.php:336
1608
+ msgid "%1$s file built for (%2$s%3$s) in %4$s seconds, on: %5$s."
1609
+ msgstr ""
1610
+
1611
+ #: src/includes/closures/Ac/ObUtils.php:336
1612
+ msgid "user token: %1$s"
1613
+ msgstr ""
1614
+
1615
+ #: src/includes/closures/Ac/ObUtils.php:337
1616
+ msgid "This %1$s file will auto-expire (and be rebuilt) on: %2$s (based on your configured expiration time)."
1617
+ msgstr ""
1618
+
1619
+ #: src/includes/closures/Ac/ObUtils.php:352
1620
+ msgid "%1$s: failed to write cache file for: `%2$s`; possible permissions issue (or race condition), please check your cache directory: `%3$s`."
1621
+ msgstr ""
1622
+
1623
+ #: src/includes/closures/Plugin/CronUtils.php:18
1624
+ msgid "Every 15 Minutes"
1625
+ msgstr ""
1626
+
1627
+ #: src/includes/closures/Plugin/DirUtils.php:21
1628
+ #: src/includes/closures/Plugin/DirUtils.php:48
1629
+ msgid "Missing `base_dir` option value."
1630
+ msgstr ""
1631
+
1632
+ #: src/includes/closures/Plugin/InstallUtils.php:16
1633
+ msgid "<strong>%1$s</strong> successfully installed! :-) <strong>Please <a href=\"%2$s\">enable caching and review options</a>.</strong>"
1634
+ msgstr ""
1635
+
1636
+ #: src/includes/closures/Plugin/InstallUtils.php:55
1637
+ msgid "<strong>%1$s:</strong> detected a new version of itself. Recompiling w/ latest version... wiping the cache... all done :-)"
1638
+ msgstr ""
1639
+
1640
+ #: src/includes/closures/Plugin/MenuPageUtils.php:46
1641
+ msgid "%"
1642
+ msgstr ""
1643
+
1644
+ #: src/includes/closures/Plugin/MenuPageUtils.php:47
1645
+ msgid "file"
1646
+ msgstr ""
1647
+
1648
+ #: src/includes/closures/Plugin/MenuPageUtils.php:48
1649
+ msgid "files"
1650
+ msgstr ""
1651
+
1652
+ #: src/includes/closures/Plugin/MenuPageUtils.php:49
1653
+ msgid "Page Cache"
1654
+ msgstr ""
1655
+
1656
+ #: src/includes/closures/Plugin/MenuPageUtils.php:50
1657
+ msgid "HTML Compressor"
1658
+ msgstr ""
1659
+
1660
+ #: src/includes/closures/Plugin/MenuPageUtils.php:51
1661
+ msgid "Current Total"
1662
+ msgstr ""
1663
+
1664
+ #: src/includes/closures/Plugin/MenuPageUtils.php:52
1665
+ msgid "Current Site"
1666
+ msgstr ""
1667
+
1668
+ #: src/includes/closures/Plugin/MenuPageUtils.php:53
1669
+ msgid "%s Day High"
1670
+ msgstr ""
1671
+
1672
+ #: src/includes/closures/Plugin/MenuPageUtils.php:114
1673
+ msgid "Settings"
1674
+ msgstr ""
1675
+
1676
+ #: src/includes/closures/Plugin/MenuPageUtils.php:117
1677
+ msgid "Upgrade"
1678
+ msgstr ""
1679
+
1680
+ #: src/includes/closures/Plugin/NoticeUtils.php:151
1681
+ msgid "dismiss &times;"
1682
+ msgstr ""
1683
+
1684
+ #: src/includes/closures/Plugin/WcpAuthorUtils.php:82
1685
+ msgid "<strong>%1$s:</strong> detected changes. Found %2$s in the cache for Author Page: <code>%3$s</code>; auto-clearing."
1686
+ msgstr ""
1687
+
1688
+ #: src/includes/closures/Plugin/WcpFeedUtils.php:113
1689
+ msgid "<strong>%1$s:</strong> detected changes. Found %2$s in the cache, for XML feeds of type: <code>%3$s</code>; auto-clearing."
1690
+ msgstr ""
1691
+
1692
+ #: src/includes/closures/Plugin/WcpHomeBlogUtils.php:38
1693
+ msgid "<strong>%1$s:</strong> detected changes. Found %2$s in the cache for the designated \"Home Page\"; auto-clearing."
1694
+ msgstr ""
1695
+
1696
+ #: src/includes/closures/Plugin/WcpHomeBlogUtils.php:96
1697
+ msgid "<strong>%1$s:</strong> detected changes. Found %2$s in the cache for the designated \"Posts Page\"; auto-clearing."
1698
+ msgstr ""
1699
+
1700
+ #: src/includes/closures/Plugin/WcpPostTypeUtils.php:51
1701
+ msgid "Untitled"
1702
+ msgstr ""
1703
+
1704
+ #: src/includes/closures/Plugin/WcpPostTypeUtils.php:61
1705
+ msgid "<strong>%1$s:</strong> detected changes. Found %2$s in the cache for Custom Post Type: <code>%3$s</code>; auto-clearing."
1706
+ msgstr ""
1707
+
1708
+ #: src/includes/closures/Plugin/WcpPostUtils.php:82
1709
+ msgid "Post"
1710
+ msgstr ""
1711
+
1712
+ #: src/includes/closures/Plugin/WcpPostUtils.php:89
1713
+ msgid "<strong>%1$s:</strong> detected changes. Found %2$s in the cache for %3$s ID: <code>%4$s</code>; auto-clearing."
1714
+ msgstr ""
1715
+
1716
+ #: src/includes/closures/Plugin/WcpSitemapUtils.php:44
1717
+ msgid "<strong>%1$s:</strong> detected changes. Found %2$s in the cache for XML sitemaps; auto-clearing."
1718
+ msgstr ""
1719
+
1720
+ #: src/includes/closures/Plugin/WcpTermUtils.php:130
1721
+ msgid "<strong>%1$s:</strong> detected changes. Found %2$s in the cache for %3$s: <code>%4$s</code>; auto-clearing."
1722
+ msgstr ""
1723
+
1724
+ #: src/includes/closures/Plugin/WcpUtils.php:164
1725
+ msgid "<strong>%1$s:</strong> detected significant changes. Found %2$s in the cache; auto-wiping."
1726
+ msgstr ""
1727
+
1728
+ #: src/includes/closures/Plugin/WcpUtils.php:210
1729
+ msgid "<strong>%1$s:</strong> detected important site changes. Found %2$s in the cache for this site; auto-clearing."
1730
+ msgstr ""
1731
+
1732
+ #: src/includes/closures/Plugin/WcpUtils.php:244
1733
+ msgid "<strong>%1$s:</strong> detected important site changes. Found %2$s in the cache for this site that were expired; auto-purging."
1734
+ msgstr ""
1735
+
1736
+ #: src/includes/closures/Plugin/WcpUtils.php:278
1737
+ msgid "<strong>%1$s:</strong> detected important site changes. Found %2$s in the cache that were expired; auto-purging."
1738
+ msgstr ""
1739
+
1740
+ #: src/includes/closures/Plugin/WcpUtils.php:298
1741
+ msgid "<strong>%1$s:</strong> detected significant changes that would normally trigger cache wiping routines. However, cache wiping routines have been disabled by a site administrator. [<a href=\"http://cometcache.com/r/kb-clear-and-wipe-cache-routines/\" target=\"_blank\">?</a>]"
1742
+ msgstr ""
1743
+
1744
+ #: src/includes/closures/Plugin/WcpUtils.php:318
1745
+ msgid "<strong>%1$s:</strong> detected important site changes that would normally trigger cache clearing routines. However, cache clearing routines have been disabled by a site administrator. [<a href=\"http://cometcache.com/r/kb-clear-and-wipe-cache-routines/\" target=\"_blank\">?</a>]"
1746
+ msgstr ""
1747
+
1748
+ #: src/includes/closures/Plugin/WcpUtils.php:338
1749
+ msgid "<strong>%1$s:</strong> detected important site changes that would normally trigger cache purging routines. However, cache purging routines have been disabled by a site administrator. [<a href=\"http://cometcache.com/r/kb-clear-and-wipe-cache-routines/\" target=\"_blank\">?</a>]"
1750
+ msgstr ""
1751
+
1752
+ #: src/includes/closures/Shared/CacheDirUtils.php:24
1753
+ msgid "Unable to determine cache directory location."
1754
+ msgstr ""
1755
+
1756
+ #: src/includes/closures/Shared/CacheDirUtils.php:121
1757
+ #: src/includes/closures/Shared/CacheDirUtils.php:277
1758
+ msgid "Invalid argument; isAdvancedCache!"
1759
+ msgstr ""
1760
+
1761
+ #: src/includes/closures/Shared/CacheDirUtils.php:148
1762
+ msgid "Unable to delete files. Rename failure on directory: `%1$s`."
1763
+ msgstr ""
1764
+
1765
+ #: src/includes/closures/Shared/CacheDirUtils.php:172
1766
+ #: src/includes/closures/Shared/CacheDirUtils.php:340
1767
+ #: src/includes/closures/Shared/CacheDirUtils.php:480
1768
+ msgid "Unable to delete symlink: `%1$s`."
1769
+ msgstr ""
1770
+
1771
+ #: src/includes/closures/Shared/CacheDirUtils.php:187
1772
+ #: src/includes/closures/Shared/CacheDirUtils.php:355
1773
+ #: src/includes/closures/Shared/CacheDirUtils.php:490
1774
+ msgid "Unable to delete file: `%1$s`."
1775
+ msgstr ""
1776
+
1777
+ #: src/includes/closures/Shared/CacheDirUtils.php:203
1778
+ #: src/includes/closures/Shared/CacheDirUtils.php:371
1779
+ #: src/includes/closures/Shared/CacheDirUtils.php:500
1780
+ msgid "Unable to delete dir: `%1$s`."
1781
+ msgstr ""
1782
+
1783
+ #: src/includes/closures/Shared/CacheDirUtils.php:211
1784
+ #: src/includes/closures/Shared/CacheDirUtils.php:379
1785
+ #: src/includes/closures/Shared/CacheDirUtils.php:508
1786
+ #: src/includes/closures/Shared/CacheDirUtils.php:603
1787
+ msgid "Unexpected resource type: `%1$s`."
1788
+ msgstr ""
1789
+
1790
+ #: src/includes/closures/Shared/CacheDirUtils.php:218
1791
+ #: src/includes/closures/Shared/CacheDirUtils.php:316
1792
+ #: src/includes/closures/Shared/CacheDirUtils.php:386
1793
+ msgid "Unable to delete files. Rename failure on tmp directory: `%1$s`."
1794
+ msgstr ""
1795
+
1796
+ #: src/includes/closures/Shared/CacheDirUtils.php:274
1797
+ msgid "Invalid argument; host token empty!"
1798
+ msgstr ""
1799
+
1800
+ #: src/includes/closures/Shared/CacheDirUtils.php:467
1801
+ #: src/includes/closures/Shared/CacheDirUtils.php:515
1802
+ msgid "Unable to delete all files/dirs. Rename failure on tmp directory: `%1$s`."
1803
+ msgstr ""
1804
+
1805
+ #: src/includes/closures/Shared/CacheDirUtils.php:519
1806
+ msgid "Unable to delete directory: `%1$s`."
1807
+ msgstr ""
1808
+
1809
+ #: src/includes/closures/Shared/CacheDirUtils.php:578
1810
+ msgid "Unable to erase symlink: `%1$s`."
1811
+ msgstr ""
1812
+
1813
+ #: src/includes/closures/Shared/CacheDirUtils.php:587
1814
+ msgid "Unable to erase file: `%1$s`."
1815
+ msgstr ""
1816
+
1817
+ #: src/includes/closures/Shared/CacheDirUtils.php:596
1818
+ msgid "Unable to erase dir: `%1$s`."
1819
+ msgstr ""
1820
+
1821
+ #: src/includes/closures/Shared/CacheDirUtils.php:610
1822
+ msgid "Unable to erase directory: `%1$s`."
1823
+ msgstr ""
1824
+
1825
+ #: src/includes/closures/Shared/CacheLockUtils.php:25
1826
+ msgid "Unable to find the wp-config.php file."
1827
+ msgstr ""
1828
+
1829
+ #: src/includes/closures/Shared/CacheLockUtils.php:42
1830
+ msgid "No writable tmp directory."
1831
+ msgstr ""
1832
+
1833
+ #: src/includes/closures/Shared/CacheLockUtils.php:48
1834
+ msgid "Unable to obtain an exclusive lock."
1835
+ msgstr ""
1836
+
1837
+ #: src/includes/closures/Shared/HookUtils.php:40
1838
+ msgid "Invalid hook."
1839
+ msgstr ""
1840
+
1841
+ #: src/includes/closures/Shared/I18nUtils.php:15
1842
+ msgid "%1$s file"
1843
+ msgid_plural "%1$s files"
1844
+ msgstr[0] ""
1845
+ msgstr[1] ""
1846
+
1847
+ #: src/includes/closures/Shared/I18nUtils.php:29
1848
+ msgid "%1$s directory"
1849
+ msgid_plural "%1$s directories"
1850
+ msgstr[0] ""
1851
+ msgstr[1] ""
1852
+
1853
+ #: src/includes/closures/Shared/I18nUtils.php:43
1854
+ msgid "%1$s file/directory"
1855
+ msgid_plural "%1$s files/directories"
1856
+ msgstr[0] ""
1857
+ msgstr[1] ""
1858
+
1859
+ #: src/includes/closures/Shared/SysUtils.php:65
1860
+ msgid "%s%%"
1861
+ msgstr ""
1862
+
1863
+ #. Plugin Name of the plugin/theme
1864
+ msgid "Comet Cache"
1865
+ msgstr ""
1866
+
1867
+ #. Plugin URI of the plugin/theme
1868
+ msgid "http://cometcache.com/"
1869
+ msgstr ""
1870
+
1871
+ #. Description of the plugin/theme
1872
+ msgid "Comet Cache is an advanced WordPress caching plugin inspired by simplicity."
1873
+ msgstr ""
1874
+
1875
+ #. Author of the plugin/theme
1876
+ msgid "WebSharks, Inc."
1877
+ msgstr ""
1878
+
1879
+ #. Author URI of the plugin/theme
1880
+ msgid "http://websharks-inc.com/"
1881
+ msgstr ""
src/includes/uninstall.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Uninstaller.
4
+ *
5
+ * @since 150422 Rewrite.
6
+ */
7
+ namespace WebSharks\Comet_Cache;
8
+
9
+ if (!defined('WPINC')) {
10
+ exit('Do NOT access this file directly: '.basename(__FILE__));
11
+ }
12
+ require_once dirname(__FILE__).'/stub.php';
13
+
14
+ if (!Conflicts::check()) {
15
+ $GLOBALS[GLOBAL_NS.'_uninstalling'] = true;
16
+ $GLOBALS[GLOBAL_NS] = new Plugin(false);
17
+ $GLOBALS[GLOBAL_NS]->uninstall();
18
+ }
src/vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer' . '/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit5dad45be0f8ab232c2bf57fb8eacc8b9::getLoader();
src/vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,413 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0 class loader
17
+ *
18
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
19
+ *
20
+ * $loader = new \Composer\Autoload\ClassLoader();
21
+ *
22
+ * // register classes with namespaces
23
+ * $loader->add('Symfony\Component', __DIR__.'/component');
24
+ * $loader->add('Symfony', __DIR__.'/framework');
25
+ *
26
+ * // activate the autoloader
27
+ * $loader->register();
28
+ *
29
+ * // to enable searching the include path (eg. for PEAR packages)
30
+ * $loader->setUseIncludePath(true);
31
+ *
32
+ * In this example, if you try to use a class in the Symfony\Component
33
+ * namespace or one of its children (Symfony\Component\Console for instance),
34
+ * the autoloader will first look for the class under the component/
35
+ * directory, and it will then fallback to the framework/ directory if not
36
+ * found before giving up.
37
+ *
38
+ * This class is loosely based on the Symfony UniversalClassLoader.
39
+ *
40
+ * @author Fabien Potencier <fabien@symfony.com>
41
+ * @author Jordi Boggiano <j.boggiano@seld.be>
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+
57
+ private $classMapAuthoritative = false;
58
+
59
+ public function getPrefixes()
60
+ {
61
+ if (!empty($this->prefixesPsr0)) {
62
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
63
+ }
64
+
65
+ return array();
66
+ }
67
+
68
+ public function getPrefixesPsr4()
69
+ {
70
+ return $this->prefixDirsPsr4;
71
+ }
72
+
73
+ public function getFallbackDirs()
74
+ {
75
+ return $this->fallbackDirsPsr0;
76
+ }
77
+
78
+ public function getFallbackDirsPsr4()
79
+ {
80
+ return $this->fallbackDirsPsr4;
81
+ }
82
+
83
+ public function getClassMap()
84
+ {
85
+ return $this->classMap;
86
+ }
87
+
88
+ /**
89
+ * @param array $classMap Class to filename map
90
+ */
91
+ public function addClassMap(array $classMap)
92
+ {
93
+ if ($this->classMap) {
94
+ $this->classMap = array_merge($this->classMap, $classMap);
95
+ } else {
96
+ $this->classMap = $classMap;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Registers a set of PSR-0 directories for a given prefix, either
102
+ * appending or prepending to the ones previously set for this prefix.
103
+ *
104
+ * @param string $prefix The prefix
105
+ * @param array|string $paths The PSR-0 root directories
106
+ * @param bool $prepend Whether to prepend the directories
107
+ */
108
+ public function add($prefix, $paths, $prepend = false)
109
+ {
110
+ if (!$prefix) {
111
+ if ($prepend) {
112
+ $this->fallbackDirsPsr0 = array_merge(
113
+ (array) $paths,
114
+ $this->fallbackDirsPsr0
115
+ );
116
+ } else {
117
+ $this->fallbackDirsPsr0 = array_merge(
118
+ $this->fallbackDirsPsr0,
119
+ (array) $paths
120
+ );
121
+ }
122
+
123
+ return;
124
+ }
125
+
126
+ $first = $prefix[0];
127
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
128
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
129
+
130
+ return;
131
+ }
132
+ if ($prepend) {
133
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
134
+ (array) $paths,
135
+ $this->prefixesPsr0[$first][$prefix]
136
+ );
137
+ } else {
138
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
139
+ $this->prefixesPsr0[$first][$prefix],
140
+ (array) $paths
141
+ );
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Registers a set of PSR-4 directories for a given namespace, either
147
+ * appending or prepending to the ones previously set for this namespace.
148
+ *
149
+ * @param string $prefix The prefix/namespace, with trailing '\\'
150
+ * @param array|string $paths The PSR-0 base directories
151
+ * @param bool $prepend Whether to prepend the directories
152
+ *
153
+ * @throws \InvalidArgumentException
154
+ */
155
+ public function addPsr4($prefix, $paths, $prepend = false)
156
+ {
157
+ if (!$prefix) {
158
+ // Register directories for the root namespace.
159
+ if ($prepend) {
160
+ $this->fallbackDirsPsr4 = array_merge(
161
+ (array) $paths,
162
+ $this->fallbackDirsPsr4
163
+ );
164
+ } else {
165
+ $this->fallbackDirsPsr4 = array_merge(
166
+ $this->fallbackDirsPsr4,
167
+ (array) $paths
168
+ );
169
+ }
170
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
171
+ // Register directories for a new namespace.
172
+ $length = strlen($prefix);
173
+ if ('\\' !== $prefix[$length - 1]) {
174
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
175
+ }
176
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
177
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
178
+ } elseif ($prepend) {
179
+ // Prepend directories for an already registered namespace.
180
+ $this->prefixDirsPsr4[$prefix] = array_merge(
181
+ (array) $paths,
182
+ $this->prefixDirsPsr4[$prefix]
183
+ );
184
+ } else {
185
+ // Append directories for an already registered namespace.
186
+ $this->prefixDirsPsr4[$prefix] = array_merge(
187
+ $this->prefixDirsPsr4[$prefix],
188
+ (array) $paths
189
+ );
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Registers a set of PSR-0 directories for a given prefix,
195
+ * replacing any others previously set for this prefix.
196
+ *
197
+ * @param string $prefix The prefix
198
+ * @param array|string $paths The PSR-0 base directories
199
+ */
200
+ public function set($prefix, $paths)
201
+ {
202
+ if (!$prefix) {
203
+ $this->fallbackDirsPsr0 = (array) $paths;
204
+ } else {
205
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Registers a set of PSR-4 directories for a given namespace,
211
+ * replacing any others previously set for this namespace.
212
+ *
213
+ * @param string $prefix The prefix/namespace, with trailing '\\'
214
+ * @param array|string $paths The PSR-4 base directories
215
+ *
216
+ * @throws \InvalidArgumentException
217
+ */
218
+ public function setPsr4($prefix, $paths)
219
+ {
220
+ if (!$prefix) {
221
+ $this->fallbackDirsPsr4 = (array) $paths;
222
+ } else {
223
+ $length = strlen($prefix);
224
+ if ('\\' !== $prefix[$length - 1]) {
225
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
226
+ }
227
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
228
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Turns on searching the include path for class files.
234
+ *
235
+ * @param bool $useIncludePath
236
+ */
237
+ public function setUseIncludePath($useIncludePath)
238
+ {
239
+ $this->useIncludePath = $useIncludePath;
240
+ }
241
+
242
+ /**
243
+ * Can be used to check if the autoloader uses the include path to check
244
+ * for classes.
245
+ *
246
+ * @return bool
247
+ */
248
+ public function getUseIncludePath()
249
+ {
250
+ return $this->useIncludePath;
251
+ }
252
+
253
+ /**
254
+ * Turns off searching the prefix and fallback directories for classes
255
+ * that have not been registered with the class map.
256
+ *
257
+ * @param bool $classMapAuthoritative
258
+ */
259
+ public function setClassMapAuthoritative($classMapAuthoritative)
260
+ {
261
+ $this->classMapAuthoritative = $classMapAuthoritative;
262
+ }
263
+
264
+ /**
265
+ * Should class lookup fail if not found in the current class map?
266
+ *
267
+ * @return bool
268
+ */
269
+ public function isClassMapAuthoritative()
270
+ {
271
+ return $this->classMapAuthoritative;
272
+ }
273
+
274
+ /**
275
+ * Registers this instance as an autoloader.
276
+ *
277
+ * @param bool $prepend Whether to prepend the autoloader or not
278
+ */
279
+ public function register($prepend = false)
280
+ {
281
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
282
+ }
283
+
284
+ /**
285
+ * Unregisters this instance as an autoloader.
286
+ */
287
+ public function unregister()
288
+ {
289
+ spl_autoload_unregister(array($this, 'loadClass'));
290
+ }
291
+
292
+ /**
293
+ * Loads the given class or interface.
294
+ *
295
+ * @param string $class The name of the class
296
+ * @return bool|null True if loaded, null otherwise
297
+ */
298
+ public function loadClass($class)
299
+ {
300
+ if ($file = $this->findFile($class)) {
301
+ includeFile($file);
302
+
303
+ return true;
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Finds the path to the file where the class is defined.
309
+ *
310
+ * @param string $class The name of the class
311
+ *
312
+ * @return string|false The path if found, false otherwise
313
+ */
314
+ public function findFile($class)
315
+ {
316
+ // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
317
+ if ('\\' == $class[0]) {
318
+ $class = substr($class, 1);
319
+ }
320
+
321
+ // class map lookup
322
+ if (isset($this->classMap[$class])) {
323
+ return $this->classMap[$class];
324
+ }
325
+ if ($this->classMapAuthoritative) {
326
+ return false;
327
+ }
328
+
329
+ $file = $this->findFileWithExtension($class, '.php');
330
+
331
+ // Search for Hack files if we are running on HHVM
332
+ if ($file === null && defined('HHVM_VERSION')) {
333
+ $file = $this->findFileWithExtension($class, '.hh');
334
+ }
335
+
336
+ if ($file === null) {
337
+ // Remember that this class does not exist.
338
+ return $this->classMap[$class] = false;
339
+ }
340
+
341
+ return $file;
342
+ }
343
+
344
+ private function findFileWithExtension($class, $ext)
345
+ {
346
+ // PSR-4 lookup
347
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
348
+
349
+ $first = $class[0];
350
+ if (isset($this->prefixLengthsPsr4[$first])) {
351
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
352
+ if (0 === strpos($class, $prefix)) {
353
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
354
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
355
+ return $file;
356
+ }
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ // PSR-4 fallback dirs
363
+ foreach ($this->fallbackDirsPsr4 as $dir) {
364
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
365
+ return $file;
366
+ }
367
+ }
368
+
369
+ // PSR-0 lookup
370
+ if (false !== $pos = strrpos($class, '\\')) {
371
+ // namespaced class name
372
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
373
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
374
+ } else {
375
+ // PEAR-like class name
376
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
377
+ }
378
+
379
+ if (isset($this->prefixesPsr0[$first])) {
380
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
381
+ if (0 === strpos($class, $prefix)) {
382
+ foreach ($dirs as $dir) {
383
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
384
+ return $file;
385
+ }
386
+ }
387
+ }
388
+ }
389
+ }
390
+
391
+ // PSR-0 fallback dirs
392
+ foreach ($this->fallbackDirsPsr0 as $dir) {
393
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
394
+ return $file;
395
+ }
396
+ }
397
+
398
+ // PSR-0 include paths.
399
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
400
+ return $file;
401
+ }
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Scope isolated include.
407
+ *
408
+ * Prevents access to $this/self from included files.
409
+ */
410
+ function includeFile($file)
411
+ {
412
+ include $file;
413
+ }
src/vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) 2015 Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
src/vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname(dirname($vendorDir));
7
+
8
+ return array(
9
+ );
src/vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname(dirname($vendorDir));
7
+
8
+ return array(
9
+ );
src/vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname(dirname($vendorDir));
7
+
8
+ return array(
9
+ 'WebSharks\\JsMinifier\\' => array($vendorDir . '/websharks/js-minifier/src/includes/classes'),
10
+ 'WebSharks\\HtmlCompressor\\' => array($vendorDir . '/websharks/html-compressor/src/includes/classes'),
11
+ 'WebSharks\\CssMinifier\\' => array($vendorDir . '/websharks/css-minifier/src/includes/classes'),
12
+ 'WebSharks\\Comet_Cache\\' => array($baseDir . '/src/includes/classes'),
13
+ );
src/vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit5dad45be0f8ab232c2bf57fb8eacc8b9
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInit5dad45be0f8ab232c2bf57fb8eacc8b9', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit5dad45be0f8ab232c2bf57fb8eacc8b9', 'loadClassLoader'));
25
+
26
+ $map = require __DIR__ . '/autoload_namespaces.php';
27
+ foreach ($map as $namespace => $path) {
28
+ $loader->set($namespace, $path);
29
+ }
30
+
31
+ $map = require __DIR__ . '/autoload_psr4.php';
32
+ foreach ($map as $namespace => $path) {
33
+ $loader->setPsr4($namespace, $path);
34
+ }
35
+
36
+ $classMap = require __DIR__ . '/autoload_classmap.php';
37
+ if ($classMap) {
38
+ $loader->addClassMap($classMap);
39
+ }
40
+
41
+ $loader->register(true);
42
+
43
+ return $loader;
44
+ }
45
+ }
46
+
47
+ function composerRequire5dad45be0f8ab232c2bf57fb8eacc8b9($file)
48
+ {
49
+ require $file;
50
+ }
src/vendor/composer/installed.json ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "websharks/wp-php-rv",
4
+ "version": "150511",
5
+ "version_normalized": "150511",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/websharks/wp-php-rv.git",
9
+ "reference": "ed0a58f3647e59c740560235da1e61904dca8d84"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/websharks/wp-php-rv/zipball/ed0a58f3647e59c740560235da1e61904dca8d84",
14
+ "reference": "ed0a58f3647e59c740560235da1e61904dca8d84",
15
+ "shasum": ""
16
+ },
17
+ "require": {
18
+ "php": ">=5.2"
19
+ },
20
+ "time": "2015-05-10 10:30:04",
21
+ "type": "library",
22
+ "installation-source": "dist",
23
+ "notification-url": "https://packagist.org/downloads/",
24
+ "license": [
25
+ "GPL-3.0+"
26
+ ],
27
+ "authors": [
28
+ {
29
+ "name": "websharks",
30
+ "homepage": "http://websharks-inc.com/",
31
+ "role": "company"
32
+ },
33
+ {
34
+ "name": "jaswsinc",
35
+ "homepage": "http://jaswsinc.com/",
36
+ "role": "developer"
37
+ },
38
+ {
39
+ "name": "raamdev",
40
+ "homepage": "http://raam.org/",
41
+ "role": "developer"
42
+ }
43
+ ],
44
+ "description": "Stub for WordPress themes/plugins that require PHP vX.x+.",
45
+ "homepage": "https://github.com/websharks/wp-php-rv",
46
+ "keywords": [
47
+ "php",
48
+ "websharks",
49
+ "wordpress"
50
+ ]
51
+ },
52
+ {
53
+ "name": "websharks/css-minifier",
54
+ "version": "dev-master",
55
+ "version_normalized": "9999999-dev",
56
+ "source": {
57
+ "type": "git",
58
+ "url": "https://github.com/websharks/css-minifier.git",
59
+ "reference": "da1d0254c41e1f59c7337aa444d743e6056046ff"
60
+ },
61
+ "dist": {
62
+ "type": "zip",
63
+ "url": "https://api.github.com/repos/websharks/css-minifier/zipball/da1d0254c41e1f59c7337aa444d743e6056046ff",
64
+ "reference": "da1d0254c41e1f59c7337aa444d743e6056046ff",
65
+ "shasum": ""
66
+ },
67
+ "require": {
68
+ "ext-mbstring": "*",
69
+ "php": ">=5.3"
70
+ },
71
+ "time": "2015-08-21 03:19:25",
72
+ "type": "library",
73
+ "installation-source": "dist",
74
+ "autoload": {
75
+ "psr-4": {
76
+ "WebSharks\\CssMinifier\\": "src/includes/classes"
77
+ }
78
+ },
79
+ "notification-url": "https://packagist.org/downloads/",
80
+ "license": [
81
+ "GPL-3.0+"
82
+ ],
83
+ "authors": [
84
+ {
85
+ "name": "websharks",
86
+ "homepage": "http://websharks-inc.com/",
87
+ "role": "company"
88
+ },
89
+ {
90
+ "name": "jaswsinc",
91
+ "homepage": "http://jaswsinc.com/",
92
+ "role": "developer"
93
+ },
94
+ {
95
+ "name": "raamdev",
96
+ "homepage": "http://raam.org/",
97
+ "role": "developer"
98
+ }
99
+ ],
100
+ "description": "Compresses CSS.",
101
+ "homepage": "https://github.com/websharks/css-minifier",
102
+ "keywords": [
103
+ "compressor",
104
+ "css",
105
+ "websharks"
106
+ ]
107
+ },
108
+ {
109
+ "name": "websharks/js-minifier",
110
+ "version": "dev-master",
111
+ "version_normalized": "9999999-dev",
112
+ "source": {
113
+ "type": "git",
114
+ "url": "https://github.com/websharks/js-minifier.git",
115
+ "reference": "3c1e99af6df9df8f691642fbb769a10e49bfba23"
116
+ },
117
+ "dist": {
118
+ "type": "zip",
119
+ "url": "https://api.github.com/repos/websharks/js-minifier/zipball/3c1e99af6df9df8f691642fbb769a10e49bfba23",
120
+ "reference": "3c1e99af6df9df8f691642fbb769a10e49bfba23",
121
+ "shasum": ""
122
+ },
123
+ "require": {
124
+ "ext-mbstring": "*",
125
+ "php": ">=5.3"
126
+ },
127
+ "time": "2015-05-11 20:53:04",
128
+ "type": "library",
129
+ "installation-source": "dist",
130
+ "autoload": {
131
+ "psr-4": {
132
+ "WebSharks\\JsMinifier\\": "src/includes/classes"
133
+ }
134
+ },
135
+ "notification-url": "https://packagist.org/downloads/",
136
+ "license": [
137
+ "GPL-3.0+"
138
+ ],
139
+ "authors": [
140
+ {
141
+ "name": "websharks",
142
+ "homepage": "http://websharks-inc.com/",
143
+ "role": "company"
144
+ },
145
+ {
146
+ "name": "jaswsinc",
147
+ "homepage": "http://jaswsinc.com/",
148
+ "role": "developer"
149
+ },
150
+ {
151
+ "name": "raamdev",
152
+ "homepage": "http://raam.org/",
153
+ "role": "developer"
154
+ }
155
+ ],
156
+ "description": "Compresses JavaScript code.",
157
+ "homepage": "https://github.com/websharks/js-minifier",
158
+ "keywords": [
159
+ "JS",
160
+ "compressor",
161
+ "javascript",
162
+ "websharks"
163
+ ]
164
+ },
165
+ {
166
+ "name": "websharks/html-compressor",
167
+ "version": "160118",
168
+ "version_normalized": "160118",
169
+ "source": {
170
+ "type": "git",
171
+ "url": "https://github.com/websharks/html-compressor.git",
172
+ "reference": "bd1ba2407b9c2c067549600a6ef1faa726c3c2d2"
173
+ },
174
+ "dist": {
175
+ "type": "zip",
176
+ "url": "https://api.github.com/repos/websharks/html-compressor/zipball/bd1ba2407b9c2c067549600a6ef1faa726c3c2d2",
177
+ "reference": "bd1ba2407b9c2c067549600a6ef1faa726c3c2d2",
178
+ "shasum": ""
179
+ },
180
+ "require": {
181
+ "ext-curl": "*",
182
+ "ext-mbstring": "*",
183
+ "ext-openssl": "*",
184
+ "php": ">=5.3",
185
+ "websharks/css-minifier": "dev-master",
186
+ "websharks/js-minifier": "dev-master"
187
+ },
188
+ "time": "2016-01-18 03:17:20",
189
+ "type": "library",
190
+ "installation-source": "dist",
191
+ "autoload": {
192
+ "psr-4": {
193
+ "WebSharks\\HtmlCompressor\\": "src/includes/classes"
194
+ }
195
+ },
196
+ "notification-url": "https://packagist.org/downloads/",
197
+ "license": [
198
+ "GPL-3.0+"
199
+ ],
200
+ "authors": [
201
+ {
202
+ "name": "websharks",
203
+ "homepage": "http://websharks-inc.com/",
204
+ "role": "company"
205
+ },
206
+ {
207
+ "name": "jaswsinc",
208
+ "homepage": "http://jaswsinc.com/",
209
+ "role": "developer"
210
+ },
211
+ {
212
+ "name": "raamdev",
213
+ "homepage": "http://raam.org/",
214
+ "role": "developer"
215
+ }
216
+ ],
217
+ "description": "Combines & compresses CSS/JS/HTML code.",
218
+ "homepage": "https://github.com/websharks/html-compressor",
219
+ "keywords": [
220
+ "compressor",
221
+ "html",
222
+ "websharks"
223
+ ]
224
+ },
225
+ {
226
+ "name": "websharks/sharkicons",
227
+ "version": "160209",
228
+ "version_normalized": "160209",
229
+ "source": {
230
+ "type": "git",
231
+ "url": "https://github.com/websharks/sharkicons.git",
232
+ "reference": "146508cc2a77bb49528f39799d864c4fdf14e3aa"
233
+ },
234
+ "dist": {
235
+ "type": "zip",
236
+ "url": "https://api.github.com/repos/websharks/sharkicons/zipball/146508cc2a77bb49528f39799d864c4fdf14e3aa",
237
+ "reference": "146508cc2a77bb49528f39799d864c4fdf14e3aa",
238
+ "shasum": ""
239
+ },
240
+ "require-dev": {
241
+ "package/bourbon": "4.2.3"
242
+ },
243
+ "time": "2016-02-09 22:36:25",
244
+ "type": "library",
245
+ "installation-source": "dist",
246
+ "autoload": {
247
+ "psr-4": []
248
+ },
249
+ "notification-url": "https://packagist.org/downloads/",
250
+ "license": [
251
+ "GPL-3.0+"
252
+ ],
253
+ "authors": [
254
+ {
255
+ "name": "websharks",
256
+ "homepage": "http://websharks-inc.com/",
257
+ "role": "company"
258
+ },
259
+ {
260
+ "name": "jaswsinc",
261
+ "homepage": "http://jaswsinc.com/",
262
+ "role": "developer"
263
+ },
264
+ {
265
+ "name": "raamdev",
266
+ "homepage": "http://raam.org/",
267
+ "role": "developer"
268
+ }
269
+ ],
270
+ "description": "Font containing WebSharks logos/icons.",
271
+ "homepage": "https://github.com/websharks/sharkicons",
272
+ "keywords": [
273
+ "php",
274
+ "websharks",
275
+ "wordpress"
276
+ ]
277
+ },
278
+ {
279
+ "name": "package/bourbon",
280
+ "version": "4.2.3",
281
+ "version_normalized": "4.2.3.0",
282
+ "dist": {
283
+ "type": "zip",
284
+ "url": "https://github.com/thoughtbot/bourbon/releases/download/v4.2.3/bourbon-v4.2.3.zip",
285
+ "reference": null,
286
+ "shasum": null
287
+ },
288
+ "type": "library",
289
+ "installation-source": "dist"
290
+ },
291
+ {
292
+ "name": "websharks/wp-i18n-tools",
293
+ "version": "dev-master",
294
+ "version_normalized": "9999999-dev",
295
+ "source": {
296
+ "type": "git",
297
+ "url": "https://github.com/websharks/wp-i18n-tools.git",
298
+ "reference": "47a2af9f88cfaf0ad33a81c2c882cd70bfed95b6"
299
+ },
300
+ "dist": {
301
+ "type": "zip",
302
+ "url": "https://api.github.com/repos/websharks/wp-i18n-tools/zipball/47a2af9f88cfaf0ad33a81c2c882cd70bfed95b6",
303
+ "reference": "47a2af9f88cfaf0ad33a81c2c882cd70bfed95b6",
304
+ "shasum": ""
305
+ },
306
+ "time": "2015-05-11 14:42:39",
307
+ "type": "library",
308
+ "installation-source": "dist",
309
+ "notification-url": "https://packagist.org/downloads/",
310
+ "license": [
311
+ "GPL-3.0+"
312
+ ],
313
+ "authors": [
314
+ {
315
+ "name": "websharks",
316
+ "homepage": "http://websharks-inc.com/",
317
+ "role": "company"
318
+ },
319
+ {
320
+ "name": "jaswsinc",
321
+ "homepage": "http://jaswsinc.com/",
322
+ "role": "developer"
323
+ },
324
+ {
325
+ "name": "raamdev",
326
+ "homepage": "http://raam.org/",
327
+ "role": "developer"
328
+ }
329
+ ],
330
+ "description": "WordPress i18n/translation tools.",
331
+ "homepage": "https://github.com/websharks/wp-i18n-tools",
332
+ "keywords": [
333
+ "gettext",
334
+ "i18n",
335
+ "translation",
336
+ "websharks",
337
+ "wordpress"
338
+ ]
339
+ }
340
+ ]
src/vendor/websharks/css-minifier/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ ## v150510
2
+
3
+ - Initial release.
src/vendor/websharks/css-minifier/LICENSE.txt ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the program. It is safest
630
+ to attach them to the start of each source file to most effectively
631
+ state the exclusion of warranty; and each file should have at least
632
+ the "copyright" line and a pointer to where the full notice is found.
633
+
634
+ <one line to give the program's name and a brief idea of what it does.>
635
+ Copyright (C) <year> <name of author>
636
+
637
+ This program is free software: you can redistribute it and/or modify
638
+ it under the terms of the GNU General Public License as published by
639
+ the Free Software Foundation, either version 3 of the License, or
640
+ (at your option) any later version.
641
+
642
+ This program is distributed in the hope that it will be useful,
643
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
+ GNU General Public License for more details.
646
+
647
+ You should have received a copy of the GNU General Public License
648
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
649
+
650
+ Also add information on how to contact you by electronic and paper mail.
651
+
652
+ If the program does terminal interaction, make it output a short
653
+ notice like this when it starts in an interactive mode:
654
+
655
+ <program> Copyright (C) <year> <name of author>
656
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
+ This is free software, and you are welcome to redistribute it
658
+ under certain conditions; type `show c' for details.
659
+
660
+ The hypothetical commands `show w' and `show c' should show the appropriate
661
+ parts of the General Public License. Of course, your program's commands
662
+ might be different; for a GUI interface, you would use an "about box".
663
+
664
+ You should also get