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