source: spip-zone/_plugins_/trad-lang/trunk/inc/salvatore.php @ 119862

Last change on this file since 119862 was 119862, checked in by Cerdic, 2 months ago

on unifie en https toutes les urls git.spip.net

  • Property svn:eol-style set to native
File size: 17.1 KB
Line 
1<?php
2
3/*
4    This file is part of Salvatore, the translation robot of Trad-lang (SPIP)
5
6    Salvatore is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    Trad-Lang is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with Trad-Lang; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20    Copyright 2003-2013
21        Florent Jugla <florent.jugla@eledo.com>,
22        Philippe Riviere <fil@rezo.net>,
23        Chryjs <chryjs!@!free!.!fr>,
24                kent1 <kent1@arscenic.info>
25*/
26
27
28/**
29 * initialiser salvatore si besoin
30 * peut etre appelle plusieurs fois
31 * @param string|array $log_function
32 * @throws Exception
33 */
34function salvatore_init($log_function = null){
35        static $initialized;
36
37        // set log function if any
38        if ($log_function){
39                salvatore_log('', $log_function);
40        }
41
42        if (is_null($initialized)){
43                @ini_set('memory_limit', '50M');
44                if (!defined('_DEBUG_TRAD_LANG')){
45                        define('_DEBUG_TRAD_LANG', 1); // undef si on ne veut pas de messages
46                }
47
48                if (!defined('_DIR_SALVATORE')){
49                        define('_DIR_SALVATORE', _DIR_RACINE . 'salvatore/');
50                }
51
52                if (!defined('_DIR_SALVATORE_TRADUCTIONS')){
53                        define('_DIR_SALVATORE_TRADUCTIONS', _DIR_SALVATORE . 'traductions/');
54                }
55
56                if (!defined('_DIR_SALVATORE_TMP')){
57                        define('_DIR_SALVATORE_TMP', _DIR_SALVATORE . 'tmp/');
58                }
59
60                if (!defined('_DIR_SALVATORE_MODULES')){
61                        define('_DIR_SALVATORE_MODULES', _DIR_SALVATORE . 'modules/');
62                }
63
64                if (!defined('_DIR_SALVATORE_DEPOTS')){
65                        define('_DIR_SALVATORE_DEPOTS', _DIR_SALVATORE . 'depots/');
66                }
67
68                if (defined('_ID_AUTEUR_SALVATORE') and is_numeric(_ID_AUTEUR_SALVATORE)){
69                        $GLOBALS['visiteur_session'] = array();
70                        $GLOBALS['visiteur_session']['id_auteur'] = _ID_AUTEUR_SALVATORE;
71                        // TODO : charger une session complete ?
72                }
73
74                // par defaut on relit les fichiers si modifies depuis moins de 1J
75                if (!defined('_SALVATORE_LECTEUR_REFRESH_DELAY')){
76                        define('_SALVATORE_LECTEUR_REFRESH_DELAY', 24 * 3600);
77                }
78
79                // pourcentage de traduction a partir duquel on exporte la langue
80                if (!defined('_SALVATORE_SEUIL_EXPORT')) {
81                        define('_SALVATORE_SEUIL_EXPORT', 50);
82                }
83
84                if (!defined('_SALVATORE_AUTHOR_COMMITS')) {
85                        define('_SALVATORE_AUTHOR_COMMITS', 'Salvatore <salvatore@rezo.net>');
86                }
87
88                // TODO : a tester/valider quand on sera en prod si on utilise encore
89                if (!defined('_SALVATORE_SVN_PROPSET')) {
90                        define('_SALVATORE_SVN_PROPSET', false);
91                }
92
93
94                if (!isset($GLOBALS['idx_lang'])){
95                        $GLOBALS['idx_lang'] = 0;
96                }
97
98                // verifications des repertoires
99                foreach ([_DIR_SALVATORE, _DIR_SALVATORE_TRADUCTIONS, _DIR_SALVATORE_MODULES, _DIR_SALVATORE_DEPOTS, _DIR_SALVATORE_TMP] as $dir){
100                        salvatore_check_dir($dir);
101                }
102                $initialized = true;
103        }
104}
105
106
107/**
108 * chargement du fichier traductions.txt
109 * Construit une liste de modules avec pour chacun un tableau associatif
110 *
111 * @param string $fichier_traductions
112 * @return array
113 * @throws Exception
114 */
115function salvatore_charger_fichier_traductions($fichier_traductions = null){
116
117        salvatore_init();
118        if (is_null($fichier_traductions)){
119                $fichier_traductions = _DIR_SALVATORE_TRADUCTIONS . 'traductions.txt';
120        }
121        salvatore_check_file($fichier_traductions);
122
123        $lignes = file($fichier_traductions);
124        $lignes = array_map('trim', $lignes);
125        $lignes = array_filter($lignes);
126
127        $liste_trad = array();
128        foreach ($lignes as $ligne){
129                if ($ligne[0]!=='#'){
130                        $liste = explode(';', trim($ligne));
131                        $methode = $url = $branche = $dir = $module = $lang = '';
132
133                        // deprecated ancien format, forcement en svn
134                        // liste courte de type
135                        // url;module;lang
136                        if (count($liste)<=3){
137                                $methode = 'svn';
138                                $branche = '';
139                                $url = $liste[0];
140                                if (empty($liste[1])){
141                                        $module = preg_replace('#.*/(.*)$#', '$1', $url);
142                                } else {
143                                        $module = $liste[1];
144                                }
145                                if (empty($liste[2])){
146                                        $lang = 'fr';
147                                } else {
148                                        $lang = $liste[2];
149                                }
150                        }
151                        // format complet et explicite de 6 valeurs
152                        // seule les valeurs pour branche et dir peuvent etre vide (branche master par defaut en git)
153                        // svn;url;;;module;lang
154                        // git;url;master;subdir/tolang;module;lang
155                        else {
156                                list($methode, $url, $branche, $dir, $module, $lang) = $liste;
157                        }
158                        $methode = trim($methode);
159                        $url = trim($url);
160                        $url = rtrim($url, '/'); // homogeneiser
161                        $dir = trim($dir);
162                        $dir = trim($dir, '/'); // homogeneiser
163                        $branche = trim($branche);
164                        $module = trim($module);
165                        $lang = trim($lang);
166
167                        if ($methode
168                                and $url
169                                and $module
170                                and $lang){
171                                // que fait la $GLOBALS['modules'] ?
172                                if (empty($GLOBALS['modules']) or in_array($module, $GLOBALS['modules'])){
173
174                                        // unifier les urls git en https, plus simple a gerer car ne necessitent pas une cle ssh sur le user php (www-data)
175                                        if (strpos($url, "git@git.spip.net:") === 0) {
176                                                $url = "https://git.spip.net/" . substr($url, 17);
177                                        }
178
179                                        // definir un dir checkout unique meme si plusieurs modules de meme nom dans differents repos
180                                        $d = explode('/', $url);
181                                        while (count($d) and in_array(end($d), ['', 'lang', 'trunk', 'ecrire'])){
182                                                array_pop($d);
183                                        }
184                                        $source = '';
185                                        if (end($d)){
186                                                $source = basename(end($d), '.git');
187                                                $source = '--' . preg_replace(',[^\w-],', '_', $source);
188                                        }
189                                        $dir_module = "{$module}{$source}-" . substr(md5("$methode:$url:$branche:$dir"), 0, 5);
190                                        $dir_checkout = preg_replace(",\W+,", "-", "$methode-$url") . ($branche ? "--$branche-" : "-") . substr(md5("$methode:$url:$branche"), 0, 5);
191
192                                        $liste_trad[] = [
193                                                'methode' => $methode,
194                                                'url' => $url,
195                                                'branche' => $branche,
196                                                'dir' => $dir,
197                                                'module' => $module,
198                                                'lang' => $lang,
199                                                'dir_module' => $dir_module,
200                                                'dir_checkout' => $dir_checkout,
201                                        ];
202                                }
203                        } else {
204                                salvatore_log("Fichier $fichier_traductions, IGNORE ligne incomplete : $ligne");
205                        }
206                }
207        }
208        return $liste_trad;
209}
210
211/**
212 * Filtrer la liste complete pour ne garder que un ou plusieurs modules specifiques
213 * @param array $liste_trad
214 * @param string|array $modules
215 * @return array
216 */
217function salvatore_filtrer_liste_traductions($liste_trad, $modules) {
218        if (is_string($modules)) {
219                $modules = explode(',', $modules);
220        }
221        $modules = array_map('trim', $modules);
222        $liste_filtree = array();
223        foreach ($liste_trad as $trad) {
224                if (in_array($trad['module'], $modules)) {
225                        $liste_filtree[] = $trad;
226                }
227        }
228        return $liste_filtree;
229}
230
231/**
232 * Extraire la lang d'un fichier de langue d'un module donne
233 * @param string $module
234 * @param string $fichier_lang
235 * @return array|mixed|string|string[]
236 */
237function salvatore_get_lang_from($module, $fichier_lang) {
238        $lang = str_replace($module, '__', basename($fichier_lang, '.php'));
239        $lang = explode('___', $lang, 2);
240        $lang = end($lang);
241
242        return $lang;
243}
244
245/**
246 * URL du gestionnaire trad-lang exportee dans les xml
247 * @return mixed
248 */
249function salvatore_get_self_url() {
250        $url_gestionnaire = $GLOBALS['meta']['adresse_site'];
251        if (defined('_SALVATORE_TEST_URL_GESTIONNAIRE')) {
252                $url_gestionnaire = _SALVATORE_TEST_URL_GESTIONNAIRE;
253        }
254        return $url_gestionnaire;
255}
256
257/**
258 * Verifier si un module de langue est gere par ce salvatore
259 * @param $dir_module
260 * @param $module
261 * @return string
262 *   l'autre gestionnaire de trad si c'est pas nous
263 *   chaine vide si c'est bien nous qui gerons
264 */
265function salvatore_verifier_gestionnaire_traduction($dir_module, $module) {
266
267        /**
268         * On teste ici si le fichier est géré par un autre salvatore
269         * Si oui on empeche son import en le signifiant
270         */
271        if ($t = salvatore_lire_gestionnaire_traduction($dir_module, $module)){
272                $url = extraire_attribut($t, 'url');
273                $gestionnaire = extraire_attribut($t, 'gestionnaire');
274                $url_gestionnaire = salvatore_get_self_url();
275                if ($gestionnaire !== 'salvatore'
276                  or protocole_implicite($url) !== protocole_implicite($url_gestionnaire)) {
277                        return "$gestionnaire@$url";
278                }
279        }
280
281        return '';
282}
283
284/**
285 * Lire la balise <traduction> du fichier .xml
286 * @param string $dir_module
287 * @param string $module
288 * @return string
289 */
290function salvatore_lire_gestionnaire_traduction($dir_module, $module) {
291        $xml_file = $dir_module . '/' . $module . '.xml';
292        /**
293         * On teste ici si le fichier est géré par un autre salvatore
294         * Si oui on empeche son import en le signifiant
295         */
296        if (file_exists($xml_file)){
297                $xml_content = spip_xml_load($xml_file);
298                if (is_array($xml_content)){
299                        // normalement on a qu'une balise <traduction...> englobante, donc on prend la premiere qu'on trouve
300                        if (spip_xml_match_nodes('/^traduction/', $xml_content, $matches)
301                          and $nodes = array_keys($matches)
302                          and $node = reset($nodes)) {
303                                return "<$node>";
304                        }
305                }
306        }
307        return '';
308}
309
310/**
311 * Retrouver la ligne de spip_tradlang_modules qui correspond a un dir_module/module, meme en cas de chanchement de repo (url/branches)
312 * Attention : ca veut dire que si on branche et qu'on veut traduire 2 branches d'un meme module
313 * il faut supprimer le fichier xml de la nouvelle branche pour qu'elle soit bien ajoutee a trad-lang
314 * et eviter qu'on pense que c'est un renommage
315 *
316 * @param $dir_module
317 * @param $module
318 * @return array|bool
319 */
320function salvatore_retrouver_tradlang_module($dir_module, $module) {
321        $base_dir_module = basename($dir_module);
322        if ($row_module = sql_fetsel('*', 'spip_tradlang_modules', 'dir_module = ' . sql_quote($base_dir_module))) {
323                return $row_module;
324        }
325
326        // peut-etre c'est un module qui a change d'url repo, et donc son dir_module a change ?
327        // sur la balise <traduction> le dir_module est ecrit dans id
328        if ($t = salvatore_lire_gestionnaire_traduction($dir_module, $module)
329          and $old_dir_module = extraire_attribut($t, 'id')
330          and $old_dir_module !== $base_dir_module){
331
332                if ($row_module = sql_fetsel('*', 'spip_tradlang_modules', 'dir_module = ' . sql_quote($old_dir_module))) {
333                        return $row_module;
334                }
335        }
336
337        return false;
338}
339
340/**
341 * Ajouter les credentials user/pass sur les urls de repo
342 * @param string $methode
343 * @param string $url_repository
344 * @param string $module
345 * @return string
346 */
347function salvatore_set_credentials($methode, $url_repository, $module){
348        global $domaines_exceptions, $domaines_exceptions_credentials,
349               $SVNUSER, $SVNPASSWD,
350               $GITUSER, $GITPASSWD;
351
352        // on ne sait pas mettre des credentials si c'est du ssh
353        if (strpos($url_repository, '://')!==false){
354                $user = $pass = false;
355                $parts = parse_url($url_repository);
356                if (empty($parts['user']) and empty($parts['pass'])){
357                        $host = $parts['host'];
358                        require_once(_DIR_ETC . 'salvatore_passwd.inc');
359
360                        if (!empty($domaines_exceptions)
361                                and is_array($domaines_exceptions)
362                                and in_array($host, $domaines_exceptions)){
363                                // on est dans une exception
364
365                                /**
366                                 * Est-ce que cette exception dispose de credentials (Github?)
367                                 */
368                                if (is_array($domaines_exceptions_credentials)
369                                        and !empty($domaines_exceptions_credentials[$host])){
370                                        $user = $domaines_exceptions_credentials[$host]['user'];
371                                        $pass = $domaines_exceptions_credentials[$host]['pass'];
372                                }
373
374                        } else {
375                                // un truc perso pour un module en particulier ?
376                                if (isset(${$module . '_user'})){
377                                        $user = ${$module . '_user'};
378                                        $pass = ${$module . '_passwd'};
379                                } elseif ($methode==='svn' and isset($SVNUSER)) {
380                                        $user = $SVNUSER;
381                                        $pass = $SVNPASSWD;
382                                } elseif ($methode==='git' and isset($GITUSER)) {
383                                        $user = $GITUSER;
384                                        $pass = $GITPASSWD;
385                                }
386                        }
387
388                        if ($user and $pass){
389                                $url_repository = str_replace("://$host", "://" . urlencode($user) . ":" . urlencode($pass) . "@$host", $url_repository);
390                        }
391                }
392
393        }
394
395        return $url_repository;
396}
397
398
399/**
400 * Verifier qu'un repertoire existe
401 * @param $dir
402 * @throws Exception
403 */
404function salvatore_check_dir($dir){
405        if (!is_dir($dir)){
406                throw new Exception("Erreur : le répertoire $dir n'existe pas");
407        }
408}
409
410/**
411 * Verifier qu'un fichier existe
412 * @param $file
413 * @throws Exception
414 */
415function salvatore_check_file($file){
416        if (!file_exists($file)){
417                throw new Exception("Erreur : Le fichier $file est introuvable");
418        }
419}
420
421/**
422 * Loger
423 * @param string $msg
424 * @param string|array $display_function
425 */
426function salvatore_log($msg = '', $display_function = null){
427        static $function = null;
428
429        if ($display_function and is_callable($display_function)){
430                $function = $display_function;
431        }
432
433        if (defined('_DEBUG_TRAD_LANG')
434                and _DEBUG_TRAD_LANG
435                and $msg){
436                if ($function){
437                        call_user_func($function, rtrim($msg));
438                } else {
439                        // fallback : utiliser echo mais enlever les balises de formatage symphony
440                        $msg = str_replace(["<info>", "</info>", "<error>", "</error>", "<comment>", "</comment>", "<question>", "</question>", "</>"], "", $msg);
441                        echo rtrim($msg) . "\n";
442                }
443        }
444}
445
446/**
447 * Echec sur erreur : on envoie un mail si possible et on echoue en lançant une exception
448 * @param $sujet
449 * @param $corps
450 * @throws Exception
451 */
452function salvatore_fail($sujet, $corps){
453        $corps = rtrim($corps) . "\n\n";
454        salvatore_envoyer_mail($sujet, $corps);
455        throw new Exception($corps);
456}
457
458/**
459 * @param string $sujet
460 * @param string $corps
461 */
462function salvatore_envoyer_mail($sujet = 'Erreur', $corps = ''){
463        if (defined('_EMAIL_ERREURS') and _EMAIL_ERREURS
464                and defined('_EMAIL_SALVATORE') and _EMAIL_SALVATORE){
465                $envoyer_mail = charger_fonction('envoyer_mail', 'inc');
466                $destinataire = _EMAIL_ERREURS;
467                $from = _EMAIL_SALVATORE;
468                $envoyer_mail($destinataire, $sujet, $corps, $from);
469                salvatore_log("Un email a été envoyé à l'adresse : " . _EMAIL_ERREURS . "\n");
470        }
471}
472
473
474/**
475 * Verifier que la base de salvatore a bien ete mise a jour
476 * pour ajouter le dir_module qui est la cle unique a la place de module
477 * lancer
478 * spip salvatore:upgrade --traductions=...
479 * avec le bon fichier de traduction pour mettre à jour la base de salvatore avant de pouvoir lancer a nouveau le lecteur ou l'ecriveur
480 */
481function salvatore_verifier_base_upgradee() {
482
483        $schema_declare = filtre_info_plugin_dist('tradlang', 'schema');
484        $schema_base = $GLOBALS['meta']['tradlang_base_version'];
485        if ($schema_base !== $schema_declare) {
486                throw new Exception("Schema de base pas a jour ($schema_base vs $schema_declare). Lancez la commande \nspip salvatore:upgrade --help");
487        }
488
489        $trouver_table = charger_fonction('trouver_table', 'base');
490        $desc = $trouver_table('spip_tradlang_modules');
491
492        // est-ce que le champ a ete cree ?
493        if (!isset($desc['field']['dir_module'])) {
494                throw new Exception("Pas de champ dir_module dans la base spip_tradlang_modules. Lancez la commande \nspip salvatore:upgrade --help");
495        }
496
497        // est-ce que tous les modules en base on bien eu un dir_module affecte (et ni vide ni =module qui est la valeur par defaut lors de l'upgrade de base)
498        $nb = sql_countsel('spip_tradlang_modules', "dir_module='' OR dir_module=module");
499        if ($nb>0) {
500                throw new Exception("Le champ dir_module de spip_tradlang_modules n'est pas renseigne pour tous les modules. Lancez la commande \nspip salvatore:upgrade --help");
501        }
502
503}
504
505/**
506 * Nettoyer la chaine de langue (venant du fichier PHP en lecture ou de la base en ecriture)
507 * @param string $chaine
508 * @param string $lang
509 * @return string
510 */
511function salvatore_nettoyer_chaine_langue($chaine, $lang){
512        static $typographie_functions = array();
513
514        if (!isset($typographie_functions[$lang])){
515                $typo = (in_array($lang, array('eo', 'fr', 'cpf')) || strncmp($lang, 'fr_', 3)==0) ? 'fr' : 'en';
516                $typographie_functions[$lang] = charger_fonction($typo, 'typographie');
517        }
518
519        /**
520         * On enlève les sauts de lignes windows pour des sauts de ligne linux
521         */
522
523        $chaine = str_replace("\r\n", "\n", $chaine);
524
525        /**
526         * protection dans les balises genre <a href="..." ou <img src="..."
527         * cf inc/filtres
528         */
529        if (preg_match_all(_TYPO_BALISE, $chaine, $regs, PREG_SET_ORDER)){
530                foreach ($regs as $reg){
531                        $insert = $reg[0];
532                        // hack: on transforme les caracteres a proteger en les remplacant
533                        // par des caracteres "illegaux". (cf corriger_caracteres())
534                        $insert = strtr($insert, _TYPO_PROTEGER, _TYPO_PROTECTEUR);
535                        $chaine = str_replace($reg[0], $insert, $chaine);
536                }
537        }
538
539        /**
540         * Protéger le contenu des balises <html> <code> <cadre> <frame> <tt> <pre>
541         */
542        define('_PROTEGE_BLOCS_HTML', ',<(html|code|cadre|pre|tt)(\s[^>]*)?>(.*)</\1>,UimsS');
543        if ((strpos($chaine, '<')!==false) and preg_match_all(_PROTEGE_BLOCS_HTML, $chaine, $matches, PREG_SET_ORDER)){
544                foreach ($matches as $reg){
545                        $insert = $reg[0];
546                        // hack: on transforme les caracteres a proteger en les remplacant
547                        // par des caracteres "illegaux". (cf corriger_caracteres())
548                        $insert = strtr($insert, _TYPO_PROTEGER, _TYPO_PROTECTEUR);
549                        $chaine = str_replace($reg[0], $insert, $chaine);
550                }
551        }
552
553        /**
554         * On applique la typographie de la langue
555         */
556        $chaine = $typographie_functions[$lang]($chaine);
557
558        /**
559         * On remet les caractères normaux sur les caractères illégaux
560         */
561        $chaine = strtr($chaine, _TYPO_PROTECTEUR, _TYPO_PROTEGER);
562
563        $chaine = unicode_to_utf_8(html_entity_decode(preg_replace('/&([lg]t;)/S', '&amp;\1', $chaine), ENT_NOQUOTES, 'utf-8'));
564
565        return $chaine;
566}
Note: See TracBrowser for help on using the repository browser.