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