[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / ecrire / inc / roles.php
diff --git a/www/ecrire/inc/roles.php b/www/ecrire/inc/roles.php
new file mode 100644 (file)
index 0000000..e96f721
--- /dev/null
@@ -0,0 +1,376 @@
+<?php
+
+/***************************************************************************\
+ *  SPIP, Systeme de publication pour l'internet                           *
+ *                                                                         *
+ *  Copyright (c) 2001-2017                                                *
+ *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
+ *                                                                         *
+ *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
+ *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
+\***************************************************************************/
+
+/**
+ * Gestion des rôles
+ *
+ * Les rôles sont une qualification précise sur une liaison entre
+ * deux objets. Ils doivent être définis dans la déclaration d'un objet
+ * pour être utilisés. Ils s'appliquent sur une colonne particulière
+ * de la table de liaison, par défaut 'role'.
+ *
+ * Cette table de liaison, lorsqu'elle a des rôles n'a plus sa clé primaire
+ * sur le couple (id_x, objet, id_objet) mais sur (id_x, objet, id_objet, colonne_role)
+ * de sorte qu'il peut exister plusieurs liens entre 2 objets, mais avec
+ * des rôles différents. Chaque ligne de la table lien correspond alors à
+ * un des rôles.
+ *
+ * @package SPIP\Core\Roles
+ */
+
+if (!defined('_ECRIRE_INC_VERSION')) {
+       return;
+}
+
+
+/**
+ * Vérifie qu'un objet dispose de rôles fonctionnels
+ *
+ * Retourne une description des rôles si c'est le cas
+ *
+ * @param string $objet
+ *     Objet source qui possède la table de liaison
+ * @param string $objet_destination
+ *     Objet sur quoi on veut lier
+ *     Si défini, le retour ne contient que les roles possibles pour cet objet
+ *     Sinon retourne tous les roles possibles quelque soit l'objet
+ * @return bool|array
+ *     false si rôles indisponibles on non déclarés
+ *     array : description des roles applicables dans 3 index : colonne, titres, roles
+ **/
+function roles_presents($objet, $objet_destination = '') {
+       $desc = lister_tables_objets_sql(table_objet_sql($objet));
+
+       // pas de liste de roles, on sort
+       if (!isset($desc['roles_titres']) or !($titres = $desc['roles_titres'])) {
+               return false;
+       }
+
+       // on vérifie que la table de liaison existe
+       include_spip('action/editer_liens');
+       if (!$lien = objet_associable($objet)) {
+               return false;
+       }
+
+       // on cherche ensuite si la colonne existe bien dans la table de liaison (par défaut 'role')
+       $colonne = isset($desc['roles_colonne']) ? $desc['roles_colonne'] : 'role';
+       $trouver_table = charger_fonction('trouver_table', 'base');
+       list(, $table_lien) = $lien;
+       $desc_lien = $trouver_table($table_lien);
+       if (!isset($desc_lien['field'][$colonne])) {
+               return false;
+       }
+
+       // sur quoi peuvent s'appliquer nos rôles
+       if (!$application = $desc['roles_objets']) {
+               return false;
+       }
+
+       // destination presente, on restreint si possible
+       if ($objet_destination) {
+               $objet_destination = table_objet($objet_destination);
+
+               // pour l'objet
+               if (isset($application[$objet_destination])) {
+                       $application = $application[$objet_destination];
+                       // sinon pour tous les objets
+               } elseif (isset($application['*'])) {
+                       $application = $application['*'];
+               } // sinon tant pis
+               else {
+                       return false;
+               }
+       }
+
+       // tout est ok
+       return array(
+               'titres' => $titres,
+               'roles' => $application,
+               'colonne' => $colonne
+       );
+}
+
+/**
+ * Retrouve la colonne de liaison d'un rôle si définie entre 2 objets
+ *
+ * @param string $objet
+ *     Objet source qui possède la table de liaison
+ * @param string $objet_destination
+ *     Objet sur quoi on veut lier
+ * @return string
+ *     Nom de la colonne, sinon vide
+ **/
+function roles_colonne($objet, $objet_destination) {
+       if ($roles = roles_presents($objet, $objet_destination)) {
+               return $roles['colonne'];
+       }
+
+       return '';
+}
+
+
+/**
+ * Extrait le rôle et la colonne de role d'un tableau de qualification
+ *
+ * Calcule également une condition where pour ce rôle.
+ *
+ * Pour un objet pouvant recevoir des roles sur sa liaison avec un autre objet,
+ * on retrouve le rôle en question dans le tableau de qualification.
+ * Si le rôle n'est pas défini dedans, on prend le rôle par défaut
+ * déclaré.
+ *
+ * @param string $objet Objet source de la liaison
+ * @param string $objet_destination Objet de destination de la liaison
+ * @param array $qualif tableau de qualifications array(champ => valeur)
+ * @return array
+ *     Liste (role, colonne, (array)condition) si role possible
+ *     Liste ('', '', array()) sinon.
+ **/
+function roles_trouver_dans_qualif($objet, $objet_destination, $qualif = array()) {
+       // si des rôles sont possibles, on les utilise
+       $role = $colonne_role = ''; # role défini
+       // condition du where par defaut
+       $cond = array();
+       if ($roles = roles_presents($objet, $objet_destination)) {
+               $colonne_role = $roles['colonne'];
+               // qu'il n'est pas défini
+               if (!isset($qualif[$colonne_role])
+                       or !($role = $qualif[$colonne_role])
+               ) {
+                       $role = $roles['roles']['defaut'];
+               }
+               // where
+               $cond = array("$colonne_role=" . sql_quote($role));
+       }
+
+       return array($role, $colonne_role, $cond);
+}
+
+/**
+ * Gérer l'ajout dans la condition where du rôle
+ *
+ * On ajoute la condition uniquement si la liaison entre les 2 objets a une colonne de rôle !
+ *
+ * @param string $objet_source Objet source (qui possède la table de liens)
+ * @param string $objet Objet de destination
+ * @param array $cond
+ *     Tableau de conditions where
+ *     qui peut avoir un index spécial 'role' définissant le role à appliquer
+ *     ou valant '*' pour tous les roles.
+ * @param bool $tous_si_absent
+ *     true pour ne pas appliquer une condition sur le rôle s'il n'est pas indiqué
+ *     dans la liste des conditions entrantes. Autrement dit, on n'applique
+ *     pas de rôle par défaut si aucun n'est défini.
+ * @return array
+ *     Liste (Tableau de conditions where complété du role, Colonne du role, role utilisé)
+ **/
+function roles_creer_condition_role($objet_source, $objet, $cond, $tous_si_absent = false) {
+       // role par défaut, colonne
+       list($role_defaut, $colonne_role) = roles_trouver_dans_qualif($objet_source, $objet);
+
+       // chercher d'eventuels rôles transmis
+       $role = (isset($cond['role']) ? $cond['role'] : ($tous_si_absent ? '*' : $role_defaut));
+       unset($cond['role']); // cette condition est particuliere...
+
+       if ($colonne_role) {
+               // on ajoute la condition du role aux autres conditions.
+               if ($role != '*') {
+                       $cond[] = "$colonne_role=" . sql_quote($role);
+               }
+       }
+
+       return array($cond, $colonne_role, $role);
+}
+
+/**
+ * Liste des identifiants dont on ne peut ajouter de rôle
+ *
+ * Lister les id objet_source associés à l'objet id_objet
+ * via la table de lien objet_lien, et détermine dans cette liste
+ * lesquels ont les rôles complets, c'est à dire qu'on ne peut leur
+ * affecteur d'autres rôles parmi ceux qui existe pour cette liaison.
+ *
+ * @see lister_objets_lies()
+ *
+ * @param string $objet_source Objet dont on veut récupérer la liste des identifiants
+ * @param string $objet Objet sur lequel est liée la source
+ * @param int $id_objet Identifiant d'objet sur lequel est liée la source
+ * @param string $objet_lien Objet dont on utilise la table de liaison
+ *             (c'est forcément soit $objet_source, soit $objet)
+ * @return array               Liste des identifiants
+ */
+function roles_complets($objet_source, $objet, $id_objet, $objet_lien) {
+
+       $presents = roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien);
+       // pas de roles sur ces objets => la liste par defaut, comme sans role
+       if ($presents === false) {
+               return lister_objets_lies($objet_source, $objet, $id_objet, $objet_lien);
+       }
+
+       // types de roles possibles
+       $roles_possibles = $presents['roles']['roles']['choix'];
+       // couples id / roles
+       $ids = $presents['ids'];
+
+       // pour chaque groupe, on fait le diff entre tous les roles possibles
+       // et les roles attribués à l'élément : s'il en reste, c'est que l'élément
+       // n'est pas complet
+       $complets = array();
+       foreach ($ids as $id => $roles_presents) {
+               if (!array_diff($roles_possibles, $roles_presents)) {
+                       $complets[] = $id;
+               }
+       }
+
+       return $complets;
+}
+
+
+/**
+ * Liste les roles attribués entre 2 objets/id_objet sur une table de liaison donnée
+ *
+ * @param string $id_objet_source Identifiant de l'objet qu'on lie
+ * @param string $objet_source Objet qu'on lie
+ * @param string $objet Objet sur lequel est liée la source
+ * @param int $id_objet Identifiant d'objet sur lequel est liée la source
+ * @param string $objet_lien Objet dont on utilise la table de liaison
+ *             (c'est forcément soit $objet_source, soit $objet)
+ * @return array                  Liste des roles
+ */
+function roles_presents_sur_id($id_objet_source, $objet_source, $objet, $id_objet, $objet_lien) {
+
+       $presents = roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien);
+       // pas de roles sur ces objets => la liste par defaut, comme sans role
+       if ($presents === false) {
+               return array();
+       }
+
+       if (!isset($presents['ids'][$id_objet_source])) {
+               return array();
+       }
+
+       return $presents['ids'][$id_objet_source];
+}
+
+
+/**
+ * Lister des rôles présents sur une liaion, pour un objet sur un autre,
+ * classés par identifiant de l'objet
+ *
+ * Lister les id objet_source associés à l'objet id_objet
+ * via la table de lien objet_lien, et groupe cette liste
+ * par identifiant (la clé) et ses roles attribués (tableau de valeur)
+ *
+ * On retourne cette liste dans l'index 'ids' et la description des roles
+ * pour la liaison dans l'index 'roles' pour éviter le le faire recalculer
+ * aux fonctions utilisant celle ci.
+ *
+ * @param string $objet_source Objet dont on veut récupérer la liste des identifiants
+ * @param string $objet Objet sur lequel est liée la source
+ * @param int $id_objet Identifiant d'objet sur lequel est liée la source
+ * @param string $objet_lien Objet dont on utilise la table de liaison
+ *             (c'est forcément soit $objet_source, soit $objet)
+ * @return array|bool
+ *     - Tableau d'index
+ *       - roles : tableau de description des roles,
+ *       - ids   : tableau des identifiants / roles.
+ *     - False si pas de role déclarés
+ */
+function roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien) {
+       static $done = array();
+
+       // stocker le résultat
+       $hash = "$objet_source-$objet-$id_objet-$objet_lien";
+       if (isset($done[$hash])) {
+               return $done[$hash];
+       }
+
+       // pas de roles sur ces objets, on sort
+       $roles = roles_presents($objet_lien, ($objet_lien == $objet) ? $objet_source : $objet);
+       if (!$roles) {
+               return $done[$hash] = false;
+       }
+
+       // inspiré de lister_objets_lies()
+       if ($objet_lien == $objet) {
+               $res = objet_trouver_liens(array($objet => $id_objet), array($objet_source => '*'));
+       } else {
+               $res = objet_trouver_liens(array($objet_source => '*'), array($objet => $id_objet));
+       }
+
+       // types de roles possibles
+       $roles_possibles = $roles['roles']['choix'];
+       // colonne du role
+       $colonne = $roles['colonne'];
+
+       // on recupere par id, et role existant
+       $ids = array();
+       while ($row = array_shift($res)) {
+               $id = $row[$objet_source];
+               if (!isset($ids[$id])) {
+                       $ids[$id] = array();
+               }
+               // tableau des roles présents
+               $ids[$id][] = $row[$colonne];
+       }
+
+       return $done[$hash] = array(
+               'roles' => $roles,
+               'ids' => $ids
+       );
+}
+
+
+/**
+ * Lister des rôles connus en base pour une liaion, pour un objet source
+ *
+ * On retourne cette liste dans le datalist de saisie libre role.
+ *
+ * @param string $objet_source Objet dont on veut récupérer la liste des identifiants
+ * @param string $objet Objet sur lequel est liée la source
+ * @param string $objet_lien Objet dont on utilise la table de liaison
+ *             (c'est forcément soit $objet_source, soit $objet)
+ * @return array|bool
+ *     - Tableau de roles : tableau de description des roles,
+ *     - false si pas de role déclarés
+ */
+function roles_connus_en_base($objet_source, $objet, $objet_lien) {
+       static $done = array();
+
+       // stocker le résultat
+       $hash = "$objet_source-$objet-$objet_lien";
+       if (isset($done[$hash])) {
+               return $done[$hash];
+       }
+
+       if (!$lien = objet_associable($objet_lien)) {
+               return $done[$hash] = false;
+       }
+
+       // pas de roles sur ces objets, on sort
+       $roles = roles_presents($objet_lien, ($objet_lien == $objet) ? $objet_source : $objet);
+       if (!$roles) {
+               return $done[$hash] = false;
+       }
+
+       list($primary, $l) = $lien;
+       $colone_role = $roles['colonne'];
+
+       $all = sql_allfetsel(
+               "DISTINCT $colone_role",
+               $l,
+               'objet=' . sql_quote(($objet_source == $objet_lien) ? $objet : $objet_source)
+       );
+       $done[$hash] = array_map('reset', $all);
+
+       return $done[$hash];
+}