Commit 3951bad6 authored by Rolf Forst's avatar Rolf Forst
Browse files

Merge branch 'dev' into 'master'

Dev

See merge request !17
parents b3f895e3 2feae9d4
......@@ -11,6 +11,7 @@ RRZE Cache optimiert das Laden von Seiten, indem Beiträge, Seiten und Custom-Po
- Funktioniert auch mit Custom-Post-Types
- Cache-Dateien werden im Verzeichnis "wp-content/advanced-cache/" gespeichert
- Der geleerte Cache wird vorübergehend im Verzeichnis "wp-content/advanced-cache-old/" gespeichert
- Schaltfläche "Cache leeren" in der WordPress-Adminleiste
- WordPress Multisite kompatibel
- Einzelne Posts können aus dem Cache ausgeschlossen werden
......@@ -38,10 +39,30 @@ Einstellungsmenü: Netzwerkverwaltung › Einstellungen › Cache
## Hooks
Der Filter 'rrzecache_skip_cache' kann den Cache umgehen. Dieser Filter ist äußerst nützlich, wenn er in Plugins enthalten ist, die dynamischen Inhalt erstellen.
Der Filter 'rrzecache_skip_cache' kann den Cache umgehen, wenn er auf true gesetzt ist.
Anwendungsbeispiel:
<pre>
add_filter('rrzecache_skip_cache', '__return_true');
</pre>
Die Aktion 'rrzecache_flush_cache_post_id' leert den Cache einer einzelnen Post-ID.
Anwendungsbeispiel:
<pre>
do_action('rrzecache_flush_cache_post_id', $post->ID);
</pre>
Die Aktion 'rrzecache_flush_cache_url' leert den Cache einer Seiten-URL.
Anwendungsbeispiel:
<pre>
do_action('rrzecache_flush_cache_url', $url);
</pre>
Die Aktion 'rrzecache_flush_cache' leert den Cache der gesamten Website.
Anwendungsbeispiel:
<pre>
do_action('rrzecache_flush_cache');
</pre>
<?php
if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] != 'GET') {
return false;
namespace RRZE\Cache;
defined('ABSPATH') || exit;
if (!skipCache()) {
getCache();
}
if (!empty($_COOKIE)) {
foreach ($_COOKIE as $k => $v) {
if (preg_match('/^(wp-postpass|wordpress_logged_in|comment_author)_/', $k)) {
return false;
function skipCache(): bool
{
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] != 'GET') {
return true;
}
if (!empty($_SERVER['QUERY_STRING'])) {
return true;
}
if (!empty($_COOKIE)) {
foreach ($_COOKIE as $k => $v) {
if (preg_match('/^(wp-postpass|wordpress_logged_in|comment_author)_/', $k)) {
return true;
}
}
}
return false;
}
$timeout_file = WP_CONTENT_DIR . '/advanced-cache/timeout';
$expires_seconds = 10 * MINUTE_IN_SECONDS;
function getCache()
{
$timeout_file = WP_CONTENT_DIR . '/advanced-cache/timeout';
$expires_seconds = 10 * MINUTE_IN_SECONDS;
if (is_readable($timeout_file) && filesize($timeout_file) > 0) {
$expires_seconds = abs(intval(file_get_contents($timeout_file))) * MINUTE_IN_SECONDS;
}
if (is_readable($timeout_file) && filesize($timeout_file) > 0) {
$expires_seconds = abs(intval(file_get_contents($timeout_file))) * MINUTE_IN_SECONDS;
}
$expires_seconds = $expires_seconds >= (1 * MINUTE_IN_SECONDS) ? $expires_seconds : (10 * MINUTE_IN_SECONDS);
$path = WP_CONTENT_DIR . '/advanced-cache' .
DIRECTORY_SEPARATOR .
parse_url('https://' . strtolower($_SERVER['HTTP_HOST']), PHP_URL_HOST) .
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path = rtrim($path, '/\\') . '/';
$cache_file = $path . 'index.html';
// Check if the cache file exists and is readable.
if (is_readable($cache_file) && ($content = file_get_contents($cache_file)) !== false && (filesize($cache_file) > 0)) {
// Check if the cache file is not stale.
if (($filemtime = filemtime($cache_file) + $expires_seconds) > time()) {
// Not stale: Output the content of the cache file.
header('Last-Modified: ' . date('r', $filemtime), true);
header('Cache-Control: max-age=' . $expires_seconds . ', must-revalidate', true);
echo $content;
exit;
} else {
// Stale: Remove the cache file.
unlink($cache_file);
$expires_seconds = $expires_seconds >= (1 * MINUTE_IN_SECONDS) ? $expires_seconds : (10 * MINUTE_IN_SECONDS);
$path = WP_CONTENT_DIR . '/advanced-cache' .
DIRECTORY_SEPARATOR .
parse_url('https://' . strtolower($_SERVER['HTTP_HOST']), PHP_URL_HOST) .
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path = rtrim($path, '/\\') . '/';
$cache_file = $path . 'index.html';
// Check if the cache file exists and is readable.
if (is_readable($cache_file) && ($content = file_get_contents($cache_file)) !== false && (filesize($cache_file) > 0)) {
// Check if the cache file is not stale.
if (($filemtime = filemtime($cache_file) + $expires_seconds) > time()) {
// Not stale: Output the content of the cache file.
header('Last-Modified: ' . date('r', $filemtime), true);
header('Cache-Control: max-age=' . $expires_seconds . ', must-revalidate', true);
echo $content;
exit;
} else {
// Stale: Remove the cache file.
unlink($cache_file);
}
}
}
<?php
namespace RRZE\Cache;
defined('ABSPATH') || exit;
class Cache
{
/**
* [protected description]
* @var object
*/
protected $options;
/**
* [protected description]
* @var object
*/
protected $siteOptions;
/**
* [__construct description]
*/
public function __construct()
{
$this->options = Options::getOptions();
$this->siteOptions = Options::getSiteOptions();
}
public function onLoaded()
{
$this->setCacheFiles();
add_action('init', [$this, 'add_publish_hooks'], 99);
add_action('pre_comment_approved', [$this, 'pre_comment_approved'], 99, 2);
add_action('transition_comment_status', [$this, 'transition_comment_status'], 10, 3);
add_action('edit_comment', [$this, 'edit_comment']);
add_action('post_submitbox_misc_actions', [$this, 'post_cache_submitbox'], 99);
add_action('template_redirect', [$this, 'template_redirect'], 0);
add_action('rrzecache_flush_cache_post_id', [__NAMESPACE__ . '\Flush', 'flushCacheByPostId']);
add_action('rrzecache_flush_cache_url', [__NAMESPACE__ . '\Flush', 'flushCacheByUrl']);
add_action('rrzecache_flush_cache', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('_core_updated_successfully', [__NAMESPACE__ . '\Flush', 'flushAllCache']);
add_action('switch_theme', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('customize_save_after', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('wp_update_nav_menu', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('wp_trash_post', [__NAMESPACE__ . '\Flush', 'flushCache']);
}
protected function setCacheFiles()
{
File::createCacheDir();
File::createCacheOldDir();
if (!file_exists(File::getTimeoutFile())) {
File::storeTimeout($this->siteOptions->cache_expires);
}
}
public function add_publish_hooks()
{
$post_types = get_post_types(['public' => true]);
if (empty($post_types)) {
return;
}
foreach ($post_types as $post_type) {
add_action('publish_' . $post_type, [$this, 'publish_post_types'], 10, 2);
add_action('publish_future_' . $post_type, [__NAMESPACE__ . '\Flush', 'flushCache']);
}
}
public function publish_post_types($post_ID, $post)
{
if (empty($post_ID) || empty($post)) {
return;
}
if (!in_array($post->post_status, ['publish', 'future'])) {
return;
}
if (!current_user_can('publish_posts')) {
return;
}
if ($this->options->reset_on_publish) {
Flush::flushCache();
} else {
Flush::flushCacheByPostId($post_ID);
}
if (isset($_POST['post_cache_submitbox_nonce']) && wp_verify_nonce($_POST['post_cache_submitbox_nonce'], 'post_cache_submitbox')) {
if ($_POST['rrze_cache_select'] == 'disabled') {
update_post_meta($post_ID, RRZECACHE_META_KEY, 'disabled');
} else {
delete_post_meta($post_ID, RRZECACHE_META_KEY);
}
}
// rrze-ac plugin
if (is_plugin_active('rrze-ac/rrze-ac.php') && isset($_POST['post_protection_submitbox_nonce']) && wp_verify_nonce($_POST['post_protection_submitbox_nonce'], 'post_protection_submitbox')) {
if ($_POST['access_permission_select'] != 'all') {
update_post_meta($post_ID, RRZECACHE_META_KEY, 'disabled');
} elseif ($_POST['access_permission_select'] == 'all' && !get_post_meta($post_ID, RRZECACHE_META_KEY, true)) {
delete_post_meta($post_ID, RRZECACHE_META_KEY);
}
}
}
public function pre_comment_approved($approved, $comment)
{
if ($approved === 1) {
if ($this->options->reset_on_comment) {
Flush::flushCache();
} else {
Flush::flushCacheByPostId($comment['comment_post_ID']);
}
}
return $approved;
}
public function transition_comment_status($new_status, $old_status, $comment)
{
if ($new_status != $old_status) {
if ($this->options->reset_on_comment) {
Flush::flushCache();
} else {
Flush::flushCacheByPostId($comment->comment_post_ID);
}
}
}
public function edit_comment($id)
{
if ($this->options->reset_on_comment) {
Flush::flushCache();
} else {
Flush::flushCacheByPostId(get_comment($id)->comment_post_ID);
}
}
public function post_cache_submitbox()
{
global $post;
wp_nonce_field('post_cache_submitbox', 'post_cache_submitbox_nonce');
$cache = get_post_meta($post->ID, RRZECACHE_META_KEY, true) ? 'disabled' : 'enabled';
// rrze-ac plugin
if (is_plugin_active('rrze-ac/rrze-ac.php')) {
$permission = get_post_meta($post->ID, ACCESS_PERMISSION_META_KEY, true);
if (!empty($permission) && $permission != 'all') {
$cache = 'disabled';
}
}
$label = $cache == 'disabled' ? __('Disabled', 'rrze-cache') : __('Enabled', 'rrze-cache');
$class = $cache == 'disabled' ? 'cache-disabled-icon' : 'cache-icon'; ?>
<div id="post-cache-wrap" class="misc-pub-section">
<span>
<span id="cache-icon" class="<?php echo $class; ?> dashicons dashicons-cloud"></span>
<?php _e('Cache:', 'rrze-cache'); ?>
<b id="post-cache-label"><?php echo $label; ?></b>
</span>
<?php if (empty($permission) || $permission == 'all') : ?>
<a href="#" id="edit-post-cache" class="edit-post-cache hide-if-no-js">
<span aria-hidden="true"><?php _e('Edit', 'rrze-cache'); ?></span>
<span class="screen-reader-text"><?php _e('Edit Cache', 'rrze-cache'); ?></span>
</a>
<div id="post-cache-field" class="hide-if-js">
<select id="rrze-cache-select" name="rrze_cache_select">
<option value="enabled" <?php selected($cache, 'enabled'); ?>>
<?php echo sanitize_text_field(__('Enable', 'rrze-cache')); ?>
</option>
<option value="disabled" <?php selected($cache, 'disabled'); ?>>
<?php echo sanitize_text_field(__('Disable', 'rrze-cache')); ?>
</option>
</select>
<a href="#" class="save-post-cache hide-if-no-js button"><?php _e('OK', 'rrze-cache'); ?></a>
<a href="#" class="cancel-post-cache hide-if-no-js button-cancel"><?php _e('Cancel', 'rrze-cache'); ?></a>
</div>
<?php endif ?>
</div>
<?php
}
public function template_redirect()
{
if ($this->skip_cache()) {
return;
}
$cached = File::getItem($this->get_cache_expires());
if (!$cached) {
ob_start([$this, 'storeCache']);
}
}
public function get_cache_expires()
{
if (is_multisite()) {
return $this->siteOptions->cache_expires;
}
return $this->options->cache_expires;
}
public function storeCache($data)
{
if (empty($data)) {
return '';
}
File::storeItem($data);
return $data;
}
protected function is_logged_in()
{
if (is_user_logged_in()) {
return true;
}
if (empty($_COOKIE)) {
return false;
}
foreach ($_COOKIE as $k => $v) {
if (preg_match('/^(wp-postpass|wordpress_logged_in|comment_author)_/', $k)) {
return true;
}
}
}
protected function is_wp_sitemap()
{
global $wp;
if (preg_match('/^wp-sitemap/', $wp->request)) {
return true;
}
return false;
}
protected function is_mobile()
{
$templatedir = get_template_directory();
return (strpos($templatedir, 'wptouch') || strpos($templatedir, 'carrington') || strpos($templatedir, 'jetpack') || strpos($templatedir, 'handheld'));
}
protected function skip_cache()
{
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] != 'GET') {
return true;
}
if (!empty($_SERVER['QUERY_STRING'])) {
return true;
}
if (basename($_SERVER['SCRIPT_NAME']) != 'index.php') {
return true;
}
if ($this->is_logged_in()) {
return true;
}
if (apply_filters('rrzecache_skip_cache', false)) {
return true;
}
if (is_search() || is_404() || is_feed() || is_trackback() || is_robots() || is_preview() || post_password_required()) {
return true;
}
if (defined('DONOTCACHEPAGE') && DONOTCACHEPAGE) {
return true;
}
if ($this->is_mobile()) {
return true;
}
if ($this->is_wp_sitemap()) {
return true;
}
if (is_singular()) {
$post_ID = $GLOBALS['wp_query']->get_queried_object_id();
$cache = get_post_meta($post_ID, RRZECACHE_META_KEY, true) ? 'disabled' : 'enabled';
if ($cache == 'disabled') {
return true;
}
}
return false;
}
}
......@@ -314,9 +314,10 @@ final class File
* Recursively deletes a directory tree.
* @param string $folder The directory path.
* @param bool $keepRootFolder Whether to keep the top-level folder.
* @param int $maxFiles The max. number of files to delete
* @return bool TRUE on success, otherwise FALSE.
*/
protected static function removeDir(string $folder, bool $keepRootFolder = false): bool
public static function removeDir(string $folder, bool $keepRootFolder = false, int $maxFiles = 0): bool
{
// Handle arguments.
if (empty($folder) || !file_exists($folder)) {
......@@ -332,16 +333,20 @@ final class File
);
// Delete all children.
$i = 1;
$maxFiles = absint($maxFiles);
foreach ($files as $fileinfo) {
$action = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
if (!@$action($fileinfo->getRealPath())) {
return false; // Abort due to the failure.
}
$i++;
if($maxFiles && $i > $maxFiles) {
return false;
}
}
clearstatcache();
return (!$keepRootFolder ? @rmdir($folder) : true);
return ((!$keepRootFolder && !$maxFiles) ? @rmdir($folder) : true);
}
/**
......@@ -362,6 +367,15 @@ final class File
return self::$cacheDir;
}
/**
* [getCacheOldDir description]
* @return string [description]
*/
public static function getCacheOldDir(): string
{
return self::$cacheOldDir;
}
/**
* [getTimeoutFile description]
* @return string [description]
......
......@@ -6,12 +6,6 @@ defined('ABSPATH') || exit;
class Main
{
/**
* [protected description]
* @var object
*/
protected $options;
/**
* [protected description]
* @var object
......@@ -23,59 +17,35 @@ class Main
*/
public function __construct()
{
$this->options = Options::getOptions();
$this->siteOptions = Options::getSiteOptions();
}
public function onLoaded()
{
$this->setCacheFiles();
$settings = new Settings();
$settings = new Settings;
$settings->onLoaded();
add_action('init', [$this, 'add_publish_hooks'], 99);
add_action('rrzecache_flush_cache_post_id', [__NAMESPACE__ . '\Flush', 'flushCacheByPostId']);
add_action('rrzecache_flush_cache_url', [__NAMESPACE__ . '\Flush', 'flushCacheByUrl']);
add_action('rrzecache_flush_cache', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('_core_updated_successfully', [__NAMESPACE__ . '\Flush', 'flushAllCache']);
add_action('switch_theme', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('customize_save_after', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('wp_update_nav_menu', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('wp_trash_post', [__NAMESPACE__ . '\Flush', 'flushCache']);
add_action('pre_comment_approved', [$this, 'pre_comment_approved'], 99, 2);
if (!is_multisite() || (is_multisite() && $this->siteOptions->cache_enabled)) {
add_action('admin_enqueue_scripts', [$this, 'admin_enqueue_scripts']);
add_action('transition_comment_status', [$this, 'transition_comment_status'], 10, 3);
add_action('edit_comment', [$this, 'edit_comment']);
if (is_multisite() && !$this->siteOptions->cache_enabled) {
return;
}
add_action('post_submitbox_misc_actions', [$this, 'post_cache_submitbox'], 99);
$cache = new Cache;
$cache->onLoaded();
add_action('template_redirect', [$this, 'template_redirect'], 0);
$schedule = new Schedule;
$schedule->onLoaded();
add_action('robots_txt', [$this, 'robots_txt'], 10, 2);
}
add_action('robots_txt', [$this, 'robots_txt'], 10, 2);
add_action('admin_enqueue_scripts', [$this, 'admin_enqueue_scripts']);
}
protected function setCacheFiles()
public function robots_txt($data, $public)
{
File::createCacheDir();
File::createCacheOldDir();
if (!file_exists(File::getTimeoutFile())) {
File::storeTimeout($this->siteOptions->cache_expires);
if ($public) {
$path = parse_url(site_url(), PHP_URL_PATH);
$data .= sprintf('%2$sDisallow: %1$s/wp-content/advanced-cache/%2$s', (empty($path) ? '' : $path), PHP_EOL);
}
return $data;
}
public function admin_enqueue_scripts($hook)
......@@ -132,264 +102,4 @@ class Main
);
}
}
public function add_publish_hooks()
{
$post_types = get_post_types(['public' => true]);
if (empty($post_types)) {
return;
}
foreach ($post_types as $post_type) {
add_action('publish_' . $post_type, [$this, 'publish_post_types'], 10, 2);
add_action('publish_future_' . $post_type, [__NAMESPACE__ . '\Flush', 'flushCache']);
}
}
public function publish_post_types($post_ID, $post)
{
if (empty($post_ID) || empty($post)) {
return;
}
if (!in_array($post->post_status, ['publish', 'future'])) {
return;
}
if (!current_user_can('publish_posts')) {
return;
}
if ($this->options->reset_on_publish) {
Flush::flushCache();
} else {
Flush::flushCacheByPostId($post_ID);
}