1<?php
2
3require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
4
5use ComboStrap\Alias;
6use ComboStrap\Aliases;
7use ComboStrap\DatabasePageRow;
8use ComboStrap\ExceptionComboRuntime;
9use ComboStrap\File;
10use ComboStrap\FileSystems;
11use ComboStrap\MarkupRef;
12use ComboStrap\LogUtility;
13use ComboStrap\MetadataDbStore;
14use ComboStrap\MetadataDokuWikiStore;
15use ComboStrap\Page;
16use ComboStrap\PageId;
17use ComboStrap\PluginUtility;
18use ComboStrap\Site;
19
20
21/**
22 * Class action_plugin_combo_move
23 * Handle the move of a page in order to update:
24 *   * the link
25 *   * the data in the database
26 */
27class action_plugin_combo_linkmove extends DokuWiki_Action_Plugin
28{
29
30
31    const CANONICAL = "move";
32
33    private static function checkAndSendAMessageIfLockFilePresent(): bool
34    {
35        $lockFile = Site::getDataDirectory()->resolve("locks_plugin_move.lock");
36        if (!FileSystems::exists($lockFile)) {
37            return false;
38        }
39        $lockFileDateTimeModified = FileSystems::getModifiedTime($lockFile);
40        $lockFileModifiedTimestamp = $lockFileDateTimeModified->getTimestamp();
41        $now = time();
42
43        $distance = $now - $lockFileModifiedTimestamp;
44        $lockFileAgeInMinute = ($distance) / 60;
45        if ($lockFileAgeInMinute > 5) {
46            LogUtility::msg("The move lockfile ($lockFile) exists and is older than 5 minutes (exactly $lockFileAgeInMinute minutes). If you are no more in a move, you should delete this file otherwise it will disable the move of page and the cache.");
47            return true;
48        }
49        return false;
50    }
51
52    /**
53     * As explained https://www.dokuwiki.org/plugin:move#support_for_other_plugins
54     * @param Doku_Event_Handler $controller
55     */
56    function register(Doku_Event_Handler $controller)
57    {
58
59        /**
60         * To rewrite the page meta in the database
61         */
62        $controller->register_hook('PLUGIN_MOVE_PAGE_RENAME', 'BEFORE', $this, 'handle_rename_before', array());
63        $controller->register_hook('PLUGIN_MOVE_PAGE_RENAME', 'AFTER', $this, 'handle_rename_after', array());
64
65        /**
66         * To rewrite the link
67         */
68        $controller->register_hook('PLUGIN_MOVE_HANDLERS_REGISTER', 'BEFORE', $this, 'handle_link', array());
69
70
71        /**
72         * Check for the presence of a lock file
73         */
74        $controller->register_hook('PARSER_WIKITEXT_PREPROCESS', 'BEFORE', $this, 'check_lock_file_age', array());
75
76
77    }
78
79    /**
80     * @param Doku_Event $event
81     * @param $params
82     *
83     * When a lock file is present,
84     * the move plugin will purge the data in {@link action_plugin_move_rewrite::handle_cache()}
85     * making the rendering fucking slow
86     * We check that the lock file is not
87     */
88    function check_lock_file_age(Doku_Event $event, $params)
89    {
90        self::checkAndSendAMessageIfLockFilePresent();
91
92    }
93
94    /**
95     * Handle the path modification of a page
96     * @param Doku_Event $event - https://www.dokuwiki.org/plugin:move#for_plugin_authors
97     * @param $params
98     *
99     */
100    function handle_rename_before(Doku_Event $event, $params)
101    {
102        /**
103         * Check that the lock file is not older
104         * Lock file bigger than 5 minutes
105         * Is not really possible
106         */
107        $result = self::checkAndSendAMessageIfLockFilePresent();
108        if ($result === true) {
109            $event->preventDefault();
110            LogUtility::msg("The move lock file is present, the move was canceled.");
111        }
112
113    }
114
115    /**
116     * Handle the path modification of a page after
117     *
118     * The metadata file should also have been moved
119     *
120     * @param Doku_Event $event - https://www.dokuwiki.org/plugin:move#for_plugin_authors
121     * @param $params
122     *
123     */
124    function handle_rename_after(Doku_Event $event, $params)
125    {
126        /**
127         *
128         * $event->data
129         * src_id ⇒ string – the original ID of the page
130         * dst_id ⇒ string – the new ID of the page
131         */
132        $sourceId = $event->data["src_id"];
133        $targetId = $event->data["dst_id"];
134        try {
135
136            /**
137             * Update the dokuwiki id and path
138             */
139            $databasePage = DatabasePageRow::createFromDokuWikiId($sourceId);
140            if (!$databasePage->exists()) {
141                return;
142            }
143            $databasePage->updatePathAndDokuwikiId($targetId);
144
145            /**
146             * Check page id
147             */
148            $targetPage = Page::createPageFromId($targetId);
149            $targetPageId = PageId::createForPage($targetPage);
150            $targetPageIdValue = $targetPageId->getValueFromStore();
151            $databasePageIdValue = $databasePage->getPageId();
152
153            if ($databasePageIdValue !== $targetPageIdValue) {
154                // this should never happened in test/dev
155                $targetPageId->setValueForce($targetPageIdValue);
156            }
157
158            /**
159             * Add the alias
160             */
161            Aliases::createForPage($targetPage)
162                ->addAlias($sourceId)
163                ->setWriteStore(MetadataDokuWikiStore::class)
164                ->sendToWriteStore()
165                ->persist()
166                ->setReadStore(MetadataDbStore::class)
167                ->sendToWriteStore()
168                ->persist();
169
170
171        } catch (Exception $exception) {
172            // We catch the errors if any to not stop the move
173            // There is no transaction feature (it happens or not)
174            $message = "An error occurred during the move replication to the database. Error message was: " . $exception->getMessage();
175            if (PluginUtility::isDevOrTest()) {
176                throw new RuntimeException($exception);
177            } else {
178                LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, self::CANONICAL);
179            }
180        }
181
182    }
183
184
185    /**
186     * Handle the move of a page
187     * @param Doku_Event $event
188     * @param $params
189     */
190    function handle_link(Doku_Event $event, $params)
191    {
192        /**
193         * The handlers is the name of the component (ie refers to the {@link syntax_plugin_combo_link} handler)
194         * and 'rewrite_combo' to the below method
195         */
196        $event->data['handlers'][syntax_plugin_combo_link::COMPONENT] = array($this, 'rewrite_combo');
197    }
198
199    /**
200     *
201     * @param $match
202     * @param $state
203     * @param $pos
204     * @param $plugin
205     * @param helper_plugin_move_handler $handler
206     */
207    public function rewrite_combo($match, $state, $pos, $plugin, helper_plugin_move_handler $handler)
208    {
209        /**
210         * The original move method
211         * is {@link helper_plugin_move_handler::internallink()}
212         *
213         */
214        if ($state == DOKU_LEXER_ENTER) {
215            $ref = syntax_plugin_combo_link::parse($match)[syntax_plugin_combo_link::ATTRIBUTE_HREF];
216            $link = new MarkupRef($ref);
217            if ($link->getUriType() == MarkupRef::WIKI_URI) {
218
219                $handler->internallink($match, $state, $pos);
220                $suffix = "]]";
221                if (substr($handler->calls, -strlen($suffix)) == $suffix) {
222                    $handler->calls = substr($handler->calls, 0, strlen($handler->calls) - strlen($suffix));
223                }
224
225            } else {
226
227                // Other type of links
228                $handler->calls .= $match;
229
230            }
231        } else {
232
233            // Description and ending
234            $handler->calls .= $match;
235
236        }
237    }
238
239
240}
241