8 | */
9 |
10 | // must be run within Dokuwiki
11 | if(!defined('DOKU_INC')) die();
12 |
13 | class action_plugin_pageredirect extends DokuWiki_Action_Plugin {
14 | /**
15 | * Registers a callback function for a given event
16 | *
17 | * @param Doku_Event_Handler $controller DokuWiki's event controller object
18 | */
19 | public function register(Doku_Event_Handler $controller) {
20 | /* @see action_plugin_pageredirect::handle_dokuwiki_started() */
21 | $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'handle_dokuwiki_started');
22 | /* @see action_plugin_pageredirect::handle_parser_metadata_render() */
23 | $controller->register_hook('PARSER_METADATA_RENDER', 'BEFORE', $this, 'handle_parser_metadata_render');
24 |
25 | // This plugin goes first, PR#555, requires dokuwiki 2014-05-05 (Ponder Stibbons)
26 | /* @see action_plugin_pageredirect::handle_tpl_act_render() */
27 | $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'handle_tpl_act_render', null, PHP_INT_MIN);
28 |
29 | $controller->register_hook('INDEXER_PAGE_ADD', 'BEFORE', $this, 'handle_indexer');
30 |
31 | // Handle move plugin
32 | $controller->register_hook('PLUGIN_MOVE_HANDLERS_REGISTER', 'BEFORE', $this, 'handle_move_register');
33 | }
34 |
35 | public function handle_dokuwiki_started(&$event, $param) {
36 | global $ID, $ACT, $REV;
37 |
38 | // skip when looking page history or action is not 'show'
39 | if(($ACT != 'show' && $ACT != '') || $REV) {
40 | return;
41 | }
42 |
43 | $metadata = $this->get_metadata($ID);
44 |
45 | // return if no redirection data
46 | if(!$metadata) {
47 | return;
48 | }
49 | list($page, $is_external) = $metadata;
50 |
51 | // return if external redirect is not allowed
52 | if($is_external && !$this->getConf('allow_external')) {
53 | return;
54 | }
55 |
56 | global $INPUT;
57 | $redirect = $INPUT->get->str('redirect', '0');
58 |
59 | // return if redirection is temporarily disabled,
60 | // or we have been redirected 5 times in a row
61 | if($redirect == 'no' || $redirect > 4) {
62 | return;
63 | }
64 | $redirect = (int)$redirect+1;
65 |
66 | // verify metadata currency
67 | // FIXME: why
68 | if(@filemtime(metaFN($ID, '.meta')) < @filemtime(wikiFN($ID))) {
69 | throw new Exception('should not get here');
70 | return;
71 | }
72 |
73 | // preserve #section from $page
74 | list($page, $section) = array_pad(explode('#', $page, 2), 2, null);
75 | if(isset($section)) {
76 | $section = '#' . $section;
77 | } else {
78 | $section = '';
79 | }
80 |
81 | // prepare link for internal redirects, keep external targets
82 | if(!$is_external) {
83 | $page = wl($page, array('redirect' => $redirect), true, '&');
84 |
85 | if($this->getConf('show_note')) {
86 | $this->flash_message($ID);
87 | }
88 |
89 | // add anchor if not external redirect
90 | $page .= $section;
91 | }
92 |
93 | $this->redirect($page);
94 | }
95 |
96 | public function handle_tpl_act_render(&$event, $param) {
97 | global $ACT;
98 |
99 | // handle on do=show
100 | if($ACT != 'show' && $ACT != '') {
101 | return true;
102 | }
103 |
104 | if($this->getConf('show_note')) {
105 | $this->render_flash();
106 | }
107 |
108 | return true;
109 | }
110 |
111 | public function handle_parser_metadata_render(&$event, $param) {
112 | if(isset($event->data->meta['relation'])) {
113 | // FIXME: why is this needed here?!
114 | unset($event->data->meta['relation']['isreplacedby']);
115 | }
116 | }
117 |
118 | public function handle_indexer(Doku_Event $event, $param) {
119 | $new_references = array();
120 | foreach ($event->data['metadata']['relation_references'] as $target) {
121 | $redirect_target = $this->get_metadata($target);
122 |
123 | if ($redirect_target) {
124 | list($page, $is_external) = $redirect_target;
125 |
126 | if (!$is_external) {
127 | $new_references[] = $page;
128 | }
129 | }
130 | }
131 |
132 | if (count($new_references) > 0) {
133 | $event->data['metadata']['relation_references'] = array_unique(array_merge($new_references, $event->data['metadata']['relation_references']));
134 | }
135 |
136 | // FIXME: if the currently indexed page contains a redirect, all pages pointing to it need a new backlink entry!
137 | // Note that these entries need to be added for every source page separately.
138 | // An alternative could be to force re-indexing of all source pages by removing their ".indexed" file but this will only happen when they are visited.
139 | }
140 |
141 | /**
142 | * remember to show note about being redirected from another page
143 | * @param string $ID page id from where the redirect originated
144 | */
145 | private function flash_message($ID) {
146 | if(headers_sent()) {
147 | // too late to do start session
148 | // and following code requires session
149 | return;
150 | }
151 |
152 | session_start();
153 | $_SESSION[DOKU_COOKIE]['redirect'] = $ID;
154 | }
155 |
156 | /**
157 | * show note about being redirected from another page
158 | */
159 | private function render_flash() {
160 | global $INPUT;
161 |
162 | $redirect = $INPUT->get->str('redirect');
163 |
164 | // loop counter
165 | if($redirect <= 0 || $redirect > 5) {
166 | return;
167 | }
168 |
169 | $ID = isset($_SESSION[DOKU_COOKIE]['redirect']) ? $_SESSION[DOKU_COOKIE]['redirect'] : null;
170 | if(!$ID) {
171 | return;
172 | }
173 | unset($_SESSION[DOKU_COOKIE]['redirect']);
174 |
175 | $page = cleanID($ID);
176 | $use_heading = useHeading('navigation') && p_get_first_heading($page);
177 | $title = hsc($use_heading ? p_get_first_heading($page) : $page);
178 |
179 | $url = wl($page, array('redirect' => 'no'), true, '&');
180 | $link = '' . $title . '';
181 | echo '' . sprintf($this->getLang('redirected_from'), $link) . '
';
182 | }
183 |
184 | private function get_metadata($ID) {
185 | // make sure we always get current metadata, but simple cache logic (i.e. render when page is newer than metadata) is enough
186 | $metadata = p_get_metadata($ID, 'relation isreplacedby', METADATA_RENDER_USING_SIMPLE_CACHE|METADATA_RENDER_UNLIMITED);
187 |
188 | // legacy compat
189 | if(is_string($metadata)) {
190 | $metadata = array($metadata);
191 | }
192 |
193 | return $metadata;
194 | }
195 |
196 | /**
197 | * Redirect to url.
198 | * @param string $url
199 | */
200 | private function redirect($url) {
201 | header("HTTP/1.1 301 Moved Permanently");
202 | send_redirect($url);
203 | }
204 |
205 | public function handle_move_register(Doku_Event $event, $params) {
206 | $event->data['handlers']['pageredirect'] = array($this, 'rewrite_redirect');
207 | }
208 |
209 | public function rewrite_redirect($match, $state, $pos, $plugin, helper_plugin_move_handler $handler) {
210 | $metadata = $this->get_metadata($ID);
211 | if ($metadata[1]) return $match; // Fail-safe for external redirection (Do not rewrite)
212 |
213 | $match = trim($match);
214 |
215 | if (substr($match, 0, 1) == "~") {
216 | // "~~REDIRECT>pagename#anchor~~" pattern
217 |
218 | // Strip syntax
219 | $match = substr($match, 2, strlen($match) - 4);
220 |
221 | list($syntax, $src, $anchor) = array_pad(preg_split("/>|#/", $match), 3, "");
222 |
223 | // Resolve new source.
224 | if (method_exists($handler, 'adaptRelativeId')) {
225 | $new_src = $handler->adaptRelativeId($src);
226 | } else {
227 | $new_src = $handler->resolveMoves($src, 'page');
228 | $new_src = $handler->relativeLink($src, $new_src, 'page');
229 | }
230 |
231 | $result = "~~".$syntax.">".$new_src;
232 | if (!empty($anchor)) $result .= "#".$anchor;
233 | $result .= "~~";
234 |
235 | return $result;
236 |
237 | } else if (substr($match, 0, 1) == "#") {
238 | // "#REDIRECT pagename#anchor" pattern
239 |
240 | // Strip syntax
241 | $match = substr($match, 1);
242 |
243 | list($syntax, $src, $anchor) = array_pad(preg_split("/ |#/", $match), 3, "");
244 |
245 | // Resolve new source.
246 | if (method_exists($handler, 'adaptRelativeId')) {
247 | $new_src = $handler->adaptRelativeId($src);
248 | } else {
249 | $new_src = $handler->resolveMoves($src, 'page');
250 | $new_src = $handler->relativeLink($src, $new_src, 'page');
251 | }
252 |
253 | $result = "\n#".$syntax." ".$new_src;
254 | if (!empty($anchor)) $result .= "#".$anchor;
255 |
256 | return $result;
257 | }
258 |
259 | // Fail-safe
260 | return $match;
261 |
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/conf/default.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $meta['show_note'] = array('onoff');
10 | $meta['allow_external'] = array('onoff');
11 |
--------------------------------------------------------------------------------
/images/important_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glensc/dokuwiki-plugin-pageredirect/40fcfbd23b89067b10a40528f5868d4cbe25aa1f/images/important_small.png
--------------------------------------------------------------------------------
/lang/de-informal/lang.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['redirect_to'] = "Diese Seite wurde verschoben, die neue URL lautet %s.";
10 | $lang['redirected_from'] = "Du wurdest von %s hierher umgeleitet.";
11 |
--------------------------------------------------------------------------------
/lang/de-informal/settings.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['show_note'] = 'Auf der Zielseite einen Hinweis zur Umleitung anzeigen?';
10 | $lang['allow_external'] = 'Umleitungen auf externe Websites erlauben?';
11 |
--------------------------------------------------------------------------------
/lang/de/lang.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['redirect_to'] = "Diese Seite wurde verschoben, die neue URL lautet %s.";
10 | $lang['redirected_from'] = "Sie wurden von %s hierher umgeleitet.";
11 |
--------------------------------------------------------------------------------
/lang/de/settings.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['show_note'] = 'Auf der Zielseite einen Hinweis zur Umleitung anzeigen?';
10 | $lang['allow_external'] = 'Umleitungen auf externe Websites erlauben?';
11 |
--------------------------------------------------------------------------------
/lang/en/lang.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['redirect_to'] = "This page has been moved, the new location is %s.";
10 | $lang['redirected_from'] = "You were redirected here from %s.";
11 |
--------------------------------------------------------------------------------
/lang/en/settings.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['show_note'] = 'Display note on page you are redirected to?';
10 | $lang['allow_external'] = 'Allow redirects to external websites?';
11 |
--------------------------------------------------------------------------------
/lang/eo/lang.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | $lang['redirect_to'] = "Ĉi tiu paĝo estas movita, nova loko estas %s.";
11 | $lang['redirected_from'] = "Vi estas alidirektita ĉi tien el %s.";
--------------------------------------------------------------------------------
/lang/eo/settings.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | $lang['show_note'] = 'Montri rimarkon sur paĝo ke vi estas alidirektita?';
--------------------------------------------------------------------------------
/lang/fr/lang.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['redirect_to'] = "Cette page a été déplacée, le nouvel emplacement est %s.";
10 | $lang['redirected_from'] = "Vous avez été redirigé depuis %s.";
11 |
--------------------------------------------------------------------------------
/lang/fr/settings.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['show_note'] = 'Afficher une note sur la page vers laquelle vous êtes redirigé ?';
10 |
--------------------------------------------------------------------------------
/lang/ja/lang.php:
--------------------------------------------------------------------------------
1 |
7 | * @author : Myeongjin
8 | */
9 |
10 | $lang['redirect_to'] = "이 문서는 옮겨졌으며, 새 위치는 %s입니다.";
11 | $lang['redirected_from'] = "%s에서 넘어왔습니다.";
12 |
--------------------------------------------------------------------------------
/lang/ko/settings.php:
--------------------------------------------------------------------------------
1 |
7 | * @author : Myeongjin
8 | */
9 |
10 | $lang['show_note'] = '넘어오는 문서에 메모를 보여줄까요?';
11 |
--------------------------------------------------------------------------------
/lang/pt-br/lang.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['redirect_to'] = "Esta página é um redirecionador para %s.";
10 | $lang['redirected_from'] = "Você foi redirecionado de %s.";
11 |
--------------------------------------------------------------------------------
/lang/ru/lang.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['redirect_to'] = "Страница была перемещена по адресу %s.";
10 | $lang['redirected_from'] = "Вы были перенаправлены сюда со страницы %s.";
11 |
--------------------------------------------------------------------------------
/lang/ru/settings.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['show_note'] = 'Показывать примечание на странице, на которую было сделано перенаправление?';
10 |
--------------------------------------------------------------------------------
/lang/zh-tw/lang.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['redirect_to'] = "本頁已重定向至 %s。";
10 | $lang['redirected_from'] = "本頁重定向自 %s。";
11 |
--------------------------------------------------------------------------------
/lang/zh-tw/settings.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | $lang['show_note'] = '在重定向目標頁顯示提示?';
10 |
--------------------------------------------------------------------------------
/plugin.info.txt:
--------------------------------------------------------------------------------
1 | base pageredirect
2 | author Elan Ruusamäe, David Lorentsen
3 | email glen@delfi.ee
4 | date 2024-03-01
5 | name Page Redirect
6 | desc Redirects page requests based on content
7 | url https://www.dokuwiki.org/plugin:pageredirect
8 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | .noteredirect {
2 | margin: 1em;
3 | margin-left: auto;
4 | margin-right: auto;
5 | width: 70% !important;
6 | min-height: 18px;
7 | clear: both;
8 | text-align: justify;
9 | vertical-align: middle;
10 | border-collapse: collapse;
11 | padding: 7px 10px 5px 32px;
12 | background-position: 10px 50%;
13 | background-repeat: no-repeat;
14 | -moz-border-radius: 10px;
15 | -khtml-border-radius: 10px;
16 | border-radius: 10px;
17 |
18 | /*border: 1px solid #B5E0FF;*/
19 | background-color: #B5E0FF;
20 | background-image: url(images/important_small.png);
21 | }
22 |
--------------------------------------------------------------------------------
/syntax.php:
--------------------------------------------------------------------------------
1 |
7 | * @author David Lorentsen
8 | */
9 |
10 | use dokuwiki\File\PageResolver;
11 |
12 | // must be run within Dokuwiki
13 | if(!defined('DOKU_INC')) die();
14 |
15 | class syntax_plugin_pageredirect extends DokuWiki_Syntax_Plugin {
16 | /**
17 | * @return string Syntax mode type
18 | */
19 | public function getType() {
20 | return 'substition';
21 | }
22 |
23 | /**
24 | * @return string Paragraph type
25 | */
26 | public function getPType() {
27 | return 'block';
28 | }
29 |
30 | /**
31 | * @return int Sort order - Low numbers go before high numbers
32 | */
33 | public function getSort() {
34 | return 1;
35 | }
36 |
37 | /**
38 | * Connect lookup pattern to lexer.
39 | *
40 | * @param string $mode Parser mode
41 | */
42 | public function connectTo($mode) {
43 | // NOTE: each document is surrounted with \n by dokuwiki parser
44 | // so it's safe to use \n in the pattern
45 | // this fixes creole greedyness:
46 | // https://github.com/glensc/dokuwiki-plugin-pageredirect/issues/18#issuecomment-249386268
47 | $this->Lexer->addSpecialPattern('(?:~~REDIRECT>.+?~~|\n#(?i:redirect) [^\r\n]+)', $mode, 'plugin_pageredirect');
48 | }
49 |
50 | /**
51 | * Handler to prepare matched data for the rendering process
52 | *
53 | * This function can only pass data to render() via its return value
54 | * render() may be not be run during the object's current life.
55 | *
56 | * Usually you should only need the $match param.
57 | *
58 | * @param string $match The text matched by the patterns
59 | * @param int $state The lexer state for the match
60 | * @param int $pos The character position of the matched text
61 | * @param Doku_Handler $handler Reference to the Doku_Handler object
62 | * @return array Return an array with all data you want to use in render
63 | */
64 | public function handle($match, $state, $pos, Doku_Handler $handler) {
65 | // strip leading "\n" from creole-compatible pattern
66 | $match = trim($match);
67 |
68 | // extract target page from match pattern
69 | if($match[0] == '#') {
70 | # #REDIRECT PAGE
71 | $id = substr($match, 10);
72 | } else {
73 | # ~~REDIRECT>PAGE~~
74 | $id = substr($match, 11, -2);
75 | }
76 | $id = trim($id);
77 |
78 | $is_external = preg_match('#^https?://#i', $id);
79 |
80 | // resolve and clean the $id if it is not external
81 | if(!$is_external) {
82 | global $ID;
83 | if (class_exists('dokuwiki\File\PageResolver')) {
84 | $resolver = new PageResolver($ID);
85 | $id = $resolver->resolveId($id);
86 | } else {
87 | $exists = null;
88 | resolve_pageid(getNS($ID), $id, $exists);
89 | }
90 | }
91 |
92 | return array($id, $is_external);
93 | }
94 |
95 | /**
96 | * Handles the actual output creation.
97 | *
98 | * The function must not assume any other of the classes methods have been run
99 | * during the object's current life. The only reliable data it receives are its
100 | * parameters.
101 | *
102 | * The function should always check for the given output format and return false
103 | * when a format isn't supported.
104 | *
105 | * $renderer contains a reference to the renderer object which is
106 | * currently handling the rendering. You need to use it for writing
107 | * the output. How this is done depends on the renderer used (specified
108 | * by $format
109 | *
110 | * The contents of the $data array depends on what the handler() function above
111 | * created
112 | *
113 | * @param string $format output format being rendered
114 | * @param Doku_Renderer $renderer reference to the current renderer object
115 | * @param array $data data created by handler()
116 | * @return boolean return true if rendered correctly
117 | */
118 | public function render($format, Doku_Renderer $renderer, $data) {
119 | if($format == 'xhtml') {
120 | // add prepared note about redirection
121 | $renderer->doc .= $this->redirect_message($data);
122 |
123 | // hook into the post render event to allow the page to be redirected
124 | global $EVENT_HANDLER;
125 | /** @var action_plugin_pageredirect $action */
126 | $action = plugin_load('action', 'pageredirect');
127 | $EVENT_HANDLER->register_hook('TPL_ACT_RENDER', 'AFTER', $action, 'handle_dokuwiki_started');
128 |
129 | return true;
130 | }
131 |
132 | if($format == 'metadata') {
133 | // add redirection to metadata
134 | $renderer->meta['relation']['isreplacedby'] = $data;
135 | if (!$data[1]) {
136 | $page = preg_split("/\?|#/", $data[0])[0];
137 | $renderer->meta['relation']['references'][$page] = page_exists($page);
138 | }
139 |
140 | return true;
141 | }
142 |
143 | return false;
144 | }
145 |
146 | /**
147 | * Create redirection message html
148 | *
149 | * @param string $page
150 | * @return string
151 | */
152 | private function redirect_message($metadata) {
153 | list($page, $is_external) = $metadata;
154 | if(!$is_external) {
155 | $link = html_wikilink($page);
156 | } else {
157 | $link = '' . hsc($page) . '';
158 | }
159 |
160 | // prepare message here instead of in render
161 | $message = '' . sprintf($this->getLang('redirect_to'), $link) . '
';
162 |
163 | return $message;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------