1<?php
2
3require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
4
5use ComboStrap\ExceptionBadSyntax;
6use ComboStrap\Identity;
7use ComboStrap\Index;
8use ComboStrap\LogUtility;
9use ComboStrap\LinkMarkup;
10use ComboStrap\Message;
11use ComboStrap\MarkupPath;
12use dokuwiki\Extension\ActionPlugin;
13
14
15/**
16 *
17 * To show a message after redirection or rewriting
18 *
19 *
20 *
21 */
22class action_plugin_combo_routermessage extends ActionPlugin
23{
24
25    // a class can not start with a number then webcomponent is not a valid class name
26    const REDIRECT_MANAGER_BOX_CLASS = "redirect-manager";
27
28    // Property key
29    const ORIGIN_PAGE = 'redirectId';
30    const ORIGIN_TYPE = 'redirectOrigin';
31    const CONF_SHOW_PAGE_NAME_IS_NOT_UNIQUE = 'ShowPageNameIsNotUnique';
32    const CONF_SHOW_MESSAGE_CLASSIC = 'ShowMessageClassic';
33    const CONF_SHOW_MESSAGE_CLASSIC_DEFAULT = 1;
34
35    function __construct()
36    {
37        // enable direct access to language strings
38        // ie $this->lang
39        $this->setupLocale();
40    }
41
42    /**
43     *
44     * Return the message properties from a query string
45     *
46     * An internal HTTP redirect pass them via query string
47     */
48    private static function getMessageQueryStringProperties(): array
49    {
50
51        $returnValues = array();
52
53        global $INPUT;
54        $origin = $INPUT->str(self::ORIGIN_PAGE, null);
55        if ($origin != null) {
56            $returnValues = array(
57                $origin,
58                $INPUT->str(self::ORIGIN_TYPE, null)
59            );
60        }
61        return $returnValues;
62
63    }
64
65
66    function register(Doku_Event_Handler $controller)
67    {
68
69        /* This will call the function _displayRedirectMessage */
70        $controller->register_hook(
71            'TPL_ACT_RENDER',
72            'BEFORE',
73            $this,
74            '_displayRedirectMessage',
75            array()
76        );
77
78
79    }
80
81
82    /**
83     * Main function; dispatches the visual comment actions
84     * @param   $event Doku_Event
85     */
86    function _displayRedirectMessage(&$event, $param)
87    {
88
89        $pageIdOrigin = null;
90        $redirectSource = null;
91
92        $messageQueryStringProperties = self::getMessageQueryStringProperties();
93        if (!empty($messageQueryStringProperties)) {
94            list($pageIdOrigin, $redirectSource) = $messageQueryStringProperties;
95        }
96
97        if ($pageIdOrigin) {
98
99            switch ($redirectSource) {
100
101                case action_plugin_combo_router::TARGET_ORIGIN_PAGE_RULES:
102                    if (!$this->showMessageIfPublicAndPlanned()) {
103                        return;
104                    }
105                    $message = Message::createInfoMessage()
106                        ->addHtmlContent("<p>" . sprintf($this->getLang('message_redirected_by_redirect'), hsc($pageIdOrigin)) . "</p>");
107                    break;
108
109                case action_plugin_combo_router::TARGET_ORIGIN_START_PAGE:
110                    $message = Message::createWarningMessage()
111                        ->addHtmlContent("<p>" . sprintf($this->lang['message_redirected_to_startpage'], hsc($pageIdOrigin)) . "</p>");
112                    break;
113                case  action_plugin_combo_router::TARGET_ORIGIN_BEST_PAGE_NAME:
114                    $message = Message::createWarningMessage()
115                        ->addHtmlContent("<p>" . sprintf($this->lang['message_redirected_to_bestpagename'], hsc($pageIdOrigin)) . "</p>");
116                    break;
117                case  action_plugin_combo_router::TARGET_ORIGIN_BEST_END_PAGE_NAME:
118                    $message = Message::createWarningMessage()
119                        ->addHtmlContent("<p>" . sprintf($this->lang['message_redirected_to_bestendpagename'], hsc($pageIdOrigin)) . "</p>");
120                    break;
121                case action_plugin_combo_router::TARGET_ORIGIN_BEST_NAMESPACE:
122                    $message = Message::createWarningMessage()
123                        ->addHtmlContent("<p>" . sprintf($this->lang['message_redirected_to_bestnamespace'], hsc($pageIdOrigin)) . "</p>");
124                    break;
125
126                case action_plugin_combo_router::TARGET_ORIGIN_SEARCH_ENGINE:
127                    $message = Message::createWarningMessage()
128                        ->addHtmlContent("<p>" . sprintf($this->lang['message_redirected_to_searchengine'], hsc($pageIdOrigin)) . "</p>");
129                    break;
130
131                case action_plugin_combo_router::GO_TO_EDIT_MODE:
132                    $message = Message::createInfoMessage()
133                        ->addHtmlContent("<p>" . $this->lang['message_redirected_to_edit_mode'] . "</p>");
134                    break;
135                case action_plugin_combo_router::TARGET_ORIGIN_PERMALINK_EXTENDED:
136                case action_plugin_combo_router::TARGET_ORIGIN_PERMALINK:
137                    $message = Message::createInfoMessage()
138                        ->addHtmlContent("<p>" . $this->lang['message_redirected_from_permalink'] . "</p>");
139                    break;
140                case action_plugin_combo_router::TARGET_ORIGIN_CANONICAL:
141                    if (!$this->showMessageIfPublicAndPlanned()) {
142                        return;
143                    }
144                    $message = Message::createInfoMessage()
145                        ->addHtmlContent("<p>" . $this->lang['message_redirected_from_canonical'] . "</p>");
146                    break;
147                default:
148                    LogUtility::msg("The redirection source ($redirectSource) is unknown. It was not expected", LogUtility::LVL_MSG_ERROR, action_plugin_combo_router::CANONICAL);
149                    return;
150
151            }
152
153
154            // Add a list of page with the same name to the message
155            // if the redirections is not planned
156            if ($redirectSource != action_plugin_combo_router::TARGET_ORIGIN_PAGE_RULES) {
157                $pageOrigin = MarkupPath::createMarkupFromId($pageIdOrigin);
158                $this->addToMessagePagesWithSameName($message, $pageOrigin);
159            }
160
161            if ($event->data === 'show' || $event->data === 'edit' || $event->data === 'search') {
162                $html = $message
163                    ->setPlugin($this)
164                    ->setClass(action_plugin_combo_routermessage::REDIRECT_MANAGER_BOX_CLASS)
165                    ->setCanonical(action_plugin_combo_router::CANONICAL)
166                    ->setSignatureName(action_plugin_combo_router::URL_MANAGER_NAME)
167                    ->toHtmlBox();
168                LogUtility::infoToPublic($html, action_plugin_combo_router::CANONICAL);
169            }
170
171
172        }
173
174
175    }
176
177
178    /**
179     * Add the page with the same page name but in an other location
180     * @param Message $message
181     * @param MarkupPath $pageOrigin
182     */
183    function addToMessagePagesWithSameName(Message $message, MarkupPath $pageOrigin)
184    {
185
186        if (!$this->getConf(self::CONF_SHOW_PAGE_NAME_IS_NOT_UNIQUE) == 1) {
187            return;
188        }
189
190        global $ID;
191        // The page name
192        $pageName = $pageOrigin->getNameOrDefault();
193        $pagesWithSameName = Index::getOrCreate()->getPagesWithSameLastName($pageOrigin);
194
195        if (count($pagesWithSameName) === 1) {
196            $page = $pagesWithSameName[0];
197            if ($page->getWikiId() === $ID) {
198                // the page itself
199                return;
200            }
201        }
202
203        if (count($pagesWithSameName) > 0) {
204
205            $message->setType(Message::TYPE_WARNING);
206
207            // Assign the value to a variable to be able to use the construct .=
208            if ($message->getPlainTextContent() <> '') {
209                $message->addHtmlContent('<br/><br/>');
210            }
211            $message->addHtmlContent($this->lang['message_pagename_exist_one']);
212            $message->addHtmlContent('<ul>');
213
214            $i = 0;
215            $listPagesHtml = "";
216            foreach ($pagesWithSameName as $page) {
217
218                if ($page->getWikiId() === $ID) {
219                    continue;
220                }
221                $i++;
222                if ($i > 10) {
223                    $listPagesHtml .= '<li>' .
224                        tpl_link(
225                            "?do=search&q=" . rawurldecode($pageName),
226                            "More ...",
227                            'class="" rel="nofollow" title="More..."',
228                            $return = true
229                        ) . '</li>';
230                    break;
231                }
232
233                try {
234                    $markupRef = LinkMarkup::createFromPageIdOrPath($page->getWikiId());
235                    $tagAttributes = $markupRef
236                        ->toAttributes()
237                        ->addOutputAttributeValue("rel", "nofollow");
238                    $listPagesHtml .= "<li>{$tagAttributes->toHtmlEnterTag("a")}{$markupRef->getDefaultLabel()}</a></li>";
239                } catch (ExceptionBadSyntax $e) {
240                    LogUtility::internalError("Internal Error: Unable to get a markup ref for the page ($page). Error: {$e->getMessage()}");
241                }
242
243            }
244            $message->addHtmlContent($listPagesHtml);
245            $message->addHtmlContent('</ul>');
246
247        }
248    }
249
250
251    /**
252     * Set the redirect in a session that will be be read after the redirect
253     * in order to show a message to the user
254     * @param string $id
255     * @param string $redirectSource
256     */
257    static function notify($id, $redirectSource)
258    {
259        // Msg via session
260        if (!defined('NOSESSION')) {
261            //reopen session, store data and close session again
262            self::sessionStart();
263            $_SESSION[DOKU_COOKIE][self::ORIGIN_PAGE] = $id;
264            $_SESSION[DOKU_COOKIE][self::ORIGIN_TYPE] = $redirectSource;
265            self::sessionClose();
266
267        }
268    }
269
270
271    private static function sessionStart()
272    {
273        $sessionStatus = session_status();
274        switch ($sessionStatus) {
275            case PHP_SESSION_DISABLED:
276                throw new RuntimeException("Sessions are disabled");
277
278            case PHP_SESSION_NONE:
279                $result = @session_start();
280                if (!$result) {
281                    throw new RuntimeException("The session was not successfully started");
282                }
283                break;
284            case PHP_SESSION_ACTIVE:
285                break;
286        }
287    }
288
289    private static function sessionClose()
290    {
291        // Close the session
292        $phpVersion = phpversion();
293        if ($phpVersion > "7.2.0") {
294            $result = session_write_close();
295            if (!$result) {
296                // Session is really not a well known mechanism
297                // Set this error in a info level to not fail the test
298                LogUtility::msg("Failure to write the session", LogUtility::LVL_MSG_INFO);
299            }
300        } else {
301            session_write_close();
302        }
303
304    }
305
306    /**
307     * We don't saw the message if it was planned and
308     * it's a reader
309     * @return bool
310     */
311    private function showMessageIfPublicAndPlanned(): bool
312    {
313        if (Identity::isWriter()) {
314            return true;
315        }
316        return $this->getConf(self::CONF_SHOW_MESSAGE_CLASSIC, self::CONF_SHOW_MESSAGE_CLASSIC_DEFAULT) == 1;
317    }
318
319}
320