From c960745763672621f1e724a40a185c012d30c18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 4 Apr 2018 09:46:30 +0200 Subject: [PATCH 01/64] advanced search (bis) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ce commit contient des modifications qui sont peut-être dans un autre commit d'une autre branche. --- Controller/AdminController.php | 7 +++++++ Controller/SearchController.php | 21 ++++++++++++++++++- Resources/config/routing.yml | 4 ++++ Resources/config/services/form.yml | 7 +++++++ Resources/translations/admin.fr.yml | 4 ++++ Resources/views/Admin/index.html.twig | 17 +++++++++++++++ .../views/Search/advanced_search.html.twig | 12 +++++++---- Search/SearchProvider.php | 13 ++++++++++++ 8 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 Resources/translations/admin.fr.yml create mode 100644 Resources/views/Admin/index.html.twig diff --git a/Controller/AdminController.php b/Controller/AdminController.php index 2009ecf21..9852be7ab 100644 --- a/Controller/AdminController.php +++ b/Controller/AdminController.php @@ -42,4 +42,11 @@ class AdminController extends Controller { return $this->render('ChillMainBundle:Admin:layout_permissions.html.twig'); } + public function configurationWarningsAction() + { + $alertManager = $this->get('chill_main.configuration_alert_manager'); + + + } + } diff --git a/Controller/SearchController.php b/Controller/SearchController.php index 49a648f43..375429de1 100644 --- a/Controller/SearchController.php +++ b/Controller/SearchController.php @@ -106,6 +106,24 @@ class SearchController extends Controller ); } + public function advancedSearchListAction(Request $request) + { + /* @var $variable Chill\MainBundle\Search\SearchProvider */ + $searchProvider = $this->get('chill.main.search_provider'); + $advancedSearchProviders = $searchProvider + ->getHasAdvancedFormSearchServices(); + + if(\count($advancedSearchProviders) === 1) { + \reset($advancedSearchProviders); + + return $this->redirectToRoute('chill_main_advanced_search', [ + 'name' => \key($advancedSearchProviders) + ]); + } + + return $this->render('ChillMainBundle:Search:choose_list.html.twig'); + } + public function advancedSearchAction($name, Request $request) { try { @@ -144,7 +162,8 @@ class SearchController extends Controller return $this->render('ChillMainBundle:Search:advanced_search.html.twig', [ 'form' => $form->createView(), - 'name' => $name + 'name' => $name, + 'title' => $search->getAdvancedSearchTitle() ]); } diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml index 3252e8f96..2b1562379 100644 --- a/Resources/config/routing.yml +++ b/Resources/config/routing.yml @@ -66,6 +66,10 @@ chill_main_search: chill_main_advanced_search: path: /{_locale}/search/advanced/{name} defaults: { _controller: ChillMainBundle:Search:advancedSearch } + +chill_main_advanced_search_list: + path: /{_locale}/search/advanced + defaults: { _controller: ChillMainBundle:Search:advancedSearchList } login: path: /login diff --git a/Resources/config/services/form.yml b/Resources/config/services/form.yml index e205292bd..30502a399 100644 --- a/Resources/config/services/form.yml +++ b/Resources/config/services/form.yml @@ -108,5 +108,12 @@ services: - "@security.token_storage" - "@chill.main.scope_repository" - "@chill.main.helper.translatable_string" + tags: + - { name: form.type } + + chill.main.form.advanced_search_type: + class: Chill\MainBundle\Form\AdvancedSearchType + arguments: + - "@chill.main.search_provider" tags: - { name: form.type } \ No newline at end of file diff --git a/Resources/translations/admin.fr.yml b/Resources/translations/admin.fr.yml new file mode 100644 index 000000000..c2d522a12 --- /dev/null +++ b/Resources/translations/admin.fr.yml @@ -0,0 +1,4 @@ +welcome_message_raw: | +

Dans l'interface d'administration, vous pouvez configurer votre instance selon vos besoins.

+ + diff --git a/Resources/views/Admin/index.html.twig b/Resources/views/Admin/index.html.twig new file mode 100644 index 000000000..999034392 --- /dev/null +++ b/Resources/views/Admin/index.html.twig @@ -0,0 +1,17 @@ +{% extends "ChillMainBundle::Admin/layoutWithVerticalMenu.html.twig" %} + +{% block admin_content %} +

{{ 'Administration interface'|trans }}

+ + {{ 'welcome_message_raw'|trans|raw }} + +
+

{{ 'Configuration alerts'|trans }}

+ +

{{ 'Here you can check the configuration of your instance.'|trans }}

+ + {{ chill_widget('configuration_warnings', {}) }} + +
+ +{% endblock %} \ No newline at end of file diff --git a/Resources/views/Search/advanced_search.html.twig b/Resources/views/Search/advanced_search.html.twig index 48c2bd230..e19d47496 100644 --- a/Resources/views/Search/advanced_search.html.twig +++ b/Resources/views/Search/advanced_search.html.twig @@ -17,10 +17,14 @@ {% extends "ChillMainBundle::layout.html.twig" %} -{% block title 'Advanced search for %name%'|trans({ '%name%' : name }) %} +{% block title title|trans %} {% block content %} +
+ +

{{ title|trans }}

+ {{ form_start(form) }} {% for f in form %} @@ -28,9 +32,7 @@ {{ form_row(f) }} {% endif %} {% endfor %} - - - +
+ {% endblock %} diff --git a/Search/SearchProvider.php b/Search/SearchProvider.php index db8aec208..e1b602c0f 100644 --- a/Search/SearchProvider.php +++ b/Search/SearchProvider.php @@ -59,6 +59,19 @@ class SearchProvider return $this->searchServices; } + + public function getHasAdvancedFormSearchServices() + { + //sort the array + uasort($this->hasAdvancedFormSearchServices, function(SearchInterface $a, SearchInterface $b) { + if ($a->getOrder() == $b->getOrder()) { + return 0; + } + return ($a->getOrder() < $b->getOrder()) ? -1 : 1; + }); + + return $this->hasAdvancedFormSearchServices; + } /** * parse the search string to extract domain and terms From da9aecefa79baa7d4b873c1be57b90a5fb82dce6 Mon Sep 17 00:00:00 2001 From: nobohan Date: Fri, 13 Apr 2018 15:20:04 +0200 Subject: [PATCH 02/64] fix deprecations: replace last fqcn in add methods for forms --- Controller/PermissionsGroupController.php | 3 ++- Form/Type/AddressType.php | 9 ++++++--- Form/UserType.php | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Controller/PermissionsGroupController.php b/Controller/PermissionsGroupController.php index 911fff79b..336cab5d5 100644 --- a/Controller/PermissionsGroupController.php +++ b/Controller/PermissionsGroupController.php @@ -11,6 +11,7 @@ use Chill\MainBundle\Form\PermissionsGroupType; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\RoleInterface; use Chill\MainBundle\Entity\Scope; +use Chill\MainBundle\Form\Type\ComposedRoleScopeType; /** * PermissionsGroup controller. @@ -491,7 +492,7 @@ class PermissionsGroupController extends Controller ->setAction($this->generateUrl('admin_permissionsgroup_add_role_scope', array('id' => $permissionsGroup->getId()))) ->setMethod('PUT') - ->add('composed_role_scope', 'composed_role_scope') + ->add('composed_role_scope', ComposedRoleScopeType::class) ->add('submit', SubmitType::class, array('label' => 'Add permission')) ->getForm() ; diff --git a/Form/Type/AddressType.php b/Form/Type/AddressType.php index 173bd9b70..fd4fe76be 100644 --- a/Form/Type/AddressType.php +++ b/Form/Type/AddressType.php @@ -23,6 +23,9 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\Extension\Core\Type\DateType; + +use Chill\MainBundle\Form\Type\PostalCodeType; /** * A type to create/update Address entity @@ -46,9 +49,9 @@ class AddressType extends AbstractType 'placeholder' => 'Choose a postal code', 'required' => true )) - ->add('validFrom', 'date', array( - 'required' => true, - 'widget' => 'single_text', + ->add('validFrom', DateType::class, array( + 'required' => true, + 'widget' => 'single_text', 'format' => 'dd-MM-yyyy' ) ) diff --git a/Form/UserType.php b/Form/UserType.php index 404461423..1fd36d9e0 100644 --- a/Form/UserType.php +++ b/Form/UserType.php @@ -21,7 +21,7 @@ class UserType extends AbstractType ->add('username') ; if ($options['is_creation']) { - $builder->add('plainPassword', new UserPasswordType(), array( + $builder->add('plainPassword', UserPasswordType::class, array( 'mapped' => false )); From 7fb208450620914df0e909cb67cace0e5672605b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 16 Apr 2018 11:53:39 +0200 Subject: [PATCH 03/64] set doctrine type "date interval" using native postgresql date interval --- DependencyInjection/ChillMainExtension.php | 9 ++++++++ Doctrine/Type/NativeDateIntervalType.php | 27 ++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 Doctrine/Type/NativeDateIntervalType.php diff --git a/DependencyInjection/ChillMainExtension.php b/DependencyInjection/ChillMainExtension.php index c2f63761f..7a5766d4c 100644 --- a/DependencyInjection/ChillMainExtension.php +++ b/DependencyInjection/ChillMainExtension.php @@ -141,6 +141,15 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, ) )); + //add dbal types (default entity_manager) + $container->prependExtensionConfig('doctrine', array( + 'dbal' => [ + 'types' => [ + 'dateinterval' => \Chill\MainBundle\Doctrine\Type\NativeDateIntervalType::class + ] + ] + )); + //add current route to chill main $container->prependExtensionConfig('chill_main', array( 'routing' => array( diff --git a/Doctrine/Type/NativeDateIntervalType.php b/Doctrine/Type/NativeDateIntervalType.php new file mode 100644 index 000000000..06143f229 --- /dev/null +++ b/Doctrine/Type/NativeDateIntervalType.php @@ -0,0 +1,27 @@ + + */ +class NativeDateIntervalType extends DateIntervalType +{ + + public function getName(): string + { + return \Doctrine\DBAL\Types\Type::DATEINTERVAL; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string + { + return 'INTERVAL'; + } + +} From f5039cc36f325347ce39377862639a7e764285e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 16 Apr 2018 12:03:47 +0200 Subject: [PATCH 04/64] add UserPickerType --- Form/Type/UserPickerType.php | 12 ++++- Resources/config/services.yml | 1 + .../Authorization/AuthorizationHelper.php | 45 ++++++++++++++++++- .../Authorization/AuthorizationHelperTest.php | 16 +++++++ 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/Form/Type/UserPickerType.php b/Form/Type/UserPickerType.php index 9cade1ad2..fd7600bf9 100644 --- a/Form/Type/UserPickerType.php +++ b/Form/Type/UserPickerType.php @@ -25,6 +25,8 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Chill\MainBundle\Entity\User; +use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; +use Symfony\Component\Security\Core\Role\Role; /** @@ -96,8 +98,14 @@ class UserPickerType extends AbstractType ->join('ug.permissionsGroup', 'pg') // role constraints ->join('pg.roleScopes', 'roleScope') - ->andWhere($qb->expr()->eq('roleScope.role', ':role')) - ->setParameter('role', $options['role']) + ->andWhere($qb->expr()->in('roleScope.role', ':roles')) + ->setParameter( + 'roles', + \array_map( + function(Role $role) { return $role->getRole(); }, + $this->authorizationHelper->getParentRoles($options['role']) + ) + ) // add active constraint ->andWhere('u.enabled = :enabled') ->setParameter('enabled', true) diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 33b64df34..c0364f3cd 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -80,6 +80,7 @@ services: class: Chill\MainBundle\Security\Authorization\AuthorizationHelper arguments: - "@security.role_hierarchy" + - "%security.role_hierarchy.roles%" chill.main.role_provider: class: Chill\MainBundle\Security\RoleProvider diff --git a/Security/Authorization/AuthorizationHelper.php b/Security/Authorization/AuthorizationHelper.php index 4c749ad62..95552aa36 100644 --- a/Security/Authorization/AuthorizationHelper.php +++ b/Security/Authorization/AuthorizationHelper.php @@ -26,6 +26,7 @@ use Chill\MainBundle\Entity\HasScopeInterface; use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; use Symfony\Component\Security\Core\Role\Role; use Chill\MainBundle\Entity\Scope; +use Chill\MainBundle\Security\RoleProvider; /** * Helper for authorizations. @@ -42,12 +43,23 @@ class AuthorizationHelper */ protected $roleHierarchy; + /** + * The role in a hierarchy, given by the parameter + * `security.role_hierarchy.roles` from the container. + * + * @var string[] + */ + protected $hierarchy; + protected $existingRoles = array('CHILL_MASTER_ROLE', 'CHILL_PERSON_SEE', 'CHILL_PERSON_UPDATE',); - public function __construct(RoleHierarchyInterface $roleHierarchy) - { + public function __construct( + RoleHierarchyInterface $roleHierarchy, + $hierarchy + ) { $this->roleHierarchy = $roleHierarchy; + $this->hierarchy = $hierarchy; } /** @@ -223,4 +235,33 @@ class AuthorizationHelper return in_array($childRole, $reachableRoles); } + + /** + * Return all the role which give access to the given role. Only the role + * which are registered into Chill are taken into account. + * + * @param Role $role + * @return Role[] the role which give access to the given $role + */ + public function getParentRoles(Role $role) + { + $parentRoles = []; + // transform the roles from role hierarchy from string to Role + $roles = \array_map( + function($string) { + return new Role($string); + }, + \array_keys($this->hierarchy) + ); + + foreach ($roles as $r) { + $childRoles = $this->roleHierarchy->getReachableRoles([$r]); + + if (\in_array($role, $childRoles)) { + $parentRoles[] = $r; + } + } + + return $parentRoles; + } } diff --git a/Tests/Security/Authorization/AuthorizationHelperTest.php b/Tests/Security/Authorization/AuthorizationHelperTest.php index abfc3c754..fb171de07 100644 --- a/Tests/Security/Authorization/AuthorizationHelperTest.php +++ b/Tests/Security/Authorization/AuthorizationHelperTest.php @@ -443,6 +443,22 @@ class AuthorizationHelperTest extends KernelTestCase ); } + public function testGetParentRoles() + { + $parentRoles = $this->getAuthorizationHelper() + ->getParentRoles(new Role('CHILL_INHERITED_ROLE_1')); + + $this->assertContains( + 'CHILL_MASTER_ROLE', + \array_map( + function(Role $role) { + return $role->getRole(); + }, + $parentRoles + ), + "Assert that `CHILL_MASTER_ROLE` is a parent of `CHILL_INHERITED_ROLE_1`"); + } + } From 53901e4681874e4b5ed30513cba1be147f9b96bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 16 Apr 2018 12:37:57 +0200 Subject: [PATCH 05/64] add ScopePickerType, which allow to pick scope depending or ACL --- Form/Type/ScopePickerType.php | 57 ++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/Form/Type/ScopePickerType.php b/Form/Type/ScopePickerType.php index 963cbb7b9..9525e81f0 100644 --- a/Form/Type/ScopePickerType.php +++ b/Form/Type/ScopePickerType.php @@ -26,6 +26,8 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Chill\MainBundle\Entity\Scope; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\MainBundle\Entity\Center; +use Symfony\Component\Security\Core\Role\Role; /** * Allow to pick amongst available scope for the current @@ -82,10 +84,10 @@ class ScopePickerType extends AbstractType $resolver // create `center` option ->setRequired('center') - ->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class ]) + ->setAllowedTypes('center', [Center::class ]) // create ``role` option ->setRequired('role') - ->setAllowedTypes('role', ['string', \Symfony\Component\Security\Core\Role\Role::class ]) + ->setAllowedTypes('role', ['string', Role::class ]) ; $resolver @@ -95,25 +97,7 @@ class ScopePickerType extends AbstractType return $this->translatableStringHelper->localize($c->getName()); }) ->setNormalizer('query_builder', function(Options $options) { - $qb = $this->scopeRepository->createQueryBuilder('s'); - $qb - // jointure to center - ->join('s.roleScopes', 'rs') - ->join('rs.permissionsGroups', 'pg') - ->join('pg.groupCenters', 'gc') - //->join('gc.users', 'user') - // add center constraint - ->where($qb->expr()->eq('IDENTITY(gc.center)', ':center')) - ->setParameter('center', $options['center']->getId()) - // role constraints - ->andWhere($qb->expr()->eq('rs.role', ':role')) - ->setParameter('role', $options['role']) - // user contraint - ->andWhere(':user MEMBER OF gc.users') - ->setParameter('user', $this->tokenStorage->getToken()->getUser()) - ; - - return $qb; + return $this->buildAccessibleScopeQuery($options['center'], $options['role']); }) ; } @@ -122,4 +106,35 @@ class ScopePickerType extends AbstractType { return EntityType::class; } + + /** + * + * @return \Doctrine\ORM\QueryBuilder + */ + protected function buildAccessibleScopeQuery(Center $center, Role $role) + { + $qb = $this->scopeRepository->createQueryBuilder('s'); + $qb + // jointure to center + ->join('s.roleScopes', 'rs') + ->join('rs.permissionsGroups', 'pg') + ->join('pg.groupCenters', 'gc') + // add center constraint + ->where($qb->expr()->eq('IDENTITY(gc.center)', ':center')) + ->setParameter('center', $center->getId()) + // role constraints + ->andWhere($qb->expr()->in('rs.role', ':roles')) + ->setParameter('roles', \array_map( + function(Role $role) { + return $role->getRole(); + }, + $this->authorizationHelper->getParentRoles($role) + )) + // user contraint + ->andWhere(':user MEMBER OF gc.users') + ->setParameter('user', $this->tokenStorage->getToken()->getUser()) + ; + + return $qb; + } } From 96cd18563b442f0a8b2421a3b3d00575e274ca05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 16 Apr 2018 17:18:59 +0200 Subject: [PATCH 06/64] include current role in ScopePickerType and UserPickerType Previously, only the "parent" roles were takent into account in those two types. This commit fixe the bug by adding the current role to the query. --- Form/Type/ScopePickerType.php | 5 ++++- Form/Type/UserPickerType.php | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Form/Type/ScopePickerType.php b/Form/Type/ScopePickerType.php index 9525e81f0..dbbc28255 100644 --- a/Form/Type/ScopePickerType.php +++ b/Form/Type/ScopePickerType.php @@ -113,6 +113,9 @@ class ScopePickerType extends AbstractType */ protected function buildAccessibleScopeQuery(Center $center, Role $role) { + $roles = $this->authorizationHelper->getParentRoles($role); + $roles[] = $role; + $qb = $this->scopeRepository->createQueryBuilder('s'); $qb // jointure to center @@ -128,7 +131,7 @@ class ScopePickerType extends AbstractType function(Role $role) { return $role->getRole(); }, - $this->authorizationHelper->getParentRoles($role) + $roles )) // user contraint ->andWhere(':user MEMBER OF gc.users') diff --git a/Form/Type/UserPickerType.php b/Form/Type/UserPickerType.php index fd7600bf9..a9a0bf62e 100644 --- a/Form/Type/UserPickerType.php +++ b/Form/Type/UserPickerType.php @@ -88,6 +88,9 @@ class UserPickerType extends AbstractType return $u->getUsername(); }) ->setNormalizer('query_builder', function(Options $options) { + $roles = $this->authorizationHelper->getParentRoles($options['role']); + $roles[] = $options['role']; + $qb = $this->userRepository->createQueryBuilder('u'); $qb // add center constraint @@ -103,7 +106,7 @@ class UserPickerType extends AbstractType 'roles', \array_map( function(Role $role) { return $role->getRole(); }, - $this->authorizationHelper->getParentRoles($options['role']) + $roles ) ) // add active constraint From 190e2f48b3a112924b42aeae84950801355a5010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 16 Apr 2018 17:20:34 +0200 Subject: [PATCH 07/64] introducer UserCircleConsistency validator This validator allow to check that the entity is consistent between user associated with the entity, and the scope. The entity is consistent if the user associated can reach the scope for the ROLE "SEE/SHOW". This is a Constraint with scope Class. Example of utilisation: ``` @UserCircleConsistency( * "CHILL_TASK_TASK_SEE", * getUserFunction="getAssignee", * path="circle" * ) class MyEntity { // ... public function getAssignee() { // return user } } ``` --- DependencyInjection/ChillMainExtension.php | 1 + Resources/config/services/validator.yml | 7 +++ Resources/translations/messages.fr.yml | 1 + Resources/translations/validators.fr.yml | 5 +- .../Entity/UserCircleConsistency.php | 52 ++++++++++++++++ .../Entity/UserCircleConsistencyValidator.php | 62 +++++++++++++++++++ 6 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 Resources/config/services/validator.yml create mode 100644 Validator/Constraints/Entity/UserCircleConsistency.php create mode 100644 Validator/Constraints/Entity/UserCircleConsistencyValidator.php diff --git a/DependencyInjection/ChillMainExtension.php b/DependencyInjection/ChillMainExtension.php index 7a5766d4c..2bd1b3f25 100644 --- a/DependencyInjection/ChillMainExtension.php +++ b/DependencyInjection/ChillMainExtension.php @@ -92,6 +92,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, $loader->load('services/pagination.yml'); $loader->load('services/export.yml'); $loader->load('services/form.yml'); + $loader->load('services/validator.yml'); } diff --git a/Resources/config/services/validator.yml b/Resources/config/services/validator.yml new file mode 100644 index 000000000..f09304375 --- /dev/null +++ b/Resources/config/services/validator.yml @@ -0,0 +1,7 @@ +services: + chill_main.validator_user_circle_consistency: + class: Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistencyValidator + arguments: + - "@chill.main.security.authorization.helper" + tags: + - { name: "validator.constraint_validator" } diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml index 06e63ce31..bf082188f 100644 --- a/Resources/translations/messages.fr.yml +++ b/Resources/translations/messages.fr.yml @@ -31,6 +31,7 @@ not valid: non valide Confirm: Confirmer Cancel: Annuler Save: Enregistrer +This form contains errors: Ce formulaire contient des erreurs 'You are going to leave a page with unsubmitted data. Are you sure you want to leave ?': "Vous allez quitter la page alors que des données n'ont pas été enregistrées. Êtes vous sûr de vouloir partir ?" diff --git a/Resources/translations/validators.fr.yml b/Resources/translations/validators.fr.yml index 4307be2fb..ba260f2c6 100644 --- a/Resources/translations/validators.fr.yml +++ b/Resources/translations/validators.fr.yml @@ -5,4 +5,7 @@ The role "%role%" should not be associated with a scope.: Le rôle "%role%" ne d "The password must contains one letter, one capitalized letter, one number and one special character as *[@#$%!,;:+\"'-/{}~=µ()£]). Other characters are allowed.": "Le mot de passe doit contenir une majuscule, une minuscule, et au moins un caractère spécial parmi *[@#$%!,;:+\"'-/{}~=µ()£]). Les autres caractères sont autorisés." The password fields must match: Les mots de passe doivent correspondre -A permission is already present for the same role and scope: Une permission est déjà présente pour le même rôle et cercle. \ No newline at end of file +A permission is already present for the same role and scope: Une permission est déjà présente pour le même rôle et cercle. + +#UserCircleConsistency +"{{ username }} is not allowed to see entities published in this circle": "{{ username }} n'est pas autorisé à voir l'élément publié dans ce cercle." \ No newline at end of file diff --git a/Validator/Constraints/Entity/UserCircleConsistency.php b/Validator/Constraints/Entity/UserCircleConsistency.php new file mode 100644 index 000000000..238aa1a7f --- /dev/null +++ b/Validator/Constraints/Entity/UserCircleConsistency.php @@ -0,0 +1,52 @@ + + * + * 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\MainBundle\Validator\Constraints\Entity; + +use Symfony\Component\Validator\Constraint; + +/** + * + * + * @Annotation + */ +class UserCircleConsistency extends Constraint +{ + public $message = "{{ username }} is not allowed to see entities published in this circle"; + + public $role; + + public $getUserFunction = 'getUser'; + + public $path = 'circle'; + + public function getDefaultOption() + { + return 'role'; + } + + public function getRequiredOptions() + { + return [ 'role' ]; + } + + public function getTargets() + { + return self::CLASS_CONSTRAINT; + } + +} diff --git a/Validator/Constraints/Entity/UserCircleConsistencyValidator.php b/Validator/Constraints/Entity/UserCircleConsistencyValidator.php new file mode 100644 index 000000000..adba2d760 --- /dev/null +++ b/Validator/Constraints/Entity/UserCircleConsistencyValidator.php @@ -0,0 +1,62 @@ + + * + * 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\MainBundle\Validator\Constraints\Entity; + +use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Chill\MainBundle\Entity\HasScopeInterface; + +/** + * + * + */ +class UserCircleConsistencyValidator extends ConstraintValidator +{ + /** + * + * @var AuthorizationHelper + */ + protected $autorizationHelper; + + function __construct(AuthorizationHelper $autorizationHelper) + { + $this->autorizationHelper = $autorizationHelper; + } + + + /** + * + * @param object $value + * @param UserCircleConsistency $constraint + */ + public function validate($value, Constraint $constraint) + { + /* @var $user \Chill\MainBundle\Entity\User */ + $user = \call_user_func([$value, $constraint->getUserFunction ]); + + if (FALSE === $this->autorizationHelper->userHasAccess($user, $value, $constraint->role)) { + $this->context + ->buildViolation($constraint->message) + ->setParameter('{{ username }}', $user->getUsername()) + ->atPath($constraint->path) + ->addViolation() + ; + } + } +} From 36c3e33cfa16316587db81ec8894dbd254192e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 17 Apr 2018 11:32:39 +0200 Subject: [PATCH 08/64] add DateIntervalType --- .../DateIntervalTransformer.php | 92 +++++++++++++++++++ Form/Type/DateIntervalType.php | 57 ++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 Form/Type/DataTransformer/DateIntervalTransformer.php create mode 100644 Form/Type/DateIntervalType.php diff --git a/Form/Type/DataTransformer/DateIntervalTransformer.php b/Form/Type/DataTransformer/DateIntervalTransformer.php new file mode 100644 index 000000000..29a81447c --- /dev/null +++ b/Form/Type/DataTransformer/DateIntervalTransformer.php @@ -0,0 +1,92 @@ + + * + * 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\MainBundle\Form\Type\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * + * + * @author Julien Fastré + */ +class DateIntervalTransformer implements DataTransformerInterface +{ + /** + * + * @param \DateInterval $value + * @throws UnexpectedTypeException + */ + public function transform($value) + { + if (empty($value)) { + return null; + } + + if (!$value instanceof \DateInterval) { + throw new UnexpectedTypeException($value, \DateInterval::class); + } + + if ($value->d > 0) { + // we check for weeks (weeks are converted to 7 days) + if ($value->d % 7 === 0) { + return [ + 'n' => $value->d / 7, + 'unit' => 'W' + ]; + } else { + return [ + 'n' => $value->d, + 'unit' => 'D' + ]; + } + } elseif ($value->m > 0) { + return [ + 'n' => $value->m, + 'unit' => 'M' + ]; + } elseif ($value->y > 0) { + return [ + 'n' => $value->y, + 'unit' => 'Y' + ]; + } + + throw new TransformationFailedException('the date interval does not ' + . 'contains any days, months or years'); + } + + public function reverseTransform($value) + { + if (empty($value) or empty($value['n'])) { + return null; + } + + $string = 'P'.$value['n'].$value['unit']; + + try { + return new \DateInterval($string); + } catch (\Exception $e) { + throw new TransformationFailedException("Could not transform value " + . "into DateInterval", 1542, $e); + } + + + } +} diff --git a/Form/Type/DateIntervalType.php b/Form/Type/DateIntervalType.php new file mode 100644 index 000000000..499c61f93 --- /dev/null +++ b/Form/Type/DateIntervalType.php @@ -0,0 +1,57 @@ + + * + * 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\MainBundle\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\Type\IntegerType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Chill\MainBundle\Form\Type\DataTransformer\DateIntervalTransformer; +use Symfony\Component\Validator\Constraints\GreaterThan; + +/** + * + * + */ +class DateIntervalType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('n', IntegerType::class, [ + 'scale' => 0, + 'constraints' => [ + new GreaterThan([ + 'value' => 0 + ]) + ] + ]) + ->add('unit', ChoiceType::class, [ + 'choices' => [ + 'Days' => 'D', + 'Weeks' => 'W', + 'Months' => 'M', + 'Years' => 'Y' + ], + 'choices_as_values' => true + ]) + ; + + $builder->addModelTransformer(new DateIntervalTransformer()); + } +} From 06bff38701a9a728d24cdeff4bfcf05ac3aee1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 17 Apr 2018 21:58:37 +0200 Subject: [PATCH 09/64] [dateinterval doctrine type] parse correctly native date interval --- Doctrine/Type/NativeDateIntervalType.php | 67 ++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/Doctrine/Type/NativeDateIntervalType.php b/Doctrine/Type/NativeDateIntervalType.php index 06143f229..fc341b3ef 100644 --- a/Doctrine/Type/NativeDateIntervalType.php +++ b/Doctrine/Type/NativeDateIntervalType.php @@ -4,6 +4,7 @@ namespace Chill\MainBundle\Doctrine\Type; use Doctrine\DBAL\Types\DateIntervalType; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\ConversionException; /** * Re-declare the date interval to use the implementation of date interval in @@ -23,5 +24,71 @@ class NativeDateIntervalType extends DateIntervalType { return 'INTERVAL'; } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateInterval) { + return $value; + } + dump($value); + try { + $strings = explode(' ', $value); + + if (count($strings) === 0) { + return null; + } + $intervalSpec = 'P'; + \reset($strings); + + do { + $intervalSpec .= $this->convertEntry($strings); + } while (next($strings) !== FALSE); + + dump($intervalSpec); + + return new \DateInterval($intervalSpec); + } catch (\Exception $exception) { + throw $this->createConversionException($value); + } + } + + private function convertEntry(&$strings) + { + $current = \current($strings); + + if (is_numeric($current)) { + $next = \next($strings); + switch($next) { + case 'year': + $unit = 'Y'; + break; + case 'mon': + case 'mons': + $unit = 'M'; + break; + case 'days': + $unit = 'D'; + break; + default: + throw $this->createConversionException($value); + } + + return $current.$unit; + + } elseif (\preg_match('/([0-9]{2}\:[0-9]{2}:[0-9]{2})/', $v) === 1) { + $tExploded = explode(':', $v); + $intervalSpec = 'T'; + $intervalSpec.= $tExploded[0].'H'; + $intervalSpec.= $tExploded[1].'M'; + $intervalSpec.= $tExploded[2].'S'; + + return $intervalSpec; + } + } + + protected function createConversionException($value) + { + return ConversionException::conversionFailedFormat($value, $this->getName(), 'xx year xx mons xx days 01:02:03', $exception); + } } From 8456045088b29429622ecccc31cbeba2f8530d08 Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 18 Apr 2018 11:40:28 +0200 Subject: [PATCH 10/64] add webpack config + export chill.js --- Resources/public/js/chill.js | 76 +++++++++++++++--------------- Resources/public/sass/_custom.scss | 30 ++++++------ Resources/views/layout.html.twig | 30 ++++++------ 3 files changed, 71 insertions(+), 65 deletions(-) diff --git a/Resources/public/js/chill.js b/Resources/public/js/chill.js index a7d661679..54d2ceb80 100644 --- a/Resources/public/js/chill.js +++ b/Resources/public/js/chill.js @@ -93,7 +93,7 @@ var chill = function() { * * @param{string} form_id An identification string of the form * @param{string} alert_message The alert message to display - * @param{boolean} check_unsaved_data If true display the alert message only when the form + * @param{boolean} check_unsaved_data If true display the alert message only when the form * contains some modified fields otherwise always display the alert when leaving * @return nothing */ @@ -123,12 +123,12 @@ var chill = function() { }); } - /** - * Mark the choices "not specified" as check by default. - * - * This function apply to `custom field choices` when the `required` + /** + * Mark the choices "not specified" as check by default. + * + * This function apply to `custom field choices` when the `required` * option is false and `expanded` is true (checkboxes or radio buttons). - * + * * @param{string} choice_name the name of the input */ function checkNullValuesInChoices(choice_name) { @@ -184,21 +184,21 @@ var chill = function() { * child) of a given form : each parent option has a category, the * child select only display options that have the same category of the * parent optionn - * - * The parent must have the class "chill-category-link-parent". - * + * + * The parent must have the class "chill-category-link-parent". + * * The children must have the class "chill-category-link-child". Each option * of the parent must have the attribute `data-link-category`, with the value of * the connected option in parent. - * + * * Example : - * + * * ```html * - * + * * * ``` - * + * * TODO ECRIRE LA DOC METTRE LES TESTS DANS git : * tester que init est ok : - quand vide @@ -224,7 +224,7 @@ var chill = function() { form.old_category = null; form.link_parent = $(form).find('.chill-category-link-parent'); form.link_child = $(form).find('.chill-category-link-child'); - + // check if the parent allow multiple or single results parent_multiple = $(form).find('.chill-category-link-parent').get(0).multiple; // if we use select2, parent_multiple will be `undefined` @@ -233,10 +233,10 @@ var chill = function() { // we suppose that multiple is false (old behaviour) parent_multiple = false } - + $(form.link_parent).addClass('select2'); $(form.link_parant).select2({allowClear: true}); // it is weird: when I fix the typo here, the whole stuff does not work anymore... - + if (parent_multiple == false) { form.old_category = null; @@ -279,9 +279,9 @@ var chill = function() { } }); } else { - var i=0, + var i=0, selected_items = $(form.link_parent).find(':selected'); - + form.old_categories = []; for (i=0;i < selected_items.length; i++) { form.old_categories.push(selected_items[i].value); @@ -314,13 +314,13 @@ var chill = function() { }); form.link_parent.change(function() { - var new_categories = [], + var new_categories = [], selected_items = $(form.link_parent).find(':selected'), visible; for (i=0;i < selected_items.length; i++) { new_categories.push(selected_items[i].value); } - + if(new_categories != form.old_categories) { $(form.link_child).find('option') .each(function(i,e) { @@ -352,16 +352,16 @@ var chill = function() { form.old_categories = new_categories; } }); - } + } }); } - + function _displayHideTargetWithCheckbox(checkbox) { var target = checkbox.dataset.displayTarget, hideableElements; - + hideableElements = document.querySelectorAll('[data-display-show-hide="' + target + '"]'); - + if (checkbox.checked) { for (let i=0; i < hideableElements.length; i = i+1) { hideableElements[i].style.display = "unset"; @@ -371,36 +371,36 @@ var chill = function() { hideableElements[i].style.display = "none"; } } - + } - + /** - * create an interaction between a checkbox and element to show if the + * create an interaction between a checkbox and element to show if the * checkbox is checked, or hide if the checkbox is not checked. - * - * The checkbox must have the data `data-display-target` with an id, + * + * The checkbox must have the data `data-display-target` with an id, * and the parts to show/hide must have the data `data-display-show-hide` * with the same value. - * - * Example : - * + * + * Example : + * * ``` * - * + * *
* *
* ``` - * + * * Hint: for forms in symfony, you could use the `id` of the form element, * accessible through `{{ form.vars.id }}`. This id should be unique. - * - * + * + * * @returns {undefined} */ function listenerDisplayCheckbox() { var elements = document.querySelectorAll("[data-display-target]"); - + for (let i=0; i < elements.length; i = i+1) { elements[i].addEventListener("change", function(e) { _displayHideTargetWithCheckbox(e.target); @@ -421,3 +421,5 @@ var chill = function() { listenerDisplayCheckbox: listenerDisplayCheckbox, }; } (); + +export { chill }; diff --git a/Resources/public/sass/_custom.scss b/Resources/public/sass/_custom.scss index 10f6d5e2e..b355a533e 100644 --- a/Resources/public/sass/_custom.scss +++ b/Resources/public/sass/_custom.scss @@ -1,4 +1,6 @@ // YOUR CUSTOM SCSS +@import 'custom/config/colors'; +@import 'custom/config/variables'; @import 'custom/timeline'; @import 'custom/mixins/entity'; @import 'custom/activity'; @@ -11,7 +13,7 @@ @import 'custom/flash_messages'; -html,body { +html,body { min-height:100%; font-family: 'Open Sans'; } @@ -83,9 +85,9 @@ ul.custom_fields.choice li { font-family: 'Open Sans'; font-weight: 300; } - - a { - color: white; + + a { + color: white; text-decoration: underline; } } @@ -97,7 +99,7 @@ ul.custom_fields.choice li { display: inline-block; text-align: center; } - + .separator { margin-left: 0.2em; margin-right: 0.2em; @@ -107,7 +109,7 @@ ul.custom_fields.choice li { .open_sansbold { font-family: 'Open Sans'; font-weight: bold; - + } @@ -140,21 +142,21 @@ div.input_with_post_text input { dl.chill_report_view_data, dl.chill_view_data { - + dt { margin-top: 1.5em; color: $chill-blue; } - + dd { padding-left: 1.5em; margin-top: 0.2em; - + ul { padding-left: 0; } } - + } @@ -164,13 +166,13 @@ blockquote.chill-user-quote { padding: 0.5em 10px; quotes: "\201C""\201D""\2018""\2019"; background-color: $chill-llight-gray; - - + + p { display: inline; } - + } .chill-no-data-statement { font-style: italic; - + } diff --git a/Resources/views/layout.html.twig b/Resources/views/layout.html.twig index e6d193894..effc1624e 100644 --- a/Resources/views/layout.html.twig +++ b/Resources/views/layout.html.twig @@ -1,5 +1,5 @@ {# - * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, + * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, / * * This program is free software: you can redistribute it and/or modify @@ -22,19 +22,19 @@ - + {{ installation.name }} - {% block title %}{% endblock %} - - {% stylesheets output="css/all.css" filter="cssrewrite" + + + {% block css%}{% endblock %} @@ -107,7 +107,7 @@ {% endfor %} - + {% for flashMessage in app.session.flashbag.get('error') %}
@@ -115,14 +115,14 @@
{% endfor %} - + {% for flashMessage in app.session.flashbag.get('notice') %}
{{ flashMessage|raw }}
- {% endfor %} + {% endfor %} {% block content %}
@@ -137,12 +137,12 @@
- +
{{ chill_widget('homepage', {} ) }}
{% endblock %} - + {% endblock %} @@ -150,8 +150,8 @@

{{ 'This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License'|trans|raw }}
{{ 'User manual'|trans }}

- - {% javascripts output="js/libs.js" + + + + + {% endspaceless %} {% endblock choice_widget_expanded %} @@ -90,7 +94,9 @@ {%- endif -%} {% endfor %} {% endspaceless %} {% endblock choice_with_other_widget %} diff --git a/chill.webpack.config.js b/chill.webpack.config.js index b14331e57..e6f4c8be8 100644 --- a/chill.webpack.config.js +++ b/chill.webpack.config.js @@ -18,10 +18,11 @@ import {chill} from './Resources/public/js/chill.js'; global.chill = chill; // css -require('./Resources/public/sass/_custom.scss'); +require('./Resources/public/sass/scratch.scss'); require('./Resources/public/css/chillmain.css'); require('./Resources/public/css/pikaday.css'); -require('./Resources/public/css/scratch.css'); +//require('./Resources/public/css/scratch.css'); //require('./Resources/public/css/select2/select2.css'); +require('select2/dist/css/select2.css'); // img From da821f50998114d3faa9765cdef9caea3bc73e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 4 May 2018 22:49:04 +0200 Subject: [PATCH 31/64] enable fixtures with sf3 --- DependencyInjection/ChillMainExtension.php | 1 + Resources/config/services/fixtures.yml | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 Resources/config/services/fixtures.yml diff --git a/DependencyInjection/ChillMainExtension.php b/DependencyInjection/ChillMainExtension.php index ea6214ae5..44c2d2034 100644 --- a/DependencyInjection/ChillMainExtension.php +++ b/DependencyInjection/ChillMainExtension.php @@ -96,6 +96,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, $loader->load('services/widget.yml'); $loader->load('services/controller.yml'); $loader->load('services/routing.yml'); + $loader->load('services/fixtures.yml'); } public function getConfiguration(array $config, ContainerBuilder $container) diff --git a/Resources/config/services/fixtures.yml b/Resources/config/services/fixtures.yml new file mode 100644 index 000000000..b3ef847e4 --- /dev/null +++ b/Resources/config/services/fixtures.yml @@ -0,0 +1,4 @@ +services: + Chill\MainBundle\DataFixtures\ORM\: + resource: ../../../DataFixtures/ORM + tags: [ 'doctrine.fixture.orm' ] From 50b079c67686995d7881dd2f2fec97550d6b9bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 4 May 2018 22:58:06 +0200 Subject: [PATCH 32/64] adding knp to test fixtures --- Resources/test/Fixtures/App/app/AppKernel.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/test/Fixtures/App/app/AppKernel.php b/Resources/test/Fixtures/App/app/AppKernel.php index 4b9643491..7f7b0de1f 100644 --- a/Resources/test/Fixtures/App/app/AppKernel.php +++ b/Resources/test/Fixtures/App/app/AppKernel.php @@ -17,6 +17,7 @@ class AppKernel extends Kernel new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(), new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(), new Symfony\Bundle\MonologBundle\MonologBundle(), + new Knp\Bundle\MenuBundle\KnpMenuBundle(), ); } From d684851f36eec8c0682587aad2a0d0a6eb54d2ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 8 May 2018 10:06:58 +0200 Subject: [PATCH 33/64] add alias for AuthorizationHelper --- Resources/config/services.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/config/services.yml b/Resources/config/services.yml index b2013e5ee..c49265b41 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -60,6 +60,7 @@ services: arguments: - "@security.role_hierarchy" - "%security.role_hierarchy.roles%" + Chill\MainBundle\Security\Authorization\AuthorizationHelper: '@chill.main.security.authorization.helper' chill.main.role_provider: class: Chill\MainBundle\Security\RoleProvider From 6f73c9ee090e968ef97094fcd9ee4a9f2cd5a839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 8 May 2018 22:56:03 +0200 Subject: [PATCH 34/64] fix errors to user picker: add placeholder and allow empty --- Form/Type/UserPickerType.php | 2 +- .../Constraints/Entity/UserCircleConsistencyValidator.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Form/Type/UserPickerType.php b/Form/Type/UserPickerType.php index a9a0bf62e..b253ce393 100644 --- a/Form/Type/UserPickerType.php +++ b/Form/Type/UserPickerType.php @@ -83,7 +83,7 @@ class UserPickerType extends AbstractType $resolver ->setDefault('class', User::class) - ->setDefault('empty_data', $this->tokenStorage->getToken()->getUser()) + ->setDefault('placeholder', 'Choose an user') ->setDefault('choice_label', function(User $u) { return $u->getUsername(); }) diff --git a/Validator/Constraints/Entity/UserCircleConsistencyValidator.php b/Validator/Constraints/Entity/UserCircleConsistencyValidator.php index adba2d760..c8ec72b50 100644 --- a/Validator/Constraints/Entity/UserCircleConsistencyValidator.php +++ b/Validator/Constraints/Entity/UserCircleConsistencyValidator.php @@ -50,6 +50,10 @@ class UserCircleConsistencyValidator extends ConstraintValidator /* @var $user \Chill\MainBundle\Entity\User */ $user = \call_user_func([$value, $constraint->getUserFunction ]); + if ($user === null) { + return; + } + if (FALSE === $this->autorizationHelper->userHasAccess($user, $value, $constraint->role)) { $this->context ->buildViolation($constraint->message) From 42f17f11e00e242a9286feec472da7fd479da50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 9 May 2018 00:05:29 +0200 Subject: [PATCH 35/64] set same layout to user menu and section menu --- .../sass/custom/modules/_navigation.scss | 116 ++++++++++++------ Resources/views/Menu/user.html.twig | 19 ++- 2 files changed, 90 insertions(+), 45 deletions(-) diff --git a/Resources/public/sass/custom/modules/_navigation.scss b/Resources/public/sass/custom/modules/_navigation.scss index dc5e889e0..be28d0348 100644 --- a/Resources/public/sass/custom/modules/_navigation.scss +++ b/Resources/public/sass/custom/modules/_navigation.scss @@ -1,52 +1,88 @@ .navigation { - background-color: $chill-blue; + background-color: $chill-blue; - a.more:after { - color: $chill-dark-gray; - } + a.more:after { + color: $chill-dark-gray; + } - li.nav-link2 { - a { - margin-bottom: 2px; - } + li.nav-link2 { + a { + margin-bottom: 2px; + } - &.lang-selection { - color: $chill-light-gray; - font-size: 0.7em; - - a.more:after { + &.lang-selection { color: $chill-light-gray; - } - } + font-size: 0.7em; - ul { - top: 58px; + a.more:after { + color: $chill-light-gray; + } + } - a { - padding-left: 0; - } - } - } + ul { + top: 58px; - div.nav, div.navigation-search { - float: right; + a { + padding-left: 0; + } + } + } - input[type=search] { - padding: 0.2em; - float: left; + div.nav, div.navigation-search { + float: right; - border: none; - } - - button { - color: $chill-light-gray; - background-color: $chill-blue; - padding: 0 0 0 7px; - top: inherit; - font-size: 1.2em; - position: unset; - float: left; - } - } + input[type=search] { + padding: 0.2em; + float: left; + + border: none; + } + + button { + color: $chill-light-gray; + background-color: $chill-blue; + padding: 0 0 0 7px; + top: inherit; + font-size: 1.2em; + position: unset; + float: left; + } + } + + li.user-menu { + min-width: 14rem; + } + + ul.user-menu-list { + + li.user-menu__entry { + display: block; + background-color: $chill-dark-gray; + border-bottom: 1px solid #FFF; + padding-top: 0; + padding-bottom: 0; + line-height: 2; + } + + li.user-menu__entry--warning-entry { + background-color: $chill-red; + font-weight: 700; + } + } + + span.notification-counter { + display: inline-block; + padding: .25em .6em .25rem .6em; + font-size: 100%; + line-height: 1; + text-align: center; + white-space: nowrap; + + border-radius: 10rem; + background-color: $chill-red; + color: $white; + font-weight: 700; + margin-left: .5rem; + } } \ No newline at end of file diff --git a/Resources/views/Menu/user.html.twig b/Resources/views/Menu/user.html.twig index f79b47e78..7f5156378 100644 --- a/Resources/views/Menu/user.html.twig +++ b/Resources/views/Menu/user.html.twig @@ -16,18 +16,27 @@ * along with this program. If not, see . #} -