[SPIP] v3.2.12 -> v3.2.12 - Reinstallation avec le spip_loader
[lhc/web/www.git] / www / prive / formulaires / editer_liens.php
1 <?php
2 /***************************************************************************\
3 * SPIP, Systeme de publication pour l'internet *
4 * *
5 * Copyright (c) 2001-2019 *
6 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
7 * *
8 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
9 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
10 \***************************************************************************/
11
12 /**
13 * Gestion du formulaire d'édition de liens
14 *
15 * @package SPIP\Core\Formulaires
16 **/
17 if (!defined('_ECRIRE_INC_VERSION')) {
18 return;
19 }
20
21
22 /**
23 * Retrouve la source et l'objet de la liaison
24 *
25 * À partir des 3 premiers paramètres transmis au formulaire,
26 * la fonction retrouve :
27 * - l'objet dont on utilise sa table de liaison (table_source)
28 * - l'objet et id_objet sur qui on lie des éléments (objet, id_objet)
29 * - l'objet que l'on veut lier dessus (objet_lien)
30 *
31 * @param string $a
32 * @param string|int $b
33 * @param int|string $c
34 * @return array
35 * ($table_source,$objet,$id_objet,$objet_lien)
36 */
37 function determine_source_lien_objet($a, $b, $c) {
38 $table_source = $objet_lien = $objet = $id_objet = null;
39 // auteurs, article, 23 :
40 // associer des auteurs à l'article 23, sur la table pivot spip_auteurs_liens
41 if (is_numeric($c) and !is_numeric($b)) {
42 $table_source = table_objet($a);
43 $objet_lien = objet_type($a);
44 $objet = objet_type($b);
45 $id_objet = $c;
46 }
47 // article, 23, auteurs
48 // associer des auteurs à l'article 23, sur la table pivot spip_articles_liens
49 if (is_numeric($b) and !is_numeric($c)) {
50 $table_source = table_objet($c);
51 $objet_lien = objet_type($a);
52 $objet = objet_type($a);
53 $id_objet = $b;
54 }
55
56 return array($table_source, $objet, $id_objet, $objet_lien);
57 }
58
59 /**
60 * Chargement du formulaire d'édition de liens
61 *
62 * #FORMULAIRE_EDITER_LIENS{auteurs,article,23}
63 * pour associer des auteurs à l'article 23, sur la table pivot spip_auteurs_liens
64 * #FORMULAIRE_EDITER_LIENS{article,23,auteurs}
65 * pour associer des auteurs à l'article 23, sur la table pivot spip_articles_liens
66 * #FORMULAIRE_EDITER_LIENS{articles,auteur,12}
67 * pour associer des articles à l'auteur 12, sur la table pivot spip_articles_liens
68 * #FORMULAIRE_EDITER_LIENS{auteur,12,articles}
69 * pour associer des articles à l'auteur 12, sur la table pivot spip_auteurs_liens
70 *
71 * @param string $a
72 * @param string|int $b
73 * @param int|string $c
74 * @param array|bool $options
75 * - Si array, tableau d'options
76 * - Si bool : valeur de l'option 'editable' uniquement
77 *
78 * @return array
79 */
80 function formulaires_editer_liens_charger_dist($a, $b, $c, $options = array()) {
81
82 // compat avec ancienne signature ou le 4eme argument est $editable
83 if (!is_array($options)) {
84 $options = array('editable' => $options);
85 } elseif (!isset($options['editable'])) {
86 $options['editable'] = true;
87 }
88
89 $editable = $options['editable'];
90
91 list($table_source, $objet, $id_objet, $objet_lien) = determine_source_lien_objet($a, $b, $c);
92 if (!$table_source or !$objet or !$objet_lien or !$id_objet) {
93 return false;
94 }
95
96 $objet_source = objet_type($table_source);
97 $table_sql_source = table_objet_sql($objet_source);
98
99 // verifier existence de la table xxx_liens
100 include_spip('action/editer_liens');
101 if (!objet_associable($objet_lien)) {
102 return false;
103 }
104
105 // L'éditabilité :) est définie par un test permanent (par exemple "associermots") ET le 4ème argument
106 include_spip('inc/autoriser');
107 $editable = ($editable and autoriser('associer' . $table_source, $objet, $id_objet)
108 and autoriser('modifier', $objet, $id_objet));
109
110 if (!$editable and !count(objet_trouver_liens(
111 array($objet_lien => '*'),
112 array(($objet_lien == $objet_source ? $objet : $objet_source) => $id_objet)
113 ))) {
114 return false;
115 }
116
117 // squelettes de vue et de d'association
118 // ils sont différents si des rôles sont définis.
119 $skel_vue = $table_source . '_lies';
120 $skel_ajout = $table_source . '_associer';
121
122 // description des roles
123 include_spip('inc/roles');
124 if ($roles = roles_presents($objet_source, $objet)) {
125 // on demande de nouveaux squelettes en conséquence
126 $skel_vue = $table_source . '_roles_lies';
127 $skel_ajout = $table_source . '_roles_associer';
128 }
129
130 $oups = _request('_oups');
131 if (unserialize(base64_decode($oups))) {
132 // on est bon, rien a faire
133 }
134 elseif(unserialize($oups)) {
135 // il faut encoder
136 $oups = base64_encode($oups);
137 }
138 else {
139 $oups = '';
140 }
141 $valeurs = array(
142 'id' => "$table_source-$objet-$id_objet-$objet_lien", // identifiant unique pour les id du form
143 '_vue_liee' => $skel_vue,
144 '_vue_ajout' => $skel_ajout,
145 '_objet_lien' => $objet_lien,
146 'id_lien_ajoute' => _request('id_lien_ajoute'),
147 'objet' => $objet,
148 'id_objet' => $id_objet,
149 'objet_source' => $objet_source,
150 'table_source' => $table_source,
151 'recherche' => '',
152 'visible' => 0,
153 'ajouter_lien' => '',
154 'supprimer_lien' => '',
155 'qualifier_lien' => '',
156 '_roles' => $roles, # description des roles
157 '_oups' => $oups,
158 'editable' => $editable,
159 );
160
161 // les options non definies dans $valeurs sont passees telles quelles au formulaire html
162 $valeurs = array_merge($options, $valeurs);
163
164 return $valeurs;
165 }
166
167 /**
168 * Traiter le post des informations d'édition de liens
169 *
170 * Les formulaires peuvent poster dans quatre variables
171 * - ajouter_lien et supprimer_lien
172 * - remplacer_lien
173 * - qualifier_lien
174 *
175 * Les deux premières peuvent être de trois formes différentes :
176 * ajouter_lien[]="objet1-id1-objet2-id2"
177 * ajouter_lien[objet1-id1-objet2-id2]="nimportequoi"
178 * ajouter_lien['clenonnumerique']="objet1-id1-objet2-id2"
179 * Dans ce dernier cas, la valeur ne sera prise en compte
180 * que si _request('clenonnumerique') est vrai (submit associé a l'input)
181 *
182 * remplacer_lien doit être de la forme
183 * remplacer_lien[objet1-id1-objet2-id2]="objet3-id3-objet2-id2"
184 * ou objet1-id1 est celui qu'on enleve et objet3-id3 celui qu'on ajoute
185 *
186 * qualifier_lien doit être de la forme, et sert en complément de ajouter_lien
187 * qualifier_lien[objet1-id1-objet2-id2][role] = array("role1", "autre_role")
188 * qualifier_lien[objet1-id1-objet2-id2][valeur] = array("truc", "chose")
189 * produira 2 liens chacun avec array("role"=>"role1","valeur"=>"truc") et array("role"=>"autre_role","valeur"=>"chose")
190 *
191 * @param string $a
192 * @param string|int $b
193 * @param int|string $c
194 * @param array|bool $options
195 * - Si array, tableau d'options
196 * - Si bool : valeur de l'option 'editable' uniquement
197 *
198 * @return array
199 */
200 function formulaires_editer_liens_traiter_dist($a, $b, $c, $options = array()) {
201 // compat avec ancienne signature ou le 4eme argument est $editable
202 if (!is_array($options)) {
203 $options = array('editable' => $options);
204 } elseif (!isset($options['editable'])) {
205 $options['editable'] = true;
206 }
207
208 $editable = $options['editable'];
209
210 $res = array('editable' => $editable ? true : false);
211 list($table_source, $objet, $id_objet, $objet_lien) = determine_source_lien_objet($a, $b, $c);
212 if (!$table_source or !$objet or !$objet_lien) {
213 return $res;
214 }
215
216
217 if (_request('tout_voir')) {
218 set_request('recherche', '');
219 }
220
221 include_spip('inc/autoriser');
222 if (autoriser('modifier', $objet, $id_objet)) {
223 // annuler les suppressions du coup d'avant !
224 if (_request('annuler_oups')
225 and $oups = _request('_oups')
226 and $oups = base64_decode($oups)
227 and $oups = unserialize($oups)
228 ) {
229 if ($oups_objets = charger_fonction("editer_liens_oups_{$table_source}_{$objet}_{$objet_lien}", 'action', true)) {
230 $oups_objets($oups);
231 } else {
232 $objet_source = objet_type($table_source);
233 include_spip('action/editer_liens');
234 foreach ($oups as $oup) {
235 if ($objet_lien == $objet_source) {
236 objet_associer(array($objet_source => $oup[$objet_source]), array($objet => $oup[$objet]), $oup);
237 } else {
238 objet_associer(array($objet => $oup[$objet]), array($objet_source => $oup[$objet_source]), $oup);
239 }
240 }
241 }
242 # oups ne persiste que pour la derniere action, si suppression
243 set_request('_oups');
244 }
245
246 $supprimer = _request('supprimer_lien');
247 $ajouter = _request('ajouter_lien');
248
249 // il est possible de preciser dans une seule variable un remplacement :
250 // remplacer_lien[old][new]
251 if ($remplacer = _request('remplacer_lien')) {
252 foreach ($remplacer as $k => $v) {
253 if ($old = lien_verifier_action($k, '')) {
254 foreach (is_array($v) ? $v : array($v) as $kn => $vn) {
255 if ($new = lien_verifier_action($kn, $vn)) {
256 $supprimer[$old] = 'x';
257 $ajouter[$new] = '+';
258 }
259 }
260 }
261 }
262 }
263
264 if ($supprimer) {
265 if ($supprimer_objets = charger_fonction(
266 "editer_liens_supprimer_{$table_source}_{$objet}_{$objet_lien}",
267 'action',
268 true
269 )) {
270 $oups = $supprimer_objets($supprimer);
271 } else {
272 include_spip('action/editer_liens');
273 $oups = array();
274
275 foreach ($supprimer as $k => $v) {
276 if ($lien = lien_verifier_action($k, $v)) {
277 $lien = explode('-', $lien);
278 list($objet_source, $ids, $objet_lie, $idl, $role) = $lien;
279 // appliquer une condition sur le rôle si défini ('*' pour tous les roles)
280 $cond = (!is_null($role) ? array('role' => $role) : array());
281 if ($objet_lien == $objet_source) {
282 $oups = array_merge(
283 $oups,
284 objet_trouver_liens(array($objet_source => $ids), array($objet_lie => $idl), $cond)
285 );
286 objet_dissocier(array($objet_source => $ids), array($objet_lie => $idl), $cond);
287 } else {
288 $oups = array_merge(
289 $oups,
290 objet_trouver_liens(array($objet_lie => $idl), array($objet_source => $ids), $cond)
291 );
292 objet_dissocier(array($objet_lie => $idl), array($objet_source => $ids), $cond);
293 }
294 }
295 }
296 }
297 set_request('_oups', $oups ? base64_encode(serialize($oups)) : null);
298 }
299
300 if ($ajouter) {
301 if ($ajouter_objets = charger_fonction("editer_liens_ajouter_{$table_source}_{$objet}_{$objet_lien}", 'action', true)
302 ) {
303 $ajout_ok = $ajouter_objets($ajouter);
304 } else {
305 $ajout_ok = false;
306 include_spip('action/editer_liens');
307 foreach ($ajouter as $k => $v) {
308 if ($lien = lien_verifier_action($k, $v)) {
309 $ajout_ok = true;
310 list($objet1, $ids, $objet2, $idl) = explode('-', $lien);
311 $qualifs = lien_retrouver_qualif($objet_lien, $lien);
312 if ($objet_lien == $objet1) {
313 lien_ajouter_liaisons($objet1, $ids, $objet2, $idl, $qualifs);
314 } else {
315 lien_ajouter_liaisons($objet2, $idl, $objet1, $ids, $qualifs);
316 }
317 set_request('id_lien_ajoute', $ids);
318 }
319 }
320 }
321 # oups ne persiste que pour la derniere action, si suppression
322 # une suppression suivie d'un ajout dans le meme hit est un remplacement
323 # non annulable !
324 if ($ajout_ok) {
325 set_request('_oups');
326 }
327 }
328 }
329
330
331 return $res;
332 }
333
334
335 /**
336 * Retrouver l'action de liaision demandée
337 *
338 * Les formulaires envoient une action dans un tableau ajouter_lien
339 * ou supprimer_lien
340 *
341 * L'action est de la forme : objet1-id1-objet2-id2
342 * ou de la forme : objet1-id1-objet2-id2-role
343 *
344 * L'action peut-être indiquée dans la clé ou dans la valeur.
345 * Si elle est indiquee dans la valeur et que la clé est non numérique,
346 * on ne la prend en compte que si un submit avec la clé a été envoyé
347 *
348 * @internal
349 * @param string $k Clé du tableau
350 * @param string $v Valeur du tableau
351 * @return string Action demandée si trouvée, sinon ''
352 */
353 function lien_verifier_action($k, $v) {
354 $action = '';
355 if (preg_match(',^\w+-[\w*]+-[\w*]+-[\w*]+(-[\w*])?,', $k)) {
356 $action = $k;
357 }
358 if (preg_match(',^\w+-[\w*]+-[\w*]+-[\w*]+(-[\w*])?,', $v)) {
359 if (is_numeric($k)) {
360 $action = $v;
361 }
362 if (_request($k)) {
363 $action = $v;
364 }
365 }
366 // ajout un role null fictif (plus pratique) si pas défini
367 if ($action and count(explode('-', $action)) == 4) {
368 $action .= '-';
369 }
370
371 return $action;
372 }
373
374
375 /**
376 * Retrouve le ou les qualificatifs postés avec une liaison demandée
377 *
378 * @internal
379 * @param string $objet_lien
380 * objet qui porte le lien
381 * @param string $lien
382 * Action du lien
383 * @return array
384 * Liste des qualifs pour chaque lien. Tableau vide s'il n'y en a pas.
385 **/
386 function lien_retrouver_qualif($objet_lien, $lien) {
387 // un role est défini dans la liaison
388 $defs = explode('-', $lien);
389 list($objet1, , $objet2, , $role) = $defs;
390 if ($objet_lien == $objet1) {
391 $colonne_role = roles_colonne($objet1, $objet2);
392 } else {
393 $colonne_role = roles_colonne($objet2, $objet1);
394 }
395
396 // cas ou le role est defini en 5e argument de l'action sur le lien (suppression, ajout rapide sans autre attribut)
397 if ($role) {
398 return array(
399 // un seul lien avec ce role
400 array($colonne_role => $role)
401 );
402 }
403
404 // retrouver les rôles postés pour cette liaison, s'il y en a.
405 $qualifier_lien = _request('qualifier_lien');
406 if (!$qualifier_lien or !is_array($qualifier_lien)) {
407 return array();
408 }
409
410 // pas avec l'action complete (incluant le role)
411 $qualif = array();
412 if ((!isset($qualifier_lien[$lien]) or !$qualif = $qualifier_lien[$lien])
413 and count($defs) == 5
414 ) {
415 // on tente avec l'action sans le role
416 array_pop($defs);
417 $lien = implode('-', $defs);
418 if (!isset($qualifier_lien[$lien]) or !$qualif = $qualifier_lien[$lien]) {
419 $qualif = array();
420 }
421 }
422
423 // $qualif de la forme array(role=>array(...),valeur=>array(...),....)
424 // on le reforme en array(array(role=>..,valeur=>..,..),array(role=>..,valeur=>..,..),...)
425 $qualifs = array();
426 while (count($qualif)) {
427 $q = array();
428 foreach ($qualif as $att => $values) {
429 if (is_array($values)) {
430 $q[$att] = array_shift($qualif[$att]);
431 if (!count($qualif[$att])) {
432 unset($qualif[$att]);
433 }
434 } else {
435 $q[$att] = $values;
436 unset($qualif[$att]);
437 }
438 }
439 // pas de rôle vide
440 if (!$colonne_role or !isset($q[$colonne_role]) or $q[$colonne_role]) {
441 $qualifs[] = $q;
442 }
443 }
444
445 return $qualifs;
446 }
447
448 /**
449 * Ajoute les liens demandés en prenant éventuellement en compte le rôle
450 *
451 * Appelle la fonction objet_associer. L'appelle autant de fois qu'il y
452 * a de rôles demandés pour cette liaison.
453 *
454 * @internal
455 * @param string $objet_source Objet source de la liaison (qui a la table de liaison)
456 * @param array|string $ids Identifiants pour l'objet source
457 * @param string $objet_lien Objet à lier
458 * @param array|string $idl Identifiants pour l'objet lié
459 * @param array $qualifs
460 * @return void
461 **/
462 function lien_ajouter_liaisons($objet_source, $ids, $objet_lien, $idl, $qualifs) {
463
464 // retrouver la colonne de roles s'il y en a a lier
465 if (is_array($qualifs) and count($qualifs)) {
466 foreach ($qualifs as $qualif) {
467 objet_associer(array($objet_source => $ids), array($objet_lien => $idl), $qualif);
468 }
469 } else {
470 objet_associer(array($objet_source => $ids), array($objet_lien => $idl));
471 }
472 }