14cadd4f8SNickeau<?php 24cadd4f8SNickeau 34cadd4f8SNickeauuse ComboStrap\CacheExpirationDate; 44cadd4f8SNickeauuse ComboStrap\CacheExpirationFrequency; 54cadd4f8SNickeauuse ComboStrap\CacheLog; 64cadd4f8SNickeauuse ComboStrap\Cron; 74cadd4f8SNickeauuse ComboStrap\Event; 8*04fd306cSNickeauuse ComboStrap\ExceptionCompile; 9*04fd306cSNickeauuse ComboStrap\ExceptionNotFound; 10*04fd306cSNickeauuse ComboStrap\ExecutionContext; 114cadd4f8SNickeauuse ComboStrap\Iso8601Date; 124cadd4f8SNickeauuse ComboStrap\LogUtility; 13*04fd306cSNickeauuse ComboStrap\MarkupCacheDependencies; 14*04fd306cSNickeauuse ComboStrap\MarkupPath; 154cadd4f8SNickeauuse ComboStrap\PagePath; 164cadd4f8SNickeauuse dokuwiki\Cache\CacheRenderer; 174cadd4f8SNickeau 18*04fd306cSNickeaurequire_once(__DIR__ . '/../vendor/autoload.php'); 194cadd4f8SNickeau 204cadd4f8SNickeau/** 214cadd4f8SNickeau * Can we use the parser cache 224cadd4f8SNickeau * 234cadd4f8SNickeau * 244cadd4f8SNickeau * 254cadd4f8SNickeau */ 264cadd4f8SNickeauclass action_plugin_combo_cacheexpiration extends DokuWiki_Action_Plugin 274cadd4f8SNickeau{ 284cadd4f8SNickeau 294cadd4f8SNickeau 304cadd4f8SNickeau const CANONICAL = CacheExpirationFrequency::CANONICAL; 314cadd4f8SNickeau const SLOT_CACHE_EXPIRATION_EVENT = "slot-cache-expiration"; 324cadd4f8SNickeau const REQUESTED_ID = "requested-id"; 334cadd4f8SNickeau 344cadd4f8SNickeau 354cadd4f8SNickeau /** 364cadd4f8SNickeau * @param Doku_Event_Handler $controller 374cadd4f8SNickeau */ 384cadd4f8SNickeau function register(Doku_Event_Handler $controller) 394cadd4f8SNickeau { 404cadd4f8SNickeau 414cadd4f8SNickeau 424cadd4f8SNickeau /** 434cadd4f8SNickeau * Page expiration feature 444cadd4f8SNickeau */ 454cadd4f8SNickeau $controller->register_hook('PARSER_CACHE_USE', 'BEFORE', $this, 'slotCreateCacheExpiration', array()); 464cadd4f8SNickeau 474cadd4f8SNickeau 484cadd4f8SNickeau /** 49*04fd306cSNickeau * Process the Async event 504cadd4f8SNickeau */ 514cadd4f8SNickeau $controller->register_hook(self::SLOT_CACHE_EXPIRATION_EVENT, 'AFTER', $this, 'handleSlotCacheExpiration'); 524cadd4f8SNickeau 534cadd4f8SNickeau } 544cadd4f8SNickeau 554cadd4f8SNickeau 564cadd4f8SNickeau /** 574cadd4f8SNickeau * 584cadd4f8SNickeau * Purge the cache if needed 594cadd4f8SNickeau * @param Doku_Event $event 604cadd4f8SNickeau * @param $params 614cadd4f8SNickeau */ 624cadd4f8SNickeau function slotCreateCacheExpiration(Doku_Event $event, $params) 634cadd4f8SNickeau { 644cadd4f8SNickeau 654cadd4f8SNickeau /** 664cadd4f8SNickeau * No cache for all mode 674cadd4f8SNickeau * (ie xhtml, instruction) 684cadd4f8SNickeau */ 694cadd4f8SNickeau $data = &$event->data; 704cadd4f8SNickeau $pageId = $data->page; 714cadd4f8SNickeau 724cadd4f8SNickeau /** 734cadd4f8SNickeau * For whatever reason, the cache file of XHTML 744cadd4f8SNickeau * may be empty - No error found on the web server or the log. 754cadd4f8SNickeau * 764cadd4f8SNickeau * We just delete it then. 774cadd4f8SNickeau * 784cadd4f8SNickeau * It has been seen after the creation of a new page or a `move` of the page. 794cadd4f8SNickeau */ 804cadd4f8SNickeau if ($data instanceof CacheRenderer) { 814cadd4f8SNickeau if ($data->mode === "xhtml") { 824cadd4f8SNickeau if (file_exists($data->cache)) { 834cadd4f8SNickeau if (filesize($data->cache) === 0) { 844cadd4f8SNickeau $data->depends["purge"] = true; 854cadd4f8SNickeau return; 864cadd4f8SNickeau } 874cadd4f8SNickeau } 884cadd4f8SNickeau } 894cadd4f8SNickeau } 904cadd4f8SNickeau 91*04fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 92*04fd306cSNickeau $cacheManager = $executionContext->getCacheManager(); 934cadd4f8SNickeau $shouldSlotExpire = $cacheManager->shouldSlotExpire($pageId); 944cadd4f8SNickeau if ($shouldSlotExpire) { 95*04fd306cSNickeau try { 96*04fd306cSNickeau $requestedWikiId = $executionContext->getRequestedPath()->getWikiId(); 97*04fd306cSNickeau } catch (ExceptionNotFound $e) { 98*04fd306cSNickeau LogUtility::internalError("Cache expiration: The requested path could not be determined, default context path was set instead."); 99*04fd306cSNickeau $requestedWikiId = $executionContext->getContextPath()->getWikiId(); 100*04fd306cSNickeau } 1014cadd4f8SNickeau Event::createEvent( 1024cadd4f8SNickeau self::SLOT_CACHE_EXPIRATION_EVENT, 1034cadd4f8SNickeau [ 1044cadd4f8SNickeau PagePath::getPersistentName() => $pageId, 105*04fd306cSNickeau self::REQUESTED_ID => $requestedWikiId 1064cadd4f8SNickeau ] 1074cadd4f8SNickeau ); 1084cadd4f8SNickeau } 1094cadd4f8SNickeau 1104cadd4f8SNickeau 1114cadd4f8SNickeau } 1124cadd4f8SNickeau 1134cadd4f8SNickeau public function handleSlotCacheExpiration($event) 1144cadd4f8SNickeau { 1154cadd4f8SNickeau $data = $event->data; 1164cadd4f8SNickeau $slotPath = $data[PagePath::getPersistentName()]; 1174cadd4f8SNickeau $requestedId = $data[self::REQUESTED_ID]; 1184cadd4f8SNickeau 1194cadd4f8SNickeau /** 1204cadd4f8SNickeau * The cache file may be dependent on the requested id 121*04fd306cSNickeau * ie (@link MarkupCacheDependencies::OUTPUT_DEPENDENCIES} 1224cadd4f8SNickeau */ 1234cadd4f8SNickeau global $ID; 1244cadd4f8SNickeau $keep = $ID; 1254cadd4f8SNickeau try { 1264cadd4f8SNickeau $ID = $requestedId; 127*04fd306cSNickeau $slot = MarkupPath::createPageFromAbsoluteId($slotPath); 1284cadd4f8SNickeau 1294cadd4f8SNickeau /** 1304cadd4f8SNickeau * Calculate a new expiration date 1314cadd4f8SNickeau * And set it here because setting a new metadata 1324cadd4f8SNickeau * will make the cache unusable 1334cadd4f8SNickeau */ 1344cadd4f8SNickeau $cacheExpirationDateMeta = CacheExpirationDate::createForPage($slot); 1354cadd4f8SNickeau $actualDate = $cacheExpirationDateMeta->getValue(); 1364cadd4f8SNickeau $cacheExpirationFrequency = CacheExpirationFrequency::createForPage($slot) 1374cadd4f8SNickeau ->getValue(); 1384cadd4f8SNickeau try { 1394cadd4f8SNickeau $newDate = Cron::getDate($cacheExpirationFrequency); 140*04fd306cSNickeau } catch (ExceptionCompile $e) { 1414cadd4f8SNickeau LogUtility::msg("Error while calculating the new expiration date. Error: {$e->getMessage()}"); 1424cadd4f8SNickeau return; 1434cadd4f8SNickeau } 1444cadd4f8SNickeau if ($newDate < $actualDate) { 1454cadd4f8SNickeau LogUtility::msg("The new calculated date cache expiration frequency ({$newDate->format(Iso8601Date::getFormat())}) is lower than the current date ({$actualDate->format(Iso8601Date::getFormat())})"); 1464cadd4f8SNickeau } 1474cadd4f8SNickeau try { 1484cadd4f8SNickeau $cacheExpirationDateMeta 1494cadd4f8SNickeau ->setValue($newDate) 1504cadd4f8SNickeau ->persist(); 151*04fd306cSNickeau } catch (ExceptionCompile $e) { 1524cadd4f8SNickeau LogUtility::msg("Error while persisting the new expiration date. Error:{$e->getMessage()}"); 1534cadd4f8SNickeau return; 1544cadd4f8SNickeau } 1554cadd4f8SNickeau 1564cadd4f8SNickeau /** 1574cadd4f8SNickeau * Cache deletion 1584cadd4f8SNickeau */ 1594cadd4f8SNickeau $message = "Expiration Date has expired"; 160*04fd306cSNickeau $outputDocument = $slot->getInstructionsDocument(); 1614cadd4f8SNickeau CacheLog::deleteCacheIfExistsAndLog( 162*04fd306cSNickeau $outputDocument, 1634cadd4f8SNickeau self::SLOT_CACHE_EXPIRATION_EVENT, 1644cadd4f8SNickeau $message); 165*04fd306cSNickeau $fetcher = $slot->createHtmlFetcherWithItselfAsContextPath(); 1664cadd4f8SNickeau CacheLog::deleteCacheIfExistsAndLog( 167*04fd306cSNickeau $fetcher, 1684cadd4f8SNickeau self::SLOT_CACHE_EXPIRATION_EVENT, 1694cadd4f8SNickeau $message); 1704cadd4f8SNickeau 1714cadd4f8SNickeau /** 1724cadd4f8SNickeau * Re-render 1734cadd4f8SNickeau */ 174*04fd306cSNickeau $fetcher2 = $slot->createHtmlFetcherWithItselfAsContextPath(); 1754cadd4f8SNickeau CacheLog::renderCacheAndLog( 176*04fd306cSNickeau $fetcher2, 1774cadd4f8SNickeau self::SLOT_CACHE_EXPIRATION_EVENT, 1784cadd4f8SNickeau $message); 1794cadd4f8SNickeau 180*04fd306cSNickeau 1814cadd4f8SNickeau } finally { 1824cadd4f8SNickeau $ID = $keep; 1834cadd4f8SNickeau } 1844cadd4f8SNickeau 1854cadd4f8SNickeau } 1864cadd4f8SNickeau 1874cadd4f8SNickeau 1884cadd4f8SNickeau} 189