diff --git a/Controller/DocumentPersonController.php b/Controller/DocumentPersonController.php index 5a5464f2c..56b6feb91 100644 --- a/Controller/DocumentPersonController.php +++ b/Controller/DocumentPersonController.php @@ -11,6 +11,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Chill\PersonBundle\Entity\Person; use Symfony\Component\Security\Core\Role\Role; +use Chill\PersonBundle\Security\Authorization\PersonVoter; +use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter; /** * @Route("/{_locale}/person/{person}/document") @@ -30,11 +32,11 @@ class DocumentPersonController extends Controller throw $this->createNotFoundException('Person not found'); } - $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); + $this->denyAccessUnlessGranted(PersonVoter::SEE, $person); $reachableScopes = $this->get('chill.main.security.authorization.helper') ->getReachableScopes( - $this->getUser(), new Role('CHILL_PERSON_DOCUMENT_SEE'), + $this->getUser(), new Role(PersonDocumentVoter::SEE), $person->getCenter()); $documents = $em @@ -61,11 +63,10 @@ class DocumentPersonController extends Controller throw $this->createNotFoundException('person not found'); } - $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); + $this->denyAccessUnlessGranted(PersonVoter::SEE, $person); - $user = $this->get('security.context')->getToken()->getUser(); $document = new PersonDocument(); - $document->setUser($user); + $document->setUser($this->getUser()); $document->setPerson($person); $document->setDate(new \DateTime('Now')); @@ -115,8 +116,7 @@ class DocumentPersonController extends Controller $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); $this->denyAccessUnlessGranted('CHILL_PERSON_DOCUMENT_UPDATE', $document); - $user = $this->get('security.context')->getToken()->getUser(); - $document->setUser($user); + $document->setUser($this->getUser()); $document->setDate(new \DateTime('Now')); $form = $this->createForm( diff --git a/DependencyInjection/ChillDocStoreExtension.php b/DependencyInjection/ChillDocStoreExtension.php index 167b6aa75..62ab5b480 100644 --- a/DependencyInjection/ChillDocStoreExtension.php +++ b/DependencyInjection/ChillDocStoreExtension.php @@ -7,6 +7,7 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; +use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter; /** * This is the class that loads and manages your bundle configuration @@ -30,6 +31,7 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf public function prepend(ContainerBuilder $container) { $this->prependRoute($container); + $this->prependAuthorization($container); } protected function prependRoute(ContainerBuilder $container) @@ -43,4 +45,16 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf ) )); } + + protected function prependAuthorization(ContainerBuilder $container) + { + $container->prependExtensionConfig('security', array( + 'role_hierarchy' => array( + PersonDocumentVoter::UPDATE => [PersonDocumentVoter::SEE_DETAILS], + PersonDocumentVoter::CREATE => [PersonDocumentVoter::SEE_DETAILS], + PersonDocumentVoter::DELETE => [PersonDocumentVoter::SEE_DETAILS], + PersonDocumentVoter::SEE_DETAILS => [PersonDocumentVoter::SEE], + ) + )); + } } diff --git a/Entity/Document.php b/Entity/Document.php index c1724fd4b..cbabcf850 100644 --- a/Entity/Document.php +++ b/Entity/Document.php @@ -28,7 +28,7 @@ class Document implements HasScopeInterface /** * @ORM\Column(type="text") */ - private $description; + private $description = ''; /** * @ORM\ManyToOne(targetEntity="Chill\DocStoreBundle\Entity\DocumentCategory") @@ -83,9 +83,9 @@ class Document implements HasScopeInterface return $this->description; } - public function setDescription(string $description): self + public function setDescription($description): self { - $this->description = $description; + $this->description = (string) $description; return $this; } diff --git a/Form/PersonDocumentType.php b/Form/PersonDocumentType.php index b9d68693e..28cc582f5 100644 --- a/Form/PersonDocumentType.php +++ b/Form/PersonDocumentType.php @@ -17,12 +17,14 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Doctrine\Common\Persistence\ObjectManager; use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Form\Type\ScopePickerType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\Extension\Core\Type\TextareaType; class PersonDocumentType extends AbstractType { - use AppendScopeChoiceTypeTrait; - /** * the user running this form * @@ -49,18 +51,9 @@ class PersonDocumentType extends AbstractType protected $translatableStringHelper; public function __construct( - TokenStorageInterface $tokenStorage, - AuthorizationHelper $authorizationHelper, - ObjectManager $om, TranslatableStringHelper $translatableStringHelper ) { - if (!$tokenStorage->getToken()->getUser() instanceof User) { - throw new \RuntimeException("you should have a valid user"); - } - $this->user = $tokenStorage->getToken()->getUser(); - $this->authorizationHelper = $authorizationHelper; - $this->om = $om; $this->translatableStringHelper = $translatableStringHelper; } @@ -68,12 +61,16 @@ class PersonDocumentType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('title') - ->add('description') + ->add('title', TextType::class) + ->add('description', TextareaType::class, [ + 'required' => false + ]) ->add('content') - //->add('center') - ->add('scope') - ->add('date', 'date', array('required' => false, 'widget' => 'single_text', 'format' => 'dd-MM-yyyy')) + ->add('scope', ScopePickerType::class, [ + 'center' => $options['center'], + 'role' => $options['role'] + ]) + ->add('date', ChillDateType::class) ->add('category', EntityType::class, array( 'class' => 'ChillDocStoreBundle:DocumentCategory', 'query_builder' => function (EntityRepository $er) { @@ -87,10 +84,6 @@ class PersonDocumentType extends AbstractType )) ; - $this->appendScopeChoices($builder, $options['role'], - $options['center'], $this->user, $this->authorizationHelper, - $this->translatableStringHelper, $this->om); - } public function configureOptions(OptionsResolver $resolver) @@ -98,7 +91,10 @@ class PersonDocumentType extends AbstractType $resolver->setDefaults([ 'data_class' => Document::class, ]); - - $this->appendScopeChoicesOptions($resolver); + + $resolver->setRequired(['role', 'center']) + ->setAllowedTypes('role', [ \Symfony\Component\Security\Core\Role\Role::class ]) + ->setAllowedTypes('center', [ \Chill\MainBundle\Entity\Center::class ]) + ; } } diff --git a/Resources/config/services.yml b/Resources/config/services.yml index e8e185fa9..4439c8dbb 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -11,9 +11,6 @@ services: Chill\DocStoreBundle\Form\PersonDocumentType: class: Chill\DocStoreBundle\Form\PersonDocumentType arguments: - - "@security.token_storage" - - "@chill.main.security.authorization.helper" - - "@doctrine.orm.entity_manager" - "@chill.main.helper.translatable_string" tags: - { name: form.type, alias: chill_docstorebundle_form_document } diff --git a/Resources/migrations/Version20180605102533.php b/Resources/migrations/Version20180605102533.php new file mode 100644 index 000000000..5661277e2 --- /dev/null +++ b/Resources/migrations/Version20180605102533.php @@ -0,0 +1,39 @@ +abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.'); + + $this->addSql('CREATE SCHEMA chill_doc'); + $this->addSql('CREATE SEQUENCE chill_doc.person_document_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_doc.document_category (bundle_id VARCHAR(255) NOT NULL, id_inside_bundle INT NOT NULL, document_class VARCHAR(255) NOT NULL, name JSON NOT NULL, PRIMARY KEY(bundle_id, id_inside_bundle))'); + $this->addSql('COMMENT ON COLUMN chill_doc.document_category.name IS \'(DC2Type:json_array)\''); + $this->addSql('CREATE TABLE chill_doc.person_document (id INT NOT NULL, category_bundle_id VARCHAR(255) DEFAULT NULL, category_id_inside_bundle INT DEFAULT NULL, scope_id INT DEFAULT NULL, user_id INT DEFAULT NULL, person_id INT DEFAULT NULL, title TEXT NOT NULL, description TEXT NOT NULL, content TEXT NOT NULL, date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_41DA53C369A0BE36EF62EFC ON chill_doc.person_document (category_bundle_id, category_id_inside_bundle)'); + $this->addSql('CREATE INDEX IDX_41DA53C682B5931 ON chill_doc.person_document (scope_id)'); + $this->addSql('CREATE INDEX IDX_41DA53CA76ED395 ON chill_doc.person_document (user_id)'); + $this->addSql('CREATE INDEX IDX_41DA53C217BBB47 ON chill_doc.person_document (person_id)'); + $this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C369A0BE36EF62EFC FOREIGN KEY (category_bundle_id, category_id_inside_bundle) REFERENCES chill_doc.document_category (bundle_id, id_inside_bundle) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C682B5931 FOREIGN KEY (scope_id) REFERENCES scopes (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53CA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema) : void + { + $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.'); + + $this->addSql('DROP SCHEMA chill_doc CASCADE'); + + } +} diff --git a/Resources/views/PersonDocument/_form.html.twig b/Resources/views/PersonDocument/_form.html.twig index 56f80b630..d5e483e36 100644 --- a/Resources/views/PersonDocument/_form.html.twig +++ b/Resources/views/PersonDocument/_form.html.twig @@ -1,4 +1 @@ -{{ form_start(form) }} - {{ form_widget(form) }} - -{{ form_end(form) }} +{{ form_widget(form) }} diff --git a/Resources/views/PersonDocument/edit.html.twig b/Resources/views/PersonDocument/edit.html.twig index db39eca56..a81705328 100644 --- a/Resources/views/PersonDocument/edit.html.twig +++ b/Resources/views/PersonDocument/edit.html.twig @@ -23,11 +23,26 @@ {% block personcontent %}

{{ 'Edit Document' | trans }}

+ {{ form_start(form) }} {{ include('ChillDocStoreBundle:PersonDocument:_form.html.twig', {'button_label': 'Update'}) }} - - {{ 'Back to list' | trans }} - + + + {{ form_end(form) }} + - {{ include('ChillDocStoreBundle:PersonDocument:_delete_form.html.twig') }} {% endblock %} diff --git a/Resources/views/PersonDocument/index.html.twig b/Resources/views/PersonDocument/index.html.twig index acedd7175..50cd0579a 100644 --- a/Resources/views/PersonDocument/index.html.twig +++ b/Resources/views/PersonDocument/index.html.twig @@ -28,10 +28,8 @@ {{ 'Title' | trans }} - {{ 'Description' | trans }} - {{ 'Content' | trans }} - {{ 'Last modification by' | trans }} - {{ 'Last update' | trans }} + {{ 'Category'|trans }} + {{ 'Circle' | trans }} {{ 'Actions' | trans }} @@ -39,28 +37,42 @@ {% for document in documents %} {{ document.title }} - {{ document.description }} - {{ document.content }} - {{ document.user }} - {{ document.date ? document.date|date('Y-m-d H:i:s') : '' }} + {{ document.category.name|localize_translatable_string }} + {{ document.scope.name|localize_translatable_string }} - - show - - - edit - + {% else %} - no records found + {{ 'Any document found'|trans }} {% endfor %} - - {{ 'Create new' | trans }} - + {% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %} + + {% endif %} {% endblock %} diff --git a/Resources/views/PersonDocument/new.html.twig b/Resources/views/PersonDocument/new.html.twig index 2a423051d..0e7ee778e 100644 --- a/Resources/views/PersonDocument/new.html.twig +++ b/Resources/views/PersonDocument/new.html.twig @@ -23,9 +23,18 @@ {% block personcontent %}

{{ 'Create new Document' | trans }}

+ {{ form_start(form) }} {{ include('ChillDocStoreBundle:PersonDocument:_form.html.twig') }} - - {{ 'back to list' | trans }} - + + {{ form_end(form) }} {% endblock %} diff --git a/Security/Authorization/PersonDocumentVoter.php b/Security/Authorization/PersonDocumentVoter.php index 55302b77c..47bfed38b 100644 --- a/Security/Authorization/PersonDocumentVoter.php +++ b/Security/Authorization/PersonDocumentVoter.php @@ -21,12 +21,14 @@ namespace Chill\DocStoreBundle\Security\Authorization; use Chill\MainBundle\Security\Authorization\AbstractChillVoter; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; -use Chill\MainBundle\Security\ProvideRoleInterface; +use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; +use Chill\DocStoreBundle\Entity\PersonDocument; +use Chill\PersonBundle\Entity\Person; /** * */ -class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleInterface +class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface { const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE'; const SEE = 'CHILL_PERSON_DOCUMENT_SEE'; @@ -44,16 +46,29 @@ class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleInter { $this->helper = $helper; } - - protected function getSupportedAttributes() + + public function getRoles() { - return array(self::CREATE, self::SEE, self::UPDATE, self::DELETE, - self::SEE_DETAILS); + return [ + self::CREATE, + self::SEE, + self::SEE_DETAILS, + self::UPDATE, + self::DELETE + ]; } - - protected function getSupportedClasses() + + protected function supports($attribute, $subject) { - return array('Chill\DocStoreBundle\Entity\PersonDocument'); + if (\in_array($attribute, $this->getRoles()) && $subject instanceof PersonDocument) { + return true; + } + + if ($subject instanceof Person && $attribute === self::CREATE) { + return true; + } + + return false; } protected function isGranted($attribute, $report, $user = null) @@ -65,11 +80,6 @@ class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleInter return $this->helper->userHasAccess($user, $report, $attribute); } - public function getRoles() - { - return $this->getSupportedAttributes(); - } - public function getRolesWithoutScope() { return array();