1*4cadd4f8SNickeau<?php 2*4cadd4f8SNickeau 3*4cadd4f8SNickeauuse ComboStrap\CacheDependencies; 4*4cadd4f8SNickeauuse ComboStrap\CacheExpirationDate; 5*4cadd4f8SNickeauuse ComboStrap\CacheExpirationFrequency; 6*4cadd4f8SNickeauuse ComboStrap\CacheLog; 7*4cadd4f8SNickeauuse ComboStrap\CacheManager; 8*4cadd4f8SNickeauuse ComboStrap\CacheMedia; 9*4cadd4f8SNickeauuse ComboStrap\CacheMenuItem; 10*4cadd4f8SNickeauuse ComboStrap\CacheReportHtmlDataBlockArray; 11*4cadd4f8SNickeauuse ComboStrap\Cron; 12*4cadd4f8SNickeauuse ComboStrap\Event; 13*4cadd4f8SNickeauuse ComboStrap\ExceptionCombo; 14*4cadd4f8SNickeauuse ComboStrap\FileSystems; 15*4cadd4f8SNickeauuse ComboStrap\Http; 16*4cadd4f8SNickeauuse ComboStrap\Iso8601Date; 17*4cadd4f8SNickeauuse ComboStrap\LogUtility; 18*4cadd4f8SNickeauuse ComboStrap\Page; 19*4cadd4f8SNickeauuse ComboStrap\PagePath; 20*4cadd4f8SNickeauuse ComboStrap\PluginUtility; 21*4cadd4f8SNickeauuse dokuwiki\Cache\CacheRenderer; 22*4cadd4f8SNickeau 23*4cadd4f8SNickeaurequire_once(__DIR__ . '/../ComboStrap/PluginUtility.php'); 24*4cadd4f8SNickeau 25*4cadd4f8SNickeau/** 26*4cadd4f8SNickeau * Can we use the parser cache 27*4cadd4f8SNickeau * 28*4cadd4f8SNickeau * 29*4cadd4f8SNickeau * 30*4cadd4f8SNickeau */ 31*4cadd4f8SNickeauclass action_plugin_combo_cacheexpiration extends DokuWiki_Action_Plugin 32*4cadd4f8SNickeau{ 33*4cadd4f8SNickeau 34*4cadd4f8SNickeau 35*4cadd4f8SNickeau const CANONICAL = CacheExpirationFrequency::CANONICAL; 36*4cadd4f8SNickeau const SLOT_CACHE_EXPIRATION_EVENT = "slot-cache-expiration"; 37*4cadd4f8SNickeau const REQUESTED_ID = "requested-id"; 38*4cadd4f8SNickeau 39*4cadd4f8SNickeau 40*4cadd4f8SNickeau /** 41*4cadd4f8SNickeau * @param Doku_Event_Handler $controller 42*4cadd4f8SNickeau */ 43*4cadd4f8SNickeau function register(Doku_Event_Handler $controller) 44*4cadd4f8SNickeau { 45*4cadd4f8SNickeau 46*4cadd4f8SNickeau 47*4cadd4f8SNickeau /** 48*4cadd4f8SNickeau * Page expiration feature 49*4cadd4f8SNickeau */ 50*4cadd4f8SNickeau $controller->register_hook('PARSER_CACHE_USE', 'BEFORE', $this, 'slotCreateCacheExpiration', array()); 51*4cadd4f8SNickeau 52*4cadd4f8SNickeau 53*4cadd4f8SNickeau /** 54*4cadd4f8SNickeau * process the Async event 55*4cadd4f8SNickeau */ 56*4cadd4f8SNickeau $controller->register_hook(self::SLOT_CACHE_EXPIRATION_EVENT, 'AFTER', $this, 'handleSlotCacheExpiration'); 57*4cadd4f8SNickeau 58*4cadd4f8SNickeau } 59*4cadd4f8SNickeau 60*4cadd4f8SNickeau 61*4cadd4f8SNickeau /** 62*4cadd4f8SNickeau * 63*4cadd4f8SNickeau * Purge the cache if needed 64*4cadd4f8SNickeau * @param Doku_Event $event 65*4cadd4f8SNickeau * @param $params 66*4cadd4f8SNickeau */ 67*4cadd4f8SNickeau function slotCreateCacheExpiration(Doku_Event $event, $params) 68*4cadd4f8SNickeau { 69*4cadd4f8SNickeau 70*4cadd4f8SNickeau /** 71*4cadd4f8SNickeau * No cache for all mode 72*4cadd4f8SNickeau * (ie xhtml, instruction) 73*4cadd4f8SNickeau */ 74*4cadd4f8SNickeau $data = &$event->data; 75*4cadd4f8SNickeau $pageId = $data->page; 76*4cadd4f8SNickeau 77*4cadd4f8SNickeau /** 78*4cadd4f8SNickeau * For whatever reason, the cache file of XHTML 79*4cadd4f8SNickeau * may be empty - No error found on the web server or the log. 80*4cadd4f8SNickeau * 81*4cadd4f8SNickeau * We just delete it then. 82*4cadd4f8SNickeau * 83*4cadd4f8SNickeau * It has been seen after the creation of a new page or a `move` of the page. 84*4cadd4f8SNickeau */ 85*4cadd4f8SNickeau if ($data instanceof CacheRenderer) { 86*4cadd4f8SNickeau if ($data->mode === "xhtml") { 87*4cadd4f8SNickeau if (file_exists($data->cache)) { 88*4cadd4f8SNickeau if (filesize($data->cache) === 0) { 89*4cadd4f8SNickeau $data->depends["purge"] = true; 90*4cadd4f8SNickeau return; 91*4cadd4f8SNickeau } 92*4cadd4f8SNickeau } 93*4cadd4f8SNickeau } 94*4cadd4f8SNickeau } 95*4cadd4f8SNickeau 96*4cadd4f8SNickeau $cacheManager = PluginUtility::getCacheManager(); 97*4cadd4f8SNickeau try { 98*4cadd4f8SNickeau $shouldSlotExpire = $cacheManager->shouldSlotExpire($pageId); 99*4cadd4f8SNickeau } catch (ExceptionCombo $e) { 100*4cadd4f8SNickeau LogUtility::msg("Error while trying to check if the slot ($pageId) should expired. Error: {$e->getMessage()}", self::CANONICAL); 101*4cadd4f8SNickeau return; 102*4cadd4f8SNickeau } 103*4cadd4f8SNickeau if ($shouldSlotExpire) { 104*4cadd4f8SNickeau Event::createEvent( 105*4cadd4f8SNickeau self::SLOT_CACHE_EXPIRATION_EVENT, 106*4cadd4f8SNickeau [ 107*4cadd4f8SNickeau PagePath::getPersistentName() => $pageId, 108*4cadd4f8SNickeau self::REQUESTED_ID => PluginUtility::getRequestedWikiId() 109*4cadd4f8SNickeau ] 110*4cadd4f8SNickeau ); 111*4cadd4f8SNickeau } 112*4cadd4f8SNickeau 113*4cadd4f8SNickeau 114*4cadd4f8SNickeau } 115*4cadd4f8SNickeau 116*4cadd4f8SNickeau public function handleSlotCacheExpiration($event) 117*4cadd4f8SNickeau { 118*4cadd4f8SNickeau $data = $event->data; 119*4cadd4f8SNickeau $slotPath = $data[PagePath::getPersistentName()]; 120*4cadd4f8SNickeau $requestedId = $data[self::REQUESTED_ID]; 121*4cadd4f8SNickeau 122*4cadd4f8SNickeau /** 123*4cadd4f8SNickeau * The cache file may be dependent on the requested id 124*4cadd4f8SNickeau * ie (@link CacheDependencies::OUTPUT_DEPENDENCIES} 125*4cadd4f8SNickeau */ 126*4cadd4f8SNickeau global $ID; 127*4cadd4f8SNickeau $keep = $ID; 128*4cadd4f8SNickeau try { 129*4cadd4f8SNickeau $ID = $requestedId; 130*4cadd4f8SNickeau $slot = Page::createPageFromQualifiedPath($slotPath); 131*4cadd4f8SNickeau 132*4cadd4f8SNickeau /** 133*4cadd4f8SNickeau * Calculate a new expiration date 134*4cadd4f8SNickeau * And set it here because setting a new metadata 135*4cadd4f8SNickeau * will make the cache unusable 136*4cadd4f8SNickeau */ 137*4cadd4f8SNickeau $cacheExpirationDateMeta = CacheExpirationDate::createForPage($slot); 138*4cadd4f8SNickeau $actualDate = $cacheExpirationDateMeta->getValue(); 139*4cadd4f8SNickeau $cacheExpirationFrequency = CacheExpirationFrequency::createForPage($slot) 140*4cadd4f8SNickeau ->getValue(); 141*4cadd4f8SNickeau try { 142*4cadd4f8SNickeau $newDate = Cron::getDate($cacheExpirationFrequency); 143*4cadd4f8SNickeau } catch (ExceptionCombo $e) { 144*4cadd4f8SNickeau LogUtility::msg("Error while calculating the new expiration date. Error: {$e->getMessage()}"); 145*4cadd4f8SNickeau return; 146*4cadd4f8SNickeau } 147*4cadd4f8SNickeau if ($newDate < $actualDate) { 148*4cadd4f8SNickeau LogUtility::msg("The new calculated date cache expiration frequency ({$newDate->format(Iso8601Date::getFormat())}) is lower than the current date ({$actualDate->format(Iso8601Date::getFormat())})"); 149*4cadd4f8SNickeau } 150*4cadd4f8SNickeau try { 151*4cadd4f8SNickeau $cacheExpirationDateMeta 152*4cadd4f8SNickeau ->setValue($newDate) 153*4cadd4f8SNickeau ->persist(); 154*4cadd4f8SNickeau } catch (ExceptionCombo $e) { 155*4cadd4f8SNickeau LogUtility::msg("Error while persisting the new expiration date. Error:{$e->getMessage()}"); 156*4cadd4f8SNickeau return; 157*4cadd4f8SNickeau } 158*4cadd4f8SNickeau 159*4cadd4f8SNickeau /** 160*4cadd4f8SNickeau * Cache deletion 161*4cadd4f8SNickeau */ 162*4cadd4f8SNickeau $message = "Expiration Date has expired"; 163*4cadd4f8SNickeau CacheLog::deleteCacheIfExistsAndLog( 164*4cadd4f8SNickeau $slot->getInstructionsDocument(), 165*4cadd4f8SNickeau self::SLOT_CACHE_EXPIRATION_EVENT, 166*4cadd4f8SNickeau $message); 167*4cadd4f8SNickeau CacheLog::deleteCacheIfExistsAndLog( 168*4cadd4f8SNickeau $slot->getHtmlDocument(), 169*4cadd4f8SNickeau self::SLOT_CACHE_EXPIRATION_EVENT, 170*4cadd4f8SNickeau $message); 171*4cadd4f8SNickeau 172*4cadd4f8SNickeau /** 173*4cadd4f8SNickeau * Re-render 174*4cadd4f8SNickeau */ 175*4cadd4f8SNickeau CacheLog::renderCacheAndLog( 176*4cadd4f8SNickeau $slot->getHtmlDocument(), 177*4cadd4f8SNickeau self::SLOT_CACHE_EXPIRATION_EVENT, 178*4cadd4f8SNickeau $message); 179*4cadd4f8SNickeau 180*4cadd4f8SNickeau } finally { 181*4cadd4f8SNickeau $ID = $keep; 182*4cadd4f8SNickeau } 183*4cadd4f8SNickeau 184*4cadd4f8SNickeau } 185*4cadd4f8SNickeau 186*4cadd4f8SNickeau 187*4cadd4f8SNickeau} 188