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 @@
+ {% if is_granted('CHILL_PERSON_UPDATE', person) %}
+
+ {% 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 }}
+ {% if person.birthdate is not null %}{{person.birthdate|localizeddate('long', 'none', app.request.locale) }}{% else %}{{ 'Unknown date of birth'|trans }}{% endif %}
+