[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / ecrire / inc / roles.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2017 *
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 rôles
15 *
16 * Les rôles sont une qualification précise sur une liaison entre
17 * deux objets. Ils doivent être définis dans la déclaration d'un objet
18 * pour être utilisés. Ils s'appliquent sur une colonne particulière
19 * de la table de liaison, par défaut 'role'.
20 *
21 * Cette table de liaison, lorsqu'elle a des rôles n'a plus sa clé primaire
22 * sur le couple (id_x, objet, id_objet) mais sur (id_x, objet, id_objet, colonne_role)
23 * de sorte qu'il peut exister plusieurs liens entre 2 objets, mais avec
24 * des rôles différents. Chaque ligne de la table lien correspond alors à
25 * un des rôles.
26 *
27 * @package SPIP\Core\Roles
28 */
29
30 if (!defined('_ECRIRE_INC_VERSION')) {
31 return;
32 }
33
34
35 /**
36 * Vérifie qu'un objet dispose de rôles fonctionnels
37 *
38 * Retourne une description des rôles si c'est le cas
39 *
40 * @param string $objet
41 * Objet source qui possède la table de liaison
42 * @param string $objet_destination
43 * Objet sur quoi on veut lier
44 * Si défini, le retour ne contient que les roles possibles pour cet objet
45 * Sinon retourne tous les roles possibles quelque soit l'objet
46 * @return bool|array
47 * false si rôles indisponibles on non déclarés
48 * array : description des roles applicables dans 3 index : colonne, titres, roles
49 **/
50 function roles_presents($objet, $objet_destination = '') {
51 $desc = lister_tables_objets_sql(table_objet_sql($objet));
52
53 // pas de liste de roles, on sort
54 if (!isset($desc['roles_titres']) or !($titres = $desc['roles_titres'])) {
55 return false;
56 }
57
58 // on vérifie que la table de liaison existe
59 include_spip('action/editer_liens');
60 if (!$lien = objet_associable($objet)) {
61 return false;
62 }
63
64 // on cherche ensuite si la colonne existe bien dans la table de liaison (par défaut 'role')
65 $colonne = isset($desc['roles_colonne']) ? $desc['roles_colonne'] : 'role';
66 $trouver_table = charger_fonction('trouver_table', 'base');
67 list(, $table_lien) = $lien;
68 $desc_lien = $trouver_table($table_lien);
69 if (!isset($desc_lien['field'][$colonne])) {
70 return false;
71 }
72
73 // sur quoi peuvent s'appliquer nos rôles
74 if (!$application = $desc['roles_objets']) {
75 return false;
76 }
77
78 // destination presente, on restreint si possible
79 if ($objet_destination) {
80 $objet_destination = table_objet($objet_destination);
81
82 // pour l'objet
83 if (isset($application[$objet_destination])) {
84 $application = $application[$objet_destination];
85 // sinon pour tous les objets
86 } elseif (isset($application['*'])) {
87 $application = $application['*'];
88 } // sinon tant pis
89 else {
90 return false;
91 }
92 }
93
94 // tout est ok
95 return array(
96 'titres' => $titres,
97 'roles' => $application,
98 'colonne' => $colonne
99 );
100 }
101
102 /**
103 * Retrouve la colonne de liaison d'un rôle si définie entre 2 objets
104 *
105 * @param string $objet
106 * Objet source qui possède la table de liaison
107 * @param string $objet_destination
108 * Objet sur quoi on veut lier
109 * @return string
110 * Nom de la colonne, sinon vide
111 **/
112 function roles_colonne($objet, $objet_destination) {
113 if ($roles = roles_presents($objet, $objet_destination)) {
114 return $roles['colonne'];
115 }
116
117 return '';
118 }
119
120
121 /**
122 * Extrait le rôle et la colonne de role d'un tableau de qualification
123 *
124 * Calcule également une condition where pour ce rôle.
125 *
126 * Pour un objet pouvant recevoir des roles sur sa liaison avec un autre objet,
127 * on retrouve le rôle en question dans le tableau de qualification.
128 * Si le rôle n'est pas défini dedans, on prend le rôle par défaut
129 * déclaré.
130 *
131 * @param string $objet Objet source de la liaison
132 * @param string $objet_destination Objet de destination de la liaison
133 * @param array $qualif tableau de qualifications array(champ => valeur)
134 * @return array
135 * Liste (role, colonne, (array)condition) si role possible
136 * Liste ('', '', array()) sinon.
137 **/
138 function roles_trouver_dans_qualif($objet, $objet_destination, $qualif = array()) {
139 // si des rôles sont possibles, on les utilise
140 $role = $colonne_role = ''; # role défini
141 // condition du where par defaut
142 $cond = array();
143 if ($roles = roles_presents($objet, $objet_destination)) {
144 $colonne_role = $roles['colonne'];
145 // qu'il n'est pas défini
146 if (!isset($qualif[$colonne_role])
147 or !($role = $qualif[$colonne_role])
148 ) {
149 $role = $roles['roles']['defaut'];
150 }
151 // where
152 $cond = array("$colonne_role=" . sql_quote($role));
153 }
154
155 return array($role, $colonne_role, $cond);
156 }
157
158 /**
159 * Gérer l'ajout dans la condition where du rôle
160 *
161 * On ajoute la condition uniquement si la liaison entre les 2 objets a une colonne de rôle !
162 *
163 * @param string $objet_source Objet source (qui possède la table de liens)
164 * @param string $objet Objet de destination
165 * @param array $cond
166 * Tableau de conditions where
167 * qui peut avoir un index spécial 'role' définissant le role à appliquer
168 * ou valant '*' pour tous les roles.
169 * @param bool $tous_si_absent
170 * true pour ne pas appliquer une condition sur le rôle s'il n'est pas indiqué
171 * dans la liste des conditions entrantes. Autrement dit, on n'applique
172 * pas de rôle par défaut si aucun n'est défini.
173 * @return array
174 * Liste (Tableau de conditions where complété du role, Colonne du role, role utilisé)
175 **/
176 function roles_creer_condition_role($objet_source, $objet, $cond, $tous_si_absent = false) {
177 // role par défaut, colonne
178 list($role_defaut, $colonne_role) = roles_trouver_dans_qualif($objet_source, $objet);
179
180 // chercher d'eventuels rôles transmis
181 $role = (isset($cond['role']) ? $cond['role'] : ($tous_si_absent ? '*' : $role_defaut));
182 unset($cond['role']); // cette condition est particuliere...
183
184 if ($colonne_role) {
185 // on ajoute la condition du role aux autres conditions.
186 if ($role != '*') {
187 $cond[] = "$colonne_role=" . sql_quote($role);
188 }
189 }
190
191 return array($cond, $colonne_role, $role);
192 }
193
194 /**
195 * Liste des identifiants dont on ne peut ajouter de rôle
196 *
197 * Lister les id objet_source associés à l'objet id_objet
198 * via la table de lien objet_lien, et détermine dans cette liste
199 * lesquels ont les rôles complets, c'est à dire qu'on ne peut leur
200 * affecteur d'autres rôles parmi ceux qui existe pour cette liaison.
201 *
202 * @see lister_objets_lies()
203 *
204 * @param string $objet_source Objet dont on veut récupérer la liste des identifiants
205 * @param string $objet Objet sur lequel est liée la source
206 * @param int $id_objet Identifiant d'objet sur lequel est liée la source
207 * @param string $objet_lien Objet dont on utilise la table de liaison
208 * (c'est forcément soit $objet_source, soit $objet)
209 * @return array Liste des identifiants
210 */
211 function roles_complets($objet_source, $objet, $id_objet, $objet_lien) {
212
213 $presents = roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien);
214 // pas de roles sur ces objets => la liste par defaut, comme sans role
215 if ($presents === false) {
216 return lister_objets_lies($objet_source, $objet, $id_objet, $objet_lien);
217 }
218
219 // types de roles possibles
220 $roles_possibles = $presents['roles']['roles']['choix'];
221 // couples id / roles
222 $ids = $presents['ids'];
223
224 // pour chaque groupe, on fait le diff entre tous les roles possibles
225 // et les roles attribués à l'élément : s'il en reste, c'est que l'élément
226 // n'est pas complet
227 $complets = array();
228 foreach ($ids as $id => $roles_presents) {
229 if (!array_diff($roles_possibles, $roles_presents)) {
230 $complets[] = $id;
231 }
232 }
233
234 return $complets;
235 }
236
237
238 /**
239 * Liste les roles attribués entre 2 objets/id_objet sur une table de liaison donnée
240 *
241 * @param string $id_objet_source Identifiant de l'objet qu'on lie
242 * @param string $objet_source Objet qu'on lie
243 * @param string $objet Objet sur lequel est liée la source
244 * @param int $id_objet Identifiant d'objet sur lequel est liée la source
245 * @param string $objet_lien Objet dont on utilise la table de liaison
246 * (c'est forcément soit $objet_source, soit $objet)
247 * @return array Liste des roles
248 */
249 function roles_presents_sur_id($id_objet_source, $objet_source, $objet, $id_objet, $objet_lien) {
250
251 $presents = roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien);
252 // pas de roles sur ces objets => la liste par defaut, comme sans role
253 if ($presents === false) {
254 return array();
255 }
256
257 if (!isset($presents['ids'][$id_objet_source])) {
258 return array();
259 }
260
261 return $presents['ids'][$id_objet_source];
262 }
263
264
265 /**
266 * Lister des rôles présents sur une liaion, pour un objet sur un autre,
267 * classés par identifiant de l'objet
268 *
269 * Lister les id objet_source associés à l'objet id_objet
270 * via la table de lien objet_lien, et groupe cette liste
271 * par identifiant (la clé) et ses roles attribués (tableau de valeur)
272 *
273 * On retourne cette liste dans l'index 'ids' et la description des roles
274 * pour la liaison dans l'index 'roles' pour éviter le le faire recalculer
275 * aux fonctions utilisant celle ci.
276 *
277 * @param string $objet_source Objet dont on veut récupérer la liste des identifiants
278 * @param string $objet Objet sur lequel est liée la source
279 * @param int $id_objet Identifiant d'objet sur lequel est liée la source
280 * @param string $objet_lien Objet dont on utilise la table de liaison
281 * (c'est forcément soit $objet_source, soit $objet)
282 * @return array|bool
283 * - Tableau d'index
284 * - roles : tableau de description des roles,
285 * - ids : tableau des identifiants / roles.
286 * - False si pas de role déclarés
287 */
288 function roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien) {
289 static $done = array();
290
291 // stocker le résultat
292 $hash = "$objet_source-$objet-$id_objet-$objet_lien";
293 if (isset($done[$hash])) {
294 return $done[$hash];
295 }
296
297 // pas de roles sur ces objets, on sort
298 $roles = roles_presents($objet_lien, ($objet_lien == $objet) ? $objet_source : $objet);
299 if (!$roles) {
300 return $done[$hash] = false;
301 }
302
303 // inspiré de lister_objets_lies()
304 if ($objet_lien == $objet) {
305 $res = objet_trouver_liens(array($objet => $id_objet), array($objet_source => '*'));
306 } else {
307 $res = objet_trouver_liens(array($objet_source => '*'), array($objet => $id_objet));
308 }
309
310 // types de roles possibles
311 $roles_possibles = $roles['roles']['choix'];
312 // colonne du role
313 $colonne = $roles['colonne'];
314
315 // on recupere par id, et role existant
316 $ids = array();
317 while ($row = array_shift($res)) {
318 $id = $row[$objet_source];
319 if (!isset($ids[$id])) {
320 $ids[$id] = array();
321 }
322 // tableau des roles présents
323 $ids[$id][] = $row[$colonne];
324 }
325
326 return $done[$hash] = array(
327 'roles' => $roles,
328 'ids' => $ids
329 );
330 }
331
332
333 /**
334 * Lister des rôles connus en base pour une liaion, pour un objet source
335 *
336 * On retourne cette liste dans le datalist de saisie libre role.
337 *
338 * @param string $objet_source Objet dont on veut récupérer la liste des identifiants
339 * @param string $objet Objet sur lequel est liée la source
340 * @param string $objet_lien Objet dont on utilise la table de liaison
341 * (c'est forcément soit $objet_source, soit $objet)
342 * @return array|bool
343 * - Tableau de roles : tableau de description des roles,
344 * - false si pas de role déclarés
345 */
346 function roles_connus_en_base($objet_source, $objet, $objet_lien) {
347 static $done = array();
348
349 // stocker le résultat
350 $hash = "$objet_source-$objet-$objet_lien";
351 if (isset($done[$hash])) {
352 return $done[$hash];
353 }
354
355 if (!$lien = objet_associable($objet_lien)) {
356 return $done[$hash] = false;
357 }
358
359 // pas de roles sur ces objets, on sort
360 $roles = roles_presents($objet_lien, ($objet_lien == $objet) ? $objet_source : $objet);
361 if (!$roles) {
362 return $done[$hash] = false;
363 }
364
365 list($primary, $l) = $lien;
366 $colone_role = $roles['colonne'];
367
368 $all = sql_allfetsel(
369 "DISTINCT $colone_role",
370 $l,
371 'objet=' . sql_quote(($objet_source == $objet_lien) ? $objet : $objet_source)
372 );
373 $done[$hash] = array_map('reset', $all);
374
375 return $done[$hash];
376 }