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, ]; */ // (OLD AVIRER) EP 08/2017 // protected $easyACL = array( const OLD_easyACL = array( /** * Default ACL for ALL (logged) users * * Les actions non mentionnées sont accessibles à tous (par défaut), * exemple 'find', 'index'... * Ces default ACL peuvent être surchargées pour un profil précis * (par exemple pour 'USER' qui est plus restreint) */ // 'ALL' => array ( 'DEFAULT' => array( // 'action' => 'condition for execution' (= 'Y', 'N', or ''), // with like "'field name' == 'value'" // !!! Used for test, DO NOT REMOVE : !!! 'action_CAS4_Y' => 'Y', 'action_CAS4_N' => 'N' // YOUR RULES : // CRUD actions : // 'edit' => 'N', // update // 'delete' => 'N', ), // Ajoute des ACL plus spécifiques (ci-dessus) pour le profil USER qui est plus restreint // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL') 'USER' => array( // !!! Used for test, DO NOT REMOVE : !!! 'action_CAS3_Y' => 'Y', 'action_CAS3_N' => 'N', // YOUR RULES : // CRUD actions 'add' => 'Y', // C 'index' => 'Y', // R all 'view' => 'Y', // R one // 'edit' => 'N', // U 'edit' => 'is_creator', // is_creator = (nom_createur == CURRENT_USER_NAME) 'delete' => 'N', // D // OTHER actions 'find' => 'Y' // create /* * ceci n'a aucun sens car l'action sur le modèle "Pages" s'appelle toujours "display" (et non pas tools, infos, ou printers...) * 'tools' => 'N', * 'infos' => 'N', * 'printers' => 'N', */ ), // Surcharge des ACL par défaut (ci-dessus) pour le profil RESPONSABLE qui est plus restreint // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL') 'RESPONSABLE' => array(), // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMIN 'ADMIN' => array( // 'add' => 'Y', // create 'edit' => 'Y' // update // 'delete' => 'Y', // update ), // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMINPLUS 'ADMINPLUS' => array( // 'edit' => 'Y', // update ), // Surcharge des ACL par défaut (ci-dessus) pour le profil SUPERADMIN 'SUPERADMIN' => array( // 'add' => 'Y', // create // 'edit' => 'Y', // update 'delete' => 'Y' ) ); // $easyACL // 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é'], ]; 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; } // 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'; } 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 "; } 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 getTypeNameSingular($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->getTypeNamePlural($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->getTypeNamePlural(),0,-1); } //@deprecated public function getNiceName() { return $this->getTypeNameSingular(); } // Nom pluriel affichable pour cette entité // ex: 'suivis', 'matériels', 'catégories', ... public function getTypeNamePlural($alias_controller_name=null) { //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']; $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); //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, '&&')) */ } /** * * @param AppController $controller * // a subclass of AppController (MaterielsController or any other) * @param array $user * @param string $role * @param string $action * @param string $id */ // public function isAuthorizedAction(AppController $controller, $roleLong, $action, $id=null, $user=null) { public function OLD_isAuthorizedAction2($controller, $roleLong, $action, $id = null, $user = null) { /* Cette fonction n'est pas encore appelée pour de vrai, juste pour test, donc inutile d'afficher ça : $this->myDebug("step 2B (intermediaire general): AppController.OLD_isAuthorizedAction2(controller, $roleLong, $action, $id, user)"); $this->myDebug("- controller name is {$controller->name}"); */ //$this->myDebug("- user is ", $user); $doDEBUG = true; $doDEBUG = false; switch ($roleLong) { case 'Utilisateur': $role = 'USER'; break; case 'Responsable': $role = 'RESPONSABLE'; break; case 'Administration': $role = 'ADMIN'; break; case 'Administration Plus': $role = 'ADMINPLUS'; break; case 'Super Administrateur': $role = 'SUPERADMIN'; break; } if ($doDEBUG) debug("role is $role"); // 1) SPECIFIC controller SPECIFIC (role) rule for action // if (isset($controller->easyACL[$role][$action])) { if (self::OLD_hasACLRule($controller::OLD_easyACL, $role, $action)) { if ($doDEBUG) debug("CAS1"); return $this->OLD_evalSpecificRule($controller::OLD_easyACL[$role][$action], $controller, $user, $role, $action); } // 2) SPECIFIC controller DEFAULT rule for action // if (null !== $controller::easyACL['DEFAULT'][$action]) { if (self::OLD_hasACLRule($controller::OLD_easyACL, 'DEFAULT', $action)) { if ($doDEBUG) debug("CAS2"); return $this->OLD_evalACL($controller::OLD_easyACL['DEFAULT'][$action]); } // 3) ALL controllers (AppController) SPECIFIC (role) rule for action // if (isset($this->easyACL[$role][$action])) { if (self::OLD_hasACLRule(self::OLD_easyACL, $role, $action)) { if ($doDEBUG) debug("CAS3"); return $this->OLD_evalSpecificRule(self::OLD_easyACL[$role][$action], $this, $user, $role, $action); } // 4) ALL controllers (AppController) DEFAULT rule for action // if (isset($this->easyACL['DEFAULT'][$action])) { // if (self::hasACLRule('DEFAULT',$action)) { if (self::OLD_hasACLRule(self::OLD_easyACL, 'DEFAULT', $action)) { if ($doDEBUG) debug("CAS4"); return $this->OLD_evalACL(self::OLD_easyACL['DEFAULT'][$action]); // return $this->evalACL(parent::getACLRule('DEFAULT',$action)); } /* * (RECURSIVE CALL) * 5) SPECIFIC controller PREVIOUS SPECIFIC (role) rule for action * ex: if role is 'SUPER', use 'ADMIN' rule * ex: if role is 'ADMIN', use 'RESP' rule * ex: if role is 'RESP', use 'USER' rule * ex: if role is 'USER', stop recursive call * Stop recursive call if role is 'USER' => no rule found, so DEFAULT IS AUTHORIZE (Y) !!! => permissive ACL */ // if ($role == 'USER') return true; if ($role == 'USER') { if ($doDEBUG) debug("CAS5"); return 'Y'; } if ($doDEBUG) debug("CAS6"); return $this->OLD_isAuthorizedAction2($controller, $this->getPreviousRole($role), $action, $id, $user); } // @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'; 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) { //$action=null, $id=null, $role=null, $userCname=null) { $this->myDebug("step 2C (general): AppController.isAuthorized()"); // $user est l'equivalent de $this->LdapAuth->user() $this->myDebug("- user is:", $user); //$action = $this->getActionPassed(); if (!$action) $action = $this->a; $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 */ return $this->isAuthorizedActionForCurrentUser($action, $id); // $user, $userCname // 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; // 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 : $username = $user ? $user[$ldapAuthType] : $_SESSION['Auth']['User'][$ldapAuthType]; //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_AT_LEAST() { return $this->userHasRoleAtLeast('Administration'); } 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() { $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"); // Initialisation des autorisations pour les actions du controleur // 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->a); 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}"); $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() public function index_generic($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; $this->set(compact('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=[]) { //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->getTypeNameSingular($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; } //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->getTypeNameSingular(); //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->getTypeNameSingular(); // ex: 'catégories' $et['entity_title'] = $c->getTypeNamePlural(); $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, ]; */ // 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->getTypeNameSingular(); //$entity_name = $entity->getMyName(); //$entity_name = $entity->nom; $entity_name_field_label = $this->getNameFieldLabel(); $entity_name = $entity->$entity_name_field_label; $this->set(compact( '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' )); //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 l'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); 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() ) { $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=="") ? '' : ''; //$tdstyle = $params!="" ? $params : ''; // Ca c'est parce que sinon y'a au moins deux tests qui passent pas, a cause de l'espace dans la balise ... //if ($valeur != "") echo ' '.$nom.' ' . $TD.$valeur.''; //if ($valeur != "") echo '' . $nom . ' ' . $balise . $valeur . ''; if ($valeur!="") echo " $nom <$TD>$valeur "; }; $printTableRow = $displayElement; $this->set(compact('printTableRow')); //@deprecated $this->set(compact('displayElement')); // Pass this function to all views // @todo : Si cette fonction ne concerne que SuivisController, il faut la déplacer dans ce controleur $dateProchainControleVerif = function ($t) { $time = Time::now(); // On récupère la date et l'heure actuelles $today = new \DateTime((new date("$time->year-$time->month-$time->day"))->format('Y-m-d')); $time1 = new time($t); $dateTime1 = new \DateTime((new date("$time1->year-$time1->month-$time1->day"))->format('y-m-d')); $interval = ($today->diff($dateTime1)); $strInterval = $interval->format('%a'); return (int) $strInterval; }; $this->set('dateProchainControleVerif', $dateProchainControleVerif); // Pass this function to all views //function echoActionButton($html, $icon_class, $title, $action, $id, $tip='', $controller='materiels', $mat=NULL, $photo=NULL) { //$echoActionButton = function($html, $icon_class, $buttonStyle='', $title, $controller='', $action, $id, $other_args=[], $tip='', $confirmMessage='') { $getActionButton = function($html, $icon_class, $buttonStyle, $title, $controller, $action, $id, $other_args=[], $tip='', $confirmMessage='', $moreButtonStyle='') { if ($controller=='') $controller='materiels'; $controllerArgs = []; $controllerArgs['controller'] = $controller; $controllerArgs['action'] = $action; $controllerArgs[] = $id; foreach ($other_args as $other_arg) $controllerArgs[] = $other_arg; return $html->link( __("$title"), $controllerArgs, /* [ 'controller' => $controller, 'action' => $action, $id, $other_args ], */ [ 'title' => $tip, 'escape' => false, 'onclick' => 'return true;', //'style' => 'margin-right: 10px'.$moreButtonStyle, 'style' => $buttonStyle, 'confirm' => $confirmMessage ] ); }; $echoActionButton = function($html, $icon_class, $buttonStyle, $title, $controller, $action, $id, $other_args=[], $tip='', $confirmMessage='', $moreButtonStyle='') { if ($controller=='') $controller='materiels'; $controllerArgs = []; $controllerArgs['controller'] = $controller; $controllerArgs['action'] = $action; $controllerArgs[] = $id; foreach ($other_args as $other_arg) $controllerArgs[] = $other_arg; echo $html->link( __("$title"), $controllerArgs, /* [ 'controller' => $controller, 'action' => $action, $id, $other_args ], */ [ 'title' => $tip, 'escape' => false, 'onclick' => 'return true;', //'style' => 'margin-right: 10px'.$moreButtonStyle, 'style' => $buttonStyle, 'confirm' => $confirmMessage ] ); }; $this->set(compact('getActionButton', 'echoActionButton')); // Pass this function to all views (en fait, seulement add.ctp et edit.ctp) //function echoSubmitButtons($context, $matos_id) { // $action : 'view' or 'index' //$echoSubmitButtons = function($context, $action, $matos_id=null, $controller=null) { $echoSubmitButtons = function($context, $action, $matos_id=null, $controller=null) { // - Bouton Enregistrer echo '
'; //echo '
'; echo "\n"; echo $context->Form->input('Enregistrer', ['type'=>'submit', 'value'=>'Submit', 'class'=>'btn btn-primary']); //echo $context->Form->button(('Annuler'), ['action' => 'view', $matos_id], ['escape' => false,'onclick' => 'return true;','style' => 'margin-right: 10px']); //echo $context->Html->link(__('Annuler'), ['action' => 'view', $matos_id], ['class' => 'btn btn-info ', 'escape' => false]); echo "\n"; //echo '
'; $action_params = ['action'=>$action, $matos_id]; if ($controller) $action_params['controller'] = $controller; //echo $context->Html->link(__('Annuler'), [$controller, 'action'=>$action, $matos_id], ['class'=>'btn btn-outline-dark btn-sm', 'style'=>"text-decoration:none;", 'escape'=>false]); echo $context->Html->link(__('Annuler'), $action_params, ['class'=>'btn btn-outline-dark btn-sm', 'style'=>"text-decoration:none;", 'escape'=>false]); //echo '
'; echo "\n"; echo '
'; /* //echo $this->Form->submit(__('Enregistrer')); echo $context->Form->button('Enregistrer', ['class'=>'btn btn-outline-success', 'type'=>'submit']); // - Bouton Cancel //echo $this->Html->link(__(' Annuler'), ['action' => 'view', $materiel->id], ['escape' => false,'onclick' => 'return true;','style' => 'margin-right: 10px']); echo $context->Form->button(('Annuler'), ['action' => 'view', $matos_id], ['escape' => false,'onclick' => 'return true;','style' => 'margin-right: 10px']); //echo ''; //echo 'Cancel'; */ //} }; $this->set(compact('echoSubmitButtons')); } // beforeRender() // "le materiel", "le suivi", "l'emprunt", "la catégorie"... // Par défaut public function getArticle() { return "Le "; } static function isLabinventDebugMode() { return TableRegistry::getTableLocator()->get('Configurations')->find()->first()->mode_debug; /* return TableRegistry::getTableLocator()->get('Configurations')->find() ->where([ 'id =' => 1 ]) ->first()->mode_debug; */ } /* Fonction de log * * cf https://book.cakephp.org/3/fr/core-libraries/logging.html * */ function ilog($msg=null, $DEBUG=false) { // true => n'envoie pas de log //$DEBUG = true; //$controller = $this->request->getParam('controller'); //$action = $this->getActionPassed(); $role = $this->getUserRole(); // ex: $this->log("/materiels/edit/12009" fait par ()", 'debug'); //debug($this->request); //debug($this->request->getParam('pass.0')); //debug($this->request->getParam('action')); //debug($this->request->url); $url = $this->request->getPath(); $user = $this->getCurrentUserName(); //debug("$url fait par $user ($role) ($msg)"); //$this->log("$url fait par $user ($role). $msg", 'debug'); //Log::write('info', "$url fait par $user ($role). $msg"); $msg = "$url fait par $user ($role) - $msg\n"; $DEBUG && debug($msg); (!$DEBUG) && $this->log($msg, 'info'); /* Autres formes possibles Log::write('debug',$msg); (https://book.cakephp.org/3/fr/core-libraries/logging.html#scopes-de-journalisation) Log::debug('this gets written only to shops.log', ['scope' => ['orders']]); Log::warning('this gets written only to shops.log', ['scope' => ['orders']]); Log::warning('This is a warning', ['orders']); Log::warning('This is a warning', 'payments'); */ } function myDebug($arg1, $arg2=null, $stop=false) { if ($this->isLabinventDebugMode()) { // Absolument nécessaire sur inventirap (à cause de php 5 ?) // car sinon, aucun vardump() ou debug() ne s'affiche, why ???... Configure::write('debug', true); debug($arg1); if ($arg2) debug($arg2); if ($stop) exit(); } } /** * Envoi un mail avec un sujet, contenant un message à destination d'une liste de mails, selon l'action effectuée. * * @param $entity : * L'entité concernée (principalement un Matériel, mais ça peut aussi etre un Document) * @param $subject : * Sujet du message à envoyer. Si $subject n'est pas renseigné, un sujet par défaut sera généré. * @param $msg : * Message à envoyer. Si $msg n'est pas renseigné, un message par défaut sera généré. */ /* (EP202009) remplacé par sendmail() plus bas public function sendEmail($entity, $subject=null, $msg=null) { /S * $_SESSION['Auth']['User'] pour retrouver TOUTES les infos de la session courante (tout est du string) : * nom $_SESSION['Auth']['User']['sn'][0] * prenom $_SESSION['Auth']['User']['givenname'][0] * mail $_SESSION['Auth']['User']['mail'][0] * login $_SESSION['Auth']['User']['xxx'][0] /!\ Ce champ est suceptible de changer de nom, dans les tests ce champ est ['cn'][0] * mdp $_SESSION['Auth']['User']['userpassword'][0] S/ $configuration = $this->confLabinvent; $action = $this->request->getAttribute('params')['action']; // add or edit or delete or ... // Si les deux cases "Activer l'envoi des mails.." sont décochées, on se fatigue pas à exécuter la fonction if (! $configuration->envoi_mail && ! $configuration->envoi_mail_guests) return null; $mailList = array(); // On détermine le message et le sujet du mail en fonction de l'action effectuee $acteur = $_SESSION['Auth']['User']['givenname'][0] . ' ' . $_SESSION['Auth']['User']['sn'][0]; // if ($entity != null) { if ($entity instanceof Materiel) { $materiel = $entity; $nom_materiel = $materiel->designation; if ($subject==null && $msg==null) { $msgMore = ''; Switch ($action) { case 'add': $subject = "Ajout d'un matériel"; $msg = "$acteur a ajouté le matériel \"$nom_materiel\""; break; // case 'edit': //$subject = "Modification d'un matériel"; //$msg = "$acteur a modifié le matériel \"$nom_materiel\""; // break; case 'delete': $subject = "Suppression d'un matériel"; $msg = "$acteur a supprimé le matériel \"$nom_materiel\""; // @todo: mettre le nom des domaine, categ, et sous-categ, et non pas l'id if ($materiel->sur_categorie_id != "") $msgMore .= "\n\nDomaine : " . $materiel->sur_categorie_id; if ($materiel->categorie_id != "") $msgMore .= "\n\nCatégorie : " . $materiel->categorie_id; if ($materiel->sous_categorie_id != "") $msgMore .= "\n\nSous-catégorie : " . $materiel->sous_categorie_id; if ($materiel->description != "") $msgMore .= "\n\nDescription :\n" . $materiel->description; break; //case 'statusCreated': //$subject = "Dé-validation d'un matériel"; //$msg = "$acteur a dé-validé le matériel \"$nom_materiel\""; // break; //case 'statusValidated': //$subject = "Validation d'un matériel"; //$msg = "$acteur a validé le matériel \"$nom_materiel\""; // break; case 'statusTobearchived': $subject = "Demande d'archivage d'un matériel"; $msg = "$acteur a demandé l'archivage du matériel \"$nom_materiel\""; break; case 'statusArchived': $subject = "Archivage d'un matériel"; $msg = "$acteur a archivé le matériel \"$nom_materiel\""; break; case 'setLabelIsPlaced': $subject = "Etiquette posée sur un matériel"; $msg = "Etiquette posée sur le matériel \"$nom_materiel\""; break; case 'printLabelRuban': $subject = "Etiquette imprimée"; $msg = "L'étiquette concerant votre matériel \"$nom_materiel\" a été imprimée"; $mailList[0] = $materiel->email_responsable; default: $subject = "Action \"$action\" sur un matériel"; $msg = "$acteur a effectué l'action \"$action\" sur le matériel \"$nom_materiel\""; break; } // end switch // (EP) Ajout de l'ID du materiel !!! $msg .= " (id=" . $materiel->id . ")."; // Only for "delete" action (for the moment...) if ($msgMore) $msg .= $msgMore; // $msg .= "\n\n"; } // subject is null // Et maintenant on construit la liste de mails... // Si l'envoi général est activé (et que l'action ne correspond pas à 'printLabelRuban'): if ($configuration->envoi_mail && $action != 'printLabelRuban') { // owner's mail (utilisateur du matériel) $mailList[0] = $materiel->email_responsable; // resp's mail $mailsRespMetier = null; $mailRespThematique = null; //if ($materiel->groupes_metier_id != null && $materiel->groupes_metier_id != 1) { if ($materiel->groupes_metier_id) { // Le ..!= 1 c'est parce que le groupe métier/thématique d'id 1 correspond au groupe N/A, soit aucun groupe $mailsRespMetier = TableRegistry::getTableLocator()->get('Users')->find() ->select('email') ->where([ 'role =' => 'Responsable', 'groupes_metier_id =' => $materiel->groupes_metier_id ]) ->toArray(); $mailRespThematique = TableRegistry::getTableLocator()->get('Users')->find() ->select('email') ->where([ 'role =' => 'Responsable', 'groupes_thematique_id =' => $materiel->groupes_thematique_id ]) ->toArray(); } if ($mailsRespMetier != null || $mailRespThematique != null) { $mailsResp = array_unique(array_merge($mailsRespMetier, $mailRespThematique)); for ($i = 0; $i < sizeof($mailsResp); $i ++) { $mailList[] = $mailsResp[$i]['email']; // $mailList[sizeof($mailList)] = $mailsResp[$i]['email']; } } // mail admin de reference (ici appele gestionnaire) -> Partie administration // Cela a été mis en commentaire car de toute façon l'utilisateur va voir un administratif pour faire valider sa fiche, // Pas la peine de spam l'administration de mails non plus hein ! /S * if ($action != 'statusValidated' && $action != 'statusArchived') { * $mailsAdmin = TableRegistry::getTableLocator()->get('Users')->find()->select('email') * ->where(['role =' => 'Administration']) * ->toArray(); * for ($i = 0; $i < sizeof($mailsAdmin); $i ++) { * $mailList[sizeof($mailList)] = $mailsAdmin[$i]['email']; * } * } S/ } } // Materiel // @todo: ajouter quelques infos dans ces cas : else if ($entity instanceof Document) { ; } else if ($entity instanceof Suivi) { ; } else if ($entity instanceof Emprunt) { ; } /S * @todo: * else if ($entity instanceof Configuration) { * ; * } * ... etc ... (il faut qu'on soit plus précis) S/ // Si l'envoi à la liste spécifiée est activé (et que l'action ne correspond pas à 'printLabelRuban'): $specificUsers = []; if ($configuration->envoi_mail_guests && $action != 'printLabelRuban') { // mail aux adresses specifiees dans la config for ($i = 0; $i < 11; $i ++) { $specificUser = $configuration['emailGuest' . $i]; if ($specificUser) $specificUsers[] = $specificUser; // $mailList[sizeof($mailList)] = $configuration['emailGuest' . $i]; $mailList[] = $specificUser; // Le if vérifie que la ligne soit pas null } } // On dedoublonne la liste des mails, c'pas tres cool de se faire spam 2-3 fois pour la meme action sur le meme materiel, non mais ! $mailList = array_unique($mailList); // ... Pour envoyer les mails aux personnes concernees foreach ($mailList as $mail) { // On envoi des mails à toute la liste, sauf pour "l'acteur", il sait ce qu'il a fait, pas besoin de le spam non plus hein if ($mail == $_SESSION['Auth']['User']['mail'][0]) continue; $message = $msg; // Sisi, cette variable $message est utile, m'enfin vous pouvez toujours essayer de la supprimer ..... Et pensez à regarder le contenu de vos mails !!! Sinon ca fait une tumeur // Génération du message "Vous recevez ce message en tant que $role" // Si $role inexistant (lorsque c'est un mail de la liste entrée en configuration), le message est plutot "Vous recevez ce message car vous avez demandé à le recevoir. [...]" if ($specificUsers) $role = "car vous etes dans la liste spécifique des emails de LabInvent. \n\nPour faire retirer votre mail de cette liste, veuillez contacter un SuperAdmin."; else { $role = TableRegistry::getTableLocator()->get('Users')->find() ->select('role') ->where([ 'email =' => $mail ]) ->first()['role']; // Default role is Utilisateur (for people in LDAP but without priviledge, not in the users table) if (is_null($role)) $role = 'Utilisateur'; $role = 'en tant que ' . $role; } if ($entity != null && ! in_array($action, [ 'delete', 'statusValidated', 'statusCreated' ])) $message .= "\n\nVeuillez vérifier et compléter si besoin la fiche correspondante."; $message .= "\n\nVous recevez ce message " . $role; $this->sendEmailTo("$subject", $message, $mail, $configuration); } return $mailList; } */ // Fonction d'envoi de mails private function sendEmailTo($subject, $msg, $mail, $config) { /* debug($subject); debug($msg); debug($mail); exit; */ if ($mail != null && !$config->test) { if (filter_var($mail, FILTER_VALIDATE_EMAIL)) { //debug("icii"); $email = new Email(); $etiquetteFrom = explode("@", $config->sender_mail); $email->transport('default') ->from([ $config->sender_mail => $etiquetteFrom[0] ]) ->to($mail) ->subject("[LabInvent] " . $subject) ->send($msg); } } } // Fonction d'envoi de mails avec photo jointe private function sendEmailImgTo($subject, $msg, $mail, $config, $nomImg) { if ($mail != null && ! $config->test) { if (filter_var($mail, FILTER_VALIDATE_EMAIL)) { $email = new Email(); $etiquetteFrom = explode("@", $config->sender_mail); // (EP) Je vais tuer le stagiaire qui a fait ça : //$email->attachments(["/var/www/html/labinvent/webroot/img/photos/$nomImg"]); // Il fallait plutot faire ça : $wwwroot_dir = new \Cake\Filesystem\Folder(WWW_ROOT); $absFileName = $wwwroot_dir->pwd() . DS . 'img' . DS . 'photos' . DS . $nomImg; $email->attachments([$absFileName]); $email->transport('default') ->from([ $config->sender_mail => $etiquetteFrom[0] ]) ->to($mail) ->subject("[LabInvent] " . $subject) ->send($msg); } } } // MI Fonction d'envoi de mails avec pièce jointe private function sendEmailPJTo($subject, $msg, $mail, $config, $nomDoc) { if ($mail!=null && !$config->test) { if (filter_var($mail, FILTER_VALIDATE_EMAIL)) { $email = new Email(); $etiquetteFrom = explode("@", $config->sender_mail); // (EP) Je vais tuer le stagiaire qui a fait ça : //$email->attachments(["/var/www/html/labinvent/webroot/files/$nomDoc"]); // Il fallait plutot faire ça : $wwwroot_dir = new \Cake\Filesystem\Folder(WWW_ROOT); //$absFileName = $wwwroot_dir->pwd() . DS . 'files' . DS . $nomDoc; $extension = explode(".", $nomDoc)[1]; // Photo ou doc ? $path_to_doc = in_array($extension, ['png','jpg','jpeg']) ? 'img'.DS.'photos' : 'files'; $absFileName = $wwwroot_dir->pwd().$path_to_doc.DS.$nomDoc; $email->attachments([$absFileName]); /* debug($mail); debug($etiquetteFrom); debug($absFileName); debug($email); exit; */ $email->transport('default') ->from([ $config->sender_mail => $etiquetteFrom[0] ]) ->to($mail) ->subject("[LabInvent] " . $subject) ->send($msg); } } } /**Version MI - version modifiée de la fonction sendEmail par malik du 18 juin au 24 août * * Envoi un mail avec un sujet, contenant un message à destination d'un email, selon l'action effectuée. * * @param $entity : * L'entité concernée (principalement un Matériel, mais ça peut aussi etre un Document) * @param $subject : * Sujet du message à envoyer. Si $subject n'est pas renseigné, un sujet par défaut sera généré. * @param $msg : * Message à envoyer. Si $msg n'est pas renseigné, un message par défaut sera généré. */ // PHP7: //public function sendmail(\App\Model\Entity $entity, $mode=3, $subject = null, $msg = null) //public function sendmail(\App\Model\Entity\Materiel $entity, $mode=3, $subject = null, $msg = null) // PHP5 : // @obsolete public function sendmail($entity, $mode=3, $subject = null, $msg = null) { //$this->sendNotificationForEntityAction($mode,$subject,$msg); $this->sendNotificationForEntityAction($mode, $entity); } public function sendNotificationForEntityAction($mode=3, $given_entity=null, $subject = null, $msg = null) { //return null; // true => n'envoie pas de notif $DEBUG = false; //$DEBUG = true; $control = $this->getName(); //debug($control); exit; // Entité (Entity) //$entity_nice_name = $this->getNiceName(); // matériel, domaine, utilisateur... $entity_nice_type_name = $this->getTypeNameSingular(); // matériel, domaine, utilisateur... // Si pas d'entité définie => return ////$entity = $this->e; $entity = $this->getCurrentEntity(); ////if ( !$entity ) return null; // Par exemple, pour les actions documents/mail-devis ou users/login ... if (!$entity && $this->e_id) $entity = $this->getEntity($this->e_id); //debug($entity); exit; // Cas particulier pour un Document, on veut aussi son type (bon commande, bon livraison, ...) if ($entity instanceof Document) { $entity = $this->getEntity($entity->id, false, ['TypeDocuments']); $type_doc = $entity->type_document->nom; $format = $entity->type_doc; $doc_infos = " ($type_doc - $format)"; } $entities_name = strtolower($control); // materiels, documents, suivis, emprunts... $entity_name_field = $this->getNameFieldLabel(); if ($entity) $entity_name = $entity->has($entity_name_field) ? $entity->$entity_name_field : $entity; else $entity_name = null; // Action //$action = $this->request->getAttribute('params')['action']; // add or edit or delete or ... $action = $this->a; //debug($action); exit; /* * 1) Définition des PARAMÈTRES GÉNÉRAUX : * * host, action, acteur, ... */ $host = $this->getCurrentURL(false); /* * $_SESSION['Auth']['User'] pour retrouver TOUTES les infos de la session courante (tout est du string) : * nom $_SESSION['Auth']['User']['sn'][0] * prenom $_SESSION['Auth']['User']['givenname'][0] * mail $_SESSION['Auth']['User']['mail'][0] * login $_SESSION['Auth']['User']['xxx'][0] /!\ Ce champ est suceptible de changer de nom, dans les tests ce champ est ['cn'][0] * mdp $_SESSION['Auth']['User']['userpassword'][0] */ // ex: 'Etienne Pallier' $acteur = $_SESSION['Auth']['User']['givenname'][0] . ' ' . $_SESSION['Auth']['User']['sn'][0]; $user = $this->getCurrentUserName(true); //debug($user);exit; $action_attrs = $this->getActionNounAndPastVerb($action); //debug($action_noun_and_verb); /* $action_noun = $action_noun_and_verb[0]; $action_verb = $action_noun_and_verb[1]; */ //$entity_type = $this->getEntityTypeName(); //$entity_type = strtolower( substr($control,0,-1) ); $article_le = $action_attrs['verb_article'] ? $action_attrs['verb_article'] : $this->getMyArticle(1); $article_dun = $action_attrs['noun_article'] ? $action_attrs['noun_article'] : $this->getMyArticle(3); $id = $entity ? $entity->id : null; /* * 2) Identification du type d'entité : 3 types * * - Materiel => $IS_ENTITY_MATERIEL => max d'infos * - Entité liée au Materiel (HasMany Document, Emprunt, Suivi, ...) => $IS_ENTITY_LINKED_TO_MATERIEL => infos sur l'entité ET sur le matériel lié * - Entité autre (BelongsTo User, Fournisseur, Categorie, ...) => $IS_ENTITY_OTHER => infos minimum sur l'entité seulement */ $IS_ENTITY_MATERIEL = $entity instanceof Materiel; $IS_ENTITY_LINKED_TO_MATERIEL = false; $entity_types_linked_to_materiel = ['Suivi', 'Emprunt', 'Document']; foreach ($entity_types_linked_to_materiel as $Entity_type_linked) { // Si c'est une entité liée et qu'elle a bien un Materiel associé // (par exemple pour Document, ce n'est pas forcément le cas car il peut être associé à un Suivi et non un Materiel) //if ( ($entity instanceof $Entity_type_linked) && $entity->materiel_id ) { //if ( is_a($entity, "App\\Model\\Entity\\".$Entity_type_linked) && $entity->materiel_id ) { if ( is_a($entity, "App\\Model\\Entity\\".$Entity_type_linked) ) { // On vérifie que cette entité est bien liée à un matériel // car ce n'est pas toujours le cas. // Par exemple, un Document peut être lié à un Suivi (au lieu d'un Materiel) if ($entity->materiel_id) { $IS_ENTITY_LINKED_TO_MATERIEL = true; break; } //else throw new \Exception("L'entité $entity devrait être liée à un matériel via son champ FK materiel_id, or ce champ est null !"); } } $IS_ENTITY_OTHER = !$IS_ENTITY_MATERIEL && !$IS_ENTITY_LINKED_TO_MATERIEL; // On définit quelques infos sur le matériel s'il est disponible $materiel = null; $contain = ['SurCategories', 'Categories', 'SousCategories']; if ($IS_ENTITY_MATERIEL) { $materiel = ($action == 'delete') ? $entity : $this->getEntity($entity->id, false, $contain); } elseif ($IS_ENTITY_LINKED_TO_MATERIEL) $materiel = $this->getCurrentEntityRelatedMateriel($entity->materiel_id, $contain); /* * 3) LOG (si log demandé) * * Exemples : * ex: "/materiels/add fait par Pallier Etienne (Super Administrateur) - Matériel ajouté = 'toto' (id=15)" * ex: "/emprunts/delete/29 fait par Pallier Etienne (Super Administrateur) - emprunt supprimé = 'Emprunt#29 (de toto)' (id=29) - matériel lié 'tititoto' (http://labinvent.devv/materiels/view/12042)" * ex: "Document modifié = 'doc-toto' (id=112)" * ex: "Utilisateur connecté = 'Pierre Durand' (id=112)" * ex: "Utilisateur déconnecté = 'Pierre Durand' (id=112)" */ if ($this->isNotifierActionSendingLog($action)) { //$msglog = "$entity_type {$action_attrs['past_verb']} = '$entity' (id=$id)"; //$msglog = "$entity_type {$action_attrs['past_verb']} = '$entity_name' (id=$id)"; //$msglog = ucfirst($entity_type).' '.$action_attrs['past_verb_conj']; //$msglog = ucfirst($entity_nice_type_name).' '.$action_attrs['past_verb_conj']; $msglog = ucfirst($entity_nice_type_name); if ($IS_ENTITY_OTHER) $msglog .= '(s)'; $msglog .= ' '.$action_attrs['past_verb_conj']; if ($IS_ENTITY_OTHER) { $msglog .= '(s)'; if ($action=='index') $msglog .= ' (avec POST)'; } if ($entity) $msglog .= " = '$entity_name' (id=$id)"; // Cas particulier d'un Document : on affiche son type if ($entity instanceof Document) $msglog .= $doc_infos; // Si c'est une entité liée à un matériel, on ajoute des infos sur le matériel : " - matériel lié 'tititoto' (url)" if ($IS_ENTITY_LINKED_TO_MATERIEL) { //$msglog .= " - matériel lié '{$materiel->designation}' ($host/materiels/view/{$materiel->id})"; $msglog .= " - lié au Materiel '{$materiel->designation}' (id={$materiel->id})"; } // CAS TRES PARTICULIER d'un Document lié à un Suivi (et non pas à un Materiel) if ($IS_ENTITY_OTHER && $entity instanceof Document && $entity->suivi_id) { //$msglog .= " - suivi lié '{$entity->Suivi->nom}' (id={$entity->suivi_id})"; $msglog .= " - lié au Suivi (id={$entity->suivi_id})"; } //debug($msglog); //(!$DEBUG) && //$this->isNotifierActionSendingLog($action) && $this->ilog("$entity_name $action_verb = '$entity' (id=$id)"); $this->ilog($msglog, $DEBUG); } /* * 4) EMAIL (si autorisé et si demandé) * */ // Si les deux cases "Activer l'envoi des mails.." sont décochées, on se fatigue pas à exécuter la fonction $configuration = $this->confLabinvent; if (!$configuration->envoi_mail && !$configuration->envoi_mail_guests) return null; // Si notification email pas demandée, on quitte if (!$DEBUG) if (! $this->isNotifierActionSendingEmail($action) ) return null; /* * 4.A - CRÉATION DU MAIL (sujet et body) * * On détermine le message et le sujet du mail en fonction de l'action effectuee * */ // (1) Sujet : d'un(e) //$subject = $subject ? $subject : "{$action_attrs['noun']} $article_dun$entity_type"; //$subject = $subject ? $subject : "{$action_attrs['noun']} $article_dun$entity_nice_type_name"; if (!$subject) { $subject = $action_attrs['noun'].' '; $subject .= $IS_ENTITY_OTHER ? 'de ' : $article_dun; $subject .= $entity_nice_type_name; if ($IS_ENTITY_OTHER) $subject .= '(s)'; } $DEBUG && debug($subject); // (2) Message (body) : // - a) TOUS : "User a fait telle action (+url)" // Par défaut /* $subject = "Action \"$action\" sur un matériel"; $msg = "$acteur a effectué l'action \"$action\" sur le matériel \"$nom_materiel\""; */ $msg_mail = $msg; //$msg_mail .= "$user a {$action_attrs['past_verb']} $article_le $entity_type '$entity'"; $msg_mail .= "$user a ".$action_attrs['past_verb']; if (! $entity) { $msg_mail .= ' des '.$entity_nice_type_name."s (action '$action')"; if ($action=='index') $msg_mail .= ' (avec POST)'; } else { //$msg_mail .= " $article_le$entity_type '$entity_name'"; $msg_mail .= " $article_le$entity_nice_type_name '$entity_name'"; if ($action=='index') $msg_mail .= ' (avec POST)'; // Cas particulier d'un Document : on affiche son type //if ($entity instanceof Document) $msg_mail .= " (type={$entity->type_doc})"; if ($entity instanceof Document) $msg_mail .= $doc_infos; // (EP) Ajout de l'url (ou id) du materiel //$msg_mail .= $action=='delete' ? " (id=$id)" : " ($host/materiels/view/$id)"; $msg_mail .= $action=='delete' ? " (id=$id)" : " ($host/$entities_name/view/$id)"; } //$msg .= "\n\nURL de la fiche : $host/materiels/view/{$materiel->id}"; // $msg .= "\n\n"; // - b) Matos (direct ou associé) only : On ajoute quelques infos sur le matos: "domaine, catégorie, sous-categ, description" // CAS TRES PARTICULIER d'un Document lié à un Suivi (et non pas à un Materiel) if ($IS_ENTITY_OTHER && $entity instanceof Document && $entity->suivi_id) { //$msglog .= " - suivi lié '{$entity->Suivi->nom}' (id={$entity->suivi_id})"; $msg_mail .= " - lié au Suivi (id={$entity->suivi_id})"; } if ($IS_ENTITY_MATERIEL || $IS_ENTITY_LINKED_TO_MATERIEL) { $msg_detail = "\n\nAttributs du matériel "; //$materiel = ($entity instanceof Materiel) ? $entity : $this->getMateriel($entity->materiel_id); ////$contain = ['SurCategories', 'Categories', 'SousCategories']; //$materiel = ($IS_ENTITY_MATERIEL) ? $this->getEntity($entity->id, false, $contain) : $this->getCurrentEntityRelatedMateriel($entity->materiel_id, $contain); if ($IS_ENTITY_MATERIEL) { ///$materiel = $this->getEntity($entity->id, false, $contain); $msg_detail .= ':'; } else { ///$materiel = $this->getCurrentEntityRelatedMateriel($entity->materiel_id, $contain); $msg_detail .= "lié ($host/materiels/view/{$materiel->id}) :"; $msg_detail .= "\n\n- Nom : {$materiel->designation}"; } // marche pas pour 'edit' car sur_category contient l'ancien nom (avant modif)... //if (!$materiel->has('sur_category')) { $fields = [ 'nom_responsable' => ['hasnot!', 'Acheteur'], 'nom_user' => ['hasnot!', 'Utilisateur'], 'sur_categorie_id' => ['sur_category', 'Domaine'], 'categorie_id' => ['category', 'Catégorie'], 'sous_categorie_id' => ['sous_category', 'Sous-catégorie'], 'description' => ['hasnot!', 'Description'] ]; foreach ($fields as $fname=>$attrs) if ($materiel->$fname) { $val = $materiel->has($attrs[0]) ? $materiel->{$attrs[0]}->nom : $materiel->$fname; $msg_detail .= "\n\n- $attrs[1] : " . $val; } //$linked = $ENTITY_TYPE_LINKED ? ' lié' : ''; //if ($msg_more) $msg_mail .= "\n\nAttributs du matériel$linked : $msg_more"; if ($msg_detail) $msg_mail .= $msg_detail; } // Entité Materiel ou associée // - (3) Matos only (sauf delete, dévalidé, TBA et archive) : "Veuillez vérifier la fiche matériel..." $actions_no_need_to_be_checked = ['view', 'index', 'delete', 'statusCreated', 'statusTobearchived', 'statusArchived']; if ( $IS_ENTITY_MATERIEL && !in_array($action,$actions_no_need_to_be_checked) ) $msg_mail .= "\n\nVeuillez vérifier et compléter si besoin la fiche correspondante."; // - (4) TOUS : "Vous recevez ce message car... (raison)" //if ($entity) { $msg_mail .= "\n\nVous recevez ce message car"; $raison = " vous êtes concerné(e) par cette action effectuée sur l'inventaire des matériels du laboratoire"; if ($action == 'mailDevis') $raison .= "\n\n(vous êtes le gestionnaire de référence du matériel)"; else $raison .= "\n\n(vous êtes l'utilisateur du matériel, ou bien le gestionnaire, ou encore le responsable thématique, métier ou projet)"; if ($action != 'mailDevis') $raison .= "\n\n(ou alors, vous êtes dans la liste mail spécifique gérée via la page de configuration du logiciel LabInvent)."; $msg_mail .= $raison; //} $DEBUG && debug($msg_mail); /* * 4.B - DESTINATAIRES du mail * * On construit la liste des destinataires * */ $mailList = []; /* * a) Envoi général (uniquement pour les entités liées à un matériel) * * Si l'envoi général est activé, on ajoute les mails des personnes concernées : * - (1) le proprio (l'utilisateur) du matériel (sauf s'il est l'acteur de l'action) * - (2) le gestionnaire de référence du matos * - (3) les responsables thématiques et métier * - (TODO:) les responsables (scientifique et chef projet) du projet */ // ssi Envoi général activé if ($configuration->envoi_mail) { if (!$IS_ENTITY_OTHER) { // - (1) Ajout de l'utilisateur du matériel //if ($this->userName != $materiel->nom_responsable) $mailList[0] = $materiel->email_responsable; //if ($this->userName == $materiel->nom_responsable) $mailList[] = $materiel->email_responsable; // - (2) Ajout du Gestionnaire de référence du matos (s'il y en a un) if ($materiel->gestionnaire_id) { $gestionnaire_ref_email = TableRegistry::getTableLocator()->get('Users')->get($materiel->gestionnaire_id)->email; $mailList[] = $gestionnaire_ref_email; } /* if ($materiel->gestionnaire_id) $mailList[] = TableRegistry::getTableLocator()->get('Users') ->find() ->where([ 'id =' => $materiel->gestionnaire_id, 'role =' => 'Administration', ]) ->select('email') //->toArray(); */ // - (3) Ajout des Responsables de groupe thématique ou métier /* $mailsRespMetier = []; $mailRespThematique = []; */ $resp_mails = []; //$groups_fk = [ 'groupes_metier_id', 'groupes_thematique_id' ]; $groups_fk = [ 'groupes_metier', 'groupes_thematique' ]; foreach ($groups_fk as $group_fk) { $group_fk_id = $group_fk.'_id'; $resp_mails[$group_fk_id] = []; if ($materiel->$group_fk_id) //$mailsRespMetier = TableRegistry::getTableLocator()->get('Users')->find() $resp_mails[$group_fk_id] = TableRegistry::getTableLocator()->get('Users')->find() ->select('email') ->where([ 'role =' => 'Responsable', "$group_fk_id =" => $materiel->$group_fk_id, //"is_resp_$group_fk" // true "is_resp_$group_fk =" => 1 ]) /* ->where([ 'role =' => 'Responsable', "$group_fk =" => $materiel->$group_fk ]) */ ->toArray(); } //$DEBUG && debug($resp_mails); //exit; // On ajoute ces responsables à la liste générale //$resp_mails = array_merge($resp_mails); //$resp_mails = array_values($resp_mails); //$resp_mails = array_values($resp_mails); //$resp_mails['groupes_metier_id']=[]; $resp_mails['groupes_thematique_id']=[]; $resp_mails = array_merge($resp_mails['groupes_metier_id'], $resp_mails['groupes_thematique_id']); //$DEBUG && debug($resp_mails); foreach ($resp_mails as $resp_mail) $mailList[] = $resp_mail['email']; } // uniquement pour entité liée à un matériel } // ssi envoi général activé /* * b) Envoi à la liste spécifique * * Si l'envoi à la liste spécifique est activé, * on ajoute simplement TOUS les mails de cette liste, * sans condition aucune, * car ce sont des personnes qui ont demandé à être informées * systématiquement de toutes les actions * */ // ssi envoi liste spécifique activé //if ($configuration->envoi_mail_guests && $action != 'printLabelRuban') { if ($configuration->envoi_mail_guests) { // mail aux adresses specifiees dans la config for ($i = 0; $i < 11; $i ++) { /* $mail_field = "emailGuest$i"; $specificUser = $configuration->$mail_field; */ $specificUser = $configuration["emailGuest$i"]; if ($specificUser) $mailList[] = $specificUser; //$specificUsers[] = $specificUser; // $mailList[sizeof($mailList)] = $configuration['emailGuest' . $i]; } } // ssi envoi liste spécifique activé /* * c) NETTOYAGE * On nettoie la liste des destinataires en supprimant : * - (1) les doublons éventuels * - (2) l'auteur de l'action (puisqu'il est déjà au courant) */ /* test : ajout de doublons $mailList[] = 'Etienne.Pallier@irap.omp.eu'; $mailList[] = 'etienne.Pallier@irap.omp.eu'; $mailList[] = 'elodie.bourrec@irap.omp.eu'; $mailList[] = 'Elodie.bourrec@irap.omp.eu'; */ // - (1) On supprime les doublons // On met tous les mails en minuscule car sinon certains doublons pourraient ne pas être identifiés... $mailList = array_map('strtolower', $mailList); $mailList = array_unique($mailList); //$DEBUG && debug($mailList); // - (2) On supprime l'auteur de l'action le cas échéant //if ($DEBUG) debug($this->u->email); $found = array_search(strtolower($this->u->email), $mailList); //$found = array_search('nathalie.baby@irap.omp.eu', $mailList); //$DEBUG && debug($found); if ($found !== false) unset($mailList[$found]); // Eventuellement, pour renuméroter (supprime les indices manquants) sort($mailList); /* * 4.C - ENVOI DU MAIL * * Enfin, on envoie le mail à toute la liste des destinataires */ // Si action 'mailDevis', on envoie un mail SEULEMENT au gestionnaire de référence du matériel if ($action == 'mailDevis') $mailList = [$gestionnaire_ref_email]; //$mailList = ['epallier@irap.omp.eu']; //$mailList = ['etienne.pallier@irap.omp.eu']; //$mailList = ['etiennoury@gmail.com']; foreach ($mailList as $email_addr) { // On envoi des mails à toute la liste, sauf pour "l'acteur", il sait ce qu'il a fait, pas besoin de le spam non plus hein //if ($mail == $_SESSION['Auth']['User']['mail'][0]) //continue; //$message = $msg; // Sisi, cette variable $message est utile, m'enfin vous pouvez toujours essayer de la supprimer ..... Et pensez à regarder le contenu de vos mails !!! Sinon ca fait une tumeur $message = $msg_mail; // Si action 'mailDevis', on ajoute le nom du fichier attaché if ($action == 'mailDevis') { // On change le contenu en fonction de si le document a été lié à un matériel ou à un suivi $id_mat = $entity->materiel_id; $id_suiv = $entity->suivi_id; $id = $id_mat ? $id_mat : $id_suiv; //$msg .= "\n\n (id doc = {$doc->id}, id du $entity_name associé = $id)"; $nomFic = $id."_".$entity->nom."_".$entity->id.".".$entity->type_doc; //$subject = "$acteur a partagé un document avec vous"; //$msg = "$acteur après avoir ajouté le document \"$nom_doc\", a voulu le partager avec vous, c'est un document ayant le format \"$type_doc\" ."; //$msg .="\n\n Le document est en pièce jointe."; //si le mode 1 est sélectionné c'est un envoi de mail avec ajout d'une photo (explique le document qui a été ajouté, et le met en pièce jointe) //si le mode 2 est sélectionné c'est un envoi de mail avec ajout d'un doc à personnaliser $fn = $entity->photo ? 'sendEmailImgTo' : 'sendEmailPJTo'; !$DEBUG && $this->$fn("$subject", $message, $email_addr, $configuration, $nomFic); } // Pour toutes les autres actions, mode par defaut (3) => simple envoi de mail else { //debug($subject); //debug($message); !$DEBUG && $this->sendEmailTo("$subject", $message, $email_addr, $configuration); } ////} // entity not null } //foreach ($mailList as $mail) $DEBUG && debug($mailList); $DEBUG && exit; return $mailList; } // sendmail() } // class AppController