Loading module/Oscar/src/Oscar/Controller/OrganizationController.php +74 −140 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ use BjyAuthorize\Exception\UnAuthorizedException; use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; use Doctrine\ORM\Query; use Elasticsearch\Common\Exceptions\BadRequest400Exception; use Elasticsearch\Common\Exceptions\NoNodesAvailableException; use Oscar\Connector\IConnector; use Oscar\Entity\Activity; use Oscar\Entity\ActivityOrganization; Loading Loading @@ -56,6 +57,7 @@ class OrganizationController extends AbstractOscarController implements UseOrgan /** * @param SessionService $sessionService * @return OrganizationController */ public function setSessionService(SessionService $sessionService): self { Loading Loading @@ -86,7 +88,8 @@ class OrganizationController extends AbstractOscarController implements UseOrgan ); } } } else { } else { $token = $this->getSessionService()->createToken(); } Loading @@ -100,6 +103,7 @@ class OrganizationController extends AbstractOscarController implements UseOrgan * Liste des organisations. * * @return array * @throws \Exception */ public function indexAction() { Loading @@ -111,8 +115,10 @@ class OrganizationController extends AbstractOscarController implements UseOrgan if ($this->getOscarUserContextService()->hasPrivileges(Privileges::ORGANIZATION_SHOW)) { $allow = true; $justXHR = false; } else { $allow = $this->getOscarUserContextService()->hasOneOfPrivilegesInAnyRoles([ } else { $allow = $this->getOscarUserContextService() ->hasOneOfPrivilegesInAnyRoles([ Privileges::ACTIVITY_ORGANIZATION_MANAGE, Privileges::PROJECT_ORGANIZATION_MANAGE, Privileges::ACTIVITY_INDEX, Loading @@ -136,13 +142,26 @@ class OrganizationController extends AbstractOscarController implements UseOrgan 'active' => $active, ]; $organizations = []; if( $justXHR === false && $this->getRequest()->getQuery('action') == 'export-csv' ){ $query = $this->getOrganizationService()->getSearchQuery($search, $filter); $organizations = $query->getQuery()->getResult(); $this->getOrganizationService()->exportCsv($organizations); die(); } try { $organizations = $this->getOrganizationService()->getOrganizationsSearchPaged($search, $page, $filter); } catch (BadRequest400Exception $e) { $error = _("Expression de recherche incorrecte") . ' : ' . $e->getMessage(); } catch (NoNodesAvailableException $e) { $error = "Le moteur de recherche est introuvable"; } if ($this->getRequest()->isXmlHttpRequest() || $this->params()->fromQuery('f') === 'json') { // test : return $this->getResponseUnauthorized("nop"); $result = ['datas' => []]; Loading @@ -169,101 +188,6 @@ class OrganizationController extends AbstractOscarController implements UseOrgan ); } /** * Liste des organisations. * * @return array */ public function exportCsvAction() { $type = $this->params()->fromQuery('t', []); $filter = [ 'roles' => $this->params()->fromQuery('roles', []), 'type' => $type ]; $organizations = $this->getOrganizationService()->getOrganizations(); // Fichier temporaire $filename = uniqid('oscar_export_organization_') . '.csv'; $handler = fopen('/tmp/' . $filename, 'w'); $headers = [ 'ID', 'NomCourt', 'NomLong', 'Code', 'Email', 'URL', 'rue1', 'rue2', 'rue3', 'CP', 'BP', 'ville', 'Pays', 'CodePays', 'Téléphone', 'SIFAC', 'SIRET', 'Type', 'TVA' ]; fputcsv($handler, $headers); $i = 0; /** @var Organization $organization */ foreach ($organizations as $organization) { $datas = [ 'ID' => $organization->getId(), 'NomCourt' => $organization->getShortName(), 'NomLong' => $organization->getFullName(), 'Code' => $organization->getCode(), 'Email' => $organization->getEmail(), 'URL' => $organization->getUrl(), 'rue1' => $organization->getStreet1(), 'rue2' => $organization->getStreet2(), 'rue3' => $organization->getStreet3(), 'CP' => $organization->getZipCode(), 'BP' => $organization->getBp(), 'ville' => $organization->getCity(), 'Pays' => $organization->getCountry(), 'CodePays' => $organization->getCodePays(), 'Téléphone' => $organization->getPhone(), 'SIFAC' => $organization->getSifacId(), 'SIRET' => $organization->getSiret(), 'Type' => $organization->getType(), 'TVA' => $organization->getNumTVACA(), ]; $activities = []; /** @var ActivityOrganization $activity */ foreach ($organization->getActivities() as $activity) { $activities[] = $activity->getActivity()->getOscarNum(); } /** @var ProjectPartner $project */ foreach ($organization->getProjects() as $project) { /** @var Activity $activity */ foreach ($project->getProject()->getActivities() as $activity) { $activities[] = $activity->getOscarNum(); } } $datas['activities'] = implode(', ', $activities); fputcsv($handler, $datas); } fclose($handler); header('Content-Disposition: attachment; filename=oscar-export-organisations.csv'); header('Content-Length: ' . filesize('/tmp/' . $filename)); header('Content-type: plain/text'); die(file_get_contents('/tmp/' . $filename)); } /** * Retourne la liste des organisations recherchées. */ Loading @@ -284,6 +208,7 @@ class OrganizationController extends AbstractOscarController implements UseOrgan if (strlen($search) < 3) { return $this->getResponseBadRequest("Not enough chars (3 required)"); } $organizations = $this->getOrganizationService()->getOrganizationsSearchPaged($search, $page); $result = ['datas' => []]; Loading Loading @@ -450,7 +375,9 @@ class OrganizationController extends AbstractOscarController implements UseOrgan ); } $output['organizations'] = $subStructures; $output['manage'] = $this->getOscarUserContextService()->hasPrivileges(Privileges::ORGANIZATION_EDIT); $output['manage'] = $this->getOscarUserContextService()->hasPrivileges( Privileges::ORGANIZATION_EDIT ); return $this->jsonOutput($output); break; Loading Loading @@ -589,7 +516,8 @@ class OrganizationController extends AbstractOscarController implements UseOrgan } catch (\Exception $e) { throw $e; } } else { } else { die('Bad connector ' . $connector); } } Loading Loading @@ -634,7 +562,8 @@ class OrganizationController extends AbstractOscarController implements UseOrgan if (!isset($_SESSION['fusion_data'])) { $this->flashMessenger()->addErrorMessage("Erreur de transmission des données."); $this->redirect()->toRoute('organization/fusion'); } else { } else { $organizationsFrom = $this->getEntityManager()->createQueryBuilder() ->select('o') ->from(Organization::class, 'o') Loading Loading @@ -664,12 +593,14 @@ class OrganizationController extends AbstractOscarController implements UseOrgan $this->flashMessenger()->addSuccessMessage("Fusion des organisations réussie."); $this->redirect()->toRoute('organization/show', ['id' => $to->getId()]); } } else { } else { $this->flashMessenger()->addErrorMessage("La procédure de fusion a été interrompue."); $this->redirect()->toRoute('organization/fusion'); return; } } else { } else { $etape = 2; $hash = uniqid('fusion_'); $_SESSION['fusion_hash'] = $hash; Loading Loading @@ -766,7 +697,8 @@ class OrganizationController extends AbstractOscarController implements UseOrgan if (!isset($_SESSION['fusion_data'])) { $this->flashMessenger()->addErrorMessage("Erreur de transmission des données."); $this->redirect()->toRoute('organization/fusion'); } else { } else { $organizationsFrom = $this->getEntityManager()->createQueryBuilder() ->select('o') ->from(Organization::class, 'o') Loading Loading @@ -805,12 +737,14 @@ class OrganizationController extends AbstractOscarController implements UseOrgan $this->flashMessenger()->addSuccessMessage("Fusion des organisations réussie."); $this->redirect()->toRoute('organization/show', ['id' => $to->getId()]); } } else { } else { $this->flashMessenger()->addErrorMessage("La procédure de fusion a été interrompue."); $this->redirect()->toRoute('organization/fusion'); return; } } else { } else { $etape = 2; $hash = uniqid('fusion_'); $_SESSION['fusion_hash'] = $hash; Loading module/Oscar/src/Oscar/Service/OrganizationService.php +93 −4 Original line number Diff line number Diff line Loading @@ -509,6 +509,94 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana return $idsOrganization; } public function exportCsv($organizations) :void { // Fichier temporaire $tmpDir = "/tmp"; $filename = uniqid('oscar_export_organization_') . '.csv'; $pathFileTmp = $tmpDir . '/' . $filename; $handler = fopen($pathFileTmp, 'w'); if( !$handler ){ $this->getLoggerService()->error("Export organisation : L'emplacement $filename n'est pas accessible en écriture."); throw new OscarException("Impossible d'exporter les données - Accès en écriture impossible"); } $headers = [ 'ID', 'NomCourt', 'NomLong', 'Code', 'Email', 'URL', 'rue1', 'rue2', 'rue3', 'CP', 'BP', 'ville', 'Pays', 'CodePays', 'Téléphone', 'SIFAC', 'SIRET', 'Type', 'TVA' ]; fputcsv($handler, $headers); $i = 0; /** @var Organization $organization */ foreach ($organizations as $organization) { $datas = [ 'ID' => $organization->getId(), 'NomCourt' => $organization->getShortName(), 'NomLong' => $organization->getFullName(), 'Code' => $organization->getCode(), 'Email' => $organization->getEmail(), 'URL' => $organization->getUrl(), 'rue1' => $organization->getStreet1(), 'rue2' => $organization->getStreet2(), 'rue3' => $organization->getStreet3(), 'CP' => $organization->getZipCode(), 'BP' => $organization->getBp(), 'ville' => $organization->getCity(), 'Pays' => $organization->getCountry(), 'CodePays' => $organization->getCodePays(), 'Téléphone' => $organization->getPhone(), 'SIFAC' => $organization->getSifacId(), 'SIRET' => $organization->getSiret(), 'Type' => $organization->getType(), 'TVA' => $organization->getNumTVACA(), ]; // $activities = []; // /** @var ActivityOrganization $activity */ // foreach ($organization->getActivities() as $activity) { // $activities[] = $activity->getActivity()->getOscarNum(); // } // /** @var ProjectPartner $project */ // foreach ($organization->getProjects() as $project) { // /** @var Activity $activity */ // foreach ($project->getProject()->getActivities() as $activity) { // $activities[] = $activity->getOscarNum(); // } // } // $datas['activities'] = implode(', ', $activities); fputcsv($handler, $datas); } fclose($handler); header('Content-Disposition: attachment; filename=oscar-export-organisations.csv'); header('Content-Length: ' . filesize($pathFileTmp)); header('Content-type: plain/text'); die(file_get_contents($pathFileTmp)); } public function getOrganizationTypes() { Loading Loading @@ -587,7 +675,9 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana if ($searchStrategy === null) { $opt = $this->getOscarConfigurationService()->getConfiguration('strategy.organization.search_engine'); $class = new \ReflectionClass($opt['class']); $searchStrategy = $class->newInstanceArgs($opt['params']); $params = $opt['params']; $params[] = $this->getLoggerService(); $searchStrategy = $class->newInstanceArgs($params); } return $searchStrategy; } Loading @@ -602,11 +692,10 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana * * @param string $search * * @return Organization[] * @return UnicaenDoctrinePaginator */ public function getOrganizationsSearchPaged($search, $page, $filter = []) public function getOrganizationsSearchPaged(string $search, int $page, array $filter = []) :UnicaenDoctrinePaginator { // $qb = $this->getSearchNativeQuery($search, $filter); $qb = $this->getSearchQuery($search, $filter); return new UnicaenDoctrinePaginator($qb, $page); Loading module/Oscar/src/Oscar/Service/PersonService.php +3 −1 Original line number Diff line number Diff line Loading @@ -81,7 +81,9 @@ class PersonService implements UseOscarConfigurationService, UseEntityManager, U if ($searchStrategy === null) { $opt = $this->getOscarConfigurationService()->getConfiguration('strategy.person.search_engine'); $class = new \ReflectionClass($opt['class']); $searchStrategy = $class->newInstanceArgs($opt['params']); $params = $opt['params']; $params[] = $this->getLoggerService(); $searchStrategy = $class->newInstanceArgs($params); } return $searchStrategy; } Loading module/Oscar/src/Oscar/Service/ProjectGrantService.php +3 −1 Original line number Diff line number Diff line Loading @@ -1936,8 +1936,10 @@ class ProjectGrantService implements UseGearmanJobLauncherService, UseOscarConfi static $searchStrategy; if ($searchStrategy === null) { $opt = $this->getOscarConfigurationService()->getConfiguration('strategy.activity.search_engine'); $params = $opt['params']; $params[] = $this->getLoggerService(); $class = new \ReflectionClass($opt['class']); $searchStrategy = $class->newInstanceArgs($opt['params']); $searchStrategy = $class->newInstanceArgs($params); } return $searchStrategy; } Loading module/Oscar/src/Oscar/Strategy/Search/ElasticSearchEngine.php +119 −73 Original line number Diff line number Diff line Loading @@ -5,46 +5,66 @@ namespace Oscar\Strategy\Search; use Elasticsearch\Client; use Elasticsearch\ClientBuilder; use Elasticsearch\Common\Exceptions\Missing404Exception; use Monolog\Logger; use Oscar\Exception\OscarException; use Oscar\Service\LoggerService; abstract class ElasticSearchEngine { private ?Client $elasticSearchClient = null; private array $hosts; private Logger $loggerService; /** * @param array $hosts * @param Logger $logger */ public function __construct(array $hosts) public function __construct(array $hosts, Logger $logger) { $this->hosts = $hosts; $this->loggerService = $logger; } // abstract public function getIndexableDatas(mixed $object): array; abstract public function getFieldsSearchedWeighted(): array; abstract public function getIndex(): string; abstract public function getType(): string; /** * @return Client * @throws OscarException */ protected function getClient(): Client { try { if ($this->elasticSearchClient === null) { $this->elasticSearchClient = ClientBuilder::create() ->setHosts($this->getHosts()) ->build(); } return $this->elasticSearchClient; } catch (\Exception $exception) { $msg = "Création du client impossible"; $this->loggerService->critical("$msg : " . $exception->getMessage()); throw new OscarException($msg); } } /** * @param mixed $object * @return callable|array * @throws OscarException */ public function addItem(mixed $object): callable|array { $client = $this->getClient(); try { $params = ['body' => []]; $params['body'][] = [ Loading @@ -57,17 +77,25 @@ abstract class ElasticSearchEngine $params['body'][] = $this->getIndexableDatas($object); return $this->getClient()->bulk($params); return $client->bulk($params); } catch (\Exception $exception) { $msg = "Impossible d'indexer l'information"; $this->loggerService->critical("$msg : " . $exception->getMessage()); throw new OscarException($msg); } } /** * @param array $items * @return void * @throws OscarException */ public function rebuildIndex(array $items): void { $this->resetIndex(); $client = $this->getClient(); try { $i = 0; foreach ($items as $item) { $i++; Loading @@ -92,7 +120,12 @@ abstract class ElasticSearchEngine } if (!empty($params['body'])) { $this->getClient()->bulk($params); $client->bulk($params); } } catch (\Exception $exception) { $msg = "Réindexation impossible"; $this->loggerService->critical("$msg : " . $exception->getMessage()); throw new OscarException($msg); } } Loading @@ -100,6 +133,7 @@ abstract class ElasticSearchEngine * Retourne les paramètres utilisés pour la recherche. * * @param string $textSearch * @param int $limit * @return array */ public function getParamsQuery(string $textSearch, int $limit = 10000): array Loading @@ -120,16 +154,24 @@ abstract class ElasticSearchEngine ]; } /** * @throws OscarException */ public function searchRaw(string $search, int $limit = 10000): array { $this->loggerService->info("Search '$search' in " . $this->getIndex()); $client = $this->getClient(); try { $search = trim($search); // TRAITEMENT de la recherche if (strpos($search, 'AND') || strpos($search, 'OR') || strpos($search, '"')) { } else { } else { $split = explode(" ", $search); if (count($split) == 1) { $search = sprintf('%s OR %s', $split[0], $split[0] . '*'); } else { } else { $assembly = []; foreach ($split as $term) { $assembly[] = trim($term); Loading @@ -140,12 +182,19 @@ abstract class ElasticSearchEngine $params = $this->getParamsQuery($search, $limit); return $this->getClient()->search($params); return $client->search($params); } catch (\Exception $exception) { $msg = "Erreur de recherche"; $this->loggerService->critical("$msg : " . $exception->getMessage()); throw new OscarException($msg); } } /** * @param string $search * @return array Liste des IDs * @throws OscarException */ public function search(string $search): array { Loading Loading @@ -217,17 +266,14 @@ abstract class ElasticSearchEngine 'index' => $this->getIndex() ]; $client = $this->getClient(); try { return $this->getClient()->indices()->delete($params); } catch (Missing404Exception $e) { return []; return $client->indices()->delete($params); } catch (\Exception $e) { throw new OscarException( sprintf( "Impossible de réinitialiser l'index de recherche : '%s'", $e->getMessage() ) ); $msg = "Impossible de réinitialiser le moteur de recherche"; $this->loggerService->critical("$msg : " . $e->getMessage()); throw new OscarException($msg); } } Loading Loading
module/Oscar/src/Oscar/Controller/OrganizationController.php +74 −140 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ use BjyAuthorize\Exception\UnAuthorizedException; use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; use Doctrine\ORM\Query; use Elasticsearch\Common\Exceptions\BadRequest400Exception; use Elasticsearch\Common\Exceptions\NoNodesAvailableException; use Oscar\Connector\IConnector; use Oscar\Entity\Activity; use Oscar\Entity\ActivityOrganization; Loading Loading @@ -56,6 +57,7 @@ class OrganizationController extends AbstractOscarController implements UseOrgan /** * @param SessionService $sessionService * @return OrganizationController */ public function setSessionService(SessionService $sessionService): self { Loading Loading @@ -86,7 +88,8 @@ class OrganizationController extends AbstractOscarController implements UseOrgan ); } } } else { } else { $token = $this->getSessionService()->createToken(); } Loading @@ -100,6 +103,7 @@ class OrganizationController extends AbstractOscarController implements UseOrgan * Liste des organisations. * * @return array * @throws \Exception */ public function indexAction() { Loading @@ -111,8 +115,10 @@ class OrganizationController extends AbstractOscarController implements UseOrgan if ($this->getOscarUserContextService()->hasPrivileges(Privileges::ORGANIZATION_SHOW)) { $allow = true; $justXHR = false; } else { $allow = $this->getOscarUserContextService()->hasOneOfPrivilegesInAnyRoles([ } else { $allow = $this->getOscarUserContextService() ->hasOneOfPrivilegesInAnyRoles([ Privileges::ACTIVITY_ORGANIZATION_MANAGE, Privileges::PROJECT_ORGANIZATION_MANAGE, Privileges::ACTIVITY_INDEX, Loading @@ -136,13 +142,26 @@ class OrganizationController extends AbstractOscarController implements UseOrgan 'active' => $active, ]; $organizations = []; if( $justXHR === false && $this->getRequest()->getQuery('action') == 'export-csv' ){ $query = $this->getOrganizationService()->getSearchQuery($search, $filter); $organizations = $query->getQuery()->getResult(); $this->getOrganizationService()->exportCsv($organizations); die(); } try { $organizations = $this->getOrganizationService()->getOrganizationsSearchPaged($search, $page, $filter); } catch (BadRequest400Exception $e) { $error = _("Expression de recherche incorrecte") . ' : ' . $e->getMessage(); } catch (NoNodesAvailableException $e) { $error = "Le moteur de recherche est introuvable"; } if ($this->getRequest()->isXmlHttpRequest() || $this->params()->fromQuery('f') === 'json') { // test : return $this->getResponseUnauthorized("nop"); $result = ['datas' => []]; Loading @@ -169,101 +188,6 @@ class OrganizationController extends AbstractOscarController implements UseOrgan ); } /** * Liste des organisations. * * @return array */ public function exportCsvAction() { $type = $this->params()->fromQuery('t', []); $filter = [ 'roles' => $this->params()->fromQuery('roles', []), 'type' => $type ]; $organizations = $this->getOrganizationService()->getOrganizations(); // Fichier temporaire $filename = uniqid('oscar_export_organization_') . '.csv'; $handler = fopen('/tmp/' . $filename, 'w'); $headers = [ 'ID', 'NomCourt', 'NomLong', 'Code', 'Email', 'URL', 'rue1', 'rue2', 'rue3', 'CP', 'BP', 'ville', 'Pays', 'CodePays', 'Téléphone', 'SIFAC', 'SIRET', 'Type', 'TVA' ]; fputcsv($handler, $headers); $i = 0; /** @var Organization $organization */ foreach ($organizations as $organization) { $datas = [ 'ID' => $organization->getId(), 'NomCourt' => $organization->getShortName(), 'NomLong' => $organization->getFullName(), 'Code' => $organization->getCode(), 'Email' => $organization->getEmail(), 'URL' => $organization->getUrl(), 'rue1' => $organization->getStreet1(), 'rue2' => $organization->getStreet2(), 'rue3' => $organization->getStreet3(), 'CP' => $organization->getZipCode(), 'BP' => $organization->getBp(), 'ville' => $organization->getCity(), 'Pays' => $organization->getCountry(), 'CodePays' => $organization->getCodePays(), 'Téléphone' => $organization->getPhone(), 'SIFAC' => $organization->getSifacId(), 'SIRET' => $organization->getSiret(), 'Type' => $organization->getType(), 'TVA' => $organization->getNumTVACA(), ]; $activities = []; /** @var ActivityOrganization $activity */ foreach ($organization->getActivities() as $activity) { $activities[] = $activity->getActivity()->getOscarNum(); } /** @var ProjectPartner $project */ foreach ($organization->getProjects() as $project) { /** @var Activity $activity */ foreach ($project->getProject()->getActivities() as $activity) { $activities[] = $activity->getOscarNum(); } } $datas['activities'] = implode(', ', $activities); fputcsv($handler, $datas); } fclose($handler); header('Content-Disposition: attachment; filename=oscar-export-organisations.csv'); header('Content-Length: ' . filesize('/tmp/' . $filename)); header('Content-type: plain/text'); die(file_get_contents('/tmp/' . $filename)); } /** * Retourne la liste des organisations recherchées. */ Loading @@ -284,6 +208,7 @@ class OrganizationController extends AbstractOscarController implements UseOrgan if (strlen($search) < 3) { return $this->getResponseBadRequest("Not enough chars (3 required)"); } $organizations = $this->getOrganizationService()->getOrganizationsSearchPaged($search, $page); $result = ['datas' => []]; Loading Loading @@ -450,7 +375,9 @@ class OrganizationController extends AbstractOscarController implements UseOrgan ); } $output['organizations'] = $subStructures; $output['manage'] = $this->getOscarUserContextService()->hasPrivileges(Privileges::ORGANIZATION_EDIT); $output['manage'] = $this->getOscarUserContextService()->hasPrivileges( Privileges::ORGANIZATION_EDIT ); return $this->jsonOutput($output); break; Loading Loading @@ -589,7 +516,8 @@ class OrganizationController extends AbstractOscarController implements UseOrgan } catch (\Exception $e) { throw $e; } } else { } else { die('Bad connector ' . $connector); } } Loading Loading @@ -634,7 +562,8 @@ class OrganizationController extends AbstractOscarController implements UseOrgan if (!isset($_SESSION['fusion_data'])) { $this->flashMessenger()->addErrorMessage("Erreur de transmission des données."); $this->redirect()->toRoute('organization/fusion'); } else { } else { $organizationsFrom = $this->getEntityManager()->createQueryBuilder() ->select('o') ->from(Organization::class, 'o') Loading Loading @@ -664,12 +593,14 @@ class OrganizationController extends AbstractOscarController implements UseOrgan $this->flashMessenger()->addSuccessMessage("Fusion des organisations réussie."); $this->redirect()->toRoute('organization/show', ['id' => $to->getId()]); } } else { } else { $this->flashMessenger()->addErrorMessage("La procédure de fusion a été interrompue."); $this->redirect()->toRoute('organization/fusion'); return; } } else { } else { $etape = 2; $hash = uniqid('fusion_'); $_SESSION['fusion_hash'] = $hash; Loading Loading @@ -766,7 +697,8 @@ class OrganizationController extends AbstractOscarController implements UseOrgan if (!isset($_SESSION['fusion_data'])) { $this->flashMessenger()->addErrorMessage("Erreur de transmission des données."); $this->redirect()->toRoute('organization/fusion'); } else { } else { $organizationsFrom = $this->getEntityManager()->createQueryBuilder() ->select('o') ->from(Organization::class, 'o') Loading Loading @@ -805,12 +737,14 @@ class OrganizationController extends AbstractOscarController implements UseOrgan $this->flashMessenger()->addSuccessMessage("Fusion des organisations réussie."); $this->redirect()->toRoute('organization/show', ['id' => $to->getId()]); } } else { } else { $this->flashMessenger()->addErrorMessage("La procédure de fusion a été interrompue."); $this->redirect()->toRoute('organization/fusion'); return; } } else { } else { $etape = 2; $hash = uniqid('fusion_'); $_SESSION['fusion_hash'] = $hash; Loading
module/Oscar/src/Oscar/Service/OrganizationService.php +93 −4 Original line number Diff line number Diff line Loading @@ -509,6 +509,94 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana return $idsOrganization; } public function exportCsv($organizations) :void { // Fichier temporaire $tmpDir = "/tmp"; $filename = uniqid('oscar_export_organization_') . '.csv'; $pathFileTmp = $tmpDir . '/' . $filename; $handler = fopen($pathFileTmp, 'w'); if( !$handler ){ $this->getLoggerService()->error("Export organisation : L'emplacement $filename n'est pas accessible en écriture."); throw new OscarException("Impossible d'exporter les données - Accès en écriture impossible"); } $headers = [ 'ID', 'NomCourt', 'NomLong', 'Code', 'Email', 'URL', 'rue1', 'rue2', 'rue3', 'CP', 'BP', 'ville', 'Pays', 'CodePays', 'Téléphone', 'SIFAC', 'SIRET', 'Type', 'TVA' ]; fputcsv($handler, $headers); $i = 0; /** @var Organization $organization */ foreach ($organizations as $organization) { $datas = [ 'ID' => $organization->getId(), 'NomCourt' => $organization->getShortName(), 'NomLong' => $organization->getFullName(), 'Code' => $organization->getCode(), 'Email' => $organization->getEmail(), 'URL' => $organization->getUrl(), 'rue1' => $organization->getStreet1(), 'rue2' => $organization->getStreet2(), 'rue3' => $organization->getStreet3(), 'CP' => $organization->getZipCode(), 'BP' => $organization->getBp(), 'ville' => $organization->getCity(), 'Pays' => $organization->getCountry(), 'CodePays' => $organization->getCodePays(), 'Téléphone' => $organization->getPhone(), 'SIFAC' => $organization->getSifacId(), 'SIRET' => $organization->getSiret(), 'Type' => $organization->getType(), 'TVA' => $organization->getNumTVACA(), ]; // $activities = []; // /** @var ActivityOrganization $activity */ // foreach ($organization->getActivities() as $activity) { // $activities[] = $activity->getActivity()->getOscarNum(); // } // /** @var ProjectPartner $project */ // foreach ($organization->getProjects() as $project) { // /** @var Activity $activity */ // foreach ($project->getProject()->getActivities() as $activity) { // $activities[] = $activity->getOscarNum(); // } // } // $datas['activities'] = implode(', ', $activities); fputcsv($handler, $datas); } fclose($handler); header('Content-Disposition: attachment; filename=oscar-export-organisations.csv'); header('Content-Length: ' . filesize($pathFileTmp)); header('Content-type: plain/text'); die(file_get_contents($pathFileTmp)); } public function getOrganizationTypes() { Loading Loading @@ -587,7 +675,9 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana if ($searchStrategy === null) { $opt = $this->getOscarConfigurationService()->getConfiguration('strategy.organization.search_engine'); $class = new \ReflectionClass($opt['class']); $searchStrategy = $class->newInstanceArgs($opt['params']); $params = $opt['params']; $params[] = $this->getLoggerService(); $searchStrategy = $class->newInstanceArgs($params); } return $searchStrategy; } Loading @@ -602,11 +692,10 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana * * @param string $search * * @return Organization[] * @return UnicaenDoctrinePaginator */ public function getOrganizationsSearchPaged($search, $page, $filter = []) public function getOrganizationsSearchPaged(string $search, int $page, array $filter = []) :UnicaenDoctrinePaginator { // $qb = $this->getSearchNativeQuery($search, $filter); $qb = $this->getSearchQuery($search, $filter); return new UnicaenDoctrinePaginator($qb, $page); Loading
module/Oscar/src/Oscar/Service/PersonService.php +3 −1 Original line number Diff line number Diff line Loading @@ -81,7 +81,9 @@ class PersonService implements UseOscarConfigurationService, UseEntityManager, U if ($searchStrategy === null) { $opt = $this->getOscarConfigurationService()->getConfiguration('strategy.person.search_engine'); $class = new \ReflectionClass($opt['class']); $searchStrategy = $class->newInstanceArgs($opt['params']); $params = $opt['params']; $params[] = $this->getLoggerService(); $searchStrategy = $class->newInstanceArgs($params); } return $searchStrategy; } Loading
module/Oscar/src/Oscar/Service/ProjectGrantService.php +3 −1 Original line number Diff line number Diff line Loading @@ -1936,8 +1936,10 @@ class ProjectGrantService implements UseGearmanJobLauncherService, UseOscarConfi static $searchStrategy; if ($searchStrategy === null) { $opt = $this->getOscarConfigurationService()->getConfiguration('strategy.activity.search_engine'); $params = $opt['params']; $params[] = $this->getLoggerService(); $class = new \ReflectionClass($opt['class']); $searchStrategy = $class->newInstanceArgs($opt['params']); $searchStrategy = $class->newInstanceArgs($params); } return $searchStrategy; } Loading
module/Oscar/src/Oscar/Strategy/Search/ElasticSearchEngine.php +119 −73 Original line number Diff line number Diff line Loading @@ -5,46 +5,66 @@ namespace Oscar\Strategy\Search; use Elasticsearch\Client; use Elasticsearch\ClientBuilder; use Elasticsearch\Common\Exceptions\Missing404Exception; use Monolog\Logger; use Oscar\Exception\OscarException; use Oscar\Service\LoggerService; abstract class ElasticSearchEngine { private ?Client $elasticSearchClient = null; private array $hosts; private Logger $loggerService; /** * @param array $hosts * @param Logger $logger */ public function __construct(array $hosts) public function __construct(array $hosts, Logger $logger) { $this->hosts = $hosts; $this->loggerService = $logger; } // abstract public function getIndexableDatas(mixed $object): array; abstract public function getFieldsSearchedWeighted(): array; abstract public function getIndex(): string; abstract public function getType(): string; /** * @return Client * @throws OscarException */ protected function getClient(): Client { try { if ($this->elasticSearchClient === null) { $this->elasticSearchClient = ClientBuilder::create() ->setHosts($this->getHosts()) ->build(); } return $this->elasticSearchClient; } catch (\Exception $exception) { $msg = "Création du client impossible"; $this->loggerService->critical("$msg : " . $exception->getMessage()); throw new OscarException($msg); } } /** * @param mixed $object * @return callable|array * @throws OscarException */ public function addItem(mixed $object): callable|array { $client = $this->getClient(); try { $params = ['body' => []]; $params['body'][] = [ Loading @@ -57,17 +77,25 @@ abstract class ElasticSearchEngine $params['body'][] = $this->getIndexableDatas($object); return $this->getClient()->bulk($params); return $client->bulk($params); } catch (\Exception $exception) { $msg = "Impossible d'indexer l'information"; $this->loggerService->critical("$msg : " . $exception->getMessage()); throw new OscarException($msg); } } /** * @param array $items * @return void * @throws OscarException */ public function rebuildIndex(array $items): void { $this->resetIndex(); $client = $this->getClient(); try { $i = 0; foreach ($items as $item) { $i++; Loading @@ -92,7 +120,12 @@ abstract class ElasticSearchEngine } if (!empty($params['body'])) { $this->getClient()->bulk($params); $client->bulk($params); } } catch (\Exception $exception) { $msg = "Réindexation impossible"; $this->loggerService->critical("$msg : " . $exception->getMessage()); throw new OscarException($msg); } } Loading @@ -100,6 +133,7 @@ abstract class ElasticSearchEngine * Retourne les paramètres utilisés pour la recherche. * * @param string $textSearch * @param int $limit * @return array */ public function getParamsQuery(string $textSearch, int $limit = 10000): array Loading @@ -120,16 +154,24 @@ abstract class ElasticSearchEngine ]; } /** * @throws OscarException */ public function searchRaw(string $search, int $limit = 10000): array { $this->loggerService->info("Search '$search' in " . $this->getIndex()); $client = $this->getClient(); try { $search = trim($search); // TRAITEMENT de la recherche if (strpos($search, 'AND') || strpos($search, 'OR') || strpos($search, '"')) { } else { } else { $split = explode(" ", $search); if (count($split) == 1) { $search = sprintf('%s OR %s', $split[0], $split[0] . '*'); } else { } else { $assembly = []; foreach ($split as $term) { $assembly[] = trim($term); Loading @@ -140,12 +182,19 @@ abstract class ElasticSearchEngine $params = $this->getParamsQuery($search, $limit); return $this->getClient()->search($params); return $client->search($params); } catch (\Exception $exception) { $msg = "Erreur de recherche"; $this->loggerService->critical("$msg : " . $exception->getMessage()); throw new OscarException($msg); } } /** * @param string $search * @return array Liste des IDs * @throws OscarException */ public function search(string $search): array { Loading Loading @@ -217,17 +266,14 @@ abstract class ElasticSearchEngine 'index' => $this->getIndex() ]; $client = $this->getClient(); try { return $this->getClient()->indices()->delete($params); } catch (Missing404Exception $e) { return []; return $client->indices()->delete($params); } catch (\Exception $e) { throw new OscarException( sprintf( "Impossible de réinitialiser l'index de recherche : '%s'", $e->getMessage() ) ); $msg = "Impossible de réinitialiser le moteur de recherche"; $this->loggerService->critical("$msg : " . $e->getMessage()); throw new OscarException($msg); } } Loading