Merge branch 'master' into accourse_impr

This commit is contained in:
Mathieu Jaumotte 2021-09-29 23:51:56 +02:00
commit 5de05c0ee9
63 changed files with 1498 additions and 891 deletions

View File

@ -32,7 +32,8 @@
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude>
</testsuite>
<testsuite name="AsideActivityBundle">
<directory suffix="Test.php">src/Bundle/ChillAsideActivityBundle/src/Tests/</directory>
<directory suffix="Test.php">src/Bundle/ChillAsideActivityBundle/src/Tests/</directory>
</testsuite>
<testsuite name="CalendarBundle">
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
</testsuite>

View File

@ -29,17 +29,19 @@ class ListenToActivityCreate
$activityData = $request->query->get('activityData');
if (array_key_exists('calendarId', $activityData)) {
$calendarId = $activityData['calendarId'];
// Attach the activity to the calendar
$em = $event->getObjectManager();
$calendar = $em->getRepository(\Chill\CalendarBundle\Entity\Calendar::class)->find($calendarId);
$calendar->setActivity($activity);
$em->persist($calendar);
$em->flush();
}
}
// Attach the activity to the calendar
$em = $event->getObjectManager();
$calendar = $em->getRepository(\Chill\CalendarBundle\Entity\Calendar::class)->find($calendarId);
$calendar->setActivity($activity);
$em->persist($calendar);
$em->flush();
}
}

View File

@ -0,0 +1,199 @@
<?php
namespace Chill\DocStoreBundle\Controller;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Form\AccompanyingCourseDocumentType;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class DocumentAccompanyingCourseController
*
* @package Chill\DocStoreBundle\Controller
* @Route("/{_locale}/parcours/{course}/document")
*
* TODO faire un controller abstrait ?
*/
class DocumentAccompanyingCourseController extends AbstractController
{
/**
*
* @var TranslatorInterface
*/
protected $translator;
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var AuthorizationHelper
*/
protected $authorizationHelper;
/**
* DocumentAccompanyingCourseController constructor.
* @param TranslatorInterface $translator
* @param EventDispatcherInterface $eventDispatcher
* @param AuthorizationHelper $authorizationHelper
*/
public function __construct(
TranslatorInterface $translator,
EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper
) {
$this->translator = $translator;
$this->eventDispatcher = $eventDispatcher;
$this->authorizationHelper = $authorizationHelper;
}
/**
* @Route("/", name="accompanying_course_document_index", methods="GET")
*/
public function index(AccompanyingPeriod $course): Response
{
$em = $this->getDoctrine()->getManager();
if ($course === NULL) {
throw $this->createNotFoundException('Accompanying period not found');
}
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $course);
$documents = $em
->getRepository("ChillDocStoreBundle:AccompanyingCourseDocument")
->findBy(
['course' => $course],
['date' => 'DESC']
);
return $this->render(
'ChillDocStoreBundle:AccompanyingCourseDocument:index.html.twig',
[
'documents' => $documents,
'accompanyingCourse' => $course
]);
}
/**
* @Route("/new", name="accompanying_course_document_new", methods="GET|POST")
*/
public function new(Request $request, AccompanyingPeriod $course): Response
{
if ($course === NULL) {
throw $this->createNotFoundException('Accompanying period not found');
}
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $course);
$document = new AccompanyingCourseDocument();
$document->setUser($this->getUser());
$document->setCourse($course);
$document->setDate(new \DateTime('Now'));
$form = $this->createForm(AccompanyingCourseDocumentType::class, $document);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->denyAccessUnlessGranted(
'CHILL_ACCOMPANYING_COURSE_DOCUMENT_CREATE', $document,
'creation of this activity not allowed');
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$em->flush();
$this->addFlash('success', $this->translator->trans("The document is successfully registered"));
return $this->redirectToRoute('accompanying_course_document_index', ['course' => $course->getId()]);
} elseif ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->translator->trans("This form contains errors"));
}
return $this->render('ChillDocStoreBundle:AccompanyingCourseDocument:new.html.twig', [
'document' => $document,
'form' => $form->createView(),
'accompanyingCourse' => $course,
]);
}
/**
* @Route("/{id}", name="accompanying_course_document_show", methods="GET")
*/
public function show(AccompanyingPeriod $course, AccompanyingCourseDocument $document): Response
{
$this->denyAccessUnlessGranted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', $course);
$this->denyAccessUnlessGranted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE', $document);
return $this->render(
'ChillDocStoreBundle:AccompanyingCourseDocument:show.html.twig',
['document' => $document, 'accompanyingCourse' => $course]);
}
/**
* @Route("/{id}/edit", name="accompanying_course_document_edit", methods="GET|POST")
*/
public function edit(Request $request, AccompanyingPeriod $course, AccompanyingCourseDocument $document): Response
{
$this->denyAccessUnlessGranted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', $course);
$this->denyAccessUnlessGranted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', $document);
$document->setUser($this->getUser());
$document->setDate(new \DateTime('Now'));
$form = $this->createForm(
AccompanyingCourseDocumentType::class, $document);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
$this->addFlash('success', $this->translator->trans("The document is successfully updated"));
return $this->redirectToRoute(
'accompanying_course_document_edit',
['id' => $document->getId(), 'course' => $course->getId()]);
} elseif ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->translator->trans("This form contains errors"));
}
return $this->render(
'ChillDocStoreBundle:AccompanyingCourseDocument:edit.html.twig',
[
'document' => $document,
'form' => $form->createView(),
'accompanyingCourse' => $course,
]);
}
/**
* @Route("/{id}", name="accompanying_course_document_delete", methods="DELETE")
*/
public function delete(Request $request, AccompanyingPeriod $course, AccompanyingCourseDocument $document): Response
{
$this->denyAccessUnlessGranted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', $course);
$this->denyAccessUnlessGranted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_DELETE', $document);
if ($this->isCsrfTokenValid('delete'.$document->getId(), $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$em->remove($document);
$em->flush();
}
return $this->redirectToRoute(
'accompanying_course_document_index', ['accompanyingCourse' => $course->getId()]);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Chill\DocStoreBundle\Entity;
use Chill\DocStoreBundle\Entity\Document;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\Table("chill_doc.accompanyingcourse_document")
*/
class AccompanyingCourseDocument extends Document
{
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
* @ORM\JoinColumn(nullable=false)
*/
private ?AccompanyingPeriod $course = null;
public function getCourse(): ?AccompanyingPeriod
{
return $this->course;
}
public function setCourse(?AccompanyingPeriod $course): self
{
$this->course = $course;
return $this;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace Chill\DocStoreBundle\Repository;
use App\Entity\AccompanyingCourseDocument;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method AccompanyingCourseDocument|null find($id, $lockMode = null, $lockVersion = null)
* @method AccompanyingCourseDocument|null findOneBy(array $criteria, array $orderBy = null)
* @method AccompanyingCourseDocument[] findAll()
* @method AccompanyingCourseDocument[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class AccompanyingCourseDocumentRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, AccompanyingCourseDocument::class);
}
// /**
// * @return AccompanyingCourseDocument[] Returns an array of AccompanyingCourseDocument objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('a')
->andWhere('a.exampleField = :val')
->setParameter('val', $value)
->orderBy('a.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?AccompanyingCourseDocument
{
return $this->createQueryBuilder('a')
->andWhere('a.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,97 @@
<?php
namespace Chill\DocStoreBundle\Form;
use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\MainBundle\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Doctrine\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 Chill\MainBundle\Form\Type\ChillTextareaType;
class AccompanyingCourseDocumentType extends AbstractType
{
/**
* the user running this form
*
* @var User
*/
protected $user;
/**
*
* @var AuthorizationHelper
*/
protected $authorizationHelper;
/**
*
* @var ObjectManager
*/
protected $om;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper
)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class)
->add('description', ChillTextareaType::class, [
'required' => false
])
->add('object', StoredObjectType::class, [
'error_bubbling' => true
])
->add('date', ChillDateType::class)
//TODO : adapt to using AccompanyingCourseDocument categories. Currently there are none...
->add('category', EntityType::class, array(
'placeholder' => 'Choose a document category',
'class' => 'ChillDocStoreBundle:DocumentCategory',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.documentClass = :docClass')
->setParameter('docClass', PersonDocument::class);
},
'choice_label' => function ($entity = null) {
return $entity ? $this->translatableStringHelper->localize($entity->getName()) : '';
},
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Document::class,
]);
// $resolver->setRequired(['role', 'center'])
// ->setAllowedTypes('role', [ \Symfony\Component\Security\Core\Role\Role::class ])
// ->setAllowedTypes('center', [ \Chill\MainBundle\Entity\Center::class ])
// ;
}
}

View File

@ -50,6 +50,9 @@ class MenuBuilder implements LocalMenuBuilderInterface
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
switch($menuId) {
case 'accompanyingCourse':
$this->buildMenuAccompanyingCourse($menu, $parameters);
break;
case 'person':
$this->buildMenuPerson($menu, $parameters);
break;
@ -57,7 +60,7 @@ class MenuBuilder implements LocalMenuBuilderInterface
throw new \LogicException("this menuid $menuId is not implemented");
}
}
protected function buildMenuPerson(MenuItem $menu, array $parameters)
{
/* @var $person \Chill\PersonBundle\Entity\Person */
@ -80,8 +83,25 @@ class MenuBuilder implements LocalMenuBuilderInterface
}
protected function buildMenuAccompanyingCourse(MenuItem $menu, array $parameters){
$course = $parameters['accompanyingCourse'];
// $user = $this->tokenStorage->getToken()->getUser();
//TODO : add condition to check user rights?
$menu->addChild($this->translator->trans('Documents'), [
'route' => 'accompanying_course_document_index',
'routeParameters' => [
'course' => $course->getId()
]
])
->setExtras([
'order'=> 400
]);
}
public static function getMenuIds(): array
{
return [ 'person' ];
return [ 'person', 'accompanyingCourse' ];
}
}

View File

@ -0,0 +1,5 @@
<form method="post" action="{{ path('accompanying_course_document_delete', {'id': document.id, 'course': course.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ document.id) }}">
<button class="btn btn-delete">{{ 'Delete' | trans }}</button>
</form>

View File

@ -0,0 +1,45 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = '' %}
{% block title %}
{# {{ 'Editing document for %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }} #}
{% endblock %}
{% block content %}
<h1>{{ 'Edit Document' | trans }}</h1>
{{ form_errors(form) }}
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.date) }}
{{ form_row(form.category) }}
{{ form_row(form.description) }}
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('accompanying_course_document_index', {'course': accompanyingCourse.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li class="edit">
<button class="btn btn-edit">{{ 'Edit'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %}

View File

@ -0,0 +1,70 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = '' %}
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% block title %}
{{ 'Documents' }}
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block content %}
<h1>{{ 'Documents' }}</h1>
<table class="table table-bordered border-dark table-striped">
<thead>
<tr>
<th>{{ 'Title' | trans }}</th>
<th>{{ 'Category'|trans }}</th>
<th>{{ 'Actions' | trans }}</th>
</tr>
</thead>
<tbody>
{% for document in documents %}
<tr>
<td>{{ document.title }}</td>
<td>{{ document.category.name|localize_translatable_string }}</td>
<td>
<ul class="record_actions">
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS', document) %}
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<li>
<a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-show"></a>
</li>
{% endif %}
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %}
<li>
<a href="{{ path('accompanying_course_document_edit', {'course': accompanyingCourse.id, 'id': document.id }) }}" class="btn btn-update"></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% else %}
<tr>
<td colspan="9" style="text-align:center;">
<span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_CREATE', accompanyingCourse) %}
<ul class="record_actions">
<li class="create">
<a href="{{ path('accompanying_course_document_new', {'course': accompanyingCourse.id}) }}" class="btn btn-create">
{{ 'Create new document' | trans }}
</a>
</li>
</ul>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,46 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = '' %}
{% block title %}
{# {{ 'New document for %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }} #}
{% endblock %}
{% block content %}
{# <h1>{{ 'New document for %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }}</h1> #}
{{ form_errors(form) }}
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.date) }}
{{ form_row(form.category) }}
{# {{ form_row(form.scope) }} #}
{{ form_row(form.description) }}
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('accompanying_course_document_index', {'course': accompanyingCourse.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li class="create">
<button class="btn btn-create">{{ 'Create'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %}

View File

@ -0,0 +1,59 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = '' %}
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% block title %}
{# {{ 'Detail of document of %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }} #}
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block content %}
<h1>{{ 'Document %title%' | trans({ '%title%': document.title }) }}</h1>
<dl class="chill_view_data">
<dt>{{ 'Title'|trans }}</dt>
<dd>{{ document.title }}</dd>
<dt>{{ 'Category'|trans }}</dt>
<dd>{{ document.category.name|localize_translatable_string }}</dd>
<dt>{{ 'Description' | trans }}</dt>
<dd>
{% if document.description is empty %}
<span class="chill-no-data-statement">{{ 'Any description'|trans }}</span>
{% else %}
<blockquote class="chill-user-quote">
{{ document.description|chill_markdown_to_html }}
</blockquote>
{% endif %}
</dd>
</dl>
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('accompanying_course_document_index', {'course': accompanyingCourse.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li>
{{ m.download_button(document.object, document.title) }}
</li>
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %}
<li>
<a href="{{ path('accompanying_course_document_edit', {'id': document.id, 'course': accompanyingCourse.id}) }}" class="btn btn-edit">
{{ 'Edit' | trans }}
</a>
</li>
{% endif %}
{% endblock %}

View File

@ -21,63 +21,68 @@
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% block title %}{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
{% block title %}
{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}
{% endblock %}
{% block js %}
{{ encore_entry_script_tags('mod_async_upload') }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block personcontent %}
<h1>{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
<h1>{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
<table class="table">
<thead>
<tr>
<th>{{ 'Title' | trans }}</th>
<th>{{ 'Category'|trans }}</th>
<th>{{ 'Circle' | trans }}</th>
<th>{{ 'Actions' | trans }}</th>
</tr>
</thead>
<tbody>
{% for document in documents %}
<tr>
<td>{{ document.title }}</td>
<td>{{ document.category.name|localize_translatable_string }}</td>
<td>{{ document.scope.name|localize_translatable_string }}</td>
<td>
<ul class="record_actions">
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<li>
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="btn btn-show"></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="btn btn-update"></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% else %}
<tr>
<td colspan="9" style="text-align:center;"><span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
<table class="table table-bordered border-dark table-striped">
{% 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="btn btn-create">
{{ 'Create new document' | trans }}
</a>
</li>
</ul>
{% endif %}
<thead>
<tr>
<th>{{ 'Title' | trans }}</th>
<th>{{ 'Category'|trans }}</th>
<th>{{ 'Circle' | trans }}</th>
<th>{{ 'Actions' | trans }}</th>
</tr>
</thead>
<tbody>
{% for document in documents %}
<tr>
<td>{{ document.title }}</td>
<td>{{ document.category.name|localize_translatable_string }}</td>
<td>{{ document.scope.name|localize_translatable_string }}</td>
<td>
<ul class="record_actions">
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<li>
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="btn btn-show"></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="btn btn-update"></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% else %}
<tr>
<td colspan="9" style="text-align:center;">
<span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% 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="btn btn-create">
{{ 'Create new document' | trans }}
</a>
</li>
</ul>
{% endif %}
{% endblock %}

View File

@ -18,39 +18,41 @@
{% set activeRouteKey = '' %}
{% block title %}{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
{% block title %}
{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}
{% endblock %}
{% block personcontent %}
<h1>{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
<h1>{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
{{ form_errors(form) }}
{{ form_errors(form) }}
{{ form_start(form) }}
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.date) }}
{{ form_row(form.category) }}
{{ form_row(form.scope) }}
{{ form_row(form.description) }}
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
{{ form_row(form.title) }}
{{ form_row(form.date) }}
{{ form_row(form.category) }}
{{ form_row(form.scope) }}
{{ form_row(form.description) }}
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li class="create">
<button class="btn btn-create">{{ 'Create'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li class="create">
<button class="btn btn-create">{{ 'Create'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
{% endblock %}
{% block js %}
{{ encore_entry_script_tags('mod_async_upload') }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %}

View File

@ -0,0 +1,88 @@
<?php
namespace Chill\DocStoreBundle\Security\Authorization;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\MainBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Psr\Log\LoggerInterface;
/**
*
*/
class AccompanyingCourseDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
const CREATE = 'CHILL_ACCOMPANYING_COURSE_DOCUMENT_CREATE';
const SEE = 'CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE';
const SEE_DETAILS = 'CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS';
const UPDATE = 'CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE';
const DELETE = 'CHILL_ACCOMPANYING_COURSE_DOCUMENT_DELETE';
/**
* @var AuthorizationHelper
*/
protected $authorizationHelper;
/**
* @var AccessDecisionManagerInterface
*/
protected $accessDecisionManager;
/**
* @var LoggerInterface
*/
protected $logger;
public function __construct(
AccessDecisionManagerInterface $accessDecisionManager,
AuthorizationHelper $authorizationHelper,
LoggerInterface $logger
)
{
$this->accessDecisionManager = $accessDecisionManager;
$this->authorizationHelper = $authorizationHelper;
$this->logger = $logger;
}
public function getRoles()
{
return [
self::CREATE,
self::SEE,
self::SEE_DETAILS,
self::UPDATE,
self::DELETE
];
}
protected function supports($attribute, $subject)
{
if (\in_array($attribute, $this->getRoles())) {
return true;
}
return false;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
return true;
}
public function getRolesWithoutScope()
{
return array();
}
public function getRolesWithHierarchy()
{
return ['accompanyingCourseDocument' => $this->getRoles() ];
}
}

View File

@ -2,31 +2,28 @@ parameters:
# cl_chill_person.example.class: Chill\PersonBundle\Example
services:
Chill\DocStoreBundle\Repository\:
autowire: true
autoconfigure: true
resource: '../Repository/'
tags: ['doctrine.repository_service']
Chill\DocStoreBundle\Repository\:
autowire: true
autoconfigure: true
resource: "../Repository/"
tags: ["doctrine.repository_service"]
Chill\DocStoreBundle\Form\DocumentCategoryType:
class: Chill\DocStoreBundle\Form\DocumentCategoryType
arguments: ['%kernel.bundles%']
tags:
- { name: form.type }
Chill\DocStoreBundle\Form\DocumentCategoryType:
class: Chill\DocStoreBundle\Form\DocumentCategoryType
arguments: ["%kernel.bundles%"]
tags:
- { name: form.type }
Chill\DocStoreBundle\Form\PersonDocumentType:
class: Chill\DocStoreBundle\Form\PersonDocumentType
arguments:
- "@chill.main.helper.translatable_string"
tags:
- { name: form.type, alias: chill_docstorebundle_form_document }
Chill\DocStoreBundle\Form\PersonDocumentType:
class: Chill\DocStoreBundle\Form\PersonDocumentType
arguments:
- "@chill.main.helper.translatable_string"
tags:
- { name: form.type, alias: chill_docstorebundle_form_document }
Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter:
class: Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter
arguments:
- "@security.access.decision_manager"
- "@chill.main.security.authorization.helper"
- "@logger"
tags:
- { name: security.voter }
- { name: chill.role }
Chill\DocStoreBundle\Security\Authorization\:
resource: "./../Security/Authorization"
autowire: true
autoconfigure: true
tags:
- { name: chill.role }

View File

@ -6,4 +6,3 @@ services:
Chill\DocStoreBundle\Controller\:
resource: "../../Controller"
tags: ["controller.service_arguments"]

View File

@ -1,6 +1,13 @@
services:
Chill\DocStoreBundle\Form\StoredObjectType:
arguments:
$em: '@Doctrine\ORM\EntityManagerInterface'
tags:
- { name: form.type }
Chill\DocStoreBundle\Form\StoredObjectType:
arguments:
$em: '@Doctrine\ORM\EntityManagerInterface'
tags:
- { name: form.type }
Chill\DocStoreBundle\Form\AccompanyingCourseDocumentType:
class: Chill\DocStoreBundle\Form\AccompanyingCourseDocumentType
arguments:
- "@chill.main.helper.translatable_string"
tags:
- { name: form.type, alias: chill_docstorebundle_form_document }

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocStore;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210903091534 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE SEQUENCE chill_doc.accompanyingcourse_document_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE chill_doc.accompanyingcourse_document (id INT NOT NULL, course_id INT NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_A45098F6591CC992 ON chill_doc.accompanyingcourse_document (course_id)');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD CONSTRAINT FK_A45098F6591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}
public function down(Schema $schema): void
{
$this->addSql('DROP SEQUENCE chill_doc.accompanyingcourse_document_id_seq CASCADE');
$this->addSql('DROP TABLE chill_doc.accompanyingcourse_document');
}
}

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocStore;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210903123835 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD category_bundle_id VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD category_id_inside_bundle INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD object_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD scope_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD user_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD title TEXT NOT NULL');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD description TEXT NOT NULL');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD CONSTRAINT FK_A45098F6369A0BE36EF62EFC 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.accompanyingcourse_document ADD CONSTRAINT FK_A45098F6232D562B FOREIGN KEY (object_id) REFERENCES chill_doc.stored_object (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD CONSTRAINT FK_A45098F6682B5931 FOREIGN KEY (scope_id) REFERENCES scopes (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD CONSTRAINT FK_A45098F6A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('CREATE INDEX IDX_A45098F6369A0BE36EF62EFC ON chill_doc.accompanyingcourse_document (category_bundle_id, category_id_inside_bundle)');
$this->addSql('CREATE INDEX IDX_A45098F6232D562B ON chill_doc.accompanyingcourse_document (object_id)');
$this->addSql('CREATE INDEX IDX_A45098F6682B5931 ON chill_doc.accompanyingcourse_document (scope_id)');
$this->addSql('CREATE INDEX IDX_A45098F6A76ED395 ON chill_doc.accompanyingcourse_document (user_id)');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP CONSTRAINT FK_A45098F6369A0BE36EF62EFC');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP CONSTRAINT FK_A45098F6232D562B');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP CONSTRAINT FK_A45098F6682B5931');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP CONSTRAINT FK_A45098F6A76ED395');
$this->addSql('DROP INDEX IDX_A45098F6369A0BE36EF62EFC');
$this->addSql('DROP INDEX IDX_A45098F6232D562B');
$this->addSql('DROP INDEX IDX_A45098F6682B5931');
$this->addSql('DROP INDEX IDX_A45098F6A76ED395');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP category_bundle_id');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP category_id_inside_bundle');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP object_id');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP scope_id');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP user_id');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP title');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP description');
$this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP date');
}
}

View File

@ -118,7 +118,7 @@ class CRUDController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->onFormValid($entity, $form, $request);
$this->onFormValid($action, $entity, $form, $request);
$em = $this->getDoctrine()->getManager();
$this->onPreRemove($action, $entity, $form, $request);
@ -607,7 +607,7 @@ class CRUDController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->onFormValid($entity, $form, $request);
$this->onFormValid($action, $entity, $form, $request);
$em = $this->getDoctrine()->getManager();
$this->onPreFlush($action, $entity, $form, $request);
@ -706,7 +706,7 @@ class CRUDController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->onFormValid($entity, $form, $request);
$this->onFormValid($action, $entity, $form, $request);
$em = $this->getDoctrine()->getManager();
$this->onPrePersist($action, $entity, $form, $request);
@ -716,7 +716,7 @@ class CRUDController extends AbstractController
$this->onPreFlush($action, $entity, $form, $request);
$em->flush();
$this->onPostFlush($action, $entity, $form, $request);
$this->getPaginatorFactory();
$this->addFlash('success', $this->generateFormSuccessMessage($action, $entity));
$result = $this->onBeforeRedirectAfterSubmission($action, $entity, $form, $request);
@ -1084,12 +1084,7 @@ class CRUDController extends AbstractController
return null;
}
/**
* @param object $entity
* @param FormInterface $form
* @param Request $request
*/
protected function onFormValid(object $entity, FormInterface $form, Request $request)
protected function onFormValid(string $action, object $entity, FormInterface $form, Request $request)
{
}

View File

@ -23,32 +23,22 @@ namespace Chill\MainBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/**
* Class AdminController
*
* @package Chill\MainBundle\Controller
* @author julien.fastre@champs-libres.coop
* @author marc@champs-libres.coop
*/
use Symfony\Component\Routing\Annotation\Route;
class AdminController extends AbstractController
{
public function indexAction($menu = 'admin',
$header_title = 'views.Main.admin.index.header_title',
$page_title = 'views.Main.admin.index.page_title') {
return $this->render('@ChillMain/Admin/layout.html.twig');
/**
* @Route("/{_locale}/admin", name="chill_main_admin_central")
*/
public function indexAction()
{
return $this->render('@ChillMain/Admin/index.html.twig');
}
public function indexPermissionsAction()
{
return $this->render('@ChillMain/Admin/layout_permissions.html.twig');
}
public function configurationWarningsAction()
{
$alertManager = $this->get('chill_main.configuration_alert_manager');
}
}

View File

@ -2,8 +2,13 @@
namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\AbstractCRUDController;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Chill\MainBundle\Entity\User;
@ -11,7 +16,10 @@ use Chill\MainBundle\Form\UserType;
use Chill\MainBundle\Entity\GroupCenter;
use Chill\MainBundle\Form\Type\ComposedGroupCenterType;
use Chill\MainBundle\Form\UserPasswordType;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
/**
@ -19,180 +27,127 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
*
* @package Chill\MainBundle\Controller
*/
class UserController extends AbstractController
class UserController extends CRUDController
{
const FORM_GROUP_CENTER_COMPOSED = 'composed_groupcenter';
/**
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/**
* @var ValidatorInterface
*/
private $validator;
private UserPasswordEncoderInterface $passwordEncoder;
/**
* UserController constructor.
*
* @param LoggerInterface $logger
* @param ValidatorInterface $validator
*/
public function __construct(LoggerInterface $logger, ValidatorInterface $validator)
{
$this->logger = $logger;
public function __construct(
LoggerInterface $chillLogger,
ValidatorInterface $validator,
UserPasswordEncoderInterface $passwordEncoder
) {
$this->logger = $chillLogger;
$this->validator = $validator;
$this->passwordEncoder = $passwordEncoder;
}
/**
* Lists all User entities.
*
*/
public function indexAction()
protected function createFormFor(string $action, $entity, string $formClass = null, array $formOptions = []): FormInterface
{
$em = $this->getDoctrine()->getManager();
$entities = $em->createQuery('SELECT u FROM ChillMainBundle:User u '
. 'ORDER BY u.username')
->getResult();
return $this->render('@ChillMain/User/index.html.twig', array(
'entities' => $entities,
));
}
/**
* Creates a new User entity.
*
*/
public function createAction(Request $request)
{
$user = new User();
$form = $this->createCreateForm($user);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$user->setPassword($this->get('security.password_encoder')
->encodePassword($user, $form['plainPassword']->getData()));
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('admin_user_show', array('id' => $user->getId())));
// for "new", add special config
if ('new' === $action) {
return $this->createForm(UserType::class, $entity, array(
'is_creation' => true
));
}
return $this->render('@ChillMain/User/new.html.twig', array(
'entity' => $user,
'form' => $form->createView(),
));
// default behaviour
return parent::createFormFor($action, $entity, $formClass, $formOptions);
}
/**
* Creates a form to create a User entity.
*
* @param User $entity The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(User $entity)
protected function onPrePersist(string $action, $entity, FormInterface $form, Request $request)
{
$form = $this->createForm(UserType::class, $entity, array(
'action' => $this->generateUrl('admin_user_create'),
'method' => 'POST',
'is_creation' => true
));
$form->add('submit', SubmitType::class, array('label' => 'Create'));
return $form;
}
/**
* Displays a form to create a new User entity.
*
*/
public function newAction()
{
$user = new User();
$form = $this->createCreateForm($user);
return $this->render('@ChillMain/User/new.html.twig', array(
'entity' => $user,
'form' => $form->createView(),
));
}
/**
* Finds and displays a User entity.
*
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ChillMainBundle:User')->find($id);
if (!$user) {
throw $this->createNotFoundException('Unable to find User entity.');
// for "new", encode the password
if ('new' === $action) {
$entity->setPassword($this->passwordEncoder
->encodePassword($entity, $form['plainPassword']->getData()));
}
return $this->render('@ChillMain/User/show.html.twig', array(
'entity' => $user,
));
// default behaviour
parent::onPrePersist($action, $entity, $form, $request);
}
/**
* Displays a form to edit an existing User entity.
*
*/
public function editAction($id)
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{
$em = $this->getDoctrine()->getManager();
$query->addOrderBy('e.usernameCanonical', 'ASC');
$user = $em->getRepository('ChillMainBundle:User')->find($id);
return parent::orderQuery($action, $query, $request, $paginator);
}
if (!$user) {
throw $this->createNotFoundException('Unable to find User entity.');
protected function generateTemplateParameter(string $action, $entity, Request $request, array $defaultTemplateParameters = [])
{
// add mini-forms for edit action
if ("edit" === $action) {
return array_merge(
$defaultTemplateParameters,
[
'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($entity, $request)->createView(),
'delete_groupcenter_form' => array_map(
function(\Symfony\Component\Form\Form $form) {
return $form->createView();
},
iterator_to_array($this->getDeleteLinkGroupCenterByUser($entity, $request), true)
)
]
);
}
$editForm = $this->createEditForm($user);
return $this->render('@ChillMain/User/edit.html.twig', array(
'entity' => $user,
'edit_form' => $editForm->createView(),
'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user)->createView(),
'delete_groupcenter_form' => array_map(
function(\Symfony\Component\Form\Form $form) {
return $form->createView();
},
iterator_to_array($this->getDeleteLinkGroupCenterByUser($user), true))
));
// default behaviour
return parent::generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters);
}
/**
* Displays a form to edit the user password.
*
* @Route("/{_locale}/admin/user/{id}/edit_password", name="admin_user_edit_password")
*/
public function editPasswordAction($id)
public function editPasswordAction(User $user, Request $request)
{
$em = $this->getDoctrine()->getManager();
$editForm = $this->createEditPasswordForm($user, $request);
$editForm->handleRequest($request);
$user = $em->getRepository('ChillMainBundle:User')->find($id);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$password = $editForm->get('new_password')->getData();
if (!$user) {
throw $this->createNotFoundException('Unable to find User entity.');
// logging for prod
$this->logger->info('update password for an user', [
'by' => $this->getUser()->getUsername(),
'user' => $user->getUsername()
]);
$user->setPassword($this->passwordEncoder->encodePassword($user, $password));
$this->getDoctrine()->getManager()->flush();
$this->addFlash('success', $this->get('translator')->trans('Password successfully updated!'));
return $this->redirect(
$request->query->has('returnPath') ? $request->query->get('returnPath') :
$this->generateUrl('chill_crud_admin_user_edit', ['id' => $user->getId()])
);
}
$editForm = $this->createEditPasswordForm($user);
return $this->render('@ChillMain/User/edit_password.html.twig', array(
return $this->render('@ChillMain/User/edit_password.html.twig', [
'entity' => $user,
'edit_form' => $editForm->createView()
));
]);
}
/**
@ -204,9 +159,6 @@ class UserController extends AbstractController
private function createEditPasswordForm(User $user)
{
return $this->createForm(UserPasswordType::class, null, array(
'action' =>
$this->generateUrl('admin_user_update_password', array('id' => $user->getId())),
'method' => 'PUT',
'user' => $user
))
->add('submit', SubmitType::class, array('label' => 'Change password'))
@ -214,7 +166,11 @@ class UserController extends AbstractController
;
}
public function deleteLinkGroupCenterAction($uid, $gcid)
/**
* @Route("/{_locale}/admin/main/user/{uid}/delete_link_groupcenter/{gcid}",
* name="admin_user_delete_groupcenter")
*/
public function deleteLinkGroupCenterAction($uid, $gcid, Request $request): RedirectResponse
{
$em = $this->getDoctrine()->getManager();
@ -236,7 +192,7 @@ class UserController extends AbstractController
} catch (\RuntimeException $ex) {
$this->addFlash('error', $this->get('translator')->trans($ex->getMessage()));
return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $uid)));
return $this->redirect($this->generateUrl('chill_crud_admin_user_edit', array('id' => $uid)));
}
$em->flush();
@ -244,11 +200,15 @@ class UserController extends AbstractController
$this->addFlash('success', $this->get('translator')
->trans('The permissions where removed.'));
return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $uid)));
return $this->redirect($this->generateUrl('chill_crud_admin_user_edit', array('id' => $uid)));
}
public function addLinkGroupCenterAction(Request $request, $uid)
/**
* @Route("/{_locale}/admin/main/user/{uid}/add_link_groupcenter",
* name="admin_user_add_groupcenter")
*/
public function addLinkGroupCenterAction(Request $request, $uid): RedirectResponse
{
$em = $this->getDoctrine()->getManager();
@ -258,7 +218,7 @@ class UserController extends AbstractController
throw $this->createNotFoundException('Unable to find User entity.');
}
$form = $this->createAddLinkGroupCenterForm($user);
$form = $this->createAddLinkGroupCenterForm($user, $request);
$form->handleRequest($request);
if ($form->isValid()) {
@ -272,8 +232,12 @@ class UserController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('The '
. 'permissions have been successfully added to the user'));
return $this->redirect($this->generateUrl('admin_user_edit',
array('id' => $uid)));
$returnPathParams = $request->query->has('returnPath') ?
['returnPath' => $request->query->get('returnPath')] : [];
return $this->redirect($this->generateUrl('chill_crud_admin_user_edit',
\array_merge(['id' => $uid], $returnPathParams)));
} else {
foreach($this->validator->validate($user) as $error)
$this->addFlash('error', $error->getMessage());
@ -311,104 +275,6 @@ class UserController extends AbstractController
return $groupCenterManaged;
}
/**
* Creates a form to edit a User entity.
*
* @param User $user The entity
*
* @return \Symfony\Component\Form\Form The form
*/
private function createEditForm(User $user)
{
$form = $this->createForm(UserType::class, $user, array(
'action' => $this->generateUrl('admin_user_update', array('id' => $user->getId())),
'method' => 'PUT',
));
$form->add('submit', SubmitType::class, array('label' => 'Update'));
return $form;
}
/**
* Edits an existing User entity.
*
*/
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ChillMainBundle:User')->find($id);
if (!$user) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$editForm = $this->createEditForm($user);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
$em->flush();
return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $id)));
}
return $this->render('@ChillMain/User/edit.html.twig', array(
'entity' => $user,
'edit_form' => $editForm->createView(),
'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user)->createView(),
'delete_groupcenter_form' => array_map(
function(\Symfony\Component\Form\Form $form) {
return $form->createView();
},
iterator_to_array($this->getDeleteLinkGroupCenterByUser($user), true))
));
}
/**
* Edits the user password
*
*/
public function updatePasswordAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ChillMainBundle:User')->find($id);
if (!$user) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$editForm = $this->createEditPasswordForm($user);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
$password = $editForm->get('new_password')->getData();
// logging for prod
$this->logger->info('update password for an user', [
'by' => $this->getUser()->getUsername(),
'user' => $user->getUsername()
]);
$user->setPassword($this->get('security.password_encoder')
->encodePassword($user, $password));
$em->flush();
$this->addFlash('success', $this->get('translator')->trans('Password successfully updated!'));
return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $id)));
}
return $this->render('@ChillMain/User/edit_password.html.twig', array(
'entity' => $user,
'edit_form' => $editForm->createView(),
));
}
/**
* Creates a form to delete a link to a GroupCenter
*
@ -416,11 +282,13 @@ class UserController extends AbstractController
*
* @return \Symfony\Component\Form\Form The form
*/
private function createDeleteLinkGroupCenterForm(User $user, GroupCenter $groupCenter)
private function createDeleteLinkGroupCenterForm(User $user, GroupCenter $groupCenter, $request)
{
$returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : [];
return $this->createFormBuilder()
->setAction($this->generateUrl('admin_user_delete_group_center',
array('uid' => $user->getId(), 'gcid' => $groupCenter->getId())))
->setAction($this->generateUrl('admin_user_delete_groupcenter',
array_merge($returnPathParams, ['uid' => $user->getId(), 'gcid' => $groupCenter->getId()])))
->setMethod('DELETE')
->add('submit', SubmitType::class, array('label' => 'Delete'))
->getForm()
@ -433,11 +301,13 @@ class UserController extends AbstractController
* @param User $user
* @return \Symfony\Component\Form\Form
*/
private function createAddLinkGroupCenterForm(User $user)
private function createAddLinkGroupCenterForm(User $user, Request $request)
{
$returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : [];
return $this->createFormBuilder()
->setAction($this->generateUrl('admin_user_add_group_center',
array('uid' => $user->getId())))
->setAction($this->generateUrl('admin_user_add_groupcenter',
array_merge($returnPathParams, ['uid' => $user->getId()])))
->setMethod('POST')
->add(self::FORM_GROUP_CENTER_COMPOSED, ComposedGroupCenterType::class)
->add('submit', SubmitType::class, array('label' => 'Add a new groupCenter'))
@ -449,11 +319,11 @@ class UserController extends AbstractController
*
* @param User $user
*/
private function getDeleteLinkGroupCenterByUser(User $user)
private function getDeleteLinkGroupCenterByUser(User $user, Request $request)
{
foreach ($user->getGroupCenters() as $groupCenter) {
yield $groupCenter->getId() => $this
->createDeleteLinkGroupCenterForm($user, $groupCenter);
->createDeleteLinkGroupCenterForm($user, $groupCenter, $request);
}
}
}

View File

@ -19,10 +19,13 @@
namespace Chill\MainBundle\DependencyInjection;
use Chill\MainBundle\Controller\UserController;
use Chill\MainBundle\Doctrine\DQL\STContains;
use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Form\UserJobType;
use Chill\MainBundle\Form\UserType;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
@ -283,13 +286,35 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
'template' => '@ChillMain/UserJob/index.html.twig',
],
'new' => [
'role' => 'ROLE_ADMIN'
'role' => 'ROLE_ADMIN'
],
'edit' => [
'role' => 'ROLE_ADMIN'
]
],
],
[
'class' => User::class,
'controller' => UserController::class,
'name' => 'admin_user',
'base_path' => '/admin/main/user',
'base_role' => 'ROLE_ADMIN',
'form_class' => UserType::class,
'actions' => [
'index' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillMain/User/index.html.twig'
],
'new' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillMain/User/new.html.twig'
],
'edit' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillMain/User/edit.html.twig'
]
]
]
],
'apis' => [
[

View File

@ -47,9 +47,9 @@ class UserType extends AbstractType
])
->add('label', TextType::class)
->add('mainCenter', EntityType::class, [
'label' => 'main center',
'label' => 'Main center',
'required' => false,
'placeholder' => 'choose a main center',
'placeholder' => 'Choose a main center',
'class' => Center::class,
'query_builder' => function (EntityRepository $er) {
$qb = $er->createQueryBuilder('c');
@ -59,16 +59,16 @@ class UserType extends AbstractType
}
])
->add('mainScope', EntityType::class, [
'label' => 'Choose a main scope',
'label' => 'Main scope',
'required' => false,
'placeholder' => 'choose a main scope',
'placeholder' => 'Choose a main scope',
'class' => Scope::class,
'choice_label' => function (Scope $c) {
return $this->translatableStringHelper->localize($c->getName());
},
])
->add('userJob', EntityType::class, [
'label' => 'Choose a job',
'label' => 'user job',
'required' => false,
'placeholder' => 'choose a job',
'class' => UserJob::class,

View File

@ -0,0 +1,49 @@
<?php
namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\UserJob;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class UserJobRepository implements ObjectRepository
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{
$this->repository = $em->getRepository(UserJob::class);
}
public function find($id): ?UserJob
{
return $this->repository->find($id);
}
/**
* @return array|UserJob[]
*/
public function findAll(): array
{
return $this->repository->findAll();
}
/**
* @return array|UserJob[]|object[]
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
{
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
}
public function findOneBy(array $criteria)
{
return $this->repository->findOneBy($criteria);
}
public function getClassName()
{
return UserJob::class;
}
}

View File

@ -0,0 +1,8 @@
{% extends '@ChillMain/Admin/layout_permissions.html.twig' %}
{% block title %}{{ ('crud.' ~ crud_name ~ '.index.title')|trans({'%crud_name%': crud_name}) }}{% endblock %}
{% block content %}
{% embed '@ChillMain/CRUD/_index.html.twig' %}
{% endembed %}
{% endblock content %}

View File

@ -1,17 +1,15 @@
{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %}
{% block vertical_menu_content %}
{% endblock %}
{% block admin_content %}
<h1>{{ 'Administration interface'|trans }}</h1>
{{ 'welcome_message_raw'|trans|raw }}
<div>
<h2>{{ 'Configuration alerts'|trans }}</h2>
<p>{{ 'Here you can check the configuration of your instance.'|trans }}</p>
{{ chill_widget('configuration_warnings', {}) }}
</div>
{% endblock %}
<p>{{ 'Welcome to the admin section !'|trans }}</p>
{{ chill_menu('admin_index', {
'layout': '@ChillMain/Admin/menu_admin_index.html.twig'
}) }}
{% endblock %}

View File

@ -1,26 +1,3 @@
{#
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
<info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
#}
{#
The layout of the admin section. All the page / template of the admin section must use this
layout.
#}
{% extends "@ChillMain/layout.html.twig" %}
{% block navigation_search_bar %}{% endblock %}
@ -38,4 +15,4 @@
</div>
</div>
{% endblock %}
{% endblock %}
{% endblock %}

View File

@ -1,37 +1,14 @@
{#
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
<info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
#}
{#
The layout of the admin section. All the page / template of the admin section must use this
layout.
#}
{% extends "@ChillMain/layoutWithVerticalMenu.html.twig" %}
{% block navigation_search_bar %}{% endblock %}
{% block navigation_section_menu %}
{{ chill_menu('admin_section', {
'layout': '@ChillMain/Menu/adminSection.html.twig',
'layout': '@ChillMain/Admin/menu_admin_section.html.twig',
}) }}
{% endblock %}
{% block layout_wvm_content %}
{% block admin_content %}<!-- block personcontent empty -->
<h2>{{ 'Welcome to the admin section !'|trans }}</h2>
{% block admin_content %}
<!-- block admin content emty -->
{% endblock %}
{% endblock %}
{% endblock %}

View File

@ -1,5 +1,5 @@
{#
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
<info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
@ -20,7 +20,7 @@
{% block vertical_menu_content %}
{{ chill_menu('admin_permissions', {
'layout': '@ChillMain/Menu/admin_permissions.html.twig',
'layout': '@ChillMain/Admin/menu_admin_permissions.html.twig',
}) }}
{% endblock %}
@ -28,4 +28,4 @@
{% block admin_content %}<!-- block personcontent empty -->
<h1>{{ 'Permissions management of your chill installation' |trans }}</h1>
{% endblock %}
{% endblock %}
{% endblock %}

View File

@ -0,0 +1,18 @@
<div class="{{ 'menu-' ~ menus.name }}">
<ul>
{% for menu in menus %}
<li>
<a class=""
href="{{ menu.uri }}">
<h3>{{ menu.label|trans }}</h3>
{% if menu.extras.explain is defined %}
<div class="" >
{{ menu.extras.explain|trans }}
</div>
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>

View File

@ -1,17 +1,22 @@
{% set formId = crudMainFormId|default('crud_main_form') %}
<div class="{% block crud_content_main_div_class %}{% endblock %}">
{% block crud_content_header %}
<h1>{{ ('crud.'~crud_name~'.title_edit')|trans }}</h1>
{% endblock crud_content_header %}
{% block crud_content_form %}
{{ form_start(form) }}
{{ form_start(form, { 'attr' : { 'id': formId } } ) }}
{% block crud_content_form_rows %}
{% for f in form %}
{{ form_row(f) }}
{% endfor %}
{% endblock crud_content_form_rows %}
{{ form_end(form) }}
{% block crud_content_after_form %}{% endblock %}
{% block crud_content_form_actions %}
<ul class="record_actions sticky-form-buttons">
{% block content_form_actions_back %}
@ -30,7 +35,7 @@
</li>
{% endif %}
{% endif %}
{% endblock content_form_actions_delete %}
{% endblock content_form_actions_delete %}
{% block content_form_actions_view %}
{% if chill_crud_action_exists(crud_name, 'view') %}
{% if is_granted(chill_crud_config('role', crud_name, 'view'), entity) %}
@ -39,17 +44,17 @@
</li>
{% endif %}
{% endif %}
{% endblock content_form_actions_view %}
{% endblock content_form_actions_view %}
{% block content_form_actions_save_and_close %}
<li class="">
<button type="submit" name="submit" value="save-and-close" class="btn btn-update">
<button type="submit" name="submit" value="save-and-close" class="btn btn-update" form="{{ formId }}">
{{ 'crud.edit.save_and_close'|trans }}
</button>
</li>
{% endblock %}
{% block content_form_actions_save_and_show %}
<li class="">
<button type="submit" name="submit" value="save-and-show" class="btn btn-update">
<button type="submit" name="submit" value="save-and-show" class="btn btn-update" form="{{ formId }}">
{{ 'crud.edit.save_and_show'|trans }}
</button>
</li>
@ -58,6 +63,5 @@
</ul>
{% endblock %}
{{ form_end(form) }}
{% endblock %}
</div>

View File

@ -5,14 +5,16 @@
{% block crud_content_form %}
{{ form_start(form) }}
{% block crud_content_form_rows %}
{% for f in form %}{% if f.vars.name != 'submit' %}
{{ form_row(f) }}
{% endif %}{% endfor %}
{% endblock crud_content_form_rows %}
{% block crud_content_form_actions %}
{% block crud_content_after_form %}{% endblock %}
{% block crud_content_form_actions %}
<ul class="record_actions sticky-form-buttons">
{% block content_form_actions_back %}
<li class="cancel">
@ -20,7 +22,7 @@
{{ 'Cancel'|trans }}
</a>
</li>
{% endblock %}
{% endblock %}
{% block content_form_actions_save_and_close %}
<li class="">
<button type="submit" name="submit" value="save-and-close" class="btn btn-create">

View File

@ -1,20 +1,3 @@
{#
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS,
* <info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
#}
<header>
<nav class="navbar navbar-dark bg-primary navbar-expand-md">
<div class="container-xxl">
@ -24,12 +7,12 @@
{{ include('@ChillMain/Layout/_header-logo.html.twig') }}
</a>
</div>
<div class="col-8">
<button class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
<button class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarContent"
aria-controls="navbarContent"
aria-expanded="false"
@ -43,8 +26,8 @@
{% block navigation_search_bar %}
<li class="nav-item navigation-search">
<form class="form-inline" action="{{ path('chill_main_search') }}" method="get">
<input class="form-control"
name="q" type="search" placeholder="{{ 'Search'|trans }}"
<input class="form-control"
name="q" type="search" placeholder="{{ 'Search'|trans }}"
{% if _search_pattern is defined %}value="{{ _search_pattern }}"{% endif %}/>
<button class="btn text-white-50" type="submit">
<i class="fa fa-lg fa-search fa-flip-horizontal"></i>
@ -71,15 +54,15 @@
</li>
{% else %}
<li class="nav-item dropdown btn btn-primary nav-language">
<a id="menu-languages"
class="nav-link dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-haspopup="true"
<a id="menu-languages"
class="nav-link dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<a href="#" class="more">{{ app.request.locale | capitalize }}</a>
</a>
<div class="dropdown-menu dropdown-menu-end"
<div class="dropdown-menu dropdown-menu-end"
aria-labelledby="menu-languages">
{% for lang in available_languages %}
<a class="dropdown-item list-group-item bg-dark text-white{% if lang == app.request.locale %} active{% endif %}"
@ -93,7 +76,7 @@
</ul>
</div>
</div>
</div>
</nav>

View File

@ -25,7 +25,7 @@
aria-expanded="false">
{{ 'Welcome' | trans }}<br/>
<b>
{{ app.user.username }}
{{ render(controller('ChillMainBundle:UI:showNotificationUserCounter')) }}
@ -40,8 +40,8 @@
{% for menu in menus %}
{% if is_granted('ROLE_PREVIOUS_ADMIN') and menu.name == 'Logout' %}
<a class="dropdown-item list-group-item bg-dark text-white"
href="{{ path('admin_user', {'_switch_user': '_exit'}) }}">
<a class="dropdown-item list-group-item bg-dark text-white"
href="{{ path('chill_crud_admin_user_index', {'_switch_user': '_exit'}) }}">
{{ 'Exit impersonation'|trans }}
{% else %}
<a class="dropdown-item list-group-item bg-dark text-white"

View File

@ -1,5 +1,5 @@
{#
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
<info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
@ -17,7 +17,7 @@
#}
{#
Layout for a vertical menu (like admin, export) to use with the
Layout for a vertical menu (like admin, export) to use with the
layout ../layoutWithVerticalMenu.html.twig.
#}
@ -25,13 +25,9 @@
<li class="title">
{% block v_menu_title %}<!-- title of the verticalMenu is empty -->{% endblock %}
</li>
{% for route in routes %}
<li class="{% apply spaceless %}
{% if route.key == activeRouteKey %}
active
{% endif %}
{% endapply %} ">
<a href="{{ path(route.key, args ) }}" >{{ route.label|trans }}</a>
{% for menu in menus %}
<li class="{% if menu is knp_menu_current %}current {% endif %}">
<a href="{{ menu.uri }}" >{{ menu.label|trans }}</a>
</li>
{% endfor %}
</ul>
</ul>

View File

@ -1,78 +1,56 @@
{% extends '@ChillMain/Admin/layout_permissions.html.twig' %}
{% block title %}{{ 'User edit'|trans }}{% endblock %}
{% extends '@ChillMain/Admin/Permission/layout_crud_permission_index.html.twig' %}
{% block admin_content -%}
<h1>{{ 'User edit'|trans }}</h1>
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
{% block crud_content_after_form %}
<h2>{{ 'Permissions granted'|trans }}</h2>
{{ form_start(edit_form) }}
{{ form_row(edit_form.username) }}
{{ form_row(edit_form.email) }}
{{ form_row(edit_form.enabled, { 'label': "User'status"}) }}
{{ form_widget(edit_form.submit, { 'attr': { 'class' : 'btn btn-chill-green center' } } ) }}
<a href="{{ path('admin_user_edit_password', { 'id' : entity.id }) }}" class="btn btn-chill-orange">{{ 'Edit password'|trans }}</a>
{{ form_end(edit_form) }}
<h2>{{ 'Permissions granted'|trans }}</h2>
{% if entity.groupcenters|length > 0 %}
<table>
<thead>
<tr>
<th>{{ 'Permission group'|trans }}</th>
<th>{{ 'Center'|trans }}</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{% for groupcenter in entity.groupcenters %}
<tr>
<td>
{% if entity.groupcenters|length > 0 %}
<table>
<thead>
<tr>
<th>{{ 'Permission group'|trans }}</th>
<th>{{ 'Center'|trans }}</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{% for groupcenter in entity.groupcenters %}
<tr>
<td>
<span class="user_group permissionsgroup">
{{ groupcenter.permissionsgroup.name }}
</span>
</td>
<td>
</td>
<td>
<span class="user_group center">
{{ groupcenter.center.name }}
</span>
</td>
<td>
{{ form_start(delete_groupcenter_form[groupcenter.id]) }}
{{ form_row(delete_groupcenter_form[groupcenter.id].submit, { 'attr': { 'class': 'btn btn-chill-red' } } ) }}
{{ form_rest(delete_groupcenter_form[groupcenter.id]) }}
{{ form_end(delete_groupcenter_form[groupcenter.id]) }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>{{ 'Any permissions granted to this user'|trans }}.</p>
{% endif %}
<h3>{{ 'Grant new permissions'|trans }}</h3>
{{ form_start(add_groupcenter_form) }}
{{ form_row(add_groupcenter_form.composed_groupcenter.center) }}
{{ form_row(add_groupcenter_form.composed_groupcenter.permissionsgroup) }}
{{ form_row(add_groupcenter_form.submit, { 'attr' : { 'class': 'btn btn-chill-green' } } ) }}
</td>
<td>
{{ form_start(delete_groupcenter_form[groupcenter.id]) }}
{{ form_row(delete_groupcenter_form[groupcenter.id].submit, { 'attr': { 'class': 'btn btn-chill-red' } } ) }}
{{ form_rest(delete_groupcenter_form[groupcenter.id]) }}
{{ form_end(delete_groupcenter_form[groupcenter.id]) }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>{{ 'Any permissions granted to this user'|trans }}.</p>
{% endif %}
{{ form_end(add_groupcenter_form) }}
<ul class="record_actions">
<li>
<a href="{{ path('admin_user_show', { 'id': entity.id }) }}">
{{ 'show'|trans }}
</a>
</li>
<li>
<a href="{{ path('admin_user') }}">
{{ 'Back to the list'|trans }}
</a>
</li>
</ul>
<h3>{{ 'Grant new permissions'|trans }}</h3>
{{ form_start(add_groupcenter_form) }}
{{ form_row(add_groupcenter_form.composed_groupcenter.center) }}
{{ form_row(add_groupcenter_form.composed_groupcenter.permissionsgroup) }}
{{ form_row(add_groupcenter_form.submit, { 'attr' : { 'class': 'btn btn-chill-green' } } ) }}
{{ form_end(add_groupcenter_form) }}
{% endblock %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %}
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends '@ChillMain/Admin/layout_permissions.html.twig' %}
{% extends '@ChillMain/Admin/Permission/layout_crud_permission_index.html.twig' %}
{% block title %}{{ 'Edit password for %username%'|trans( { '%username%': entity.username } ) }}{% endblock %}
@ -7,19 +7,17 @@
{{ form_start(edit_form) }}
{{ form_row(edit_form.new_password) }}
{{ form_widget(edit_form.submit, { 'attr': { 'class': 'btn btn-chill-orange' } } ) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ chill_return_path_or('chill_crud_admin_user_index') }}" class="btn btn-cancel">
{{ 'Cancel'|trans }}
</a>
</li>
<li>
{{ form_widget(edit_form.submit, { 'attr': { 'class': 'btn btn-edit' } } ) }}
</li>
</ul>
{{ form_end(edit_form) }}
<ul class="record_actions">
<li>
<a href="{{ path('admin_user') }}">
{{ 'Back to the list'|trans }}
</a>
</li>
<li>
<a href="{{ path('admin_user_edit', { 'id' : entity.id }) }}">
{{ 'Back to the user edition'|trans }}
</a>
</li>
</ul>
{% endblock %}

View File

@ -1,53 +1,60 @@
{% extends '@ChillMain/Admin/layout_permissions.html.twig' %}
{% block title %}{{ 'user list'|trans|capitalize }}{% endblock %}
{% extends '@ChillMain/Admin/Permission/layout_crud_permission_index.html.twig' %}
{% block admin_content -%}
<h1>{{ 'user list'|trans|capitalize }}</h1>
<table class="records_list">
<thead>
<tr>
<th>{{ 'Username'|trans|capitalize }}</th>
<th>{{ 'Actions'|trans|capitalize }}</th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr class="{% if entity.isEnabled == false %}user-disabled{% else %}user-enabled{% endif %}" >
<td><a href="{{ path('admin_user_show', { 'id': entity.id }) }}">{{ entity.username }}</a></td>
{% embed '@ChillMain/CRUD/_index.html.twig' %}
{% block table_entities_thead_tr %}
<th>{{ 'crud.admin_user.index.is_active'|trans }}</th>
<th>{{ 'crud.admin_user.index.usernames'|trans }}</th>
<th>{{ 'crud.admin_user.index.mains'|trans }}</th>
<th>&nbsp;</th>
{% endblock %}
{% block table_entities_tbody %}
{% for entity in entities %}
<tr data-username="{{ entity.username|e('html_attr') }}">
<td>
<ul>
<li>
<a href="{{ path('admin_user_show', { 'id': entity.id }) }}">{{ 'show'|trans }}</a>
</li>
<li>
<a href="{{ path('admin_user_edit', { 'id': entity.id }) }}">{{ 'edit'|trans }}</a>
</li>
<li>
<a href="{{ path('admin_user_edit_password', { 'id' : entity.id }) }}">{{ 'Edit password'|trans }}</a>
</li>
{% if is_granted('ROLE_ALLOWED_TO_SWITCH') %}
<li>
<a href="{{ path('chill_main_homepage', {'_switch_user': entity.username }) }}">
{{ 'Impersonate'|trans }}
</a>
</li>
{% if entity.isEnabled %}
<i class="fa fa-check chill-green"></i>
{% else %}
<i class="fa fa-times chill-red"></i>
{% endif %}
</ul>
</td>
<td>
{{ entity.username }}
<br/>
{{ entity.label }}
<br/>
{{ entity.email }}
</td>
<td>
{% if entity.userJob %}
{{ entity.userJob.label|localize_translatable_string }}
<br/>
{% endif %}
{% if entity.mainScope %}
{{ entity.mainScope.name|localize_translatable_string }}
<br/>
{% endif %}
{% if entity.mainCenter %}
{{ entity.mainCenter.name }}
{% endif %}
</td>
<td>
<ul class="record_actions">
<li>
<a class="btn btn-edit" href="{{ path('chill_crud_admin_user_edit', { 'id': entity.id }) }}"></a>
</li>
<li>
<a class="btn btn-chill-red" href="{{ path('admin_user_edit_password', { 'id' : entity.id }) }}" title="{{ 'Edit password'|trans|e('html_attr') }}"><i class="fa fa-ellipsis-h"></i></a>
</li>
{% if is_granted('ROLE_ALLOWED_TO_SWITCH') %}
<li>
<a class="btn btn-chill-blue" href="{{ path('chill_main_homepage', {'_switch_user': entity.username }) }}" title="{{ "Impersonate"|trans|e('html_attr') }}"><i class="fa fa-user-secret"></i></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<ul>
<li>
<a href="{{ path('admin_user_new') }}">
{{ 'Add a new user'|trans|capitalize }}
</a>
</li>
</ul>
{% endblock admin_content %}
{% endfor %}
{% endblock %}
{% endembed %}
{% endblock %}

View File

@ -1,22 +1,7 @@
{% extends '@ChillMain/Admin/layout_permissions.html.twig' %}
{% block title %}{{ 'User creation'|trans }}{% endblock %}
{% extends '@ChillMain/Admin/Permission/layout_crud_permission_index.html.twig' %}
{% block admin_content -%}
<h1>{{ 'User creation'|trans }}</h1>
{{ form_start(form) }}
{{ form_row(form.username) }}
{{ form_row(form.email) }}
{{ form_row(form.plainPassword) }}
{{ form_widget(form.submit, { 'attr' : { 'class': 'btn btn-chill-blue' } }) }}
{{ form_end(form) }}
<ul class="record_actions">
<li>
<a href="{{ path('admin_user') }}">
{{ 'Back to the list'|trans }}
</a>
</li>
</ul>
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %}
{% endblock %}

View File

@ -1,25 +1,3 @@
{#
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS,
<info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
#}
{#
The basic layout of Chill. All the page / template of Chill must use this template.
#}
<!DOCTYPE html>
<html lang="fr">
<head>

View File

@ -1,26 +1,3 @@
{#
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS,
<info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
#}
{#
Layout with a vertical menu and a conainer for the content (like admin, export)
( for the vertical menu you can extends Menu/veticalMenu.html.twig ).
#}
{% extends "@ChillMain/layout.html.twig" %}
{% block sublayout_content %}

View File

@ -23,7 +23,7 @@ use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Chill\MainBundle\Security\Authorization\ChillExportVoter;
/**
*
*
*
*/
class AdminSectionMenuBuilder implements LocalMenuBuilderInterface
@ -33,30 +33,31 @@ class AdminSectionMenuBuilder implements LocalMenuBuilderInterface
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
public function __construct(AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
// all the entries below must have ROLE_ADMIN permissions
if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
return;
}
$menu->addChild('Users and permissions', [
'route' => 'chill_main_admin_permissions'
])
->setExtras([
'icons' => ['key'],
'order' => 200
'order' => 200,
'explain' => "Configure permissions for users"
]);
}
public static function getMenuIds(): array
{
return [ 'admin_section' ];
return [ 'admin_section', 'admin_index' ];
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Chill\MainBundle\Routing\MenuBuilder;
use Knp\Menu\MenuItem;
class PermissionMenuBuilder implements \Chill\MainBundle\Routing\LocalMenuBuilderInterface
{
/**
* @inheritDoc
*/
public static function getMenuIds(): array
{
return [ 'admin_permissions' ];
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
$menu->addChild('Permissions group list', [
'route' => 'admin_permissionsgroup'
])->setExtras([
'order' => 300
]);
$menu->addChild('crud.admin_user.index.title', [
'route' => 'chill_crud_admin_user_index'
])->setExtras(['order' => 400]);
$menu->addChild('List circles', [
'route' => 'admin_scope'
])->setExtras(['order' => 200]);
$menu->addChild('Center list', [
'route' => 'admin_center'
])->setExtras(['order' => 100]);
$menu->addChild('User jobs', [
'route' => 'chill_crud_admin_user_job_index'
])->setExtras(['order' => 150]);
}
}

View File

@ -2,12 +2,17 @@
namespace Chill\MainBundle\Tests\Controller;
use Chill\MainBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserControllerTest extends WebTestCase
{
private $client;
private array $toDelete = [];
public function setUp()
{
self::bootKernel();
@ -22,18 +27,14 @@ class UserControllerTest extends WebTestCase
public function testList()
{
// get the list
$crawler = $this->client->request('GET', '/fr/admin/user/');
$crawler = $this->client->request('GET', '/fr/admin/main/user');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(),
"Unexpected HTTP status code for GET /admin/user/");
$link = $crawler->selectLink('Ajouter un nouvel utilisateur')->link();
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $link);
$this->assertRegExp('|/fr/admin/user/new$|', $link->getUri());
"Unexpected HTTP status code for GET /admin/main/user");
}
public function testNew()
{
$crawler = $this->client->request('GET', '/fr/admin/user/new');
$crawler = $this->client->request('GET', '/fr/admin/main/user/new');
$username = 'Test_user'. uniqid();
$password = 'Password1234!';
@ -54,22 +55,15 @@ class UserControllerTest extends WebTestCase
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test_user")')->count(),
'Missing element td:contains("Test user")');
$update = $crawler->selectLink('Modifier')->link();
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $update);
$this->assertRegExp('|/fr/admin/user/[0-9]{1,}/edit$|', $update->getUri());
//test the auth of the new client
$this->isPasswordValid($username, $password);
return $update;
}
protected function isPasswordValid($username, $password)
{
/* @var $passwordEncoder \Symfony\Component\Security\Core\Encoder\UserPasswordEncoder */
$passwordEncoder = self::$kernel->getContainer()
->get('security.password_encoder');
$passwordEncoder = self::$container
->get(UserPasswordEncoderInterface::class);
$user = self::$kernel->getContainer()
->get('doctrine.orm.entity_manager')
@ -81,46 +75,33 @@ class UserControllerTest extends WebTestCase
/**
*
* @param \Symfony\Component\DomCrawler\Link $update
* @depends testNew
* @dataProvider dataGenerateUserId
*/
public function testUpdate(\Symfony\Component\DomCrawler\Link $update)
public function testUpdate(int $userId, string $username)
{
$crawler = $this->client->click($update);
$crawler = $this->client->request('GET', "/fr/admin/main/user/$userId/edit");
$username = 'Foo bar '.uniqid();
$form = $crawler->selectButton('Mettre à jour')->form(array(
$form = $crawler->selectButton('Enregistrer & fermer')->form(array(
'chill_mainbundle_user[username]' => $username,
));
$this->client->submit($form);
$crawler = $this->client->followRedirect();
// Check the element contains an attribute with value equals "Foo"
$this->assertGreaterThan(0, $crawler->filter('[value="'.$username.'"]')->count(),
'Missing element [value="Foo bar"]');
$updatePassword = $crawler->selectLink('Modifier le mot de passe')->link();
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $updatePassword);
$this->assertRegExp('|/fr/admin/user/[0-9]{1,}/edit_password$|',
$updatePassword->getUri());
return array('link' => $updatePassword, 'username' => $username);
$this->assertGreaterThan(0, $crawler->filter('[data-username="'.$username.'"]')->count(),
'Missing element [data-username="Foo bar"]');
}
/**
*
* @param \Symfony\Component\DomCrawler\Link $updatePassword
* @depends testUpdate
* @dataProvider dataGenerateUserId
*/
public function testUpdatePassword(array $params)
public function testUpdatePassword(int $userId, $username)
{
$link = $params['link'];
$username = $params['username'];
$crawler = $this->client->request('GET', "/fr/admin/user/$userId/edit_password");
$newPassword = '1234Password!';
$crawler = $this->client->click($link);
$form = $crawler->selectButton('Changer le mot de passe')->form(array(
'chill_mainbundle_user_password[new_password][first]' => $newPassword,
'chill_mainbundle_user_password[new_password][second]' => $newPassword,
@ -130,10 +111,38 @@ class UserControllerTest extends WebTestCase
$this->assertTrue($this->client->getResponse()->isRedirect(),
"the response is a redirection");
$this->client->followRedirect();
$this->isPasswordValid($username, $newPassword);
}
protected function tearDown()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
foreach ($this->toDelete as list($class, $id)) {
$obj = $em->getRepository($class)->find($id);
$em->remove($obj);
}
$em->flush();
}
public function dataGenerateUserId()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$user = new User();
$user->setUsername('Test_user '.uniqid());
$user->setPassword(self::$container->get(UserPasswordEncoderInterface::class)->encodePassword($user,
'password'));
$em->persist($user);
$em->flush();
$this->toDelete[] = [User::class, $user->getId()];
yield [ $user->getId(), $user->getUsername() ];
}
}

View File

@ -6,10 +6,6 @@ chill_main_admin_permissionsgroup:
resource: "@ChillMainBundle/config/routes/permissionsgroup.yaml"
prefix: "{_locale}/admin/permissionsgroup"
chill_main_admin_user:
resource: "@ChillMainBundle/config/routes/user.yaml"
prefix: "{_locale}/admin/user"
chill_main_admin_scope:
resource: "@ChillMainBundle/config/routes/scope.yaml"
prefix: "{_locale}/admin/scope"
@ -57,15 +53,15 @@ chill_main_homepage:
path: /{_locale}/homepage
controller: Chill\MainBundle\Controller\DefaultController::indexAction
chill_main_admin_central:
path: /{_locale}/admin
controller: Chill\MainBundle\Controller\AdminController::indexAction
options:
menus:
admin_permissions:
order: 0
label: Main admin menu
# chill_main_admin_central:
# path: /{_locale}/admin
# controller: Chill\MainBundle\Controller\AdminController::indexAction
# options:
# menus:
# admin_permissions:
# order: 0
# label: Main admin menu
#
chill_main_admin_permissions:
path: /{_locale}/admin/permissions
controller: Chill\MainBundle\Controller\AdminController::indexPermissionsAction

View File

@ -1,11 +1,6 @@
admin_center:
path: /
controller: Chill\MainBundle\Controller\CenterController::indexAction
options:
menus:
admin_permissions:
order: 100
label: Center list
admin_center_show:
path: /{id}/show
@ -14,11 +9,6 @@ admin_center_show:
admin_center_new:
path: /new
controller: Chill\MainBundle\Controller\CenterController::newAction
options:
menus:
admin_permissions:
order: 101
label: New center
admin_center_create:
path: /create

View File

@ -1,11 +1,6 @@
admin_permissionsgroup:
path: /
controller: Chill\MainBundle\Controller\PermissionsGroupController::indexAction
options:
menus:
admin_permissions:
order: 300
label: Permissions group list
admin_permissionsgroup_show:
path: /{id}/show
@ -14,11 +9,6 @@ admin_permissionsgroup_show:
admin_permissionsgroup_new:
path: /new
controller: Chill\MainBundle\Controller\PermissionsGroupController::newAction
options:
menus:
admin_permissions:
order: 301
label: New permission group
admin_permissionsgroup_create:
path: /create
@ -33,12 +23,12 @@ admin_permissionsgroup_update:
path: /{id}/update
controller: Chill\MainBundle\Controller\PermissionsGroupController::updateAction
methods: [POST, PUT]
admin_permissionsgroup_delete_role_scope:
path: /{pgid}/delete_link_role_scope/{rsid}
controller: Chill\MainBundle\Controller\PermissionsGroupController::deleteLinkRoleScopeAction
methods: [DELETE]
admin_permissionsgroup_add_role_scope:
path: /{id}/add_link_role_scope
controller: Chill\MainBundle\Controller\PermissionsGroupController::addLinkRoleScopeAction

View File

@ -1,11 +1,6 @@
admin_scope:
path: /
controller: Chill\MainBundle\Controller\ScopeController::indexAction
options:
menus:
admin_permissions:
order: 200
label: List circles
admin_scope_show:
path: /{id}/show
@ -14,11 +9,6 @@ admin_scope_show:
admin_scope_new:
path: /new
controller: Chill\MainBundle\Controller\ScopeController::newAction
options:
menus:
admin_permissions:
order: 201
label: New circle
admin_scope_create:
path: /create

View File

@ -1,54 +0,0 @@
admin_user:
path: /
controller: Chill\MainBundle\Controller\UserController::indexAction
options:
menus:
admin_permissions:
order: 400
label: List users
admin_user_show:
path: /{id}/show
controller: Chill\MainBundle\Controller\UserController::showAction
admin_user_new:
path: /new
controller: Chill\MainBundle\Controller\UserController::newAction
options:
menus:
admin_permissions:
order: 401
label: Add a new user
admin_user_create:
path: /create
controller: Chill\MainBundle\Controller\UserController::createAction
methods: POST
admin_user_edit:
path: /{id}/edit
controller: Chill\MainBundle\Controller\UserController::editAction
admin_user_edit_password:
path: /{id}/edit_password
controller: Chill\MainBundle\Controller\UserController::editPasswordAction
admin_user_update:
path: /{id}/update
controller: Chill\MainBundle\Controller\UserController::updateAction
methods: [POST, PUT]
admin_user_update_password:
path: /{id}/update_password
controller: Chill\MainBundle\Controller\UserController::updatePasswordAction
methods: [POST, PUT]
admin_user_delete_group_center:
path: /{uid}/delete_link_groupcenter/{gcid}
controller: Chill\MainBundle\Controller\UserController::deleteLinkGroupCenterAction
methods: [DELETE]
admin_user_add_group_center:
path: /{uid}/add_link_groupcenter
controller: Chill\MainBundle\Controller\UserController::addLinkGroupCenterAction
methods: [POST]

View File

@ -29,10 +29,8 @@ services:
tags: ['controller.service_arguments']
Chill\MainBundle\Controller\UserController:
arguments:
$logger: '@Psr\Log\LoggerInterface'
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
tags: ['controller.service_arguments']
autowire: true
autoconfigure: true
Chill\MainBundle\Controller\NotificationController:
arguments:

View File

@ -1,17 +1,22 @@
services:
Chill\MainBundle\Routing\MenuBuilder\:
resource: '../../Routing/MenuBuilder'
autowire: true
autoconfigure: true
Chill\MainBundle\Routing\MenuBuilder\UserMenuBuilder:
arguments:
$tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'
tags:
- { name: 'chill.menu_builder' }
Chill\MainBundle\Routing\MenuBuilder\SectionMenuBuilder:
arguments:
$authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
$translator: '@Symfony\Component\Translation\TranslatorInterface'
tags:
- { name: 'chill.menu_builder' }
Chill\MainBundle\Routing\MenuBuilder\AdminSectionMenuBuilder:
arguments:
$authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'

View File

@ -143,10 +143,8 @@ The role '%role%' has been removed: Le rôle "%role%" a été enlevé de ce grou
The role '%role%' on circle '%scope%' has been removed: Le rôle "%role%" sur le cercle "%scope%" a été enlevé de ce groupe de permission
#admin section for users
List users: Liste des utilisateurs
user list: Liste des utilisateurs
User edit: Modification d'un utilisateur
User creation: Créer un utilisateur
User'status: Statut de l'utilisateur
Disabled, the user is not allowed to login: Désactivé, l'utilisateur n'est pas autorisé à se connecter
Enabled, the user is active: Actif, l'utilisateur peut se connecter
@ -281,6 +279,16 @@ crud:
success: Les données ont été enregistrées
view:
link_duplicate: Dupliquer
## admin for users
admin_user:
index:
title: Utilisateurs
add_new: "Créer"
is_active: "Actif ?"
usernames: "Identifiants"
mains: "Champs principaux"
title_new: "Nouvel utilisateur"
title_edit: Modifier un utilisateur
No entities: Aucun élément
CHILL_FOO_SEE: Voir un élément

View File

@ -247,7 +247,6 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
if (\random_int(0, 10) > 3) {
// always add social scope:
$accompanyingPeriod->addScope($this->getReference('scope_social'));
var_dump(count($accompanyingPeriod->getScopes()));
$accompanyingPeriod->setAddressLocation($this->createAddress());
$manager->persist($accompanyingPeriod->getAddressLocation());

View File

@ -146,7 +146,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* groups={"general", "creation"}
* )
*/
private ?\DateTimeImmutable $deathdate;
private ?\DateTimeImmutable $deathdate = null;
/**
* The person's place of birth

View File

@ -66,7 +66,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
'routeParameters' => [
'id' => $period->getId()
]])
->setExtras(['order' => 40]);
->setExtras(['order' => 50]);
}

View File

@ -199,7 +199,7 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac
}
if (NULL !== $birthdate) {
$qb->andWhere($qb->expr()->eq('s.birthdate', ':birthdate'))
$qb->andWhere($qb->expr()->eq('p.birthdate', ':birthdate'))
->setParameter('birthdate', $birthdate);
}

View File

@ -1,35 +1,36 @@
<div class="banner banner-accompanying-course">
<div id="header-accompanying_course-name" class="header-name">
<div class="container-xxl">
<div class="row">
<div id="header-accompanying_course-name" class="header-name">
<div class="container-xxl">
<div class="row">
<div class="col-md-6 ps-md-5 ps-xxl-0">
<h1>
<i class="fa fa-random fa-fw"></i>
{{ 'Accompanying Course'|trans }}
<span class="id-number">{{ accompanyingCourse.id }}</span>
</h1>
</div>
<div class="col-md-6 ps-md-5 ps-xxl-0">
<h1>
<i class="fa fa-random fa-fw"></i>
{{ 'Accompanying Course'|trans }}
<span class="id-number">{{ accompanyingCourse.id }}</span>
</h1>
</div>
{# vue teleport fragment here #}
<div class="col-md-3 mt-3 mb-1 my-md-3" id="banner-flags"></div>
{# vue teleport fragment here #}
<div class="col-md-3 mt-3 mb-1 my-md-3" id="banner-flags"></div>
{# vue teleport fragment here #}
<div class="col-md-3 mt-1 mb-3 my-md-3 pe-md-5 pe-xxl-0" id="banner-status"></div>
{# vue teleport fragment here #}
<div class="col-md-3 mt-1 mb-3 my-md-3 pe-md-5 pe-xxl-0" id="banner-status"></div>
</div>
</div>
</div>
</div>
</div>
<div id="header-accompanying_course-details" class="header-details">
<div class="container-xxl">
<div class="row justify-content-md-right">
</div>
<div id="header-accompanying_course-details" class="header-details">
<div class="container-xxl">
<div
class="row justify-content-md-right">
{# vue teleport fragment here #}
<div class="col-md-10 ps-md-5 ps-xxl-0" id="banner-social-issues"></div>
{# vue teleport fragment here #}
<div class="col-md-10 ps-md-5 ps-xxl-0" id="banner-social-issues"></div>
</div>
</div>
</div>
<a id="banner-accompanying-course"></a>
</div>
</div>
</div>
<a id="banner-accompanying-course"></a>
</div>