[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / plugins / odt2spip_32 / inc / odt2spip.php
1 <?php
2
3 if (!defined('_ECRIRE_INC_VERSION')) {
4 return;
5 }
6
7 /**
8 * Indique si la commande 'libreoffice' est disponible sur ce serveur
9 * @return bool
10 */
11 function odt2spip_commande_libreoffice_disponible() {
12 static $est_disponible = null;
13 if (is_null($est_disponible)) {
14 if (defined('_LIBREOFFICE_PATH') and _LIBREOFFICE_PATH) {
15 $est_disponible = true;
16 } else {
17 $est_disponible = (bool)odt2spip_obtenir_commande_serveur('libreoffice');
18 }
19 }
20 return $est_disponible;
21 }
22
23 /**
24 * Obtient le chemin d'un executable sur le serveur.
25 *
26 * @param string $command
27 * Nom de la commande
28 * @return string
29 * Chemin de la commande
30 **/
31 function odt2spip_obtenir_commande_serveur($command) {
32 static $commands = array();
33
34 if (array_key_exists($command, $commands)) {
35 return $commands[$command];
36 }
37
38 exec("which $command", $output, $err);
39 if (!$err and count($output) and $cmd = trim($output[0])) {
40 spip_log("Commande '$command' trouvée dans $cmd", 'odtspip.' . _LOG_DEBUG);
41 return $commands[$command] = $cmd;
42 }
43
44 spip_log("Commande '$command' introuvable sur ce serveur…", 'odtspip.' . _LOG_DEBUG);
45 return $commands[$command] = '';
46 }
47
48
49 /**
50 * Indique si une clé est autorisée à utiliser ce site comme
51 * serveur de conversion
52 */
53 function odt2spip_cle_autorisee($key) {
54 include_spip('inc/config');
55 // récupérer la liste des clés
56 $keys = lire_config('odt2spip/authorized_keys');
57 $keys = explode("\n", trim($keys));
58 $keys = array_filter(array_map('trim', $keys));
59 $liste = array();
60 foreach ($keys as $line) {
61 list($k, $nom) = explode(':', $line, 2);
62 $liste[trim($k)] = trim($nom);
63 }
64 // tester si la clé est correcte
65 $ok = in_array($key, array_keys($liste));
66 if ($ok) {
67 spip_log('Cle autorisée du site : ' . $liste[$key], 'odtspip.' . _LOG_INFO);
68 } else {
69 spip_log('Cle invalide utilisée : ' . $key, 'odtspip.' . _LOG_INFO);
70 }
71 // maintenir un temps fixe d’exécution, si possible
72 if (function_exists('hash_equals')) {
73 hash_equals($key, $key);
74 }
75 return $ok;
76 }
77
78 /**
79 * Indique si un convertisseur de document est disponible
80 *
81 * C’est disponible si
82 * - la commande libreoffice est disponible
83 * - OU un serveur de conversion est indiqué
84 *
85 * @return bool
86 */
87 function odt2spip_convertisseur_disponible() {
88 static $est_disponible = null;
89 if (is_null($est_disponible)) {
90 include_spip('inc/config');
91 if (odt2spip_commande_libreoffice_disponible()) {
92 $est_disponible = true;
93 } elseif (
94 function_exists('curl_file_create') // php 5.5+
95 and lire_config('odt2spip/serveur_api_url')
96 and lire_config('odt2spip/serveur_api_cle')
97 ) {
98 $est_disponible = true;
99 } else {
100 $est_disponible = false;
101 }
102 }
103 return $est_disponible;
104 }
105
106 /**
107 * Retourne la liste des extensions de documents acceptées
108 * @param bool $accept True pour retourner au format 'accept' d’html5
109 * @return string|string[]
110 */
111 function odt2spip_liste_extensions_acceptees($accept = false) {
112 if (odt2spip_convertisseur_disponible()) {
113 // TODO: vérifier la liste des extensions possibles
114 $liste = array('odt', 'doc', 'docx', 'html');
115 } else {
116 $liste = array('odt');
117 }
118 if ($accept) {
119 return '.' . implode(',.', $liste);
120 }
121 return $liste;
122 }
123
124
125 /**
126 * Retourne le répertoire de stockage des documents à traiter
127 * @return string
128 * @throws \Exception
129 */
130 function odt2spip_get_repertoire_temporaire() {
131 // ss-rep temporaire specifique de l'auteur en cours: tmp/odt2spip/id_auteur/
132 // => le créer s'il n'existe pas
133 $base_dezip = _DIR_TMP . 'odt2spip/'; // avec / final
134 if (!is_dir($base_dezip) and !sous_repertoire(_DIR_TMP, 'odt2spip')) {
135 throw new \Exception(_T('odtspip:err_repertoire_tmp'));
136 }
137
138 include_spip('inc/session');
139 $id_auteur = (int)session_get('id_auteur');
140 $rep_dezip = $base_dezip . $id_auteur . '/';
141
142 if (!is_dir($rep_dezip) and !sous_repertoire($base_dezip, $id_auteur)) {
143 throw new \Exception(_T('odtspip:err_repertoire_tmp'));
144 }
145
146 // $rep_pictures = $rep_dezip.'Pictures/';
147 return $rep_dezip;
148 }
149
150 /**
151 * Déplace un fichier posté dans un répertoire temporaire de travail
152 * @return string
153 * @throws \Exception
154 */
155 function odt2spip_deplacer_fichier_upload($key) {
156 $rep_dezip = odt2spip_get_repertoire_temporaire();
157
158 // traitement d'un fichier envoyé par $_POST
159 if (
160 empty($_FILES[$key]['name'])
161 or $_FILES[$key]['error'] != 0
162 or !($fichier = $rep_dezip . addslashes($_FILES[$key]['name']))
163 ) {
164 throw new \Exception(_T('odtspip:err_telechargement_fichier'));
165 }
166
167 include_spip('inc/documents');
168 if (!deplacer_fichier_upload($_FILES[$key]['tmp_name'], $fichier, true)) {
169 throw new \Exception(_T('odtspip:err_telechargement_fichier'));
170 }
171
172 return $fichier;
173 }
174
175 /**
176 * Dézippe un fichier dans le répertoire temporaire d’odt2spip
177 * @param string $fichier Chemin du fichier ODT
178 * @return bool
179 * @throws \Exception
180 */
181 function odt2spip_deziper_fichier($fichier) {
182 $rep_dezip = odt2spip_get_repertoire_temporaire();
183
184 // dezipper le fichier odt a la mode SPIP
185 include_spip('inc/pclzip');
186 $zip = new \PclZip($fichier);
187 $ok = $zip->extract(
188 PCLZIP_OPT_PATH,
189 $rep_dezip,
190 PCLZIP_OPT_SET_CHMOD,
191 _SPIP_CHMOD,
192 PCLZIP_OPT_REPLACE_NEWER
193 );
194
195 if ($zip->error_code < 0) {
196 spip_log('charger_decompresser erreur zip ' . $zip->error_code . ' pour fichier ' . $fichier, 'odtspip.' . _LOG_ERREUR);
197 throw new \Exception($zip->errorName(true));
198 }
199
200 return ($ok > 0);
201 }
202
203 /**
204 * Intègre le contenu du fichier dans l’objet indiqué (ou un nouvel enfant)
205 *
206 * @param string $fichier
207 * @param string $objet
208 * @param int $id_objet
209 * @param string $objet_dest Nouvel objet enfant, si indiqué
210 * @param array $options {
211 * @var bool attacher_fichier
212 * }
213 * @return array {
214 * @var bool|int $id_objet ou fales,
215 * @var string|null $errors,
216 * }
217 */
218 function odt2spip_integrer_fichier($fichier, $objet, $id_objet, $objet_dest = '', $options = array()) {
219 list($champs, $erreurs) = odt2spip_analyser_fichier($fichier);
220 if ($erreurs) {
221 return array(false, $erreurs);
222 }
223 // si necessaire créer l'objet
224 if ($objet_dest) {
225 include_spip('action/editer_objet');
226 $id_objet = objet_inserer($objet_dest, $id_objet);
227 $objet = $objet_dest;
228 if (!$id_objet) {
229 return array(false, _T('odtspip:err_creer_nouvel_objet'));
230 }
231 }
232
233 odt2spip_objet_modifier($fichier, $objet, $id_objet, $champs, $options);
234
235 // vider le contenu du rep de dezippage
236 include_spip('inc/getdocument');
237 effacer_repertoire_temporaire(odt2spip_get_repertoire_temporaire());
238
239 // identifiant d’objet créé éventuellement.
240 return array($id_objet, null);
241 }
242
243 /**
244 * Analyse le fichier ODT transmis
245 * @param string $fichier Chemin vers le fichier ODT
246 * @return array
247 */
248 function odt2spip_analyser_fichier($fichier) {
249 try {
250 if (!odt2spip_deziper_fichier($fichier)) {
251 return array(false, _T('odtspip:err_decompresser_fichier'));
252 }
253 } catch (\Exception $e) {
254 return array(false, _T('odtspip:err_decompresser_fichier'));
255 }
256
257 try {
258 $rep_dezip = odt2spip_get_repertoire_temporaire();
259 } catch (\Exception $e) {
260 return array(false, _T('odtspip:err_repertoire_temporaire'));
261 }
262
263 // Création de l'array avec les parametres de l'article:
264 // c'est ici que le gros de l'affaire se passe!
265 $odt2spip_generer_sortie = charger_fonction('odt2spip_generer_sortie', 'inc');
266 try {
267 $champs = $odt2spip_generer_sortie($rep_dezip, $fichier);
268 } catch (\Exception $e) {
269 spip_log($e->getMessage(), 'odtspip.' . _LOG_ERREUR);
270 return array(false, _T('odtspip:err_analyse_odt'));
271 }
272
273 return array($champs, null);
274 }
275
276 /**
277 * Modifie le contenu d’un objet avec les champs indiqués
278 *
279 * Note qu’une clé contient la liste des images.
280 *
281 * @param string $fichier
282 * @param string $objet
283 * @param int $id_objet
284 * @param array $set
285 * @param array $options
286 * @return bool
287 */
288 function odt2spip_objet_modifier($fichier, $objet, $id_objet, $set, $options = array()) {
289
290 // le remplir
291 include_spip('action/editer_objet');
292 objet_modifier($objet, $id_objet, $set);
293
294 // si necessaire recup les id_doc des images associées et les lier à l'article
295 if (!empty($set['Timages']) > 0) {
296 foreach ($set['Timages'] as $id_img) {
297 $champs = array(
298 'parents' => array($objet . '|' . $id_objet),
299 'statut' => 'publie'
300 );
301 document_modifier($id_img, $champs);
302 }
303 }
304
305 // si nécessaire attacher le fichier source à l'article
306 if (!empty($options['attacher_fichier']) and !empty($options['fichier_source'])) {
307 odt2spip_objet_lier_fichier($options['fichier_source'], $objet, $id_objet, $set['titre']);
308 }
309
310 // si nécessaire attacher le fichier odt généré à l'article
311 if (
312 !empty($options['attacher_fichier_odt']) and !empty($options['fichier_source'])
313 and ($fichier != $options['fichier_source'] or !empty($options['attacher_fichier']))
314 ) {
315 odt2spip_objet_lier_fichier($fichier, $objet, $id_objet, $set['titre']);
316 }
317
318 return true;
319 }
320
321 /**
322 * Lie un fichier en tant que document d’un objet.
323 *
324 * @param string $fichier
325 * @param string $objet
326 * @param int $id_objet
327 * @param string $titre
328 */
329 function odt2spip_objet_lier_fichier($fichier, $objet, $id_objet, $titre) {
330 $ajouter_documents = charger_fonction('ajouter_documents', 'action');
331 $id_document = $ajouter_documents(
332 'new',
333 array(
334 array(
335 'tmp_name' => $fichier,
336 'name' => basename($fichier),
337 'titrer' => 0,
338 'distant' => 0,
339 'type' => 'document'
340 ),
341 ),
342 $objet,
343 $id_objet,
344 'document'
345 );
346 if (
347 $id_document
348 and $id_doc_odt = intval($id_document[0])
349 and $id_doc_odt == $id_document[0]
350 ) {
351 $c = array(
352 'titre' => $titre,
353 'descriptif' => _T('odtspip:cet_article_version_odt'),
354 'statut' => 'publie'
355 );
356 document_modifier($id_doc_odt, $c);
357 }
358 }
359
360 /**
361 * Convertir un fichier vers le format odt en utilisant
362 * un outil de conversion, local ou distant
363 *
364 * @param string $fichier_source
365 * @return string|bool
366 */
367 function odt2spip_convertir_fichier($fichier_source) {
368 if (!odt2spip_convertisseur_disponible()) {
369 return false;
370 }
371 if (odt2spip_commande_libreoffice_disponible()) {
372 include_spip('inc/convertir_avec_libreoffice');
373 $fichier = convertir_avec_libreoffice($fichier_source, 'odt');
374 return $fichier;
375 }
376 if ($fichier = odt2spip_convertir_fichier_par_api($fichier_source)) {
377 return $fichier;
378 }
379 return false;
380 }
381
382 /**
383 * Convertir un fichier vers le format odt en utilisant
384 * un serveur distant de conversion
385 *
386 * @param string $fichier_source
387 * @return string|bool
388 */
389 function odt2spip_convertir_fichier_par_api($fichier_source, $format = 'odt') {
390 include_spip('inc/config');
391 $api_url = lire_config('odt2spip/serveur_api_url');
392 $api_key = lire_config('odt2spip/serveur_api_cle');
393 if (!$api_url or !$api_key) {
394 return false;
395 }
396 $api_url = rtrim($api_url, '/') . '/convert_to.api/' . $format;
397
398 $post = array(
399 'api_key' => $api_key,
400 'file'=> curl_file_create(realpath($fichier_source))
401 );
402
403 // Poster la requête et récupérer le contenu du fichier
404 // FIXME: idéalement il faudrait streamer le fichier retourné… mais comment ?
405 $ch = curl_init();
406 curl_setopt($ch, CURLOPT_URL, $api_url);
407 curl_setopt($ch, CURLOPT_POST, 1);
408 curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
409 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
410 curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
411 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
412 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
413 $content = curl_exec($ch);
414 curl_close($ch);
415
416 // Écrire le nouveau fichier localement
417 if ($content) {
418 $fichier = dirname($fichier_source) . DIRECTORY_SEPARATOR . pathinfo($fichier_source, PATHINFO_FILENAME) . '.' . $format;
419 if (file_put_contents($fichier, $content)) {
420 spip_log('Fichier converti dans : ' . $fichier, 'odtspip.' . _LOG_DEBUG);
421 return $fichier;
422 }
423 }
424
425 return false;
426 }