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

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

Reorganisation : les fonctions vcs de salvatore dans salvatore/vcs/

  • Property svn:eol-style set to native
File size: 17.7 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        $salvatore_lastmodified_file = "salvatore_" . $source['methode'] . "_lastmodified_file";
422        return $salvatore_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        $salvatore_status_file = "salvatore_" . $source['methode'] . "_status_file";
441        return $salvatore_status_file($dir_depots . $source['dir_checkout'], $files_list);
442}
443
444/*
445if ($row_module['limite_trad']==0){
446        foreach ($liste_lang as $lang){
447                passthru('svn add ' . _DIR_SALVATORE_TMP . $module . '/' . $module . "_$lang.php* 2> /dev/null") ? salvatore_log("$log\n") : '';
448        }
449} elseif (!in_array($module, array('ecrire', 'spip', 'public'))) {
450        salvatore_log('Limite trad = ' . $seuil_export);
451        foreach ($liste_lang as $lang){
452                if ((intval(($xml_infos[$lang]['traduits']/$count_trad_reference)*100)>$seuil_export)
453                        and (substr(exec('svn status ' . _DIR_SALVATORE_TMP . $module . '/' . $module . "_$lang.php"), 0, 1)=='?')){
454                        passthru('svn add ' . _DIR_SALVATORE_TMP . $module . '/' . $module . "_$lang.php* 2> /dev/null") ? salvatore_log("$log\n") : '';
455                }
456        }
457}
458*/
459
460/*
461
462                if (substr(exec('svn status ' . _DIR_SALVATORE_TMP . $module . '/' . $module . '_' . $lang . '.php'), 0, 1)=='?'){
463                        if ($row_module['limite_trad']==0){
464                                passthru('svn add ' . _DIR_SALVATORE_TMP . $module . '/' . $module . "_$lang.php 2> /dev/null") ? salvatore_log("$log\n") : '';
465                        } elseif (!in_array($module, array('ecrire', 'spip', 'public'))) {
466                                if ((intval(($xml_infos[$lang]['traduits']/$count_trad_reference)*100)>$seuil_export)){
467                                        passthru('svn add ' . _DIR_SALVATORE_TMP . $module . '/' . $module . "_$lang.php* 2> /dev/null") ? salvatore_log("$log\n") : '';
468                                }
469                        }
470                }
471
472
473 */
Note: See TracBrowser for help on using the repository browser.