[PLUGINS] +clavettes et dependances
[lhc/web/clavette_www.git] / www / plugins / ressource / inc / ressource.php
1 <?php
2
3 /*
4 * transforme un raccourci de ressource en un joli html a embed
5 *
6 *
7 */
8
9 define('_EXTRAIRE_RESSOURCES', ',' . '<"?(https?://|[\w -]+\.[\w -]+).*>'.',UimsS');
10 define('_RESSOURCE_VIGNETTE_LARGEUR_DEFAUT','small');
11 define('_RESSOURCE_IMAGE_LARGEUR_DEFAUT', 'large');
12
13 /* pipeline pour typo */
14 function ressource_post_typo($t) {
15 if (strpos($t, '<') !== false) {
16 $t = preg_replace_callback(_EXTRAIRE_RESSOURCES, 'traiter_ressources', $t);
17 }
18 return $t;
19 }
20
21 /* pipeline pour propre */
22 function ressource_pre_liens($t) {
23 if (strpos($t, '<') !== false) {
24 $t = preg_replace_callback(_EXTRAIRE_RESSOURCES, 'traiter_ressources', $t);
25
26 // echapper les autoliens eventuellement inseres (en une seule fois)
27 if (strpos($t,"<html>")!==false)
28 $t = echappe_html($t);
29 }
30 return $t;
31 }
32
33 function traiter_ressources($r) {
34 if ($ressource = charger_fonction('ressource', 'inc', true))
35 $html = $ressource($r[0]);
36 else
37 $html = htmlspecialchars($r[0]);
38
39 return '<html>'.$html.'</html>';
40 }
41
42 function inc_ressource_dist($r) {
43 // $r contient tout le texte définissant la ressource :
44 // <fichier.rtf option1 option2...>
45
46 // 1. phraser le raccourci
47 $attrs = phraser_tag('<res src='.substr($r,1));
48
49 # debug :)
50 $attrs['debug'] = $r;
51
52 // 2. keywords : right => align=right, etc
53 foreach(array(
54 'right' => 'align',
55 'left' => 'align',
56 'center' => 'align',
57 ) as $k => $v) {
58 if ($attrs[$k] == $k) {
59 $attrs[$v] = $k;
60 unset($attrs[$k]);
61 }
62 }
63
64 // 2. constituer les meta-donnees associees a $res[src]
65 $meta = ressource_meta($attrs);
66
67 // 4. traiter les parametres d'image / logo / vignette / resize
68 // supprimera le href si necessaire
69 $image = ressource_image($attrs, $meta);
70
71 $final = array_merge($meta, $attrs);
72
73 // renvoyer le html final
74 $final = array_merge($final, $image);
75
76 $html = embed_ressource($final);
77 return $html;
78 }
79
80 function ressource_meta($res) {
81 $meta = $res;
82
83 // on va beaucoup travailler avec l'attribut src
84 $src = $res['src'];
85
86 // identifier la ressource
87 // s'agit-il d'un fichier decrit dans la mediathèque,
88 // d'un fichier local, d'un oembed, d'un doc distant connu, etc ?
89
90 // ressource fichier.rtf => rtf/fichier.rtf
91 if (preg_match(',^[^/]+\.([^.]+)$,', $src, $r))
92 $fichier = $r[1].'/'.$r[0];
93 else
94 $fichier = $src;
95
96 // determiner temporairement l'extension de la ressource (ca pourra changer
97 // si on en fait une copie locale et qu'elle indique un autre type mime)
98 if (preg_match(',\.(\w+)([?#].*)?$,S', $src, $r)) {
99 $meta['extension'] = strtolower($r[1]);
100
101 if ($meta['extension'] == 'jpeg')
102 $meta['extension'] = 'jpg';
103 }
104
105 # d'abord fouiller la mediatheque
106 include_spip('base/abstract_sql');
107 if ($s = sql_fetsel('*', 'spip_documents', 'fichier='.sql_quote($fichier))) {
108 $meta = $s;
109 $meta['href'] = get_spip_doc($s['fichier']);
110 $meta['local'] = copie_locale($meta['href'], 'test');
111 }
112 else
113 if (preg_match(',^https?://,', $src)) {
114 $meta['href'] = $src;
115
116 /* pipeline ! */
117 /* exemple : traitement par autoembed */
118 include_spip('autoembed/autoembed');
119 if (function_exists('embed_url')
120 AND $u = embed_url($src)) {
121 $meta['embed'] = $u;
122 }
123
124 /* autre exemple de traitement avec oembed */
125 include_spip('oembed_fonctions');
126 if (function_exists('oembed')
127 AND $u = oembed($src)
128 AND $u != $src)
129 {
130 $meta['embed'] = $u;
131 }
132
133 /* recuperer un album flickr */
134 if (preg_match(',^https?://(www\.)?flickr\.com/.*/sets/(\d+),', $src, $r)) {
135 $meta['album'] = $r[2];
136 if ($html = recuperer_fond('modeles/album_flickr', $meta)) {
137 $meta['embed'] = $html;
138 }
139 }
140
141 $meta = pipeline('ressource_meta',
142 array(
143 'args' => $res,
144 'data' => $meta
145 )
146 );
147
148 /* chargement distant */
149 if (!isset($meta['html'])) {
150 include_spip('inc/distant');
151 if (!$local = copie_locale($src, 'test')
152 AND !in_array($meta['extension'], array('mp3'))
153 ) {
154 include_spip('inc/queue');
155 queue_add_job('copie_locale', 'copier', array($src), $file = 'inc/distant', $no_duplicate = true, $time=0, $priority=0);
156 }
157 if ($local = copie_locale($src, 'test')) {
158 $meta['local'] = $local;
159 }
160 }
161
162 }
163 // fichier dans IMG/ ?
164 else if (preg_match(',^[^/]+\.([^.]+)$,', $src, $r)
165 AND $h = _DIR_IMG.$r[1].'/'.$r[0]
166 AND @file_exists($h)
167 ) {
168 $meta['local'] = $h;
169 $meta['href'] = $h;
170 }
171
172 ##### tests pour le plugin OPUS :
173 ##### <image.jpg> correspond à opus/[article]/image.jpg
174 else if ($r
175 AND isset($GLOBALS['contexte'])
176 AND isset($GLOBALS['contexte']['id_article'])
177 AND $q = sql_fetsel('url_site', 'spip_articles', 'id_article='.sql_quote($GLOBALS['contexte']['id_article']))
178 AND $opus = $q['url_site']
179 AND $h = _DIR_RACINE.$opus.'/'.$r[0]
180 AND file_exists($h)) {
181 $meta['local'] = $h;
182 $meta['href'] = $h;
183 $meta['logodocument'] = $h;
184 }
185 ####
186
187 // si on l'a, renseigner ce qu'on peut dire du fichier
188 if (isset($meta['local'])
189 AND @file_exists($meta['local'])) {
190 $meta['extension'] = strtolower(preg_replace(',^.*\.,', '', $meta['local']));
191 $meta['taille'] = @filesize($meta['local']);
192 if ($r = getimagesize($meta['local'])) {
193 // donnees brutes du fichier
194 $meta['width'] = $r[0];
195 $meta['height'] = $r[1];
196 $meta['largeur'] = $meta['width'];
197 $meta['hauteur'] = $meta['height'];
198 }
199
200 if ($meta['extension'] == 'html') {
201 // choper ce qu'on peut du html
202 ressource_html($meta);
203 }
204
205 // extraire ses donnees !
206 if (!isset($meta['extract'])
207 AND $u = ressource_extract($meta)) {
208 $meta['fullextract'] = $u;
209 $meta['extract'] = propre(couper($u, 500));
210 }
211 }
212
213 // recupere le type mime de la ressource
214 if (isset($meta['extension']))
215 $meta['type_document'] = ressource_mime($meta['extension']);
216
217 return $meta;
218 }
219
220 // choper les trucs du genre meta opengraph ; meta description etc
221 function ressource_html(&$meta) {
222
223 include_spip('fonctionsale');
224 if (function_exists('sale')) {
225 $u = sale(spip_file_get_contents($meta['local']));
226
227 $meta['fullextract'] = $u;
228 $meta['extract'] = propre(couper($u, 500));
229 }
230 }
231
232 function ressource_mime($e) {
233 global $tables_images, $tables_sequences, $tables_documents, $tables_mime, $mime_alias;
234 include_spip('base/typedoc');
235
236
237 $mime = $tables_mime[$e];
238 if (!$t = $tables_documents[$e]
239 AND !$t = $tables_images[$e]
240 AND !$t = $tables_sequences[$e])
241 $t = $e;
242
243 return $t;
244
245 }
246
247 /*
248 * recoit une chaine
249 * renvoie un array
250 * les valeurs par defaut sont mappees
251 * inspire de http://w-shadow.com/blog/2009/10/20/how-to-extract-html-tags-and-their-attributes-with-php/
252 */
253 function phraser_tag($rr) {
254 $attribute_pattern =
255 '@
256 (
257 (?P<name>\w+) # attribute name
258 \s*=\s*
259 (
260 (?P<quote>[\"\'])(?P<value_quoted>.*?)(?P=quote) # a quoted value
261 | # or
262 (?P<value_unquoted>[^\s"\']+?)(?:\s+) # an unquoted value
263 )
264 |(?P<auto>\w+)
265 )
266 @xsiS';
267
268 // d'abord eliminer le type du tag et l'evntuelle fermeture auto
269 $res = array();
270 $rr = preg_replace(',^<\w+\s+,S', '', $rr);
271 $rr = preg_replace(',\s*/?'.'>$,S', ' ', $rr);
272
273 // ensuite parser le reste des attributs
274 preg_match_all($attribute_pattern, $rr, $z, PREG_SET_ORDER);
275
276 foreach($z as $t) {
277 if (isset($t['auto'])) {
278 if (is_numeric($t['auto'])) # 200
279 $res['width'] = $t['auto'];
280 elseif (preg_match(',^\d+x\d+$,', $t['auto'])) # 200x300
281 $res['geometry'] = $t['auto'];
282 else
283 $res[$t['auto']] = $t['auto'];
284 }
285 elseif (isset($t['value_unquoted'])) {
286 $res[$t['name']] = $t['value_unquoted'];
287 }
288 elseif (isset($t['value_quoted'])) {
289 $res[$t['name']] = $t['value_quoted'];
290 }
291 }
292
293 return $res;
294 }
295
296 function embed_ressource($res) {
297 // si la ressource a un embed, charger le modele ressource_embed
298 // qui l'encapsule dans un <figure>, ajoute eventuellement une légende etc.
299 if (isset($res['embed'])) {
300 if (!isset($res['width'])
301 AND $w = extraire_attribut($res['embed'], 'width'))
302 $res['width'] = $w;
303 if (!isset($res['height'])
304 AND $h = extraire_attribut($res['embed'], 'height'))
305 $res['height'] = $h;
306 return recuperer_fond('modeles/ressource_embed', $res);
307 }
308
309 // si la ressource est un document, renvoyer <doc1>
310 if (isset($res['id_document'])) {
311 # return recuperer_fond('modeles/doc', $res);
312 }
313
314 if ($res['type_document'] == 'PDF') {
315 return
316 recuperer_fond('modeles/application', $res);
317 }
318
319 return
320 # "<pre>".var_export($res,true)."</pre>" .
321 recuperer_fond('modeles/ressource', $res);
322 }
323
324 /* ici c'est flou… */
325 function ressource_image($attrs, $meta) {
326 $image = array();
327
328 // creer une vignette pour le doc ; si une largeur est exigee,
329 // adapter la taille.
330 if ($attrs['largeur'] OR $attrs['hauteur']) {
331 $resize = true;
332 }
333 // size
334 else {
335 if (!$attrs['size']) {
336 if ($attrs['image']) # ???? c'est quoi ? le mode ?
337 $attrs['size'] = _RESSOURCE_VIGNETTE_LARGEUR_DEFAUT;
338 else
339 $attrs['size'] = _RESSOURCE_IMAGE_LARGEUR_DEFAUT;
340 }
341
342 if (in_array($meta['extension'], array('gif', 'png', 'jpg'))
343 AND strlen($b = image_stdsize($meta, $attrs))) {
344 $a = $b;
345 $resize = true;
346 }
347 }
348
349 // Verifier d'abord si le parametre 'icon' force l'icon
350 # todo : icone => icon
351 if ($attrs['icon']) {
352 $f = charger_fonction('vignette','inc');
353 $img = $f($meta['extension'], false);
354 if ($resize)
355 $a = image_reduire($img, $attrs['largeur'] ? $attrs['largeur'] : -1, $attrs['hauteur'] ? $attrs['hauteur'] : -1);
356 else
357 $a = '<img src="'.$img.'" />';
358
359 }
360 // methode normale : reduire l'image si possible, sinon icon
361 else {
362 if (!$a) {
363 $w = sinon($attrs['largeur'],500);
364 $h = sinon($attrs['hauteur'],700);
365 $a = vignette_automatique($meta['id_vignette'], $meta,
366 '' /*url*/, $w, $h, null /* align */);
367 }
368 }
369
370 if ($a) $image['logodocument'] = $a;
371
372 // experimental : DEST
373 // TODO: parametre à mieux nommer ?
374 // parametre |dest=800 pour reduire l'image LIEE a 800px max
375 if ($attrs['dest']) {
376 $tmp = image_reduire($meta['local'], $attrs['dest']);
377 if ($tmp = extraire_attribut($tmp, 'src'))
378 $image['href'] = $tmp;
379 }
380
381 return $image;
382 }
383
384
385
386
387 # s t m d z b o
388 function image_stdsize($meta, $attrs) {
389 include_spip('inc/filtres_images');
390
391 $s = $attrs['size'];
392 if (isset($meta['local']))
393 $img = $meta['local'];
394 else
395 $img = $attrs['src'];
396
397 # intercepter les URLs flickr pour choper les jolies reductions
398 if (preg_match(',^(http://farm.*.staticflickr.com/(\d+/[0-9a-z_]+?))(_[zbo])?\.jpg$,', $img, $r)) {
399 if (in_array($s, array('s', 't', 'm', 'z', 'b') )){
400 $img = $r[1].'_'.$s.'.jpg';
401 return '<img src="'.$img.'" />';
402 }
403 if (in_array($s, array('d'))) {
404 $img = $r[1].'.jpg';
405 return '<img src="'.$img.'" />';
406 }
407 }
408 elseif (preg_match(',^(http://farm.*.staticflickr.com/(\d+/[0-9a-z_]+?))(_[k])?\.jpg$,', $img, $r)) {
409 if (in_array($s, array('k') )){
410 $img = $r[1].'_'.$s.'.jpg';
411 return '<img src="'.$img.'" />';
412 }
413 if (in_array($s, array('d'))) {
414 $img = $r[1].'.jpg';
415 return '<img src="'.$img.'" />';
416 }
417 }
418 elseif (preg_match(',^(http://farm.*.staticflickr.com/(\d+/[0-9a-z_]+?))(_[h])?\.jpg$,', $img, $r)) {
419 if (in_array($s, array('h') )){
420 $img = $r[1].'_'.$s.'.jpg';
421 return '<img src="'.$img.'" />';
422 }
423 if (in_array($s, array('d'))) {
424 $img = $r[1].'.jpg';
425 return '<img src="'.$img.'" />';
426 }
427 }
428
429 // IMG/jpg/truc.jpg depuis l'espace prive
430 if (_DIR_RACINE
431 AND substr(_DIR_RACINE.$img, 0, strlen(_DIR_IMG)) == _DIR_IMG)
432 $img = _DIR_RACINE.$img;
433
434 if (!is_numeric($s)) {
435 switch($s) {
436 case 'sq':
437 case 'square':
438 # la c'est dur
439 $d = 75;
440 $img = image_passe_partout($img, $d, $d);
441 $img = image_recadre($img, $d, $d);
442 break;
443 case 't':
444 case 'thumb':
445 case 'thumbnail':
446 $a = 100;
447 break;
448 case 'm':
449 case 's':
450 case 'small':
451 $a = 240;
452 break;
453 case 'z':
454 case 'medium640':
455 $a = 640;
456 break;
457 case 'b':
458 case 'large':
459 $a = 1024;
460 break;
461 // xl = 'k' chez flickr (2048px)
462 case 'k':
463 case 'xl':
464 case 'extra':
465 case 'extralarge':
466 $a = 2048;
467 break;
468 case 'o':
469 case 'original':
470 $a = 10000000;
471 break;
472 case '-':
473 case '':
474 case 'd':
475 case 'default':
476 default:
477 $a = 500;
478 break;
479 }
480 }
481
482 if ($a)
483 $img = image_reduire($img, $a);
484 else if (is_numeric($s))
485 $img = image_reduire($img, $s);
486
487 return $img;
488 }
489
490
491
492 function ressource_extract($meta) {
493 /*
494
495 global $extracteur;
496
497 $extension = $meta['extension'];
498
499 include_spip('extract/'.$extension);
500 if (function_exists($lire = $extracteur[$extension])) {
501 $charset = 'iso-8859-1';
502 $contenu = $lire($meta['local'], $charset);
503 var_dump($lire, $contenu);
504 }
505 */
506
507 switch($meta['extension']) {
508 case 'html':
509 case 'doc':
510 case 'docx':
511 case 'rtf':
512 case 'odt':
513 $conv = converthtml($meta['local'], $err);
514 include_spip('fonctionsale');
515 if (function_exists('sale')) {
516 $conv = sale($conv);
517 }
518 break;
519 default:
520 break;
521 }
522
523 return $conv;
524 }
525
526 /**
527 * Multiple Curl Handlers
528 * @author Jorge Hebrard ( jorge.hebrard@gmail.com )
529 **/
530 class curlNode{
531 static private $listenerList;
532 private $callback;
533 public function __construct($url){
534 $new =& self::$listenerList[];
535 $new['url'] = $url;
536 $this->callback =& $new;
537 }
538 /**
539 * Callbacks needs 3 parameters: $url, $html (data of the url), and $lag (execution time)
540 **/
541 public function addListener($callback){
542 $this->callback['callback'] = $callback;
543 }
544 /**
545 * curl_setopt() wrapper. Enjoy!
546 **/
547 public function setOpt($key,$value){
548 $this->callback['opt'][$key] = $value;
549 }
550 /**
551 * Request all the created curlNode objects, and invoke associated callbacks.
552 **/
553 static public function request(){
554
555 //create the multiple cURL handle
556 $mh = curl_multi_init();
557
558 $running=null;
559
560 # Setup all curl handles
561 # Loop through each created curlNode object.
562 foreach(self::$listenerList as &$listener){
563 $url = $listener['url'];
564 $current =& $ch[];
565
566 # Init curl and set default options.
567 # This can be improved by creating
568 $current = curl_init();
569
570 curl_setopt($current, CURLOPT_URL, $url);
571 # Since we don't want to display multiple pages in a single php file, do we?
572 curl_setopt($current, CURLOPT_HEADER, 0);
573 curl_setopt($current, CURLOPT_RETURNTRANSFER, 1);
574
575 # Set defined options, set through curlNode->setOpt();
576 if (isset($listener['opt'])){
577 foreach($listener['opt'] as $key => $value){
578 curl_setopt($current, $key, $value);
579 }
580 }
581
582 curl_multi_add_handle($mh,$current);
583
584 $listener['handle'] = $current;
585 $listener['start'] = microtime(1);
586 } unset($listener);
587
588 # Main loop execution
589 do {
590 # Exec until there's no more data in this iteration.
591 # This function has a bug, it
592 while(($execrun = curl_multi_exec($mh, $running)) == CURLM_CALL_MULTI_PERFORM);
593 if($execrun != CURLM_OK) break; # This should never happen. Optional line.
594
595 # Get information about the handle that just finished the work.
596 while($done = curl_multi_info_read($mh)) {
597 # Call the associated listener
598 foreach(self::$listenerList as $listener){
599 # Strict compare handles.
600 if ($listener['handle'] === $done['handle']) {
601 # Get content
602 $html = curl_multi_getcontent($done['handle']);
603 # Call the callback.
604 call_user_func($listener['callback'],
605 $listener['url'],
606 $html,(microtime(1)-$listener['start']));
607 # Remove unnecesary handle (optional, script works without it).
608 curl_multi_remove_handle($mh, $done['handle']);
609 }
610 }
611
612 }
613 # Required, or else we would end up with a endless loop.
614 # Without it, even when the connections are over, this script keeps running.
615 if (!$running) break;
616
617 # I don't know what these lines do, but they are required for the script to work.
618 while (($res = curl_multi_select($mh)) === 0);
619 if ($res === false) break; # Select error, should never happen.
620 } while (true);
621
622 # Finish out our script ;)
623 curl_multi_close($mh);
624
625 }
626 }
627
628 function converthtml($f, $err) {
629 define('_CONVERT_URL', 'http://office.rezo.net/office/v1/?email=fil@rezo.net&key=1223649b375bb98e1b57141f96643cd47a3029c3');
630
631 $signature = md5_file($f);
632
633 // 1. a-t-on le fichier en local
634 $html = sous_repertoire(_DIR_TMP,'converthtml').$signature.'.html';
635 if (file_exists($html))
636 return spip_file_get_contents($html);
637
638 // 2. sinon le chercher sur le serveur office.rezo
639 if (!defined('_CONVERT_URL'))
640 return false;
641
642 $url = parametre_url(_CONVERT_URL, 'signature', $signature, '&');
643
644 include_spip('inc/queue');
645 queue_add_job('convert_html_fetch', 'convert_html_fetch('.$f.')', array($url, $f, $html), $file = 'inc/ressource', $no_duplicate = true, $time=0, $priority=0);
646 #convert_html_fetch($url, $f);
647
648 return '';
649 }
650
651 function convert_html_fetch($url, $f, $html=null) {
652 if (!$html) return;
653 include_spip('inc/distant');
654 if ($rep = recuperer_page($url)
655 AND $rep = json_decode($rep)
656 AND isset($rep->content)) {
657 ecrire_fichier($html, $rep->content);
658 return;
659 }
660
661
662 // 3. si 404, l'envoyer au serveur
663 #include_spip('inc/queue');
664 #queue_add_job('convert_html_send', 'convert_html_send('.$f.')', array($url, $f), $file = 'inc/ressource', $no_duplicate = true, $time=0, $priority=0);
665 convert_html_send($url, $f);
666
667 }
668
669 function convert_html_send($url, $f) {
670 include_spip('inc/filtres');
671 spip_log('curl @'.$f.' '.$url.' ('.taille_en_octets(filesize($f)).')');
672 $data = array('file' => '@'.$f);
673 $ch = curl_init();
674 curl_setopt($ch, CURLOPT_VERBOSE, 1);
675 curl_setopt($ch, CURLOPT_URL, $url);
676 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
677 curl_setopt($ch, CURLOPT_POST, true);
678 curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
679 $n = curl_exec($ch);
680 spip_log($n);
681 curl_close($ch);
682 }
683
684
685 function xconverthtml($f, &$err) {
686
687
688 $k = escapeshellarg($f);
689
690 exec("/usr/bin/textutil -convert html -stdout -noload -nostore $k", $ret, $err);
691
692 if ($err) {
693 spip_log($err);
694 } else {
695 $ret = join($ret, "\n");
696 // les notes de bas de page word sont parfois transformees en truc chelou
697 $ret = str_replace('<span class="Apple-converted-space"> </span>', '~', $ret);
698 return nettoyer_utf8($ret);
699 }
700 }
701
702 function nettoyer_utf8($t) {
703 if (!preg_match('!\S!u', $t))
704 $t = preg_replace_callback(',&#x([0-9a-f]+);,i', 'utf8_do', utf8_encode(utf8_decode($t)));
705 return $t;
706 }
707