source: spip-zone/_plugins_/trad-lang/trunk/salvatore/ecriveur.php @ 119866

Last change on this file since 119866 was 119866, checked in by Cerdic, 15 months ago

normalisation des noms de fonctions vcs, surchargeables et une fonction helper pour les charger

  • Property svn:eol-style set to native
File size: 16.4 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-2020
21        Florent Jugla <florent.jugla@eledo.com>,
22        Philippe Riviere <fil@rezo.net>,
23        Chryjs <chryjs!@!free!.!fr>,
24        kent1 <kent1@arscenic.info>
25        Cerdic <cedric@yterium.com>
26*/
27
28include_spip('base/abstract_sql');
29include_spip('inc/charsets');
30include_spip('inc/config');
31include_spip('inc/filtres');
32include_spip('inc/texte');
33include_spip('inc/xml');
34include_spip('inc/lang_liste');
35include_spip('inc/session');
36
37include_spip('salvatore/vcs/svn');
38include_spip('salvatore/vcs/git');
39
40
41/**
42 * @param array $liste_sources
43 * @param string $message_commit
44 * @param string $dir_modules
45 * @param string $dir_depots
46 * @throws Exception
47 */
48function salvatore_ecrire($liste_sources, $message_commit='', $dir_modules = null, $dir_depots=null){
49        include_spip('inc/salvatore');
50        salvatore_init();
51
52        // on va lire dans la base, il faut qu'elle soit a jour
53        salvatore_verifier_base_upgradee();
54
55        if (is_null($dir_modules)){
56                $dir_modules = _DIR_SALVATORE_MODULES;
57        }
58        salvatore_check_dir($dir_modules);
59
60        if (is_null($dir_depots)) {
61                $dir_depots = _DIR_SALVATORE_DEPOTS;
62        }
63        salvatore_check_dir($dir_depots);
64
65        $url_gestionnaire = salvatore_get_self_url();
66
67        foreach ($liste_sources as $source){
68                salvatore_log("\n<info>--- Module " . $source['module'] . " | " . $source['dir_module'] . " | " . $source['url'] . "</info>");
69
70                $module = $source['module'];
71                $dir_module = $dir_modules . $source['dir_module'];
72
73                if ($autre_gestionnaire = salvatore_verifier_gestionnaire_traduction($dir_module, $module)){
74                        salvatore_fail("[Ecriveur] Erreur sur $module", "Erreur : export impossible, le fichier est traduit autre part : $autre_gestionnaire\n");
75                }
76
77                $id_tradlang_module = sql_getfetsel('id_tradlang_module', 'spip_tradlang_modules', 'dir_module = ' . sql_quote($source['dir_module']));
78                if (!$id_tradlang_module) {
79                        salvatore_fail("[Ecriveur] Erreur sur $module", "Erreur : export impossible, le module n'est pas en base\n");
80                }
81                else {
82                        // url de l'interface de traduction d'un module
83                        $url_trad_module = url_absolue(generer_url_entite($id_tradlang_module, 'tradlang_module'), $url_gestionnaire);
84                        salvatore_exporter_module($id_tradlang_module, $source, $url_gestionnaire, $url_trad_module, $dir_modules, $dir_depots, $message_commit);
85                }
86        }
87}
88
89/**
90 * Genere les fichiers de traduction d'un module
91 *
92 * @param int $id_tradlang_module
93 * @param array $source
94 * @param string $url_site
95 * @param string $url_trad_module
96 * @param string $dir_modules
97 * @param string $dir_depots
98 * @param string $message_commit
99 * @return false|int
100 */
101function salvatore_exporter_module($id_tradlang_module, $source, $url_site, $url_trad_module, $dir_modules, $dir_depots, $message_commit = ''){
102
103        $url_repo = $source['url'];
104
105        $row_module = sql_fetsel('*', 'spip_tradlang_modules', 'id_tradlang_module=' . intval($id_tradlang_module));
106        if (!$row_module) {
107                $module = $source['module'];
108                salvatore_log("<error>Le module #$id_tradlang_module $module n'existe pas</error>");
109                return false;
110        }
111        $lang_ref = $row_module['lang_mere'];
112        $dir_module = $dir_modules . $row_module['dir_module'];
113        $module = $row_module['module'];
114
115        if (is_numeric($row_module['limite_trad']) and $row_module['limite_trad']>0){
116                $seuil_export = $row_module['limite_trad'];
117        }
118        else {
119                $seuil_export = lire_config('tradlang/seuil_export_tradlang', _SALVATORE_SEUIL_EXPORT);
120        }
121
122        $file_commit_infos = $dir_module . '/' . $module . '.commit.json';
123        if (file_exists($file_commit_infos)) {
124                salvatore_fail("[Ecriveur] Erreur sur $module", "Erreur : il y a deja un fichier $file_commit_infos avec des commits en attente");
125        }
126
127
128        $xml_infos = $commit_infos = array();
129        $liste_lang = $liste_lang_non_exportees = $liste_lang_a_supprimer = array();
130
131        $count_trad_reference = sql_countsel('spip_tradlangs', 'id_tradlang_module=' . intval($id_tradlang_module) . ' AND lang=' . sql_quote($row_module['lang_mere']) . " AND statut='OK'", 'id');
132        $minimal = ceil((($count_trad_reference*$seuil_export)/100));
133        salvatore_log("Minimal = $minimal ($seuil_export %)");
134
135        $langues = sql_allfetsel('lang,COUNT(*) as count', 'spip_tradlangs', 'id_tradlang_module=' . intval($id_tradlang_module) . " AND statut != 'NEW' AND statut != 'attic'", 'lang', 'lang');
136        foreach ($langues as $langue){
137                /**
138                 * Le fichier est il suffisamment traduit
139                 */
140                if ($langue['count']>=$minimal){
141                        $liste_lang[] = $langue['lang'];
142                        $commit_infos[$langue['lang']] = array();
143                } 
144                else {
145                        /**
146                         * Le fichier n'est pas suffisamment traduit et n'existe pas, on ne fera donc rien
147                         */
148                        if (!file_exists($dir_module . '/' . $module . '_' . $langue['lang'] . '.php')){
149                                $liste_lang_non_exportees[] = $langue['lang'];
150                        } else {
151                                /**
152                                 * Il n'est pas suffisamment traduit, cependant, il existe déjà
153                                 * On ne va donc pas le supprimer à la barbare, mais on le met à jour quand même
154                                 */
155                                $liste_lang[] = $langue['lang'];
156                                $commit_infos[$langue['lang']] = array();
157                                $liste_lang_a_supprimer[] = $langue['lang'];
158                                $percent = (($langue['count']/$count_trad_reference)*100);
159                                if ($percent<($seuil_export-15)){
160                                        $commit_infos[$langue['lang']]['message'] = "La langue '" . $langue['lang'] . "' devrait être supprimée car trop peu traduite (" . number_format($percent, 2) . " %)\n";
161                                }
162                        }
163                }
164        }
165
166        // traiter chaque langue
167        foreach ($liste_lang as $lang){
168                salvatore_log("Generation de la langue $lang");
169                $indent = "\t";
170
171                $php_lines = $chaines = $id_tradlangs = array();
172                $initiale = '';
173
174                // On ne prend que les MODIF, les RELIRE et les OK pour ne pas rendre les sites multilingues en français
175                $chaines = sql_allfetsel('id_tradlang,id,str,comm,statut,md5', 'spip_tradlangs', 'id_tradlang_module=' . intval($id_tradlang_module) . ' AND lang=' . sql_quote($lang) . " AND statut!='NEW' AND statut!='attic'", 'id');
176                $id_tradlangs = array_column($chaines, 'id_tradlang');
177                $chaines = array_combine(array_column($chaines, 'id'), $chaines);
178                ksort($chaines);
179
180                $total_chaines = ['OK' => 0, 'MODIF' => 0, 'RELIRE' => 0];
181                foreach ($chaines as $chaine){
182                        $total_chaines[$chaine['statut']]++;
183
184                        $comment = salvatore_clean_comment($chaine['comm']);
185
186                        if ($initiale !== strtoupper($chaine['id'][0])){
187                                $initiale = strtoupper($chaine['id'][0]);
188                                $php_lines[] = "\n$indent// $initiale";
189                        }
190
191                        if (strlen($chaine['statut']) and ($chaine['statut']!=='OK')){
192                                $comment .= ' ' . $chaine['statut'];
193                        }
194                        if ($comment){
195                                $comment = ' # ' . trim($comment); // on rajoute les commentaires ?
196                        }
197
198                        // nettoyger la chaine de langue et calcul du md5
199                        $str = salvatore_nettoyer_chaine_langue($chaine['str'], $lang);
200                        $newmd5 = md5($str);
201
202                        /**
203                         * Si le md5 ou la chaine à changé, on la met à jour dans la base
204                         */
205                        if (($chaine['md5']!==$newmd5) || ($str!=$chaine['str'])){
206                                $r = sql_updateq('spip_tradlangs', array('md5' => $newmd5, 'str' => $str), 'id_tradlang=' . intval($chaine['id_tradlang']));
207                        }
208
209                        $php_lines[] = $indent . var_export($chaine['id'], 1) . ' => ' . var_export($str, 1) . ',' . $comment;
210                }
211
212                salvatore_log(" - traduction (".$total_chaines['OK']."/$count_trad_reference OK | ".$total_chaines['RELIRE']."/$count_trad_reference RELIRE | ".$total_chaines['MODIF']."/$count_trad_reference MODIFS), export");
213                $file_name = salvatore_exporter_fichier_php($dir_module, $module, $lang, $php_lines, $url_trad_module, ($lang==$lang_ref) ? $url_repo : false);
214
215                // noter la langue et les traducteurs pour lang/module.xml
216                $people_unique = array();
217                $xml_infos[$lang] = array(
218                        'traducteurs' => array(),
219                        'traduits' => $total_chaines['OK'],
220                        'modifs' => $total_chaines['MODIF'],
221                        'relire' => $total_chaines['RELIRE'],
222                );
223                if (defined('_ID_AUTEUR_SALVATORE') and intval(_ID_AUTEUR_SALVATORE)>0){
224                        $people_unique[] = _ID_AUTEUR_SALVATORE;
225                }
226
227                // ici on prend tous les statut de chaine (?)
228                $traducteurs = sql_allfetsel('DISTINCT(traducteur)', 'spip_tradlangs', 'id_tradlang_module=' . intval($id_tradlang_module) . ' AND lang=' . sql_quote($lang));
229                foreach ($traducteurs as $t){
230                        $traducteurs_lang = explode(',', $t['traducteur']);
231                        foreach ($traducteurs_lang as $traducteur){
232                                if (!in_array($traducteur, $people_unique)){
233                                        $traducteur_supp = array();
234                                        if (is_numeric($traducteur) and $id_auteur = intval($traducteur)){
235                                                $traducteur_supp['nom'] = extraire_multi(sql_getfetsel('nom', 'spip_auteurs', 'id_auteur = ' . $id_auteur));
236                                                $traducteur_supp['lien'] = url_absolue(generer_url_entite($id_auteur, 'auteur'), $url_site);
237                                        } elseif (trim(strlen($traducteur))>0) {
238                                                $traducteur_supp['nom'] = trim($traducteur);
239                                                $traducteur_supp['lien'] = '';
240                                        }
241                                        if (isset($traducteur_supp['nom'])){
242                                                $xml_infos[$lang]['traducteurs'][strtolower($traducteur_supp['nom'])] = $traducteur_supp;
243                                        }
244                                        $people_unique[] = $traducteur;
245                                }
246                        }
247                }
248                unset($people_unique);
249
250                $commit_infos[$lang]['lang'] = $lang;
251                $commit_infos[$lang]['file_name'] = basename($file_name);
252                $commit_infos[$lang]['lastmodified'] = salvatore_read_lastmodified_file(basename($file_name), $source, $dir_depots);
253                $commit_infos[$lang]['must_add'] = false;
254
255                if ($row_module['limite_trad']==0){
256                        $commit_infos[$lang]['must_add'] = true;
257                } elseif (!in_array($module, array('ecrire', 'spip', 'public'))) {
258                        if ((intval(($xml_infos[$lang]['traduits']/$count_trad_reference)*100)>$seuil_export)){
259                                $commit_infos[$lang]['must_add'] = true;
260                        }
261                }
262
263                // trouver le commiteur si c'est un fichier deja versionne ou a ajouter
264                if ($commit_infos[$lang]['lastmodified'] or $commit_infos[$lang]['must_add']) {
265                        $where = [
266                                "objet='tradlang'",
267                                sql_in('id_objet', $id_tradlangs),
268                                "id_auteur != '-1'",
269                                'id_auteur !=' . intval(_ID_AUTEUR_SALVATORE),
270                        ];
271                        if ($commit_infos[$lang]['lastmodified']) {
272                                $where[] = "date>".sql_quote(date('Y-m-d H:i:s', $commit_infos[$lang]['lastmodified']));
273                        }
274                        $auteur_versions = sql_allfetsel('DISTINCT id_auteur', 'spip_versions',  $where);
275                        if (count($auteur_versions)==1){
276                                $auteur = sql_fetsel('nom,email', 'spip_auteurs', 'id_auteur = ' . intval($auteur_versions[0]['id_auteur']));
277                                if ($auteur and $auteur['email']){
278                                        $commit_infos[$lang]['author'] = $auteur['email'];
279                                        if ($auteur['nom']) {
280                                                $commit_infos[$lang]['author'] = $auteur['nom'] . "<" . $commit_infos[$lang]['author'] . ">";
281                                        }
282                                        salvatore_log("Le commiteur pour la langue $lang : " . $commit_infos[$lang]['author']);
283                                }
284                        }
285                }
286        }
287
288        // le fichier XML recapitulatif
289        $indent = "\t";
290        $xml = "<traduction
291{$indent}module=\"$module\"
292{$indent}id=\"".$row_module['dir_module']."\"
293{$indent}gestionnaire=\"salvatore\"
294{$indent}url=\"$url_site\"
295{$indent}source=\"$url_repo\"
296{$indent}reference=\"$lang_ref\">\n";
297        foreach ($xml_infos as $lang => $info){
298                $detail = "";
299                if ($info['traduits'] > 0) {
300                        $detail = " total=\"$count_trad_reference\" traduits=\"" . $info['traduits'] . '" relire="' . $info['relire'] . '" modifs="' . $info['modifs'] . '" nouveaux="' . ($count_trad_reference-($info['modifs']+$info['traduits']+$info['relire'])) . '" pourcent="' . number_format((($info['traduits']/$count_trad_reference)*100), 2) . "\"";
301                }
302                if (count($info['traducteurs'])>0){
303                        $xml .= "$indent<langue code=\"$lang\" url=\"" . parametre_url($url_trad_module, 'lang_cible', $lang) . "\"{$detail}>\n";
304                        ksort($info['traducteurs']);
305                        foreach ($info['traducteurs'] as $nom => $people){
306                                $xml .= $indent . $indent . '<traducteur nom="' . entites_html($people['nom']) . '" lien="' . entites_html($people['lien']) . "\" />\n";
307                        }
308                        $xml .= "$indent</langue>\n";
309                } else {
310                        $xml .= "$indent<langue code=\"$lang\" url=\"" . parametre_url($url_trad_module, 'lang_cible', $lang) . "\"{$detail} />\n";
311                }
312        }
313        $xml .= "</traduction>\n";
314        $file_xml = $dir_module . '/' . $module . '.xml';
315        file_put_contents($file_xml, $xml);
316        $commit_infos['.xml'] = array(
317                'file_name' => basename($file_xml),
318                'lastmodified' => salvatore_read_lastmodified_file(basename($file_xml), $source, $dir_depots),
319                'must_add' => true,
320        );
321
322
323
324        if (isset($liste_lang_non_exportees) and (count($liste_lang_non_exportees)>0)){
325                salvatore_log("Les langues suivantes ne sont pas exportées car trop peu traduites : " . implode(', ', $liste_lang_non_exportees));
326        }
327        if (isset($liste_lang_a_supprimer) and (count($liste_lang_a_supprimer)>0)){
328                salvatore_log("<error>Les langues suivantes devraient être supprimées car trop peu traduites : " . implode(', ', $liste_lang_a_supprimer)."</error>");
329        }
330
331        $nb_to_commit = 0;
332        // et on ecrit un json pour que le pousseur sache quoi commit
333        if (count($commit_infos)) {
334                $nb_to_commit = count($commit_infos);
335                if ($message_commit) {
336                        $commit_infos['.message'] = $message_commit;
337                }
338                file_put_contents($file_commit_infos, json_encode($commit_infos));
339        }
340
341        $log = salvatore_read_status_modif($module, $source, $dir_depots);
342        salvatore_log($log);
343        return $nb_to_commit;
344}
345
346/**
347 * Nettoyer le commentaire avant ecriture dans le PHP
348 * @param $comment
349 * @return mixed|string
350 */
351function salvatore_clean_comment($comment) {
352        if (strlen(trim($comment))>1){
353                // On remplace les sauts de lignes des commentaires sinon ça crée des erreurs php
354                $comment = str_replace(array("\r\n", "\n", "\r"), ' ', $comment);
355                // Conversion des commentaires en utf-8
356                $comment = unicode_to_utf_8(html_entity_decode(preg_replace('/&([lg]t;)/S', '&amp;\1', $comment), ENT_NOQUOTES, 'utf-8'));
357                return $comment;
358        }
359        return '';
360}
361
362
363/**
364 * Generer un fichier de langue a partir de ses lignes php
365 * @param string $dir_module
366 * @param string $module
367 * @param string $lang
368 * @param array $php_lines
369 * @param string $url_trad_module
370 * @param $origin
371 * @return string
372 */
373function salvatore_exporter_fichier_php($dir_module, $module, $lang, $php_lines, $url_trad_module, $origin) {
374        $file_name = $dir_module . '/' . $module . '_' . $lang . '.php';
375        $file_content = '<' . '?php
376// This is a SPIP language file  --  Ceci est un fichier langue de SPIP
377';
378        if ($origin) {
379                $file_content .= '// Fichier source, a modifier dans ' . $origin;
380        }
381        else {
382                $url_trad_module = parametre_url($url_trad_module, 'lang_cible', $lang, '&');
383                $file_content .= '// extrait automatiquement de ' . $url_trad_module . '
384// ** ne pas modifier le fichier **
385';
386        }
387
388        // historiquement les fichiers de lang de spip_loader ne peuvent pas etre securises
389        if ($module !== 'tradloader') {
390                $file_content .= "\nif (!defined('_ECRIRE_INC_VERSION')) {
391        return;
392}\n\n";
393        }
394
395        # supprimer la virgule du dernier item
396        $php_lines[count($php_lines)-1] = preg_replace('/,([^,]*)$/', '\1', $php_lines[count($php_lines)-1]);
397
398        $file_content .=
399                '$GLOBALS[$GLOBALS[\'idx_lang\']] = array(' . "\n"
400                . implode("\n", $php_lines)
401          . "\n);\n";
402        file_put_contents($file_name, $file_content);
403        return $file_name;
404}
405
406
407/**
408 * Lire la date de derniere modif d'un fichier de langue
409 * @param string $file_name
410 * @param array $source
411 * @param string $dir_depots
412 * @return false|int
413 */
414function salvatore_read_lastmodified_file($file_name, $source, $dir_depots) {
415
416        $file_path_relative = $file_name;
417        if ($source['dir']) {
418                $file_path_relative = $source['dir'] . DIRECTORY_SEPARATOR . $file_path_relative;
419        }
420
421        $vcs_lastmodified_file = salvatore_vcs_function($source['methode'],  "lastmodified_file");
422        return $vcs_lastmodified_file($dir_depots . $source['dir_checkout'], $file_path_relative);
423}
424
425
426/**
427 * Afficher le status des fichiers modifies pour un module
428 * @param string $module
429 * @param array $source
430 * @param $dir_depots
431 * @return string
432 */
433function salvatore_read_status_modif($module, $source, $dir_depots) {
434        $pre = "";
435        if ($source['dir']) {
436                $pre = $source['dir'] . DIRECTORY_SEPARATOR;
437        }
438        $files_list = [$pre . $module . '_*', $pre . $module . '.xml'];
439
440        $vcs_status_file = salvatore_vcs_function($source['methode'],  "status_file");
441        return $vcs_status_file($dir_depots . $source['dir_checkout'], $files_list);
442}
Note: See TracBrowser for help on using the repository browser.