improve CRUD and switch to symfony3

This commit is contained in:
Julien Fastré 2018-06-05 14:53:30 +02:00
parent a1ee85b0c0
commit eda8f2c033
11 changed files with 167 additions and 78 deletions

View File

@ -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(

View File

@ -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],
)
));
}
}

View File

@ -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;
}

View File

@ -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)
@ -99,6 +92,9 @@ class PersonDocumentType extends AbstractType
'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 ])
;
}
}

View File

@ -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 }

View File

@ -0,0 +1,39 @@
<?php declare(strict_types=1);
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Create schema for chill_doc
*/
final class Version20180605102533 extends AbstractMigration
{
public function up(Schema $schema) : void
{
$this->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');
}
}

View File

@ -1,4 +1 @@
{{ form_start(form) }}
{{ form_widget(form) }}
<button class="sc-button bt-create">{{ button_label|default('Save') }}</button>
{{ form_end(form) }}
{{ form_widget(form) }}

View File

@ -23,11 +23,26 @@
{% block personcontent %}
<h1>{{ 'Edit Document' | trans }}</h1>
{{ form_start(form) }}
{{ include('ChillDocStoreBundle:PersonDocument:_form.html.twig', {'button_label': 'Update'}) }}
<a href="{{ path('person_document_index', {'person': person.id}) }} " class="sc-button">
{{ 'Back to list' | trans }}
</a>
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="sc-button bt-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li class="edit">
<button class="sc-button bt-edit">{{ 'Edit'|trans }}</button>
</li>
{% if is_granted('CHILL_PERSON_DOCUMENT_DELETE', document) %}
<li class="delete">
{{ include('ChillDocStoreBundle:PersonDocument:_delete_form.html.twig') }}
</li>
{% endif %}
</ul>
{{ form_end(form) }}
{{ include('ChillDocStoreBundle:PersonDocument:_delete_form.html.twig') }}
{% endblock %}

View File

@ -28,10 +28,8 @@
<thead>
<tr>
<th>{{ 'Title' | trans }}</th>
<th>{{ 'Description' | trans }}</th>
<th>{{ 'Content' | trans }}</th>
<th>{{ 'Last modification by' | trans }}</th>
<th>{{ 'Last update' | trans }}</th>
<th>{{ 'Category'|trans }}</th>
<th>{{ 'Circle' | trans }}</th>
<th>{{ 'Actions' | trans }}</th>
</tr>
</thead>
@ -39,28 +37,42 @@
{% for document in documents %}
<tr>
<td>{{ document.title }}</td>
<td>{{ document.description }}</td>
<td>{{ document.content }}</td>
<td>{{ document.user }}</td>
<td>{{ document.date ? document.date|date('Y-m-d H:i:s') : '' }}</td>
<td>{{ document.category.name|localize_translatable_string }}</td>
<td>{{ document.scope.name|localize_translatable_string }}</td>
<td>
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="sc-button">
show
</a>
<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="sc-button bt-edit">
edit
</a>
<ul class="record_actions">
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
<li>
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="sc-button">
{{ 'See'|trans }}
</a>
</li>
{% endif %}
{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %}
<li>
<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="sc-button bt-edit">
{{ 'Edit'|trans }}
</a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% else %}
<tr>
<td colspan="9">no records found</td>
<td colspan="9"><span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('person_document_new', {'person': person.id}) }}" class="sc-button bt-create">
{{ 'Create new' | trans }}
</a>
{% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %}
<ul class="record_actions">
<li class="create">
<a href="{{ path('person_document_new', {'person': person.id}) }}" class="sc-button bt-create">
{{ 'Create new document' | trans }}
</a>
</li>
</ul>
{% endif %}
{% endblock %}

View File

@ -23,9 +23,18 @@
{% block personcontent %}
<h1>{{ 'Create new Document' | trans }}</h1>
{{ form_start(form) }}
{{ include('ChillDocStoreBundle:PersonDocument:_form.html.twig') }}
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="sc-button bt-create">
{{ 'back to list' | trans }}
</a>
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="sc-button bt-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li class="create">
<button class="sc-button bt-create">{{ 'Create'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
{% endblock %}

View File

@ -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';
@ -45,15 +47,28 @@ 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();