You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

338 lines
14 KiB

  1. <?php
  2. /**
  3. * Plugin oEmbed
  4. * Licence GPL3
  5. *
  6. */
  7. if (!defined('_ECRIRE_INC_VERSION')) {
  8. return;
  9. }
  10. /**
  11. * Lister les providers connus
  12. * @return array
  13. */
  14. function oembed_lister_providers() {
  15. // liste des providers par defaut
  16. // voir
  17. // http://oembed.com/
  18. // http://code.google.com/p/oohembed/source/browse/app/provider/endpoints.json
  19. // https://github.com/starfishmod/jquery-oembed-all/blob/master/jquery.oembed.js
  20. // https://github.com/panzi/oembedendpoints/blob/master/endpoints-simple.json
  21. // voir aussi http://embed.ly/providers qui donne les scheme mais pas les endpoint
  22. $providers = array(
  23. 'http://*.youtube.com/watch*' => 'https://www.youtube.com/oembed',
  24. 'http://*.youtube.com/playlist*' => 'https://www.youtube.com/oembed',
  25. 'http://youtu.be/*' => 'https://www.youtube.com/oembed',
  26. 'http://*.vimeo.com/*' => 'https://vimeo.com/api/oembed.json',
  27. 'http://vimeo.com/*' => 'https://vimeo.com/api/oembed.json',
  28. 'http://*.dailymotion.com/*' => 'https://www.dailymotion.com/services/oembed',
  29. 'http://dai.ly/*' => 'https://www.dailymotion.com/services/oembed',
  30. 'http://*.flickr.com/*' => 'https://www.flickr.com/services/oembed/',
  31. 'http://flickr.com/*' => 'https://www.flickr.com/services/oembed/',
  32. 'http://flic.kr/*' => 'https://www.flickr.com/services/oembed/',
  33. 'http://soundcloud.com/*' => 'https://soundcloud.com/oembed',
  34. 'http://mixcloud.com/*' => 'https://mixcloud.com/oembed',
  35. 'http://*.soundcloud.com/*' => 'https://soundcloud.com/oembed',
  36. 'http://*.mixcloud.com/*' => 'https://mixcloud.com/oembed',
  37. 'http://*.slideshare.net/*/*' => 'https://www.slideshare.net/api/oembed/2',
  38. 'http://www.slideshare.net/*/*' => 'https://www.slideshare.net/api/oembed/2',
  39. 'http://instagr.am/*' => 'https://api.instagram.com/oembed',
  40. 'http://*.instagr.am/*' => 'https://api.instagram.com/oembed',
  41. 'http://instagram.com/*' => 'https://api.instagram.com/oembed',
  42. 'http://*.instagram.com/*' => 'https://api.instagram.com/oembed',
  43. 'http://huffduffer.com/*/*' => 'http://huffduffer.com/oembed',
  44. 'http://nfb.ca/film/*' => 'http://www.nfb.ca/remote/services/oembed/',
  45. 'http://dotsub.com/view/*' => 'http://dotsub.com/services/oembed',
  46. 'http://clikthrough.com/theater/video/*' => 'http://clikthrough.com/services/oembed',
  47. 'http://kinomap.com/*' => 'http://www.kinomap.com/oembed',
  48. 'http://photobucket.com/albums/*' => 'http://api.photobucket.com/oembed',
  49. 'http://photobucket.com/groups/*' => 'http://api.photobucket.com/oembed',
  50. 'http://*.smugmug.com/*' => 'https://api.smugmug.com/services/oembed/',
  51. 'http://meetup.com/*' => 'https://api.meetup.com/oembed',
  52. 'http://meetup.ps/*' => 'http://api.meetup.com/oembed',
  53. 'http://*.wordpress.com/*' => 'https://public-api.wordpress.com/oembed/1.0/',
  54. 'http://twitter.com/*/status/*' => 'https://publish.twitter.com/oembed',
  55. 'http://twitter.com/*/likes' => 'https://publish.twitter.com/oembed',
  56. 'http://twitter.com/*/lists/*' => 'https://publish.twitter.com/oembed',
  57. 'http://twitter.com/*/timelines/*' => 'https://publish.twitter.com/oembed',
  58. 'http://twitter.com/i/moments/*' => 'https://publish.twitter.com/oembed',
  59. 'http://techcrunch.com/*' => 'http://public-api.wordpress.com/oembed/1.0/',
  60. 'http://wp.me/*' => 'http://public-api.wordpress.com/oembed/1.0/',
  61. 'http://my.opera.com/*' => 'http://my.opera.com/service/oembed',
  62. 'http://www.collegehumor.com/video/*' => 'http://www.collegehumor.com/oembed.json',
  63. 'http://imgur.com/*' => 'http://api.imgur.com/oembed',
  64. 'http://*.imgur.com/*' => 'http://api.imgur.com/oembed',
  65. 'http://*.onf.ca/*' => 'http://www.onf.ca/remote/services/oembed/',
  66. 'http://vine.co/v/*' => 'https://vine.co/oembed.json',
  67. 'http://*.tumblr.com/post/*' => 'https://www.tumblr.com/oembed/1.0',
  68. 'http://*.kickstarter.com/projects/*' => 'https://www.kickstarter.com/services/oembed',
  69. 'http://speakerdeck.com/*' => 'https://speakerdeck.com/oembed.json',
  70. 'http://issuu.com/*/docs/*' => 'https://issuu.com/oembed',
  71. 'http://*.calameo.com/books/*' => 'https://www.calameo.com/services/oembed',
  72. 'http://*.calameo.com/read/*' => 'https://www.calameo.com/services/oembed',
  73. 'http://www.facebook.com/*/posts/*' => 'https://www.facebook.com/plugins/post/oembed.json/',
  74. 'http://www.facebook.com/*/activity/*' => 'https://www.facebook.com/plugins/post/oembed.json/',
  75. 'http://www.facebook.com/*/photos/*' => 'https://www.facebook.com/plugins/post/oembed.json/',
  76. 'http://www.facebook.com/media/*' => 'https://www.facebook.com/plugins/post/oembed.json/',
  77. 'http://www.facebook.com/questions/*' => 'https://www.facebook.com/plugins/post/oembed.json/',
  78. 'http://www.facebook.com/notes/*' => 'https://www.facebook.com/plugins/post/oembed.json/',
  79. 'http://www.facebook.com/*/videos/*' => 'https://www.facebook.com/plugins/video/oembed.json/',
  80. 'http://*.arte.tv/*/videos/*' => 'https://api.arte.tv/api/player/v1/oembed/',
  81. 'http://egliseinfo.catholique.fr/*' => 'http://egliseinfo.catholique.fr/api/oembed',
  82. #'https://gist.github.com/*' => 'http://github.com/api/oembed?format=json'
  83. );
  84. // pipeline pour permettre aux plugins d'ajouter/supprimer/modifier des providers
  85. $providers = pipeline('oembed_lister_providers', $providers);
  86. // merger avec la globale pour perso mes_options dans un site
  87. // pour supprimer un scheme il suffit de le renseigner avec un endpoint vide
  88. if (isset($GLOBALS['oembed_providers'])) {
  89. $providers = array_merge($providers, $GLOBALS['oembed_providers']);
  90. // retirer les providers avec un endpoint vide
  91. $providers = array_filter($providers);
  92. }
  93. return $providers;
  94. }
  95. // Merci WordPress :)
  96. // http://core.trac.wordpress.org/browser/trunk/wp-includes/class-oembed.php
  97. /**
  98. * Récupérer les données oembed d'une url
  99. *
  100. * @param string $url url de la page qui contient le document à récupérer avec oembed
  101. * @param int $maxwidth largeur max du document
  102. * null : la valeur configuree par defaut ou pour le provider est utilisee
  103. * '' : pas de valeur max
  104. * @param int $maxheight hauteur max du document
  105. * null : la valeur configuree par defaut ou pour le provider est utilisee
  106. * '' : pas de valeur max
  107. * @param string $format format à utiliser pour la requete oembed (json ou xml)
  108. * @param string $detecter_lien tenter la détection automatique de lien oembed dans la page indiquée
  109. * @param bool $force_reload forcer le rechargement de l'oembed depuis la source sans utiliser le cache local
  110. * @return bool|array false si aucun retour ou erreur ; tableau des éléménents de la réponse oembed
  111. */
  112. function oembed_recuperer_data($url, $maxwidth = null, $maxheight = null, $format = 'json', $detecter_lien = 'non', $force_reload = false) {
  113. static $cache = array();
  114. $provider = false;
  115. $provider = oembed_verifier_provider($url);
  116. if ((!$provider)
  117. and (($detecter_lien != 'non')
  118. or lire_config('oembed/detecter_lien', 'non') == 'oui')) {
  119. $provider = oembed_detecter_lien($url);
  120. }
  121. if (!$provider) {
  122. return false;
  123. }
  124. $data_url = url_absolue($provider['endpoint'], url_de_base());
  125. // certains oembed fournissent un endpoint qui contient deja l'URL, parfois differente de celle de la page
  126. if (!parametre_url($data_url, 'url')) {
  127. $data_url = parametre_url($data_url, 'url', $url, '&');
  128. }
  129. include_spip('inc/config');
  130. if (!$maxwidth) {
  131. $maxwidth = lire_config('oembed/maxwidth', '600');
  132. }
  133. if (!$maxheight) {
  134. $maxheight = lire_config('oembed/maxheight', '400');
  135. }
  136. $data_url = parametre_url($data_url, 'maxwidth', $maxwidth, '&');
  137. $data_url = parametre_url($data_url, 'maxheight', $maxheight, '&');
  138. $data_url = parametre_url($data_url, 'format', $format, '&');
  139. if (isset($provider['provider_name']) and $provider['provider_name']) {
  140. $provider_name = $provider['provider_name'];
  141. }
  142. else {
  143. // pre-traitement du provider si besoin
  144. $provider_name = explode('//', $provider['endpoint']);
  145. $provider_name = explode('/', $provider_name[1]);
  146. $provider_name = reset($provider_name);
  147. }
  148. $provider_name = preg_replace(',\W+,', '_', strtolower($provider_name));
  149. if ($oembed_endpoint_pretraite = charger_fonction("pretraite_$provider_name", 'oembed/input', true)) {
  150. $a = func_get_args();
  151. $args = array('url'=>array_shift($a));
  152. if (count($a)) {
  153. $args['maxwidth'] = array_shift($a);
  154. }
  155. if (count($a)) {
  156. $args['maxheight'] = array_shift($a);
  157. }
  158. if (count($a)) {
  159. $args['format'] = array_shift($a);
  160. }
  161. $args['endpoint'] = $provider['endpoint'];
  162. $data_url = $oembed_endpoint_pretraite($data_url, $args);
  163. }
  164. if (isset($cache[$data_url])) {
  165. return $cache[$data_url];
  166. }
  167. $oembed_cache = sous_repertoire(_DIR_CACHE, 'oembed').md5($data_url). '.'.$format;
  168. // si cache oembed dispo et pas de recalcul demande, l'utiliser (perf issue)
  169. if (!$force_reload and file_exists($oembed_cache) and _VAR_MODE !== 'recalcul') {
  170. lire_fichier($oembed_cache, $cache[$data_url]);
  171. $cache[$data_url]=unserialize($cache[$data_url]);
  172. return $cache[$data_url];
  173. }
  174. $oembed_recuperer_url = charger_fonction('oembed_recuperer_url', 'inc');
  175. $cache[$data_url] = $oembed_recuperer_url($data_url, $url, $format);
  176. // si une fonction de post-traitement est fourni pour ce provider+type, l'utiliser
  177. if ($cache[$data_url]) {
  178. $provider_name2= str_replace(' ', '_', strtolower($cache[$data_url]['provider_name']));
  179. $type = strtolower($cache[$data_url]['type']);
  180. // securisons le nom de la fonction (provider peut contenir n'importe quoi)
  181. $f1 = preg_replace(',\W,', '', "posttraite_{$provider_name2}_$type");
  182. $f2 = preg_replace(',\W,', '', "posttraite_{$provider_name2}");
  183. $f3 = preg_replace(',\W,', '', "posttraite_{$provider_name}_$type");
  184. $f4 = preg_replace(',\W,', '', "posttraite_{$provider_name}");
  185. if (
  186. $oembed_provider_posttraite = charger_fonction($f1, 'oembed/input', true)
  187. or $oembed_provider_posttraite = charger_fonction($f2, 'oembed/input', true)
  188. or $oembed_provider_posttraite = charger_fonction($f3, 'oembed/input', true)
  189. or $oembed_provider_posttraite = charger_fonction($f4, 'oembed/input', true) ) {
  190. $cache[$data_url] = $oembed_provider_posttraite($cache[$data_url], $url);
  191. }
  192. ecrire_fichier($oembed_cache, serialize($cache[$data_url]));
  193. }
  194. spip_log('infos oembed pour '.$url.' : '.var_export($cache[$data_url], true), 'oembed.'._LOG_DEBUG);
  195. return $cache[$data_url];
  196. }
  197. /**
  198. * Vérfier qu'une url est dans la liste des providers autorisés
  199. *
  200. * @param string $url l'url à tester
  201. * @return bool|array
  202. * false si non ; details du provider dans un tabeau associatif si oui
  203. */
  204. function oembed_verifier_provider($url) {
  205. if (strncmp($url, $GLOBALS['meta']['adresse_site'], strlen($GLOBALS['meta']['adresse_site'])) == 0) {
  206. return false;
  207. }
  208. $providers = oembed_lister_providers();
  209. foreach ($providers as $scheme => $endpoint) {
  210. $regex = '#' . str_replace('\*', '(.+)', preg_quote($scheme, '#')) . '#';
  211. $regex = preg_replace('|^#http\\\://|', '#https?\://', $regex);
  212. if (preg_match($regex, $url)) {
  213. return array('endpoint' => $endpoint);
  214. }
  215. }
  216. return false;
  217. }
  218. /**
  219. * Détecter les liens oembed dans le head d'une page web
  220. *
  221. * @param string $url url de la page à analyser
  222. * @return bool|string false si pas de lien ; url du contenu oembed
  223. */
  224. function oembed_detecter_lien($url) {
  225. $providers = array();
  226. $oembed_recuperer_url = charger_fonction('oembed_recuperer_url', 'inc');
  227. // on recupere le contenu de la page
  228. if ($html = $oembed_recuperer_url($url, $url, 'html')) {
  229. // types de liens oembed à détecter
  230. $linktypes = array(
  231. 'application/json+oembed' => 'json',
  232. 'text/json+oembed' => 'json', // ex de 500px
  233. 'text/xml+oembed' => 'xml',
  234. 'application/xml+oembed' => 'xml', // uniquement pour Vimeo
  235. );
  236. // on ne garde que le head de la page
  237. $head = substr($html, 0, stripos($html, '</head>'));
  238. // un test rapide...
  239. $tagfound = false;
  240. foreach ($linktypes as $linktype => $format) {
  241. if (stripos($head, $linktype)) {
  242. $tagfound = true;
  243. break;
  244. }
  245. }
  246. if ($tagfound && preg_match_all('/<link([^<>]+)>/i', $head, $links)) {
  247. foreach ($links[0] as $link) {
  248. $type = extraire_attribut($link, 'type');
  249. $href = extraire_attribut($link, 'href');
  250. if (!empty($type) and !empty($linktypes[$type]) and !empty($href)) {
  251. $providers[$linktypes[$type]] = $href;
  252. // on a le json, ça nous suffit
  253. if ('json' == $linktypes[$type]) {
  254. break;
  255. }
  256. }
  257. }
  258. }
  259. }
  260. $res = array();
  261. // on préfère le json au xml
  262. if (!empty($providers['json'])) {
  263. $res['endpoint'] = $providers['json'];
  264. } elseif (!empty($providers['xml'])) {
  265. $res['endpoint'] = $providers['xml'];
  266. } else {
  267. return false;
  268. }
  269. // detecter certains providers specifiques : ex mastodon, chaque instance a son nom et on peut pas l'identifier par son URL
  270. if (strpos($html, '//github.com/tootsuite/mastodon') !== false or strpos($html, '//joinmastodon.org') !== false) {
  271. $res['provider_name'] = 'Mastodon';
  272. }
  273. return $res;
  274. }
  275. /**
  276. * Embarquer un lien oembed si possible
  277. * @param string $lien
  278. * @return string
  279. */
  280. function oembed_embarquer_lien($lien) {
  281. static $base = null;
  282. $url = extraire_attribut($lien, 'href');
  283. $texte = null;
  284. if ($url
  285. and (
  286. oembed_verifier_provider($url)
  287. or (lire_config('oembed/detecter_lien', 'non') == 'oui'))
  288. ) {
  289. if (is_null($base)) {
  290. $base = url_de_base();
  291. }
  292. // on embarque jamais un lien de soi meme car c'est une mise en abime qui donne le tourni
  293. // (et peut provoquer une boucle infinie de requetes http)
  294. if (strncmp($url, $base, strlen($base)) != 0) {
  295. $fond = recuperer_fond('modeles/oembed', array('url' => $url, 'lien' => $lien));
  296. if ($fond = trim($fond)) {
  297. $texte = $fond;
  298. }
  299. }
  300. }
  301. return $texte;
  302. }