Commit 666aeb7c authored by Christian BUFFIN's avatar Christian BUFFIN
Browse files

Corrections et améliorations du paramétrage des types d'annexes

parent d7be45fc
......@@ -29,22 +29,23 @@ class TypagesController extends AppController {
];
/**
* @throws ForbiddenException
*
* @author Théo GUILLON <theo.guillon@libriciel.coop>
* @access public
* @created 22/04/2020
* @version V1.2.0
* Vérification de l'accès aux actions en fonction du profil de l'utilisateur connecté.
* Les vérifications de l'accès aux enregistrements se font dans les méthodes d'actions.
*/
public function index()
{
if (true !== $this->Droits->authorized(ListeDroit::GESTION_TYPAGE)) {
throw new ForbiddenException(__d('default', 'default.flasherrorPasDroitPage'));
}
public function beforeFilter() {
parent::beforeFilter();
$this->set('title', __d('typage', 'typage.titreListeTypages'));
$this->Droits->assertAuthorized([ListeDroit::GESTION_TYPAGE]);
}
$query = [
/**
* Retourne les résultats du moteur de recherche, que ce soit pour la liste complète ou pour la liste de ceux
* présents dans l'entité.
*
* @return array
*/
protected function _getSearchResults() {
$paginate = [
'contain' => [
'Organisation' => [
'id',
......@@ -52,63 +53,97 @@ class TypagesController extends AppController {
'order' => ['raisonsociale']
]
],
'conditions' => [],
'order' => [
'Typage.libelle ASC'
]
];
if ($this->request->is('post')) {
if (isset($this->request->data['TypageOrganisation']) && empty($this->request->data['TypageOrganisation']['organisation_id'])) {
$this->redirect(['action' => 'index']);
// Applications des filtres
if ($this->request->is('post') && $this->request->params['action'] === 'index') {
// Filtrer par entité associée
if (!empty($this->request->data['Filtre']['organisation'])) {
$paginate['conditions'][] = $this->Typage->getConditionOrganisation($this->request->data['Filtre']['organisation']);
}
// Filtrer par entité créatrice
$createdbyorganisation = (string)Hash::get($this->request->data, 'Filtre.createdbyorganisation');
if ($createdbyorganisation !== '') {
$paginate['conditions'][] = ['Typage.createdbyorganisation' => $createdbyorganisation];
}
}
$success = true;
$this->Typage->TypageOrganisation->begin();
$organisations_ids = Hash::extract($this->request->data, 'TypageOrganisation.organisation_id');
$typage_ids = Hash::extract($this->request->data, 'TypageOrganisation.typage_id');
foreach ($organisations_ids as $organisation_id) {
$typageEntite = $this->Typage->TypageOrganisation->find('list', [
'conditions' => [
'organisation_id' => $organisation_id
],
'fields' => [
'typage_id'
]
]);
$result = array_intersect($typageEntite, $typage_ids);
$typage_ids = array_diff($typage_ids, $result);
foreach ($typage_ids as $typage_id) {
$data = [
'TypageOrganisation' => [
'typage_id' => $typage_id,
'organisation_id' => $organisation_id,
]
];
// Ajout de conditions suivant l'utilisateur connecté et l'action
if ($this->request->params['action'] === 'entite') {
$paginate['conditions'][] = $this->Typage->getConditionOrganisation($this->Session->read('Organisation.id'));
}
$this->Typage->TypageOrganisation->create($data);
$success = $success && false !== $this->Typage->TypageOrganisation->save(null, ['atomic' => false]);
}
}
$this->paginate = $paginate;
return $this->paginate($this->Typage);
}
if ($success == true) {
$this->Typage->commit();
$this->Session->setFlash(__d('typage', 'typage.flashsuccessTypageAffecterEnregistrer'), 'flashsuccess');
protected function _optionsFiltre($organisation_id = null) {
$options = [
'organisations' => $this->Typage->Organisation->find('list', ['order' => ['Organisation.raisonsociale ASC']]),
];
$this->redirect(['action' => 'index']);
} else {
$this->Typage->rollback();
$this->Session->setFlash(__d('typage', 'typage.flasherrorErreurEnregistrementTypageAffecter'), 'flasherror');
return $options;
}
/**
* @author Théo GUILLON <theo.guillon@libriciel.coop>
* @access public
* @created 22/04/2020
* @version V1.2.0
*/
public function index()
{
$this->set('title', __d('typage', 'typage.titreListeTypages'));
if ($this->request->is('post')) {
//@fixme: la value dans le formulaire d'association doit revenir à vide à chaque fois + dans les autres
if (isset($this->request->data['TypageOrganisation']) && empty($this->request->data['TypageOrganisation']['organisation_id']) === false) {
$success = true;
$this->Typage->TypageOrganisation->begin();
$organisations_ids = Hash::extract($this->request->data, 'TypageOrganisation.organisation_id');
$typage_ids = Hash::extract($this->request->data, 'TypageOrganisation.typage_id');
foreach ($organisations_ids as $organisation_id) {
$typageEntite = $this->Typage->TypageOrganisation->find('list', [
'conditions' => [
'organisation_id' => $organisation_id
],
'fields' => [
'typage_id'
]
]);
$result = array_intersect($typageEntite, $typage_ids);
$typage_ids = array_diff($typage_ids, $result);
foreach ($typage_ids as $typage_id) {
$data = [
'TypageOrganisation' => [
'typage_id' => $typage_id,
'organisation_id' => $organisation_id,
]
];
$this->Typage->TypageOrganisation->create($data);
$success = $success && false !== $this->Typage->TypageOrganisation->save(null, ['atomic' => false]);
}
}
if ($success == true) {
$this->Typage->commit();
$this->Session->setFlash(__d('typage', 'typage.flashsuccessTypageAffecterEnregistrer'), 'flashsuccess');
} else {
$this->Typage->rollback();
$this->Session->setFlash(__d('typage', 'typage.flasherrorErreurEnregistrementTypageAffecter'), 'flasherror');
}
}
}
$this->paginate = $query;
$typages = $this->paginate($this->Typage);
$mesOrganisations = $this->WebcilUsers->organisations(
'list',
[
......@@ -118,7 +153,11 @@ class TypagesController extends AppController {
]
);
$this->set(compact('typages', 'mesOrganisations'));
$this->set([
'mesOrganisations' => $mesOrganisations,
'options' => $this->_optionsFiltre(),
'typages' => $this->_getSearchResults(),
]);
}
/**
......@@ -128,48 +167,16 @@ class TypagesController extends AppController {
* @version V1.2.0
*/
public function entite() {
if (true !== $this->Droits->authorized(ListeDroit::GESTION_TYPAGE)) {
throw new ForbiddenException(__d('default', 'default.flasherrorPasDroitPage'));
}
$this->set('title', __d('typage', 'typage.titreGestionTypageEntitee'));
$organisation_id = $this->Session->read('Organisation.id');
$query = [
'conditions' => [],
'fields' => [
'Typage.id',
'Typage.libelle'
],
'order' => [
'Typage.libelle ASC'
]
];
$subQuery = [
'alias' => 'typages_organisations',
'fields' => [
'typages_organisations.typage_id'
],
'conditions' => [
'typages_organisations.organisation_id' => $organisation_id
]
];
$sql = $this->Typage->TypageOrganisation->sql($subQuery);
$query['conditions'][] = "Typage.id IN ( {$sql} )";
$this->paginate = $query;
$typages = $this->paginate($this->Typage);
$this->set(compact('typages'));
$this->set([
'typages' => $this->_getSearchResults(),
]);
}
/**
* Fonction qui permet l'ajout d'une nouvelle norme
*
* @throws ForbiddenException
*
* @author Théo GUILLON <theo.guillon@libriciel.coop>
* @access public
* @created 22/04/2020
......@@ -177,71 +184,25 @@ class TypagesController extends AppController {
*/
public function add()
{
return $this->edit();
}
/**
* Récupère et renvoie un type d'annexe en fonction de l'utilisateur connecté (SU ou utilisateur appartenant l'ayant
* créé) et vérifie que celui-ci n'est associé à aucun entité.
*
* Utilisé pour vérification avant modification ou suppression d'un type d'annexe.
*
* @throws NotFoundException
* @throws ForbiddenException
*
* @param int $id L'id du type d'annexe
* @return array
*/
protected function _getAndCheckTypageAccessAndUsage($id)
{
$query = [
'fields' => array_merge(
$this->Typage->fields(),
[$this->Typage->vfLinkedOrganisationsCount()]
),
'conditions' => [
'Typage.id' => $id
]
];
$typage = $this->Typage->find('first', $query);
if (empty($typage) === true) {
throw new NotFoundException();
}
if ($typage['Typage']['organisations_count'] > 0) {
throw new ForbiddenException(__d('default', 'default.flasherrorPasDroitPage'));
}
// A-t-on les droits sur l'enregistrement ?
if ($this->Droits->isSu() === false && $typage['Typage']['createdbyorganisation'] !== null && $typage['Typage']['createdbyorganisation'] !== $this->Session->read('Organisation.id')) {
throw new ForbiddenException(__d('default', 'default.flasherrorPasDroitPage'));
}
return $typage;
return $this->edit(null);
}
/**
* Fonction qui permet la modification d'une norme
*
* @param int $id
* @throws ForbiddenException
*
* @author Théo GUILLON <theo.guillon@libriciel.coop>
* @access public
* @created 22/04/2020
* @version V1.2.0
*/
public function edit($id = null)
public function edit($id)
{
if (true !== $this->Droits->authorized(ListeDroit::GESTION_TYPAGE)) {
throw new ForbiddenException(__d('default', 'default.flasherrorPasDroitPage'));
}
if ($this->request->params['action'] === 'add') {
$this->set('title', __d('typage', 'typage.titreAddType'));
} else {
$typage = $this->_getAndCheckTypageAccessAndUsage($id);
$typage = $this->Droits->getAndCheckLinkedOrganisationsRecord('Typage', $id, false);
$this->set('title', __d('typage', 'typage.titreeditType'));
}
......@@ -285,10 +246,6 @@ class TypagesController extends AppController {
* @version V1.2.0
*/
public function dissocier($type_id) {
if (true !== $this->Droits->authorized(ListeDroit::GESTION_TYPAGE)) {
throw new ForbiddenException(__d('default', 'default.flasherrorPasDroitPage'));
}
$this->Typage->TypageOrganisation->begin();
$success = false !== $this->Typage->TypageOrganisation->deleteAll([
......@@ -318,11 +275,7 @@ class TypagesController extends AppController {
* @version V1.2.0
*/
public function delete($id) {
if (true !== $this->Droits->authorized(ListeDroit::GESTION_TYPAGE)) {
throw new ForbiddenException(__d('default', 'default.flasherrorPasDroitPage'));
}
$this->_getAndCheckTypageAccessAndUsage($id);
$this->Droits->getAndCheckLinkedOrganisationsRecord('Typage', $id, true);
$this->Typage->begin();
if ($this->Typage->delete($id) === true) {
......
......@@ -37,13 +37,13 @@ msgid "typage.flashsuccessTypageAffecterEnregistrer"
msgstr "L'association du type d'annexe a été enregistrée"
msgid "typage.flashsuccesSaveTypage"
msgstr "Le type d'annexe a bien été enregisté"
msgstr "Le type d'annexe a bien été enregistré"
msgid "typage.flashsuccessSuppressionTypage"
msgstr "La suppression du type d'annexe a été enregistée"
msgstr "La suppression du type d'annexe a été enregistrée"
msgid "typage.flashsuccessDissocier"
msgstr "La dissociation du type d'annexe a été enregistée"
msgstr "La dissociation du type d'annexe a été enregistrée"
################
......@@ -70,6 +70,21 @@ msgstr "Une erreur est survenue lors de la dissociation du type d'annexe"
######################### View/Typages/index.ctp #########################
msgid "typage.btnFiltrerTypage"
msgstr " Filtrer les types d'annexes"
msgid "typage.champFiltreEntite"
msgstr "Filtrer par entité"
msgid "typage.champFiltreEntiteAssociee"
msgstr "Filtrer par entité associée"
msgid "typage.champFiltreEntiteCreatrice"
msgstr "Filtrer par entité créatrice"
msgid "typage.placeholderChoisirEntite"
msgstr "Choisir une entité"
msgid "typage.btnAffecterEntite"
msgstr "Associer un type d'annexe à mon entité"
......@@ -85,6 +100,9 @@ msgstr "Actions"
msgid "typage.textAucunTypage"
msgstr "Il n'existe aucun type d'annexe"
msgid "typage.filtreTextAucunTypage"
msgstr "Il n'existe aucun type d'annexe pour ces filtres"
msgid "typage.textAucunTypageEntite"
msgstr "Il n'existe aucun type d'annexe associé à votre entité"
......
......@@ -21,8 +21,9 @@
*/
App::uses('AppModel', 'Model');
App::uses('LinkedOrganisationInterface', 'Model/Interface');
class Typage extends AppModel {
class Typage extends AppModel implements LinkedOrganisationInterface {
public $name = 'Typage';
......@@ -90,4 +91,41 @@ class Typage extends AppModel {
$sql = $this->TypageOrganisation->sql($subQuery);
return "( {$sql} ) AS \"{$this->alias}__{$fieldName}\"";
}
/**
* Retourne les id des entités liées au type d'annexe.
*
* @param int $id
* @return array
*/
public function getLinkedOrganisationsIds($id) {
$query = [
'fields' => ['organisation_id'],
'conditions' => ['typage_id' => $id],
];
return Hash::extract(
$this->TypageOrganisation->find('all', $query),
'{n}.TypageOrganisation.organisation_id'
);
}
/**
* Retourne une condition permettant de limiter aux types d'annexes liés à une ou plusieurs entités.
*
* @param array|int $organisation_id
* @return string
*/
public function getConditionOrganisation($organisation_id) {
$query = [
'alias' => 'typages_organisations',
'fields' => [
'typages_organisations.typage_id'
],
'conditions' => [
'typages_organisations.organisation_id' => $organisation_id
]
];
$sql = $this->TypageOrganisation->sql($query);
return "{$this->alias}.{$this->primaryKey} IN ( {$sql} )";
}
}
<?php
App::uses('AppController', 'Controller');
App::uses( 'ControllerTestCaseAccessTrait', 'Test/Trait/Controller' );
/**
* Tests d'intégration de la classe TypagesController.
*
* ./cake_utils.sh tests app Controller/TypagesController
*
* @package app.Test.Case.Controller
*/
class TypagesControllerTest extends ControllerTestCase
{
use ControllerTestCaseAccessTrait;
public $fixtures = [
'app.Fiche',
'app.ListeDroit',
'app.Notification',
'app.Organisation',
'app.OrganisationUser',
'app.OrganisationUserRole',
'app.Role',
'app.RoleDroit',
'app.Typage',
'app.TypageOrganisation',
'app.User',
'app.Valeur',
];
public function setUp() {
parent::setUp();
$this->controller = $this->generate('Typages');
}
public function dataAccessAdd() {
return [
// 1. Utilisateurs pouvant accéder à la fonctionnalité
[200, 'Superadministrateur.superadmin', '/typages/add'],
[200, 'Administrateur.ibleu', '/typages/add'],
[200, 'DPO.nroux', '/typages/add'],
// 2. Utilisateurs ne pouvant pas accéder à la fonctionnalité
[403, 'Rédacteur.rjaune', '/typages/add'],
[403, 'Valideur.cnoir', '/typages/add'],
[403, 'Consultant.mrose', '/typages/add'],
];
}
/**
* @dataProvider dataAccessAdd
*/
public function testAccessAdd($expectedStatus, $user, $url, $options = []) {
$this->assertActionAccess($expectedStatus, $user, $url, $options);
}
public function dataAccessDelete() {
return [
// 1. Utilisateurs pouvant accéder à la fonctionnalité
// 1.1. Enregistrement existant
// 1.1.1. Enregistrement existant et attaché à des entités
[403, 'Superadministrateur.superadmin', '/typages/delete/1'],
[403, 'Administrateur.ibleu', '/typages/delete/1'],
[403, 'DPO.nroux', '/typages/delete/1'],
// 1.1.2. Enregistrement existant et non attaché à des entités
[302, 'Superadministrateur.superadmin', '/typages/delete/3'],
//[302, 'Administrateur.ibleu', '/typages/delete/3'],
//[302, 'DPO.nroux', '/typages/delete/3'],
// 2.2. Enregistrement inexistant
[404, 'Superadministrateur.superadmin', '/typages/delete/666'],
[404, 'Administrateur.ibleu', '/typages/delete/666'],
[404, 'DPO.nroux', '/typages/delete/666'],
// 2. Utilisateurs ne pouvant pas accéder à la fonctionnalité
// 1.1. Enregistrement existant
[403, 'Rédacteur.rjaune', '/typages/delete/3'],
[403, 'Valideur.cnoir', '/typages/delete/3'],
[403, 'Consultant.mrose', '/typages/delete/3'],
// 2.2. Enregistrement inexistant
[403, 'Rédacteur.rjaune', '/typages/delete/666'],
[403, 'Valideur.cnoir', '/typages/delete/666'],
[403, 'Consultant.mrose', '/typages/delete/666'],
];
}
/**
* @dataProvider dataAccessDelete
*/
public function testAccessDelete($expectedStatus, $user, $url, $options = []) {
$this->assertActionAccess($expectedStatus, $user, $url, $options);
}
public function dataAccessDissocier() {
return [
// 1. Utilisateurs pouvant accéder à la fonctionnalité
// 1.1. Enregistrement existant
[302, 'Superadministrateur.superadmin', '/typages/dissocier/1'],
[302, 'Administrateur.ibleu', '/typages/dissocier/1'],
[302, 'DPO.nroux', '/typages/dissocier/1'],
// Un utilisateur mono-collectivité ne peut pas accéder à l'enregistrement d'une autre collectivité
[403, 'Administrateur.findigo', '/typages/dissocier/2'],
[403, 'DPO.hvermeil', '/typages/dissocier/2'],
// 1.2. Enregistrement inexistant
[404, 'Superadministrateur.superadmin', '/typages/dissocier/666'],
[404, 'Administrateur.ibleu', '/typages/dissocier/666'],
[404, 'DPO.nroux', '/typages/dissocier/666'],
// 2. Utilisateurs ne pouvant pas accéder à la fonctionnalité
// 1.1. Enregistrement existant
[403, 'Rédacteur.rjaune', '/typages/dissocier/1'],
[403, 'Valideur.cnoir', '/typages/dissocier/1'],
[403, 'Consultant.mrose', '/typages/dissocier/1'],
// 2.2. Enregistrement inexistant
[403, 'Rédacteur.rjaune', '/typages/dissocier/666'],
[403, 'Valideur.cnoir', '/typages/dissocier/666'],
[403, 'Consultant.mrose', '/typages/dissocier/666'],
];
}
/**
* @dataProvider dataAccessDissocier
*/
public function xtestAccessDissocier($expectedStatus, $user, $url, $options = []) {
$this->assertActionAccess($expectedStatus, $user, $url, $options);
}
public function dataAccessEdit() {
return [
// 1. Utilisateurs pouvant accéder à la fonctionnalité
// 1.1. Enregistrement existant
[200, 'Superadministrateur.superadmin', '/typages/edit/3'],
//[302, 'Administrateur.ibleu', '/typages/edit/3'],
//[302, 'DPO.nroux', '/typages/edit/3'],
// 2.2. Enregistrement inexistant
[404, 'Superadministrateur.superadmin', '/typages/edit/666'],
[404, 'Administrateur.ibleu', '/typages/edit/666'],
[404, 'DPO.nroux', '/typages/edit/666'],
// 2. Utilisateurs ne pouvant pas accéder à la fonctionnalité
// 1.1. Enregistrement existant
[403, 'Rédacteur.rjaune', '/typages/edit/3'],
[403, 'Valideur.cnoir', '/typages/edit/3'],
[403, 'Consultant.mrose', '/typages/edit/3'],
// 2.2. Enregistrement inexistant
[403, 'Rédacteur.rjaune', '/typages/edit/666'],
[403, 'Valideur.cnoir', '/typages/edit/666'],
[403, 'Consultant.mrose', '/typages/edit/666'],
];
}
/**
* @dataProvider dataAccessEdit
*/
public function testAccessEdit($expectedStatus, $user, $url, $options = []) {
$this->assertActionAccess($expectedStatus, $user, $url, $options);
}
public function dataAccessIndex() {
return [
// 1. Utilisateurs pouvant accéder à la fonctionnalité
[200, 'Superadministrateur.superadmin', '/typages/index'],
[200, 'Administrateur.ibleu', '/typages/index'],
[200, 'DPO.nroux', '/typages/index'],
// 2. Utilisateurs ne pouvant pas accéder à la fonctionnalité
[403, 'Rédacteur.rjaune', '/typages/index'],
[403, 'Valideur.cnoir', '/typages/index'],
<