From 30a6baef242f8ac842304b9b5ddfbd4d84ce36a3 Mon Sep 17 00:00:00 2001 From: Mat Date: Thu, 15 Nov 2018 10:27:57 +0100 Subject: [PATCH 1/7] hide advanced search button below similarity search results --- Resources/views/Person/list.html.twig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/views/Person/list.html.twig b/Resources/views/Person/list.html.twig index 11252404c..c4b53a8c1 100644 --- a/Resources/views/Person/list.html.twig +++ b/Resources/views/Person/list.html.twig @@ -71,11 +71,14 @@ {% endif %} + + {% if search_name != "person_similarity" %}
  • {{ 'Advanced search'|trans }}
  • + {% endif %} {% if preview == true and persons|length < total %}
  • From d4262d0b28e6fb788ba2fb24f4d9b6e53dc46821 Mon Sep 17 00:00:00 2001 From: Tchama Date: Fri, 5 Jul 2019 16:31:32 +0200 Subject: [PATCH 2/7] adapt import person script: add email, mobilenumber, gender. allow empties values cases --- Command/ImportPeopleFromCSVCommand.php | 138 ++++++++++++++++++------- 1 file changed, 100 insertions(+), 38 deletions(-) diff --git a/Command/ImportPeopleFromCSVCommand.php b/Command/ImportPeopleFromCSVCommand.php index 0237cfad1..0ac056642 100644 --- a/Command/ImportPeopleFromCSVCommand.php +++ b/Command/ImportPeopleFromCSVCommand.php @@ -38,44 +38,39 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\Event; /** - * + * Class ImportPeopleFromCSVCommand * + * @package Chill\PersonBundle\Command * @author Julien Fastré */ class ImportPeopleFromCSVCommand extends ContainerAwareCommand { /** - * * @var InputInterface */ protected $input; /** - * * @var OutputInterface */ protected $output; /** - * * @var \Psr\Log\LoggerInterface */ protected $logger; /** - * * @var \Chill\MainBundle\Templating\TranslatableStringHelper */ protected $helper; /** - * * @var \Doctrine\Common\Persistence\ObjectManager */ protected $em; /** - * * @var EventDispatcherInterface */ protected $eventDispatcher; @@ -88,13 +83,11 @@ class ImportPeopleFromCSVCommand extends ContainerAwareCommand protected $line; /** - * * @var array where key are column names, and value the custom field slug */ protected $customFieldMapping = array(); /** - * * @var CustomFieldProvider */ protected $customFieldProvider; @@ -112,10 +105,13 @@ class ImportPeopleFromCSVCommand extends ContainerAwareCommand ['firstname', 'The column header for firstname', 'firstname'], ['lastname', 'The column header for lastname', 'lastname'], ['birthdate', 'The column header for birthdate', 'birthdate'], + ['gender', 'The column header for gender', 'gender'], ['opening_date', 'The column header for opening date', 'opening_date'], ['closing_date', 'The column header for closing date', 'closing_date'], ['memo', 'The column header for memo', 'memo'], + ['email', 'The column header for email', 'email'], ['phonenumber', 'The column header for phonenumber', 'phonenumber'], + ['mobilenumber', 'The column header for mobilenumber', 'mobilenumber'], ['street1', 'The column header for street 1', 'street1'], ['postalcode', 'The column header for postal code', 'postalcode'], ['locality', 'The column header for locality', 'locality'], @@ -144,8 +140,10 @@ class ImportPeopleFromCSVCommand extends ContainerAwareCommand parent::__construct('chill:person:import'); } - + /** + * + */ protected function configure() { $this @@ -256,7 +254,10 @@ EOF return $this; } - + /** + * @param InputInterface $input + * @param OutputInterface $output + */ protected function interact(InputInterface $input, OutputInterface $output) { // preparing the basic @@ -286,6 +287,9 @@ EOF $this->loadAnswerMatching(); } + /** + * @param $row + */ protected function matchColumnToCustomField($row) { @@ -359,6 +363,9 @@ EOF } } + /** + * + */ protected function dumpAnswerMatching() { if ($this->input->hasOption('dump-choice-matching') && !empty($this->input->getOption('dump-choice-matching'))) { @@ -372,6 +379,12 @@ EOF } } + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int|null|void + * @throws \Exception + */ protected function execute(InputInterface $input, OutputInterface $output) { $this->input = $input; @@ -499,10 +512,11 @@ EOF } /** - * + * * @param array $row * @param array $headers the processed header : an array as prepared by self::processingHeaders * @return Person + * @throws \Exception */ protected function createPerson($row, $headers) { @@ -534,6 +548,9 @@ EOF case 'birthdate': $this->processBirthdate($person, $value); break; + case 'gender': + $person->setGender($value); + break; case 'opening_date': // we have processed this when creating the person object, skipping; break; @@ -543,9 +560,15 @@ EOF case 'memo': $person->setMemo($value); break; + case 'email': + $person->setEmail($value); + break; case 'phonenumber': $person->setPhonenumber($value); break; + case 'mobilenumber': + $person->setMobilenumber($value); + break; // we just keep the column number for those data case 'postalcode': @@ -563,26 +586,34 @@ EOF // handle address if (\in_array('postalcode', $headers)) { - $address = new Address(); - $postalCode = $this->guessPostalCode($postalCodeValue, $localityValue ?? ''); - - if ($postalCode === null) { - throw new \Exception("The locality is not found"); + if (! empty($postalCodeValue)) { + + $address = new Address(); + $postalCode = $this->guessPostalCode($postalCodeValue, $localityValue ?? ''); + + if ($postalCode === null) { + throw new \Exception("The locality is not found"); + } + + $address->setPostcode($postalCode); + + if (\in_array('street1', $headers)) { + $address->setStreetAddress1($street1Value); + } + $address->setValidFrom(new \DateTime('today')); + + $person->addAddress($address); } - - $address->setPostcode($postalCode); - - if (\in_array('street1', $headers)) { - $address->setStreetAddress1($street1Value); - } - $address->setValidFrom(new \DateTime('today')); - - $person->addAddress($address); } return $person; } + /** + * @param $row + * @param $headers + * @return Center|mixed|null|object + */ protected function getCenter($row, $headers) { if ($this->input->hasOption('force-center') && !empty($this->input->getOption('force-center'))) { @@ -606,6 +637,10 @@ EOF } } + /** + * @param $centerName + * @return Center|mixed|null|object + */ protected function guessCenter($centerName) { if (!\array_key_exists('_center_picked', $this->cacheAnswersMapping)) { @@ -673,6 +708,11 @@ EOF return $center; } + /** + * @param $postalCode + * @param $locality + * @return mixed|null + */ protected function guessPostalCode($postalCode, $locality) { if (!\array_key_exists('_postal_code_picked', $this->cacheAnswersMapping)) { @@ -738,9 +778,15 @@ EOF return $pc; } - + /** + * @param Person $person + * @param $value + * @throws \Exception + */ protected function processBirthdate(Person $person, $value) { + if (empty($value)) { return; } + $date = $this->processDate($value, $this->input->getOption('birthdate_format')); if ($date instanceof \DateTime) { @@ -763,8 +809,15 @@ EOF } + /** + * @param Person $person + * @param $value + * @throws \Exception + */ protected function processClosingDate(Person $person, $value) { + if (empty($value)) { return; } + // we skip if the opening date is now (or after yesterday) /* @var $period \Chill\PersonBundle\Entity\AccompanyingPeriod */ $period = $person->getCurrentAccompanyingPeriod(); @@ -797,6 +850,11 @@ EOF $value)); } + /** + * @param Person $person + * @param $row + * @throws \Exception + */ protected function processingCustomFields(Person $person, $row) { /* @var $factory \Symfony\Component\Form\FormFactory */ @@ -836,9 +894,10 @@ EOF /** * Process a text type on a custom field - * + * * @param type $value - * @param \Chill\PersonBundle\Command\Symfony\Component\Form\FormInterface $form + * @param \Symfony\Component\Form\FormInterface $form + * @return type */ protected function processTextType( $value, @@ -861,14 +920,15 @@ EOF /** * Process a custom field choice. - * + * * The method try to guess if the result exists amongst the text of the possible * choices. If the texts exists, then this is picked. Else, ask the user. - * + * * @param string $value * @param \Symfony\Component\Form\FormInterface $form * @param \Chill\CustomFieldsBundle\Entity\CustomField $cf * @return string + * @throws \Exception */ protected function processChoiceType( $value, @@ -982,11 +1042,12 @@ EOF } /** - * Recursive method to collect the possibles answer from a ChoiceType (or + * Recursive method to collect the possibles answer from a ChoiceType (or * its inherited types). - * + * * @param \Symfony\Component\Form\FormInterface $form - * @return array where + * @return array where + * @throws \Exception */ private function collectChoicesAnswers($choices) { @@ -1011,8 +1072,11 @@ EOF return $answers; } - - + /** + * @param $value + * @param $formats + * @return bool|\DateTime + */ protected function processDate($value, $formats) { $possibleFormats = explode("|", $formats); @@ -1046,6 +1110,4 @@ EOF } - - } From f1eefa7ed744f44af7b836e6ac788309488ab97e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 30 Oct 2019 22:54:24 +0100 Subject: [PATCH 3/7] add search by phone + format phonenumber --- CHANGELOG.md | 20 +++ DependencyInjection/ChillPersonExtension.php | 12 +- DependencyInjection/Configuration.php | 14 +- Entity/PersonRepository.php | 29 +++- Repository/PersonRepository.php | 100 +++++++++++++ Resources/config/doctrine/Person.orm.yml | 2 +- Resources/config/services.yml | 6 - Resources/config/services/repository.yml | 13 ++ Resources/config/services/search_by_phone.yml | 11 ++ Resources/public/sass/index.js | 5 + Resources/public/sass/mobile-alt-solid.svg | 1 + .../public/sass/person_by_phonenumber.scss | 25 ++++ Resources/public/sass/phone-alt-solid.svg | 1 + Resources/translations/messages.fr.yml | 1 + Resources/views/Person/list.html.twig | 9 ++ .../Person/list_by_phonenumber.html.twig | 108 ++++++++++++++ Resources/views/Person/view.html.twig | 4 +- Search/PersonSearchByPhone.php | 133 ++++++++++++++++++ chill.webpack.config.js | 1 + 19 files changed, 469 insertions(+), 26 deletions(-) create mode 100644 Repository/PersonRepository.php create mode 100644 Resources/config/services/repository.yml create mode 100644 Resources/config/services/search_by_phone.yml create mode 100644 Resources/public/sass/index.js create mode 100644 Resources/public/sass/mobile-alt-solid.svg create mode 100644 Resources/public/sass/person_by_phonenumber.scss create mode 100644 Resources/public/sass/phone-alt-solid.svg create mode 100644 Resources/views/Person/list_by_phonenumber.html.twig create mode 100644 Search/PersonSearchByPhone.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a69343d..bab2f2d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,3 +48,23 @@ Version 1.5.7 - add a link between accompanying person and user - add an icon when the file is opened / closed in result list, and in person rendering macro - improve command to move person and all data: allow to delete some entities during move and add events + +Master branch +============= + +- add search by phonenumber, with a custom SearchInterface + + This can be activated or desactivated by config: + + ``` + chill_person: + enabled: true + search: + enabled: true + + # enable search by phone. 'always' show the result on every result. 'on-domain' will show the result only if the domain is given in the search box. 'never' disable this feature + search_by_phone: on-domain # One of "always"; "on-domain"; "never" + ``` +- format phonenumber using twilio (if available) ; +- add `record_actions` in person search result list: users can click on a little eye to open person page ; + diff --git a/DependencyInjection/ChillPersonExtension.php b/DependencyInjection/ChillPersonExtension.php index 3f78351cc..cdf707f1d 100644 --- a/DependencyInjection/ChillPersonExtension.php +++ b/DependencyInjection/ChillPersonExtension.php @@ -44,10 +44,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - // set configuration for double metaphone - $container->setParameter('cl_chill_person.search.use_double_metaphone', - $config['search']['use_double_metaphone']); - // set configuration for validation $container->setParameter('chill_person.validation.birtdate_not_before', $config['validation']['birthdate_not_after']); @@ -67,6 +63,14 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $loader->load('services/command.yml'); $loader->load('services/actions.yml'); $loader->load('services/form.yml'); + $loader->load('services/repository.yml'); + + // load service advanced search only if configure + if ($config['search']['search_by_phone'] != 'never') { + $loader->load('services/search_by_phone.yml'); + $container->setParameter('chill_person.search.search_by_phone', + $config['search']['search_by_phone']); + } if ($container->getParameter('chill_person.accompanying_period') !== 'hidden') { $loader->load('services/exports_accompanying_period.yml'); diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index f71344029..471e7d958 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -30,12 +30,14 @@ class Configuration implements ConfigurationInterface ->arrayNode('search') ->canBeDisabled() ->children() - ->booleanNode('use_double_metaphone') - ->defaultFalse() - ->end() // use_double_metaphone, parent = children for 'search' - ->booleanNode('use_trigrams') - ->defaultFalse() - ->end() // use_trigrams, parent = children of 'search' + ->enumNode('search_by_phone') + ->values(['always', 'on-domain', 'never']) + ->defaultValue('on-domain') + ->info('enable search by phone. \'always\' show the result ' + . 'on every result. \'on-domain\' will show the result ' + . 'only if the domain is given in the search box. ' + . '\'never\' disable this feature') + ->end() ->end() //children for 'search', parent = array node 'search' ->end() // array 'search', parent = children of root ->arrayNode('validation') diff --git a/Entity/PersonRepository.php b/Entity/PersonRepository.php index 6d1b86f0b..9619b2264 100644 --- a/Entity/PersonRepository.php +++ b/Entity/PersonRepository.php @@ -1,15 +1,30 @@ + * + * 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 . + */ namespace Chill\PersonBundle\Entity; -use Doctrine\ORM\EntityRepository; +@trigger_error(__CLASS__." is deprecated since 2019-10-30. Use " + .\Chill\PersonBundle\Repository\PersonRepository::class.' instead.', + E_USER_DEPRECATED); /** - * PersonRepository - * - * This class was generated by the Doctrine ORM. Add your own custom - * repository methods below. + * + * @deprecated since 2019-10-30. Use \Chill\PersonBundle\Repository\PersonRepository instead. */ -class PersonRepository extends EntityRepository +class PersonRepository extends \Chill\PersonBundle\Repository\PersonRepository { } diff --git a/Repository/PersonRepository.php b/Repository/PersonRepository.php new file mode 100644 index 000000000..3b61845b8 --- /dev/null +++ b/Repository/PersonRepository.php @@ -0,0 +1,100 @@ + + * + * 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 . + */ + +namespace Chill\PersonBundle\Repository; + +use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\QueryBuilder; + +/** + * PersonRepository + * + */ +class PersonRepository extends EntityRepository +{ + public function findByPhone( + string $phonenumber, + $centers, + $firstResult, + $maxResults, + array $only = ['mobile', 'phone'] + ) { + $qb = $this->createQueryBuilder('p'); + $qb->select('p'); + + $this->addByCenters($qb, $centers); + $this->addPhoneNumber($qb, $phonenumber, $only); + + $qb->setFirstResult($firstResult) + ->setMaxResults($maxResults) + ; + + return $qb->getQuery()->getResult(); + } + + public function countByPhone( + string $phonenumber, + $centers, + array $only = ['mobile', 'phone'] + ): int + { + $qb = $this->createQueryBuilder('p'); + $qb->select('COUNT(p)'); + + $this->addByCenters($qb, $centers); + $this->addPhoneNumber($qb, $phonenumber, $only); + + return $qb->getQuery()->getSingleScalarResult(); + } + + protected function addPhoneNumber(QueryBuilder $qb, string $phonenumber, array $only) + { + if (count($only) === 0) { + throw new \Exception("No array field to search"); + } + + $phonenumber = $this->parsePhoneNumber($phonenumber); + + $orX = $qb->expr()->orX(); + + if (\in_array('mobile', $only)) { + $orX->add($qb->expr()->like("REPLACE(p.mobilenumber, ' ', '')", ':phonenumber')); + } + if (\in_array('phone', $only)) { + $orX->add($qb->expr()->like("REPLACE(p.phonenumber, ' ', '')", ':phonenumber')); + } + + $qb->andWhere($orX); + + $qb->setParameter('phonenumber', '%'.$phonenumber.'%'); + } + + + protected function parsePhoneNumber($phonenumber): string + { + return \str_replace(' ', '', $phonenumber); + } + + protected function addByCenters(QueryBuilder $qb, array $centers) + { + if (count($centers) > 0) { + $qb->andWhere($qb->expr()->in('p.center', ':centers')); + $qb->setParameter('centers', $centers); + } + } +} diff --git a/Resources/config/doctrine/Person.orm.yml b/Resources/config/doctrine/Person.orm.yml index 543757f9a..081a710da 100644 --- a/Resources/config/doctrine/Person.orm.yml +++ b/Resources/config/doctrine/Person.orm.yml @@ -4,7 +4,7 @@ Chill\PersonBundle\Entity\Person: indexes: person_names: columns: [firstName, lastName] - repositoryClass: Chill\PersonBundle\Entity\PersonRepository + repositoryClass: Chill\PersonBundle\Repository\PersonRepository fields: id: type: integer diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 26ffd979c..b17bf6acb 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -47,9 +47,3 @@ services: tags: - { name: form.type, alias: chill_personbundle_person_creation } - - chill.person.repository.person: - class: Chill\PersonBundle\Entity\PersonRepository - factory: ['@doctrine.orm.entity_manager', getRepository] - arguments: - - 'Chill\PersonBundle\Entity\Person' diff --git a/Resources/config/services/repository.yml b/Resources/config/services/repository.yml new file mode 100644 index 000000000..d693ef841 --- /dev/null +++ b/Resources/config/services/repository.yml @@ -0,0 +1,13 @@ +services: + chill.person.repository.person: + class: Chill\PersonBundle\Person\PersonRepository + deprecated: the service '%service_id%' is deprecated since 2019-10-30 and will be removed soon. Use 'Chill\PersonBundle\Repository\PersonRepository' instead + factory: ['@doctrine.orm.entity_manager', getRepository] + arguments: + - 'Chill\PersonBundle\Entity\Person' + + Chill\PersonBundle\Repository\PersonRepository: + class: Chill\PersonBundle\Person\PersonRepository + factory: ['@doctrine.orm.entity_manager', getRepository] + arguments: + - 'Chill\PersonBundle\Entity\Person' diff --git a/Resources/config/services/search_by_phone.yml b/Resources/config/services/search_by_phone.yml new file mode 100644 index 000000000..4a20e898a --- /dev/null +++ b/Resources/config/services/search_by_phone.yml @@ -0,0 +1,11 @@ +services: + Chill\PersonBundle\Search\PersonSearchByPhone: + arguments: + - '@Chill\PersonBundle\Repository\PersonRepository' + - '@security.token_storage' + - '@chill.main.security.authorization.helper' + - '@chill_main.paginator_factory' + - '@Symfony\Component\Templating\EngineInterface' + - '%chill_person.search.search_by_phone%' + tags: + - { name: chill.search, alias: 'person_by_phone' } \ No newline at end of file diff --git a/Resources/public/sass/index.js b/Resources/public/sass/index.js new file mode 100644 index 000000000..35945c7ff --- /dev/null +++ b/Resources/public/sass/index.js @@ -0,0 +1,5 @@ +require('./phone-alt-solid.svg'); +require('./mobile-alt-solid.svg'); +require('./person_by_phonenumber.scss'); + + diff --git a/Resources/public/sass/mobile-alt-solid.svg b/Resources/public/sass/mobile-alt-solid.svg new file mode 100644 index 000000000..ae8b81bb1 --- /dev/null +++ b/Resources/public/sass/mobile-alt-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/public/sass/person_by_phonenumber.scss b/Resources/public/sass/person_by_phonenumber.scss new file mode 100644 index 000000000..a1066c08f --- /dev/null +++ b/Resources/public/sass/person_by_phonenumber.scss @@ -0,0 +1,25 @@ +.person-list__--by-phonenumber { + .person-list__--by-phonenumber__phones { + ul { + list-style: none inside; + padding: 0; + margin: 0; + + li { + margin: 0.80rem; + + img { + vertical-align: baseline; + height: 0.90rem; + margin-right: 0.20rem; + } + pre { + display: inline; + } + } + } + + + } +} +; diff --git a/Resources/public/sass/phone-alt-solid.svg b/Resources/public/sass/phone-alt-solid.svg new file mode 100644 index 000000000..6460d2d9e --- /dev/null +++ b/Resources/public/sass/phone-alt-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml index 17005b044..85ed089ac 100644 --- a/Resources/translations/messages.fr.yml +++ b/Resources/translations/messages.fr.yml @@ -93,6 +93,7 @@ Reset: 'Remise à zéro' '%nb% person with similar name. Please verify that this is a new person': '{1} Une personne a un nom similaire. Vérifiez qu''il ne s''agit pas d''elle. | ]1, Inf] %nb% personnes ont un nom similaire. Vérifiez qu''il ne s''agit pas de l''une d''elles.' 'The person has been created': 'Le dossier a été créé' 'Person search results': 'Recherche de personnes' +Person search results by phonenumber: Recherche de personnes par numéro de téléphone 'Search within persons': 'Recherche parmi les personnes' '%total% persons matching the search pattern:': '{0} Aucune personne ne correspond aux termes de recherche : | {1} Une personne a été trouvée par la recherche : | ]1,Inf] %total% personnes correspondent aux termes de recherche :' 'Last opening since %last_opening%': 'Dernière ouverture le %last_opening%.' diff --git a/Resources/views/Person/list.html.twig b/Resources/views/Person/list.html.twig index ab1a12a7d..3234d496f 100644 --- a/Resources/views/Person/list.html.twig +++ b/Resources/views/Person/list.html.twig @@ -33,6 +33,7 @@ {% trans %}Name{% endtrans %} {% trans %}Date of birth{% endtrans %} {% trans %}Nationality{% endtrans %} +   @@ -63,6 +64,14 @@ {{ 'Without nationality'|trans }} {% endif %} + + + {% endfor %} diff --git a/Resources/views/Person/list_by_phonenumber.html.twig b/Resources/views/Person/list_by_phonenumber.html.twig new file mode 100644 index 000000000..78195f165 --- /dev/null +++ b/Resources/views/Person/list_by_phonenumber.html.twig @@ -0,0 +1,108 @@ +{# + * Copyright (C) 2014, Champs Libres Cooperative SCRLFS, + * + * 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 . +#} +

    {{ title|default('Person search results by phonenumber')|trans }}

    + +

    + {{ '%total% persons matching the search pattern:'|transchoice( total, { '%total%' : total}) }} + + {{ pattern }} + +

    + +

    {{ 'Results %start%-%end% of %total%'|trans({ '%start%' : start, '%end%': start + persons|length, '%total%' : total } ) }}

    + +{% if persons|length > 0 %} + + + + + + + + + + + + {% for person in persons %} + + + + + + + {% endfor %} + +
    {% trans %}Name{% endtrans %}{% trans %}Date of birth{% endtrans %}{% trans %}Phonenumber{% endtrans %} 
    + {% set is_open = person.isOpen() %} + + {{person.firstName}} {{person.lastName}} + {% spaceless %} + {% if chill_person.fields.accompanying_period == 'visible' %} + {% if is_open == false %} + + {% else %} + + {% endif %} + {% endif %} + {% endspaceless %} + + + {% if person.birthdate is not null %}{{person.birthdate|localizeddate('long', 'none', app.request.locale) }}{% else %}{{ 'Unknown date of birth'|trans }}{% endif %} + + + +
      +
    • + {% if is_granted('CHILL_PERSON_UPDATE', person) %} +
    • + {% endif %} +
    +
    + + +{% endif %} + +{% if preview == false %} +{{ chill_pagination(paginator) }} +{% endif %} + diff --git a/Resources/views/Person/view.html.twig b/Resources/views/Person/view.html.twig index 61be78b6e..c6db21ef5 100644 --- a/Resources/views/Person/view.html.twig +++ b/Resources/views/Person/view.html.twig @@ -206,13 +206,13 @@ This view should receive those arguments: {%- if chill_person.fields.phonenumber == 'visible' -%}
    {{ 'Phonenumber'|trans }} :
    -
    {% if person.phonenumber is not empty %}
    {{ person.phonenumber}}
    {% else %}{{ 'No data given'|trans }}{% endif %}
    +
    {% if person.phonenumber is not empty %}
    {{ person.phonenumber|chill_format_phonenumber }}
    {% else %}{{ 'No data given'|trans }}{% endif %}
    {% endif %} {%- if chill_person.fields.mobilenumber == 'visible' -%}
    {{ 'Mobilenumber'|trans }} :
    -
    {% if person.mobilenumber is not empty %}
    {{ person.mobilenumber}}
    {% else %}{{ 'No data given'|trans }}{% endif %}
    +
    {% if person.mobilenumber is not empty %}
    {{ person.mobilenumber|chill_format_phonenumber }}
    {% else %}{{ 'No data given'|trans }}{% endif %}
    {% endif %} {%- if chill_person.fields.contact_info == 'visible' -%} diff --git a/Search/PersonSearchByPhone.php b/Search/PersonSearchByPhone.php new file mode 100644 index 000000000..08152144b --- /dev/null +++ b/Search/PersonSearchByPhone.php @@ -0,0 +1,133 @@ + + * + * 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 . + */ +namespace Chill\PersonBundle\Search; + +use Chill\MainBundle\Search\AbstractSearch; +use Chill\PersonBundle\Repository\PersonRepository; +use Chill\PersonBundle\Entity\Person; +use Chill\MainBundle\Search\SearchInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Chill\PersonBundle\Security\Authorization\PersonVoter; +use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Pagination\PaginatorFactory; +use Symfony\Component\Security\Core\Role\Role; +use Symfony\Component\Templating\EngineInterface; + +/** + * + * + */ +class PersonSearchByPhone extends AbstractSearch +{ + + /** + * + * @var PersonRepository + */ + private $personRepository; + + /** + * + * @var TokenStorageInterface + */ + private $tokenStorage; + + /** + * + * @var AuthorizationHelper + */ + private $helper; + + /** + * + * @var PaginatorFactory + */ + protected $paginatorFactory; + + /** + * + * @var bool + */ + protected $activeByDefault; + + /** + * + * @var Templating + */ + protected $engine; + + const NAME = 'phone'; + + public function __construct( + PersonRepository $personRepository, + TokenStorageInterface $tokenStorage, + AuthorizationHelper $helper, + PaginatorFactory $paginatorFactory, + EngineInterface $engine, + $activeByDefault) + { + $this->personRepository = $personRepository; + $this->tokenStorage = $tokenStorage; + $this->helper = $helper; + $this->paginatorFactory = $paginatorFactory; + $this->engine = $engine; + $this->activeByDefault = $activeByDefault === 'always'; + } + + public function getOrder(): int + { + return 110; + } + + public function isActiveByDefault(): bool + { + return $this->activeByDefault; + } + + public function renderResult(array $terms, $start = 0, $limit = 50, $options = array(), $format = 'html') + { + $phonenumber = $terms['_default']; + $centers = $this->helper->getReachableCenters($this->tokenStorage + ->getToken()->getUser(), new Role(PersonVoter::SEE)); + $total = $this->personRepository + ->countByPhone($phonenumber, $centers); + $persons = $this->personRepository + ->findByPhone($phonenumber, $centers, $start, $limit) + ; + $paginator = $this->paginatorFactory + ->create($total); + + return $this->engine->render('ChillPersonBundle:Person:list_by_phonenumber.html.twig', + array( + 'persons' => $persons, + 'pattern' => $this->recomposePattern($terms, array(), $terms['_domain'] ?? self::NAME), + 'phonenumber' => $phonenumber, + 'total' => $total, + 'start' => $start, + 'search_name' => self::NAME, + 'preview' => $options[SearchInterface::SEARCH_PREVIEW_OPTION], + 'paginator' => $paginator + )); + } + + public function supports($domain, $format): bool + { + return $domain === 'phone' && $format === 'html'; + } +} diff --git a/chill.webpack.config.js b/chill.webpack.config.js index 97222b223..cfc6b7daa 100644 --- a/chill.webpack.config.js +++ b/chill.webpack.config.js @@ -1,4 +1,5 @@ // this file loads all assets from the Chill person bundle require('./Resources/public/css/person.css'); +require('./Resources/public/sass/index.js'); //require('./Resources/public/sass/person.scss'); From cc48987ab30356c69857c1c74be850e750e22830 Mon Sep 17 00:00:00 2001 From: Tchama Date: Tue, 5 Nov 2019 14:34:02 +0100 Subject: [PATCH 4/7] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab2f2d1f..96224bd3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,3 +68,5 @@ Master branch - format phonenumber using twilio (if available) ; - add `record_actions` in person search result list: users can click on a little eye to open person page ; +- add new fields (email, mobilenumber, gender) into importPeopleFromCSV command + From e43bbaf1a173cf41039fbb4f5cfb5e37ed0e558e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 6 Nov 2019 11:46:45 +0100 Subject: [PATCH 5/7] add indexfor searching by phonenumber --- .../migrations/Version20191106103452.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Resources/migrations/Version20191106103452.php diff --git a/Resources/migrations/Version20191106103452.php b/Resources/migrations/Version20191106103452.php new file mode 100644 index 000000000..5b27fe7e5 --- /dev/null +++ b/Resources/migrations/Version20191106103452.php @@ -0,0 +1,30 @@ +addSql("CREATE INDEX phonenumber_trgm_idx + ON public.chill_person_person USING gin + (phonenumber gin_trgm_ops)"); + + $this->addSql("CREATE INDEX mobilenumber_trgm_idx + ON public.chill_person_person USING gin + (mobilenumber gin_trgm_ops)"); + + } + + public function down(Schema $schema) : void + { + $this->addSql("DROP INDEX phonenumber_trgm_idx"); + $this->addSql("DROP INDEX mobilenumber_trgm_idx"); + } +} From 06bbf585eff3bc428965577d516f8056bbd7695f Mon Sep 17 00:00:00 2001 From: Tchama Date: Tue, 12 Nov 2019 11:27:36 +0100 Subject: [PATCH 6/7] fix Person Repository error --- Entity/PersonRepository.php | 5 ++++- Repository/PersonRepository.php | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Entity/PersonRepository.php b/Entity/PersonRepository.php index 9619b2264..b8f121a1b 100644 --- a/Entity/PersonRepository.php +++ b/Entity/PersonRepository.php @@ -15,8 +15,11 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + namespace Chill\PersonBundle\Entity; +use Doctrine\ORM\EntityRepository; + @trigger_error(__CLASS__." is deprecated since 2019-10-30. Use " .\Chill\PersonBundle\Repository\PersonRepository::class.' instead.', E_USER_DEPRECATED); @@ -25,6 +28,6 @@ namespace Chill\PersonBundle\Entity; * * @deprecated since 2019-10-30. Use \Chill\PersonBundle\Repository\PersonRepository instead. */ -class PersonRepository extends \Chill\PersonBundle\Repository\PersonRepository +class PersonRepository extends EntityRepository { } diff --git a/Repository/PersonRepository.php b/Repository/PersonRepository.php index 3b61845b8..42294ac2e 100644 --- a/Repository/PersonRepository.php +++ b/Repository/PersonRepository.php @@ -18,14 +18,14 @@ namespace Chill\PersonBundle\Repository; -use Doctrine\ORM\EntityRepository; + use Doctrine\ORM\QueryBuilder; /** * PersonRepository * */ -class PersonRepository extends EntityRepository +class PersonRepository extends \Chill\PersonBundle\Entity\PersonRepository { public function findByPhone( string $phonenumber, From b96cd8018f988beb433252abaa2df3ba63df50f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 4 Dec 2019 14:04:02 +0100 Subject: [PATCH 7/7] load assets using a configure function --- CHANGELOG.md | 4 ++-- Resources/public/index.js | 1 + chill.webpack.config.js | 7 +++---- 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 Resources/public/index.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 96224bd3b..f1ca611f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,7 @@ Version 1.5.7 - add an icon when the file is opened / closed in result list, and in person rendering macro - improve command to move person and all data: allow to delete some entities during move and add events -Master branch +Version 1.5.8 ============= - add search by phonenumber, with a custom SearchInterface @@ -67,6 +67,6 @@ Master branch ``` - format phonenumber using twilio (if available) ; - add `record_actions` in person search result list: users can click on a little eye to open person page ; - - add new fields (email, mobilenumber, gender) into importPeopleFromCSV command +- configure asset using a function diff --git a/Resources/public/index.js b/Resources/public/index.js new file mode 100644 index 000000000..f4bcba7fb --- /dev/null +++ b/Resources/public/index.js @@ -0,0 +1 @@ +require('./sass/person.scss'); \ No newline at end of file diff --git a/chill.webpack.config.js b/chill.webpack.config.js index cfc6b7daa..724184f63 100644 --- a/chill.webpack.config.js +++ b/chill.webpack.config.js @@ -1,5 +1,4 @@ // this file loads all assets from the Chill person bundle - -require('./Resources/public/css/person.css'); -require('./Resources/public/sass/index.js'); -//require('./Resources/public/sass/person.scss'); +module.exports = function(encore, entries) { + entries.push(__dirname + '/Resources/public/index.js'); +};