Loading CHANGELOG.md +1 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ Journal des modifications - Annuaire des thèses/HDR : affichage systématique de l'établissement d'inscription et plus seulement si le filtre est vierge. - Formulaire de création de compte local : obligation de choisir une civilité (Mme, M. ou Neutre). - Formulaire Admission : mention de la civilité neutre en plus de Mme M. - Recherche textuelle de compte utilisateur : possibilité de chercher 2 mots ou plus, portant aussi sur l'individu lié éventuel. - [FIX] Modification d'une structure impossible - [FIX] Recherche d'individus : suppression de la limite de 100 résultats maximum. - [FIX] Dépôt de thèse : plantage de l'annulation de la validation des corrections de la thèse. Loading module/Application/src/Application/Controller/UtilisateurController.php +2 −2 Original line number Diff line number Diff line Loading @@ -44,10 +44,10 @@ use UnicaenAuthentification\Options\ModuleOptions; use UnicaenAuthentification\Service\User as AuthentificationUserService; use UnicaenAuthToken\Controller\TokenController; use UnicaenAuthToken\Service\TokenServiceAwareTrait; use UnicaenAuthToken\Service\TokenServiceException; use Webmozart\Assert\Assert; /** * @property \Application\Service\Utilisateur\UtilisateurSearchService $searchService * @method \Application\Controller\Plugin\Forward forward() */ class UtilisateurController extends \UnicaenAuthentification\Controller\UtilisateurController implements SearchControllerInterface Loading Loading @@ -122,7 +122,7 @@ class UtilisateurController extends \UnicaenAuthentification\Controller\Utilisat * * @return ViewModel|Response */ public function indexAction() public function indexAction(): Response|ViewModel { $result = $this->search(); if ($result instanceof Response) { Loading module/Application/src/Application/Entity/Db/Repository/UtilisateurRepository.php +58 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ namespace Application\Entity\Db\Repository; use Doctrine\DBAL\Exception; use Individu\Entity\Db\Individu; use Application\Entity\Db\Utilisateur; use Doctrine\ORM\NonUniqueResultException; Loading Loading @@ -62,6 +63,63 @@ class UtilisateurRepository extends DefaultEntityRepository return $utilisateur; } /** * Recherche textuelle d'utilisateurs, en SQL pur. * * @param string $text * @param int $limit * * @return int[] Ids des utilisateurs trouvés */ public function findByText(string $text, int $limit = 100): array { if (strlen($text) < 2) return []; $pieces = explode(' ', $text); $sql = "select u.* " . "from utilisateur u " . "left join individu i on i.id = u.individu_id " . "left join individu_rech ir on ir.id = i.id "; $criteria = [ 'u.username', 'u.email', 'u.display_name', 'ir.haystack', ]; $sqlCri = []; foreach ($pieces as $p) { $sqlCri[$p] = []; foreach ($criteria as $c) { $sqlCri[$p][] = "$c like str_reduce($$%" . $p . "%$$)"; } $sqlCri[$p] = '(' . implode(' or ', $sqlCri[$p]) . ')'; } $sqlCri = implode(' and ', $sqlCri); $sql .= 'where (' . $sqlCri . ') '; if ($limit > 0) { $sql .= " limit " . $limit; } try { $stmt = $this->getEntityManager()->getConnection()->executeQuery($sql); } catch (Exception $e) { throw new RuntimeException( "Erreur rencontrée lors de l'exécution de la requête de recherche", null, $e); } try { return $stmt->fetchAllAssociative(); } catch (Exception $e) { throw new RuntimeException( "Impossible d'obtenir les résultats de la requête de recherche", null, $e); } } /** * Recherche un utilisateur par le token permettant de changer de mot de passe. */ Loading module/Application/src/Application/Service/Utilisateur/UtilisateurSearchService.php +20 −11 Original line number Diff line number Diff line Loading @@ -13,9 +13,6 @@ class UtilisateurSearchService extends SearchService { use UtilisateurServiceAwareTrait; /** * @inheritDoc */ protected function createQueryBuilder(): QueryBuilder { $qb = $this->utilisateurService->getRepository()->createQueryBuilder('u'); Loading @@ -30,18 +27,30 @@ class UtilisateurSearchService extends SearchService { // filtre textuel $textFilter = new TextSearchFilter("Recherche textuelle", 'text'); $textFilter->setAttributes(['title' => "Recherche sur le Display name, Username, Email ou Individu"]); $textFilter->setAttributes(['title' => "Recherche sur l'identité, l'identifiant de connexion, l'adresse électronique, ou l'utilisateur lié"]); $textFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) { $qb ->andWhere($qb->expr()->orX( 'UPPER(u.username) LIKE UPPER(:text)', 'UPPER(u.displayName) LIKE UPPER(:text)', 'UPPER(u.email) LIKE UPPER(:text)' )) ->setParameter('text', '%' . $filter->getValue() . '%'); $utilisateurs = $this->utilisateurService->getRepository()->findByText($filter->getValue(), limit: 0); if (count($utilisateurs) > 0) { $utilisateursIds = array_map(fn(array $utilisateur) => $utilisateur['id'], $utilisateurs); $qb->andWhere($qb->expr()->in('u.id', $utilisateursIds)); } }); $this->addFilter($textFilter); // // filtre textuel // $textFilter = new TextSearchFilter("Recherche textuelle", 'text'); // $textFilter->setAttributes(['title' => "Recherche sur le Display name, Username, Email ou Individu"]); // $textFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) { // $qb // ->andWhere($qb->expr()->orX( // 'UPPER(u.username) LIKE UPPER(:text)', // 'UPPER(u.displayName) LIKE UPPER(:text)', // 'UPPER(u.email) LIKE UPPER(:text)' // )) // ->setParameter('text', '%' . $filter->getValue() . '%'); // }); // $this->addFilter($textFilter); // filtre "est lié à un individu" $individuFilter = new CheckboxSearchFilter("Lié à un<br>individu", 'individu'); $individuFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) { Loading Loading
CHANGELOG.md +1 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ Journal des modifications - Annuaire des thèses/HDR : affichage systématique de l'établissement d'inscription et plus seulement si le filtre est vierge. - Formulaire de création de compte local : obligation de choisir une civilité (Mme, M. ou Neutre). - Formulaire Admission : mention de la civilité neutre en plus de Mme M. - Recherche textuelle de compte utilisateur : possibilité de chercher 2 mots ou plus, portant aussi sur l'individu lié éventuel. - [FIX] Modification d'une structure impossible - [FIX] Recherche d'individus : suppression de la limite de 100 résultats maximum. - [FIX] Dépôt de thèse : plantage de l'annulation de la validation des corrections de la thèse. Loading
module/Application/src/Application/Controller/UtilisateurController.php +2 −2 Original line number Diff line number Diff line Loading @@ -44,10 +44,10 @@ use UnicaenAuthentification\Options\ModuleOptions; use UnicaenAuthentification\Service\User as AuthentificationUserService; use UnicaenAuthToken\Controller\TokenController; use UnicaenAuthToken\Service\TokenServiceAwareTrait; use UnicaenAuthToken\Service\TokenServiceException; use Webmozart\Assert\Assert; /** * @property \Application\Service\Utilisateur\UtilisateurSearchService $searchService * @method \Application\Controller\Plugin\Forward forward() */ class UtilisateurController extends \UnicaenAuthentification\Controller\UtilisateurController implements SearchControllerInterface Loading Loading @@ -122,7 +122,7 @@ class UtilisateurController extends \UnicaenAuthentification\Controller\Utilisat * * @return ViewModel|Response */ public function indexAction() public function indexAction(): Response|ViewModel { $result = $this->search(); if ($result instanceof Response) { Loading
module/Application/src/Application/Entity/Db/Repository/UtilisateurRepository.php +58 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ namespace Application\Entity\Db\Repository; use Doctrine\DBAL\Exception; use Individu\Entity\Db\Individu; use Application\Entity\Db\Utilisateur; use Doctrine\ORM\NonUniqueResultException; Loading Loading @@ -62,6 +63,63 @@ class UtilisateurRepository extends DefaultEntityRepository return $utilisateur; } /** * Recherche textuelle d'utilisateurs, en SQL pur. * * @param string $text * @param int $limit * * @return int[] Ids des utilisateurs trouvés */ public function findByText(string $text, int $limit = 100): array { if (strlen($text) < 2) return []; $pieces = explode(' ', $text); $sql = "select u.* " . "from utilisateur u " . "left join individu i on i.id = u.individu_id " . "left join individu_rech ir on ir.id = i.id "; $criteria = [ 'u.username', 'u.email', 'u.display_name', 'ir.haystack', ]; $sqlCri = []; foreach ($pieces as $p) { $sqlCri[$p] = []; foreach ($criteria as $c) { $sqlCri[$p][] = "$c like str_reduce($$%" . $p . "%$$)"; } $sqlCri[$p] = '(' . implode(' or ', $sqlCri[$p]) . ')'; } $sqlCri = implode(' and ', $sqlCri); $sql .= 'where (' . $sqlCri . ') '; if ($limit > 0) { $sql .= " limit " . $limit; } try { $stmt = $this->getEntityManager()->getConnection()->executeQuery($sql); } catch (Exception $e) { throw new RuntimeException( "Erreur rencontrée lors de l'exécution de la requête de recherche", null, $e); } try { return $stmt->fetchAllAssociative(); } catch (Exception $e) { throw new RuntimeException( "Impossible d'obtenir les résultats de la requête de recherche", null, $e); } } /** * Recherche un utilisateur par le token permettant de changer de mot de passe. */ Loading
module/Application/src/Application/Service/Utilisateur/UtilisateurSearchService.php +20 −11 Original line number Diff line number Diff line Loading @@ -13,9 +13,6 @@ class UtilisateurSearchService extends SearchService { use UtilisateurServiceAwareTrait; /** * @inheritDoc */ protected function createQueryBuilder(): QueryBuilder { $qb = $this->utilisateurService->getRepository()->createQueryBuilder('u'); Loading @@ -30,18 +27,30 @@ class UtilisateurSearchService extends SearchService { // filtre textuel $textFilter = new TextSearchFilter("Recherche textuelle", 'text'); $textFilter->setAttributes(['title' => "Recherche sur le Display name, Username, Email ou Individu"]); $textFilter->setAttributes(['title' => "Recherche sur l'identité, l'identifiant de connexion, l'adresse électronique, ou l'utilisateur lié"]); $textFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) { $qb ->andWhere($qb->expr()->orX( 'UPPER(u.username) LIKE UPPER(:text)', 'UPPER(u.displayName) LIKE UPPER(:text)', 'UPPER(u.email) LIKE UPPER(:text)' )) ->setParameter('text', '%' . $filter->getValue() . '%'); $utilisateurs = $this->utilisateurService->getRepository()->findByText($filter->getValue(), limit: 0); if (count($utilisateurs) > 0) { $utilisateursIds = array_map(fn(array $utilisateur) => $utilisateur['id'], $utilisateurs); $qb->andWhere($qb->expr()->in('u.id', $utilisateursIds)); } }); $this->addFilter($textFilter); // // filtre textuel // $textFilter = new TextSearchFilter("Recherche textuelle", 'text'); // $textFilter->setAttributes(['title' => "Recherche sur le Display name, Username, Email ou Individu"]); // $textFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) { // $qb // ->andWhere($qb->expr()->orX( // 'UPPER(u.username) LIKE UPPER(:text)', // 'UPPER(u.displayName) LIKE UPPER(:text)', // 'UPPER(u.email) LIKE UPPER(:text)' // )) // ->setParameter('text', '%' . $filter->getValue() . '%'); // }); // $this->addFilter($textFilter); // filtre "est lié à un individu" $individuFilter = new CheckboxSearchFilter("Lié à un<br>individu", 'individu'); $individuFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) { Loading