1<?php
2/**
3 * Translation Plugin: Simple multilanguage plugin
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <andi@splitbrain.org>
7 * @author     Guy Brand <gb@isis.u-strasbg.fr>
8 */
9
10// must be run within Dokuwiki
11if(!defined('DOKU_INC')) die();
12
13/**
14 * Class action_plugin_translation
15 */
16class action_plugin_translation extends DokuWiki_Action_Plugin {
17
18    /**
19     * For the helper plugin
20     * @var helper_plugin_translation
21     */
22    var $helper = null;
23
24    var $locale;
25
26    /**
27     * Constructor. Load helper plugin
28     */
29    function __construct() {
30        $this->helper = plugin_load('helper', 'translation');
31    }
32
33    /**
34     * Registers a callback function for a given event
35     *
36     * @param Doku_Event_Handler $controller
37     */
38    function register(Doku_Event_Handler $controller) {
39        $scriptName = basename($_SERVER['PHP_SELF']);
40
41        // should the lang be applied to UI?
42        if($this->getConf('translateui')) {
43            switch($scriptName) {
44                case 'js.php':
45                    $controller->register_hook('INIT_LANG_LOAD', 'BEFORE', $this, 'translation_js');
46                    $controller->register_hook('JS_CACHE_USE', 'BEFORE', $this, 'translation_jscache');
47                    break;
48
49                case 'ajax.php':
50                    $controller->register_hook('INIT_LANG_LOAD', 'BEFORE', $this, 'translate_media_manager');
51                    break;
52
53                case 'mediamanager.php':
54                    $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'setJsCacheKey');
55                    break;
56
57                default:
58                    $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'setJsCacheKey');
59            }
60        }
61
62        if($scriptName !== 'js.php' && $scriptName !== 'ajax.php') {
63            $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'translation_hook');
64            $controller->register_hook('DETAIL_STARTED', 'BEFORE', $this, 'translation_hook');
65            $controller->register_hook('MEDIAMANAGER_STARTED', 'BEFORE', $this, 'translation_hook');
66        }
67
68        $controller->register_hook('SEARCH_QUERY_PAGELOOKUP', 'AFTER', $this, 'translation_search');
69        $controller->register_hook('COMMON_PAGETPL_LOAD', 'AFTER', $this, 'page_template_replacement');
70    }
71
72    /**
73     * Hook Callback. Make current language available as page template placeholder and handle
74     * original language copying
75     *
76     * @param Doku_Event $event
77     * @param $args
78     */
79    function page_template_replacement(Doku_Event $event, $args) {
80        global $ID;
81
82        // load orginal content as template?
83        if($this->getConf('copytrans') && $this->helper->istranslatable($ID, false)) {
84            // look for existing translations
85            $translations = $this->helper->getAvailableTranslations($ID);
86            if($translations) {
87                // find original language (might've been provided via parameter or use first translation)
88                $orig = (string) $_REQUEST['fromlang'];
89                if(!$orig) $orig = array_shift(array_keys($translations));
90
91                // load file
92                $origfile = $translations[$orig];
93                $event->data['tpl'] = io_readFile(wikiFN($origfile));
94
95                // prefix with warning
96                $warn = io_readFile($this->localFN('totranslate'));
97                if($warn) $warn .= "\n\n";
98                $event->data['tpl'] = $warn . $event->data['tpl'];
99
100                // show user a choice of translations if any
101                if(count($translations) > 1) {
102                    $links = array();
103                    foreach($translations as $t => $l) {
104                        $links[] = '<a href="' . wl($ID, array('do' => 'edit', 'fromlang' => $t)) . '">' . $this->helper->getLocalName($t) . '</a>';
105                    }
106
107                    msg(
108                        sprintf(
109                            $this->getLang('transloaded'),
110                            $this->helper->getLocalName($orig),
111                            join(', ', $links)
112                        )
113                    );
114                }
115
116            }
117        }
118
119        // apply placeholders
120        $event->data['tpl'] = str_replace('@LANG@', $this->helper->realLC(''), $event->data['tpl']);
121        $event->data['tpl'] = str_replace('@TRANS@', $this->helper->getLangPart($ID), $event->data['tpl']);
122    }
123
124    /**
125     * Hook Callback. Load correct translation when loading JavaScript
126     *
127     * @param Doku_Event $event
128     * @param $args
129     */
130    function translation_js(Doku_Event $event, $args) {
131        global $conf;
132        if(!isset($_GET['lang'])) return;
133        if(!in_array($_GET['lang'], $this->helper->translations)) return;
134        $lang = $_GET['lang'];
135        $event->data = $lang;
136        $conf['lang'] = $lang;
137    }
138
139    /**
140     * Hook Callback. Pass language code to JavaScript dispatcher
141     *
142     * @param Doku_Event $event
143     * @param $args
144     * @return bool
145     */
146    function setJsCacheKey(Doku_Event $event, $args) {
147        if(!isset($this->locale)) return false;
148        $count = count($event->data['script']);
149        for($i = 0; $i < $count; $i++) {
150            if(strpos($event->data['script'][$i]['src'], '/lib/exe/js.php') !== false) {
151                $event->data['script'][$i]['src'] .= '&lang=' . hsc($this->locale);
152            }
153        }
154
155        return false;
156    }
157
158    /**
159     * Hook Callback. Make sure the JavaScript is translation dependent
160     *
161     * @param Doku_Event $event
162     * @param $args
163     */
164    function translation_jscache(Doku_Event $event, $args) {
165        if(!isset($_GET['lang'])) return;
166        if(!in_array($_GET['lang'], $this->helper->translations)) return;
167
168        $lang = $_GET['lang'];
169        // reuse the constructor to reinitialize the cache key
170        if(method_exists($event->data, '__construct')) {
171            // New PHP 5 style constructor
172            $event->data->__construct(
173                $event->data->key . $lang,
174                $event->data->ext
175            );
176        } else {
177            // Old PHP 4 style constructor - deprecated
178            $event->data->cache(
179                $event->data->key . $lang,
180                $event->data->ext
181            );
182        }
183    }
184
185    /**
186     * Hook Callback. Translate the AJAX loaded media manager
187     *
188     * @param Doku_Event $event
189     * @param $args
190     */
191    function translate_media_manager(Doku_Event $event, $args) {
192        global $conf;
193        if(isset($_REQUEST['ID'])) {
194            $id = getID();
195            $lc = $this->helper->getLangPart($id);
196        } elseif(isset($_SESSION[DOKU_COOKIE]['translationlc'])) {
197            $lc = $_SESSION[DOKU_COOKIE]['translationlc'];
198        } else {
199            return;
200        }
201        if(!$lc) return;
202
203        $conf['lang'] = $lc;
204        $event->data = $lc;
205    }
206
207    /**
208     * Hook Callback. Change the UI language in foreign language namespaces
209     *
210     * @param Doku_Event $event
211     * @param $args
212     * @return bool
213     */
214    function translation_hook(Doku_Event $event, $args) {
215        global $ID;
216        /** @noinspection PhpUnusedLocalVariableInspection we include the language file later on */
217        global $lang;
218        global $conf;
219        global $ACT;
220        // redirect away from start page?
221        if($this->getConf('redirectstart') && $ID == $conf['start'] && $ACT == 'show') {
222            $lc = $this->helper->getBrowserLang();
223
224            list($translatedStartpage,) = $this->helper->buildTransID($lc, $conf['start']);
225            if (cleanID($translatedStartpage) !== cleanID($ID)) {
226                send_redirect(wl(cleanID($translatedStartpage), '', true));
227            }
228        }
229
230        // check if we are in a foreign language namespace
231        $lc = $this->helper->getLangPart($ID);
232
233        // store language in session (for page related views only)
234        if(in_array($ACT, array('show', 'recent', 'diff', 'edit', 'preview', 'source', 'subscribe'))) {
235            $_SESSION[DOKU_COOKIE]['translationlc'] = $lc;
236        }
237        if(!$lc) $lc = $_SESSION[DOKU_COOKIE]['translationlc'];
238        if(!$lc) return false;
239        $this->locale = $lc;
240
241        if(!$this->getConf('translateui')) {
242            return true;
243        }
244
245        if(file_exists(DOKU_INC . 'inc/lang/' . $lc . '/lang.php')) {
246            require(DOKU_INC . 'inc/lang/' . $lc . '/lang.php');
247        }
248        $conf['lang_before_translation'] = $conf['lang']; //store for later access in syntax plugin
249        $conf['lang'] = $lc;
250
251        return true;
252    }
253
254    /**
255     * Hook Callback.  Resort page match results so that results are ordered by translation, having the
256     * default language first
257     *
258     * @param Doku_Event $event
259     * @param $args
260     */
261    function translation_search(Doku_Event $event, $args) {
262
263        if($event->data['has_titles']) {
264            // sort into translation slots
265            $res = array();
266            foreach($event->result as $r => $t) {
267                $tr = $this->helper->getLangPart($r);
268                if(!is_array($res["x$tr"])) $res["x$tr"] = array();
269                $res["x$tr"][] = array($r, $t);
270            }
271            // sort by translations
272            ksort($res);
273            // combine
274            $event->result = array();
275            foreach($res as $r) {
276                foreach($r as $l) {
277                    $event->result[$l[0]] = $l[1];
278                }
279            }
280        } else {
281            # legacy support for old DokuWiki hooks
282
283            // sort into translation slots
284            $res = array();
285            foreach($event->result as $r) {
286                $tr = $this->helper->getLangPart($r);
287                if(!is_array($res["x$tr"])) $res["x$tr"] = array();
288                $res["x$tr"][] = $r;
289            }
290            // sort by translations
291            ksort($res);
292            // combine
293            $event->result = array();
294            foreach($res as $r) {
295                $event->result = array_merge($event->result, $r);
296            }
297        }
298    }
299
300}
301
302//Setup VIM: ex: et ts=4 :
303