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