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 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class helper_plugin_translation extends DokuWiki_Plugin {
13    var $trans       = array();
14    var $tns         = '';
15    var $defaultlang = '';
16
17    /**
18     * Initialize
19     */
20    function helper_plugin_translation(){
21        global $conf;
22        require_once(DOKU_INC.'inc/pageutils.php');
23        require_once(DOKU_INC.'inc/utf8.php');
24
25        // load wanted translation into array
26        $this->trans = strtolower(str_replace(',',' ',$this->getConf('translations')));
27        $this->trans = array_unique(array_filter(explode(' ',$this->trans)));
28        sort($this->trans);
29
30        // get default translation
31        if(!$conf['lang_before_translation']){
32          $dfl = $conf['lang'];
33        } else {
34          $dfl = $conf['lang_before_translation'];
35        }
36        if(in_array($dfl,$this->trans)){
37            $this->defaultlang = $dfl;
38        }else{
39            $this->defaultlang = '';
40            array_unshift($this->trans,'');
41        }
42
43
44        $this->tns = cleanID($this->getConf('translationns'));
45        if($this->tns) $this->tns .= ':';
46    }
47
48    /**
49     * Check if the given ID is a translation and return the language code.
50     */
51    function getLangPart($id){
52        $rx = '/^'.$this->tns.'('.join('|',$this->trans).'):/';
53        if(preg_match($rx,$id,$match)){
54            return $match[1];
55        }
56        return '';
57    }
58
59    /**
60     * Returns the browser language if it matches with one of the configured
61     * languages
62     */
63    function getBrowserLang(){
64        $rx = '/(^|,|:|;|-)('.join('|',$this->trans).')($|,|:|;|-)/i';
65        if(preg_match($rx,$_SERVER['HTTP_ACCEPT_LANGUAGE'],$match)){
66            return strtolower($match[2]);
67        }
68        return false;
69    }
70
71
72    /**
73     * Returns the ID and name to the wanted translation, empty
74     * $lng is default lang
75     */
76    function buildTransID($lng,$idpart){
77        global $conf;
78        global $saved_conf;
79        if($lng){
80            $link = ':'.$this->tns.$lng.':'.$idpart;
81            $name = $lng;
82        }else{
83            $link = ':'.$this->tns.$idpart;
84            if(!$conf['lang_before_translation']){
85              $name = $conf['lang'];
86            } else {
87              $name = $conf['lang_before_translation'];
88            }
89        }
90        return array($link,$name);
91    }
92
93    /**
94     * Check if current ID should be translated and any GUI
95     * should be shown
96     */
97    function istranslatable($id,$checkact=true){
98        global $ACT;
99
100        if($checkact && $ACT != 'show') return false;
101        if($this->tns && strpos($id,$this->tns) !== 0) return false;
102        $skiptrans = trim($this->getConf('skiptrans'));
103        if($skiptrans &&  preg_match('/'.$skiptrans.'/ui',':'.$id)) return false;
104        $meta = p_get_metadata($id);
105        if($meta['plugin']['translation']['notrans']) return false;
106
107        return true;
108    }
109
110	function showAbout()
111	{
112	        global $ID;
113        	global $conf;
114        	global $INFO;
115
116		$this->checkage();
117
118		$LN = confToHash(dirname(__FILE__).'/lang/langnames.txt');
119
120		$rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/';
121		$idpart = preg_replace($rx,'',$ID);
122
123		$out = '';
124		$out .= '<sup>';
125		if($this->getConf('localabout')){
126
127			//$out .= '<sup>'.html_wikilink($this->getConf('about'),'?').'</sup>';
128
129			//http://localhost/dokuwiki/doku.php?id=pl:test:podtest
130			//$out .= '[';
131			//$out .= getNS(cleanID(getID())); //pl:test
132			//$out .= $INFO['namespace']; //pl:test
133			//$out .= cleanID($ID); //pl:test:podtes
134			//$out .= getID(); //pl:test:podtest
135			//$out .= getNS($ID); //pl:test
136			//$out .= ']';
137
138			$lc = '';
139
140			//try main lang namespace
141			foreach($this->trans as $t){
142				list($link,$name) = $this->buildTransID($t,$idpart);
143				$link = cleanID($link);
144				if($ID == $link){
145				    $lc = hsc($name);
146				}
147				if ($lc) break;
148			}
149
150			//try browser language
151            		if(!$lc) $lc = $this->getBrowserLang();
152
153			//try wiki language
154            		if(!$lc) $lc = $conf['lang'];
155
156			if(!$lc) { //can't find language
157				$localabout = $this->getConf('about'); //http://localhost/dokuwiki/doku.php?id=translation:about
158			} else { //i found language!
159            			$localabout = $lc.':'.$this->getConf('about'); //http://localhost/dokuwiki/doku.php?id=en:translation:about
160			}
161
162			//make link
163			$out .= html_wikilink($localabout,'?');
164	    	} else
165		{
166			$out .= html_wikilink($this->getConf('about'),'?');
167		}
168		$out .= '</sup>';
169
170		return $out;
171	}
172
173    /**
174     * Displays the available and configured translations. Needs to be placed in the template.
175     */
176    function showTranslations(){
177        global $ID;
178        global $conf;
179        global $INFO;
180
181        if(!$this->istranslatable($ID)) return;
182
183        $this->checkage();
184
185        $LN = confToHash(dirname(__FILE__).'/lang/langnames.txt');
186
187        $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/';
188        $idpart = preg_replace($rx,'',$ID);
189
190        $out  = '<div class="plugin_translation">';
191
192	//scx
193	//show text
194	if ($this->getConf('description')){
195		$out .= '<span>'.$this->getLang('translations');
196		if ($this->getConf('showabout')) $out .= $this->showAbout();
197		$out .= ':</span> ';
198	}
199
200        if($this->getConf('dropdown')){ // use dropdown
201            if($INFO['exists']){
202                $class = 'wikilink1';
203            }else{
204                $class = 'wikilink2';
205            }
206
207	    //scx
208	    //link to about (left)
209	    //if (!$this->getConf('description') && $this->getConf('showabout')) {
210	    //	//$out .= '&nbsp';
211	    //	$out .= $this->showAbout();
212	    //	$out .= '&nbsp';
213	    //}
214
215            ////$out .= '<form action="'.wl().'" id="translation__dropdown">';
216            ////$out .= '<select name="id" class="'.$class.'">';
217	    $out2 = "";
218            foreach($this->trans as $t){
219                list($link,$name) = $this->buildTransID($t,$idpart);
220                $link = cleanID($link);
221                if($ID == $link){
222                    $sel = ' selected="selected"';
223		    if($this->getConf('dropdown2'))
224		    {
225			$out .= $this->makecountrylink($LN, $idpart, $t, false);
226			$out .= "&nbsp;";
227		    }
228                }else{
229                    $sel = '';
230                }
231                if(page_exists($link,'',false)){
232                    $class = 'wikilink1';
233                }else{
234                    $class = 'wikilink2';
235                }
236
237		//scx
238		//linktitle
239		$linktitle = '';
240		if (strlen($LN[$name]) > 0){
241			$linktitle = $LN[$name];
242		} else{
243			$linktitle = hsc($name);
244		}
245
246                $out2 .= '<option value="'.$link.'"'.$sel.' class="'.$class.'" title="'.$linktitle.'">'.hsc($name).'</option>';
247            }
248	    $out .= '<form action="'.wl().'" id="translation__dropdown">';
249            $out .= '<select name="id" class="'.$class.'">';
250	    $out .= $out2;
251            $out .= '</select>';
252            $out .= '<input name="go" type="submit" value="&rarr;" />';
253            $out .= '</form>';
254
255
256	    //scx
257	    //link to about (right)
258	    if (!$this->getConf('description') && $this->getConf('showabout')) {
259	    	$out .= '&nbsp';
260	    	$out .= $this->showAbout();
261	    	//$out .= '&nbsp';
262	    }
263        }else{ // use list
264	    //scx
265	    //require(DOKU_PLUGIN.'translation/flags/langnames.php');
266            $out .= '<ul>';
267
268	    if (!$this->getConf('description') && $this->getConf('showabout')) {
269	    	$out .= '&nbsp';
270	    	$out .= $this->showAbout();
271	    	//$out .= '&nbsp';
272	    }
273
274            foreach($this->trans as $t){
275                $out .= $this->makecountrylink($LN, $idpart, $t, true);
276            }
277            $out .= '</ul>';
278        }
279
280        $out .= '</div>';
281
282        return $out;
283    }
284
285
286	function makecountrylink($LN, $idpart, $t, $div)
287	{
288		global $ID;
289        	global $conf;
290        	global $INFO;
291
292		require(DOKU_PLUGIN.'translation/flags/langnames.php');
293
294		list($link,$name) = $this->buildTransID($t,$idpart);
295		$link = cleanID($link);
296                if(page_exists($link,'',false)){
297                    $class = 'wikilink1';
298                }else{
299                    $class = 'wikilink2';
300                }
301
302		//linktitle
303		$linktitle = '';
304		if (strlen($LN[$name]) > 0){
305			$linktitle = $LN[$name];
306		} else{
307			$linktitle = hsc($name);
308		}
309
310		//$out .= 'link='.$link; //link=de:start
311		//$out .= 'wl(link)='.wl($link); //wl(link)=/dokuwiki/doku.php?id=de:start
312	 	//$out .= 'class='.$class; //class=wikilink2
313		//$out .= 'name='.$name; //name=de
314		//$out .= 'LN[name]='.$LN[$name]; //LN[name]=Deutsch
315		//$out .= 'hsc(name)='.hsc($name); //hsc(name)=de
316		//$out .= 'linktitle='.$linktitle; //linktitle=Deutsch
317
318		//if (show flag AND ((flag exist) OR (flag not exist AND show blank flag))
319		if (($langflag[hsc($name)] != NULL && strlen($langflag[hsc($name)]) > 0 && $this->getConf('flags')) || $this->getConf('flags') && $this->getConf('blankflag')) {
320			//$out .= '  <li><div class="li"><a href="'.wl($link).'" class="'.$class.'" title="'.$LN[$name].'">'.hsc($name).'</a></div></li>';
321
322			resolve_pageid(getNS($ID),$link,$exists);
323			if ($div)
324			{
325				if ($exists){ //solid box
326					$out .= '  <li><div class="li">';
327				} else{ //50% transparent box (50% transparent flag)
328					$out .= '  <li><div class="flag_not_exists">';
329				}
330			}
331
332			//html_wikilink works very slow for images
333			//$flag['title'] = $langname[$name];
334            		//$flag['src'] = DOKU_URL.'lib/plugins/translation/flags/'.$langflag[$name];
335
336			//$out .= html_wikilink($link,$flag);
337
338			$out .= '<a href="'.wl($link).'"';
339			$out .= 'title="'.$linktitle.'"';
340			//class for image
341			$out .= 'class="wikilink3"';
342			$out .= '>';
343
344			//show flag
345			if ($langflag[hsc($name)] != NULL && strlen($langflag[hsc($name)]) > 0){
346				$out .= '<img src="'.DOKU_URL.'lib/plugins/translation/flags/'.$langflag[hsc($name)].'" alt='.$linktitle.'" border="0">';
347			} else{ //show blank flag
348				//$out .= '<img src="'.DOKU_BASE.'lib/images/blank.gif'.'" width=15 height=11 alt="'.$linktitle.'" border="0">';
349				$out .= '<img src="'.DOKU_BASE.'lib/plugins/translation/flags/blankflag.gif'.'" width=15 height=11 alt="'.$linktitle.'" border="0">';
350			}
351			$out .= '</a>';
352
353		}
354		else{ //show text (also if flag not exist and blankflag=false)
355			//$out .= '  <li><div class="li"><a href="'.wl($link).'" class="'.$class.'" title="'.$LN[$name].'">'.hsc($name).'</a></div></li>';
356
357			//$out .= '<a href="'.wl($link);
358			//$out .= '" class="'.$class.'" title="'.$LN[$name].'">';
359			//$out .= hsc($name);
360			//$out .= '</a>';
361			if ($div)
362			{
363				$out .= '  <li><div class="li">';
364			}
365			$out .= html_wikilink($link,hsc($name));
366		}
367		if ($div)
368		{
369			$out .= '</div></li>';
370		}
371
372		return $out;
373	}
374
375
376
377    /**
378     * Checks if the current page is a translation of a page
379     * in the default language. Displays a notice when it is
380     * older than the original page. Tries to lin to a diff
381     * with changes on the original since the translation
382     */
383    function checkage(){
384        global $ID;
385        global $INFO;
386        if(!$this->getConf('checkage')) return;
387        if(!$INFO['exists']) return;
388        $lng = $this->getLangPart($ID);
389        if($lng == $this->defaultlang) return;
390
391        $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/';
392        $idpart = preg_replace($rx,'',$ID);
393
394        // compare modification times
395        list($orig,$name) = $this->buildTransID($this->defaultlang,$idpart);
396        $origfn = wikiFN($orig);
397        if($INFO['lastmod'] >= @filemtime($origfn) ) return;
398
399        // get revision from before translation
400        $orev = 0;
401        $revs = getRevisions($orig,0,100);
402        foreach($revs as $rev){
403            if($rev < $INFO['lastmod']){
404                $orev = $rev;
405                break;
406            }
407        }
408
409        // see if the found revision still exists
410        if($orev && !page_exists($orig,$orev)) $orev=0;
411
412        // build the message and display it
413        $orig = cleanID($orig);
414        $msg = sprintf($this->getLang('outdated'),wl($orig));
415        if($orev){
416            $msg .= sprintf(' '.$this->getLang('diff'),
417                    wl($orig,array('do'=>'diff','rev'=>$orev)));
418        }
419
420        echo '<div class="notify">'.$msg.'</div>';
421    }
422}
423