self::PROFILE_USER, 'Responsable' => self::PROFILE_RESPONSABLE, 'Administration' => self::PROFILE_ADMIN, //'Administration Plus' => self::PROFILE_ADMINPLUS, 'Super Administrateur' => self::PROFILE_SUPERADMIN ]; // - ATTRIBUTS VARIABLES // Genre // Par défaut : masculin public $is_masculin = true; /* public function isMasculin() { return isset($this->is_masculin) ? $this->is_masculin ; true; } */ // (EP) Seulement pour les tests // Permet à un test de forcer (si true) le re-chargement d'une entité (car modifiée en BD) protected static $RELOAD = FALSE; // (EP 202007) passé dans la table configuration => "mode_nolimit" // Il suffit de passer ceci à true pour TOUT autoriser à superadmin //protected $SUPERADMIN_CAN_DO_EVERYTHING = false; // (prod) Par défaut (false), il se comporte un peu comme ADMIN //protected $SUPERADMIN_CAN_DO_EVERYTHING = true; // (dev only) no limit, peut TOUT faire (attention, pas en prod svp !!!) protected $confLabinvent = null; // Le Controleur courant protected $c = null; // L'Action courante //protected $action = null; protected $a = null; /* La table courante (objet Table) * * $t = TableRegistry::getTableLocator()->get('Materiels'); (ou autre entité telle que 'Suivis' par exemple...) * */ protected $t = null; /* L'entité courante (objet Entity) * * $e = $t->newEntity(); * ou encore * $e = $t->get(12); * ou encore * $e = $t->find(...)->first(); * ou encore * $e = $t->find(...)->last(); * ... */ //protected $entity =null; protected $e = null; // remplace $entity // id de l'entité courante (shortcut pour $this->e->id) //protected $entity_id = null; protected $e_id = null; // remplace $entity_id // L'utilisateur courant (objet User Entity) // Current (priviledged) USER (if so, otherwise = NULL) from DB (Entity class) //private $CURRENT_PRIVILEDGED_USER = null; //protected $current_user_entity = null; protected $u = null; // remplace $current_user_entity protected $userName = null; // Current ROLE (by default = "Utilisateur") //private $CURRENT_ROLE = null; protected $user_role = null; // L'entité Materiel liée à une autre entité (telle qu'un suivi, un emprunt, un doc attaché...) protected static $related_materiel = null; /* * (EP 20200525) NEW ACL * * AUTORISATIONS * * Voir le tableau (feuille de calcul Google Sheet) https://docs.google.com/spreadsheets/d/16uAq_ko6bpKGxRZTL9rWq5XZ6kaVGMqowLURpBBdJJU/edit#gid=0 * */ //private $default_authorizations = [ const default_authorizations = [ /* * Chaque entrée initialise un tableau à 2 conditions : [ conditions d'accès sur le statut du matériel, conditions d'accès sur l'appartenance du matériel] * La 1ère condition, celle sur le statut, peut prendre des valeurs diverses telles que : 0 (pas de condition), 'CREATED', 'NOT ARCHIVED', ... * La 2ème condition ne peut prendre que 2 valeurs : 0 (pas de condition d'accès) ou 1 (le matériel doit "appartenir" à l'utilisateur) * Si une entrée est égale à 'default', ça signifie qu'elle a la même valeur que l'entrée 'default' (ici [0,0]) * * Exemples : * - [0,0] = aucune condition (toute personne connectée peut faire cette action, quelque soit le profil) * - [0,1] = pas de condition d'accès sur le statut du matériel, mais condition sur l'appartenance (le matériel doit "appartenir" à l'utilisateur) * - ['CREATED',0] = condition sur le statut (le matériel doit être CREATED), mais pas sur l'appartenance (il peut appartenir à n'importe qui) * - ['NOT ARCHIVED',0] = condition sur le statut (le matériel ne doit PAS être ARCHIVED), mais pas sur l'appartenance (il peut appartenir à n'importe qui) * ... */ 'alias' => '', 'default' => 0, 'user' => 'default', 'resp' => 'default', 'admin' => 'default', //'adminp' => 'default', 'super' => 'default' /* // Par défaut (Règle Genérale), il n'y a pas de condition d'accès, donc on peut accéder à l'action sans condition 'default' => 0, //'default' => [0,0], // idem 0 // accès interdit //'default' => -1, // chaque profil utilise par défaut les régles générales (celles de l'entrée 'default') /S 'USER' => 'default', 'RESP' => 'default', 'ADMIN' => 'default', 'SUPER' => 'default', S/ 'Utilisateur' => 'default', 'Responsable' => 'default', 'Administration' => 'default', 'Administration Plus' => 'default', 'Super Administrateur' => 'default', //'Super Administrateur' => -1 */ ]; // default_authorizations /* const default_authorizations_adminonly = [ 'default' => 0, 'user' => -1, 'resp' => -1, 'admin' => 'default', 'adminp' => 'default', 'super' => 'default' ]; */ /* * * Tableau (array) des actions autorisées à envoyer des notifications (log et/ou email) * * 'log' = logger seulement * 'mail' => envoyer un mail seulement * 'both' = faire les 2 (logger ET envoyer un mail) * * Hérité par CHAQUE controleur (qui a donc sa propre instance de ce tableau, indépendante des instances des autres controleurs) * * On l'initialise déjà avec les actions autorisées par défaut, pour tout controleur * On le complètera ensuite pour les autres actions * * protected $notifier_actions = [ 'add' => 'both', 'edit' => 'log', 'delete' => 'both', // ... ]; * * Par défaut => aucune action autorisée * */ protected $notifier_actions = []; /* * Tableau (array) des autorisations pour les actions du controleur * * Hérité par CHAQUE controleur (qui a donc sa propre instance de ce tableau, indépendante des instances des autres controleurs) * * On l'initialise déjà avec les actions autorisées par défaut, pour tout controleur * On le complètera ensuite pour les autres actions * * Par défaut => aucune action autorisée * */ protected $is_authorized_action = []; /* // autorisé pour tous : 'index' => self::default_authorizations, 'view' => self::default_authorizations, 'find' => self::default_authorizations, // admin(+) only : 'add' => self::default_authorizations_adminonly, 'edit' => self::default_authorizations_adminonly, 'delete' => self::default_authorizations_adminonly, // Uniquement pour le controleur de pages PagesController (1 seule action autorisée : display) //'display' => self::default_authorizations, ]; */ // Valeurs par défaut pour TOUS les controleurs //const actionNounAndPastVerbs = [ //protected static $actionNounAndPastVerbs = [ protected $actionsNounAndPastVerb = [ // C 'add' => ['Ajout','ajouté'], // R 'view' => ['Visualisation (détail)','visualisé'], 'index' => ['Visualisation (liste)','visualisé'], // U 'edit' => ['Modification','modifié'], // D 'delete' => ['Suppression','supprimé'], ]; // Les matos peuvent-ils être commandés (bouton commander) ? // vrai par défaut (sauf si explicité dans la config) public static function hasOrderButton() { // OPTIM : si déjà lu, on relit pas if (! is_null(self::$HAS_ORDER_BUTTON)) return self::$HAS_ORDER_BUTTON; // Pas encore lu, on lit $has_order_button = Configure::readOrFail('MANDATORY_AND_READONLY_FIELDS.GENERAL.HAS_ORDER_BUTTON'); //debug($has_order_button); $has_order_button = $has_order_button['selected']; //if (is_null($has_order_button)) $has_order_button = true; self::$HAS_ORDER_BUTTON = $has_order_button; return $has_order_button; } protected function setActionsNounAndPastVerb(array $actionsNounAndPastVerb) { /* Non, finalement on fait ça dans le get() // Si genre féminin, on féminise tous les verbes en ajoutant 'e' à la fin (ajouté-e, modifié-e, ...) if (! $this->is_masculin) { //debug("here2"); foreach ($this->actionsNounAndPastVerb as $action=>$nounAndPastVerb) { //debug($nounAndPastVerb[1]); //debug(substr($nounAndPastVerb[1],-2)); if (substr($nounAndPastVerb[1],-2) == 'é') $this->actionsNounAndPastVerb[$action][1] = $nounAndPastVerb[1].'e'; //debug($this->actionsNounAndPastVerb[$action][1]); } } */ foreach ($actionsNounAndPastVerb as $action=>$nounAndPastVerb) $this->actionsNounAndPastVerb[$action] = $nounAndPastVerb; } /* * Retourne ['nom','verbe_au_passé non conjugué', 'verbe au passé conjugué'] correspondants à l'action $action * * ex: * => ['Ajout','ajouté','ajouté-e'] pour l'action 'add' * => ['Modification','modifié','modifié-e'] pour l'action 'edit' * ... * * Actions générales : add, edit, delete, view... * */ public function getActionNounAndPastVerb($action) { //if (in_array($action, array_keys($this->actionNounAndPastVerbs))) return $this->actionNounAndPastVerbs[$action]; //$control = $this->name.'Controller'; //if (in_array($action, array_keys($control::$actionNounAndPastVerbs))) if (in_array($action, array_keys($this->actionsNounAndPastVerb))) $elems = $this->actionsNounAndPastVerb[$action]; /* //elseif (in_array($action, array_keys(AppController::actionNounAndPastVerbs))) $elems = AppController::actionNounAndPastVerbs[$action]; elseif (in_array($action, array_keys(AppController::$actionNounAndPastVerbs))) $elems = AppController::$actionNounAndPastVerbs[$action]; */ // Pas de définition pour cette action //return [null,null]; // par défaut, par exemple 'edit-ion' et 'edit-é' pour action 'edit' else $elems = [$action.'ion', $action.'é']; //return $elems; $elems = [ // Nom 'noun' => $elems[0], // Verbe au participe passé, sans conjuguaison : 'ajouté', 'modifié' 'past_verb' => $elems[1], // Verbe au participe passé, conjugué (masculin/féminin) : 'ajouté-e', 'modifié-e' 'past_verb_conj' => $elems[1], 'noun_article' => isset($elems[2]) ? $elems[2] : null, 'verb_article' => isset($elems[3]) ? $elems[3] : null, ]; // normalement substr(string,-1), mais -2 à cause accent 'é', bizarre... if ( !$this->is_masculin && substr($elems['past_verb_conj'],-2)=='é' ) $elems['past_verb_conj'] = $elems['past_verb_conj'].'e'; return $elems; } // "le materiel", "le suivi", "l'emprunt", "la catégorie"... // Par défaut public function getArticle() { return "Le "; } // Jolis labels pour chaque champ de l'entité // Par défaut, aucun => on utilise donc directement le nom des champs protected $nice_field_labels = []; public function getNiceLabelForFieldName($fname) { return isset($this->nice_field_labels[$fname]) ? $this->nice_field_labels[$fname] : $fname; } public function getFieldsLabelsForEntity(Entity $e) { $fields_label = []; //foreach ( array_keys($e->toArray()) as $fname ) foreach ( $e->toArray() as $fname=>$fval ) if (!is_array($fval)) $fields_label[$fname] = $this->getNiceLabelForFieldName($fname); return $fields_label; } /* * Retourne le nom du controleur qui correspond à la FK $fk_name * ex: * - sur_categorie_id => SurCategories * - materiel_id => Materiels * - ... * */ protected function getControllerNameForFK($fk_name) { // 1) on enlève la fin '_id' $fk_name = substr($fk_name,0,-3); //debug($fk_name); exit; // 2) on met la 1ère lettre de chaque mot en majuscule $words = explode('_',$fk_name); $controller_name = ''; foreach ($words as $word) $controller_name .= ucfirst($word); // 3) on ajoute un 's' return $controller_name.'s'; } // SousCategories => SousCategoriesController instance protected function getControllerInstanceForName($controller_name) { $c = 'App\\Controller\\'.$controller_name.'Controller'; return new $c(); } protected function getRealControllerNameForAlias($alias_name) { return $alias_name; } /* * Retourne le nom de la fk contained qui correspond au nom de controleur $controller_name * ex: * - SurCategories => sur_categories * - Materiels => materiels * - ... * */ protected function getFkContainedNameForControllerName($controller_name) { return Inflector::underscore($controller_name); } protected function is_vowel($char) { return in_array(strtolower($char), ['a','e','i','o','u','y']); } // $form=1 => 'le', 'la', ou "l'" // $form=2 => 'du' ou 'de la' ou "de l'" // $form=3 => "d'un" ou "d'une" protected function getMyArticle($form=1) { $first_char_is_vowel = $this->is_vowel(substr($this->getName(),0,1)); // $form=1 => 'Le', 'La', ou "L'" if ($form==1) { // Si commence par voyelle => "l'" (l'emprunt, l'utilisateur, l'entité...) if ($first_char_is_vowel) return "l'"; return $this->is_masculin ? "le " : "la "; } // $form=2 => 'du' ou 'de la' ou "de l'" elseif ($form==2) { if ($first_char_is_vowel) return "de l'"; return $this->is_masculin ? "du " : "de la "; } // $form=3 => "d'un" ou "d'une" else return $this->is_masculin ? "d'un " : "d'une "; } // MODE DEBUG LOCAL : lié à variable $this->DEBUG public function d($msg) { if ( $this->DEBUG || $this->isLabinventDebugMode() ) pr($msg); } public function d2($msg) { if ( $this->DEBUG || $this->isLabinventDebugMode() ) debug($msg); } // Nom du champ "nom" de l'entité (par défaut => 'nom') // To be overriden public function getNameFieldLabel() { //return 'name'; return 'nom'; } /* * (EP202010) * La valeur du champ 'nom' (ou designation, ou ...) de cette entité * (en minuscules) * * On utilie getMyName() pour éviter d'écraser * la méthode par défaut CakePhp getName() * qui renvoie le nom du controleur * (équivalente à $this->name) */ public function getMyName() { $fname = $this->getNameFieldLabel(); return strtolower($this->$fname); } /* Nom affichable pour cette entité * (en minuscules) * * Par défaut => $this->name * * Ex: * - Suivi => 'suivi' * - Materiel => 'matériel' * - Category => 'catégorie' * - User => 'utilisateur' * ... */ //public static function getNiceTypeName() { public function getNiceNameSingularLowerCase($alias_controller_name=null) { //return strtolower($this->name); //return __CLASS__; //return strtolower( substr($this->getName(),0,-1) ); // 1) on récupère chaque mot du nom du controleur $words = explode(' ',$this->getNiceNamePluralLowerCase($alias_controller_name)); //debug($words); // 2) on supprime les 's' éventuels à la fin de chaque mot $type_name_singular = ''; foreach($words as $word) $type_name_singular .= (substr($word,-1)=='s') ? substr($word,0,-1).' ' : $word.' '; //debug($type_name_singular); return rtrim($type_name_singular); //return substr($this->getNiceNamePluralLowerCase(),0,-1); } public function getNiceName() { return $this->getNiceNameSingularLowerCase(); } // Nom pluriel affichable pour cette entité public function getNiceNamePluralLowerCase($alias_controller_name=null) { // ex: 'suivis', 'matériels', 'catégories', ... //return strtolower($this->name); //return __CLASS__; return strtolower($this->getName()); } // ex: 'sur_categorie_id' public function getFkName() { // ex: SurCategories => sur_categories //debug($this->getName()); //debug($this->name); //$fkname = Inflector::tableize($this->getName()); //$fkname = Inflector::humanize($this->getName()); //$fkname = Inflector::camelize($this->getName()); $fkname = Inflector::underscore($this->getName()); //debug($fkname); //exit; // ex: sur_categorie $fkname = substr($fkname,0,-1); return $fkname.'_id'; } /* * Retourne le matériel courant, c'est à dire : * - soit l'entité courante si on est dans MaterielsController * - soit le materiel associé à l'entité courante si on est dans un autre controleur (lié aux matériels : SuivisController, EmpruntsController, DocumentsController...) protected function getCurrentMateriel($id) { //debug("controleur is ".$this->name); //if ($this->name == 'Materiels') return $this->e; // Si on est dans MaterielsController, retourner l'entité courante (puisque c'est un matériel) // Sinon (on est dans un controleur lié aux matériels tel que SuivisController, EmpruntsController, DocumentsController...) // => on retourne le matériel associé (related) à l'entité courante (Suivi, Emprunt, Document...) $m = $this->name=='Materiels' ? $this->getEntity($id) : $this->getMateriel($id); return $m; } */ /** * * (EP 20200922) NEW NOTIFICATIONS MANAGEMENT * */ public function getNotifications() { return $this->notifier_actions; } protected function setNotificationAllowedOnActions(array $actions) { $this->notifier_actions = $actions; } protected function isNotifierAction($action) { //debug($action); //debug($this->notifier_actions); return in_array( $action, array_keys($this->notifier_actions) ); } protected function isNotifierActionSendingLog($action) { return $this->isNotifierAction($action) && in_array( $this->notifier_actions[$action], ['log','both'] ); } protected function isNotifierActionSendingEmail($action) { return $this->isNotifierAction($action) && in_array( $this->notifier_actions[$action], ['mail','both'] ); } /** * (EP 20200525) NEW ACL * * @param string $action * @param array $authorizations = [ * 'default' => 0, * 'user' => 'default', * 'resp' => 'default', * 'admin' => 'default', * //'adminp' => 'default', * 'super' => 'default' * ] * */ //protected function setAuthorizationsForAction($action, $default, $user=null, $resp=null, $admin=null, $adminp=null, $super=null) { // php7: //protected function setAuthorizationsForAction(string $action, $default, array $authorizations=null) { // php5: protected function setAuthorizationsForAction($action, $default, array $authorizations=[]) { /* * Paramètres "nommés" simulés avec le paramètre $authorizations : * (https://phil.tech/2013/named-parameters-in-php) * * Comment ça marche ? * 1) "$authorizations +=" n'ajoute QUE les arguments qui n'ont pas déjà été passés en paramètres * 2) "extract" transforme les arguments de $authorizations en variables directes : $user, $resp, ... */ $authorizations += [ 'user' => null, 'resp' => null, 'admin' => null, //'adminp' => null, 'super' => null, ]; //extract($authorizations); /* // (raccourci) Reference vers le tableau $action_rules = &$this->is_authorized_action[$action]; // initialisation du tableau pour cette action pour le controleur spécifique en cours (des zéros partout = pas d'autorisation, par défaut) $action_rules = self::default_authorizations; */ //$roles_short = ['default', 'user', 'resp', 'admin', 'adminp', 'super']; //$roles_short = ['user', 'resp', 'admin', 'adminp', 'super']; $roles_short = ['user', 'resp', 'admin', 'super']; $alias = ''; $action_and_alias = explode('(',$action); if (count($action_and_alias)==2) { $action = trim($action_and_alias[0]); $alias = substr($action_and_alias[1],0,-1); } //debug("action=$action, alias=$alias"); // initialisation du tableau pour cette action pour le controleur spécifique en cours $this->is_authorized_action[$action] = self::default_authorizations; // Ajout de l'alias dans le tableau des autorisations pour cette action $this->is_authorized_action[$action]['alias'] = $alias; // (raccourci) Reference vers le tableau $action_rules = &$this->is_authorized_action[$action]; $S = false; //$S = $action == 'edit'; //$S = $action == 'statusTobearchived'; //$S = $action == 'sortiePdf'; //$S = $action == 'admissionPdf'; //$S = $this->name == 'Suivis'; // Initialisation de la règle générale par défaut (pour cette action) // Si la règle par défaut est un alias vers une autre action (ex: 'add'), on remplace l'alias par sa valeur (la règle associée à l'action pointée par l'alias) // Par exemple, si la règle pour 'edit' est la même que pour 'add', on initialise la règle pour 'edit' par la règle pour 'add', logique $roles = array_keys(self::PROFILES); //$default = $authorizations['default']; // 1) Initialisation de la règle générale (par défaut) et de la règle concernant CHAQUE role (pour cette action) $action_rules['default'] = $default; if ($S) { debug("0a) action $action"); debug("action_rules:"); debug($action_rules); } /* $roles_var = ['user', 'resp', 'admin', 'adminp', 'super']; //foreach ($roles_var as $role_var) if ($$role_var) $action_rules[$role] = $$role_var; for ($i=0; $i<=4; $i++) { $role_var = $roles_var[$i]; $role = $roles[$i]; if ($i==0) debug("$role_var, ".$$role_var.", $role"); if ($$role_var !== null) $action_rules[$role] = $$role_var; } */ // 2) Intialisation de la règle pour chaque profil //debug("user value is"); debug($user); //$roles_var = [$user, $resp, $admin, $adminp, $super]; //foreach ($roles_var as $role_var) if ($$role_var) $action_rules[$role] = $$role_var; foreach ($roles_short as $role_short) { $role_cond = &$authorizations[$role_short]; //if ($S) { debug("condition for role $role_short"); debug($role_cond); } if (isset($role_cond) && $role_cond!==null) $action_rules[$role_short] = $role_cond; } if ($S) { debug("0b) action $action"); debug("action_rules:"); debug($action_rules); } // 3) Des-aliasing de chaque règle // - règle générale (par défaut) /* ENORME BUG PHP : in_array(0,['add','edit'])) => renvoie TRUE !!! 0 == 'add' => renvoie TRUE !!! */ $all_actions = array_keys($this->is_authorized_action); //if ($default!==0 && in_array($default,['add','edit','admission','sortie'])) { if ($default!==0 && in_array($default,$all_actions)) { $action_rules['default'] = $this->is_authorized_action[$default]['default']; } // - règle pour chaque role if ($S) { debug("1) action $action"); debug("action_rules:"); debug($action_rules); } //foreach ($roles as $role) //$roles_short = ['user']; foreach ($roles_short as $role) { //debug("- role is $role"); //if ($role == 'Administration Plus') continue; if (! isset($action_rules[$role])) continue; $rule = &$action_rules[$role]; //debug("RULE is"); debug($rule); // règle = 'default' if ($rule === 0) continue; // car sinon, bug php, in_array répond toujours TRUE !!! elseif ($rule === 'default') $rule = $action_rules['default']; // règle = un autre role elseif (in_array($rule,$roles_short)) { //debug("$rule in roles"); $rule = $action_rules[$rule]; } // règle = une autre action (= 'add' ou 'edit') elseif (in_array($rule,['add','edit'])) $rule = $this->is_authorized_action[$rule][$role]; elseif (is_array($rule) && $rule[0]==='default') $rule[0] = $action_rules['default'][0]; //if ($role=='user') debug("RULE is"); debug($rule); //debug("action_rules:"); debug($action_rules); } if ($S) { debug("2) action $action"); debug("action_rules:"); debug($action_rules); } } // setAuthorizationsForAction public function getAuthorizations($action=null, $role=null) { if (!$action) return $this->is_authorized_action; if (!$role) return $this->is_authorized_action[$action]; return $this->is_authorized_action[$action][$role]; } /* * Retourne le materiel associé à l'entité courante * (par exemple, le materiel associé au suivi $id) * * $id : * - si $action=='add' => c'est l'id du materiel * - sinon => c'est l'id de l'entité (suivi, emprunt...) * * Cette méthode est surchargée par MaterielsController * qui ne renvoit que le materiel d'id $id * */ //protected function getRelatedMaterielForId($id, $action=null) { protected function getRelatedMaterielForId($id, $IS_RELATED_ENTITY_ID=false) { if (!$id) throw new \ErrorException("L'id ($id) n'est pas positionné (=0 ou null) !!!"); // Si action 'add', id = celui du materiel //if ($action == 'add') return $this->getMateriel($id); // Si $IS_RELATED_ENTITY_ID => $id est l'id d'une entité associée (a priori Materiels) if ($IS_RELATED_ENTITY_ID) return $this->getMateriel($id); // Sinon, id = celui de l'entité (emprunt) return $this->getEntity($id, $WITH_RELATED_MATERIEL=true)->materiel; } protected function getRelatedMaterielForEntityId($id, $related_matos_id=null) { if (!$id && !$related_matos_id) throw new \ErrorException("L'id ($id) et le related_matos_id ($related_matos_id) ne sont pas positionnés (=0 ou null) !!!"); if ($id) return $this->getEntity($id, $WITH_RELATED_MATERIEL=true)->materiel; return $this->getMateriel($related_matos_id); } protected function getMaterielOrRelatedMaterielForEntityId($id, $related_matos_id=null) { return ($this->name == 'Materiels') ? $this->getEntity($id) : $this->getRelatedMaterielForEntityId($id, $related_matos_id); } /* // Lève une exception si pas implémentée protected function getRelatedMaterielForId($id, $action=null) { throw new \Exception("Pour déterminer si la condition d'accès à l'action est respectée, il faut un matériel défini (or \$m est null) !"); } */ // WRAPPER sur tableau des autorisations is_authorized_action[] public function getAccessConditionForActionAndRole($action, $role) { //debug($this->is_authorized_action[$action]); // Si pas de règle définie pour l'action => accès interdit if (!isset($this->is_authorized_action[$action][$role])) return -1; //throw new \ErrorException("L'action '$action' n'a pas de condition d'accès définie dans le controleur $this->name (role '$role') !!"); $access_condition = $this->is_authorized_action[$action][$role]; if ($access_condition==='default') $access_condition = $this->is_authorized_action[$action]['default']; return $access_condition; } // @deprecated public function isAuthorizedAction($action, $id=null, $related_matos_id=null, $user=null) { return $this->isAuthorizedActionForCurrentUser($action, $id, $related_matos_id, $user); } /** * @param string $action * @param int $id => entity id * @param int $related_matos_id => related materiel id * @param $user => Utilisateur courant (null par défaut car on prend celui de la session en cours ; si pas null on prend ce user (qui est quand meme l'utilisateur courant)) * * @throws \ErrorException * @throws \Exception * * @return boolean */ // php7: //protected function isAuthorizedAction(string $action, int $id=null, bool $IS_RELATED_ENTITY_ID=false, $user=null) { // php5: //protected function isAuthorizedAction($action, $id=null, $IS_RELATED_ENTITY_ID=false, $user=null) { //public function isAuthorizedAction($action, $id=null, $IS_RELATED_ENTITY_ID=false, $user=null) { public function isAuthorizedActionForCurrentUser($action, $id=null, $related_matos_id=null, $user=null) { //return $this->isAuthorizedActionForRole($this->user_role, $action, $id); //return $this->isAuthorizedActionForRole($this->getUserRole($user), $action, $id, $IS_RELATED_ENTITY_ID, $user); ####$this->d("IN isAuthorizedActionForCurrentUser(): controleur $this->name, action $action"); //return $this->isAuthorizedActionForRole($this->getUserRole($user), $action, $id, $related_matos_id, $user); //return $this->isAuthorizedActionForCurrentUser($action, $id, $related_matos_id, $user); $role_long = $this->getUserRole($user); ####$this->d("*************************** CONTROLEUR ".$this->name.", ACTION $action, ROLE $role_long, id=$id, related_matos_id=$related_matos_id"); //$this->d("********* USER :"); $this->d2($user); //if ($this->SUPERADMIN_CAN_DO_EVERYTHING) return TRUE; if ($this->confLabinvent->mode_nolimit) return TRUE; //if ($action=='statusTobearchived'); exit; //$m = ($m_id) ? $this->getCurrentMateriel($m_id) : null; //$m = null; /* if ($this->name=='Materiels' && $id!==null) $m = $this->getEntity($id); if ($this->name!='Materiels' && $related_matos_id!==null) $m = $this->getRelatedMaterielForId($id); debug("matos is"); debug($m); */ switch ($role_long) { case 'Utilisateur': $role='user'; break; case 'Responsable': $role='resp'; break; case 'Administration': $role='admin'; break; //case 'Administration Plus': $role='adminp'; break; case 'Super Administrateur': $role='super'; } /* * Cas particulier du controleur de pages (PagesController) et de son unique action "display" * => on remplace l'action par le nom de la page */ if ($this->name == 'Pages') { // Si la page n'existe pas => accès rejeté $action = "display/".$this->page; if (! in_array($action, array_keys($this->is_authorized_action))) return false; } // Pour cette action et ce role : //debug("role is $role"); //$access_condition = $this->is_authorized_action[$action][$role]; $access_condition = $this->getAccessConditionForActionAndRole($action,$role); ####$this->d("0) Condition (complète) (NOT desaliased) :"); $this->d($access_condition); //debug($this->is_authorized_action); //debug($this->is_authorized_action[$action]); //debug("access_condition"); debug($access_condition); //debug($this->is_authorized_action); // Si la condition est un alias vers une autre entrée (style 'default' ou 'admin'...), on remplace cet alias par sa valeur //if ( in_array($access_condition, ['default', 'user', 'resp', 'admin']) ) if ( !is_int($access_condition) && ( $access_condition=='default' || in_array($access_condition, array_keys(self::PROFILES)) ) ) { $this->is_authorized_action[$action][$role] = $this->is_authorized_action[$action][$access_condition]; // 2) Maintenant, on n'a plus d'alias vers une condition, mais une vraie condition qu'on peut appliquer directement $access_condition = $this->is_authorized_action[$action][$role]; } // - si -1 => acccès refusé ####$this->d("1) Condition (complète) (desaliased) :"); $this->d($access_condition); //if ($action=='debugOff') debug($access_condition); exit; if ($access_condition === -1) return FALSE; if ($access_condition === 0) return TRUE; // A partir d'ici, on doit avoir un array //if ($action=='sortiePdf') exit; if (! is_array($access_condition)) throw new \ErrorException("La condition ($access_condition) pour autoriser l'action ($action) doit être -1, 0, ou bien un tableau !"); //if ($action=='admissionPdf') exit; // - sinon, cette condition est un tableau à 2 valeurs : [condition de statut , condition d'appartenance] // Les 2 conditions doivent être respectées : return $condition_on_status AND $condition_on_belonging $condition_on_status = $access_condition[0]; $condition_on_belonging = $access_condition[1]; // Il faut maintenant calculer chacune de ces conditions /* * a) Condition de Statut * * Peut prendre diverses formes : * - soit un zéro pour dire OK (pas de condition) : '0' * - soit un simple statut : 'CREATED', 'VALIDATED', ... * - soit un NON statut : 'NOT ARCHIVED' * - peut aussi être composé de 2 conditions séparées par un '&&' (ET) ou un '||' (OU) */ $this->d("- a) condition statut :"); $this->d($condition_on_status); $m = null; // 0 => pas de condition, c'est ok ! //if ($condition === 0) return TRUE; if ($condition_on_status === 0) $condition_on_status_result=TRUE; // -1 => pas ok ! elseif ($condition_on_status === -1) $condition_on_status_result=FALSE; else { //$m = $this->getRelatedMaterielForId($id, $action); // Uniquement pour 'add' d'un controleur autre que Materiels (car sinon c'est inutile) ////if ($action=='add' && $id==0) $id = posted_data.materiel_id (données POSTED contiennent l id du matos à tester, galère !!!) // Si c'est le controleur des materiels, on prend directement le materiel d'id $id // Sinon, on prend le materiel associé à l'entité //$m = ($this->name == 'Materiels') ? $this->getEntity($id) : $this->getRelatedMaterielForEntityId($id, $related_matos_id); $m = $this->getMaterielOrRelatedMaterielForEntityId($id, $related_matos_id); //debug($this->e); exit; $this->d("AppC: (related) matos id & status is $m->id, $m->status"); //$m = $this->getRelatedMaterielForId($id, $IS_RELATED_ENTITY_ID); //$condition_on_status_result = $this->eval_condition_on_status($condition_on_status, $id, $action, $m); //$condition_on_status_result = $this->eval_condition_on_status($condition_on_status, $id, $action, $m); //$condition_on_status_result = $this->eval_condition_on_status($condition_on_status, $id, $action, $IS_RELATED_ENTITY_ID, $m); //debug("condition");debug($condition_on_status); exit; $condition_on_status_result = $this->eval_condition_on_status($condition_on_status, $id, $action, $related_matos_id, $m); } $this->d("$condition_on_status evalué à "); $this->d2($condition_on_status_result); //if ($action=='printLabelRuban') { debug("coucou2"); exit; } // Si condition statut non respectée => access denied //if ($action=='admission') {debug("here"); exit;} if (! $condition_on_status_result) return FALSE; //if ($this->name=='Suivis') exit; /* // par défaut => faux $condition_on_status_result = FALSE; $statuses = MaterielsController::statuses; //debug("statuts:"); debug($statuses); //$statuses = ['CREATED', 'VALIDATED', 'TOBEARCHIVED', 'ARCHIVED']; // 0 => pas de condition, c'est ok ! if ($condition_on_status == 0) $condition_on_status_result = TRUE; // autre => à évaluer : qqch comme 'CREATED' ou 'NOT ARCHIVED' ou 'CREATED && autre chose'... else { // Est-ce une double condition séparée par un && ou un || ? $tokens = ['&&','||']; $found = false; foreach ($tokens as $token) { $pos = strpos($condition_on_status,$token); if ($pos > 0) { $conditions = explode($token, $condition_on_status); $conditions_result = []; foreach ($conditions as $condition) $conditions_result[] = $this->eval_condition_on_status($condition); $found = true; break; } } // found && ou || => 2 conditions à évaluer if ($found) $condition_on_status_result = eval("return $conditions_result[0] $token $conditions_result[1];"); // not found => 1 seule condition à évaluer : avec ou sans 'NOT' else { // - soit un "NON statut" : commence par 'NOT ' $NOT = FALSE; if (strpos($condition_on_status,'NOT ')===0) { $NOT = TRUE; // on prend le statut après le "NOT " : $condition_on_status = substr($condition_on_status,4); } // - soit un "statut" if (! in_array($condition_on_status, $statuses)) throw new \Exception("La condition de statut doit être soit un 'statut' soit un 'NOT statut' !"); if (!$m) throw new \Exception('Pour déterminer si la condition de statut est respectée, il faut un matériel défini (or $m est null) !'); $condition_on_status_result = $m->hasStatus($condition_on_status); if ($NOT) $condition_on_status_result = !$condition_on_status; } } */ // b) Condition d'Appartenance (belonging) $this->d("- b) condition appartenance :"); $this->d($condition_on_belonging); if (! in_array($condition_on_belonging, [-1,0,1])) throw new \Exception("La condition d'appartenance doit être 0 (pas de condition) ou 1 (doit appartenir) !"); // 0 => pas de condition, c'est ok ! if ($condition_on_belonging === -1) $condition_on_belonging_result = FALSE; elseif ($condition_on_belonging === 0) $condition_on_belonging_result = TRUE; // 1 => le matériel doit "appartenir" à l'utilisateur connecté else { //debug("********* USER1 IS:"); debug($user); $u = $this->getUserEntity($user); //debug("********* USER2 IS:"); debug($u); //exit; //$m = $m?:$this->getRelatedMaterielForId($id, $action); //debug($m); //$m = $m?:$this->getRelatedMaterielForId($id, $IS_RELATED_ENTITY_ID); $m = $m?:$this->getMaterielOrRelatedMaterielForEntityId($id, $related_matos_id); //debug($m); //debug($m); //$m = $m ? $m : $this->getRelatedMaterielForId($id); /* if (!$m) { throw new \Exception('Pour déterminer si la condition d\'appartenance est respectée, il faut un matériel défini (or $m est null) !'); } */ $condition_on_belonging_result = $m->belongsToUser($u->nom); // Si l'utilisateur courant a le profil "Responsable", il peut aussi faire cette action s'il est responsable du matériel $m if ($this->USER_IS_RESP()) $condition_on_belonging_result = $condition_on_belonging_result || $m->isSameGroupAsUser($u->groupes_metier_id, $u->groupes_thematique_id); } $this->d("$condition_on_belonging evalué à "); $this->d2($condition_on_belonging_result); //if ($this->name=='Emprunts') exit; //if ($this->name=='Documents') exit; // 3) return (1) && (2) $condition_result = $condition_on_status_result && $condition_on_belonging_result; $this->d("2) Condition globale (a && b) evaluée à :"); $this->d2($condition_result); return $condition_result; } // isAuthorizedActionForCurrentUser() /* * Fonction récursive qui évalue une condition de statut matériel. * Cette condition peut être simple ('CREATED') ou composée ('NOT CREATED && toto') */ //protected function eval_condition_on_status($condition, $id, $action, $m=null) { //protected function eval_condition_on_status($condition, $id, $action, $IS_RELATED_ENTITY_ID=false, $m=null) { protected function eval_condition_on_status($condition, $id, $action, $related_matos_id=null, $m=null) { // On enlève les espaces inutiles $condition = trim($condition); /* // 1) '0' => pas de condition, c'est ok ! //if ($condition === 0) return TRUE; if ($condition === '0') return TRUE; if ($condition === '-1') return FALSE; //if ($this->name=='Suivis') exit; */ $this->d("La (sous-)condition à évaluer est: $condition"); // 2) '[NOT] statut' => [NOT] eval(statut) $NOT = FALSE; if (strpos($condition,'NOT ') === 0) { $NOT = TRUE; // on prend le statut après le "NOT " : $condition = substr($condition,4); } // 3) un simple statut tel que 'CREATED', ou 'VALIDATED', ... //$m = $m?:$this->getRelatedMaterielForId($id, $action); //$m = $m?:$this->getRelatedMaterielForId($id, $IS_RELATED_ENTITY_ID); $m = $m?:$this->getMaterielOrRelatedMaterielForEntityId($id,$related_matos_id); //debug("m is"); debug($m); //if (! in_array($condition, $statuses)) throw new \Exception("La condition de statut doit être soit un 'statut' soit un 'NOT statut' !"); if (in_array($condition, array_keys(MaterielsController::statuses))) { //if (!$m) throw new \Exception("Pour déterminer si la condition de statut ($condition) est respectée, il faut un matériel défini (Or \$m est null) !"); $condition_result = ($m->status == $condition); if ($NOT) $condition_result = ! $condition_result; return $condition_result; } // 4) une simple valeur de config telle que conf.hasPrinter... $token = 'conf.'; //if ($condition == 'VALIDATED && conf.hasPrinter') { if (strpos($condition,$token) === 0) { $condition = substr($condition, strlen($token)); //debug("conf attr is $condition"); // ex: $this->confLabinvent->hasPrinter pour 'conf.hasPrinter' return $this->confLabinvent->$condition; } // 5) Double condition séparée par un && ou un || telle que 'expr1 && expr2' => eval(expr1) && eval(expr2) $tokens = ['&&','||']; //debug("tokens:"); debug($tokens); foreach ($tokens as $token) { $pos = strpos($condition,$token); if ($pos > 0) { $conditions = explode($token, $condition); $this->d("conditions:"); $this->d($conditions); $conditions_result = []; // Appel récursif //foreach ($conditions as $condition) $conditions_result[] = $this->eval_condition_on_status($condition, $id, $action, $m); //foreach ($conditions as $condition) $conditions_result[] = $this->eval_condition_on_status($condition, $id, $action, $IS_RELATED_ENTITY_ID, $m); foreach ($conditions as $condition) $conditions_result[] = $this->eval_condition_on_status($condition, $id, $action, $related_matos_id, $m); //debug($conditions_result[0]); //debug($conditions_result[1]); // Marche pas avec false !!! (uniquement avec true, dommage) //return eval("return (bool)$conditions_result[0] $token (bool)$conditions_result[1];"); // token AND (&&) if ($token == '&&') return $conditions_result[0] && $conditions_result[1]; // token OR (||) return $conditions_result[0] || $conditions_result[1]; } } // SINON => Exception, il y a qqch qui cloche dans la syntaxe throw new \Exception("La condition de statut ($condition) pour le controleur (".$this->name.") et l'action ($action) doit être soit un 'statut' soit un 'NOT statut' soit une condition double (séparée par '&&' ou '||') !"); } // eval_condition_on_status() // if (isset($this->easyACL['DEFAULT'][$action])) { public function OLD_hasACLRule($easyACL, $role, $action) { // return isset($this->easyACL[$role][$action]); // return (null !== self::easyACL[$role][$action]); // return array_key_exists($role, self::easyACL) && array_key_exists($action, self::easyACL[$role]); return array_key_exists($role, $easyACL) && array_key_exists($action, $easyACL[$role]); } // @todo public function startsWith($haystack, $needle) { return strpos($haystack, $needle) === 0; } /* * //@todo * public function endsWith($haystack, $needle) { * return strpos($haystack, $needle, strlen($haystack)-1) === 0; * } */ public function OLD_evalACL($condition) { return $condition; // Simple case if ($condition == 'Y') return true; if ($condition == 'F') return false; // Complex case // @todo return true; } public function OLD_evalSpecificRule($condition, AppController $controller, $user, $role, $action, $id = null) { // if starts with "&&" eval DEFAULT rule && specific rule if ($this->startsWith($condition, '&&')) { //debug($condition); //if (! isset($controller::easyACL['DEFAULT'][$action])) return new \Exception('bad rule'); if (! array_key_exists($action, $controller::OLD_easyACL['DEFAULT']) ) return new \Exception('bad rule'); //return $this->evalACL($controller->easyACL['DEFAULT'][$action]) && $this->evalACL($condition); return $this->OLD_evalACL($controller::OLD_easyACL['DEFAULT'][$action]).' ' . $this->OLD_evalACL($condition); } // otherwise, eval only specific rule return $this->OLD_evalACL($condition); } public function isAuthorizedByAcl($acl) { debug("role is", $this->user_role); $aclDefault = $acl['ALL']; $aclForCurrentUser = $acl[$this->user_role]; /* if ($aclDefault) if ($this->startsWith($aclForCurrentUser, '&&')) */ } // @todo public function getMandatoryFieldsForAction($action) { $fields = []; // meme fonctionnement recursif que isAuthorizedAction() ci-dessus return $fields; } // @todo public function getHiddenFieldsForAction($action) { $fields = []; // meme fonctionnement recursif que isAuthorizedAction() ci-dessus return $fields; } // @todo public function getReadOnlyFieldsForAction($action) { $fields = []; // meme fonctionnement recursif que isAuthorizedAction() ci-dessus return $fields; } // @todo public function getDefaultValueFieldsForAction($action) { $fields = []; // meme fonctionnement recursif que isAuthorizedAction() ci-dessus return $fields; } public static function getRoleLevel($role) { // return $this->allProfiles[$role]; // debug("role is" .$role); return self::PROFILES[$role]; } public static function forceReload() { self::$RELOAD = TRUE; } public static function getPreviousRole($role) { switch ($role) { /* * case 'RESPONSABLE': $rolePrev='USER'; break; * case 'ADMIN': $rolePrev='RESPONSABLE'; break; * case 'ADMINPLUS': $rolePrev='ADMIN'; break; * case 'SUPERADMIN': $rolePrev='ADMINPLUS'; break; */ case 'RESPONSABLE': $rolePrev = 'Utilisateur'; break; case 'ADMIN': $rolePrev = 'Responsable'; break; /* case 'ADMINPLUS': $rolePrev = 'Administration'; break; */ case 'SUPERADMIN': //$rolePrev = 'Administration Plus'; $rolePrev = 'Administration'; break; } return $rolePrev; } // to be overriden by subclasses //abstract static public function getActions(); static public function getActions() { //throw new NotImplementedException(); $actions = [ // - CRUD /* */ 'add', 'view', 'index', 'edit', 'delete', /* // - Autres */ //'find', ]; // Retourne toutes les méthodes publiques, mêmes celles qui ne sont pas des actions, donc pas bon //$actions = get_class_methods('App\Controller\MaterielsController'); return $actions; } public function getActionPassed() { // BETTER: // return $this->request->getAttribute('params')['action']; return $this->request->getParam('action'); } // Retourne l'id passé en paramètre de l'url, sinon 0 (si null) public function getIdPassed() { // return (int) $this->request->getAttribute('params')['pass'][0]; return (int) $this->request->getParam('pass.0'); } /* * Liste minimum d'entités associées à charger lorsqu'on récupère une entité * * Par défaut => vide * * Peut être surchargé par chaque controleur */ protected function getMinimumListOfRelatedEntitiesToLoad() { return []; } /* (EP 202009) * */ public function getCurrentEntity() { /* $control = $this->getName(); // Materiels, Suivis, Emprunts, Documents //return $this->Materiels->current_entity; return $this->$control->current_entity; */ return $this->{$this->name}->current_entity; } /* (EP 20200428) * * Méthode pour optimiser les accès à la BD. * Retourne l'entité dont l'id est $id (en la mettant en cache pour éviter de la rechercher 2 fois) * Si $id = null => retourne l'entité courante ($this->e) (entité concernée par l'action en cours) * * Cette méthode ne doit être appelée que lorsque c'est approprié * (actions 'edit', 'view', ..., mais pas 'add', 'find', 'index', ...) * sinon ça provoque une exception... * * Optimisation : l'entité n'est récupérée dans la BD qu'une seule fois pour toutes * */ // PHP7 only //protected function getEntity($id=null) : Entity { // PHP>=5 //public function getEntity($id=null, $WITH_RELATED_MATERIEL=false, $WITH_ASSOCIATED_ENTITIES=true) { public function getEntity($id=null, $WITH_RELATED_MATERIEL=false, array $contain=[]) { // Si pas d'id => exception (stop) //assert($this->e_id>0); if (!$this->e_id && !$id) throw new \Exception(__("cette methode doit etre appelée avec un id déjà positionné !!!")); // Par défaut id = id de l'entité courante if (is_null($id)) $id = $this->e_id; //pr($this->e); // Si l'entité courante n'est pas la bonne, la mettre à null pour obliger à la recharger //if ($id && $this->e && $this->e->id!=$id) $this->e = null; //if ($id && $this->e && $this->e_id!=$id) $this->e = null; if ($this->e && $this->e_id!=$id) $this->e = null; //if (self::$RELOAD) $this->e=null; //pr("reload is ".(int)self::$RELOAD); // Forcer le reload du matos si on veut des entités associées if ($contain) $this->e = null; /* TODO: à affiner, c'est bourrin, il faudrait plutot mettre à null SSI une (au moins) des entités associées demandées est absente, * du style (exemple pour l'entité associée SurCategory) : * $DO_RELOAD = false; foreach ($contain as $related_entity) { if ( $m->sur_categorie_id && !$m->has('sur_category') ) { $DO_RELOAD=true; break; } } * */ // TODO: ? //if ($WITH_RELATED_MATERIEL && $this->e && !$this->e->has('Materiel')) $this->e = null; // Forcer la relecture de l'entité ? (seulement pour les tests) if (self::$RELOAD) $this->e=null; //debug("e is :"); debug($this->e); $control = "Controleur ".$this->name." avec id $id => "; if ($this->e) $this->d($control."OPTIMISATION: Utilisation du cache"); if (!$this->e) { $this->d($control."(RE)LOAD !!!"); // Les 3 sont possibles //$model = $this->request->getParam('controller'); // ex: Materiels //$model = $this->name; // ex: Materiels $model = $this->modelClass; // ex: Materiels /* debug("model2"); $model = $this->$model; debug($model); */ //ex: if (! $this->e) $this->e = $this->Materiels->get($this->e_id); //$this->e = $this->$model->get($this->e_id); //$related_entities = $WITH_RELATED_MATERIEL ? ['Materiels']:[]; $related_entities = $contain; if ($WITH_RELATED_MATERIEL) $related_entities[] = 'Materiels'; /* $related_entities = []; if ($WITH_ASSOCIATED_ENTITIES) $related_entities = $this->getMinimumListOfRelatedEntitiesToLoad(); if ($WITH_RELATED_MATERIEL) $related_entities[] = 'Materiels'; */ $this->e = $this->$model->get($id, ['contain'=>$related_entities]); $this->e_id = $this->e->id; } //pr($this->e); /* * Avec les entités associées : if (! $this->e) $this->e = $this->$model->get($this->e_id, [ 'contain' => ['Comments'] ]); */ //debug($this->e); return $this->e; } public function getUsersTable() { return TableRegistry::getTableLocator()->get('Users'); } // Retourne l'entity User qui a le login $userlogin ("epallier") public function getUserByLogin($userlogin) { if ($this->u && $this->u->username == $userlogin) return $this->u; //return $users->find()->where(['username' => $userlogin])->first(); return $this->getUsersTable()->findByUsername($userlogin); } // Retourne l'entity User qui a le nom complet $fullname ("Pallier Etienne") public function getUserByFullname($fullname) { if ($this->u && $this->u->nom == $fullname) return $this->u; //return $users->find()->where(['username' => $userlogin])->first(); return $this->getUsersTable()->findByNom($fullname); } /** * (EP) * Autorisations PAR DÉFAUT * Appelé à la fin de isAuthorized() de chaque controller si cette fonction n'a pas return true */ /* (EP) migré directement dans isAuthorized() public function isAuthorizedCommons($user) { $this->myDebug("step 2B (intermediaire general): AppController.isAuthorizedCommons(user)"); $action = $this->getActionPassed(); //$this->myDebug("action iss: $action", null, true); $this->myDebug("- action is: $action"); // $role = $this->getUserRole($user); // Seul Administration (et +) peut ajouter, supprimer ou modifier (pour la plupart des controleurs) if (in_array($action, ['add', 'edit', 'delete'])) return ($this->USER_IS_ADMIN_AT_LEAST()); /S if ($this->USER_IS_ADMIN_AT_LEAST()) return true; // Les autres n'y ont pas accès return false; S/ // Sinon, on applique les règles générales par défaut // Ne pas faire ça car $this sera interprété comme le controleur SPECIFIQUE et non AppController : //return $this->isAuthorized($user); // Donc, il faut être explicite : return AppController::isAuthorized($user); } */ /** * * @param $user * @return boolean isAuthorized is located in the Auth component * Check whether a LOGGED in user has a set of permissions to perform a given action * Give authorization in general * * Autorisations APRES connexion, donc à partir d'ici le user est obligatoirement identifié * (AVANT connexion, c'est initialize() et beforeFilter() qui gèrent) * * On tente d’autoriser ici les actions qui n’ont pas été autorisées * par la méthode isAuthorized du controleur spécifique * * ref: https://book.cakephp.org/4/fr/controllers/components/authentication.html#autorisation * * $user est l'equivalent de $this->LdapAuth->user() * */ //public function isAuthorized(Array $user) { return $this->isAuthorizedAction($user=$user); } //public function isAuthorized(Array $user) { return AppController::isAuthorizedAction($user=$user); } //protected function isAuthorizedAction($action = null, $id=null, $role=null, $user=null, $userCname=null) { //public function isAuthorized(Array $user, $action=null) { public function isAuthorized($user, $action=null, $id=null, $role=null) { // Car ces msg de debug empechent certaines actions de fonctionner !!! (ex: configurations/debugOff) $DODEBUG=false; //$action=null, $id=null, $role=null, $userCname=null) { $DODEBUG && $this->myDebug("step 2C (general): AppController.isAuthorized()"); // $user est l'equivalent de $this->LdapAuth->user() $DODEBUG && $this->myDebug("- user is:", $user); //$action = $this->getActionPassed(); if (!$action) $action = $this->a; $DODEBUG && $this->myDebug("- action is: $action"); //if (!$id) $id = $this->getIdPassed(); if (!$id) $id = $this->e_id; /* $IS_RELATED_ENTITY_ID = false; return $this->isAuthorizedAction($action, $id, $IS_RELATED_ENTITY_ID); // $user, $userCname */ $authorized = $this->isAuthorizedActionForCurrentUser($action, $id); // $user, $userCname //debug("autorisé ????"); debug($authorized); return $authorized; // LA SUITE EST A VIRER // $role = $this->getUserRole($user); /* * // ATTENTION, normalement, on devrait tester si role est défini..., mais c'est sans doute pas utile * // cf https://book.cakephp.org/3.0/fr/tutorials-and-examples/blog-auth-example/auth.html * if (isset($user['role']) && $user['role'] === 'admin') { * return true; * } */ //$configuration = $this->confLabinvent; //$role = $this->getUserRole($user); /* * $role = TableRegistry::getTableLocator()->get('Users')->find() * ->where(['username' => $user[$configuration->authentificationType_ldap][0]]) * ->first()['role']; */ ///$this->myDebug("- role is " . $this->user_role); /* $prefix = $this->request->getParam('prefix'); $this->myDebug("- prefix is $prefix"); */ // BETTER: // $action = $this->request->getAttribute('params')['action']; // $action = $this->request->getParam('action'); //$action = $this->getActionPassed(); // error_log($action); /* // Seul Administration (et +) peut ajouter, supprimer ou modifier (pour la plupart des controleurs) if (in_array($action, ['add', 'edit', 'delete'])) return ($this->USER_IS_ADMIN_AT_LEAST()); */ // On autorise ou pas l’action demandée : // - Super-Admin peut accéder à toutes les actions //if ($role == 'Super Administrateur') return true; //if ($this->USER_IS_SUPERADMIN) return true; if ($this->USER_IS_SUPERADMIN()) return true; // - Actions générales accessibles à TOUS les roles (profils), pour TOUT controleur if (in_array($action, [ 'add', 'index', 'view', 'find', /* (EP 202004) sale ! => migré dans chaque controleur spécifique concerné // QrCode 'creer', // Suivis 'getNextDate', // Materiels 'getDateGarantie' */ ])) return true; // - Pour toutes les autres actions, par défaut => accès refusé (denied) return false; } // isAuthorized() // (EP) Used by Materiels and Users Controllers protected function setUsersLists() { // On recup tous les users du LDAP (ou fakeLDAP si on n'est pas en mode LDAP) //$users = TableRegistry::get('LdapConnections')->getListUsers(); //sort($users); $users_login_and_email = TableRegistry::getTableLocator()->get('LdapConnections')->getUsersLoginAndEmail(); $users_name = array_keys($users_login_and_email); // Formatage en $users_option_list["Etienne Pallier"] = "Etienne Pallier" ... $users_option_list = []; for ($i = 0; $i < sizeof($users_name); $i ++) $users_option_list[$users_name[$i]] = $users_name[$i]; $this->set(compact( 'users_option_list', 'users_login_and_email' )); return $users_name; } // @deprecated public function getTablePriviledgedUserFromCurrentSessionUserIfExists($user = null) { return $this->getUserEntity($user); } public function getCurrentUserEntity() { return $this->getUserEntity(); } public function getUserEntity($user = null) { //if (! $this->CURRENT_PRIVILEDGED_USER) { //debug("user is"); debug($user); //debug("1)"); debug($_SESSION); if (! $this->u) { $configuration = $this->confLabinvent; //$ldapAuthType = $configuration->ldap_authenticationType; // fakeldap => 'uid' $ldapAuthType = $configuration->ldap_authenticationType ? $configuration->ldap_authenticationType : UsersController::DEFAULT_AUTH_TYPE; // Dans les tests, $username est égal à qqch comme : 0 => 'user4_RESP' // par défaut, user est DEJA une Entity User => rien à faire // Si $user non défini ou bien array, il faut rechercher ce user dans la BD pour créer un User entity if (!$user || is_array($user)) { //debug("2)"); debug($_SESSION['Auth']['User'][$ldapAuthType]); // (EP 202006) Marche pas avec les TESTS : ça flingue carrément la variable $_SESSION !!! //$username = $user ? $user[$ldapAuthType] : $this->LdapAuth->user($ldapAuthType); // Donc je remplace par : //TODO: $ldapAuthType = samaccountname (strtolower) ///$username = $user ? $user[$ldapAuthType] : $_SESSION['Auth']['User'][$ldapAuthType]; if ($user) $username = $user[$ldapAuthType]; else { /* * (EP202102) * Cas particulier où on vient juste de passer en mode LDAP (avec un type "samaccountname") * MAIS on est toujours en fakeldap (et donc avec un type 'uid') * Evite de planter sur $_SESSION['Auth']['User']['samaccountname'] * (on continue plutôt de faire $_SESSION['Auth']['User']['uid'], jusqu'à un logout salvateur...) */ $ldapAuthTypeTemp = isset($_SESSION['Auth']['User'][$ldapAuthType]) ? $ldapAuthType : UsersController::DEFAULT_AUTH_TYPE; $username = $_SESSION['Auth']['User'][$ldapAuthTypeTemp]; } //debug("ldapAuthType is '$ldapAuthType'"); /* CAKEPHP 3 way : $req = $this->getRequest(); //debug($req); $session = $req->getSession(); debug($session->read()); //debug($session->read('Auth.User')); */ //debug("username is"); debug($username); //debug("3)"); debug($_SESSION); //debug($this->LdapAuth); //exit; if (!$username) throw new \ErrorException("L'utilisateur courant n'a pas été trouvé !!!"); //debug("user name is:"); debug($username); $username = $username[0]; $user = TableRegistry::getTableLocator()->get('Users') ->find() ->where(['username' => $username]) ->first(); // ->where(['username' => $this->LdapAuth->user('cn')[0]]) } // if (! $priviledgedUser) $priviledgedUser = "Unpriviledged User (not in table utilisateurs)"; $this->u = $user; } return $this->u; } public function getUserRole($user = null) { //if (! $this->CURRENT_ROLE) { if (! $this->user_role) { $priviledgedUser = $this->getTablePriviledgedUserFromCurrentSessionUserIfExists($user); // default role is "Utilisateur" (for people who are not in the table utilisateurs) //$this->CURRENT_ROLE = ($priviledgedUser) ? $priviledgedUser['role'] : 'Utilisateur'; $this->user_role = ($priviledgedUser) ? $priviledgedUser['role'] : 'Utilisateur'; } //return $this->CURRENT_ROLE; return $this->user_role; } private function userHasRole($expectedRole, $ORMORE = false) { $role = $this->getUserRole(); if (!$ORMORE) return ($role == $expectedRole); return ($this->getRoleLevel($role) >= $this->getRoleLevel($expectedRole)); /* * //$hasRole = false; * switch ($expectedRole) { * case 'Super Administrateur' : * return (in_array($role, ['Super Administrateur'])); * break; * case 'Administration Plus' : * return (in_array($role, ['Administration Plus', 'Super Administrateur'])); * break; * case 'Administration' : * return (in_array($role, ['Administration', 'Administration Plus', 'Super Administrateur' ])); * break; * case 'Responsable' : * return (in_array($role, ['Responsable', 'Administration', 'Administration Plus', 'Super Administrateur'])); * break; * case 'Utilisateur' : * return (in_array($role, ['Utilisateur', 'Responsable', 'Administration', 'Administration Plus', 'Super Administrateur'])); * break; * } * return $false; */ } public function userHasRoleAtLeast($expectedRole) { return $this->userHasRole($expectedRole, true); } public function USER_IS_ADMIN_OR_MORE() { return $this->userHasRoleAtLeast('Administration'); } public function USER_IS_ADMIN_AT_LEAST() { return $this->USER_IS_ADMIN_OR_MORE(); } public function USER_IS_RESP_AT_LEAST() { return $this->userHasRoleAtLeast('Responsable'); } public function USER_IS_SUPERADMIN() { return $this->userHasRole('Super Administrateur'); } public function USER_IS_ADMIN() { return $this->userHasRole('Administration'); } public function USER_IS_RESP() { return $this->userHasRole('Responsable'); } public function USER_IS_USER() { return $this->userHasRole('Utilisateur'); } /** * Initialization hook method. * Use this method to add common initialization code like loading components. * e.g. `$this->loadComponent('Security');` * * ref: https://book.cakephp.org/4/fr/controllers/components/authentication.html#id1 * ref: https://book.cakephp.org/4/fr/controllers/components/authentication.html#configuration-des-gestionnaires-d-authentification * * @return void */ public function initialize() { /* * TRES DANGEREUX !!! * Car ce msg de debug empeche certaines actions de fonctionner !!! (ex: configurations/debugOff) */ $DODEBUG=false; $DODEBUG && $this->myDebug("step 0B (general): AppController.initialize()"); parent::initialize(); $this->loadComponent('RequestHandler'); $this->loadComponent('Flash'); // Composant en charge de l'authentification // - sans LDAP : /* $this->loadComponent('Auth', [ 'authenticate' => [ 'Form' => [ 'fields' => [ 'username' => 'email', 'password' => 'password' ] ] // avec finder redéfini 'Form' => [ 'finder' => 'auth' // va utiliser une méthode findAuth() dans UsersTable ] ], 'loginAction' => [ 'controller' => 'Users', 'action' => 'login' ], // If unauthorized, return them to page they were just on 'unauthorizedRedirect' => $this->referer() ]); // Allow the display action so our PagesController // continues to work. Also enable the read only actions. $this->Auth->allow(['display', 'view', 'index']); // Les autres actions sont redirigées sur /users/login */ // - avec LDAP : /* * If none of your users have hashed passwords, comment this block and $this->Auth->allow() calls. * (par exemple dans beforeFilter()) * Then go and edit the user, saving a new password for them. * After saving a new password for the user, * make sure to uncomment the lines we just temporarily commented! */ $this->loadComponent('LdapAuth', [ // Les AUTHORIZATIONS sont faites par Controleur // (et non pas par un Component ou plugin) // TODO: utiliser Authorization component plugin (cakephp v4) //'authorize'=> 'Controller', 'authorize' => ['Controller'], /* pour Auth (au lieu de LdapAuth) on aurait aussi mis ceci : 'authenticate' => [ 'Form' => [ 'fields' => [ 'userna100000000000000000000000000000000me' => 'email', 'password' => 'password' ] ] ], */ // Redirection après login : 'loginRedirect' => [ 'controller' => 'Pages', //'action' => 'home' 'action' => 'display', 'home' ], // Redirection après logout : 'logoutRedirect' => [ 'controller' => 'Pages', 'action' => 'home' ] ]); // On peut aussi configurer après : // Message d'erreur en cas de refus de connexion $this->LdapAuth->setConfig('authError', "Désolé, vous n'êtes pas autorisé à accéder à cette zone."); /* Autres possibilités de config : * $this->Auth->config('authenticate', [ * 'Form' => ['userModel' => 'Members'] * //'Basic' => ['userModel' => 'Members'], * ]); * $this->Auth->config('authorize', ['Controller']); */ // Actions autorisées SANS authentification // Allow the display action so our PagesController continues to work. // Also enable the read only actions. // Déplacé dans beforeFilter() //$this->LdapAuth->allow(['display', 'view', 'index']); // Autoriser TOUTES les actions SANS authentification : //$this->LdapAuth->allow(); // On charge la configuration /* $this->confLabinvent = TableRegistry::getTableLocator()->get('Configurations')->find() ->where([ 'id =' => 1 ]) ->first(); */ $this->confLabinvent = TableRegistry::getTableLocator()->get('Configurations')->find()->first(); // (EP 23/5/19) Exception si la config est vide, inutile d'aller plus loin ! if (is_null($this->confLabinvent)) throw new \Exception("EXCEPTION: La table 'configurations' de la base de données est vide"); // (IP2I) Initialisation des autorisations pour les actions du controleur /* $configKey = 'GeneralAuthorizations'; if ( Configure::check($configKey) ) { $defaultAuthorizations = Configure::readOrFail($configKey); //debug($defaultAuthorizations); } else debug("error"); $configKey = 'MaterielsAuthorizations'; if ( Configure::check($configKey) ) { $defaultAuthorizations = Configure::readOrFail($configKey); //debug($defaultAuthorizations); } else debug("error"); */ // 1) Initialisation des autorisations par défaut du parent (AppController) $this->setDefaultAuthorizations(); // 2) Ajout des autorisations spécifiques par le controleur courant $SPECIFIC_METHOD_EXISTS = false; if ($this->confLabinvent->labNameShort) { $labshortname = $this->confLabinvent->labNameShort; ####$this->d("lab name is set to $labshortname"); $setAuthorizationsSpecific = "setAuthorizations_$labshortname"; // Si une méthode spécifique (dans le controleur courant) existe pour CE labo, on l'appelle à la place de la méthode générale // Par exemple pour l'IRAP, ça sera $this=>setAuthorizations_IRAP() : // if (method_exists($this, 'setAuthorizations_IRAP')) $this->setAuthorizations_IRAP(); if (method_exists($this, $setAuthorizationsSpecific)) { $SPECIFIC_METHOD_EXISTS = true; $this->$setAuthorizationsSpecific(); } } // Sinon, on appelle la méthode générale if (! $SPECIFIC_METHOD_EXISTS) $this->setAuthorizations(); } // initialize() // Méthode à spécialiser dans chaque controleur // Sinon, par défaut c'est celle-ci qui est utilisée et elle ne fait rien ! protected function setAuthorizations() {} // Actions autorisées par défaut pour TOUS les controleurs protected function setDefaultAuthorizations() { // autorisé pour tous $this->setAuthorizationsForAction('index (liste générale)', 0); $this->setAuthorizationsForAction('view (vue détaillée)', 0); $this->setAuthorizationsForAction('find (rechercher)', 0); /* $this->setAuthorizationsForAction('index', 0); $this->setAuthorizationsForAction('view', 0); $this->setAuthorizationsForAction('find', 0); */ // admin(+) only : $this->setAuthorizationsForAction('add(créer)', 0, [ //$this->setAuthorizationsForAction('add', 0, [ 'user' => -1, 'resp' => -1, ]); $this->setAuthorizationsForAction('edit (modifier)', 0, [ //$this->setAuthorizationsForAction('edit', 0, [ 'user' => -1, 'resp' => -1, ]); $this->setAuthorizationsForAction('delete (supprimer)', 0, [ //$this->setAuthorizationsForAction('delete', 0, [ 'user' => -1, 'resp' => -1, ]); //debug($this->is_authorized_action);exit; } protected function getCurrentURL($WITH_REQUEST_URI=true) { //debug($_SERVER); $link = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://" // ok en prod, mais pas pour les TESTS //. $_SERVER['HTTP_HOST']; // donc, on teste avant : . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : "serveur_de_test.fr"); if ($WITH_REQUEST_URI) $link .= $_SERVER['REQUEST_URI']; return $link; } protected function getCurrentUserName($reverse=false) { //debug($this->LdapAuth->user('id')); exit; if ($this->LdapAuth->user()) { if (!$this->userName || $reverse) { //$this->userName = $this->LdapAuth->user('sn')[0] . ' ' . $this->LdapAuth->user('givenname')[0]; $prenom = $this->LdapAuth->user('givenname')[0]; $nom = $this->LdapAuth->user('sn')[0]; $this->userName = $reverse ? $prenom.' '.$nom : $nom.' '.$prenom; } } // par défaut, null return $this->userName; } /** * * {@inheritdoc} * * @see \Cake\Controller\Controller::beforeFilter() * 1) Autorisations SANS (ou AVANT) connexion * 2) Ensuite, c'est isAuthorized qui gère * * Cette méthode est appelée pendant l’event Controller.initialize qui se produit * AVANT chaque action du controller. * C’est un endroit pratique pour vérifier le statut d’une session ou les permissions d’un utilisateur. * ATTENTION: cette méthode sera appelée pour les actions manquantes. * */ public function beforeFilter(Event $event) { // Affichages pour debug //pr($event); ####$this->myDebug("step 1B (general): AppController.beforeFilter()"); $controllerName = $this->request->getParam('controller'); ####$this->myDebug("- controller passed : $controllerName"); $passedArgs = $this->request->getParam('pass'); ####$this->myDebug("- args passed : ", $passedArgs); $query = $this->request->getQueryParams(); ####$this->myDebug("- query passed : ", $query); // Initialisations pour la suite // - L'entité concernée par l'action // Par défaut null car certaines actions n'ont pas d'entité (ex : 'add', 'find', 'index', ...) $this->e = null; // - L'action demandée et son id le cas échéant (nul par défaut, égal 0) $this->a = $this->getActionPassed(); ####$this->myDebug("- action passed : ".$this->a); //debug("- action passed : ".$this->a); $this->e_id = $this->getIdPassed(); ####$this->myDebug("- id passed : ".$this->e_id); parent::beforeFilter($event); // !!! Ne jamais autoriser l'action 'login', sinon cela va créer des problèmes sur le fonctionnement normal de AuthComponent (cf doc) !!! /* * EXEMPLES d'utilisation: * // to allow all access to all actions: * if (isset($this->Auth)) { * $this->Auth->allow('*'); * } * // Allow access to index & view actions: * if (isset($this->Auth)) { * $this->Auth->allowedActions = array('index', 'view'); * } */ // (EP 21/5/19) NEW $configuration = $this->confLabinvent; $D = $configuration->mode_debug; //$this->myDebug($configuration); // TODO: (EP) A quoi ça sert ??? $this->request->getSession()->write("authType", $configuration->ldap_authenticationType); // Groupes métier et technique // id du Groupe métier N/A /* $this->idGmNa = TableRegistry::getTableLocator()->get('GroupesMetiers') ->find()->where(['nom =' => 'N/A'])->first()['id']; // id du Groupe technique N/A $this->idGtNa = TableRegistry::getTableLocator()->get('GroupesThematiques') ->find()->where(['nom =' => 'N/A'])->first()['id']; */ $this->idGmNa = null; $this->idGtNa = null; // Si le user est connecté (identifié), on positionne quelques variables utiles // - pour le controleur spécifique, // - et pour la vue // Utilisateur connecté (identifié) ? ####$this->myDebug("- Utilisateur connecté ? : ", $this->LdapAuth->user() ? "oui" : "non"); ####$this->myDebug($this->LdapAuth->user()); //if ($configuration->ldap_used && !$this->); if ($this->LdapAuth->user()) { // ATTENTION, $priviledgedUser = NULL si l'utilisateur courant n'est pas un utilisateur privilégié // (c'est à dire s'il n'est pas dans la table "utilisateurs") //$this->priviledgedUser = $this->getTablePriviledgedUserFromCurrentSessionUserIfExists(); $this->u = $this->getTablePriviledgedUserFromCurrentSessionUserIfExists(); //$role = $this->getUserRole(); //$this->user_role = $this->getUserRole(); $this->user_role = $this->getUserRole(); $profile = self::PROFILES["$this->user_role"]; ####$this->myDebug("- priviledgedUser is {$this->u}"); //debug("- priviledgedUser is {$this->u}"); exit; ####$this->myDebug("- user_role is {$this->getUserRole()}", "- profile is: $profile"); // L'utilisateur connecté (return null si pas connecté) // $this->Auth->user('id'); //$this->userName = $this->getCurrentUserName(); //$this->set('username', $this->LdapAuth->user('sn')[0] . ' ' . $this->LdapAuth->user('givenname')[0]); $this->USER_IS_UTILISATEUR = ($profile == self::PROFILE_USER); $this->USER_IS_RESPONSABLE = ($profile == self::PROFILE_RESPONSABLE); $this->USER_IS_ADMIN = ($profile == self::PROFILE_ADMIN); //$this->USER_IS_ADMINPLUS = ($profile == self::PROFILE_ADMINPLUS); $this->USER_IS_SUPERADMIN = ($profile == self::PROFILE_SUPERADMIN); $this->USER_IS_RESPONSABLE_OR_MORE = ($profile >= self::PROFILE_RESPONSABLE); $this->USER_IS_ADMIN_OR_MORE = ($profile >= self::PROFILE_ADMIN); //$this->USER_IS_ADMINPLUS_OR_MORE = ($profile >= self::PROFILE_ADMINPLUS); // Positionne ces variables pour TOUTES les vues $this->set('role', $this->user_role); $this->set('profile', $profile); $this->set('username', $this->getCurrentUserName()); $this->set('priviledgedUser', $this->u); $this->set('USER_IS_UTILISATEUR', $this->USER_IS_UTILISATEUR); $this->set('USER_IS_ADMIN', $this->USER_IS_ADMIN); //$this->set('USER_IS_ADMINPLUS', $this->USER_IS_ADMINPLUS); $this->set('USER_IS_SUPERADMIN', $this->USER_IS_SUPERADMIN); $this->set('USER_IS_RESPONSABLE_OR_MORE', $this->USER_IS_RESPONSABLE_OR_MORE); $this->set('USER_IS_RESPONSABLE', $this->USER_IS_RESPONSABLE); //$this->set(compact('USER_IS_ADMIN_OR_MORE')); $this->set('USER_IS_ADMIN_OR_MORE', $this->USER_IS_ADMIN_OR_MORE); //$this->set('USER_IS_ADMINPLUS_OR_MORE', $this->USER_IS_ADMINPLUS_OR_MORE); // (EP) TODO: vraiment utile ??? A virer, non ? $this->set('PROFILE_USER', self::PROFILE_USER); $this->set('PROFILE_ADMIN', self::PROFILE_ADMIN); $this->set('PROFILE_RESPONSABLE', self::PROFILE_RESPONSABLE); //$this->set('PROFILE_ADMINPLUS', self::PROFILE_ADMINPLUS); $this->set('PROFILE_SUPERADMIN', self::PROFILE_SUPERADMIN); // $this->set('allProfiles', $this->allProfiles); $this->set('allProfiles', self::PROFILES); // update STATS de connexion // Toute action (par utlisateur CONNECTÉ), // autre que LOGIN ou LOGOUT (déjà traitées dans leur fonction spécifique de UsersController : login() et logout()) if (! in_array($this->a, ['login', 'logout']) ) (new UsersController())->statsUpdateForCurrentUserWhen(null, 'durant la session'); } // Si user connecté // Now, set these constants for all VIEWS $this->set(compact('configuration')); $this->set(compact('D')); $this->set('idGmNa', $this->idGmNa); $this->set('idGtNa', $this->idGtNa); // On autorise certaines actions SANS connexion (ces actions sont donc publiques) // - pour le mode spécial “installation” => on autorise pleins d’actions sans login // - pour le mode normal habituel => on autorise seulement l’action “display” pour quelques pages publiques (comme la page “about”) //$configuration = $this->confLabinvent; if ($configuration->mode_install) $this->LdapAuth->allow([ 'display', 'add', 'edit', 'installOff' ]); /* // Seule l'action display() est autorisée SANS authentification // (mais PagesController.display() va restreindre ça seulement à la page about) else $this->LdapAuth->allow(['display']); */ // Autoriser TOUTES les actions SANS authentification //$this->LdapAuth->allow(); ####$this->myDebug("- Actions allowed are: ", $this->LdapAuth->allowedActions); /* (EP 20200427 migré dans initialize()) // Message d'erreur en cas de refus de connexion $this->LdapAuth->setConfig('authError', "Désolé, vous n'êtes pas autorisé à accéder à cette zone."); */ } // beforeFilter() /** * Delete generic method * * @param string|null $id * Entity id. * @return \Cake\Network\Response|null Redirects to index. * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ //public function delete_generic($id = null, $return_to_materiel_view=false) public function delete_generic($id = null, $parent_to_return_to=null, $parent_fk_name=null) { // Construction des infos nécessaires $this->request->allowMethod([ 'post', 'delete' ]); $tableName = $this->name; $tableInstance = TableRegistry::getTableLocator()->get($tableName); $entity = $tableInstance->get($id); //if ($return_to_materiel_view) $matos_id = $entity->materiel_id; //if ($parent_to_return_to) $parent_id = $entity->sur_categorie_id; if ($parent_to_return_to) $parent_id = $entity->$parent_fk_name; // Suppression de l'entité $id if ($tableInstance->delete($entity)) { $this->Flash->success(__("L'entité a bien été supprimée")); } else { $this->Flash->error(__("L'entité n'a pas pu être supprimée")); } // Retour à la vue détaillée du matériel qui contenait cette entité if ($parent_to_return_to) { return $this->redirect([ //'controller' => 'materiels', //'controller' => 'sur-categories', 'controller' => $parent_to_return_to, 'action' => 'view', $parent_id ]); } // Retour à la liste des entités (index) else $this->setAction('index'); /* equivaut à return $this->redirect([ 'action' => 'index' ]); */ } /** * Index method * * @return \Cake\Network\Response|null * TODO: à terminer... */ public function index_generic( $entity_plural, $fields_names=[], $contained_controllers=null, $HAS_ADD_BUTTON=true, $HAS_EDIT_DEL_BUTTONS=true, $sortBy = null, $conditions = [], $sortWhitelist = [], $limit = 20 ) { foreach ($fields_names as $fname=>$infos) { // Si $fname est une FK, on définit son nice_name à partir de son controleur if (substr($fname,-3)=='_id') { $fk_controller_name = $infos['controller_name']; //$fk_controller_instance = new $fk_controller_name; $fk_controller_instance = $this->getControllerInstanceForName($fk_controller_name); if (!isset($fields_names[$fname]['nice_name'])) $fields_names[$fname]['nice_name'] = ucwords($fk_controller_instance->getNiceNameSingularLowerCase()); $fields_names[$fname]['name_field_label'] = $fk_controller_instance->getNameFieldLabel(); // getRealControllerNameForAlias } } $controller_name = $this->getName(); //debug($controller_name); $this->paginate = [ 'contain' => $contained_controllers, //'contain' => ['SurCategories'] 'conditions' => $conditions, //'order' => ['nom' => 'asc'], //'sortWhitelist' => $sortWhitelist, /* 'sortWhitelist' => [ 'nom', 'email', 'role', 'GroupesThematiques.nom', 'GroupesMetiers.nom', 'SurCategories.nom', ], */ //'limit' => 20, 'limit' => $limit, ]; //if ($sortBy) $this->paginate['order'] = [$sortBy => 'asc']; if ($sortBy) $this->paginate['order'] = is_array($sortBy) ? $sortBy : [$sortBy => 'asc']; if ($sortWhitelist) $this->paginate['sortWhitelist'] = array_values($sortWhitelist); //$categories = $this->paginate($this->Categories); $entities = $this->paginate($this->$controller_name); //debug($entities->first()); //debug($entities->toArray()); //debug($entities->toArray()); //foreach ($entities as $e) debug($e->nom); /* $collection = new Collection($entities); $sorted = $collection->sortBy($sortBy); foreach ($sorted as $e) debug($e->nom); */ // 'order' => array('Tapplicant.AppID' => 'desc'), // $this->Paginator->sort('Hobby.name', 'Hobby'); // nb entités par page //debug(count($entities)); // Nb total entités total $this->set('nb_entities', $this->$controller_name->find()->count()); $CAN_ADD = true; //$CAN_ADD = $this->isAuthorizedActionForCurrentUser('add'); $this->set(compact( 'CAN_ADD', 'entities', 'entity_plural', 'fields_names', 'HAS_EDIT_DEL_BUTTONS', 'HAS_ADD_BUTTON', 'sortWhitelist' )); // Pas bien..., mais pratique : // on passe le controleur à la vue index pour qu'elle // puisse appeler la methode isAuthorizedAction() $this->set("controller", $this); /* (EP) Pas nécessaire $this->set('_serialize', [ 'categories' ]); */ } public function index_groups($group_type1, $group_type2, $lab_website_url=null) { // https://book.cakephp.org/3/fr/controllers/components/pagination.html $groups = $this->paginate(); // ce qui revient au même que : //$groupesThematiques = $this->paginate($this->GroupesThematiques); //$group_type1 = 'thematique'; //$group_type2 = 'thématique'; // Si on est sur l'instance de l'IRAP (InventIrap) // on affiche l'url vers la page des Groupes métiers sur le site web de l'IRAP $lab_website_url = ($this->confLabinvent->labNameShort != 'IRAP') ? null : $lab_website_url; $CAN_ADD = $this->isAuthorizedActionForCurrentUser('add'); $this->set(compact('CAN_ADD', 'group_type1', 'group_type2', 'lab_website_url', 'groups')); /* Utile seulement pour JSON $this->set('_serialize', [ 'groupesThematiques' ]); */ } //public function view_generic($id, $associated_entity_types, $parent_entity_controller_name=[]) { //public function view_generic($id, $associated_entity_types, $containavirer=[]) { public function view_generic( $id, $child_entity_types=[], $sub_controller_class_name=null, $sub_controller=null, $sub_controller_singular=null, $improved=[], $excluded=[] ) { //debug($contain_children); // ex: SurCategories $controller_name = $this->getName(); //debug($controller_name); exit; // On charge l'entité $id /* $entity = $this->SurCategories->get($id, [ 'contain' => [] ]); */ //$entity = $this->getTableLocator()->get('Categories'); //$entity = $this->$controller_name->newEntity(); //debug($entity->getSchema()); /* On transforme le $contain_children pour lui ajouter des clés de tri (croissant) sur le nom des entités. * On va obtenir qqch comme ceci : [ 'SousCategories' => [ 'sort' => [ 'SousCategories.nom' => 'ASC' ] ], 'Materiels' => [ 'sort' => [ 'Materiels.designation' => 'ASC' ] ] ] */ $contain_children_sorted = []; $children_controller_instances = []; foreach ($child_entity_types as $child_controller_name) { $c = $this->getControllerInstanceForName($child_controller_name); // (optimisation) on stocke l'instance pour plus tard $children_controller_instances[$child_controller_name] = $c; //$fname_label = 'nom'; $fname_label = $c->getNameFieldLabel(); $contain_children_sorted[$child_controller_name] = [ 'sort' => ["$child_controller_name.$fname_label" => 'ASC'] ]; } //debug($children_controller_instances); //debug($contain_children_sorted); //exit; $entity = $this->$controller_name->get($id, [ //'contain' => ['SurCategories'] //'contain' => $parent_entity_controller_name 'contain' => $contain_children_sorted ]); //debug($entity); exit; $fields_label = $this->getFieldsLabelsForEntity($entity); //debug($fields_label); exit; /* * I - Entités PARENTES => $parent_entities_infos */ // On regarde si l'entité contient des fk (parents car belongsTo) // Si oui, on recharge l'entité AVEC toutes les entités (parentes) associées $fks = []; foreach ($entity->toArray() as $fname=>$fval) { if ( strpos($fname,'_id') === strlen($fname)-3 ) if ($fval) $fks[$fname] = $this->getControllerNameForFK($fname); } //debug($fks); exit; $contain_parents = array_values($fks); if ($contain_parents) { $contain_all = array_merge($contain_parents, $contain_children_sorted); //debug($contain_all); $entity = $this->$controller_name->get($id, [ //'contain' => ['SurCategories'] //'contain' => $parent_entity_controller_name 'contain' => $contain_all ]); } //debug($entity); //exit; // S'il y a des entités associées (parentes ou enfants), // on récupère les infos nécessaires à la vue pour pouvoir les lire $parent_entities_infos = []; /* $associated_entities_infos['sur_categorie_id'] = [ 'controller_name' => 'SurCategories', 'type_name' => 'Domaine', 'fk_contained_name' => 'sur_category' ]; */ //debug($entity); exit; //debug($associated_entities_infos); //exit; //foreach ($contain as $associated_entity_controller_name) { foreach ($fks as $fk_name => $parent_entity_controller_name) { //if ($fk_name=='groupes_thematique_id') continue; $parent_entity_real_controller_name = $this->getRealControllerNameForAlias($parent_entity_controller_name); $c = $this->getControllerInstanceForName($parent_entity_real_controller_name); /* $c = 'App\\Controller\\'.$parent_entity_real_controller_name.'Controller'; $c = new $c(); */ // 'Domaine' $parent_entity_type_name=$c->getNiceNameSingularLowerCase($parent_entity_controller_name); //debug($parent_entity_type_name); // nom de l'instance de l'entité parente // 'sur_category' $parent_entity_contained_name = Inflector::classify($parent_entity_controller_name); $parent_entity_contained_name = Inflector::underscore($parent_entity_contained_name); //debug($parent_entity_contained_name);exit; //$parent_entity_name=$entity->sur_category->nom; $parent_entity_name_field_label = $c->getNameFieldLabel(); $parent_entity_name = $entity->$parent_entity_contained_name->$parent_entity_name_field_label; //debug($parent_entity_name);exit; //$associated_entity_id=$entity->$associated_entity_contained_name->id; $parent_entities_infos[$fk_name] = []; // shorcut $parent_entity_infos = &$parent_entities_infos[$fk_name]; $parent_entity_infos['controller_name'] = $parent_entity_real_controller_name; //$parent_entity_infos['controller_name'] = $parent_entity_controller_name; $parent_entity_infos['type_name'] = $parent_entity_type_name; $parent_entity_infos['name'] = $parent_entity_name; $parent_entity_infos['fk_contained_name'] = $parent_entity_contained_name; //if ($fk_name=='groupes_thematique_id') $parent_entity_infos['improved'] = 'groupe_thematique_with_resp'; if ( isset($improved[$fk_name]) ) $parent_entity_infos['improved'] = $improved[$fk_name]; } //debug($parent_entities_infos); //exit; /* $parent_entities_infos['sur_categorie_id'] = [ 'controller_name' => 'SurCategories', 'type_name' => 'Domaine', 'fk_contained_name' => 'sur_category' ]; 'sur_categorie_id' => [ 'controller_name' => 'SurCategories', 'type_name' => 'Domaine', 'fk_contained_name' => 'sur_category' 'name' => 'Optique' ] */ //debug($parent_entities_infos); exit; /* // S'il y a une entité parente, on récupère ses infos $parent_entity_infos = []; if ($parent_entity_controller_name) { //$parent_entity_name=$entity->sur_category->nom; //$c_parent = 'App\\Controller\\'.'SurCategories'.'Controller'; $c_parent = 'App\\Controller\\'.$parent_entity_controller_name.'Controller'; $c_parent = new $c_parent(); // 'Domaine' $parent_entity_type_name=$c_parent->getNiceNameSingularLowerCase(); //debug($parent_entity_type_name); // nom de l'instance de l'entité parente // 'sur_category' $parent_entity_contained_name = Inflector::classify($parent_entity_controller_name); $parent_entity_contained_name = Inflector::underscore($parent_entity_contained_name); //debug($parent_entity_contained_name);exit; //$parent_entity_name=$entity->sur_category->nom; $parent_entity_name_field_label = $c_parent->getNameFieldLabel(); $parent_entity_name=$entity->$parent_entity_contained_name->$parent_entity_name_field_label; //debug($parent_entity_name);exit; $parent_entity_id=$entity->$parent_entity_contained_name->id; $parent_entity_infos['controller_name'] = $parent_entity_controller_name; $parent_entity_infos['type_name'] = $parent_entity_type_name; $parent_entity_infos['name'] = $parent_entity_name; $parent_entity_infos['id'] = $parent_entity_id; } */ /* * II - Entités ENFANTS => $child_entities_list */ $child_entities_list = []; // ex: 'sur_categorie_id' $fk_name = $this->getFkName(); //debug($fk_name);exit; //debug($fk_name); //$fk_name = 'sur_categorie_id'; /* $categories = TableRegistry::get('Categories')->find()->where([ 'sur_categorie_id =' => $id ]); $materiels = TableRegistry::get('Materiels')->find('all')->where([ 'sur_categorie_id =' => $id ]); */ foreach ($child_entity_types as $child_entity_type) { $child_entities_list[$child_entity_type] = []; // shortcut $et = &$child_entities_list[$child_entity_type]; $et['fk_contained_name'] = $this->getFkContainedNameForControllerName($child_entity_type); //$et['controller_name'] = $entity_type; // ex: 'App\Controller\CategoriesController' // instance du controleur //$c = $this->getControllerInstanceForName($child_entity_type); $c = $children_controller_instances[$child_entity_type]; /* $c = 'App\\Controller\\'.$entity_type.'Controller'; $c = new $c(); */ // ex: 'catégorie' $et['entity_type_name_singular'] = $c->getNiceNameSingularLowerCase(); // ex: 'catégories' $et['entity_title'] = $c->getNiceNamePluralLowerCase(); $et['is_masculin'] = $c->is_masculin; $et['name_field_label'] = $c->getNameFieldLabel(); //$et['entities'] = $this->SurCategories->$entity_type->find()->where([ //$et['entities'] = $this->$controller_name->$entity_type->find()->where([ //$table = $this->$controller_name; //$table = $this->getTableLocator()->get($controller_name); //$table = $this->getTableLocator()->get($controller_name); /* $et['entities'] = $table->$entity_type->find()->where([ "$fk_name =" => $id ]); */ /* $table = TableRegistry::getTableLocator()->get($entity_type); //$table = TableRegistry::getTableLocator()->get('SurCategories'); //debug($table); exit; $et['entities'] = $table->find()->where([ "$fk_name =" => $id ]); */ } // foreach child //debug($child_entities_list); exit; //$controller_name = $this->SurCategories->get($id, [ /* $categories = [ 'controller_name' => 'Categories', 'entity_type_name' => 'catégorie', 'title' => 'Catégories', //'id_name' => 'categs', 'is_masculine' => false, 'name_field_name' => 'nom', 'entities' => $categories, ]; $materiels = [ 'controller_name' => 'Materiels', 'entity_type_name' => 'matériel', 'title' => 'Matériels', //'id_name' => 'categs', 'is_masculine' => true, 'name_field_name' => 'designation', 'entities' => $materiels, ]; */ // - can edit or delete $CAN_EDIT = $this->isAuthorizedActionForCurrentUser('edit', $id); $CAN_DELETE = $this->isAuthorizedActionForCurrentUser('delete', $id); // On passe ces variables à la vue //$entities_list = [$categories, $materiels]; //$entity_article = 'le '; $entity_article = $this->getMyArticle(); //$entity_type_name = 'domaine'; //$entity_type_name_singular = $this->getNiceNameSingularLowerCase(); $entity_type_name_singular = $this->getNiceNameSingularLowerCase(); //$entity_name = $entity->getMyName(); //$entity_name = $entity->nom; $entity_name_field_label = $this->getNameFieldLabel(); $entity_name = $entity->$entity_name_field_label; // Sous-controleur défini ? => définir l'autorisation d'ajouter une sous-entité if ($sub_controller) { $sub_controller_instance = $this->getControllerInstanceForName($sub_controller_class_name); $CAN_ADD_SUB_ENTITY = $sub_controller_instance->isAuthorizedActionForCurrentUser('add'); } $this->set(compact( 'id', 'CAN_EDIT', 'CAN_DELETE', 'fields_label', // Infos sur l'entité courante 'entity_article', 'entity_type_name_singular', 'entity_name', 'entity', 'parent_entities_infos', //'parent_entity_infos', //'parent_entity_controller_name','parent_entity_type_name', //'parent_entity_name','parent_entity_id', // Infos sur les entités associées 'child_entities_list', // Optionnel : sous-controleur pour pouvoir ajouter une sous-entité (ex: ajouter une sous-catégorie à une catégorie) 'sub_controller', 'sub_controller_singular', 'CAN_ADD_SUB_ENTITY', 'excluded' )); //debug($this->viewVars);exit; //$this->set('surCategory', $surCategory); //$this->set('categories', $categories); //$this->set('materiels', $materiels); // (EP) Automatique depuis 3.1 (et inutile pour nous) /* // Spécifie quelles variables de vues JsonView doit sérialiser $this->set('_serialize', [ 'surCategory' ]); */ } // view_generic() /** * Add or Edit generic method (do either add() or edit()) * => Factorisation de add() et edit() * (voir aussi https://book.cakephp.org/3.0/en/orm.html) * * @param $IS_ADD: True = add ; False = edit * @return \Cake\Network\Response|void Redirects on successful add/edit, renders view otherwise. */ //protected function add_or_edit($IS_ADD, $id=null, $values=null, $errors=null, $entity_name=null, array $associated_entities=[], $with_parent=false) { protected function add_or_edit($IS_ADD, $id=null, $errors=null, $entity_name=null, array $associated_entities=[], $with_parent=false) { // On refuse de creer une entité sans préciser l'id de l'entité parente associée (en général materiel id) if ($with_parent && $id===null) return; $controller = $this->request->getParam('controller'); $this->myDebug("step 3: $controller .add_or_edit()"); $IS_EDIT = !$IS_ADD; /* $this->entityBuilt = null; $this->parent = null; $this->parents = null; */ /* (EP 20200326) * Pour être plus générique, * On utilise désormais $entity au lieu d'un terme plus spécifique comme $emprunt * (ou $suivi ou $materiel pour les controleurs de Suivis et Materiels, ...) * On utilisera aussi $this->$controller au lieu de $this->Emprunts (ou $this->Suivis ou $this->Materiels, ...) */ $entity = ( $IS_ADD ? $this->$controller->newEntity() : // cf https://book.cakephp.org/3/fr/orm/retrieving-data-and-resultsets.html#eager-loading-associations // Ceci permettra des accès du type $suivi->type_suivi->nom depuis la vue //$this->Emprunts->get($id, ['contain' => ['TypeSuivis']]); $this->$controller->get($id, ['contain' => $associated_entities ]) ); //debug($suivi); // POST (on arrive ici après un SUBMIT) $allowed_request_types = $IS_ADD ? 'post' : ['patch', 'post', 'put']; if ($this->request->is($allowed_request_types)) { //$entity = $this->Emprunts->patchEntity($suivi, $this->request->getData()); $entity = $this->$controller->patchEntity($entity, $this->request->getData()); // TODO : à virer, trouver comment faire ça autrement, c'est sale !!! if ($controller == 'Suivis') { if ($IS_ADD) { if ($this->request->getData('typemesure') !== null && $this->request->getData('typemesure') == "1") $entity->typemesure = "Indirect"; $entity->panne_resolu = false; } } $verb = $IS_ADD ? 'ajouté' : 'modifié'; // SAVE KO if (! $this->$controller->save($entity)) $this->Flash->error(__("$entity_name n'a pas pu être $verb")); // SAVED OK else { $this->Flash->success(__("$entity_name a bien été $verb")); if ($with_parent) { //$parent_id = $IS_ADD ? $this->request->getParam('pass.0') : $entity->materiel_id; $parent_id = $IS_ADD ? $id : $entity->materiel_id; // (EP) Redirection vers la vue parente (materiel emprunté) (depuis add ou edit) return $this->redirect([ 'controller' => 'Materiels', 'action' => 'view', $parent_id ]); } else { if ($IS_ADD) return $this->redirect([ 'action' => 'index', ]); else return $this->redirect([ 'action' => 'view', $id ]); } /* // ADD if ($IS_ADD) return $this->redirect([ 'controller' => 'Materiels', 'action' => 'view', $this->request->getAttribute('params')['pass'][0] ]); // EDIT else $this->setAction('view', $id); /S equivalent à else return $this->redirect([ 'action' => 'view', $id ]); S/ */ } } // POST // START (on arrive ici la première fois qu'on ouvre la vue) if ($with_parent) { $parents = $this->$controller->Materiels->find('list'); //$materiel_id = $IS_EDIT ? $entity->materiel_id : $this->request->getAttribute('params')['pass'][0]; //$parent_id = $IS_EDIT ? $entity->materiel_id : $this->request->getParam('pass.0'); $parent_id = $IS_EDIT ? $entity->materiel_id : $id; // parent = materiel $parent = $this->$controller->Materiels->get($parent_id); //$matos_id = $IS_ADD ? $parent->id : $entity->materiel_id; } //$materiel = $this->$controller->Materiels->get($materiel_id); /* (EP) inutile $numMateriel = $this->$controller->Materiels->find() ->select('numero_laboratoire') ->where([ 'id =' => $materiel_id ]) ->first()['numero_laboratoire']; */ $this->set(compact('IS_ADD','entity')); $with_parent && $this->set(compact('parent','parents')); /* (EP) inutile $this->set('_serialize', [ 'entity' ]); */ // On délare ces variables pour la méthode add_or_edit() du controleur spécifique // (car on ne peut pas faire un return !!!) $this->entityBuilt = $entity; $this->parent = $with_parent ? $parent : null; $this->parents = $with_parent ? $parents : null; } //add_or_edit() // Méthode utilisée (ou utilisable) par tous les controleurs liés à l'entité Materiel : Suivis, Emprunts, Documents (...) // Optimisée pour ne charger le "matériel lié" qu'une seule fois pour TOUS les controleurs intéressés // @deprecated protected function getMateriel($matos_id=null, array $contain=[]) { return $this->getCurrentEntityRelatedMateriel($matos_id, $contain); } protected function getCurrentEntityRelatedMateriel($matos_id=null, array $contain=[]) { $control = $this->name; // ex: Suivis // Par défaut, on prend le matos id passé via la requete url //$parent_id = $IS_EDIT ? $entity->materiel_id : $this->request->getParam('pass.0'); //if (!$matos_id) $matos_id = $this->request->getParam('pass.0'); if (!$matos_id) $matos_id = $this->getIdPassed(); //debug($control); //$model = $this->modelClass; // ex: Suivis $m = AppController::$related_materiel; /* (EP) Si le materiel en mémoire n'est pas celui demandé, on le met à null pour qu'il soit rechargé * * => En utilisation normale, hors test, pas besoin de faire ça. * => C'est uniquement pour les TESTS car sinon ils ne passent pas... * car les tests utilisent la MEME instance du controleur pour faire plusieurs posts avec des id différents * (alors qu'en utilisation hors test, l'instance est recréée à chaque appel du style materiels/view/11995 puis materiels/view/11963... * et donc AppController::$related_materiel est remis à null à chaque fois) * Exemple : * - $this->post("/emprunts/add/1", $data); * - $this->post("/emprunts/add/3", $data); * - ... * (idem pour suivis) */ ////if ($matos_id && $m && $matos_id!=$m->id) $m = null; if ($m && $matos_id!=$m->id) $m = null; // Forcer la relecture du materiel ? (pour les tests) if (self::$RELOAD) $m=null; // Forcer le reload du matos si on veut des entités associées if ($contain) $m = null; /* TODO: à affiner, c'est bourrin, il faudrait plutot mettre à null SSI une (au moins) des entités associées demandées est absente, * du style (exemple pour l'entité associée SurCategory) : * $DO_RELOAD = false; foreach ($contain as $related_entity) { if ( $m->sur_categorie_id && !$m->has('sur_category') ) { $DO_RELOAD=true; break; } } * */ // Si le matériel n'est pas encore en mémoire, on va le chercher en BD // Moins optimisé : Un exemplaire PAR controleur appelant (car attribut d'instance) : //if (!$this->related_materiel) { // Très optimisé : Un seul exemplaire (singleton car attribut de classe) quelquesoit le controleur qui appelle cette méthode : if (! $m) { //debug("load, matos_id=$matos_id"); // UNE FOIS SEULEMENT !!! (pour tous les controleurs) /* // Par défaut, on prend le matos id passé via la requete url //$parent_id = $IS_EDIT ? $entity->materiel_id : $this->request->getParam('pass.0'); if (!$matos_id) $matos_id = $this->request->getParam('pass.0'); */ //$this->related_materiel = $this->Suivis->Materiels->get($matos_id); //$this->related_materiel = $this->$control->Materiels->get($matos_id); //$m = AppController::$related_materiel = $this->$control->Materiels->get($matos_id); $m = AppController::$related_materiel = $this->$control->Materiels->get($matos_id, [ 'contain' => $contain ]); assert($m->id == AppController::$related_materiel->id); //debug($this->related_materiel); exit; } //return $this->related_materiel; return $m; } // getMateriel() public function afterFilter(Event $event) { parent::afterFilter($event); ####$this->myDebug("step ?? (general): AppController.afterFilter()"); // Tout le temps 'index', why ??N //debug($this->request->getAttribute('params')['action']); if (in_array($this->request->getAttribute('params')['action'], [ 'edit', 'add' ])) $this->request->getSession()->write("retourForm1", true); else if ($this->request->getAttribute('params')['action'] != 'creer') $this->request->getSession()->write("retourForm1", false); //exit; /* * A la fin de l'action, envoi d'une notification * - ssi entité définie * ET * - ssi autorisée * ET * - pour 'add' et 'edit' : seulement si il y eu un POST de data */ //debug($this->e); //if ($this->e) $emails = $this->sendNotificationForEntityAction($this->e); //$action = $this->request->getAttribute('params')['action']; // si le user n'est pas loggé, on ne va pas plus loin if ( ! isset($_SESSION['Auth']['User']) ) return true; /* Moved to beforeFilter() c'est mieux // STATS // - LOGIN //debug($this->a); if ($this->a == 'login') { $this->statsUpdateForCurrentUserWhen(null, 'sur login'); } // - LOGOUT (marche pas, why ?...) elseif ($this->a == 'logout') { //debug("logout"); $this->statsUpdateForCurrentUserWhen(null,'sur logout'); //exit; } // - Toute autre action else (new UsersController())->statsUpdateForCurrentUserWhen(null, 'durant la session'); */ // pb avec QrCode : pas de $this->e, donc on évite ////if ($this instanceof QrCodesController) return true; // Pour toutes les actions autres que add, edit, ou index (sauf APRES un POST) if ( ! in_array( $this->a, ['add','edit','index'] ) || $this->request->is(['post','patch','put']) //( in_array($this->a,['add','edit']) && $this->request->is(['post','patch','put']) ) ) { /* debug($this->e_id); exit; debug($this->e_id); debug($this->a); debug($this->e->getErrors()); */ /* * Si l'action n'est pas autorisée à déclencher une notification => on quitte * * $this pointe sur le controleur en cours qui a appelé sendmail, * par exemple MaterielsController ou DocumentsController... * Donc c'est la methode isNotifierAction() de ce controleur spécifique qui est appelée */ //if ($this->isNotifierAction($this->a) && $this->e_id && !$this->e->getErrors()) //if ($this->isNotifierAction($this->a) && $this->e_id) { //debug($this->a); exit; if ($this->isNotifierAction($this->a)) { //debug("ici2"); exit; // Positionne $this->e ssi il n'existe pas déjà ////$this->getEntity($this->e_id); //if (!$this->e->getErrors()) if ( // Cas de Users avec l'action /users/login par exemple (ou Materiel avec l'action QrCode...) !$this->getCurrentEntity() || // Toutes les autres actions !$this->getCurrentEntity()->getErrors() ) { // On notifie cette action (sauf si CANCELLED) //if ($this->ACTION_CANCELLED) { debug("CANCELLED"); exit; } if ($this->ACTION_CANCELLED) return; $emails = $this->sendNotificationForEntityAction(); //debug($emails); } } } //exit; } // afterFilter() /** * Before render callback. * * @param \Cake\Event\Event $event * The beforeRender event * @return void */ public function beforeRender(Event $event) { // TODO: ? // cf https://book.cakephp.org/4/en/views/themes.html //$this->viewBuilder()->setTheme('Modern'); // this theme would be found in plugins/Modern/templates $this->myDebug("step 4B (general) : AppController.beforeRender() - [step 3 = action() si existe]"); // (EP 23/5/19) moved to beforeFilter() /* $this->set('PROFILE_USER', self::PROFILE_USER); $this->set('PROFILE_ADMIN', self::PROFILE_ADMIN); $this->set('PROFILE_RESPONSABLE', self::PROFILE_RESPONSABLE); $this->set('PROFILE_ADMINPLUS', self::PROFILE_ADMINPLUS); $this->set('PROFILE_SUPERADMIN', self::PROFILE_SUPERADMIN); // $this->set('allProfiles', $this->allProfiles); $this->set('allProfiles', self::PROFILES); */ // Ca sert à quoi ??? //if (! array_key_exists('_serialize', $this->viewVars) && in_array($this->response->type(), [ if (! array_key_exists('_serialize', $this->viewVars) && in_array($this->response->getType(), [ 'application/json', 'application/xml' ])) $this->set('_serialize', true); // (EP 21/5/19) moved to beforeFilter() //$this->set('username', $this->LdapAuth->user('sn')[0] . ' ' . $this->LdapAuth->user('givenname')[0]); // moved to beforeFilter() // TODO: (EP) A quoi ça sert ??? /* $configuration = $this->confLabinvent; $this->set('configuration', $configuration); $this->request->getSession()->write("authType", $configuration->ldap_authenticationType); */ // moved to beforeFilter() //$priviledgedUser = $this->getTablePriviledgedUserFromCurrentSessionUserIfExists(); /* * $user = TableRegistry::getTableLocator()->get('Users')->find() * ->where(['username' => $this->LdapAuth->user($configuration->authentificationType_ldap)[0]]) * ->first(); * $role = $user['role']; * if ($role == null) * $role = 'Utilisateur'; */ // Role = 'Utilisateur', 'Responsable", ... // (EP 21/5/19) moved to beforeFilter() /* $role = $this->getUserRole(); $this->set('role', $role); */ // Profile = PROFILE_USER (=1), PROFILE_RESPONSABLE (=2), ... // $profile = $this->allProfiles["$role"]; // (EP 21/5/19) moved to beforeFilter() /* $profile = self::PROFILES["$role"]; $this->set('profile', $profile); */ // (EP 21/5/19) moved to beforeFilter() /* $USER_IS_UTILISATEUR = ($profile == self::PROFILE_USER); $USER_IS_ADMIN = ($profile == self::PROFILE_ADMIN); $USER_IS_ADMINPLUS = ($profile == self::PROFILE_ADMINPLUS); $USER_IS_SUPERADMIN = ($profile == self::PROFILE_SUPERADMIN); $USER_IS_RESPONSABLE_OR_MORE = ($profile >= self::PROFILE_RESPONSABLE); $USER_IS_RESPONSABLE = ($profile == self::PROFILE_RESPONSABLE); $USER_IS_ADMIN_OR_MORE = ($profile >= self::PROFILE_ADMIN); $USER_IS_ADMINPLUS_OR_MORE = ($profile >= self::PROFILE_ADMINPLUS); $this->set('USER_IS_UTILISATEUR', $USER_IS_UTILISATEUR); $this->set('USER_IS_ADMIN', $USER_IS_ADMIN); $this->set('USER_IS_ADMINPLUS', $USER_IS_ADMINPLUS); $this->set('USER_IS_SUPERADMIN', $USER_IS_SUPERADMIN); $this->set('USER_IS_RESPONSABLE_OR_MORE', $USER_IS_RESPONSABLE_OR_MORE); $this->set('USER_IS_RESPONSABLE', $USER_IS_RESPONSABLE); $this->set(compact('USER_IS_ADMIN_OR_MORE')); $this->set('USER_IS_ADMINPLUS_OR_MORE', $USER_IS_ADMINPLUS_OR_MORE); $this->set('priviledgedUser', $priviledgedUser); */ /* * @todo EP 08/2017 Nouvelle organisation des ACL avec $easyACL */ //$action = $this->getActionPassed(); if (in_array($this->a, array( 'add', 'edit', 'view', 'index' ))) { $hiddenFields = $this->getHiddenFieldsForAction($this->a); $this->set('hiddenFields', $hiddenFields); $this->myDebug(compact("hiddenFields")); if (in_array($this->a, array( 'add', 'edit' ))) { $mandatoryFields = $this->getMandatoryFieldsForAction($this->a); $this->set('mandatoryFields', $mandatoryFields); $readOnlyFields = $this->getReadOnlyFieldsForAction($this->a); $this->set('readOnlyFields', $readOnlyFields); $haveDefaultValueFields = $this->getDefaultValueFieldsForAction($this->a); // only for DEBUG : //$this->myDebug("mandat, ro, default fields=", $mandatoryFields, $readOnlyFields, $haveDefaultValueFields); $this->set(compact('haveDefaultValueFields')); //$mandatoryFields = array("un"=>"mand", "deux"=>"alkjl"); $this->myDebug(compact("mandatoryFields")); $this->myDebug(compact("readOnlyFields")); $this->myDebug(compact("haveDefaultValueFields")); } } // Moved to beforeFilter() /* $this->set('idGmNa', TableRegistry::getTableLocator()->get('GroupesMetiers')->find() ->where([ 'nom =' => 'N/A' ]) ->first()['id']); $this->set('idGtNa', TableRegistry::getTableLocator()->get('GroupesThematiques')->find() ->where([ 'nom =' => 'N/A' ]) ->first()['id']); */ // Pass this function to all views $mydebug = function($debugmode, $arg, $stop=false) { if ($debugmode) { // Absolument nécessaire sur inventirap (à cause de php 5 ?) // car sinon, aucun vardump() ou debug() ne s'affiche, why ???... //Configure::write('debug', true); debug($arg); if ($stop) exit(); } }; $this->set(compact('mydebug')); // Pass this function to all views $displayElement = function ($nom, $valeur, $params = "") { $TD = ($params=="") ? 'TD' : "TD $params"; //$TD = ($params=="") ? '