1 | <?php |
---|
2 | |
---|
3 | /***************************************************************************\ |
---|
4 | * SPIP, Systeme de publication pour l'internet * |
---|
5 | * * |
---|
6 | * Copyright (c) 2001-2014 * |
---|
7 | * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * |
---|
8 | * * |
---|
9 | * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * |
---|
10 | * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * |
---|
11 | \***************************************************************************/ |
---|
12 | |
---|
13 | /** |
---|
14 | * Gestion des actualisation des sites syndiqués |
---|
15 | * |
---|
16 | * @package SPIP\Sites\Genie |
---|
17 | **/ |
---|
18 | |
---|
19 | if (!defined("_ECRIRE_INC_VERSION")) { |
---|
20 | return; |
---|
21 | } |
---|
22 | include_spip('inc/syndic'); |
---|
23 | |
---|
24 | ## valeurs modifiables dans mes_options |
---|
25 | if (!defined('_PERIODE_SYNDICATION')) { |
---|
26 | /** |
---|
27 | * Période de syndication (en minutes) |
---|
28 | * |
---|
29 | * Attention il est très mal vu de prendre une periode < 20 minutes |
---|
30 | */ |
---|
31 | define('_PERIODE_SYNDICATION', 2 * 60); |
---|
32 | } |
---|
33 | if (!defined('_PERIODE_SYNDICATION_SUSPENDUE')) { |
---|
34 | /** |
---|
35 | * Durée d'une suspension de syndication si un site ne répond pas (en minutes) |
---|
36 | */ |
---|
37 | define('_PERIODE_SYNDICATION_SUSPENDUE', 24 * 60); |
---|
38 | } |
---|
39 | |
---|
40 | |
---|
41 | /** |
---|
42 | * Cron de mise à jour des sites syndiqués |
---|
43 | * |
---|
44 | * @param int $t Date de dernier passage |
---|
45 | * @return int |
---|
46 | **/ |
---|
47 | function genie_syndic_dist($t) { |
---|
48 | return executer_une_syndication(); |
---|
49 | } |
---|
50 | |
---|
51 | |
---|
52 | /** |
---|
53 | * Effectuer la syndication d'un unique site |
---|
54 | * |
---|
55 | * Choisit le site le plus proche à mettre à jour |
---|
56 | * |
---|
57 | * @return |
---|
58 | * retourne 0 si aucun a faire ou echec lors de la tentative |
---|
59 | **/ |
---|
60 | function executer_une_syndication() { |
---|
61 | |
---|
62 | // On va tenter un site 'sus' ou 'off' de plus de 24h, et le passer en 'off' |
---|
63 | // s'il echoue |
---|
64 | $where = sql_in("syndication", array('sus', 'off')) . " |
---|
65 | AND statut<>'refuse' |
---|
66 | AND NOT(" . sql_date_proche('date_syndic', (0 - _PERIODE_SYNDICATION_SUSPENDUE), "MINUTE") . ')'; |
---|
67 | $id_syndic = sql_getfetsel("id_syndic", "spip_syndic", "statut<>" . sql_quote("refuse") . " AND " . $where, '', |
---|
68 | "date_syndic", "1"); |
---|
69 | if ($id_syndic) { |
---|
70 | // inserer la tache dans la file, avec controle d'unicite |
---|
71 | job_queue_add('syndic_a_jour', 'syndic_a_jour', array($id_syndic), 'genie/syndic', true); |
---|
72 | } |
---|
73 | |
---|
74 | // Et un site 'oui' de plus de 2 heures, qui passe en 'sus' s'il echoue |
---|
75 | $where = "syndication='oui' |
---|
76 | AND statut<>'refuse' |
---|
77 | AND NOT(" . sql_date_proche('date_syndic', (0 - _PERIODE_SYNDICATION), "MINUTE") . ')'; |
---|
78 | $id_syndic = sql_getfetsel("id_syndic", "spip_syndic", "statut<>" . sql_quote("refuse") . " AND " . $where, '', |
---|
79 | "date_syndic", "1"); |
---|
80 | |
---|
81 | if ($id_syndic) { |
---|
82 | // inserer la tache dans la file, avec controle d'unicite |
---|
83 | job_queue_add('syndic_a_jour', 'syndic_a_jour', array($id_syndic), 'genie/syndic', true); |
---|
84 | } |
---|
85 | |
---|
86 | return 0; |
---|
87 | } |
---|
88 | |
---|
89 | |
---|
90 | /** |
---|
91 | * Mettre à jour le site |
---|
92 | * |
---|
93 | * Attention, cette fonction ne doit pas etre appellee simultanement |
---|
94 | * sur un meme site: un verrouillage a du etre pose en amont. |
---|
95 | * => elle doit toujours etre appelee par job_queue_add |
---|
96 | * |
---|
97 | * @param int $now_id_syndic |
---|
98 | * Identifiant du site à mettre à jour |
---|
99 | * @return bool|string |
---|
100 | */ |
---|
101 | function syndic_a_jour($now_id_syndic) { |
---|
102 | include_spip('inc/texte'); |
---|
103 | $call = debug_backtrace(); |
---|
104 | if ($call[1]['function'] !== 'queue_start_job') { |
---|
105 | spip_log("syndic_a_jour doit etre appelee par JobQueue Cf. http://trac.rezo.net/trac/spip/changeset/10294", |
---|
106 | _LOG_ERREUR); |
---|
107 | } |
---|
108 | |
---|
109 | $row = sql_fetsel("*", "spip_syndic", "id_syndic=" . intval($now_id_syndic)); |
---|
110 | |
---|
111 | if (!$row) { |
---|
112 | return; |
---|
113 | } |
---|
114 | |
---|
115 | $url_syndic = $row['url_syndic']; |
---|
116 | $url_site = $row['url_site']; |
---|
117 | |
---|
118 | if ($row['moderation'] == 'oui') { |
---|
119 | $moderation = 'dispo'; |
---|
120 | } // a valider |
---|
121 | else { |
---|
122 | $moderation = 'publie'; |
---|
123 | } // en ligne sans validation |
---|
124 | |
---|
125 | // determiner le statut a poser en cas d'echec : sus par defaut |
---|
126 | // off si le site est deja off, ou sus depuis trop longtemps |
---|
127 | $statut = 'sus'; |
---|
128 | if ( |
---|
129 | $row['statut'] == 'off' |
---|
130 | or ($row['statut'] == 'sus' and time() - strtotime($row['date_syndic']) > _PERIODE_SYNDICATION_SUSPENDUE * 60) |
---|
131 | ) { |
---|
132 | $statut = 'off'; |
---|
133 | } |
---|
134 | |
---|
135 | sql_updateq('spip_syndic', array('syndication' => $statut, 'date_syndic' => date('Y-m-d H:i:s')), |
---|
136 | "id_syndic=" . intval($now_id_syndic)); |
---|
137 | |
---|
138 | // Aller chercher les donnees du RSS et les analyser |
---|
139 | include_spip('inc/distant'); |
---|
140 | $rss = recuperer_page($url_syndic, true); |
---|
141 | if (!$rss) { |
---|
142 | $articles = _T('sites:avis_echec_syndication_02'); |
---|
143 | } else { |
---|
144 | $articles = analyser_backend($rss, $url_syndic); |
---|
145 | } |
---|
146 | |
---|
147 | // Renvoyer l'erreur le cas echeant |
---|
148 | if (!is_array($articles)) { |
---|
149 | return $articles; |
---|
150 | } |
---|
151 | |
---|
152 | // Les enregistrer dans la base |
---|
153 | |
---|
154 | $faits = array(); |
---|
155 | foreach ($articles as $data) { |
---|
156 | inserer_article_syndique($data, $now_id_syndic, $moderation, $url_site, $url_syndic, $row['resume'], $faits); |
---|
157 | } |
---|
158 | |
---|
159 | // moderation automatique des liens qui sont sortis du feed |
---|
160 | if (count($faits) > 0) { |
---|
161 | $faits = sql_in("id_syndic_article", $faits, 'NOT'); |
---|
162 | if ($row['miroir'] == 'oui') { |
---|
163 | sql_update('spip_syndic_articles', array('statut' => "'off'", 'maj' => 'maj'), |
---|
164 | "id_syndic=$now_id_syndic AND $faits"); |
---|
165 | } |
---|
166 | // suppression apres 2 mois des liens qui sont sortis du feed |
---|
167 | if ($row['oubli'] == 'oui') { |
---|
168 | |
---|
169 | sql_delete('spip_syndic_articles', "id_syndic=$now_id_syndic AND NOT(" . sql_date_proche('maj', -2, |
---|
170 | 'MONTH') . ') AND NOT(' . sql_date_proche('date', -2, 'MONTH') . ") AND $faits"); |
---|
171 | } |
---|
172 | } |
---|
173 | |
---|
174 | // Noter que la syndication est OK |
---|
175 | sql_updateq("spip_syndic", array("syndication" => 'oui'), "id_syndic=" . intval($now_id_syndic)); |
---|
176 | |
---|
177 | return false; # c'est bon |
---|
178 | } |
---|
179 | |
---|
180 | |
---|
181 | /** |
---|
182 | * Insère un article syndiqué |
---|
183 | * |
---|
184 | * Vérifie que l'article n'a pas déjà été inséré par |
---|
185 | * un autre item du même feed qui aurait le meme link. |
---|
186 | * |
---|
187 | * @pipeline_appel pre_insertion |
---|
188 | * @pipeline_appel post_insertion |
---|
189 | * @pipeline_appel post_syndication |
---|
190 | * |
---|
191 | * @param array $data |
---|
192 | * @param int $now_id_syndic |
---|
193 | * @param string $statut |
---|
194 | * @param string $url_site |
---|
195 | * @param string $url_syndic |
---|
196 | * @param string $resume |
---|
197 | * @param array $faits |
---|
198 | * @return bool |
---|
199 | * true si l'article est nouveau, false sinon. |
---|
200 | **/ |
---|
201 | function inserer_article_syndique($data, $now_id_syndic, $statut, $url_site, $url_syndic, $resume, &$faits) { |
---|
202 | // Creer le lien s'il est nouveau - cle=(id_syndic,url) |
---|
203 | $le_lien = $data['url']; |
---|
204 | |
---|
205 | /** |
---|
206 | * URL unique de syndication |
---|
207 | * |
---|
208 | * Si true, un lien déjà syndiqué arrivant par une autre source est ignoré |
---|
209 | * par defaut `false`, chaque source a sa liste de liens, éventuellement les mêmes |
---|
210 | * |
---|
211 | * @var bool |
---|
212 | */ |
---|
213 | if (!defined('_SYNDICATION_URL_UNIQUE')) { |
---|
214 | define('_SYNDICATION_URL_UNIQUE', false); |
---|
215 | } |
---|
216 | |
---|
217 | /** |
---|
218 | * Actualiser les contenus syndiqués |
---|
219 | * |
---|
220 | * Si false, on ne met pas à jour un lien déjà syndiqué avec ses nouvelles |
---|
221 | * données ; par defaut `true` : on met a jour si le contenu a changé |
---|
222 | * |
---|
223 | * Attention si on modifie à la main un article syndiqué, les modifs sont |
---|
224 | * écrasées lors de la syndication suivante |
---|
225 | * |
---|
226 | * @var bool |
---|
227 | **/ |
---|
228 | if (!defined('_SYNDICATION_CORRECTION')) { |
---|
229 | define('_SYNDICATION_CORRECTION', true); |
---|
230 | } |
---|
231 | |
---|
232 | // est-ce un nouvel article ? |
---|
233 | $ajout = false; |
---|
234 | |
---|
235 | // Chercher les liens de meme cle |
---|
236 | // S'il y a plusieurs liens qui repondent, il faut choisir le plus proche |
---|
237 | // (ie meme titre et pas deja fait), le mettre a jour et ignorer les autres |
---|
238 | $n = 0; |
---|
239 | $s = sql_select("id_syndic_article,titre,id_syndic,statut", "spip_syndic_articles", |
---|
240 | "url=" . sql_quote($le_lien) |
---|
241 | . (_SYNDICATION_URL_UNIQUE |
---|
242 | ? '' |
---|
243 | : " AND id_syndic=$now_id_syndic") |
---|
244 | . " AND " . sql_in('id_syndic_article', $faits, 'NOT'), "", "maj DESC"); |
---|
245 | while ($a = sql_fetch($s)) { |
---|
246 | $id = $a['id_syndic_article']; |
---|
247 | $id_syndic = $a['id_syndic']; |
---|
248 | if ($a['titre'] == $data['titre']) { |
---|
249 | $id_syndic_article = $id; |
---|
250 | break; |
---|
251 | } |
---|
252 | $n++; |
---|
253 | } |
---|
254 | // S'il y en avait qu'un, le prendre quel que soit le titre |
---|
255 | if ($n == 1) { |
---|
256 | $id_syndic_article = $id; |
---|
257 | } // Si l'article n'existe pas, on le cree |
---|
258 | elseif (!isset($id_syndic_article)) { |
---|
259 | $champs = array( |
---|
260 | 'id_syndic' => $now_id_syndic, |
---|
261 | 'url' => $le_lien, |
---|
262 | 'date' => date("Y-m-d H:i:s", $data['date'] ? $data['date'] : $data['lastbuilddate']), |
---|
263 | 'statut' => $statut |
---|
264 | ); |
---|
265 | // Envoyer aux plugins |
---|
266 | $champs = pipeline('pre_insertion', |
---|
267 | array( |
---|
268 | 'args' => array( |
---|
269 | 'table' => 'spip_syndic_articles', |
---|
270 | ), |
---|
271 | 'data' => $champs |
---|
272 | ) |
---|
273 | ); |
---|
274 | $ajout = $id_syndic_article = sql_insertq('spip_syndic_articles', $champs); |
---|
275 | if (!$ajout) { |
---|
276 | return; |
---|
277 | } |
---|
278 | |
---|
279 | pipeline('post_insertion', |
---|
280 | array( |
---|
281 | 'args' => array( |
---|
282 | 'table' => 'spip_syndic_articles', |
---|
283 | 'id_objet' => $id_syndic_article |
---|
284 | ), |
---|
285 | 'data' => $champs |
---|
286 | ) |
---|
287 | ); |
---|
288 | } |
---|
289 | $faits[] = $id_syndic_article; |
---|
290 | |
---|
291 | |
---|
292 | // Si le lien n'est pas nouveau, plusieurs options : |
---|
293 | if (!$ajout) { |
---|
294 | // 1. Lien existant : on corrige ou pas ? |
---|
295 | if (!_SYNDICATION_CORRECTION) { |
---|
296 | return; |
---|
297 | } |
---|
298 | // 2. Le lien existait deja, lie a un autre spip_syndic |
---|
299 | if (_SYNDICATION_URL_UNIQUE and $id_syndic != $now_id_syndic) { |
---|
300 | return; |
---|
301 | } |
---|
302 | } |
---|
303 | |
---|
304 | // Descriptif, en mode resume ou mode 'full text' |
---|
305 | // on prend en priorite data['descriptif'] si on est en mode resume, |
---|
306 | // et data['content'] si on est en mode "full syndication" |
---|
307 | if ($resume != 'non') { |
---|
308 | // mode "resume" |
---|
309 | $desc = (isset($data['descriptif']) and strlen($data['descriptif'])) ? $data['descriptif'] |
---|
310 | : (isset($data['content']) ? $data['content'] : ''); |
---|
311 | $desc = couper(trim_more(textebrut($desc)), 300); |
---|
312 | } else { |
---|
313 | // mode "full syndication" |
---|
314 | // choisir le contenu pertinent |
---|
315 | // & refaire les liens relatifs |
---|
316 | $desc = strlen($data['content']) ? |
---|
317 | $data['content'] : $data['descriptif']; |
---|
318 | $desc = liens_absolus($desc, $url_syndic); |
---|
319 | } |
---|
320 | |
---|
321 | // tags & enclosures (preparer spip_syndic_articles.tags) |
---|
322 | $tags = ($data['enclosures'] ? $data['enclosures'] : ''); |
---|
323 | # eviter les doublons (cle = url+titre) et passer d'un tableau a une chaine |
---|
324 | if ($data['tags']) { |
---|
325 | $vus = array(); |
---|
326 | foreach ($data['tags'] as $tag) { |
---|
327 | $cle = supprimer_tags($tag) . extraire_attribut($tag, 'href'); |
---|
328 | $vus[$cle] = $tag; |
---|
329 | } |
---|
330 | $tags .= ($tags ? ', ' : '') . join(', ', $vus); |
---|
331 | } |
---|
332 | |
---|
333 | // Mise a jour du contenu (titre,auteurs,description,date?,source...) |
---|
334 | $vals = array( |
---|
335 | 'titre' => $data['titre'], |
---|
336 | 'lesauteurs' => $data['lesauteurs'], |
---|
337 | 'descriptif' => $desc, |
---|
338 | 'lang' => substr($data['lang'], 0, 10), |
---|
339 | 'source' => (isset($data['source']) ? substr($data['source'], 0, 255) : ''), |
---|
340 | 'url_source' => (isset($data['url_source']) ? substr($data['url_source'], 0, 255) : ''), |
---|
341 | 'tags' => $tags |
---|
342 | ); |
---|
343 | |
---|
344 | // Mettre a jour la date si lastbuilddate |
---|
345 | if (isset($data['lastbuilddate']) and $data['lastbuilddate']) { |
---|
346 | $vals['date'] = date("Y-m-d H:i:s", $data['lastbuilddate']); |
---|
347 | } |
---|
348 | |
---|
349 | include_spip('inc/modifier'); |
---|
350 | objet_modifier_champs('syndic_article',$id_syndic_article,array('data'=>$vals),$vals); |
---|
351 | |
---|
352 | // Point d'entree post_syndication |
---|
353 | pipeline('post_syndication', |
---|
354 | array( |
---|
355 | 'args' => array( |
---|
356 | 'table' => 'spip_syndic_articles', |
---|
357 | 'id_objet' => $id_syndic_article, |
---|
358 | 'url' => $le_lien, |
---|
359 | 'id_syndic' => $now_id_syndic, |
---|
360 | 'ajout' => $ajout, |
---|
361 | ), |
---|
362 | 'data' => $data |
---|
363 | ) |
---|
364 | ); |
---|
365 | |
---|
366 | return $ajout; |
---|
367 | } |
---|
368 | |
---|
369 | /** |
---|
370 | * Nettoyer les contenus de flux qui utilisent des espaces insécables en début |
---|
371 | * pour faire un retrait. |
---|
372 | * |
---|
373 | * Peut être sous la forme de l'entité ` ` ou en utf8 `\xc2\xa0` |
---|
374 | * |
---|
375 | * @param string $texte |
---|
376 | * @return string |
---|
377 | */ |
---|
378 | function trim_more($texte) { |
---|
379 | $texte = trim($texte); |
---|
380 | // chr(194)chr(160) |
---|
381 | $texte = preg_replace(",^(\s|( )|(\xc2\xa0))+,ums", "", $texte); |
---|
382 | |
---|
383 | return $texte; |
---|
384 | } |
---|