xref: /plugin/autotranslation/action.php (revision f7999a744ea543094a1edac40e0698d27be4cb23)
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
13if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
14require_once(DOKU_PLUGIN . 'action.php');
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 action_plugin_translation() {
30        $this->helper =& plugin_load('helper', 'translation');
31    }
32
33    /**
34     * Register the events
35     */
36    function register(Doku_Event_Handler $controller) {
37        // should the lang be applied to UI?
38        $scriptName = basename($_SERVER['PHP_SELF']);
39
40        // should the lang be applied to UI?
41        if($this->getConf('translateui')) {
42            switch($scriptName) {
43                case 'js.php':
44                    $controller->register_hook('INIT_LANG_LOAD', 'BEFORE', $this, 'translation_js');
45                    $controller->register_hook('JS_CACHE_USE', 'BEFORE', $this, 'translation_jscache');
46                    break;
47
48                case 'ajax.php':
49                    $controller->register_hook('INIT_LANG_LOAD', 'BEFORE', $this, 'translate_media_manager');
50                    break;
51
52                case 'mediamanager.php':
53                    $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'setJsCacheKey');
54                    break;
55
56                default:
57                    $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'setJsCacheKey');
58            }
59        }
60
61        if($scriptName !== 'js.php' && $scriptName !== 'ajax.php') {
62            $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'translation_hook');
63            $controller->register_hook('MEDIAMANAGER_STARTED', 'BEFORE', $this, 'translation_hook');
64        }
65
66        $controller->register_hook('SEARCH_QUERY_PAGELOOKUP', 'AFTER', $this, 'translation_search');
67        $controller->register_hook('COMMON_PAGETPL_LOAD', 'AFTER', $this, 'page_template_replacement');
68    }
69
70    /**
71     * Hook Callback. Make current language available as page template placeholder and handle
72     * original language copying
73     *
74     * @param $event
75     * @param $args
76     */
77    function page_template_replacement(&$event, $args) {
78        global $ID;
79
80        // load orginal content as template?
81        if($this->getConf('copytrans') && $this->helper->istranslatable($ID, false)) {
82            // look for existing translations
83            $translations = $this->helper->getAvailableTranslations($ID);
84            if($translations) {
85                // find original language (might've been provided via parameter or use first translation)
86                $orig = (string) $_REQUEST['fromlang'];
87                if(!$orig) $orig = array_shift(array_keys($translations));
88
89                // load file
90                $origfile = $translations[$orig];
91                $event->data['tpl'] = io_readFile(wikiFN($origfile));
92
93                // prefix with warning
94                $warn = io_readFile($this->localFN('totranslate'));
95                if($warn) $warn .= "\n\n";
96                $event->data['tpl'] = $warn . $event->data['tpl'];
97
98                // show user a choice of translations if any
99                if(count($translations) > 1) {
100                    $links = array();
101                    foreach($translations as $t => $l) {
102                        $links[] = '<a href="' . wl($ID, array('do' => 'edit', 'fromlang' => $t)) . '">' . $this->helper->getLocalName($t) . '</a>';
103                    }
104
105                    msg(
106                        sprintf(
107                            $this->getLang('transloaded'),
108                            $this->helper->getLocalName($orig),
109                            join(', ', $links)
110                        )
111                    );
112                }
113
114            }
115        }
116
117        // apply placeholders
118        $event->data['tpl'] = str_replace('@LANG@', $this->helper->realLC(''), $event->data['tpl']);
119        $event->data['tpl'] = str_replace('@TRANS@', $this->helper->getLangPart($ID), $event->data['tpl']);
120    }
121
122    /**
123     * Hook Callback. Load correct translation when loading JavaScript
124     *
125     * @param $event
126     * @param $args
127     */
128    function translation_js(&$event, $args) {
129        global $conf;
130        if(!isset($_GET['lang'])) return;
131        if(!in_array($_GET['lang'], $this->helper->translations)) return;
132        $lang = $_GET['lang'];
133        $event->data = $lang;
134        $conf['lang'] = $lang;
135    }
136
137    /**
138     * Hook Callback. Pass language code to JavaScript dispatcher
139     *
140     * @param $event
141     * @param $args
142     * @return bool
143     */
144    function setJsCacheKey(&$event, $args) {
145        if(!isset($this->locale)) return false;
146        $count = count($event->data['script']);
147        for($i = 0; $i < $count; $i++) {
148            if(!empty($event->data['script'][$i]['src']) && strpos($event->data['script'][$i]['src'], '/lib/exe/js.php') !== false) {
149                $event->data['script'][$i]['src'] .= '&lang=' . hsc($this->locale);
150            }
151        }
152
153        return false;
154    }
155
156    /**
157     * Hook Callback. Make sure the JavaScript is translation dependent
158     *
159     * @param $event
160     * @param $args
161     */
162    function translation_jscache(&$event, $args) {
163        if(!isset($_GET['lang'])) return;
164        if(!in_array($_GET['lang'], $this->helper->translations)) return;
165
166        $lang = $_GET['lang'];
167        // reuse the constructor to reinitialize the cache key
168        if(method_exists($event->data, '__construct')) {
169            // New PHP 5 style constructor
170            $event->data->__construct(
171                $event->data->key . $lang,
172                $event->data->ext
173            );
174        } else {
175            // Old PHP 4 style constructor - deprecated
176            $event->data->cache(
177                $event->data->key . $lang,
178                $event->data->ext
179            );
180        }
181    }
182
183    /**
184     * Hook Callback. Translate the AJAX loaded media manager
185     *
186     * @param $event
187     * @param $args
188     */
189    function translate_media_manager(&$event, $args) {
190        global $conf;
191        if(isset($_REQUEST['ID'])) {
192            $id = getID();
193            $lc = $this->helper->getLangPart($id);
194        } elseif(isset($_SESSION[DOKU_COOKIE]['translationlc'])) {
195            $lc = $_SESSION[DOKU_COOKIE]['translationlc'];
196        } else {
197            return;
198        }
199        if(!$lc) return;
200
201        $conf['lang'] = $lc;
202        $event->data = $lc;
203    }
204
205    /**
206     * Hook Callback. Change the UI language in foreign language namespaces
207     */
208    function translation_hook(&$event, $args) {
209        global $ID;
210        global $lang;
211        global $conf;
212        global $ACT;
213        // redirect away from start page?
214        if($this->conf['redirectstart'] && $ID == $conf['start'] && $ACT == 'show') {
215            $lc = $this->helper->getBrowserLang();
216            if(!$lc) $lc = $conf['lang'];
217            $this->_redirect($lc.':'.$conf['start']);
218            exit;
219        }
220
221        // Check if we can redirect
222        if($this->getConf('redirectlocalized')){
223            $this->translation_redirect_localized();
224        }
225
226        // check if we are in a foreign language namespace
227        $lc = $this->helper->getLangPart($ID);
228
229        // store language in session (for page related views only)
230        if(in_array($ACT, array('show', 'recent', 'diff', 'edit', 'preview', 'source', 'subscribe'))) {
231            $_SESSION[DOKU_COOKIE]['translationlc'] = $lc;
232        }
233        if(!$lc) $lc = $_SESSION[DOKU_COOKIE]['translationlc'];
234        if(!$lc) return;
235        $this->locale = $lc;
236
237        if(!$this->getConf('translateui')) {
238            return true;
239        }
240
241        if(file_exists(DOKU_INC . 'inc/lang/' . $lc . '/lang.php')) {
242            require(DOKU_INC . 'inc/lang/' . $lc . '/lang.php');
243        }
244        $conf['lang_before_translation'] = $conf['lang']; //store for later access in syntax plugin
245        $conf['lang'] = $lc;
246
247        return true;
248    }
249
250    /**
251     * Hook Callback.  Resort page match results so that results are ordered by translation, having the
252     * default language first
253     */
254    function translation_search(&$event, $args) {
255
256        if($event->data['has_titles']) {
257            // sort into translation slots
258            $res = array();
259            foreach($event->result as $r => $t) {
260                $tr = $this->helper->getLangPart($r);
261                if(!is_array($res["x$tr"])) $res["x$tr"] = array();
262                $res["x$tr"][] = array($r, $t);
263            }
264            // sort by translations
265            ksort($res);
266            // combine
267            $event->result = array();
268            foreach($res as $r) {
269                foreach($r as $l) {
270                    $event->result[$l[0]] = $l[1];
271                }
272            }
273        } else {
274            # legacy support for old DokuWiki hooks
275
276            // sort into translation slots
277            $res = array();
278            foreach($event->result as $r) {
279                $tr = $this->helper->getLangPart($r);
280                if(!is_array($res["x$tr"])) $res["x$tr"] = array();
281                $res["x$tr"][] = $r;
282            }
283            // sort by translations
284            ksort($res);
285            // combine
286            $event->result = array();
287            foreach($res as $r) {
288                $event->result = array_merge($event->result, $r);
289            }
290        }
291    }
292
293    /**
294     * Redirects to the localized version of the page when showing and browser says so and translation was explicitly requested
295     **/
296    function translation_redirect_localized() {
297        global $ID;
298        global $conf;
299        global $ACT;
300
301        // redirect to localized page?
302        if( $ACT != 'show' ) { return; }
303
304        $override = isset($_REQUEST['tns']); // override enabled - comes from the bottom bar.
305        $lang = !empty($conf['lang_before_translation']) ? $conf['lang_before_translation'] : $conf['lang']; // Check for original language
306
307        // get current page language - if empty then default;
308        $currentSessionLanguage = $_SESSION[DOKU_COOKIE]['translationcur'];
309        $pageLang = $this->helper->getLangPart($ID);
310
311        if ( empty($pageLang) ) {
312            $pageLang = $lang;
313        }
314
315        // If both match, we're fine.
316        if ( $currentSessionLanguage == $pageLang ) {
317            return;
318        }
319
320        // check current translation
321        if ( empty( $currentSessionLanguage ) && !$override ) {
322
323            // If not set - we must just have entered - set the browser language
324            $currentSessionLanguage = $this->helper->getBrowserLang();
325
326            // if no browser Language set, take entered namespace language - empty for default.
327            if ( !$currentSessionLanguage ) {
328                $currentSessionLanguage = $pageLang;
329            }
330
331            // Set new Language
332            $_SESSION[DOKU_COOKIE]['translationcur'] = $currentSessionLanguage;
333
334            // Write Language back
335            $pageLang = $currentSessionLanguage;
336        }
337
338
339        if ( $override && $pageLang != $currentSessionLanguage ) {
340            // Set new Language
341            $currentSessionLanguage = $pageLang;
342            $_SESSION[DOKU_COOKIE]['translationcur'] = $currentSessionLanguage;
343        } else if ( !$override ) {
344            // Write Language back
345            $pageLang = $currentSessionLanguage;
346        }
347
348        // If this is the default language, make empty
349        if ( $pageLang == $lang ) {
350            $pageLang = '';
351        }
352
353        // Generate new Page ID
354        list($newPage,$name) = $this->helper->buildTransID($pageLang,$this->helper->getIDPart($ID));
355        $newPage = cleanID($newPage);
356
357        // Check if Page exists
358        if ( $newPage != $ID && page_exists($newPage, '', false) ) {
359            // $newPage redirect
360
361            if ( auth_quickaclcheck($newPage) < AUTH_READ ) { return; }
362
363            session_write_close();
364            $this->_redirect($newPage);
365        }
366        else
367        if ( $override ) {
368            // cleanup redirect
369            session_write_close();
370
371            if ( auth_quickaclcheck($newPage) < AUTH_READ ) { return; }
372
373            $this->_redirect($ID);
374        }
375
376        // no redirect;
377    }
378
379
380    function _redirect($url)
381    {
382        unset($_GET['id']);
383        $more = array();
384
385        if ( !empty($_GET) ) {
386            $params = '';
387            foreach( $_GET as $key => $value ) {
388                // Possible multiple encodings.
389                $more[$key] = $value;
390            }
391        }
392
393        if ( wl( $url, $more, true, '&') != DOKU_URL . substr($_SERVER['REQUEST_URI'], 1) ) {
394            header('Location: ' . wl( $url, $more, true, '&'), 302);
395            exit;
396        }
397    }
398}
399
400//Setup VIM: ex: et ts=4 :
401