mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 09:18:24 +00:00 
			
		
		
		
	script import utilisateurs
This commit is contained in:
		
							
								
								
									
										403
									
								
								Command/ChillImportUsersCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										403
									
								
								Command/ChillImportUsersCommand.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,403 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Command; | ||||
|  | ||||
| use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||||
| use Symfony\Component\Console\Input\InputArgument; | ||||
| use Symfony\Component\Console\Input\InputInterface; | ||||
| use Symfony\Component\Console\Input\InputOption; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
| use League\Csv\Reader; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Chill\MainBundle\Entity\Center; | ||||
| use Symfony\Component\Console\Question\ConfirmationQuestion; | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Symfony\Component\Validator\Validator\ValidatorInterface; | ||||
| use Symfony\Component\Validator\ConstraintViolationListInterface; | ||||
| use Psr\Log\LoggerInterface; | ||||
| use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; | ||||
| use Chill\MainBundle\Entity\GroupCenter; | ||||
| use Chill\MainBundle\Entity\PermissionsGroup; | ||||
| use Symfony\Component\Console\Question\ChoiceQuestion; | ||||
|  | ||||
| class ChillImportUsersCommand extends ContainerAwareCommand | ||||
| { | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var EntityManagerInterface | ||||
|      */ | ||||
|     protected $em; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var ValidatorInterface | ||||
|      */ | ||||
|     protected $validator; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var LoggerInterface | ||||
|      */ | ||||
|     protected $logger; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var UserPasswordEncoderInterface | ||||
|      */ | ||||
|     protected $passwordEncoder; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var \Chill\MainBundle\Repository\UserRepository | ||||
|      */ | ||||
|     protected $userRepository; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $doChanges = true; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var OutputInterface | ||||
|      */ | ||||
|     protected $tempOutput; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var InputInterface | ||||
|      */ | ||||
|     protected $tempInput; | ||||
|      | ||||
|     /** | ||||
|      * Centers and aliases. | ||||
|      *  | ||||
|      * key are aliases, values are an array of centers | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $centers = []; | ||||
|      | ||||
|     /** | ||||
|      *  | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $permissionGroups = []; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $groupCenters = []; | ||||
|      | ||||
|     public function __construct( | ||||
|         EntityManagerInterface $em, | ||||
|         LoggerInterface $logger, | ||||
|         UserPasswordEncoderInterface $passwordEncoder, | ||||
|         ValidatorInterface $validator | ||||
|     ) { | ||||
|         $this->em = $em; | ||||
|         $this->passwordEncoder = $passwordEncoder; | ||||
|         $this->validator = $validator; | ||||
|         $this->logger = $logger; | ||||
|          | ||||
|          | ||||
|         $this->userRepository = $em->getRepository(User::class); | ||||
|          | ||||
|         parent::__construct('chill:main:import-users'); | ||||
|     } | ||||
|  | ||||
|      | ||||
|      | ||||
|     protected function configure() | ||||
|     { | ||||
|         $this | ||||
|             ->setDescription('Import users from csv file') | ||||
|             ->setHelp("Import users from a csv file. Users are added to centers contained in the file. Headers are used to detect columns. Adding to multiple centers can be done by using a `grouping centers` file, which will group multiple centers into a signle alias, used in 'centers' column.") | ||||
|             ->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`') | ||||
|             ->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias') | ||||
|             ->addOption('dry-run', null, InputOption::VALUE_OPTIONAL, 'Do not commit the changes') | ||||
|         ; | ||||
|     } | ||||
|  | ||||
|     protected function execute(InputInterface $input, OutputInterface $output) | ||||
|     { | ||||
|         $this->tempOutput = $output; | ||||
|         $this->tempInput = $input; | ||||
|          | ||||
|         if ($input->hasOption('dry-run')) { | ||||
|             $this->doChanges = false; | ||||
|         } | ||||
|          | ||||
|         if ($input->hasOption('grouping-centers')) { | ||||
|             $this->prepareGroupingCenters(); | ||||
|         } | ||||
|          | ||||
|         $this->loadUsers(); | ||||
|     } | ||||
|      | ||||
|     protected function loadUsers() | ||||
|     { | ||||
|         $reader = Reader::createFromPath($this->tempInput->getArgument('csvfile')); | ||||
|         $reader->setHeaderOffset(0); | ||||
|          | ||||
|         foreach ($reader->getRecords() as $line => $r) { | ||||
|             $this->logger->debug("starting handling new line", [ | ||||
|                 'line' => $line | ||||
|             ]); | ||||
|              | ||||
|             if ($this->doesUserExists($r)) { | ||||
|                 $this->tempOutput->writeln(sprintf("User with username '%s' already " | ||||
|                     . "exists, skipping")); | ||||
|                  | ||||
|                 $this->logger->info("One user already exists, skipping creation", [ | ||||
|                     'username_in_file' => $r['username'], | ||||
|                     'email_in_file' => $r['email'], | ||||
|                     'line' => $line | ||||
|                 ]); | ||||
|                  | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             $this->createUser($line, $r); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     protected function doesUserExists($data) | ||||
|     { | ||||
|         if ($this->userRepository->countByUsernameOrEmail($data['username']) > 0) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if ($this->userRepository->countByUsernameOrEmail($data['email']) > 0) { | ||||
|             return true; | ||||
|         } | ||||
|          | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
|     protected function createUser($offset, $data) | ||||
|     { | ||||
|         $user = new User(); | ||||
|         $user | ||||
|             ->setEmail($data['email']) | ||||
|             ->setUsername($data['username']) | ||||
|             ->setEnabled(true) | ||||
|             ->setPassword($this->passwordEncoder->encodePassword($user,  | ||||
|                 \bin2hex(\random_bytes(32)))) | ||||
|             ; | ||||
|          | ||||
|         $errors = $this->validator->validate($user); | ||||
|          | ||||
|         if ($errors->count() > 0) { | ||||
|             $errorMessages = $this->concatenateViolations($errors); | ||||
|              | ||||
|             $this->tempOutput->writeln(sprintf("%d errors found with user with username \"%s\" at line %d", $errors->count(), $data['username'], $offset)); | ||||
|             $this->tempOutput->writeln($errorMessages); | ||||
|  | ||||
|             throw new \RuntimeException("Found errors while creating an user. " | ||||
|                 . "Watch messages in command output"); | ||||
|         } | ||||
|          | ||||
|         $pgs = $this->getPermissionGroup($data['permission group']); | ||||
|         $centers = $this->getCenters($data['center']); | ||||
|          | ||||
|         foreach($pgs as $pg) { | ||||
|             foreach ($centers as $center) { | ||||
|                 $user->addGroupCenter($this->createOrGetGroupCenter($center, $pg)); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  | ||||
|         if ($this->doChanges) { | ||||
|             $this->em->persist($user); | ||||
|             $this->flush(); | ||||
|         } | ||||
|                          | ||||
|         $this->logger->notice("Create user", [ | ||||
|             'username' => $user->getUsername(), | ||||
|             'id' => $user->getId(), | ||||
|             'nb_of_groupCenters' => $user->getGroupCenters()->count() | ||||
|         ]); | ||||
|  | ||||
|     } | ||||
|      | ||||
|     protected function getPermissionGroup($alias) | ||||
|     { | ||||
|         if (\array_key_exists($alias, $this->permissionGroups)) { | ||||
|             return $this->permissionGroups[$alias]; | ||||
|         } | ||||
|          | ||||
|         $permissionGroupsByName = []; | ||||
|          | ||||
|         foreach($this->em->getRepository(PermissionsGroup::class) | ||||
|             ->findAll() as $permissionGroup) { | ||||
|             $permissionGroupsByName[$permissionGroup->getName()] = $permissionGroup; | ||||
|         } | ||||
|          | ||||
|         if (count($permissionGroupsByName) === 0) { | ||||
|             throw new \RuntimeException("no permission groups found. Create them " | ||||
|                 . "before importing users"); | ||||
|         } | ||||
|          | ||||
|         $question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?",  | ||||
|             \array_keys($permissionGroupsByName)); | ||||
|         $question | ||||
|             ->setMultiselect(true) | ||||
|             ->setAutocompleterValues(\array_keys($permissionGroupsByName)) | ||||
|             ->setNormalizer(function($value) { | ||||
|                 if (NULL === $value) { return ''; } | ||||
|                  | ||||
|                 return \trim($value); | ||||
|             }) | ||||
|             ; | ||||
|         $helper = $this->getHelper('question'); | ||||
|          | ||||
|         $keys = $helper->ask($this->tempInput, $this->tempOutput, $question); | ||||
|          | ||||
|         $this->tempOutput->writeln("You have chosen ".\implode(", ", $keys)); | ||||
|          | ||||
|         if ($helper->ask($this->tempInput, $this->tempOutput,  | ||||
|             new ConfirmationQuestion("Are you sure ?", true))) { | ||||
|              | ||||
|             foreach ($keys as $key) { | ||||
|                 $this->permissionGroups[$alias][] = $permissionGroupsByName[$key]; | ||||
|             } | ||||
|              | ||||
|             return $this->permissionGroups[$alias]; | ||||
|         } else { | ||||
|             $this->logger->error("Error while responding to a a question"); | ||||
|              | ||||
|             $this->tempOutput("Ok, I accept, but I do not know what to do. Please try again."); | ||||
|              | ||||
|             throw new \RuntimeException("Error while responding to a question"); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      *  | ||||
|      * @param Center $center | ||||
|      * @param \Chill\MainBundle\Command\PermissionGroup $pg | ||||
|      * @return GroupCenter | ||||
|      */ | ||||
|     protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter | ||||
|     { | ||||
|         if (\array_key_exists($center->getId(), $this->groupCenters)) { | ||||
|             if (\array_key_exists($pg->getId(), $this->groupCenters[$center->getId()])) { | ||||
|                 return $this->groupCenters[$center->getId()][$pg->getId()]; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         $repository = $this->em->getRepository(GroupCenter::class); | ||||
|          | ||||
|         $groupCenter = $repository->findOneBy(array( | ||||
|                 'center' => $center, | ||||
|                 'permissionsGroup' => $pg | ||||
|             )); | ||||
|          | ||||
|         if ($groupCenter === NULL) { | ||||
|             $groupCenter = new GroupCenter(); | ||||
|             $groupCenter | ||||
|                 ->setCenter($center) | ||||
|                 ->setPermissionsGroup($pg) | ||||
|                 ; | ||||
|              | ||||
|             $this->em->persist($groupCenter); | ||||
|         } | ||||
|          | ||||
|         $this->groupCenters[$center->getId()][$pg->getId()] = $groupCenter; | ||||
|          | ||||
|         return $groupCenter; | ||||
|     } | ||||
|      | ||||
|     protected function prepareGroupingCenters() | ||||
|     { | ||||
|         $reader = Reader::createFromPath($this->tempInput->getOption('grouping-centers')); | ||||
|         $reader->setHeaderOffset(0); | ||||
|          | ||||
|         foreach ($reader->getRecords() as $r) { | ||||
|             $this->centers[$r['alias']] =  | ||||
|                 \array_merge( | ||||
|                     $this->centers[$r['alias']] ?? [], | ||||
|                     $this->getCenters($r['center'] | ||||
|                     ) | ||||
|                 ); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * return a list of centers matching the name of alias. | ||||
|      *  | ||||
|      * If the name match one center, this center is returned in an array. | ||||
|      *  | ||||
|      * If the name match an alias, the centers corresponding to the alias are  | ||||
|      * returned in an array. | ||||
|      *  | ||||
|      * If the center is not found or alias is not created, a new center is created | ||||
|      * and suggested to user | ||||
|      *  | ||||
|      * @param string $name the name of the center or the alias regrouping center | ||||
|      * @return Center[] | ||||
|      */ | ||||
|     protected function getCenters($name) | ||||
|     { | ||||
|         if (\array_key_exists($name, $this->centers)) { | ||||
|             return $this->centers[$name]; | ||||
|         } | ||||
|          | ||||
|         // search for a center with given name | ||||
|         $center = $this->em->getRepository(Center::class) | ||||
|             ->findOneByName($name); | ||||
|          | ||||
|         if ($center instanceof Center) { | ||||
|             $this->centers[$name] = [$center]; | ||||
|              | ||||
|             return $this->centers[$name]; | ||||
|         } | ||||
|          | ||||
|         // suggest and create | ||||
|         $center = (new Center()) | ||||
|             ->setName($name); | ||||
|          | ||||
|         $this->tempOutput->writeln("Center with name \"$name\" not found."); | ||||
|         $qFormatter = $this->getHelper('question'); | ||||
|         $question = new ConfirmationQuestion("Create a center with name \"$name\" ?", true); | ||||
|          | ||||
|         if ($qFormatter->ask($this->tempInput, $this->tempOutput, $question)) { | ||||
|             $this->centers[$name] = [ $center ]; | ||||
|              | ||||
|             $errors = $this->validator->validate($center); | ||||
|              | ||||
|             if ($errors->count() > 0) { | ||||
|                 $errorMessages = $this->concatenateViolations($errors); | ||||
|                  | ||||
|                 $this->tempOutput->writeln(sprintf("%d errors found with center with name \"%s\"", $errors->count(), $name)); | ||||
|                 $this->tempOutput->writeln($errorMessages); | ||||
|                  | ||||
|                 throw new \RuntimeException("Found errors while creating one center. " | ||||
|                     . "Watch messages in command output"); | ||||
|             } | ||||
|              | ||||
|             $this->em->persist($center); | ||||
|              | ||||
|             return $this->centers[$name]; | ||||
|         } | ||||
|          | ||||
|         return null; | ||||
|     } | ||||
|      | ||||
|     protected function concatenateViolations(ConstraintViolationListInterface $list) | ||||
|     { | ||||
|         $str = []; | ||||
|          | ||||
|         foreach ($list as $e) { | ||||
|             /* @var $e \Symfony\Component\Validator\ConstraintViolationInterface */ | ||||
|             $str[] = $e->getMessage(); | ||||
|         } | ||||
|          | ||||
|         return \implode(";", $str); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -1,7 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 2014-2016 Julien Fastré <julien.fastre@champs-libres.coop> | ||||
|  * Copyright (C) 2014-2018 Julien Fastré <julien.fastre@champs-libres.coop> | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
| @@ -108,6 +108,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, | ||||
|         $loader->load('services/security.yml'); | ||||
|         $loader->load('services/notification.yml'); | ||||
|         $loader->load('services/redis.yml'); | ||||
|         $loader->load('services/command.yml'); | ||||
|     } | ||||
|      | ||||
|     public function getConfiguration(array $config, ContainerBuilder $container) | ||||
|   | ||||
| @@ -229,6 +229,8 @@ class User implements AdvancedUserInterface { | ||||
|     public function setEnabled($enabled) | ||||
|     { | ||||
|         $this->enabled = $enabled; | ||||
|          | ||||
|         return $this; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										67
									
								
								Repository/UserRepository.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								Repository/UserRepository.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| <?php | ||||
| /* | ||||
|  * Copyright (C) 2018 Julien Fastré <julien.fastre@champs-libres.coop> | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| namespace Chill\MainBundle\Repository; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * | ||||
|  */ | ||||
| class UserRepository extends \Doctrine\ORM\EntityRepository | ||||
| { | ||||
|     public function countByUsernameOrEmail($pattern) | ||||
|     { | ||||
|         $qb = $this->queryByUsernameOrEmail($pattern); | ||||
|          | ||||
|         $qb->select('COUNT(u)'); | ||||
|          | ||||
|         return (int) $qb->getQuery()->getSingleScalarResult(); | ||||
|     } | ||||
|      | ||||
|     public function findByUsernameOrEmail($pattern)  | ||||
|     { | ||||
|         $qb = $this->queryByUsernameOrEmail($pattern); | ||||
|          | ||||
|         return $qb->getQuery()->getResult(); | ||||
|     } | ||||
|      | ||||
|     public function findOneByUsernameOrEmail($pattern) | ||||
|     { | ||||
|         $qb = $this->queryByUsernameOrEmail($pattern); | ||||
|          | ||||
|         return $qb->getQuery()->getSingleResult(); | ||||
|     } | ||||
|      | ||||
|     protected function queryByUsernameOrEmail($pattern) | ||||
|     { | ||||
|         $qb = $this->createQueryBuilder('u'); | ||||
|          | ||||
|         $searchByPattern = $qb->expr()->orX(); | ||||
|          | ||||
|         $searchByPattern | ||||
|             ->add($qb->expr()->eq('u.usernameCanonical', 'LOWER(UNACCENT(:pattern))')) | ||||
|             ->add($qb->expr()->eq('u.emailCanonical', 'LOWER(UNACCENT(:pattern))')) | ||||
|             ; | ||||
|          | ||||
|         $qb | ||||
|             ->where($searchByPattern) | ||||
|             ->setParameter('pattern', $pattern) | ||||
|             ; | ||||
|          | ||||
|         return $qb; | ||||
|     } | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| Chill\MainBundle\Entity\User: | ||||
|     type: entity | ||||
|     table: users | ||||
|     repositoryClass: Chill\MainBundle\Repository\UserRepository | ||||
|     cache: | ||||
|         usage: NONSTRICT_READ_WRITE | ||||
|         region: acl_cache_region | ||||
|   | ||||
							
								
								
									
										10
									
								
								Resources/config/services/command.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Resources/config/services/command.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| services: | ||||
|     Chill\MainBundle\Command\ChillImportUsersCommand: | ||||
|         arguments: | ||||
|             $em: '@Doctrine\ORM\EntityManagerInterface' | ||||
|             $logger: '@Psr\Log\LoggerInterface' | ||||
|             $passwordEncoder: '@Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface' | ||||
|             $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface' | ||||
|              | ||||
|         tags: | ||||
|             - { name: console.command } | ||||
		Reference in New Issue
	
	Block a user