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