mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-07-26 18:47:43 +00:00
Merge remote-tracking branch 'origin/master' into fix-person-tests
This commit is contained in:
commit
df00ac66f5
@ -11,9 +11,10 @@ before_script:
|
|||||||
- PGPASSWORD=$POSTGRES_PASSWORD psql -U $POSTGRES_USER -h db -c "CREATE EXTENSION IF NOT EXISTS unaccent; CREATE EXTENSION IF NOT EXISTS pg_trgm;"
|
- PGPASSWORD=$POSTGRES_PASSWORD psql -U $POSTGRES_USER -h db -c "CREATE EXTENSION IF NOT EXISTS unaccent; CREATE EXTENSION IF NOT EXISTS pg_trgm;"
|
||||||
# Install and run Composer
|
# Install and run Composer
|
||||||
- curl -sS https://getcomposer.org/installer | php
|
- curl -sS https://getcomposer.org/installer | php
|
||||||
- php composer.phar install
|
- php -d memory_limit=2G composer.phar install
|
||||||
- php tests/app/bin/console doctrine:migrations:migrate -n
|
- php tests/app/bin/console doctrine:migrations:migrate -n
|
||||||
- php tests/app/bin/console doctrine:fixtures:load -n
|
- php -d memory_limit=2G tests/app/bin/console doctrine:fixtures:load -n
|
||||||
|
- echo "before_script finished"
|
||||||
|
|
||||||
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
# See http://docs.gitlab.com/ee/ci/services/README.html for examples.
|
# See http://docs.gitlab.com/ee/ci/services/README.html for examples.
|
||||||
@ -38,4 +39,4 @@ variables:
|
|||||||
# Run our tests
|
# Run our tests
|
||||||
test:
|
test:
|
||||||
script:
|
script:
|
||||||
- bin/phpunit --colors=never
|
- php -d memory_limit=3G bin/phpunit --colors=never
|
||||||
|
@ -73,7 +73,8 @@
|
|||||||
"symfony/web-profiler-bundle": "^5.0",
|
"symfony/web-profiler-bundle": "^5.0",
|
||||||
"symfony/var-dumper": "4.*",
|
"symfony/var-dumper": "4.*",
|
||||||
"symfony/debug-bundle": "^5.1",
|
"symfony/debug-bundle": "^5.1",
|
||||||
"symfony/phpunit-bridge": "^5.2"
|
"symfony/phpunit-bridge": "^5.2",
|
||||||
|
"nelmio/alice": "^3.8"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"auto-scripts": {
|
"auto-scripts": {
|
||||||
|
@ -13,6 +13,9 @@ API
|
|||||||
|
|
||||||
Chill provides a basic framework to build REST api.
|
Chill provides a basic framework to build REST api.
|
||||||
|
|
||||||
|
Basic configuration
|
||||||
|
*******************
|
||||||
|
|
||||||
Configure a route
|
Configure a route
|
||||||
=================
|
=================
|
||||||
|
|
||||||
@ -34,7 +37,7 @@ You can also:
|
|||||||
* `How to create your custom normalizer <https://symfony.com/doc/current/serializer/custom_normalizer.html>`_
|
* `How to create your custom normalizer <https://symfony.com/doc/current/serializer/custom_normalizer.html>`_
|
||||||
|
|
||||||
Auto-loading the routes
|
Auto-loading the routes
|
||||||
***********************
|
=======================
|
||||||
|
|
||||||
Ensure that those lines are present in your file `app/config/routing.yml`:
|
Ensure that those lines are present in your file `app/config/routing.yml`:
|
||||||
|
|
||||||
@ -47,7 +50,7 @@ Ensure that those lines are present in your file `app/config/routing.yml`:
|
|||||||
|
|
||||||
|
|
||||||
Create your model
|
Create your model
|
||||||
*****************
|
=================
|
||||||
|
|
||||||
Create your model on the usual way:
|
Create your model on the usual way:
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ Create your model on the usual way:
|
|||||||
|
|
||||||
|
|
||||||
Configure api
|
Configure api
|
||||||
*************
|
=============
|
||||||
|
|
||||||
Configure the api using Yaml (see the full configuration: :ref:`api_full_configuration`):
|
Configure the api using Yaml (see the full configuration: :ref:`api_full_configuration`):
|
||||||
|
|
||||||
@ -171,7 +174,7 @@ Configure the api using Yaml (see the full configuration: :ref:`api_full_configu
|
|||||||
}
|
}
|
||||||
|
|
||||||
The :code:`_index` and :code:`_entity` action
|
The :code:`_index` and :code:`_entity` action
|
||||||
=============================================
|
*********************************************
|
||||||
|
|
||||||
The :code:`_index` and :code:`_entity` action are default actions:
|
The :code:`_index` and :code:`_entity` action are default actions:
|
||||||
|
|
||||||
@ -189,7 +192,7 @@ Entity:
|
|||||||
Path: :code:`/api/1.0/person/accompanying-period/origin/{id}.{_format}`
|
Path: :code:`/api/1.0/person/accompanying-period/origin/{id}.{_format}`
|
||||||
|
|
||||||
Role
|
Role
|
||||||
====
|
****
|
||||||
|
|
||||||
By default, the key `base_role` is used to check ACL. Take care of creating the :code:`Voter` required to take that into account.
|
By default, the key `base_role` is used to check ACL. Take care of creating the :code:`Voter` required to take that into account.
|
||||||
|
|
||||||
@ -216,7 +219,7 @@ You can also define a role for each method. In this case, this role is used for
|
|||||||
HEAD: MY ROLE_SEE
|
HEAD: MY ROLE_SEE
|
||||||
|
|
||||||
Customize the controller
|
Customize the controller
|
||||||
========================
|
************************
|
||||||
|
|
||||||
You can customize the controller by hooking into the default actions. Take care of extending :code:`Chill\MainBundle\CRUD\Controller\ApiController`.
|
You can customize the controller by hooking into the default actions. Take care of extending :code:`Chill\MainBundle\CRUD\Controller\ApiController`.
|
||||||
|
|
||||||
@ -264,7 +267,7 @@ And set your controller in configuration:
|
|||||||
HEAD: true
|
HEAD: true
|
||||||
|
|
||||||
Create your own actions
|
Create your own actions
|
||||||
=======================
|
***********************
|
||||||
|
|
||||||
You can add your own actions:
|
You can add your own actions:
|
||||||
|
|
||||||
@ -361,8 +364,297 @@ Then, create the corresponding action into your controller:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Managing association
|
||||||
|
********************
|
||||||
|
|
||||||
|
ManyToOne association
|
||||||
|
=====================
|
||||||
|
|
||||||
|
In ManyToOne association, you can add associated entities using the :code:`PATCH` request. By default, the serializer deserialize entities only with their id and discriminator type, if any.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
curl -X 'PATCH' \
|
||||||
|
'http://localhost:8001/api/1.0/person/accompanying-course/2668.json' \
|
||||||
|
-H 'accept: */*' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
# see the data sent to the server: \
|
||||||
|
-d '{
|
||||||
|
"type": "accompanying_period",
|
||||||
|
"id": 2668,
|
||||||
|
"origin": { "id": 11 }
|
||||||
|
}'
|
||||||
|
|
||||||
|
ManyToMany associations
|
||||||
|
=======================
|
||||||
|
|
||||||
|
In OneToMany association, you can easily create route for adding and removing entities, using :code:`POST` and :code:`DELETE` requests.
|
||||||
|
|
||||||
|
Prepare your entity, creating the methods :code:`addYourEntity` and :code:`removeYourEntity`:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
namespace Chill\PersonBundle\Entity;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AccompanyingPeriod Class
|
||||||
|
*
|
||||||
|
* @ORM\Entity
|
||||||
|
* @ORM\Table(name="chill_person_accompanying_period")
|
||||||
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
|
* "accompanying_period"=AccompanyingPeriod::class
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
class AccompanyingPeriod
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Collection
|
||||||
|
* @ORM\ManyToMany(
|
||||||
|
* targetEntity=Scope::class,
|
||||||
|
* cascade={}
|
||||||
|
* )
|
||||||
|
* @Groups({"read"})
|
||||||
|
*/
|
||||||
|
private $scopes;
|
||||||
|
|
||||||
|
public function addScope(Scope $scope): self
|
||||||
|
{
|
||||||
|
$this->scopes[] = $scope;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeScope(Scope $scope): void
|
||||||
|
{
|
||||||
|
$this->scopes->removeElement($scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Create your route into the configuration:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
chill_main:
|
||||||
|
apis:
|
||||||
|
-
|
||||||
|
class: Chill\PersonBundle\Entity\AccompanyingPeriod
|
||||||
|
name: accompanying_course
|
||||||
|
base_path: /api/1.0/person/accompanying-course
|
||||||
|
controller: Chill\PersonBundle\Controller\AccompanyingCourseApiController
|
||||||
|
actions:
|
||||||
|
scope:
|
||||||
|
methods:
|
||||||
|
POST: true
|
||||||
|
DELETE: true
|
||||||
|
GET: false
|
||||||
|
HEAD: false
|
||||||
|
PUT: false
|
||||||
|
PATCH: false
|
||||||
|
roles:
|
||||||
|
POST: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
|
||||||
|
DELETE: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
|
||||||
|
GET: null
|
||||||
|
HEAD: null
|
||||||
|
PUT: null
|
||||||
|
PATCH: null
|
||||||
|
controller_action: null
|
||||||
|
path: null
|
||||||
|
single-collection: single
|
||||||
|
|
||||||
|
This will create a new route, which will accept two methods: DELETE and POST:
|
||||||
|
|
||||||
|
.. code-block:: raw
|
||||||
|
|
||||||
|
+--------------+---------------------------------------------------------------------------------------+
|
||||||
|
| Property | Value |
|
||||||
|
+--------------+---------------------------------------------------------------------------------------+
|
||||||
|
| Route Name | chill_api_single_accompanying_course_scope |
|
||||||
|
| Path | /api/1.0/person/accompanying-course/{id}/scope.{_format} |
|
||||||
|
| Path Regex | {^/api/1\.0/person/accompanying\-course/(?P<id>[^/]++)/scope\.(?P<_format>[^/]++)$}sD |
|
||||||
|
| Host | ANY |
|
||||||
|
| Host Regex | |
|
||||||
|
| Scheme | ANY |
|
||||||
|
| Method | POST|DELETE |
|
||||||
|
| Requirements | {id}: \d+ |
|
||||||
|
| Class | Symfony\Component\Routing\Route |
|
||||||
|
| Defaults | _controller: csapi_accompanying_course_controller:scopeApi |
|
||||||
|
| Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
|
||||||
|
+--------------+---------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Then, create the controller action. Call the method:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
namespace Chill\PersonBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
|
||||||
|
class MyController extends ApiController
|
||||||
|
{
|
||||||
|
public function scopeApi($id, Request $request, string $_format): Response
|
||||||
|
{
|
||||||
|
return $this->addRemoveSomething('scope', $id, $request, $_format, 'scope', Scope::class, [ 'groups' => [ 'read' ] ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
This will allow to add a scope by his id, and delete them.
|
||||||
|
|
||||||
|
Curl requests:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# add a scope with id 5
|
||||||
|
curl -X 'POST' \
|
||||||
|
'http://localhost:8001/api/1.0/person/accompanying-course/2868/scope.json' \
|
||||||
|
-H 'accept: */*' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{
|
||||||
|
"type": "scope",
|
||||||
|
"id": 5
|
||||||
|
}'
|
||||||
|
|
||||||
|
# remove a scope with id 5
|
||||||
|
curl -X 'DELETE' \
|
||||||
|
'http://localhost:8001/api/1.0/person/accompanying-course/2868/scope.json' \
|
||||||
|
-H 'accept: */*' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{
|
||||||
|
"id": 5,
|
||||||
|
"type": "scope"
|
||||||
|
}'
|
||||||
|
|
||||||
|
Deserializing an association where multiple types are allowed
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
Sometimes, multiples types are allowed as association to one entity:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
class Resource
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
|
||||||
|
* @ORM\JoinColumn(nullable=true)
|
||||||
|
*/
|
||||||
|
private $thirdParty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity=Person::class)
|
||||||
|
* @ORM\JoinColumn(nullable=true)
|
||||||
|
*/
|
||||||
|
private $person;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param $resource Person|ThirdParty
|
||||||
|
*/
|
||||||
|
public function setResource($resource): self
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ThirdParty|Person
|
||||||
|
* @Groups({"read", "write"})
|
||||||
|
*/
|
||||||
|
public function getResource()
|
||||||
|
{
|
||||||
|
return $this->person ?? $this->thirdParty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
This is not well taken into account by the Symfony serializer natively.
|
||||||
|
|
||||||
|
You must, then, create your own CustomNormalizer. You can help yourself using this:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
namespace Chill\PersonBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
||||||
|
use Chill\PersonBundle\Repository\AccompanyingPeriod\ResourceRepository;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
|
||||||
|
use Symfony\Component\Serializer\Exception;
|
||||||
|
use Chill\MainBundle\Serializer\Normalizer\DiscriminatedObjectDenormalizer;
|
||||||
|
|
||||||
|
|
||||||
|
class AccompanyingPeriodResourceNormalizer implements DenormalizerInterface, DenormalizerAwareInterface
|
||||||
|
{
|
||||||
|
use DenormalizerAwareTrait;
|
||||||
|
use ObjectToPopulateTrait;
|
||||||
|
|
||||||
|
public function __construct(ResourceRepository $repository)
|
||||||
|
{
|
||||||
|
$this->repository = $repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
// .. snipped for brevity
|
||||||
|
|
||||||
|
if ($resource === NULL) {
|
||||||
|
$resource = new Resource();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\array_key_exists('resource', $data)) {
|
||||||
|
$res = $this->denormalizer->denormalize(
|
||||||
|
$data['resource'],
|
||||||
|
// call for a "multiple type"
|
||||||
|
DiscriminatedObjectDenormalizer::TYPE,
|
||||||
|
$format,
|
||||||
|
// into the context, we add the list of allowed types:
|
||||||
|
[
|
||||||
|
DiscriminatedObjectDenormalizer::ALLOWED_TYPES =>
|
||||||
|
[
|
||||||
|
Person::class, ThirdParty::class
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$resource->setResource($res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function supportsDenormalization($data, string $type, string $format = null)
|
||||||
|
{
|
||||||
|
return $type === Resource::class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Serialization for collection
|
Serialization for collection
|
||||||
============================
|
****************************
|
||||||
|
|
||||||
A specific model has been defined for returning collection:
|
A specific model has been defined for returning collection:
|
||||||
|
|
||||||
@ -381,8 +673,9 @@ A specific model has been defined for returning collection:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Where this is relevant, this model should be re-used in custom controller actions.
|
||||||
|
|
||||||
This can be achieved quickly by assembling results into a :code:`Chill\MainBundle\Serializer\Model\Collection`. The pagination information is given by using :code:`Paginator` (see :ref:`Pagination <pagination-ref>`).
|
In custom actions, this can be achieved quickly by assembling results into a :code:`Chill\MainBundle\Serializer\Model\Collection`. The pagination information is given by using :code:`Paginator` (see :ref:`Pagination <pagination-ref>`).
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -400,10 +693,11 @@ This can be achieved quickly by assembling results into a :code:`Chill\MainBundl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.. _api_full_configuration:
|
.. _api_full_configuration:
|
||||||
|
|
||||||
Full configuration example
|
Full configuration example
|
||||||
==========================
|
**************************
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ Chill will be available at ``http://localhost:8001.`` Currently, there isn't any
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker-compose exec --user $(id -u) php bin/console doctrine:fixtures:load
|
docker-compose exec --user $(id -u) php bin/console doctrine:fixtures:load --purge-with-truncate
|
||||||
|
|
||||||
There are several users available:
|
There are several users available:
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
<testsuite name="MainBundle">
|
<testsuite name="MainBundle">
|
||||||
<directory suffix="Test.php">src/Bundle/ChillMainBundle/Tests/</directory>
|
<directory suffix="Test.php">src/Bundle/ChillMainBundle/Tests/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
<!-- remove tests for person temporarily
|
||||||
<testsuite name="PersonBundle">
|
<testsuite name="PersonBundle">
|
||||||
<directory suffix="Test.php">src/Bundle/ChillPersonBundle/Tests/</directory>
|
<directory suffix="Test.php">src/Bundle/ChillPersonBundle/Tests/</directory>
|
||||||
<!-- test for export will be runned later -->
|
<!-- test for export will be runned later -->
|
||||||
@ -29,7 +30,8 @@
|
|||||||
<!-- find a solution to create multiple configs -->
|
<!-- find a solution to create multiple configs -->
|
||||||
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php</exclude>
|
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php</exclude>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
-->
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
<listeners>
|
<listeners>
|
||||||
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
|
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
|
||||||
|
@ -5,7 +5,7 @@ namespace Chill\MainBundle\CRUD\Controller;
|
|||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||||
|
|
||||||
@ -25,12 +25,33 @@ class AbstractCRUDController extends AbstractController
|
|||||||
*
|
*
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @return object
|
* @return object
|
||||||
|
* @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found
|
||||||
*/
|
*/
|
||||||
protected function getEntity($action, $id, Request $request): ?object
|
protected function getEntity($action, $id, Request $request): object
|
||||||
{
|
{
|
||||||
return $this->getDoctrine()
|
$e = $this->getDoctrine()
|
||||||
->getRepository($this->getEntityClass())
|
->getRepository($this->getEntityClass())
|
||||||
->find($id);
|
->find($id);
|
||||||
|
|
||||||
|
if (NULL === $e) {
|
||||||
|
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an entity.
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param Request $request
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
protected function createEntity(string $action, Request $request): object
|
||||||
|
{
|
||||||
|
$type = $this->getEntityClass();
|
||||||
|
|
||||||
|
return new $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -222,4 +243,9 @@ class AbstractCRUDController extends AbstractController
|
|||||||
{
|
{
|
||||||
return $this->container->get('chill_main.paginator_factory');
|
return $this->container->get('chill_main.paginator_factory');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getValidator(): ValidatorInterface
|
||||||
|
{
|
||||||
|
return $this->get('validator');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,10 @@ use Symfony\Component\Routing\Annotation\Route;
|
|||||||
use Symfony\Component\Serializer\SerializerInterface;
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
use Chill\MainBundle\Serializer\Model\Collection;
|
use Chill\MainBundle\Serializer\Model\Collection;
|
||||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||||
|
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
|
||||||
|
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
||||||
|
|
||||||
class ApiController extends AbstractCRUDController
|
class ApiController extends AbstractCRUDController
|
||||||
{
|
{
|
||||||
@ -38,11 +42,6 @@ class ApiController extends AbstractCRUDController
|
|||||||
return $postFetch;
|
return $postFetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NULL === $entity) {
|
|
||||||
throw $this->createNotFoundException(sprintf("The %s with id %s "
|
|
||||||
. "is not found", $this->getCrudName(), $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = $this->checkACL($action, $request, $_format, $entity);
|
$response = $this->checkACL($action, $request, $_format, $entity);
|
||||||
if ($response instanceof Response) {
|
if ($response instanceof Response) {
|
||||||
return $response;
|
return $response;
|
||||||
@ -81,12 +80,186 @@ class ApiController extends AbstractCRUDController
|
|||||||
{
|
{
|
||||||
switch ($request->getMethod()) {
|
switch ($request->getMethod()) {
|
||||||
case Request::METHOD_GET:
|
case Request::METHOD_GET:
|
||||||
case REQUEST::METHOD_HEAD:
|
case Request::METHOD_HEAD:
|
||||||
return $this->entityGet('_entity', $request, $id, $_format);
|
return $this->entityGet('_entity', $request, $id, $_format);
|
||||||
|
case Request::METHOD_PUT:
|
||||||
|
case Request::METHOD_PATCH:
|
||||||
|
return $this->entityPut('_entity', $request, $id, $_format);
|
||||||
|
case Request::METHOD_POST:
|
||||||
|
return $this->entityPostAction('_entity', $request, $id, $_format);
|
||||||
default:
|
default:
|
||||||
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This method is not implemented");
|
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This method is not implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function entityPost(Request $request, $_format): Response
|
||||||
|
{
|
||||||
|
switch($request->getMethod()) {
|
||||||
|
case Request::METHOD_POST:
|
||||||
|
return $this->entityPostAction('_entity', $request, $_format);
|
||||||
|
default:
|
||||||
|
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This method is not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function entityPostAction($action, Request $request, string $_format): Response
|
||||||
|
{
|
||||||
|
$entity = $this->createEntity($action, $request);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$entity = $this->deserialize($action, $request, $_format, $entity);
|
||||||
|
} catch (NotEncodableValueException $e) {
|
||||||
|
throw new BadRequestException("invalid json", 400, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = $this->validate($action, $request, $_format, $entity);
|
||||||
|
|
||||||
|
$response = $this->onAfterValidation($action, $request, $_format, $entity, $errors);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors->count() > 0) {
|
||||||
|
$response = $this->json($errors);
|
||||||
|
$response->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->checkACL($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getDoctrine()->getManager()->persist($entity);
|
||||||
|
$this->getDoctrine()->getManager()->flush();
|
||||||
|
|
||||||
|
$response = $this->onAfterFlush($action, $request, $_format, $entity, $errors);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
$response = $this->onBeforeSerialize($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json(
|
||||||
|
$entity,
|
||||||
|
Response::HTTP_OK,
|
||||||
|
[],
|
||||||
|
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public function entityPut($action, Request $request, $id, string $_format): Response
|
||||||
|
{
|
||||||
|
$entity = $this->getEntity($action, $id, $request, $_format);
|
||||||
|
|
||||||
|
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
|
||||||
|
if ($postFetch instanceof Response) {
|
||||||
|
return $postFetch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL === $entity) {
|
||||||
|
throw $this->createNotFoundException(sprintf("The %s with id %s "
|
||||||
|
. "is not found", $this->getCrudName(), $id));
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->checkACL($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->onBeforeSerialize($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$entity = $this->deserialize($action, $request, $_format, $entity);
|
||||||
|
} catch (NotEncodableValueException $e) {
|
||||||
|
throw new BadRequestException("invalid json", 400, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = $this->validate($action, $request, $_format, $entity);
|
||||||
|
|
||||||
|
$response = $this->onAfterValidation($action, $request, $_format, $entity, $errors);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors->count() > 0) {
|
||||||
|
$response = $this->json($errors);
|
||||||
|
$response->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getDoctrine()->getManager()->flush();
|
||||||
|
|
||||||
|
$response = $this->onAfterFlush($action, $request, $_format, $entity, $errors);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json(
|
||||||
|
$entity,
|
||||||
|
Response::HTTP_OK,
|
||||||
|
[],
|
||||||
|
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function onAfterValidation(string $action, Request $request, string $_format, $entity, ConstraintViolationListInterface $errors, array $more = []): ?Response
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function onAfterFlush(string $action, Request $request, string $_format, $entity, ConstraintViolationListInterface $errors, array $more = []): ?Response
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getValidationGroups(string $action, Request $request, string $_format, $entity): ?array
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validate(string $action, Request $request, string $_format, $entity, array $more = []): ConstraintViolationListInterface
|
||||||
|
{
|
||||||
|
$validationGroups = $this->getValidationGroups($action, $request, $_format, $entity);
|
||||||
|
|
||||||
|
return $this->getValidator()->validate($entity, null, $validationGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize the content of the request into the class associated with the curd
|
||||||
|
*/
|
||||||
|
protected function deserialize(string $action, Request $request, string $_format, $entity = null): object
|
||||||
|
{
|
||||||
|
$default = [];
|
||||||
|
|
||||||
|
if (NULL !== $entity) {
|
||||||
|
$default[AbstractNormalizer::OBJECT_TO_POPULATE] = $entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
$context = \array_merge(
|
||||||
|
$default,
|
||||||
|
$this->getContextForSerialization($action, $request, $_format, $entity)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->getSerializer()->deserialize($request->getContent(), $this->getEntityClass(), $_format, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base action for indexing entities
|
* Base action for indexing entities
|
||||||
@ -173,6 +346,110 @@ class ApiController extends AbstractCRUDController
|
|||||||
return $this->serializeCollection($action, $request, $_format, $paginator, $entities);
|
return $this->serializeCollection($action, $request, $_format, $paginator, $entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or remove an associated entity, using `add` and `remove` methods.
|
||||||
|
*
|
||||||
|
* This method:
|
||||||
|
*
|
||||||
|
* 1. Fetch the base entity (throw 404 if not found)
|
||||||
|
* 2. checkACL,
|
||||||
|
* 3. run onPostCheckACL, return response if any,
|
||||||
|
* 4. deserialize posted data into the entity given by $postedDataType, with the context in $postedDataContext
|
||||||
|
* 5. run 'add+$property' for POST method, or 'remove+$property' for DELETE method
|
||||||
|
* 6. validate the base entity (not the deserialized one). Groups are fetched from getValidationGroups, validation is perform by `validate`
|
||||||
|
* 7. run onAfterValidation
|
||||||
|
* 8. if errors, return a 422 response with errors
|
||||||
|
* 9. flush the data
|
||||||
|
* 10. run onAfterFlush
|
||||||
|
* 11. return a 202 response for DELETE with empty body, or HTTP 200 for post with serialized posted entity
|
||||||
|
*
|
||||||
|
* @param string action
|
||||||
|
* @param mixed id
|
||||||
|
* @param Request $request
|
||||||
|
* @param string $_format
|
||||||
|
* @param string $property the name of the property. This will be used to make a `add+$property` and `remove+$property` method
|
||||||
|
* @param string $postedDataType the type of the posted data (the content)
|
||||||
|
* @param string $postedDataContext a context to deserialize posted data (the content)
|
||||||
|
* @throw BadRequestException if unable to deserialize the posted data
|
||||||
|
* @throw BadRequestException if the method is not POST or DELETE
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function addRemoveSomething(string $action, $id, Request $request, string $_format, string $property, string $postedDataType, $postedDataContext = []): Response
|
||||||
|
{
|
||||||
|
$entity = $this->getEntity($action, $id, $request);
|
||||||
|
|
||||||
|
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
|
||||||
|
if ($postFetch instanceof Response) {
|
||||||
|
return $postFetch;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->checkACL($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->onBeforeSerialize($action, $request, $_format, $entity);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$postedData = $this->getSerializer()->deserialize($request->getContent(), $postedDataType, $_format, $postedDataContext);
|
||||||
|
} catch (\Symfony\Component\Serializer\Exception\UnexpectedValueException $e) {
|
||||||
|
throw new BadRequestException(sprintf("Unable to deserialize posted ".
|
||||||
|
"data: %s", $e->getMessage()), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($request->getMethod()) {
|
||||||
|
case Request::METHOD_DELETE:
|
||||||
|
// oups... how to use property accessor to remove element ?
|
||||||
|
$entity->{'remove'.\ucfirst($property)}($postedData);
|
||||||
|
break;
|
||||||
|
case Request::METHOD_POST:
|
||||||
|
$entity->{'add'.\ucfirst($property)}($postedData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BadRequestException("this method is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = $this->validate($action, $request, $_format, $entity, [$postedData]);
|
||||||
|
|
||||||
|
$response = $this->onAfterValidation($action, $request, $_format, $entity, $errors, [$postedData]);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors->count() > 0) {
|
||||||
|
// only format accepted
|
||||||
|
return $this->json($errors, 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getDoctrine()->getManager()->flush();
|
||||||
|
|
||||||
|
|
||||||
|
$response = $this->onAfterFlush($action, $request, $_format, $entity, $errors, [$postedData]);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($request->getMethod()) {
|
||||||
|
case Request::METHOD_DELETE:
|
||||||
|
return $this->json('', Response::HTTP_OK);
|
||||||
|
case Request::METHOD_POST:
|
||||||
|
return $this->json(
|
||||||
|
$postedData,
|
||||||
|
Response::HTTP_OK,
|
||||||
|
[],
|
||||||
|
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity, [$postedData])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize collections
|
* Serialize collections
|
||||||
*
|
*
|
||||||
@ -189,7 +466,27 @@ class ApiController extends AbstractCRUDController
|
|||||||
|
|
||||||
protected function getContextForSerialization(string $action, Request $request, string $_format, $entity): array
|
protected function getContextForSerialization(string $action, Request $request, string $_format, $entity): array
|
||||||
{
|
{
|
||||||
return [];
|
switch ($request->getMethod()) {
|
||||||
|
case Request::METHOD_GET:
|
||||||
|
return [ 'groups' => [ 'read' ]];
|
||||||
|
case Request::METHOD_PUT:
|
||||||
|
case Request::METHOD_PATCH:
|
||||||
|
case Request::METHOD_POST:
|
||||||
|
return [ 'groups' => [ 'write' ]];
|
||||||
|
default:
|
||||||
|
throw new \LogicException("get context for serialization is not implemented for this method");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the context for serialization post alter query (in case of
|
||||||
|
* PATCH, PUT, or POST method)
|
||||||
|
*
|
||||||
|
* This is called **after** the entity was altered.
|
||||||
|
*/
|
||||||
|
protected function getContextForSerializationPostAlter(string $action, Request $request, string $_format, $entity, array $more = []): array
|
||||||
|
{
|
||||||
|
return [ 'groups' => [ 'read' ]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -183,48 +183,26 @@ class CRUDRoutesLoader extends Loader
|
|||||||
$methods = \array_keys(\array_filter($action['methods'], function($value, $key) { return $value; },
|
$methods = \array_keys(\array_filter($action['methods'], function($value, $key) { return $value; },
|
||||||
ARRAY_FILTER_USE_BOTH));
|
ARRAY_FILTER_USE_BOTH));
|
||||||
|
|
||||||
$route = new Route($path, $defaults, $requirements);
|
if (count($methods) === 0) {
|
||||||
$route->setMethods($methods);
|
throw new \RuntimeException("The api configuration named \"{$crudConfig['name']}\", action \"{$name}\", ".
|
||||||
|
"does not have any allowed methods. You should remove this action from the config ".
|
||||||
|
"or allow, at least, one method");
|
||||||
|
}
|
||||||
|
|
||||||
$collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route);
|
if ('_entity' === $name && \in_array(Request::METHOD_POST, $methods)) {
|
||||||
}
|
unset($methods[\array_search(Request::METHOD_POST, $methods)]);
|
||||||
|
$entityPostRoute = $this->createEntityPostRoute($name, $crudConfig, $action,
|
||||||
|
$controller);
|
||||||
|
$collection->add("chill_api_single_{$crudConfig['name']}_{$name}_create",
|
||||||
|
$entityPostRoute);
|
||||||
|
}
|
||||||
|
|
||||||
return $collection;
|
if (count($methods) === 0) {
|
||||||
}
|
// the only method was POST,
|
||||||
|
// continue to next
|
||||||
/**
|
|
||||||
* Load routes for api multi
|
|
||||||
*
|
|
||||||
* @param $crudConfig
|
|
||||||
* @return RouteCollection
|
|
||||||
*/
|
|
||||||
protected function loadApiMultiConfig(array $crudConfig): RouteCollection
|
|
||||||
{
|
|
||||||
$collection = new RouteCollection();
|
|
||||||
$controller ='csapi_'.$crudConfig['name'].'_controller';
|
|
||||||
|
|
||||||
foreach ($crudConfig['actions'] as $name => $action) {
|
|
||||||
// filter only on single actions
|
|
||||||
$singleCollection = $action['single-collection'] ?? $name === '_index' ? 'collection' : NULL;
|
|
||||||
if ('single' === $singleCollection) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaults = [
|
|
||||||
'_controller' => $controller.':'.($action['controller_action'] ?? '_entity' === $name ? 'entityApi' : $name.'Api')
|
|
||||||
];
|
|
||||||
|
|
||||||
// path are rewritten
|
|
||||||
// if name === 'default', we rewrite it to nothing :-)
|
|
||||||
$localName = '_entity' === $name ? '' : '/'.$name;
|
|
||||||
$localPath = $action['path'] ?? '/{id}'.$localName.'.{_format}';
|
|
||||||
$path = $crudConfig['base_path'].$localPath;
|
|
||||||
|
|
||||||
$requirements = $action['requirements'] ?? [ '{id}' => '\d+' ];
|
|
||||||
|
|
||||||
$methods = \array_keys(\array_filter($action['methods'], function($value, $key) { return $value; },
|
|
||||||
ARRAY_FILTER_USE_BOTH));
|
|
||||||
|
|
||||||
$route = new Route($path, $defaults, $requirements);
|
$route = new Route($path, $defaults, $requirements);
|
||||||
$route->setMethods($methods);
|
$route->setMethods($methods);
|
||||||
|
|
||||||
@ -233,4 +211,18 @@ class CRUDRoutesLoader extends Loader
|
|||||||
|
|
||||||
return $collection;
|
return $collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function createEntityPostRoute(string $name, $crudConfig, array $action, $controller): Route
|
||||||
|
{
|
||||||
|
$localPath = $action['path'].'.{_format}';
|
||||||
|
$defaults = [
|
||||||
|
'_controller' => $controller.':'.($action['controller_action'] ?? 'entityPost')
|
||||||
|
];
|
||||||
|
$path = $crudConfig['base_path'].$localPath;
|
||||||
|
$requirements = $action['requirements'] ?? [];
|
||||||
|
$route = new Route($path, $defaults, $requirements);
|
||||||
|
$route->setMethods([ Request::METHOD_POST ]);
|
||||||
|
|
||||||
|
return $route;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Controller;
|
namespace Chill\MainBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Serializer\Model\Collection;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Chill\MainBundle\Search\UnknowSearchDomainException;
|
use Chill\MainBundle\Search\UnknowSearchDomainException;
|
||||||
@ -34,6 +35,7 @@ use Symfony\Component\HttpFoundation\JsonResponse;
|
|||||||
use Chill\MainBundle\Search\SearchProvider;
|
use Chill\MainBundle\Search\SearchProvider;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
|
use Chill\MainBundle\Search\SearchApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class SearchController
|
* Class SearchController
|
||||||
@ -42,32 +44,24 @@ use Chill\MainBundle\Pagination\PaginatorFactory;
|
|||||||
*/
|
*/
|
||||||
class SearchController extends AbstractController
|
class SearchController extends AbstractController
|
||||||
{
|
{
|
||||||
/**
|
protected SearchProvider $searchProvider;
|
||||||
*
|
|
||||||
* @var SearchProvider
|
|
||||||
*/
|
|
||||||
protected $searchProvider;
|
|
||||||
|
|
||||||
/**
|
protected TranslatorInterface $translator;
|
||||||
*
|
|
||||||
* @var TranslatorInterface
|
|
||||||
*/
|
|
||||||
protected $translator;
|
|
||||||
|
|
||||||
/**
|
protected PaginatorFactory $paginatorFactory;
|
||||||
*
|
|
||||||
* @var PaginatorFactory
|
protected SearchApi $searchApi;
|
||||||
*/
|
|
||||||
protected $paginatorFactory;
|
|
||||||
|
|
||||||
function __construct(
|
function __construct(
|
||||||
SearchProvider $searchProvider,
|
SearchProvider $searchProvider,
|
||||||
TranslatorInterface $translator,
|
TranslatorInterface $translator,
|
||||||
PaginatorFactory $paginatorFactory
|
PaginatorFactory $paginatorFactory,
|
||||||
|
SearchApi $searchApi
|
||||||
) {
|
) {
|
||||||
$this->searchProvider = $searchProvider;
|
$this->searchProvider = $searchProvider;
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
$this->paginatorFactory = $paginatorFactory;
|
$this->paginatorFactory = $paginatorFactory;
|
||||||
|
$this->searchApi = $searchApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -153,6 +147,19 @@ class SearchController extends AbstractController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function searchApi(Request $request, $_format): JsonResponse
|
||||||
|
{
|
||||||
|
//TODO this is an incomplete implementation
|
||||||
|
$query = $request->query->get('q', '');
|
||||||
|
|
||||||
|
$results = $this->searchApi->getResults($query, 0, 150);
|
||||||
|
$paginator = $this->paginatorFactory->create(count($results));
|
||||||
|
|
||||||
|
$collection = new Collection($results, $paginator);
|
||||||
|
|
||||||
|
return $this->json($collection);
|
||||||
|
}
|
||||||
|
|
||||||
public function advancedSearchListAction(Request $request)
|
public function advancedSearchListAction(Request $request)
|
||||||
{
|
{
|
||||||
/* @var $variable Chill\MainBundle\Search\SearchProvider */
|
/* @var $variable Chill\MainBundle\Search\SearchProvider */
|
||||||
|
@ -55,11 +55,11 @@ class LoadCenters extends AbstractFixture implements OrderedFixtureInterface
|
|||||||
public function load(ObjectManager $manager)
|
public function load(ObjectManager $manager)
|
||||||
{
|
{
|
||||||
foreach (static::$centers as $new) {
|
foreach (static::$centers as $new) {
|
||||||
$centerA = new Center();
|
$center = new Center();
|
||||||
$centerA->setName($new['name']);
|
$center->setName($new['name']);
|
||||||
|
|
||||||
$manager->persist($centerA);
|
$manager->persist($center);
|
||||||
$this->addReference($new['ref'], $centerA);
|
$this->addReference($new['ref'], $center);
|
||||||
static::$refs[] = $new['ref'];
|
static::$refs[] = $new['ref'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@ use Chill\MainBundle\Doctrine\DQL\OverlapsI;
|
|||||||
use Symfony\Component\DependencyInjection\Definition;
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
use Chill\MainBundle\Doctrine\DQL\Replace;
|
use Chill\MainBundle\Doctrine\DQL\Replace;
|
||||||
|
use Chill\MainBundle\Doctrine\Type\NativeDateIntervalType;
|
||||||
|
use Chill\MainBundle\Doctrine\Type\PointType;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ChillMainExtension
|
* Class ChillMainExtension
|
||||||
@ -166,37 +169,49 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
|||||||
$container->prependExtensionConfig('twig', $twigConfig);
|
$container->prependExtensionConfig('twig', $twigConfig);
|
||||||
|
|
||||||
//add DQL function to ORM (default entity_manager)
|
//add DQL function to ORM (default entity_manager)
|
||||||
$container->prependExtensionConfig('doctrine', array(
|
$container
|
||||||
'orm' => array(
|
->prependExtensionConfig(
|
||||||
'dql' => array(
|
'doctrine',
|
||||||
'string_functions' => array(
|
[
|
||||||
'unaccent' => Unaccent::class,
|
'orm' => [
|
||||||
'GET_JSON_FIELD_BY_KEY' => GetJsonFieldByKey::class,
|
'dql' => [
|
||||||
'AGGREGATE' => JsonAggregate::class,
|
'string_functions' => [
|
||||||
'REPLACE' => Replace::class,
|
'unaccent' => Unaccent::class,
|
||||||
),
|
'GET_JSON_FIELD_BY_KEY' => GetJsonFieldByKey::class,
|
||||||
'numeric_functions' => [
|
'AGGREGATE' => JsonAggregate::class,
|
||||||
'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class,
|
'REPLACE' => Replace::class,
|
||||||
'SIMILARITY' => Similarity::class,
|
],
|
||||||
'OVERLAPSI' => OverlapsI::class
|
'numeric_functions' => [
|
||||||
]
|
'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class,
|
||||||
)
|
'SIMILARITY' => Similarity::class,
|
||||||
)
|
'OVERLAPSI' => OverlapsI::class,
|
||||||
));
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
//add dbal types (default entity_manager)
|
//add dbal types (default entity_manager)
|
||||||
$container->prependExtensionConfig('doctrine', array(
|
$container
|
||||||
'dbal' => [
|
->prependExtensionConfig(
|
||||||
'types' => [
|
'doctrine',
|
||||||
'dateinterval' => [
|
[
|
||||||
'class' => \Chill\MainBundle\Doctrine\Type\NativeDateIntervalType::class
|
'dbal' => [
|
||||||
],
|
// This is mandatory since we are using postgis as database.
|
||||||
'point' => [
|
'mapping_types' => [
|
||||||
'class' => \Chill\MainBundle\Doctrine\Type\PointType::class
|
'geometry' => 'string',
|
||||||
]
|
],
|
||||||
]
|
'types' => [
|
||||||
]
|
'dateinterval' => [
|
||||||
));
|
'class' => NativeDateIntervalType::class
|
||||||
|
],
|
||||||
|
'point' => [
|
||||||
|
'class' => PointType::class
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
//add current route to chill main
|
//add current route to chill main
|
||||||
$container->prependExtensionConfig('chill_main', array(
|
$container->prependExtensionConfig('chill_main', array(
|
||||||
@ -212,6 +227,9 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
|||||||
$container->prependExtensionConfig('monolog', array(
|
$container->prependExtensionConfig('monolog', array(
|
||||||
'channels' => array('chill')
|
'channels' => array('chill')
|
||||||
));
|
));
|
||||||
|
|
||||||
|
//add crud api
|
||||||
|
$this->prependCruds($container);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -235,4 +253,97 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
|||||||
|
|
||||||
// Note: the controller are loaded inside compiler pass
|
// Note: the controller are loaded inside compiler pass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*/
|
||||||
|
protected function prependCruds(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$container->prependExtensionConfig('chill_main', [
|
||||||
|
'apis' => [
|
||||||
|
[
|
||||||
|
'class' => \Chill\MainBundle\Entity\Address::class,
|
||||||
|
'name' => 'address',
|
||||||
|
'base_path' => '/api/1.0/main/address',
|
||||||
|
'base_role' => 'ROLE_USER',
|
||||||
|
'actions' => [
|
||||||
|
'_index' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'_entity' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_POST => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'class' => \Chill\MainBundle\Entity\AddressReference::class,
|
||||||
|
'name' => 'address_reference',
|
||||||
|
'base_path' => '/api/1.0/main/address-reference',
|
||||||
|
'base_role' => 'ROLE_USER',
|
||||||
|
'actions' => [
|
||||||
|
'_index' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'_entity' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'class' => \Chill\MainBundle\Entity\PostalCode::class,
|
||||||
|
'name' => 'postal_code',
|
||||||
|
'base_path' => '/api/1.0/main/postal-code',
|
||||||
|
'base_role' => 'ROLE_USER',
|
||||||
|
'actions' => [
|
||||||
|
'_index' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'_entity' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'class' => \Chill\MainBundle\Entity\Country::class,
|
||||||
|
'name' => 'country',
|
||||||
|
'base_path' => '/api/1.0/main/country',
|
||||||
|
'base_role' => 'ROLE_USER',
|
||||||
|
'actions' => [
|
||||||
|
'_index' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'_entity' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,6 +221,7 @@ class Configuration implements ConfigurationInterface
|
|||||||
->booleanNode(Request::METHOD_POST)->defaultFalse()->end()
|
->booleanNode(Request::METHOD_POST)->defaultFalse()->end()
|
||||||
->booleanNode(Request::METHOD_DELETE)->defaultFalse()->end()
|
->booleanNode(Request::METHOD_DELETE)->defaultFalse()->end()
|
||||||
->booleanNode(Request::METHOD_PUT)->defaultFalse()->end()
|
->booleanNode(Request::METHOD_PUT)->defaultFalse()->end()
|
||||||
|
->booleanNode(Request::METHOD_PATCH)->defaultFalse()->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->arrayNode('roles')
|
->arrayNode('roles')
|
||||||
@ -232,6 +233,7 @@ class Configuration implements ConfigurationInterface
|
|||||||
->scalarNode(Request::METHOD_POST)->defaultNull()->end()
|
->scalarNode(Request::METHOD_POST)->defaultNull()->end()
|
||||||
->scalarNode(Request::METHOD_DELETE)->defaultNull()->end()
|
->scalarNode(Request::METHOD_DELETE)->defaultNull()->end()
|
||||||
->scalarNode(Request::METHOD_PUT)->defaultNull()->end()
|
->scalarNode(Request::METHOD_PUT)->defaultNull()->end()
|
||||||
|
->scalarNode(Request::METHOD_PATCH)->defaultNull()->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Doctrine\Event;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Doctrine\Common\EventSubscriber;
|
||||||
|
use Doctrine\ORM\Events;
|
||||||
|
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
|
||||||
|
class TrackCreateUpdateSubscriber implements EventSubscriber
|
||||||
|
{
|
||||||
|
private Security $security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Security $security
|
||||||
|
*/
|
||||||
|
public function __construct(Security $security)
|
||||||
|
{
|
||||||
|
$this->security = $security;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Events::prePersist,
|
||||||
|
Events::preUpdate
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prePersist(LifecycleEventArgs $args): void
|
||||||
|
{
|
||||||
|
$object = $args->getObject();
|
||||||
|
|
||||||
|
if ($object instanceof TrackCreationInterface
|
||||||
|
&& $this->security->getUser() instanceof User) {
|
||||||
|
$object->setCreatedBy($this->security->getUser());
|
||||||
|
$object->setCreatedAt(new \DateTimeImmutable('now'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->onUpdate($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function preUpdate(LifecycleEventArgs $args): void
|
||||||
|
{
|
||||||
|
$object = $args->getObject();
|
||||||
|
|
||||||
|
$this->onUpdate($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function onUpdate(object $object): void
|
||||||
|
{
|
||||||
|
if ($object instanceof TrackUpdateInterface
|
||||||
|
&& $this->security->getUser() instanceof User) {
|
||||||
|
$object->setUpdatedBy($this->security->getUser());
|
||||||
|
$object->setUpdatedAt(new \DateTimeImmutable('now'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Doctrine\Model;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
|
||||||
|
interface TrackCreationInterface
|
||||||
|
{
|
||||||
|
public function setCreatedBy(User $user): self;
|
||||||
|
|
||||||
|
public function setCreatedAt(\DateTimeInterface $datetime): self;
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Doctrine\Model;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
|
||||||
|
interface TrackUpdateInterface
|
||||||
|
{
|
||||||
|
public function setUpdatedBy(User $user): self;
|
||||||
|
|
||||||
|
public function setUpdatedAt(\DateTimeInterface $datetime): self;
|
||||||
|
}
|
@ -137,7 +137,7 @@ class Address
|
|||||||
* @var ThirdParty|null
|
* @var ThirdParty|null
|
||||||
*
|
*
|
||||||
* @ORM\ManyToOne(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
|
* @ORM\ManyToOne(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
|
||||||
* @ORM\JoinColumn(nullable=true)
|
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
|
||||||
*/
|
*/
|
||||||
private $linkedToThirdParty;
|
private $linkedToThirdParty;
|
||||||
|
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Entity;
|
namespace Chill\MainBundle\Entity;
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\AddressReferenceRepository;
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Chill\MainBundle\Doctrine\Model\Point;
|
use Chill\MainBundle\Doctrine\Model\Point;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity(repositoryClass=AddressReferenceRepository::class)
|
* @ORM\Entity()
|
||||||
* @ORM\Table(name="chill_main_address_reference")
|
* @ORM\Table(name="chill_main_address_reference")
|
||||||
* @ORM\HasLifecycleCallbacks()
|
* @ORM\HasLifecycleCallbacks()
|
||||||
*/
|
*/
|
||||||
|
@ -24,13 +24,16 @@ use Doctrine\ORM\Mapping as ORM;
|
|||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Chill\MainBundle\Entity\RoleScope;
|
use Chill\MainBundle\Entity\RoleScope;
|
||||||
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity()
|
* @ORM\Entity()
|
||||||
* @ORM\Table(name="scopes")
|
* @ORM\Table(name="scopes")
|
||||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
||||||
*
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
* "scope"=Scope::class
|
||||||
|
* })
|
||||||
*/
|
*/
|
||||||
class Scope
|
class Scope
|
||||||
{
|
{
|
||||||
@ -40,6 +43,7 @@ class Scope
|
|||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
* @ORM\Column(name="id", type="integer")
|
* @ORM\Column(name="id", type="integer")
|
||||||
* @ORM\GeneratedValue(strategy="AUTO")
|
* @ORM\GeneratedValue(strategy="AUTO")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
@ -49,6 +53,7 @@ class Scope
|
|||||||
* @var array
|
* @var array
|
||||||
*
|
*
|
||||||
* @ORM\Column(type="json_array")
|
* @ORM\Column(type="json_array")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $name = [];
|
private $name = [];
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use Doctrine\Common\Collections\Collection;
|
|||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
|
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User
|
* User
|
||||||
@ -14,6 +15,9 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|||||||
* @ORM\Entity(repositoryClass="Chill\MainBundle\Repository\UserRepository")
|
* @ORM\Entity(repositoryClass="Chill\MainBundle\Repository\UserRepository")
|
||||||
* @ORM\Table(name="users")
|
* @ORM\Table(name="users")
|
||||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
||||||
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
|
* "user"=User::class
|
||||||
|
* })
|
||||||
*/
|
*/
|
||||||
class User implements AdvancedUserInterface {
|
class User implements AdvancedUserInterface {
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
div#header-accompanying_course-name {
|
div#header-accompanying_course-name {
|
||||||
background: none repeat scroll 0 0 #718596;
|
background: none repeat scroll 0 0 #718596;
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
padding-top: 1em;
|
h1 {
|
||||||
padding-bottom: 1em;
|
margin: 0.4em 0;
|
||||||
|
}
|
||||||
span {
|
span {
|
||||||
a {
|
a {
|
||||||
color: white;
|
color: white;
|
||||||
@ -39,115 +39,25 @@ div.subheader {
|
|||||||
height: 130px;
|
height: 130px;
|
||||||
}
|
}
|
||||||
|
|
||||||
//// VUEJS ////
|
//// SCRATCH BUTTONS
|
||||||
|
.sc-button {
|
||||||
div.vue-component {
|
&.disabled {
|
||||||
padding: 1.5em;
|
cursor: default;
|
||||||
margin: 2em 0;
|
&.bt-remove {
|
||||||
border: 2px dashed grey;
|
background-color: #d9d9d9;
|
||||||
position: relative;
|
|
||||||
&:before {
|
|
||||||
content: "vuejs component";
|
|
||||||
position: absolute;
|
|
||||||
left: 1.5em;
|
|
||||||
top: -0.9em;
|
|
||||||
background-color: white;
|
|
||||||
color: grey;
|
|
||||||
padding: 0 0.3em;
|
|
||||||
}
|
|
||||||
dd { margin-left: 1em; }
|
|
||||||
}
|
|
||||||
|
|
||||||
//// MODAL ////
|
|
||||||
.modal-mask {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 9998;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.75);
|
|
||||||
display: table;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
.modal-header .close { // bootstrap classes, override sc-button 0 radius
|
|
||||||
border-top-right-radius: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following styles are auto-applied to elements with
|
|
||||||
* transition="modal" when their visibility is toggled
|
|
||||||
* by Vue.js.
|
|
||||||
*
|
|
||||||
* You can easily play with the modal transition by editing
|
|
||||||
* these styles.
|
|
||||||
*/
|
|
||||||
.modal-enter {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
.modal-leave-active {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
.modal-enter .modal-container,
|
|
||||||
.modal-leave-active .modal-container {
|
|
||||||
-webkit-transform: scale(1.1);
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//// AddPersons modal
|
|
||||||
div.modal-body.up {
|
|
||||||
margin: auto 4em;
|
|
||||||
div.search {
|
|
||||||
position: relative;
|
|
||||||
input {
|
|
||||||
padding: 1.2em 1.5em 1.2em 2.5em;
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
i {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0.5em;
|
|
||||||
padding: 0.65em 0;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div.results {
|
|
||||||
div.count {
|
|
||||||
margin: -0.5em 0 0.7em;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
div.list-item {
|
|
||||||
line-height: 26pt;
|
|
||||||
padding: 0.3em 0.8em;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
&.checked {
|
|
||||||
background-color: #ececec;
|
|
||||||
border-bottom: 1px dotted #8b8b8b;
|
|
||||||
}
|
|
||||||
div.container {
|
|
||||||
& > input {
|
|
||||||
margin-right: 0.8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div.right_actions {
|
|
||||||
margin: 0 0 0 auto;
|
|
||||||
& > * {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
||||||
a.sc-button {
|
|
||||||
border: 1px solid lightgrey;
|
|
||||||
font-size: 70%;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//// à ranger
|
||||||
.discret {
|
.discret {
|
||||||
color: grey;
|
color: grey;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
ul.record_actions {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div v-if="address.address">
|
||||||
|
{{ address.address.street }}, {{ address.address.streetNumber }}
|
||||||
|
</div>
|
||||||
|
<div v-if="address.city">
|
||||||
|
{{ address.city.code }} {{ address.city.name }}
|
||||||
|
</div>
|
||||||
|
<div v-if="address.country">
|
||||||
|
{{ address.country.name }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<add-address
|
||||||
|
@addNewAddress="addNewAddress">
|
||||||
|
</add-address>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
|
||||||
|
import AddAddress from '../_components/AddAddress.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
AddAddress
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
address() {
|
||||||
|
return this.$store.state.address;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addNewAddress({ address, modal }) {
|
||||||
|
console.log('@@@ CLICK button addNewAdress', address);
|
||||||
|
this.$store.dispatch('addAddress', address.selected);
|
||||||
|
modal.showModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -0,0 +1,16 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
|
||||||
|
import { addressMessages } from './js/i18n'
|
||||||
|
import { store } from './store'
|
||||||
|
|
||||||
|
import App from './App.vue';
|
||||||
|
|
||||||
|
const i18n = _createI18n(addressMessages);
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
template: `<app></app>`,
|
||||||
|
})
|
||||||
|
.use(store)
|
||||||
|
.use(i18n)
|
||||||
|
.component('app', App)
|
||||||
|
.mount('#address');
|
@ -0,0 +1,22 @@
|
|||||||
|
const addressMessages = {
|
||||||
|
fr: {
|
||||||
|
add_an_address: 'Ajouter une adresse',
|
||||||
|
select_an_address: 'Sélectionner une adresse',
|
||||||
|
fill_an_address: 'Compléter l\'adresse',
|
||||||
|
select_country: 'Choisir le pays',
|
||||||
|
select_city: 'Choisir une localité',
|
||||||
|
select_address: 'Choisir une adresse',
|
||||||
|
isNoAddress: 'L\'adresse n\'est pas celle d\'un domicile fixe ?',
|
||||||
|
floor: 'Étage',
|
||||||
|
corridor: 'Couloir',
|
||||||
|
steps: 'Escalier',
|
||||||
|
flat: 'Appartement',
|
||||||
|
buildingName: 'Nom du batiment',
|
||||||
|
extra: 'Complément d\'adresse',
|
||||||
|
distribution: 'Service particulier de distribution'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
addressMessages
|
||||||
|
};
|
@ -0,0 +1,43 @@
|
|||||||
|
import 'es6-promise/auto';
|
||||||
|
import { createStore } from 'vuex';
|
||||||
|
|
||||||
|
// le fetch POST serait rangé dans la logique du composant qui appelle AddAddress
|
||||||
|
//import { postAddress } from '... api'
|
||||||
|
|
||||||
|
const debug = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
|
const store = createStore({
|
||||||
|
strict: debug,
|
||||||
|
state: {
|
||||||
|
address: {},
|
||||||
|
errorMsg: {}
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
addAddress(state, address) {
|
||||||
|
console.log('@M addAddress address', address);
|
||||||
|
state.address = address;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
addAddress({ commit }, payload) {
|
||||||
|
console.log('@A addAddress payload', payload);
|
||||||
|
commit('addAddress', payload); // à remplacer par
|
||||||
|
|
||||||
|
// fetch POST qui envoie l'adresse, et récupère la confirmation que c'est ok.
|
||||||
|
// La confirmation est l'adresse elle-même.
|
||||||
|
//
|
||||||
|
// postAddress(payload)
|
||||||
|
// .fetch(address => new Promise((resolve, reject) => {
|
||||||
|
// commit('addAddress', address);
|
||||||
|
// resolve();
|
||||||
|
// }))
|
||||||
|
// .catch((error) => {
|
||||||
|
// state.errorMsg.push(error.message);
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export { store };
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Endpoint countries GET
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
const fetchCountries = () => {
|
||||||
|
console.log('<<< fetching countries');
|
||||||
|
return [
|
||||||
|
{id: 1, name: 'France', countryCode: 'FR'},
|
||||||
|
{id: 2, name: 'Belgium', countryCode: 'BE'}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Endpoint cities GET
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
const fetchCities = (country) => {
|
||||||
|
console.log('<<< fetching cities for', country);
|
||||||
|
return [
|
||||||
|
{id: 1, name: 'Bruxelles', code: '1000', country: 'BE'},
|
||||||
|
{id: 2, name: 'Aisne', code: '85045', country: 'FR'},
|
||||||
|
{id: 3, name: 'Saint-Gervais', code: '85230', country: 'FR'}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Endpoint chill_main_address_reference_api_show
|
||||||
|
* method GET, get AddressReference Object
|
||||||
|
* @returns {Promise} a promise containing all AddressReference object
|
||||||
|
*/
|
||||||
|
const fetchReferenceAddresses = (city) => {
|
||||||
|
console.log('<<< fetching references addresses for', city); // city n'est pas utilisé pour le moment
|
||||||
|
|
||||||
|
const url = `/api/1.0/main/address-reference.json`;
|
||||||
|
return fetch(url)
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
fetchCountries,
|
||||||
|
fetchCities,
|
||||||
|
fetchReferenceAddresses
|
||||||
|
};
|
@ -0,0 +1,219 @@
|
|||||||
|
<template>
|
||||||
|
<button class="sc-button bt-create centered mt-4" @click="openModal">
|
||||||
|
{{ $t('add_an_address') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<teleport to="body">
|
||||||
|
<modal v-if="modal.showModal"
|
||||||
|
v-bind:modalDialogClass="modal.modalDialogClass"
|
||||||
|
@close="modal.showModal = false">
|
||||||
|
|
||||||
|
<template v-slot:header>
|
||||||
|
<h3 class="modal-title">{{ $t('add_an_address') }}</h3>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:body>
|
||||||
|
|
||||||
|
<h4>{{ $t('select_an_address') }}</h4>
|
||||||
|
|
||||||
|
<label for="isNoAddress">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="isNoAddress"
|
||||||
|
v-bind:placeholder="$t('isNoAddress')"
|
||||||
|
v-model="isNoAddress"
|
||||||
|
v-bind:value="value"/>
|
||||||
|
{{ $t('isNoAddress') }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<country-selection
|
||||||
|
v-bind:address="address"
|
||||||
|
v-bind:getCities="getCities">
|
||||||
|
</country-selection>
|
||||||
|
|
||||||
|
<city-selection
|
||||||
|
v-bind:address="address"
|
||||||
|
v-bind:getReferenceAddresses="getReferenceAddresses">
|
||||||
|
</city-selection>
|
||||||
|
|
||||||
|
<address-selection
|
||||||
|
v-bind:address="address"
|
||||||
|
v-bind:updateMapCenter="updateMapCenter">
|
||||||
|
</address-selection>
|
||||||
|
|
||||||
|
<address-map
|
||||||
|
v-bind:address="address"
|
||||||
|
ref="addressMap">
|
||||||
|
</address-map>
|
||||||
|
|
||||||
|
<address-more
|
||||||
|
v-if="!isNoAddress"
|
||||||
|
v-bind:address="address">
|
||||||
|
</address-more>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<div class="address_form__fields__isNoAddress"></div>
|
||||||
|
<div class="address_form__select">
|
||||||
|
<div class="address_form__select__header"></div>
|
||||||
|
<div class="address_form__select__left"></div>
|
||||||
|
<div class="address_form__map"></div>
|
||||||
|
</div>
|
||||||
|
<div class="address_form__fields">
|
||||||
|
<div class="address_form__fields__header"></div>
|
||||||
|
<div class="address_form__fields__left"></div>
|
||||||
|
<div class="address_form__fields__right"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
à discuter,
|
||||||
|
mais je pense qu'il est préférable de profiter de l'imbriquation des classes css
|
||||||
|
|
||||||
|
div.address_form {
|
||||||
|
div.select {
|
||||||
|
div.header {}
|
||||||
|
div.left {}
|
||||||
|
div.map {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:footer>
|
||||||
|
<button class="sc-button green"
|
||||||
|
@click.prevent="$emit('addNewAddress', { address, modal })">
|
||||||
|
<i class="fa fa-plus fa-fw"></i>{{ $t('action.add')}}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</modal>
|
||||||
|
</teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Modal from './Modal';
|
||||||
|
import { fetchCountries, fetchCities, fetchReferenceAddresses } from '../_api/AddAddress'
|
||||||
|
import CountrySelection from './AddAddress/CountrySelection';
|
||||||
|
import CitySelection from './AddAddress/CitySelection';
|
||||||
|
import AddressSelection from './AddAddress/AddressSelection';
|
||||||
|
import AddressMap from './AddAddress/AddressMap';
|
||||||
|
import AddressMore from './AddAddress/AddressMore'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AddAddresses',
|
||||||
|
components: {
|
||||||
|
Modal,
|
||||||
|
CountrySelection,
|
||||||
|
CitySelection,
|
||||||
|
AddressSelection,
|
||||||
|
AddressMap,
|
||||||
|
AddressMore
|
||||||
|
},
|
||||||
|
props: [
|
||||||
|
],
|
||||||
|
emits: ['addNewAddress'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
modal: {
|
||||||
|
showModal: false,
|
||||||
|
modalDialogClass: "modal-dialog-scrollable modal-xl"
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
loaded: {
|
||||||
|
countries: [],
|
||||||
|
cities: [],
|
||||||
|
addresses: [],
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
country: {},
|
||||||
|
city: {},
|
||||||
|
address: {},
|
||||||
|
},
|
||||||
|
addressMap: {
|
||||||
|
center : [48.8589, 2.3469], // Note: LeafletJs demands [lat, lon] cfr https://macwright.com/lonlat/
|
||||||
|
zoom: 12
|
||||||
|
},
|
||||||
|
isNoAddress: false,
|
||||||
|
floor: null,
|
||||||
|
corridor: null,
|
||||||
|
steps: null,
|
||||||
|
floor: null,
|
||||||
|
flat: null,
|
||||||
|
buildingName: null,
|
||||||
|
extra: null,
|
||||||
|
distribution: null,
|
||||||
|
},
|
||||||
|
errorMsg: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isNoAddress: {
|
||||||
|
set(value) {
|
||||||
|
console.log('value', value);
|
||||||
|
this.address.isNoAddress = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.isNoAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openModal() {
|
||||||
|
this.modal.showModal = true;
|
||||||
|
this.resetAll();
|
||||||
|
this.getCountries();
|
||||||
|
|
||||||
|
//this.$nextTick(function() {
|
||||||
|
// this.$refs.search.focus(); // positionner le curseur à l'ouverture de la modale
|
||||||
|
//})
|
||||||
|
},
|
||||||
|
getCountries() {
|
||||||
|
console.log('getCountries');
|
||||||
|
this.address.loaded.countries = fetchCountries(); // à remplacer par
|
||||||
|
// fetchCountries().then(countries => new Promise((resolve, reject) => {
|
||||||
|
// this.address.loaded.countries = countries;
|
||||||
|
// resolve()
|
||||||
|
// }))
|
||||||
|
// .catch((error) => {
|
||||||
|
// this.errorMsg.push(error.message);
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
getCities(country) {
|
||||||
|
console.log('getCities for', country.name);
|
||||||
|
this.address.loaded.cities = fetchCities(); // à remplacer par
|
||||||
|
// fetchCities(country).then(cities => new Promise((resolve, reject) => {
|
||||||
|
// this.address.loaded.cities = cities;
|
||||||
|
// resolve()
|
||||||
|
// }))
|
||||||
|
// .catch((error) => {
|
||||||
|
// this.errorMsg.push(error.message);
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
getReferenceAddresses(city) {
|
||||||
|
console.log('getReferenceAddresses for', city.name);
|
||||||
|
fetchReferenceAddresses(city) // il me semble que le paramètre city va limiter le poids des adresses de références reçues
|
||||||
|
.then(addresses => new Promise((resolve, reject) => {
|
||||||
|
console.log('addresses', addresses);
|
||||||
|
this.address.loaded.addresses = addresses.results;
|
||||||
|
resolve();
|
||||||
|
}))
|
||||||
|
.catch((error) => {
|
||||||
|
this.errorMsg.push(error.message);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateMapCenter(point) {
|
||||||
|
console.log('point', point);
|
||||||
|
this.address.addressMap.center[0] = point.coordinates[1]; // TODO use reverse()
|
||||||
|
this.address.addressMap.center[1] = point.coordinates[0];
|
||||||
|
this.$refs.addressMap.update(); // cast child methods
|
||||||
|
},
|
||||||
|
resetAll() {
|
||||||
|
console.log('reset all selected');
|
||||||
|
this.address.loaded.addresses = [];
|
||||||
|
this.address.selected.address = {};
|
||||||
|
this.address.loaded.cities = [];
|
||||||
|
this.address.selected.city = {};
|
||||||
|
this.address.selected.country = {};
|
||||||
|
console.log('cities and addresses', this.address.loaded.cities, this.address.loaded.addresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div id='address_map' style='height:400px; width:400px;'></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import L from 'leaflet';
|
||||||
|
import markerIconPng from 'leaflet/dist/images/marker-icon.png'
|
||||||
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
|
||||||
|
let map;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AddressMap',
|
||||||
|
props: ['address'],
|
||||||
|
computed: {
|
||||||
|
center() {
|
||||||
|
return this.address.addressMap.center;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
init() {
|
||||||
|
map = L.map('address_map').setView([48.8589, 2.3469], 12);
|
||||||
|
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
const markerIcon = L.icon({
|
||||||
|
iconUrl: markerIconPng,
|
||||||
|
});
|
||||||
|
|
||||||
|
L.marker([48.8589, 2.3469], {icon: markerIcon}).addTo(map);
|
||||||
|
|
||||||
|
},
|
||||||
|
update() {
|
||||||
|
console.log('update map with : ', this.address.addressMap.center)
|
||||||
|
map.setView(this.address.addressMap.center, 12);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted(){
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h4>{{ $t('fill_an_address') }}</h4>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="floor"
|
||||||
|
:placeholder="$t('floor')"
|
||||||
|
v-model="floor"/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="corridor"
|
||||||
|
:placeholder="$t('corridor')"
|
||||||
|
v-model="corridor"/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="steps"
|
||||||
|
:placeholder="$t('steps')"
|
||||||
|
v-model="steps"/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="flat"
|
||||||
|
:placeholder="$t('flat')"
|
||||||
|
v-model="flat"/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="buildingName"
|
||||||
|
:placeholder="$t('buildingName')"
|
||||||
|
v-model="buildingName"/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="extra"
|
||||||
|
:placeholder="$t('extra')"
|
||||||
|
v-model="extra"/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="distribution"
|
||||||
|
:placeholder="$t('distribution')"
|
||||||
|
v-model="distribution"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "AddressMore",
|
||||||
|
props: ['address'],
|
||||||
|
computed: {
|
||||||
|
floor: {
|
||||||
|
set(value) {
|
||||||
|
console.log('value', value);
|
||||||
|
this.address.floor = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.floor;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
corridor: {
|
||||||
|
set(value) {
|
||||||
|
console.log('value', value);
|
||||||
|
this.address.corridor = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.corridor;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
steps: {
|
||||||
|
set(value) {
|
||||||
|
console.log('value', value);
|
||||||
|
this.address.steps = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.steps;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
flat: {
|
||||||
|
set(value) {
|
||||||
|
console.log('value', value);
|
||||||
|
this.address.flat = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.flat;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buildingName: {
|
||||||
|
set(value) {
|
||||||
|
console.log('value', value);
|
||||||
|
this.address.buildingName = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.buildingName;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
set(value) {
|
||||||
|
console.log('value', value);
|
||||||
|
this.address.extra = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.extra;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
distribution: {
|
||||||
|
set(value) {
|
||||||
|
console.log('value', value);
|
||||||
|
this.address.distribution = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.distribution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<select
|
||||||
|
v-model="selected">
|
||||||
|
<option :value="{}" disabled selected>{{ $t('select_address') }}</option>
|
||||||
|
<option
|
||||||
|
v-for="item in this.addresses"
|
||||||
|
v-bind:item="item"
|
||||||
|
v-bind:key="item.id"
|
||||||
|
v-bind:value="item">
|
||||||
|
{{ item.street }}, {{ item.streetNumber }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AddressSelection',
|
||||||
|
props: ['address', 'updateMapCenter'],
|
||||||
|
computed: {
|
||||||
|
addresses() {
|
||||||
|
return this.address.loaded.addresses;
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
set(value) {
|
||||||
|
console.log('selected value', value);
|
||||||
|
this.address.selected.address = value;
|
||||||
|
this.updateMapCenter(value.point);
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.selected.address;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<select
|
||||||
|
v-model="selected">
|
||||||
|
<option :value="{}" disabled selected>{{ $t('select_city') }}</option>
|
||||||
|
<option
|
||||||
|
v-for="item in this.cities"
|
||||||
|
v-bind:item="item"
|
||||||
|
v-bind:key="item.id"
|
||||||
|
v-bind:value="item">
|
||||||
|
{{ item.code }}-{{ item.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CitySelection',
|
||||||
|
props: ['address', 'getReferenceAddresses'],
|
||||||
|
computed: {
|
||||||
|
cities() {
|
||||||
|
return this.address.loaded.cities;
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
set(value) {
|
||||||
|
console.log('selected value', value.name);
|
||||||
|
this.address.selected.city = value;
|
||||||
|
this.getReferenceAddresses(value);
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.selected.city;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<select
|
||||||
|
v-model="selected">
|
||||||
|
<option :value="{}" disabled selected>{{ $t('select_country') }}</option>
|
||||||
|
<option
|
||||||
|
v-for="item in this.countries"
|
||||||
|
v-bind:item="item"
|
||||||
|
v-bind:key="item.id"
|
||||||
|
v-bind:value="item">
|
||||||
|
{{ item.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CountrySelection',
|
||||||
|
props: ['address', 'getCities'],
|
||||||
|
computed: {
|
||||||
|
countries() {
|
||||||
|
return this.address.loaded.countries;
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
set(value) {
|
||||||
|
console.log('selected value', value.name);
|
||||||
|
this.address.selected.country = value;
|
||||||
|
this.getCities(value);
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.address.selected.country;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -1,41 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="modal">
|
<transition name="modal">
|
||||||
<div class="modal-mask">
|
<div class="modal-mask">
|
||||||
<!-- :: styles bootstrap :: -->
|
<!-- :: styles bootstrap :: -->
|
||||||
<div class="modal-dialog" :class="modalDialogClass">
|
<div class="modal-dialog" :class="modalDialogClass">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
<button class="close sc-button grey" @click="$emit('close')">
|
<button class="close sc-button grey" @click="$emit('close')">
|
||||||
<i class="fa fa-times" aria-hidden="true"></i></button>
|
<i class="fa fa-times" aria-hidden="true"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body up" style="overflow-y: unset;">
|
<div class="body-head">
|
||||||
<slot name="body-fixed"></slot>
|
<slot name="body-head"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<slot name="body"></slot>
|
<slot name="body"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="sc-button cancel" @click="$emit('close')">{{ $t('action.close') }}</button>
|
<button class="sc-button cancel" @click="$emit('close')">{{ $t('action.close') }}</button>
|
||||||
<slot name="footer"></slot>
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- :: end styles bootstrap :: -->
|
||||||
</div>
|
</div>
|
||||||
<!-- :: end styles bootstrap :: -->
|
</transition>
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/*
|
/*
|
||||||
* This Modal component is a mix between :
|
* This Modal component is a mix between Vue3 modal implementation
|
||||||
* - Vue3 modal implementation
|
* [+] with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden
|
||||||
* => with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden
|
* [+] with slot we can pass content from parent component
|
||||||
* => with slot we can pass content from parent component
|
* [+] some classes are passed from parent component
|
||||||
* => some classes are passed from parent component
|
* and Bootstrap 4.6 _modal.scss module
|
||||||
* - Bootstrap 4.6 _modal.scss module
|
* [+] using bootstrap css classes, the modal have a responsive behaviour,
|
||||||
* => using bootstrap css classes, the modal have a responsive behaviour,
|
* [+] modal design can be configured using css classes (size, scroll)
|
||||||
* => modal design can be configured using css classes (size, scroll)
|
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
name: 'Modal',
|
name: 'Modal',
|
||||||
@ -43,3 +42,39 @@ export default {
|
|||||||
emits: ['close']
|
emits: ['close']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.modal-mask {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 9998;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.75);
|
||||||
|
display: table;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
.modal-header .close { // bootstrap classes, override sc-button 0 radius
|
||||||
|
border-top-right-radius: 0.3rem;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The following styles are auto-applied to elements with
|
||||||
|
* transition="modal" when their visibility is toggled
|
||||||
|
* by Vue.js.
|
||||||
|
*
|
||||||
|
* You can easily play with the modal transition by editing
|
||||||
|
* these styles.
|
||||||
|
*/
|
||||||
|
.modal-enter {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.modal-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.modal-enter .modal-container,
|
||||||
|
.modal-leave-active .modal-container {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -7,11 +7,15 @@ const datetimeFormats = {
|
|||||||
month: "numeric",
|
month: "numeric",
|
||||||
day: "numeric"
|
day: "numeric"
|
||||||
},
|
},
|
||||||
|
text: {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
},
|
||||||
long: {
|
long: {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "short",
|
month: "numeric",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
weekday: "short",
|
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "numeric",
|
minute: "numeric",
|
||||||
hour12: false
|
hour12: false
|
||||||
@ -33,12 +37,16 @@ const messages = {
|
|||||||
ok: "OK",
|
ok: "OK",
|
||||||
cancel: "Annuler",
|
cancel: "Annuler",
|
||||||
close: "Fermer",
|
close: "Fermer",
|
||||||
next: "Suivant",
|
|
||||||
previous: "Précédent",
|
|
||||||
back: "Retour",
|
back: "Retour",
|
||||||
check_all: "cocher tout",
|
check_all: "cocher tout",
|
||||||
reset: "réinitialiser"
|
reset: "réinitialiser"
|
||||||
},
|
},
|
||||||
|
nav: {
|
||||||
|
next: "Suivant",
|
||||||
|
previous: "Précédent",
|
||||||
|
top: "Haut",
|
||||||
|
bottom: "Bas",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<p>{{ 'This program is free software: you can redistribute it and/or modify it under the terms of the <strong>GNU Affero General Public License</strong>'|trans|raw }}
|
<p>{{ 'This program is free software: you can redistribute it and/or modify it under the terms of the <strong>GNU Affero General Public License</strong>'|trans|raw }}
|
||||||
<br/> <a href="https://{{ app.request.locale }}.wikibooks.org/wiki/Chill" target="_blank">{{ 'User manual'|trans }}</a></p>
|
<br/> <a name="bottom" href="https://{{ app.request.locale }}.wikibooks.org/wiki/Chill" target="_blank">{{ 'User manual'|trans }}</a></p>
|
||||||
</footer>
|
</footer>
|
@ -1 +1 @@
|
|||||||
<img class="logo" src="{{ asset('build/images/logo-chill-sans-slogan_white.png') }}">
|
<img name="top" class="logo" src="{{ asset('build/images/logo-chill-sans-slogan_white.png') }}">
|
||||||
|
36
src/Bundle/ChillMainBundle/Search/Model/Result.php
Normal file
36
src/Bundle/ChillMainBundle/Search/Model/Result.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Search\Model;
|
||||||
|
|
||||||
|
class Result
|
||||||
|
{
|
||||||
|
private float $relevance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mixed an arbitrary result
|
||||||
|
*/
|
||||||
|
private $result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param float $relevance
|
||||||
|
* @param $result
|
||||||
|
*/
|
||||||
|
public function __construct(float $relevance, $result)
|
||||||
|
{
|
||||||
|
$this->relevance = $relevance;
|
||||||
|
$this->result = $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRelevance(): float
|
||||||
|
{
|
||||||
|
return $this->relevance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult()
|
||||||
|
{
|
||||||
|
return $this->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
89
src/Bundle/ChillMainBundle/Search/SearchApi.php
Normal file
89
src/Bundle/ChillMainBundle/Search/SearchApi.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Search;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Chill\MainBundle\Search\SearchProvider;
|
||||||
|
use Symfony\Component\VarDumper\Resources\functions\dump;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ***Warning*** This is an incomplete implementation ***Warning***
|
||||||
|
*/
|
||||||
|
class SearchApi
|
||||||
|
{
|
||||||
|
private EntityManagerInterface $em;
|
||||||
|
private SearchProvider $search;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $em, SearchProvider $search)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->search = $search;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Model/Result[]
|
||||||
|
*/
|
||||||
|
public function getResults(string $query, int $offset, int $maxResult): array
|
||||||
|
{
|
||||||
|
// **warning again**: this is an incomplete implementation
|
||||||
|
$results = [];
|
||||||
|
|
||||||
|
foreach ($this->getPersons($query) as $p) {
|
||||||
|
$results[] = new Model\Result((float)\rand(0, 100) / 100, $p);
|
||||||
|
}
|
||||||
|
foreach ($this->getThirdParties($query) as $t) {
|
||||||
|
$results[] = new Model\Result((float)\rand(0, 100) / 100, $t);
|
||||||
|
}
|
||||||
|
|
||||||
|
\usort($results, function(Model\Result $a, Model\Result $b) {
|
||||||
|
return ($a->getRelevance() <=> $b->getRelevance()) * -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countResults(string $query): int
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getThirdParties(string $query)
|
||||||
|
{
|
||||||
|
$thirdPartiesIds = $this->em->createQuery('SELECT t.id FROM '.ThirdParty::class.' t')
|
||||||
|
->getScalarResult();
|
||||||
|
$nbResults = rand(0, 15);
|
||||||
|
|
||||||
|
if ($nbResults === 1) {
|
||||||
|
$nbResults++;
|
||||||
|
} elseif ($nbResults === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$ids = \array_map(function ($e) use ($thirdPartiesIds) { return $thirdPartiesIds[$e]['id'];},
|
||||||
|
\array_rand($thirdPartiesIds, $nbResults));
|
||||||
|
|
||||||
|
$a = $this->em->getRepository(ThirdParty::class)
|
||||||
|
->findById($ids);
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPersons(string $query)
|
||||||
|
{
|
||||||
|
$params = [
|
||||||
|
SearchInterface::SEARCH_PREVIEW_OPTION => false
|
||||||
|
];
|
||||||
|
$search = $this->search->getResultByName($query, 'person_regular', 0, 50, $params, 'json');
|
||||||
|
$ids = \array_map(function($r) { return $r['id']; }, $search['results']);
|
||||||
|
|
||||||
|
|
||||||
|
if (count($ids) === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->em->getRepository(Person::class)
|
||||||
|
->findById($ids)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Address;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
|
||||||
|
class AddressNormalizer implements NormalizerAwareInterface, NormalizerInterface
|
||||||
|
{
|
||||||
|
use NormalizerAwareTrait;
|
||||||
|
|
||||||
|
public function normalize($address, string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
$data['address_id'] = $address->getId();
|
||||||
|
$data['text'] = $address->getStreet().', '.$address->getBuildingName();
|
||||||
|
$data['postcode']['name'] = $address->getPostCode()->getName();
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsNormalization($data, string $format = null)
|
||||||
|
{
|
||||||
|
return $data instanceof Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -20,19 +20,32 @@
|
|||||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
use Chill\MainBundle\Repository\CenterRepository;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class CenterNormalizer implements NormalizerInterface
|
class CenterNormalizer implements NormalizerInterface, DenormalizerInterface
|
||||||
{
|
{
|
||||||
|
private CenterRepository $repository;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(CenterRepository $repository)
|
||||||
|
{
|
||||||
|
$this->repository = $repository;
|
||||||
|
}
|
||||||
|
|
||||||
public function normalize($center, string $format = null, array $context = array())
|
public function normalize($center, string $format = null, array $context = array())
|
||||||
{
|
{
|
||||||
/** @var Center $center */
|
/** @var Center $center */
|
||||||
return [
|
return [
|
||||||
'id' => $center->getId(),
|
'id' => $center->getId(),
|
||||||
|
'type' => 'center',
|
||||||
'name' => $center->getName()
|
'name' => $center->getName()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -41,4 +54,30 @@ class CenterNormalizer implements NormalizerInterface
|
|||||||
{
|
{
|
||||||
return $data instanceof Center;
|
return $data instanceof Center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
if (FALSE === \array_key_exists('type', $data)) {
|
||||||
|
throw new InvalidArgumentException('missing "type" key in data');
|
||||||
|
}
|
||||||
|
if ('center' !== $data['type']) {
|
||||||
|
throw new InvalidArgumentException('type should be equal to "center"');
|
||||||
|
}
|
||||||
|
if (FALSE === \array_key_exists('id', $data)) {
|
||||||
|
throw new InvalidArgumentException('missing "id" key in data');
|
||||||
|
}
|
||||||
|
|
||||||
|
$center = $this->repository->find($data['id']);
|
||||||
|
|
||||||
|
if (null === $center) {
|
||||||
|
throw new UnexpectedValueException("The type with id {$data['id']} does not exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $center;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsDenormalization($data, string $type, string $format = null)
|
||||||
|
{
|
||||||
|
return $type === Center::class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,15 +20,16 @@
|
|||||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
|
||||||
class DateNormalizer implements NormalizerInterface
|
class DateNormalizer implements NormalizerInterface, DenormalizerInterface
|
||||||
{
|
{
|
||||||
public function normalize($date, string $format = null, array $context = array())
|
public function normalize($date, string $format = null, array $context = array())
|
||||||
{
|
{
|
||||||
/** @var \DateTimeInterface $date */
|
/** @var \DateTimeInterface $date */
|
||||||
return [
|
return [
|
||||||
'datetime' => $date->format(\DateTimeInterface::ISO8601),
|
'datetime' => $date->format(\DateTimeInterface::ISO8601)
|
||||||
'u' => $date->getTimestamp()
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,4 +37,24 @@ class DateNormalizer implements NormalizerInterface
|
|||||||
{
|
{
|
||||||
return $data instanceof \DateTimeInterface;
|
return $data instanceof \DateTimeInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
switch ($type) {
|
||||||
|
case \DateTime::class:
|
||||||
|
return \DateTime::createFromFormat(\DateTimeInterface::ISO8601, $data['datetime']);
|
||||||
|
case \DateTimeInterface::class:
|
||||||
|
case \DateTimeImmutable::class:
|
||||||
|
default:
|
||||||
|
return \DateTimeImmutable::createFromFormat(\DateTimeInterface::ISO8601, $data['datetime']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsDenormalization($data, string $type, string $format = null): bool
|
||||||
|
{
|
||||||
|
return $type === \DateTimeInterface::class ||
|
||||||
|
$type === \DateTime::class ||
|
||||||
|
$type === \DateTimeImmutable::class ||
|
||||||
|
(\is_array($data) && array_key_exists('datetime', $data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
|
||||||
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
|
||||||
|
use Symfony\Component\Serializer\Exception\RuntimeException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denormalize an object given a list of supported class
|
||||||
|
*/
|
||||||
|
class DiscriminatedObjectDenormalizer implements ContextAwareDenormalizerInterface, DenormalizerAwareInterface
|
||||||
|
{
|
||||||
|
use DenormalizerAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type to set for enabling this type
|
||||||
|
*/
|
||||||
|
public const TYPE = '@multi';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be present in context and contains an array of
|
||||||
|
* allowed types.
|
||||||
|
*/
|
||||||
|
public const ALLOWED_TYPES = 'denormalize_multi.allowed_types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
foreach ($context[self::ALLOWED_TYPES] as $localType) {
|
||||||
|
if ($this->denormalizer->supportsDenormalization($data, $localType, $format)) {
|
||||||
|
try {
|
||||||
|
return $this->denormalizer->denormalize($data, $localType, $format, $context); } catch (RuntimeException $e) {
|
||||||
|
$lastException = $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException(sprintf("Could not find any denormalizer for those ".
|
||||||
|
"ALLOWED_TYPES: %s", \implode(", ", $context[self::ALLOWED_TYPES])));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function supportsDenormalization($data, string $type, string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
if (self::TYPE !== $type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === count($context[self::ALLOWED_TYPES] ?? [])) {
|
||||||
|
throw new \LogicException("The context should contains a list of
|
||||||
|
allowed types");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($context[self::ALLOWED_TYPES] as $localType) {
|
||||||
|
if ($this->denormalizer->supportsDenormalization($data, $localType, $format)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||||
|
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface as SerializerMetadata;
|
||||||
|
|
||||||
|
|
||||||
|
class DoctrineExistingEntityNormalizer implements DenormalizerInterface
|
||||||
|
{
|
||||||
|
private EntityManagerInterface $em;
|
||||||
|
|
||||||
|
private ClassMetadataFactoryInterface $serializerMetadataFactory;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $em, ClassMetadataFactoryInterface $serializerMetadataFactory)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->serializerMetadataFactory = $serializerMetadataFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
if (\array_key_exists(AbstractNormalizer::OBJECT_TO_POPULATE, $context)) {
|
||||||
|
return $context[AbstractNormalizer::OBJECT_TO_POPULATE];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->em->getRepository($type)
|
||||||
|
->find($data['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsDenormalization($data, string $type, string $format = null)
|
||||||
|
{
|
||||||
|
if (FALSE === \is_array($data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FALSE === \array_key_exists('id', $data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FALSE === $this->em->getClassMetadata($type) instanceof ClassMetadata) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// does have serializer metadata, and class discriminator ?
|
||||||
|
if ($this->serializerMetadataFactory->hasMetadataFor($type)) {
|
||||||
|
|
||||||
|
$classDiscriminator = $this->serializerMetadataFactory
|
||||||
|
->getMetadataFor($type)->getClassDiscriminatorMapping();
|
||||||
|
|
||||||
|
if ($classDiscriminator) {
|
||||||
|
$typeProperty = $classDiscriminator->getTypeProperty();
|
||||||
|
|
||||||
|
// check that only 2 keys
|
||||||
|
// that the second key is property
|
||||||
|
// and that the type match the class for given type property
|
||||||
|
return count($data) === 2
|
||||||
|
&& \array_key_exists($typeProperty, $data)
|
||||||
|
&& $type === $classDiscriminator->getClassForType($data[$typeProperty]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not have any class discriminator. Check that the id is the only one key
|
||||||
|
return count($data) === 1;
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
* @internal we keep this normalizer, because the property 'text' may be replace by a rendering in the future
|
||||||
*/
|
*/
|
||||||
class UserNormalizer implements NormalizerInterface
|
class UserNormalizer implements NormalizerInterface
|
||||||
{
|
{
|
||||||
@ -32,8 +32,10 @@ class UserNormalizer implements NormalizerInterface
|
|||||||
{
|
{
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
return [
|
return [
|
||||||
|
'type' => 'user',
|
||||||
'id' => $user->getId(),
|
'id' => $user->getId(),
|
||||||
'username' => $user->getUsername()
|
'username' => $user->getUsername(),
|
||||||
|
'text' => $user->getUsername()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Tests\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Chill\MainBundle\Serializer\Normalizer\DoctrineExistingEntityNormalizer;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
|
||||||
|
|
||||||
|
class DoctrineExistingEntityNormalizerTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
protected DoctrineExistingEntityNormalizer $normalizer;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
$serializerFactory = self::$container->get(ClassMetadataFactoryInterface::class);
|
||||||
|
|
||||||
|
$this->normalizer = new DoctrineExistingEntityNormalizer($em, $serializerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataProviderUserId
|
||||||
|
*/
|
||||||
|
public function testGetMappedClass($userId)
|
||||||
|
{
|
||||||
|
$data = [ 'type' => 'user', 'id' => $userId];
|
||||||
|
$supports = $this->normalizer->supportsDenormalization($data, User::class);
|
||||||
|
|
||||||
|
$this->assertTrue($supports);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataProviderUserId()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$userIds = self::$container->get(EntityManagerInterface::class)
|
||||||
|
->getRepository(User::class)
|
||||||
|
->createQueryBuilder('u')
|
||||||
|
->select('u.id')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getResult()
|
||||||
|
;
|
||||||
|
|
||||||
|
yield [ $userIds[0]['id'] ];
|
||||||
|
}
|
||||||
|
}
|
58
src/Bundle/ChillMainBundle/chill.api.specs.yaml
Normal file
58
src/Bundle/ChillMainBundle/chill.api.specs.yaml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
openapi: "3.0.0"
|
||||||
|
info:
|
||||||
|
version: "1.0.0"
|
||||||
|
title: "Chill api"
|
||||||
|
description: "Api documentation for chill. Currently, work in progress"
|
||||||
|
servers:
|
||||||
|
- url: "/api"
|
||||||
|
description: "Your current dev server"
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Center:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/1.0/search.json:
|
||||||
|
get:
|
||||||
|
summary: perform a search across multiple entities
|
||||||
|
tags:
|
||||||
|
- search
|
||||||
|
- person
|
||||||
|
- thirdparty
|
||||||
|
description: >
|
||||||
|
**Warning**: This is currently a stub (not really implemented
|
||||||
|
|
||||||
|
The search is performed across multiple entities. The entities must be listed into
|
||||||
|
`type` parameters.
|
||||||
|
|
||||||
|
The results are ordered by relevance, from the most to the lowest relevant.
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
- name: q
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
description: the pattern to search
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: type[]
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
description: the type entities amongst the search is performed
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- person
|
||||||
|
- thirdparty
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: "OK"
|
||||||
|
|
@ -62,5 +62,7 @@ module.exports = function(encore, entries)
|
|||||||
buildCKEditor(encore);
|
buildCKEditor(encore);
|
||||||
encore.addEntry('ckeditor5', __dirname + '/Resources/public/modules/ckeditor5/index.js');
|
encore.addEntry('ckeditor5', __dirname + '/Resources/public/modules/ckeditor5/index.js');
|
||||||
|
|
||||||
|
// Address
|
||||||
|
encore.addEntry('address', __dirname + '/Resources/public/vuejs/Address/index.js');
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -73,6 +73,13 @@ chill_main_search:
|
|||||||
requirements:
|
requirements:
|
||||||
_format: html|json
|
_format: html|json
|
||||||
|
|
||||||
|
chill_main_search_global:
|
||||||
|
path: '/api/1.0/search.{_format}'
|
||||||
|
controller: Chill\MainBundle\Controller\SearchController::searchApi
|
||||||
|
format: 'json'
|
||||||
|
requirements:
|
||||||
|
_format: 'json'
|
||||||
|
|
||||||
chill_main_advanced_search:
|
chill_main_advanced_search:
|
||||||
path: /{_locale}/search/advanced/{name}
|
path: /{_locale}/search/advanced/{name}
|
||||||
controller: Chill\MainBundle\Controller\SearchController::advancedSearchAction
|
controller: Chill\MainBundle\Controller\SearchController::advancedSearchAction
|
||||||
|
@ -3,6 +3,18 @@ parameters:
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
|
Chill\MainBundle\Serializer\Normalizer\:
|
||||||
|
resource: '../Serializer/Normalizer'
|
||||||
|
autowire: true
|
||||||
|
tags:
|
||||||
|
- { name: 'serializer.normalizer', priority: 64 }
|
||||||
|
|
||||||
|
Chill\MainBundle\Doctrine\Event\:
|
||||||
|
resource: '../Doctrine/Event/'
|
||||||
|
autowire: true
|
||||||
|
tags:
|
||||||
|
- { name: 'doctrine.event_subscriber' }
|
||||||
|
|
||||||
chill.main.helper.translatable_string:
|
chill.main.helper.translatable_string:
|
||||||
class: Chill\MainBundle\Templating\TranslatableStringHelper
|
class: Chill\MainBundle\Templating\TranslatableStringHelper
|
||||||
arguments:
|
arguments:
|
||||||
|
@ -16,6 +16,7 @@ services:
|
|||||||
$searchProvider: '@chill_main.search_provider'
|
$searchProvider: '@chill_main.search_provider'
|
||||||
$translator: '@Symfony\Contracts\Translation\TranslatorInterface'
|
$translator: '@Symfony\Contracts\Translation\TranslatorInterface'
|
||||||
$paginatorFactory: '@Chill\MainBundle\Pagination\PaginatorFactory'
|
$paginatorFactory: '@Chill\MainBundle\Pagination\PaginatorFactory'
|
||||||
|
$searchApi: '@Chill\MainBundle\Search\SearchApi'
|
||||||
tags: ['controller.service_arguments']
|
tags: ['controller.service_arguments']
|
||||||
|
|
||||||
Chill\MainBundle\Controller\PermissionsGroupController:
|
Chill\MainBundle\Controller\PermissionsGroupController:
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
services:
|
services:
|
||||||
chill_main.search_provider:
|
chill_main.search_provider:
|
||||||
class: Chill\MainBundle\Search\SearchProvider
|
class: Chill\MainBundle\Search\SearchProvider
|
||||||
|
|
||||||
|
Chill\MainBundle\Search\SearchProvider: '@chill_main.search_provider'
|
||||||
|
|
||||||
|
Chill\MainBundle\Search\SearchApi:
|
||||||
|
arguments:
|
||||||
|
$em: '@Doctrine\ORM\EntityManagerInterface'
|
||||||
|
$search: '@Chill\MainBundle\Search\SearchProvider'
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
---
|
---
|
||||||
services:
|
services:
|
||||||
Chill\MainBundle\Serializer\Normalizer\CenterNormalizer:
|
|
||||||
tags:
|
|
||||||
- { name: 'serializer.normalizer', priority: 64 }
|
|
||||||
|
|
||||||
Chill\MainBundle\Serializer\Normalizer\DateNormalizer:
|
# note: the autowiring for serializers and normalizers is declared
|
||||||
tags:
|
# into ../services.yaml
|
||||||
- { name: 'serializer.normalizer', priority: 64 }
|
|
||||||
|
|
||||||
Chill\MainBundle\Serializer\Normalizer\UserNormalizer:
|
Chill\MainBundle\Serializer\Normalizer\DoctrineExistingEntityNormalizer:
|
||||||
|
autowire: true
|
||||||
tags:
|
tags:
|
||||||
- { name: 'serializer.normalizer', priority: 64 }
|
- { name: 'serializer.normalizer', priority: 8 }
|
||||||
|
|
||||||
Chill\MainBundle\Serializer\Normalizer\CollectionNormalizer:
|
|
||||||
tags:
|
|
||||||
- { name: 'serializer.normalizer', priority: 64 }
|
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Main;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify ON DELETE behaviour to handle deletion of parents in associated tables
|
||||||
|
*/
|
||||||
|
final class Version20210525144016 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Specify ON DELETE behaviour to handle deletion of parents in associated tables';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE chill_main_address DROP CONSTRAINT FK_165051F6114B8DD9');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_address ADD CONSTRAINT FK_165051F6114B8DD9 FOREIGN KEY (linkedToThirdParty_id) REFERENCES chill_3party.third_party (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE chill_main_address DROP CONSTRAINT fk_165051f6114b8dd9');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_address ADD CONSTRAINT fk_165051f6114b8dd9 FOREIGN KEY (linkedtothirdparty_id) REFERENCES chill_3party.third_party (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
}
|
||||||
|
}
|
@ -5,13 +5,19 @@ namespace Chill\PersonBundle\Controller;
|
|||||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||||
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
|
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
|
use Symfony\Component\Serializer\Exception\RuntimeException;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
||||||
|
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Symfony\Component\Workflow\Registry;
|
||||||
|
|
||||||
class AccompanyingCourseApiController extends ApiController
|
class AccompanyingCourseApiController extends ApiController
|
||||||
{
|
{
|
||||||
@ -19,10 +25,37 @@ class AccompanyingCourseApiController extends ApiController
|
|||||||
|
|
||||||
protected ValidatorInterface $validator;
|
protected ValidatorInterface $validator;
|
||||||
|
|
||||||
public function __construct(EventDispatcherInterface $eventDispatcher, $validator)
|
private Registry $registry;
|
||||||
{
|
|
||||||
|
public function __construct(
|
||||||
|
EventDispatcherInterface $eventDispatcher,
|
||||||
|
ValidatorInterface $validator,
|
||||||
|
Registry $registry
|
||||||
|
) {
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
$this->validator = $validator;
|
$this->validator = $validator;
|
||||||
|
$this->registry = $registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirmApi($id, Request $request, $_format): Response
|
||||||
|
{
|
||||||
|
/** @var AccompanyingPeriod $accompanyingPeriod */
|
||||||
|
$accompanyingPeriod = $this->getEntity('participation', $id, $request);
|
||||||
|
|
||||||
|
$this->checkACL('confirm', $request, $_format, $accompanyingPeriod);
|
||||||
|
$workflow = $this->registry->get($accompanyingPeriod);
|
||||||
|
|
||||||
|
if (FALSE === $workflow->can($accompanyingPeriod, 'confirm')) {
|
||||||
|
throw new BadRequestException('It is not possible to confirm this period');
|
||||||
|
}
|
||||||
|
|
||||||
|
$workflow->apply($accompanyingPeriod, 'confirm');
|
||||||
|
|
||||||
|
$this->getDoctrine()->getManager()->flush();
|
||||||
|
|
||||||
|
return $this->json($accompanyingPeriod, Response::HTTP_OK, [], [
|
||||||
|
'groups' => [ 'read' ]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function participationApi($id, Request $request, $_format)
|
public function participationApi($id, Request $request, $_format)
|
||||||
@ -42,11 +75,10 @@ class AccompanyingCourseApiController extends ApiController
|
|||||||
|
|
||||||
switch ($request->getMethod()) {
|
switch ($request->getMethod()) {
|
||||||
case Request::METHOD_POST:
|
case Request::METHOD_POST:
|
||||||
$participation = $accompanyingPeriod->addPerson($person);
|
$participation = $accompanyingPeriod->createParticipationFor($person);
|
||||||
break;
|
break;
|
||||||
case Request::METHOD_DELETE:
|
case Request::METHOD_DELETE:
|
||||||
$participation = $accompanyingPeriod->removePerson($person);
|
$participation = $accompanyingPeriod->closeParticipationFor($person);
|
||||||
$participation->setEndDate(new \DateTimeImmutable('now'));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new BadRequestException("This method is not supported");
|
throw new BadRequestException("This method is not supported");
|
||||||
@ -56,12 +88,76 @@ class AccompanyingCourseApiController extends ApiController
|
|||||||
|
|
||||||
if ($errors->count() > 0) {
|
if ($errors->count() > 0) {
|
||||||
// only format accepted
|
// only format accepted
|
||||||
return $this->json($errors);
|
return $this->json($errors, 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getDoctrine()->getManager()->flush();
|
$this->getDoctrine()->getManager()->flush();
|
||||||
|
|
||||||
return $this->json($participation);
|
return $this->json($participation, 200, [], ['groups' => [ 'read' ]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resourceApi($id, Request $request, string $_format): Response
|
||||||
|
{
|
||||||
|
return $this->addRemoveSomething('resource', $id, $request, $_format, 'resource', Resource::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeApi($id, Request $request, string $_format): Response
|
||||||
|
{
|
||||||
|
return $this->addRemoveSomething('scope', $id, $request, $_format, 'scope', Scope::class, [ 'groups' => [ 'read' ] ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function commentApi($id, Request $request, string $_format): Response
|
||||||
|
{
|
||||||
|
return $this->addRemoveSomething('comment', $id, $request, $_format, 'comment', Comment::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function socialIssueApi($id, Request $request, string $_format): Response
|
||||||
|
{
|
||||||
|
return $this->addRemoveSomething('socialissue', $id, $request, $_format, 'socialIssue', SocialIssue::class, [ 'groups' => [ 'read' ] ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requestorApi($id, Request $request, string $_format): Response
|
||||||
|
{
|
||||||
|
/** @var AccompanyingPeriod $accompanyingPeriod */
|
||||||
|
$action = 'requestor';
|
||||||
|
$accompanyingPeriod = $this->getEntity($action, $id, $request);
|
||||||
|
// a requestor may be a person or a thirdParty
|
||||||
|
|
||||||
|
$this->checkACL($action, $request, $_format, $accompanyingPeriod);
|
||||||
|
$this->onPostCheckACL($action, $request, $_format, $accompanyingPeriod);
|
||||||
|
|
||||||
|
if (Request::METHOD_DELETE === $request->getMethod()) {
|
||||||
|
$accompanyingPeriod->setRequestor(NULL);
|
||||||
|
} elseif (Request::METHOD_POST === $request->getMethod()) {
|
||||||
|
$requestor = null;
|
||||||
|
$exceptions = [];
|
||||||
|
foreach ([Person::class, ThirdParty::class] as $class) {
|
||||||
|
try {
|
||||||
|
$requestor = $this->getSerializer()
|
||||||
|
->deserialize($request->getContent(), $class, $_format, []);
|
||||||
|
} catch (RuntimeException $e) {
|
||||||
|
$exceptions[] = $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($requestor === null) {
|
||||||
|
throw new BadRequestException('Could not find any person or requestor', 0, $exceptions[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$accompanyingPeriod->setRequestor($requestor);
|
||||||
|
} else {
|
||||||
|
throw new BadRequestException('method not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = $this->validator->validate($accompanyingPeriod);
|
||||||
|
|
||||||
|
if ($errors->count() > 0) {
|
||||||
|
// only format accepted
|
||||||
|
return $this->json($errors, 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getDoctrine()->getManager()->flush();
|
||||||
|
|
||||||
|
return $this->json($accompanyingPeriod->getRequestor(), 200, [], ['groups' => [ 'read']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
|
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
|
||||||
|
@ -6,6 +6,7 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
|||||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||||
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
|
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
@ -42,6 +43,41 @@ class AccompanyingCourseController extends Controller
|
|||||||
$this->dispatcher = $dispatcher;
|
$this->dispatcher = $dispatcher;
|
||||||
$this->validator = $validator;
|
$this->validator = $validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{_locale}/person/parcours/new", name="chill_person_accompanying_course_new")
|
||||||
|
*/
|
||||||
|
public function newAction(Request $request): Response
|
||||||
|
{
|
||||||
|
$period = new AccompanyingPeriod();
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
if ($request->query->has('person_id')) {
|
||||||
|
$personIds = $request->query->get('person_id');
|
||||||
|
|
||||||
|
if (FALSE === \is_array($personIds)) {
|
||||||
|
throw new BadRequestException("person_id parameter should be an array");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($personIds as $personId) {
|
||||||
|
$person = $em->getRepository(Person::class)->find($personId);
|
||||||
|
if (NULL !== $person) {
|
||||||
|
$period->addPerson($person);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $period);
|
||||||
|
|
||||||
|
$em->persist($period);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('chill_person_accompanying_course_show', [
|
||||||
|
'accompanying_period_id' => $period->getId()
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Homepage of Accompanying Course section
|
* Homepage of Accompanying Course section
|
||||||
*
|
*
|
||||||
@ -86,78 +122,4 @@ class AccompanyingCourseController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get API Data for showing endpoint
|
|
||||||
*
|
|
||||||
* @Route(
|
|
||||||
* "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/show.{_format}",
|
|
||||||
* name="chill_person_accompanying_course_api_show"
|
|
||||||
* )
|
|
||||||
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
|
|
||||||
*/
|
|
||||||
public function showAPI(AccompanyingPeriod $accompanyingCourse, $_format): Response
|
|
||||||
{
|
|
||||||
// TODO check ACL on AccompanyingPeriod
|
|
||||||
|
|
||||||
$this->dispatcher->dispatch(
|
|
||||||
AccompanyingPeriodPrivacyEvent::ACCOMPANYING_PERIOD_PRIVACY_EVENT,
|
|
||||||
new AccompanyingPeriodPrivacyEvent($accompanyingCourse, [
|
|
||||||
'action' => 'showApi'
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
switch ($_format) {
|
|
||||||
case 'json':
|
|
||||||
return $this->json($accompanyingCourse);
|
|
||||||
default:
|
|
||||||
throw new BadRequestException('Unsupported format');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get API Data for showing endpoint
|
|
||||||
*
|
|
||||||
* @Route(
|
|
||||||
* "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/participation.{_format}",
|
|
||||||
* name="chill_person_accompanying_course_api_add_participation",
|
|
||||||
* methods={"POST","DELETE"},
|
|
||||||
* format="json",
|
|
||||||
* requirements={
|
|
||||||
* "_format": "json",
|
|
||||||
* }
|
|
||||||
* )
|
|
||||||
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
|
|
||||||
*/
|
|
||||||
public function participationAPI(Request $request, AccompanyingPeriod $accompanyingCourse, $_format): Response
|
|
||||||
{
|
|
||||||
switch ($_format) {
|
|
||||||
case 'json':
|
|
||||||
$person = $this->serializer->deserialize($request->getContent(), Person::class, $_format, [
|
|
||||||
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new BadRequestException('Unsupported format');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL === $person) {
|
|
||||||
throw new BadRequestException('person id not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO add acl
|
|
||||||
$participation = ($request->getMethod() === 'POST') ?
|
|
||||||
$accompanyingCourse->addPerson($person) : $accompanyingCourse->removePerson($person);
|
|
||||||
|
|
||||||
$errors = $this->validator->validate($accompanyingCourse);
|
|
||||||
|
|
||||||
if ($errors->count() > 0) {
|
|
||||||
// only format accepted
|
|
||||||
return $this->json($errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->getDoctrine()->getManager()->flush();
|
|
||||||
|
|
||||||
return $this->json($participation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2015-2021 Champs-Libres Coopérative <info@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/>.
|
|
||||||
*/
|
|
||||||
namespace Chill\PersonBundle\Controller;
|
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
|
|
||||||
|
|
||||||
class ApiPersonController extends Controller
|
|
||||||
{
|
|
||||||
public function viewAction($id, $_format)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2021 Champs-Libres Coopérative <info@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/>.
|
||||||
|
*/
|
||||||
|
namespace Chill\PersonBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
|
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
|
||||||
|
class PersonApiController extends ApiController
|
||||||
|
{
|
||||||
|
private AuthorizationHelper $authorizationHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AuthorizationHelper $authorizationHelper
|
||||||
|
*/
|
||||||
|
public function __construct(AuthorizationHelper $authorizationHelper)
|
||||||
|
{
|
||||||
|
$this->authorizationHelper = $authorizationHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createEntity(string $action, Request $request): object
|
||||||
|
{
|
||||||
|
$person = parent::createEntity($action, $request);
|
||||||
|
|
||||||
|
// TODO temporary hack to allow creation of person with fake center
|
||||||
|
$centers = $this->authorizationHelper->getReachableCenters($this->getUser(),
|
||||||
|
new Role(PersonVoter::CREATE));
|
||||||
|
$person->setCenter($centers[0]);
|
||||||
|
|
||||||
|
return $person;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2014-2016 Julien Fastré <julien.fastre@champs-libres.coop>
|
* Copyright (C) 2014-2016 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
*
|
*
|
||||||
@ -166,6 +165,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
|||||||
$this->prependHomepageWidget($container);
|
$this->prependHomepageWidget($container);
|
||||||
$this->prependDoctrineDQL($container);
|
$this->prependDoctrineDQL($container);
|
||||||
$this->prependCruds($container);
|
$this->prependCruds($container);
|
||||||
|
$this->prependWorkflows($container);
|
||||||
|
|
||||||
//add person_fields parameter as global
|
//add person_fields parameter as global
|
||||||
$chillPersonConfig = $container->getExtensionConfig($this->getAlias());
|
$chillPersonConfig = $container->getExtensionConfig($this->getAlias());
|
||||||
@ -195,6 +195,39 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function prependWorkflows(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$container->prependExtensionConfig('framework', [
|
||||||
|
'workflows' => [
|
||||||
|
'accompanying_period_lifecycle' => [
|
||||||
|
'type' => 'state_machine',
|
||||||
|
'audit_trail' => [
|
||||||
|
'enabled' => true
|
||||||
|
],
|
||||||
|
'marking_store' => [
|
||||||
|
'type' => 'method',
|
||||||
|
'property' => 'step',
|
||||||
|
],
|
||||||
|
'supports' => [
|
||||||
|
'Chill\PersonBundle\Entity\AccompanyingPeriod'
|
||||||
|
],
|
||||||
|
'initial_marking' => 'DRAFT',
|
||||||
|
'places' => [
|
||||||
|
'DRAFT',
|
||||||
|
'CONFIRMED',
|
||||||
|
],
|
||||||
|
'transitions' => [
|
||||||
|
'confirm' => [
|
||||||
|
'from' => 'DRAFT',
|
||||||
|
'to' => 'CONFIRMED'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a widget "add a person" on the homepage, automatically
|
* Add a widget "add a person" on the homepage, automatically
|
||||||
*
|
*
|
||||||
@ -322,7 +355,14 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
|||||||
'actions' => [
|
'actions' => [
|
||||||
'_entity' => [
|
'_entity' => [
|
||||||
'roles' => [
|
'roles' => [
|
||||||
Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
|
Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
Request::METHOD_PATCH => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
Request::METHOD_PUT => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
],
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_PUT => true,
|
||||||
|
Request::METHOD_PATCH => true,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'participation' => [
|
'participation' => [
|
||||||
@ -336,8 +376,79 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
|||||||
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
|
Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
'resource' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_POST => true,
|
||||||
|
Request::METHOD_DELETE => true,
|
||||||
|
Request::METHOD_GET => false,
|
||||||
|
Request::METHOD_HEAD => false,
|
||||||
|
],
|
||||||
|
'roles' => [
|
||||||
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'comment' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_POST => true,
|
||||||
|
Request::METHOD_DELETE => true,
|
||||||
|
Request::METHOD_GET => false,
|
||||||
|
Request::METHOD_HEAD => false,
|
||||||
|
],
|
||||||
|
'roles' => [
|
||||||
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'requestor' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_POST => true,
|
||||||
|
Request::METHOD_DELETE => true,
|
||||||
|
Request::METHOD_GET => false,
|
||||||
|
Request::METHOD_HEAD => false,
|
||||||
|
],
|
||||||
|
'roles' => [
|
||||||
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'scope' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_POST => true,
|
||||||
|
Request::METHOD_DELETE => true,
|
||||||
|
Request::METHOD_GET => false,
|
||||||
|
Request::METHOD_HEAD => false,
|
||||||
|
],
|
||||||
|
'roles' => [
|
||||||
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'socialissue' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_POST => true,
|
||||||
|
Request::METHOD_DELETE => true,
|
||||||
|
Request::METHOD_GET => false,
|
||||||
|
Request::METHOD_HEAD => false,
|
||||||
|
],
|
||||||
|
'controller_action' => 'socialIssueApi',
|
||||||
|
'roles' => [
|
||||||
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'confirm' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_POST => true,
|
||||||
|
Request::METHOD_GET => false,
|
||||||
|
Request::METHOD_HEAD => false,
|
||||||
|
],
|
||||||
|
'roles' => [
|
||||||
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||||
|
]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -360,7 +471,49 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
[
|
||||||
|
'class' => \Chill\PersonBundle\Entity\SocialWork\SocialIssue::class,
|
||||||
|
'name' => 'social_work_social_issue',
|
||||||
|
'base_path' => '/api/1.0/person/social-work/social-issue',
|
||||||
|
'base_role' => 'ROLE_USER',
|
||||||
|
'actions' => [
|
||||||
|
'_index' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'_entity' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'class' => \Chill\PersonBundle\Entity\Person::class,
|
||||||
|
'name' => 'person',
|
||||||
|
'base_path' => '/api/1.0/person/person',
|
||||||
|
'base_role' => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
|
||||||
|
'controller' => \Chill\PersonBundle\Controller\PersonApiController::class,
|
||||||
|
'actions' => [
|
||||||
|
'_entity' => [
|
||||||
|
'methods' => [
|
||||||
|
Request::METHOD_GET => true,
|
||||||
|
Request::METHOD_HEAD => true,
|
||||||
|
Request::METHOD_POST=> true,
|
||||||
|
],
|
||||||
|
'roles' => [
|
||||||
|
Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
|
||||||
|
Request::METHOD_HEAD => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
|
||||||
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\PersonVoter::CREATE,
|
||||||
|
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -22,25 +22,34 @@
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Entity;
|
namespace Chill\PersonBundle\Entity;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
||||||
|
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
|
use DateTimeInterface;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AccompanyingPeriod Class
|
* AccompanyingPeriod Class
|
||||||
*
|
*
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
* @ORM\Table(name="chill_person_accompanying_period")
|
* @ORM\Table(name="chill_person_accompanying_period")
|
||||||
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
|
* "accompanying_period"=AccompanyingPeriod::class
|
||||||
|
* })
|
||||||
*/
|
*/
|
||||||
class AccompanyingPeriod
|
class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Mark an accompanying period as "occasional"
|
* Mark an accompanying period as "occasional"
|
||||||
@ -80,6 +89,7 @@ class AccompanyingPeriod
|
|||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
* @ORM\Column(name="id", type="integer")
|
* @ORM\Column(name="id", type="integer")
|
||||||
* @ORM\GeneratedValue(strategy="AUTO")
|
* @ORM\GeneratedValue(strategy="AUTO")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
@ -87,6 +97,7 @@ class AccompanyingPeriod
|
|||||||
* @var \DateTime
|
* @var \DateTime
|
||||||
*
|
*
|
||||||
* @ORM\Column(type="date")
|
* @ORM\Column(type="date")
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $openingDate;
|
private $openingDate;
|
||||||
|
|
||||||
@ -94,6 +105,7 @@ class AccompanyingPeriod
|
|||||||
* @var \DateTime
|
* @var \DateTime
|
||||||
*
|
*
|
||||||
* @ORM\Column(type="date", nullable=true)
|
* @ORM\Column(type="date", nullable=true)
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $closingDate = null;
|
private $closingDate = null;
|
||||||
|
|
||||||
@ -101,6 +113,7 @@ class AccompanyingPeriod
|
|||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $remark = '';
|
private $remark = '';
|
||||||
|
|
||||||
@ -108,17 +121,28 @@ class AccompanyingPeriod
|
|||||||
* @var Collection
|
* @var Collection
|
||||||
*
|
*
|
||||||
* @ORM\OneToMany(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\Comment",
|
* @ORM\OneToMany(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\Comment",
|
||||||
* mappedBy="accompanyingPeriod"
|
* mappedBy="accompanyingPeriod",
|
||||||
|
* cascade={"persist", "remove"},
|
||||||
|
* orphanRemoval=true
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
private $comments;
|
private $comments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(
|
||||||
|
* targetEntity=Comment::class
|
||||||
|
* )
|
||||||
|
* @Groups({"read"})
|
||||||
|
*/
|
||||||
|
private ?Comment $initialComment = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection
|
* @var Collection
|
||||||
*
|
*
|
||||||
* @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class,
|
* @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class,
|
||||||
* mappedBy="accompanyingPeriod",
|
* mappedBy="accompanyingPeriod",
|
||||||
* cascade={"persist", "refresh", "remove", "merge", "detach"})
|
* cascade={"persist", "refresh", "remove", "merge", "detach"})
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $participations;
|
private $participations;
|
||||||
|
|
||||||
@ -128,36 +152,42 @@ class AccompanyingPeriod
|
|||||||
* @ORM\ManyToOne(
|
* @ORM\ManyToOne(
|
||||||
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive")
|
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive")
|
||||||
* @ORM\JoinColumn(nullable=true)
|
* @ORM\JoinColumn(nullable=true)
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $closingMotive = null;
|
private $closingMotive = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=User::class)
|
* @ORM\ManyToOne(targetEntity=User::class)
|
||||||
* @ORM\JoinColumn(nullable=true)
|
* @ORM\JoinColumn(nullable=true)
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $user;
|
private $user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=User::class)
|
* @ORM\ManyToOne(targetEntity=User::class)
|
||||||
* @ORM\JoinColumn(nullable=true)
|
* @ORM\JoinColumn(nullable=true)
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $createdBy;
|
private $createdBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
* @ORM\Column(type="string", length=32, nullable=true)
|
* @ORM\Column(type="string", length=32, nullable=true)
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $step = self::STEP_DRAFT;
|
private $step = self::STEP_DRAFT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=Origin::class)
|
* @ORM\ManyToOne(targetEntity=Origin::class)
|
||||||
* @ORM\JoinColumn(nullable=true)
|
* @ORM\JoinColumn(nullable=true)
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $origin;
|
private $origin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
* @ORM\Column(type="string", nullable=true)
|
* @ORM\Column(type="string", nullable=true)
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $intensity;
|
private $intensity;
|
||||||
|
|
||||||
@ -172,6 +202,7 @@ class AccompanyingPeriod
|
|||||||
* joinColumns={@ORM\JoinColumn(name="accompanying_period_id", referencedColumnName="id")},
|
* joinColumns={@ORM\JoinColumn(name="accompanying_period_id", referencedColumnName="id")},
|
||||||
* inverseJoinColumns={@ORM\JoinColumn(name="scope_id", referencedColumnName="id")}
|
* inverseJoinColumns={@ORM\JoinColumn(name="scope_id", referencedColumnName="id")}
|
||||||
* )
|
* )
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $scopes;
|
private $scopes;
|
||||||
|
|
||||||
@ -189,19 +220,22 @@ class AccompanyingPeriod
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
* @ORM\Column(type="boolean")
|
* @ORM\Column(type="boolean", options={"default": false} )
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $requestorAnonymous = false;
|
private $requestorAnonymous = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
* @ORM\Column(type="boolean")
|
* @ORM\Column(type="boolean", options={"default": false} )
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $emergency = false;
|
private $emergency = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
* @ORM\Column(type="boolean")
|
* @ORM\Column(type="boolean", options={"default": false} )
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $confidential = false;
|
private $confidential = false;
|
||||||
|
|
||||||
@ -210,21 +244,54 @@ class AccompanyingPeriod
|
|||||||
*
|
*
|
||||||
* @ORM\OneToMany(
|
* @ORM\OneToMany(
|
||||||
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\Resource",
|
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\Resource",
|
||||||
* mappedBy="accompanyingPeriod"
|
* mappedBy="accompanyingPeriod",
|
||||||
|
* cascade={"persist", "remove"},
|
||||||
|
* orphanRemoval=true
|
||||||
* )
|
* )
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $resources;
|
private $resources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToMany(
|
||||||
|
* targetEntity=SocialIssue::class
|
||||||
|
* )
|
||||||
|
* @ORM\JoinTable(
|
||||||
|
* name="chill_person_accompanying_period_social_issues"
|
||||||
|
* )
|
||||||
|
* @Groups({"read"})
|
||||||
|
*/
|
||||||
|
private Collection $socialIssues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
|
||||||
|
*/
|
||||||
|
private \DateTimeInterface $createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(
|
||||||
|
* targetEntity=User::class
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
private User $updatedBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
|
||||||
|
*/
|
||||||
|
private \DateTimeInterface $updatedAt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AccompanyingPeriod constructor.
|
* AccompanyingPeriod constructor.
|
||||||
*
|
*
|
||||||
* @param \DateTime $dateOpening
|
* @param \DateTime $dateOpening
|
||||||
* @uses AccompanyingPeriod::setClosingDate()
|
* @uses AccompanyingPeriod::setClosingDate()
|
||||||
*/
|
*/
|
||||||
public function __construct(\DateTime $dateOpening) {
|
public function __construct(\DateTime $dateOpening = null) {
|
||||||
$this->setOpeningDate($dateOpening);
|
$this->setOpeningDate($dateOpening ?? new \DateTime('now'));
|
||||||
$this->participations = new ArrayCollection();
|
$this->participations = new ArrayCollection();
|
||||||
$this->scopes = new ArrayCollection();
|
$this->scopes = new ArrayCollection();
|
||||||
|
$this->socialIssues = new ArrayCollection();
|
||||||
|
$this->comments = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,23 +381,55 @@ class AccompanyingPeriod
|
|||||||
return $this->remark;
|
return $this->remark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Groups({"read"})
|
||||||
|
*/
|
||||||
public function getComments(): Collection
|
public function getComments(): Collection
|
||||||
{
|
{
|
||||||
return $this->comments;
|
return $this->comments->filter(function (Comment $c) {
|
||||||
|
return $c !== $this->initialComment;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addComment(Comment $comment): self
|
public function addComment(Comment $comment): self
|
||||||
{
|
{
|
||||||
$this->comments[] = $comment;
|
$this->comments[] = $comment;
|
||||||
|
$comment->setAccompanyingPeriod($this);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeComment(Comment $comment): void
|
public function removeComment(Comment $comment): void
|
||||||
{
|
{
|
||||||
|
$comment->setAccompanyingPeriod(null);
|
||||||
$this->comments->removeElement($comment);
|
$this->comments->removeElement($comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Groups({"write"})
|
||||||
|
*/
|
||||||
|
public function setInitialComment(?Comment $comment = null): self
|
||||||
|
{
|
||||||
|
if (NULL !== $this->initialComment) {
|
||||||
|
$this->removeComment($this->initialComment);
|
||||||
|
}
|
||||||
|
if ($comment instanceof Comment) {
|
||||||
|
$this->addComment($comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->initialComment = $comment;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Groups({"read"})
|
||||||
|
*/
|
||||||
|
public function getInitialComment(): ?Comment
|
||||||
|
{
|
||||||
|
return $this->initialComment;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Participations Collection
|
* Get Participations Collection
|
||||||
*/
|
*/
|
||||||
@ -382,9 +481,9 @@ class AccompanyingPeriod
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add Person
|
* Open a new participation for a person
|
||||||
*/
|
*/
|
||||||
public function addPerson(Person $person = null): AccompanyingPeriodParticipation
|
public function createParticipationFor(Person $person): AccompanyingPeriodParticipation
|
||||||
{
|
{
|
||||||
$participation = new AccompanyingPeriodParticipation($this, $person);
|
$participation = new AccompanyingPeriodParticipation($this, $person);
|
||||||
$this->participations[] = $participation;
|
$this->participations[] = $participation;
|
||||||
@ -392,10 +491,24 @@ class AccompanyingPeriod
|
|||||||
return $participation;
|
return $participation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addPerson(Person $person = null): self
|
||||||
|
{
|
||||||
|
if (NULL !== $person) {
|
||||||
|
$this->createParticipationFor($person);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove Person
|
* Close a participation for a person
|
||||||
|
*
|
||||||
|
* Search for the person's participation and set the end date at
|
||||||
|
* 'now'.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function removePerson(Person $person): ?AccompanyingPeriodParticipation
|
public function closeParticipationFor($person): ?AccompanyingPeriodParticipation
|
||||||
{
|
{
|
||||||
$participation = $this->getOpenParticipationContainsPerson($person);
|
$participation = $this->getOpenParticipationContainsPerson($person);
|
||||||
|
|
||||||
@ -407,6 +520,17 @@ class AccompanyingPeriod
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove Person
|
||||||
|
*/
|
||||||
|
public function removePerson(Person $person): self
|
||||||
|
{
|
||||||
|
$this->closeParticipationFor($person);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getClosingMotive(): ?ClosingMotive
|
public function getClosingMotive(): ?ClosingMotive
|
||||||
{
|
{
|
||||||
return $this->closingMotive;
|
return $this->closingMotive;
|
||||||
@ -514,9 +638,9 @@ class AccompanyingPeriod
|
|||||||
return $this->requestorPerson;
|
return $this->requestorPerson;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRequestorPerson(Person $requestorPerson): self
|
private function setRequestorPerson(Person $requestorPerson = null): self
|
||||||
{
|
{
|
||||||
$this->requestorPerson = ($this->requestorThirdParty === null) ? $requestorPerson : null;
|
$this->requestorPerson = $requestorPerson;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -526,21 +650,53 @@ class AccompanyingPeriod
|
|||||||
return $this->requestorThirdParty;
|
return $this->requestorThirdParty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRequestorThirdParty(ThirdParty $requestorThirdParty): self
|
private function setRequestorThirdParty(ThirdParty $requestorThirdParty = null): self
|
||||||
{
|
{
|
||||||
$this->requestorThirdParty = ($this->requestorPerson === null) ? $requestorThirdParty : null;
|
$this->requestorThirdParty = $requestorThirdParty;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Person|ThirdParty
|
* @return Person|ThirdParty
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
public function getRequestor()
|
public function getRequestor()
|
||||||
{
|
{
|
||||||
return $this->requestorPerson ?? $this->requestorThirdParty;
|
return $this->requestorPerson ?? $this->requestorThirdParty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a requestor
|
||||||
|
*
|
||||||
|
* The requestor is either an instance of ThirdParty, or an
|
||||||
|
* instance of Person
|
||||||
|
*
|
||||||
|
* @param $requestor Person|ThirdParty
|
||||||
|
* @return self
|
||||||
|
* @throw UnexpectedValueException if the requestor is not a Person or ThirdParty
|
||||||
|
* @Groups({"write"})
|
||||||
|
*/
|
||||||
|
public function setRequestor($requestor): self
|
||||||
|
{
|
||||||
|
if ($requestor instanceof Person) {
|
||||||
|
$this->setRequestorThirdParty(NULL);
|
||||||
|
$this->setRequestorPerson($requestor);
|
||||||
|
} elseif ($requestor instanceof ThirdParty) {
|
||||||
|
$this->setRequestorThirdParty($requestor);
|
||||||
|
$this->setRequestorPerson(NULL);
|
||||||
|
} elseif (NULL === $requestor) {
|
||||||
|
$this->setRequestorPerson(NULL);
|
||||||
|
$this->setRequestorThirdParty(NULL);
|
||||||
|
} else {
|
||||||
|
throw new \UnexpectedValueException("requestor is not an instance of Person or ThirdParty");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function isRequestorAnonymous(): bool
|
public function isRequestorAnonymous(): bool
|
||||||
{
|
{
|
||||||
return $this->requestorAnonymous;
|
return $this->requestorAnonymous;
|
||||||
@ -637,6 +793,7 @@ class AccompanyingPeriod
|
|||||||
|
|
||||||
public function addResource(Resource $resource): self
|
public function addResource(Resource $resource): self
|
||||||
{
|
{
|
||||||
|
$resource->setAccompanyingPeriod($this);
|
||||||
$this->resources[] = $resource;
|
$this->resources[] = $resource;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -644,9 +801,27 @@ class AccompanyingPeriod
|
|||||||
|
|
||||||
public function removeResource(Resource $resource): void
|
public function removeResource(Resource $resource): void
|
||||||
{
|
{
|
||||||
|
$resource->setAccompanyingPeriod(null);
|
||||||
$this->resources->removeElement($resource);
|
$this->resources->removeElement($resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSocialIssues(): Collection
|
||||||
|
{
|
||||||
|
return $this->socialIssues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSocialIssue(SocialIssue $socialIssue): self
|
||||||
|
{
|
||||||
|
$this->socialIssues[] = $socialIssue;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeSocialIssue(SocialIssue $socialIssue): void
|
||||||
|
{
|
||||||
|
$this->socialIssues->removeElement($socialIssue);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of all persons which are participating to this course
|
* Get a list of all persons which are participating to this course
|
||||||
*
|
*
|
||||||
@ -662,4 +837,25 @@ class AccompanyingPeriod
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setCreatedAt(\DateTimeInterface $datetime): self
|
||||||
|
{
|
||||||
|
$this->createdAt = $datetime;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUpdatedBy(User $user): self
|
||||||
|
{
|
||||||
|
$this->updatedBy = $user;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUpdatedAt(\DateTimeInterface $datetime): self
|
||||||
|
{
|
||||||
|
$this->updatedAt = $datetime;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,17 +25,25 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
|
|||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
* @ORM\Table(name="chill_person_accompanying_period_comment")
|
* @ORM\Table(name="chill_person_accompanying_period_comment")
|
||||||
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
|
* "accompanying_period_comment"=Comment::class
|
||||||
|
* })
|
||||||
*/
|
*/
|
||||||
class Comment
|
class Comment implements TrackCreationInterface, TrackUpdateInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
* @ORM\GeneratedValue
|
* @ORM\GeneratedValue
|
||||||
* @ORM\Column(type="integer")
|
* @ORM\Column(type="integer")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
@ -43,34 +51,39 @@ class Comment
|
|||||||
* @ORM\ManyToOne(
|
* @ORM\ManyToOne(
|
||||||
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod",
|
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod",
|
||||||
* inversedBy="comments")
|
* inversedBy="comments")
|
||||||
* @ORM\JoinColumn(nullable=false)
|
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||||
*/
|
*/
|
||||||
private $accompanyingPeriod;
|
private $accompanyingPeriod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=User::class)
|
* @ORM\ManyToOne(targetEntity=User::class)
|
||||||
* @ORM\JoinColumn(nullable=false)
|
* @ORM\JoinColumn(nullable=false)
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $creator;
|
private $creator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="datetime")
|
* @ORM\Column(type="datetime")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $createdAt;
|
private $createdAt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="datetime")
|
* @ORM\Column(type="datetime")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $updatedAt;
|
private $updatedAt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=User::class)
|
* @ORM\ManyToOne(targetEntity=User::class)
|
||||||
* @ORM\JoinColumn(nullable=false)
|
* @ORM\JoinColumn(nullable=false)
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $updatedBy;
|
private $updatedBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $content;
|
private $content;
|
||||||
|
|
||||||
@ -103,6 +116,11 @@ class Comment
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setCreatedBy(User $user): self
|
||||||
|
{
|
||||||
|
return $this->setCreator($user);
|
||||||
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): ?\DateTimeInterface
|
public function getCreatedAt(): ?\DateTimeInterface
|
||||||
{
|
{
|
||||||
return $this->createdAt;
|
return $this->createdAt;
|
||||||
@ -132,7 +150,7 @@ class Comment
|
|||||||
return $this->updatedBy;
|
return $this->updatedBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUpdatedBy(?User $updatedBy): self
|
public function setUpdatedBy(User $updatedBy): self
|
||||||
{
|
{
|
||||||
$this->updatedBy = $updatedBy;
|
$this->updatedBy = $updatedBy;
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
|
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
@ -34,16 +35,19 @@ class Origin
|
|||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
* @ORM\GeneratedValue
|
* @ORM\GeneratedValue
|
||||||
* @ORM\Column(type="integer")
|
* @ORM\Column(type="integer")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="json")
|
* @ORM\Column(type="json")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $label;
|
private $label;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="date_immutable", nullable=true)
|
* @ORM\Column(type="date_immutable", nullable=true)
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $noActiveAfter;
|
private $noActiveAfter;
|
||||||
|
|
||||||
|
@ -25,12 +25,18 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
|
|||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\PersonBundle\Repository\AccompanyingPeriod\ResourceRepository;
|
||||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
* @ORM\Table(name="chill_person_accompanying_period_resource")
|
* @ORM\Table(name="chill_person_accompanying_period_resource")
|
||||||
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
|
* "accompanying_period_resource"=Resource::class
|
||||||
|
* })
|
||||||
*/
|
*/
|
||||||
class Resource
|
class Resource
|
||||||
{
|
{
|
||||||
@ -38,6 +44,7 @@ class Resource
|
|||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
* @ORM\GeneratedValue
|
* @ORM\GeneratedValue
|
||||||
* @ORM\Column(type="integer")
|
* @ORM\Column(type="integer")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
@ -90,7 +97,7 @@ class Resource
|
|||||||
return $this->thirdParty;
|
return $this->thirdParty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setThirdParty(?ThirdParty $thirdParty): self
|
private function setThirdParty(?ThirdParty $thirdParty): self
|
||||||
{
|
{
|
||||||
$this->thirdParty = $thirdParty;
|
$this->thirdParty = $thirdParty;
|
||||||
|
|
||||||
@ -102,7 +109,7 @@ class Resource
|
|||||||
return $this->person;
|
return $this->person;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPerson(?Person $person): self
|
private function setPerson(?Person $person): self
|
||||||
{
|
{
|
||||||
$this->person = $person;
|
$this->person = $person;
|
||||||
|
|
||||||
@ -122,7 +129,33 @@ class Resource
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Person|ThirdParty
|
*
|
||||||
|
* @param $resource Person|ThirdParty
|
||||||
|
*/
|
||||||
|
public function setResource($resource): self
|
||||||
|
{
|
||||||
|
if ($resource instanceof ThirdParty) {
|
||||||
|
$this->setThirdParty($resource);
|
||||||
|
$this->setPerson(NULL);
|
||||||
|
} elseif ($resource instanceof Person) {
|
||||||
|
$this->setPerson($resource);
|
||||||
|
$this->setThirdParty(NULL);
|
||||||
|
} elseif (NULL === $resource) {
|
||||||
|
$this->setPerson(NULL);
|
||||||
|
$this->setThirdParty(NULL);
|
||||||
|
} else {
|
||||||
|
throw new \UnexpectedValueException(sprintf("the resource ".
|
||||||
|
"should be an instance of %s or %s", Person::class,
|
||||||
|
ThirdParty::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ThirdParty|Person
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
public function getResource()
|
public function getResource()
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,8 @@ namespace Chill\PersonBundle\Entity;
|
|||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AccompanyingPeriodParticipation Class
|
* AccompanyingPeriodParticipation Class
|
||||||
@ -32,6 +34,9 @@ use Doctrine\ORM\Mapping as ORM;
|
|||||||
* @package Chill\PersonBundle\Entity
|
* @package Chill\PersonBundle\Entity
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
* @ORM\Table(name="chill_person_accompanying_period_participation")
|
* @ORM\Table(name="chill_person_accompanying_period_participation")
|
||||||
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
|
* "accompanying_period_participation"=AccompanyingPeriodParticipation::class
|
||||||
|
* })
|
||||||
*/
|
*/
|
||||||
class AccompanyingPeriodParticipation
|
class AccompanyingPeriodParticipation
|
||||||
{
|
{
|
||||||
@ -39,12 +44,14 @@ class AccompanyingPeriodParticipation
|
|||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
* @ORM\GeneratedValue
|
* @ORM\GeneratedValue
|
||||||
* @ORM\Column(type="integer")
|
* @ORM\Column(type="integer")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="accompanyingPeriodParticipations")
|
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="accompanyingPeriodParticipations")
|
||||||
* @ORM\JoinColumn(name="person_id", referencedColumnName="id", nullable=false)
|
* @ORM\JoinColumn(name="person_id", referencedColumnName="id", nullable=false)
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $person;
|
private $person;
|
||||||
|
|
||||||
@ -56,11 +63,13 @@ class AccompanyingPeriodParticipation
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="date", nullable=false)
|
* @ORM\Column(type="date", nullable=false)
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $startDate;
|
private $startDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="date", nullable=true)
|
* @ORM\Column(type="date", nullable=true)
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $endDate = null;
|
private $endDate = null;
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ use Doctrine\Common\Collections\Collection;
|
|||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Criteria;
|
use Doctrine\Common\Collections\Criteria;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Person Class
|
* Person Class
|
||||||
@ -45,6 +46,9 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|||||||
* columns={"firstName", "lastName"}
|
* columns={"firstName", "lastName"}
|
||||||
* )})
|
* )})
|
||||||
* @ORM\HasLifecycleCallbacks()
|
* @ORM\HasLifecycleCallbacks()
|
||||||
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
|
* "person"=Person::class
|
||||||
|
* })
|
||||||
*/
|
*/
|
||||||
class Person implements HasCenterInterface
|
class Person implements HasCenterInterface
|
||||||
{
|
{
|
||||||
|
@ -4,10 +4,15 @@ namespace Chill\PersonBundle\Entity\SocialWork;
|
|||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
* @ORM\Table(name="chill_person_social_issue")
|
* @ORM\Table(name="chill_person_social_issue")
|
||||||
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
|
* "social_issue"=SocialIssue::class
|
||||||
|
* })
|
||||||
*/
|
*/
|
||||||
class SocialIssue
|
class SocialIssue
|
||||||
{
|
{
|
||||||
@ -35,6 +40,7 @@ class SocialIssue
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="json")
|
* @ORM\Column(type="json")
|
||||||
|
* @Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private $title = [];
|
private $title = [];
|
||||||
|
|
||||||
@ -59,6 +65,11 @@ class SocialIssue
|
|||||||
return $this->parent;
|
return $this->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasParent(): bool
|
||||||
|
{
|
||||||
|
return $this->parent !== null;
|
||||||
|
}
|
||||||
|
|
||||||
public function setParent(?self $parent): self
|
public function setParent(?self $parent): self
|
||||||
{
|
{
|
||||||
$this->parent = $parent;
|
$this->parent = $parent;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Chill\PersonBundle\Menu;
|
namespace Chill\PersonBundle\Menu;
|
||||||
|
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Knp\Menu\MenuItem;
|
use Knp\Menu\MenuItem;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
@ -32,24 +33,31 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
|
|
||||||
public function buildMenu($menuId, MenuItem $menu, array $parameters): void
|
public function buildMenu($menuId, MenuItem $menu, array $parameters): void
|
||||||
{
|
{
|
||||||
|
$period = $parameters['accompanyingCourse'];
|
||||||
|
|
||||||
$menu->addChild($this->translator->trans('Resume Accompanying Course'), [
|
$menu->addChild($this->translator->trans('Resume Accompanying Course'), [
|
||||||
'route' => 'chill_person_accompanying_course_index',
|
'route' => 'chill_person_accompanying_course_index',
|
||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
|
'accompanying_period_id' => $period->getId()
|
||||||
]])
|
]])
|
||||||
->setExtras(['order' => 10]);
|
->setExtras(['order' => 10]);
|
||||||
|
|
||||||
$menu->addChild($this->translator->trans('Edit Accompanying Course'), [
|
$menu->addChild($this->translator->trans('Edit Accompanying Course'), [
|
||||||
'route' => 'chill_person_accompanying_course_show',
|
'route' => 'chill_person_accompanying_course_show',
|
||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
|
'accompanying_period_id' => $period->getId()
|
||||||
]])
|
]])
|
||||||
->setExtras(['order' => 20]);
|
->setExtras(['order' => 20]);
|
||||||
|
|
||||||
|
if (AccompanyingPeriod::STEP_DRAFT === $period->getStep()) {
|
||||||
|
// no more menu items if the period is draft
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$menu->addChild($this->translator->trans('Accompanying Course Details'), [
|
$menu->addChild($this->translator->trans('Accompanying Course Details'), [
|
||||||
'route' => 'chill_person_accompanying_course_history',
|
'route' => 'chill_person_accompanying_course_history',
|
||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
|
'accompanying_period_id' => $period->getId()
|
||||||
]])
|
]])
|
||||||
->setExtras(['order' => 30]);
|
->setExtras(['order' => 30]);
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,14 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
'icons' => [ 'plus' ]
|
'icons' => [ 'plus' ]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$menu->addChild($this->translator->trans('Create an accompanying course'), [
|
||||||
|
'route' => 'chill_person_accompanying_course_new'
|
||||||
|
])
|
||||||
|
->setExtras([
|
||||||
|
'order' => 11,
|
||||||
|
'icons' => [ 'plus' ]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,31 +27,27 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ClosingMotiveRepository
|
|
||||||
* Entity repository for closing motives
|
|
||||||
*
|
|
||||||
* @package Chill\PersonBundle\Repository
|
|
||||||
*/
|
|
||||||
final class ClosingMotiveRepository
|
final class ClosingMotiveRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager)
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
$this->repository = $entityManager->getRepository(ClosingMotive::class);
|
$this->repository = $entityManager->getRepository(ClosingMotive::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bool $onlyLeaf
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function getActiveClosingMotive(bool $onlyLeaf = true)
|
public function getActiveClosingMotive(bool $onlyLeaf = true)
|
||||||
{
|
{
|
||||||
$rsm = new ResultSetMappingBuilder($this->repository->getEntityManager());
|
$rsm = new ResultSetMappingBuilder($this->entityManager);
|
||||||
$rsm->addRootEntityFromClassMetadata($this->repository->getClassName(), 'cm');
|
$rsm->addRootEntityFromClassMetadata($this->repository->getClassName(), 'cm');
|
||||||
|
|
||||||
$sql = "SELECT ".(string) $rsm."
|
$sql = "SELECT " . (string) $rsm . "
|
||||||
FROM chill_person_accompanying_period_closingmotive AS cm
|
FROM chill_person_accompanying_period_closingmotive AS cm
|
||||||
WHERE
|
WHERE
|
||||||
active IS TRUE ";
|
active IS TRUE ";
|
||||||
@ -65,8 +61,7 @@ final class ClosingMotiveRepository
|
|||||||
$sql .= " ORDER BY cm.ordering ASC";
|
$sql .= " ORDER BY cm.ordering ASC";
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->repository
|
->entityManager
|
||||||
->getEntityManager()
|
|
||||||
->createNativeQuery($sql, $rsm)
|
->createNativeQuery($sql, $rsm)
|
||||||
->getResult();
|
->getResult();
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,6 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Comment|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Comment|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Comment[] findAll()
|
|
||||||
* @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class CommentRepository
|
final class CommentRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -26,12 +26,6 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Origin|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Origin|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Origin[] findAll()
|
|
||||||
* @method Origin[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class OriginRepository
|
final class OriginRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -23,15 +23,9 @@
|
|||||||
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
|
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
|
||||||
|
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Resource|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Resource|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Resource[] findAll()
|
|
||||||
* @method Resource[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class ResourceRepository
|
final class ResourceRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
@ -40,4 +34,9 @@ final class ResourceRepository
|
|||||||
{
|
{
|
||||||
$this->repository = $entityManager->getRepository(Resource::class);
|
$this->repository = $entityManager->getRepository(Resource::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function find($id, $lockMode = null, $lockVersion = null): ?Resource
|
||||||
|
{
|
||||||
|
return $this->repository->find($id, $lockMode, $lockVersion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,6 @@ use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method AccompanyingPeriodParticipation|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method AccompanyingPeriodParticipation[] findAll()
|
|
||||||
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class AccompanyingPeriodParticipationRepository
|
final class AccompanyingPeriodParticipationRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -26,12 +26,6 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method AccompanyingPeriod|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method AccompanyingPeriod|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method AccompanyingPeriod[] findAll()
|
|
||||||
* @method AccompanyingPeriod[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class AccompanyingPeriodRepository
|
final class AccompanyingPeriodRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -6,12 +6,6 @@ use Chill\PersonBundle\Entity\Household\HouseholdMembers;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method HouseholdMembers|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method HouseholdMembers|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method HouseholdMembers[] findAll()
|
|
||||||
* @method HouseholdMembers[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class HouseholdMembersRepository
|
final class HouseholdMembersRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
@ -20,33 +14,4 @@ final class HouseholdMembersRepository
|
|||||||
{
|
{
|
||||||
$this->repository = $entityManager->getRepository(HouseholdMembers::class);
|
$this->repository = $entityManager->getRepository(HouseholdMembers::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return HouseholdMembers[] Returns an array of HouseholdMembers objects
|
|
||||||
// */
|
|
||||||
/*
|
|
||||||
public function findByExampleField($value)
|
|
||||||
{
|
|
||||||
return $this->createQueryBuilder('h')
|
|
||||||
->andWhere('h.exampleField = :val')
|
|
||||||
->setParameter('val', $value)
|
|
||||||
->orderBy('h.id', 'ASC')
|
|
||||||
->setMaxResults(10)
|
|
||||||
->getQuery()
|
|
||||||
->getResult()
|
|
||||||
;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
public function findOneBySomeField($value): ?HouseholdMembers
|
|
||||||
{
|
|
||||||
return $this->createQueryBuilder('h')
|
|
||||||
->andWhere('h.exampleField = :val')
|
|
||||||
->setParameter('val', $value)
|
|
||||||
->getQuery()
|
|
||||||
->getOneOrNullResult()
|
|
||||||
;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,6 @@ use Chill\PersonBundle\Entity\Household\Household;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Household|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Household|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Household[] findAll()
|
|
||||||
* @method Household[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class HouseholdRepository
|
final class HouseholdRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
@ -20,33 +14,4 @@ final class HouseholdRepository
|
|||||||
{
|
{
|
||||||
$this->repository = $entityManager->getRepository(Household::class);
|
$this->repository = $entityManager->getRepository(Household::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return Household[] Returns an array of Household objects
|
|
||||||
// */
|
|
||||||
/*
|
|
||||||
public function findByExampleField($value)
|
|
||||||
{
|
|
||||||
return $this->createQueryBuilder('h')
|
|
||||||
->andWhere('h.exampleField = :val')
|
|
||||||
->setParameter('val', $value)
|
|
||||||
->orderBy('h.id', 'ASC')
|
|
||||||
->setMaxResults(10)
|
|
||||||
->getQuery()
|
|
||||||
->getResult()
|
|
||||||
;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
public function findOneBySomeField($value): ?Household
|
|
||||||
{
|
|
||||||
return $this->createQueryBuilder('h')
|
|
||||||
->andWhere('h.exampleField = :val')
|
|
||||||
->setParameter('val', $value)
|
|
||||||
->getQuery()
|
|
||||||
->getOneOrNullResult()
|
|
||||||
;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,6 @@ use Chill\PersonBundle\Entity\PersonAltName;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* PersonAltNameRepository
|
|
||||||
*
|
|
||||||
* This class was generated by the Doctrine ORM. Add your own custom
|
|
||||||
* repository methods below.
|
|
||||||
*/
|
|
||||||
final class PersonAltNameRepository
|
final class PersonAltNameRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -7,11 +7,6 @@ use Chill\PersonBundle\Entity\PersonNotDuplicate;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* Class PersonNotDuplicateRepository
|
|
||||||
*
|
|
||||||
* @package Chill\PersonBundle\Repository
|
|
||||||
*/
|
|
||||||
final class PersonNotDuplicateRepository
|
final class PersonNotDuplicateRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
@ -21,12 +16,7 @@ final class PersonNotDuplicateRepository
|
|||||||
$this->repository = $entityManager->getRepository(PersonNotDuplicate::class);
|
$this->repository = $entityManager->getRepository(PersonNotDuplicate::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function findNotDuplicatePerson(Person $person): array
|
||||||
* @param \Chill\PersonBundle\Entity\Person $person
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function findNotDuplicatePerson(Person $person)
|
|
||||||
{
|
{
|
||||||
$qb = $this->repository->createQueryBuilder('pnd');
|
$qb = $this->repository->createQueryBuilder('pnd');
|
||||||
$qb->select('pnd')
|
$qb->select('pnd')
|
||||||
@ -36,6 +26,7 @@ final class PersonNotDuplicateRepository
|
|||||||
$result = $qb->getQuery()->getResult();
|
$result = $qb->getQuery()->getResult();
|
||||||
|
|
||||||
$persons = [];
|
$persons = [];
|
||||||
|
|
||||||
foreach ($result as $row) {
|
foreach ($result as $row) {
|
||||||
if ($row->getPerson1() === $person) {
|
if ($row->getPerson1() === $person) {
|
||||||
$persons[] = $row->getPerson2();
|
$persons[] = $row->getPerson2();
|
||||||
|
@ -32,17 +32,15 @@ final class PersonRepository
|
|||||||
$this->repository = $entityManager->getRepository(Person::class);
|
$this->repository = $entityManager->getRepository(Person::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function find($id, $lockMode = null, $lockVersion = null)
|
public function find($id, $lockMode = null, $lockVersion = null): ?Person
|
||||||
{
|
{
|
||||||
return $this->repository->find($id, $lockMode, $lockVersion);
|
return $this->repository->find($id, $lockMode, $lockVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $phonenumber
|
|
||||||
* @param $centers
|
* @param $centers
|
||||||
* @param $firstResult
|
* @param $firstResult
|
||||||
* @param $maxResults
|
* @param $maxResults
|
||||||
* @param array $only
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
@ -67,10 +65,7 @@ final class PersonRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $phonenumber
|
|
||||||
* @param $centers
|
* @param $centers
|
||||||
* @param array $only
|
|
||||||
* @return int
|
|
||||||
* @throws \Doctrine\ORM\NoResultException
|
* @throws \Doctrine\ORM\NoResultException
|
||||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||||
*/
|
*/
|
||||||
@ -78,8 +73,7 @@ final class PersonRepository
|
|||||||
string $phonenumber,
|
string $phonenumber,
|
||||||
$centers,
|
$centers,
|
||||||
array $only = ['mobile', 'phone']
|
array $only = ['mobile', 'phone']
|
||||||
): int
|
): int {
|
||||||
{
|
|
||||||
$qb = $this->repository->createQueryBuilder('p');
|
$qb = $this->repository->createQueryBuilder('p');
|
||||||
$qb->select('COUNT(p)');
|
$qb->select('COUNT(p)');
|
||||||
|
|
||||||
@ -90,12 +84,9 @@ final class PersonRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param QueryBuilder $qb
|
|
||||||
* @param string $phonenumber
|
|
||||||
* @param array $only
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
protected function addPhoneNumber(QueryBuilder $qb, string $phonenumber, array $only)
|
protected function addPhoneNumber(QueryBuilder $qb, string $phonenumber, array $only): void
|
||||||
{
|
{
|
||||||
if (count($only) === 0) {
|
if (count($only) === 0) {
|
||||||
throw new \Exception("No array field to search");
|
throw new \Exception("No array field to search");
|
||||||
@ -117,20 +108,12 @@ final class PersonRepository
|
|||||||
$qb->setParameter('phonenumber', '%'.$phonenumber.'%');
|
$qb->setParameter('phonenumber', '%'.$phonenumber.'%');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function parsePhoneNumber(string $phonenumber): string
|
||||||
* @param $phonenumber
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function parsePhoneNumber($phonenumber): string
|
|
||||||
{
|
{
|
||||||
return \str_replace(' ', '', $phonenumber);
|
return \str_replace(' ', '', $phonenumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function addByCenters(QueryBuilder $qb, array $centers): void
|
||||||
* @param QueryBuilder $qb
|
|
||||||
* @param array $centers
|
|
||||||
*/
|
|
||||||
protected function addByCenters(QueryBuilder $qb, array $centers)
|
|
||||||
{
|
{
|
||||||
if (count($centers) > 0) {
|
if (count($centers) > 0) {
|
||||||
$qb->andWhere($qb->expr()->in('p.center', ':centers'));
|
$qb->andWhere($qb->expr()->in('p.center', ':centers'));
|
||||||
|
@ -6,12 +6,6 @@ use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Evaluation|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Evaluation|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Evaluation[] findAll()
|
|
||||||
* @method Evaluation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class EvaluationRepository
|
final class EvaluationRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -6,12 +6,6 @@ use Chill\PersonBundle\Entity\SocialWork\Goal;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Goal|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Goal|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Goal[] findAll()
|
|
||||||
* @method Goal[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class GoalRepository
|
final class GoalRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -6,12 +6,6 @@ use Chill\PersonBundle\Entity\SocialWork\Result;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Result|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Result|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Result[] findAll()
|
|
||||||
* @method Result[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class ResultRepository
|
final class ResultRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -6,12 +6,6 @@ use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method SocialAction|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method SocialAction|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method SocialAction[] findAll()
|
|
||||||
* @method SocialAction[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class SocialActionRepository
|
final class SocialActionRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -7,12 +7,6 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method SocialIssue|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method SocialIssue|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method SocialIssue[] findAll()
|
|
||||||
* @method SocialIssue[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
final class SocialIssueRepository
|
final class SocialIssueRepository
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
@ -1,25 +1,106 @@
|
|||||||
<template>
|
<template>
|
||||||
<accompanying-course></accompanying-course>
|
<banner></banner>
|
||||||
|
<sticky-nav></sticky-nav>
|
||||||
|
|
||||||
|
<h1 v-if="accompanyingCourse.step === 'DRAFT'">{{ $t('course.title.draft') }}</h1>
|
||||||
|
<h1 v-else>{{ $t('course.title.active') }}</h1>
|
||||||
|
|
||||||
<persons-associated></persons-associated>
|
<persons-associated></persons-associated>
|
||||||
<requestor></requestor>
|
<requestor></requestor>
|
||||||
|
<social-issue></social-issue>
|
||||||
|
<referrer></referrer>
|
||||||
|
<resources></resources>
|
||||||
|
<comment v-if="accompanyingCourse.step === 'DRAFT'"></comment>
|
||||||
|
<confirm v-if="accompanyingCourse.step === 'DRAFT'"></confirm>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
import Banner from './components/Banner.vue';
|
||||||
import AccompanyingCourse from './components/AccompanyingCourse.vue';
|
import StickyNav from './components/StickyNav.vue';
|
||||||
import PersonsAssociated from './components/PersonsAssociated.vue';
|
import PersonsAssociated from './components/PersonsAssociated.vue';
|
||||||
import Requestor from './components/Requestor.vue';
|
import Requestor from './components/Requestor.vue';
|
||||||
|
import SocialIssue from './components/SocialIssue.vue';
|
||||||
|
import Referrer from './components/Referrer.vue';
|
||||||
|
import Resources from './components/Resources.vue';
|
||||||
|
import Comment from './components/Comment.vue';
|
||||||
|
import Confirm from './components/Confirm.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
AccompanyingCourse,
|
Banner,
|
||||||
|
StickyNav,
|
||||||
PersonsAssociated,
|
PersonsAssociated,
|
||||||
Requestor
|
Requestor,
|
||||||
|
SocialIssue,
|
||||||
|
Referrer,
|
||||||
|
Resources,
|
||||||
|
Comment,
|
||||||
|
Confirm,
|
||||||
},
|
},
|
||||||
computed: mapState([
|
computed: mapState([
|
||||||
'accompanyingCourse'
|
'accompanyingCourse'
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div#accompanying-course {
|
||||||
|
h1 {
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
div.vue-component {
|
||||||
|
h2 {
|
||||||
|
margin-left: 0.7em;
|
||||||
|
position: relative;
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
/*
|
||||||
|
content: "\f192"; //circle-dot
|
||||||
|
content: "\f1dd"; //paragraph
|
||||||
|
content: "\f292"; //hashtag
|
||||||
|
content: "\f069"; //asterisk
|
||||||
|
*/
|
||||||
|
content: "\f142"; //ellipsis-v
|
||||||
|
font-family: "ForkAwesome";
|
||||||
|
color: #718596ab;
|
||||||
|
left: -22px;
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
a[name^="section"] {
|
||||||
|
position: absolute;
|
||||||
|
top: -3.5em; // ref. stickNav
|
||||||
|
}
|
||||||
|
}
|
||||||
|
padding: 0.8em 0em;
|
||||||
|
margin: 2em 0;
|
||||||
|
border: 1px dotted #718596ab;
|
||||||
|
border-radius: 5px;
|
||||||
|
border-left: 1px dotted #718596ab;
|
||||||
|
border-right: 1px dotted #718596ab;
|
||||||
|
/* debug components
|
||||||
|
position: relative;
|
||||||
|
&:before {
|
||||||
|
content: "vuejs component";
|
||||||
|
position: absolute;
|
||||||
|
left: 1.5em;
|
||||||
|
top: -0.9em;
|
||||||
|
background-color: white;
|
||||||
|
color: grey;
|
||||||
|
padding: 0 0.3em;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
dd {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
& > div {
|
||||||
|
margin: 1em 3em 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
const
|
|
||||||
locale = 'fr',
|
|
||||||
format = 'json'
|
|
||||||
, accompanying_period_id = window.accompanyingCourseId //tmp
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Endpoint chill_person_accompanying_course_api_show
|
* Endpoint v.2 chill_api_single_accompanying_course__entity
|
||||||
* method GET, get AccompanyingCourse Object
|
* method GET/HEAD, get AccompanyingCourse Instance
|
||||||
*
|
*
|
||||||
* @accompanying_period_id___ integer
|
* @id integer - id of accompanyingCourse
|
||||||
* @TODO var is not used but necessary in method signature
|
|
||||||
*/
|
*/
|
||||||
let getAccompanyingCourse = (accompanying_period_id___) => { //tmp
|
const getAccompanyingCourse = (id) => {
|
||||||
const url = `/${locale}/person/api/1.0/accompanying-course/${accompanying_period_id}/show.${format}`;
|
const url = `/api/1.0/person/accompanying-course/${id}.json`;
|
||||||
return fetch(url)
|
return fetch(url)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.ok) { return response.json(); }
|
if (response.ok) { return response.json(); }
|
||||||
@ -21,21 +14,150 @@ let getAccompanyingCourse = (accompanying_period_id___) => { //tmp
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Endpoint chill_person_accompanying_course_api_add_participation,
|
* Endpoint v.2 chill_api_single_accompanying_course__entity
|
||||||
|
* method PATCH, patch AccompanyingCourse Instance
|
||||||
|
*
|
||||||
|
* @id integer - id of accompanyingCourse
|
||||||
|
* @body Object - dictionary with changes to post
|
||||||
|
*/
|
||||||
|
const patchAccompanyingCourse = (id, body) => {
|
||||||
|
console.log('body', body);
|
||||||
|
const url = `/api/1.0/person/accompanying-course/${id}.json`;
|
||||||
|
return fetch(url, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Endpoint to change 'DRAFT' step to 'CONFIRMED'
|
||||||
|
*/
|
||||||
|
const confirmAccompanyingCourse = (id) => {
|
||||||
|
const url = `/api/1.0/person/accompanying-course/${id}/confirm.json`
|
||||||
|
return fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json;charset=utf-8'}
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Endpoint
|
||||||
|
*/
|
||||||
|
const getSocialIssues = () => {
|
||||||
|
const url = `/api/1.0/person/social-work/social-issue.json`;
|
||||||
|
return fetch(url)
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Endpoint v.2 chill_api_single_accompanying_course_participation,
|
||||||
* method POST/DELETE, add/close a participation to the accompanyingCourse
|
* method POST/DELETE, add/close a participation to the accompanyingCourse
|
||||||
*
|
*
|
||||||
* @accompanying_period_id integer - id of accompanyingCourse
|
* @id integer - id of accompanyingCourse
|
||||||
* @person_id integer - id of person
|
* @payload integer - id of person
|
||||||
* @method string - POST or DELETE
|
* @method string - POST or DELETE
|
||||||
*/
|
*/
|
||||||
let postParticipation = (accompanying_period_id, person_id, method) => {
|
const postParticipation = (id, payload, method) => {
|
||||||
const url = `/${locale}/person/api/1.0/accompanying-course/${accompanying_period_id}/participation.${format}`
|
const body = { type: payload.type, id: payload.id };
|
||||||
|
const url = `/api/1.0/person/accompanying-course/${id}/participation.json`;
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: method,
|
method: method,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json;charset=utf-8'
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({id: person_id})
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Endpoint v.2 chill_api_single_accompanying_course_requestor,
|
||||||
|
* method POST/DELETE, add/close a requestor to the accompanyingCourse
|
||||||
|
*
|
||||||
|
* @id integer - id of accompanyingCourse
|
||||||
|
* @payload object of type person|thirdparty
|
||||||
|
* @method string - POST or DELETE
|
||||||
|
*/
|
||||||
|
const postRequestor = (id, payload, method) => {
|
||||||
|
//console.log('payload', payload);
|
||||||
|
const body = (payload)? { type: payload.type, id: payload.id } : {};
|
||||||
|
console.log('body', body);
|
||||||
|
const url = `/api/1.0/person/accompanying-course/${id}/requestor.json`;
|
||||||
|
return fetch(url, {
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Endpoint v.2 chill_api_single_accompanying_course_resource,
|
||||||
|
* method POST/DELETE, add/remove a resource to the accompanyingCourse
|
||||||
|
*
|
||||||
|
* @id integer - id of accompanyingCourse
|
||||||
|
* @payload object of type person|thirdparty
|
||||||
|
* @method string - POST or DELETE
|
||||||
|
*/
|
||||||
|
const postResource = (id, payload, method) => {
|
||||||
|
//console.log('payload', payload);
|
||||||
|
const body = { type: "accompanying_period_resource" };
|
||||||
|
switch (method) {
|
||||||
|
case 'DELETE':
|
||||||
|
body['id'] = payload.id;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
body['resource'] = { type: payload.type, id: payload.id };
|
||||||
|
}
|
||||||
|
console.log('body', body);
|
||||||
|
const url = `/api/1.0/person/accompanying-course/${id}/resource.json`;
|
||||||
|
return fetch(url, {
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Endpoint to Add/remove SocialIssue
|
||||||
|
*/
|
||||||
|
const postSocialIssue = (id, body, method) => {
|
||||||
|
//console.log('api body and method', body, method);
|
||||||
|
const url = `/api/1.0/person/accompanying-course/${id}/socialissue.json`;
|
||||||
|
return fetch(url, {
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.ok) { return response.json(); }
|
if (response.ok) { return response.json(); }
|
||||||
@ -45,5 +167,11 @@ let postParticipation = (accompanying_period_id, person_id, method) => {
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
getAccompanyingCourse,
|
getAccompanyingCourse,
|
||||||
postParticipation
|
patchAccompanyingCourse,
|
||||||
|
confirmAccompanyingCourse,
|
||||||
|
getSocialIssues,
|
||||||
|
postParticipation,
|
||||||
|
postRequestor,
|
||||||
|
postResource,
|
||||||
|
postSocialIssue
|
||||||
};
|
};
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="vue-component">
|
|
||||||
<h3>{{ $t('course.title') }}</h3>
|
|
||||||
<dl>
|
|
||||||
<dt>{{ $t('course.id') }}</dt>
|
|
||||||
<dd>{{ accompanyingCourse.id }}</dd>
|
|
||||||
<dt>{{ $t('course.opening_date') }}</dt>
|
|
||||||
<dd>{{ $d(accompanyingCourse.openingDate.datetime, 'short') }}</dd>
|
|
||||||
<dt>{{ $t('course.closing_date') }}</dt>
|
|
||||||
<dd>{{ $d(accompanyingCourse.closingDate.datetime, 'short') }}</dd>
|
|
||||||
<dt>{{ $t('course.remark') }}</dt>
|
|
||||||
<dd>{{ accompanyingCourse.remark }}</dd>
|
|
||||||
<dt>{{ $t('course.closing_motive') }}</dt>
|
|
||||||
<dd>{{ accompanyingCourse.closing_motive }}</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'AccompanyingCourse',
|
|
||||||
computed: {
|
|
||||||
accompanyingCourse() {
|
|
||||||
return this.$store.state.accompanyingCourse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="vue-component" style="display: none;">
|
||||||
|
<dl>
|
||||||
|
<dt>{{ $t('course.id') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.id }}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl v-if="accompanyingCourse.closingDate">
|
||||||
|
<dt>{{ $t('course.closing_date') }}</dt>
|
||||||
|
<dd>{{ $d(accompanyingCourse.closingDate.datetime, 'short') }}</dd>
|
||||||
|
|
||||||
|
<dt>{{ $t('course.closing_motive') }}</dt>
|
||||||
|
<dd v-if="accompanyingCourse.closingMotive">{{ accompanyingCourse.closingMotive.name.fr }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<teleport to="#header-accompanying_course-name #banner-flags">
|
||||||
|
<toggle-flags></toggle-flags>
|
||||||
|
</teleport>
|
||||||
|
|
||||||
|
<teleport to="#header-accompanying_course-name #banner-status">
|
||||||
|
<div v-if="accompanyingCourse.step === 'DRAFT'">
|
||||||
|
<span class="badge badge-secondary">
|
||||||
|
{{ $t('course.step.draft') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div>
|
||||||
|
<span class="badge badge-primary">
|
||||||
|
{{ $t('course.step.active') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<i>{{ $t('course.open_at') }}{{ $d(accompanyingCourse.openingDate.datetime, 'text') }}</i>
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
<span v-if="accompanyingCourse.user">
|
||||||
|
{{ $t('course.by') }}<b>{{ accompanyingCourse.user.username }}</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</teleport>
|
||||||
|
|
||||||
|
<teleport to="#header-accompanying_course-details #banner-social-issues">
|
||||||
|
<div class="grid-12">
|
||||||
|
<social-issue
|
||||||
|
v-for="issue in accompanyingCourse.socialIssues"
|
||||||
|
v-bind:key="issue.id"
|
||||||
|
v-bind:issue="issue">
|
||||||
|
</social-issue>
|
||||||
|
</div>
|
||||||
|
</teleport>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ToggleFlags from './Banner/ToggleFlags';
|
||||||
|
import SocialIssue from './Banner/SocialIssue.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Banner',
|
||||||
|
components: {
|
||||||
|
ToggleFlags,
|
||||||
|
SocialIssue
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
accompanyingCourse() {
|
||||||
|
return this.$store.state.accompanyingCourse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div#banner-flags,
|
||||||
|
div#banner-status {
|
||||||
|
margin: 1.5em 0;
|
||||||
|
div {
|
||||||
|
text-align: right;
|
||||||
|
margin-bottom: 0.8em;
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div#banner-status {
|
||||||
|
span.badge {
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<span class="badge badge-secondary">{{ issue.text }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SocialIssues",
|
||||||
|
props: ['issue']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
span.badge {
|
||||||
|
font-size: 95%;
|
||||||
|
text-transform: capitalize !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a @click="toggleIntensity" class="flag-toggle">
|
||||||
|
{{ $t('course.occasional') }}
|
||||||
|
<i class="fa" :class="{ 'fa-toggle-on': isRegular, 'fa-toggle-on fa-flip-horizontal': !isRegular }"></i>
|
||||||
|
{{ $t('course.regular') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button class="badge badge-pill" :class="{ 'badge-primary': isEmergency, 'badge-secondary': !isEmergency }" @click="toggleEmergency">
|
||||||
|
{{ $t('course.emergency') }}
|
||||||
|
</button>
|
||||||
|
<button class="badge badge-pill" :class="{ 'badge-primary': isConfidential, 'badge-secondary': !isConfidential }" @click="toggleConfidential">
|
||||||
|
{{ $t('course.confidential') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
export default {
|
||||||
|
name: "ToggleFlags",
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
intensity: state => state.accompanyingCourse.intensity,
|
||||||
|
emergency: state => state.accompanyingCourse.emergency,
|
||||||
|
confidential: state => state.accompanyingCourse.confidential,
|
||||||
|
}),
|
||||||
|
isRegular() {
|
||||||
|
return (this.intensity === 'regular') ? true : false;
|
||||||
|
},
|
||||||
|
isEmergency() {
|
||||||
|
return (this.emergency) ? true : false;
|
||||||
|
},
|
||||||
|
isConfidential() {
|
||||||
|
return (this.confidential) ? true : false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleIntensity() {
|
||||||
|
let value;
|
||||||
|
switch (this.intensity) {
|
||||||
|
case "occasional":
|
||||||
|
value = "regular";
|
||||||
|
break;
|
||||||
|
case "regular":
|
||||||
|
value = "occasional";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//temporaire (modif backend)
|
||||||
|
value = "occasional";
|
||||||
|
}
|
||||||
|
this.$store.dispatch('toggleIntensity', value);
|
||||||
|
},
|
||||||
|
toggleEmergency() {
|
||||||
|
this.$store.dispatch('toggleEmergency', (!this.isEmergency));
|
||||||
|
},
|
||||||
|
toggleConfidential() {
|
||||||
|
this.$store.dispatch('toggleConfidential', (!this.isConfidential));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
a.flag-toggle {
|
||||||
|
color: white;
|
||||||
|
padding: 0 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
text-decoration: underline;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
margin: auto 0.4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.badge {
|
||||||
|
margin-left: 0.8em;
|
||||||
|
&.badge-secondary {
|
||||||
|
opacity: 0.5;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vue-component">
|
||||||
|
<h2><a name="section-60"></a>{{ $t('comment.title') }}</h2>
|
||||||
|
|
||||||
|
<!--div class="error flash_message" v-if="errors.length > 0">
|
||||||
|
{{ errors[0] }}
|
||||||
|
TODO fix errors flashbag for app component
|
||||||
|
</div-->
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<form @submit.prevent="submitform">
|
||||||
|
|
||||||
|
<label for="content">{{ $t('comment.label') }}</label>
|
||||||
|
|
||||||
|
<div v-if="initialComment">
|
||||||
|
{{ $t('comment.created_by', [
|
||||||
|
initialComment.creator.text,
|
||||||
|
$d(initialComment.createdAt.datetime, 'long')
|
||||||
|
]) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
name="content"
|
||||||
|
v-bind:placeholder="$t('comment.content')"
|
||||||
|
rows="8"
|
||||||
|
cols="80"
|
||||||
|
ckeditor="ckeditor"
|
||||||
|
v-model="content">
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button type="submit" class="sc-button bt-save">{{ $t('action.save') }}</button>
|
||||||
|
</li>
|
||||||
|
<li v-if="initialComment !== null">
|
||||||
|
<a class="sc-button bt-delete"
|
||||||
|
@click="removeComment">
|
||||||
|
{{ $t('action.delete') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Comment",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formdata: {
|
||||||
|
type: "accompanying_period_comment",
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
initialComment() {
|
||||||
|
return this.$store.state.accompanyingCourse.initialComment;
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
set(value) {
|
||||||
|
this.formdata.content = value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return (this.initialComment)? this.initialComment.content : null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
errors() {
|
||||||
|
return this.$store.state.errorMsg;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitform() {
|
||||||
|
console.log('submit');
|
||||||
|
this.$store.dispatch('postFirstComment', this.formdata);
|
||||||
|
},
|
||||||
|
removeComment() {
|
||||||
|
console.log('remove');
|
||||||
|
this.$store.dispatch('postFirstComment', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* TODO
|
||||||
|
* - [x] delete button in ul record_actions, but not in form
|
||||||
|
* - [ ] display updatedAt => initialComment fetch PATCH content changes MUST NOT change object id !!
|
||||||
|
* - [ ] ckeditor integration
|
||||||
|
*/
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
div.vue-component > div {
|
||||||
|
//margin: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vue-component">
|
||||||
|
<h2><a name="section-70"></a>
|
||||||
|
{{ $t('confirm.title') }}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ $t('confirm.text_draft') }}
|
||||||
|
<span class="badge badge-secondary">{{ $t('course.step.draft') }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ $t('confirm.text_active') }}
|
||||||
|
<span class="badge badge-primary">{{ $t('course.step.active') }}</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button class="sc-button bt-save" @click="modal.showModal = true">
|
||||||
|
{{ $t('confirm.ok') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<teleport to="body">
|
||||||
|
<modal v-if="modal.showModal" :modalDialogClass="modal.modalDialogClass" @close="modal.showModal = false">
|
||||||
|
<template v-slot:header>
|
||||||
|
<h2 class="modal-title">{{ $t('confirm.sure') }}</h2>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body>
|
||||||
|
<p>{{ $t('confirm.sure_description') }}</p>
|
||||||
|
</template>
|
||||||
|
<template v-slot:footer>
|
||||||
|
<button class="sc-button red" @click="confirmCourse">
|
||||||
|
{{ $t('confirm.ok') }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</modal>
|
||||||
|
</teleport>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Confirm",
|
||||||
|
components: {
|
||||||
|
Modal,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
modal: {
|
||||||
|
showModal: false,
|
||||||
|
modalDialogClass: "modal-dialog-centered modal-md"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
accompanyingCourse() {
|
||||||
|
return this.$store.state.accompanyingCourse
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmCourse() {
|
||||||
|
console.log('@@ CLICK confirmCourse');
|
||||||
|
this.$store.dispatch('confirmAccompanyingCourse');
|
||||||
|
console.log('confirm last');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
div.vue-component > div {
|
||||||
|
//margin: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,8 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vue-component">
|
<div class="vue-component">
|
||||||
<h3>{{ $t('persons_associated.title')}}</h3>
|
<h2><a name="section-10"></a>{{ $t('persons_associated.title')}}</h2>
|
||||||
<label>{{ $tc('persons_associated.counter', counter) }}</label>
|
|
||||||
<table class="rounded">
|
<div>
|
||||||
|
<label>{{ $tc('persons_associated.counter', counter) }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="rounded" v-if="participations.length > 0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="chill-orange">{{ $t('persons_associated.firstname') }}</th>
|
<th class="chill-orange">{{ $t('persons_associated.firstname') }}</th>
|
||||||
@ -22,20 +26,24 @@
|
|||||||
</person-item>
|
</person-item>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<add-persons></add-persons>
|
|
||||||
<ul class="record_actions">
|
<div>
|
||||||
<!--li>
|
<add-persons
|
||||||
<button class="sc-button orange" @click="savePersons">
|
buttonTitle="persons_associated.add_persons"
|
||||||
{{ $t('action.save') }}
|
modalTitle="add_persons.title"
|
||||||
</button>
|
v-bind:key="addPersons.key"
|
||||||
</li-->
|
v-bind:options="addPersons.options"
|
||||||
</ul>
|
@addNewPersons="addNewPersons"
|
||||||
|
ref="addPersons"> <!-- to cast child method -->
|
||||||
|
</add-persons>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex';
|
import { mapState } from 'vuex';
|
||||||
import PersonItem from "./PersonItem.vue"
|
import PersonItem from "./PersonsAssociated/PersonItem.vue"
|
||||||
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'
|
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -44,23 +52,40 @@ export default {
|
|||||||
PersonItem,
|
PersonItem,
|
||||||
AddPersons
|
AddPersons
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
addPersons: {
|
||||||
|
key: 'persons_associated',
|
||||||
|
options: {
|
||||||
|
type: ['person'],
|
||||||
|
priority: null,
|
||||||
|
uniq: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: mapState({
|
computed: mapState({
|
||||||
participations: state => state.accompanyingCourse.participations,
|
participations: state => state.accompanyingCourse.participations,
|
||||||
counter: state => state.accompanyingCourse.participations.length
|
counter: state => state.accompanyingCourse.participations.length
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
removeParticipation(item) {
|
removeParticipation(item) {
|
||||||
this.$store.dispatch('removeParticipation', item)
|
console.log('@@ CLICK remove participation: item', item);
|
||||||
|
this.$store.dispatch('removeParticipation', item);
|
||||||
},
|
},
|
||||||
closeParticipation(item) {
|
closeParticipation(item) {
|
||||||
console.log('@@ CLICK close participation: item', item);
|
console.log('@@ CLICK close participation: item', item);
|
||||||
this.$store.dispatch('closeParticipation', item)
|
this.$store.dispatch('closeParticipation', item);
|
||||||
},
|
},
|
||||||
/*
|
addNewPersons({ selected, modal }) {
|
||||||
savePersons() {
|
console.log('@@@ CLICK button addNewPersons', selected);
|
||||||
console.log('[wip] saving persons');
|
selected.forEach(function(item) {
|
||||||
|
this.$store.dispatch('addParticipation', item);
|
||||||
|
}, this
|
||||||
|
);
|
||||||
|
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||||
|
modal.showModal = false;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -29,10 +29,12 @@
|
|||||||
</button>
|
</button>
|
||||||
</li-->
|
</li-->
|
||||||
<li>
|
<li>
|
||||||
<button class="sc-button bt-remove"
|
<button v-if="!participation.endDate"
|
||||||
|
class="sc-button bt-remove"
|
||||||
:title="$t('action.remove')"
|
:title="$t('action.remove')"
|
||||||
@click.prevent="$emit('close', participation)">
|
@click.prevent="$emit('close', participation)">
|
||||||
</button>
|
</button>
|
||||||
|
<button v-else class="sc-button bt-remove disabled"></button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vue-component">
|
||||||
|
<h2><a name="section-40"></a>{{ $t('referrer.title') }}</h2>
|
||||||
|
|
||||||
|
<div class="my-4">
|
||||||
|
<label for="" class="">
|
||||||
|
{{ $t('referrer.label') }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<VueMultiselect
|
||||||
|
track-by="id"
|
||||||
|
label="text"
|
||||||
|
:multiple="false"
|
||||||
|
:searchable="true"
|
||||||
|
:placeholder="$t('referrer.placeholder')"
|
||||||
|
@update:model-value="updateReferrer"
|
||||||
|
:model-value="value"
|
||||||
|
:options="options">
|
||||||
|
</VueMultiselect>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
class="sc-button bt-create"
|
||||||
|
type="button"
|
||||||
|
name="button"
|
||||||
|
@click="assignMe">
|
||||||
|
{{ $t('referrer.assign_me') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import VueMultiselect from 'vue-multiselect';
|
||||||
|
//import { getUsers } from '../api';
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Referrer",
|
||||||
|
components: { VueMultiselect },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
value: state => state.accompanyingCourse.user,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getOptions();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getOptions() {
|
||||||
|
//getUsers().then(response => new Promise((resolve, reject) => {
|
||||||
|
// console.log(response);
|
||||||
|
// resolve();
|
||||||
|
//})).catch(er => this.$store.commit('catchError'), error));
|
||||||
|
},
|
||||||
|
updateReferrer(value) {
|
||||||
|
//this.$store.dispatch('updateReferrer', this.transformValue(value));
|
||||||
|
},
|
||||||
|
transformValue(value) {
|
||||||
|
let payload = value;
|
||||||
|
return { payload, body, method };
|
||||||
|
},
|
||||||
|
assignMe() {
|
||||||
|
console.log('assign me');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,85 +1,121 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vue-component">
|
<div class="vue-component">
|
||||||
<h3>{{ $t('requestor.title') }}</h3>
|
|
||||||
{{ accompanyingCourse.id }}
|
|
||||||
{{ accompanyingCourse.remark }}<br><br>
|
|
||||||
|
|
||||||
<!-- TESTS AREA -->
|
<h2><a name="section-20"></a>{{ $t('requestor.title') }}</h2>
|
||||||
<ul class="record_actions">
|
|
||||||
<li>
|
|
||||||
<button class="sc-button bt-create" @click="modal1.showModal = true">
|
|
||||||
{{ $t('action.show_modal') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button class="sc-button bt-create" @click="modal2.showModal = true">
|
|
||||||
Ouvrir une seconde modale
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<teleport to="body">
|
<div v-if="accompanyingCourse.requestor">
|
||||||
<modal v-if="modal1.showModal" :modalDialogClass="modal1.modalDialogClass" @close="modal1.showModal = false">
|
<label>
|
||||||
<template v-slot:header>
|
<input type="checkbox" v-model="isAnonymous" :value="value" />
|
||||||
<h3 class="modal-title">Le titre de ma modale</h3>
|
{{ $t('requestor.is_anonymous') }}
|
||||||
</template>
|
</label>
|
||||||
<template v-slot:body>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit. </p>
|
|
||||||
<p>Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.</p>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit. </p>
|
|
||||||
<p>Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.</p>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit. </p>
|
|
||||||
<p>Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.</p>
|
|
||||||
</template>
|
|
||||||
<template v-slot:footer>
|
|
||||||
<button class="sc-button green" @click="modal1.showModal = false; modal2.showModal = true">
|
|
||||||
{{ $t('action.next')}}</button>
|
|
||||||
</template>
|
|
||||||
</modal>
|
|
||||||
</teleport>
|
|
||||||
|
|
||||||
<teleport to="body">
|
<dt>{{ $t('requestor.type') }}</dt>
|
||||||
<modal v-if="modal2.showModal" :modalDialogClass="modal2.modalDialogClass" @close="modal2.showModal = false">
|
<dd>{{ accompanyingCourse.requestor.type }}</dd>
|
||||||
<template v-slot:header>
|
<dt>{{ $t('requestor.text') }}</dt>
|
||||||
<h3 class="modal-title">Une autre modale</h3>
|
<dd>{{ accompanyingCourse.requestor.text }}</dd>
|
||||||
</template>
|
<dt>{{ $t('requestor.is_anonymous') }}</dt>
|
||||||
<template v-slot:body>
|
<dd>{{ accompanyingCourse.requestorAnonymous }}</dd>
|
||||||
<p>modal 2</p>
|
|
||||||
</template>
|
<div v-if="accompanyingCourse.requestor.type === 'person'">
|
||||||
<template v-slot:footer>
|
<dt>{{ $t('requestor.person_id') }}</dt>
|
||||||
<button class="sc-button green" @click="modal2.showModal = false">
|
<dd>{{ accompanyingCourse.requestor.person_id }}</dd>
|
||||||
{{ $t('action.save')}}</button>
|
<dt>{{ $t('requestor.birthdate') }}</dt>
|
||||||
</template>
|
<dd>{{ $d(accompanyingCourse.requestor.birthdate.datetime, 'short') }}</dd>
|
||||||
</modal>
|
<dt>{{ $t('requestor.center') }}</dt>
|
||||||
</teleport>
|
<dd>{{ accompanyingCourse.requestor.center.name }}</dd>
|
||||||
<!-- END TESTS -->
|
<dt>{{ $t('requestor.firstName') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.requestor.firstName }}</dd>
|
||||||
|
<dt>{{ $t('requestor.lastName') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.requestor.lastName }}</dd>
|
||||||
|
<dt>{{ $t('requestor.phonenumber') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.requestor.phonenumber }}</dd>
|
||||||
|
<dt>{{ $t('requestor.mobilenumber') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.requestor.mobilenumber }}</dd>
|
||||||
|
<dt>{{ $t('requestor.altNames') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.requestor.altNames }}</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="accompanyingCourse.requestor.type === 'thirdparty'">
|
||||||
|
<dt>{{ $t('requestor.person_id') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.requestor.thirdparty_id }}</dd>
|
||||||
|
<dt>{{ $t('requestor.address') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.requestor.address.text }}</dd>
|
||||||
|
<dt>{{ $t('requestor.location') }}</dt>
|
||||||
|
<dd>{{ accompanyingCourse.requestor.address.postcode.name }}</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button class="sc-button bt-remove"
|
||||||
|
:title="$t('action.remove')"
|
||||||
|
@click="removeRequestor">
|
||||||
|
{{ $t('action.remove') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<label>{{ $t('requestor.counter') }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<add-persons v-if="accompanyingCourse.requestor === null"
|
||||||
|
buttonTitle="requestor.add_requestor"
|
||||||
|
modalTitle="requestor.add_requestor"
|
||||||
|
v-bind:key="addPersons.key"
|
||||||
|
v-bind:options="addPersons.options"
|
||||||
|
@addNewPersons="addNewPersons"
|
||||||
|
ref="addPersons"> <!-- to cast child method -->
|
||||||
|
</add-persons>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Modal from 'ChillMainAssets/vuejs/_components/Modal'
|
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Requestor',
|
name: 'Requestor',
|
||||||
components: {
|
components: {
|
||||||
Modal,
|
AddPersons,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
modal1: {
|
addPersons: {
|
||||||
showModal: false,
|
key: 'requestor',
|
||||||
modalDialogClass: "modal-dialog-scrollable modal-xl" // modal-lg modal-md modal-sm
|
options: {
|
||||||
},
|
type: ['person', 'thirdparty'],
|
||||||
modal2: {
|
priority: null,
|
||||||
showModal: false,
|
uniq: true,
|
||||||
modalDialogClass: "modal-dialog-centered modal-sm" // modal-lg modal-md modal-sm
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
accompanyingCourse() {
|
accompanyingCourse() {
|
||||||
return this.$store.state.accompanyingCourse
|
return this.$store.state.accompanyingCourse
|
||||||
|
},
|
||||||
|
isAnonymous: {
|
||||||
|
set(value) {
|
||||||
|
console.log('requestorIsAnonymous value',value);
|
||||||
|
this.$store.dispatch('requestorIsAnonymous', value);
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.$store.state.accompanyingCourse.requestorAnonymous;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
removeRequestor() {
|
||||||
|
console.log('@@ CLICK remove requestor: item');
|
||||||
|
this.$store.dispatch('removeRequestor');
|
||||||
|
},
|
||||||
|
addNewPersons({ selected, modal }) {
|
||||||
|
console.log('@@@ CLICK button addNewPersons', selected);
|
||||||
|
this.$store.dispatch('addRequestor', selected.shift());
|
||||||
|
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||||
|
modal.showModal = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vue-component">
|
||||||
|
|
||||||
|
<h2><a name="section-50"></a>{{ $t('resources.title')}}</h2>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label>{{ $tc('resources.counter', counter) }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="rounded" v-if="resources.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="chill-orange">{{ $t('resources.text') }}</th>
|
||||||
|
<th class="chill-orange">{{ $t('resources.description') }}</th>
|
||||||
|
<th class="chill-orange">{{ $t('action.actions') }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<resource-item
|
||||||
|
v-for="resource in resources"
|
||||||
|
v-bind:resource="resource"
|
||||||
|
v-bind:key="resource.id"
|
||||||
|
@remove="removeResource">
|
||||||
|
</resource-item>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<add-persons
|
||||||
|
buttonTitle="resources.add_resources"
|
||||||
|
modalTitle="resources.add_resources"
|
||||||
|
v-bind:key="addPersons.key"
|
||||||
|
v-bind:options="addPersons.options"
|
||||||
|
@addNewPersons="addNewPersons"
|
||||||
|
ref="addPersons"> <!-- to cast child method -->
|
||||||
|
</add-persons>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
||||||
|
import ResourceItem from './Resources/ResourceItem.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Resources',
|
||||||
|
components: {
|
||||||
|
AddPersons,
|
||||||
|
ResourceItem
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
addPersons: {
|
||||||
|
key: 'resources',
|
||||||
|
options: {
|
||||||
|
type: ['person', 'thirdparty'],
|
||||||
|
priority: null,
|
||||||
|
uniq: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: mapState({
|
||||||
|
resources: state => state.accompanyingCourse.resources,
|
||||||
|
counter: state => state.accompanyingCourse.resources.length
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
removeResource(item) {
|
||||||
|
console.log('@@ CLICK remove resource: item', item);
|
||||||
|
this.$store.dispatch('removeResource', item);
|
||||||
|
},
|
||||||
|
addNewPersons({ selected, modal }) {
|
||||||
|
console.log('@@@ CLICK button addNewPersons', selected);
|
||||||
|
selected.forEach(function(item) {
|
||||||
|
this.$store.dispatch('addResource', item);
|
||||||
|
}, this
|
||||||
|
);
|
||||||
|
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||||
|
modal.showModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span class="badge badge-pill badge-secondary"
|
||||||
|
v-bind:title="resource.resource.id">
|
||||||
|
<span v-if="resource.resource.type === 'person'" >{{ $t('item.type_person') }}</span>
|
||||||
|
<span v-if="resource.resource.type === 'thirdparty'" >{{ $t('item.type_thirdparty') }}</span>
|
||||||
|
</span>
|
||||||
|
{{ resource.resource.text }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td v-if="resource.resource.type === 'person'">
|
||||||
|
{{ $tc('person.born') }}{{ $d(resource.resource.birthdate.datetime, 'short') }}
|
||||||
|
</td>
|
||||||
|
<td v-else-if="resource.resource.type === 'thirdparty'">
|
||||||
|
{{ resource.resource.address.text }}<br>
|
||||||
|
{{ resource.resource.address.postcode.name }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<a class="sc-button bt-show" target="_blank"
|
||||||
|
:href="url.show"
|
||||||
|
:title="$t('action.show')">
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="sc-button bt-update" target="_blank"
|
||||||
|
:href="url.edit"
|
||||||
|
:title="$t('action.edit')">
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
class="sc-button bt-remove"
|
||||||
|
:title="$t('action.remove')"
|
||||||
|
@click.prevent="$emit('remove', resource)">
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ResourceItem',
|
||||||
|
props: ['resource'],
|
||||||
|
emits: ['remove'],
|
||||||
|
computed: {
|
||||||
|
type() {
|
||||||
|
return this.resource.resource.type;
|
||||||
|
},
|
||||||
|
url() {
|
||||||
|
return (this.type === 'person') ? {
|
||||||
|
show: `/fr/person/${this.resource.resource.id}/general`,
|
||||||
|
edit: `/fr/person/${this.resource.resource.id}/general/edit`
|
||||||
|
} : {
|
||||||
|
show: `/fr/thirdparty/thirdparty/${this.resource.resource.id}/show`,
|
||||||
|
edit: `/fr/thirdparty/thirdparty/${this.resource.resource.id}/update`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vue-component">
|
||||||
|
<h2><a name="section-30"></a>{{ $t('social_issue.title') }}</h2>
|
||||||
|
|
||||||
|
<div class="my-4">
|
||||||
|
<!--label for="field">{{ $t('social_issue.label') }}</label
|
||||||
|
-->
|
||||||
|
<VueMultiselect
|
||||||
|
name="field"
|
||||||
|
:close-on-select="false"
|
||||||
|
:allow-empty="true"
|
||||||
|
:show-labels="false"
|
||||||
|
track-by="id"
|
||||||
|
label="text"
|
||||||
|
:multiple="true"
|
||||||
|
:searchable="false"
|
||||||
|
:placeholder="$t('social_issue.label')"
|
||||||
|
@update:model-value="updateSocialIssues"
|
||||||
|
:model-value="value"
|
||||||
|
:options="options">
|
||||||
|
</VueMultiselect>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import VueMultiselect from 'vue-multiselect';
|
||||||
|
import { getSocialIssues } from '../api';
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "SocialIssue",
|
||||||
|
components: { VueMultiselect },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
value: state => state.accompanyingCourse.socialIssues,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getOptions();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getOptions() {
|
||||||
|
getSocialIssues().then(response => new Promise((resolve, reject) => {
|
||||||
|
//console.log('get socialIssues', response.results);
|
||||||
|
this.options = response.results;
|
||||||
|
resolve();
|
||||||
|
})).catch(error => this.$store.commit('catchError', error));
|
||||||
|
},
|
||||||
|
updateSocialIssues(value) {
|
||||||
|
this.$store.dispatch('updateSocialIssues', this.transformValue(value));
|
||||||
|
},
|
||||||
|
transformValue(updated) {
|
||||||
|
let stored = this.value;
|
||||||
|
let added = updated.filter(x => stored.indexOf(x) === -1).shift();
|
||||||
|
let removed = stored.filter(x => updated.indexOf(x) === -1).shift();
|
||||||
|
let method = (typeof removed === 'undefined') ? 'POST' : 'DELETE';
|
||||||
|
let changed = (typeof removed === 'undefined') ? added : removed;
|
||||||
|
let body = { type: "social_issue", id: changed.id };
|
||||||
|
//console.log('body', body);
|
||||||
|
//console.log('@@@', method, changed.text);
|
||||||
|
let payload = updated;
|
||||||
|
return { payload, body, method };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||||
|
<style lang="scss">
|
||||||
|
span.multiselect__tag {
|
||||||
|
background: #e2793d;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,191 @@
|
|||||||
|
<template>
|
||||||
|
<teleport to="#content_conainter .container.content .container">
|
||||||
|
<div id="navmap">
|
||||||
|
<nav>
|
||||||
|
<a class="top" href="#top">
|
||||||
|
<i class="fa fa-fw fa-square"></i>
|
||||||
|
<span>{{ $t('nav.top') }}</span>
|
||||||
|
</a>
|
||||||
|
<item
|
||||||
|
v-for="item of items"
|
||||||
|
:key="item.key"
|
||||||
|
:item="item"
|
||||||
|
:step="step">
|
||||||
|
</item>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Item from './StickyNav/Item.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "StickyNav",
|
||||||
|
components: {
|
||||||
|
Item
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
header: document.querySelector("header.navigation.container"),
|
||||||
|
bannerName: document.querySelector("#header-accompanying_course-name"),
|
||||||
|
bannerDetails: document.querySelector("#header-accompanying_course-details"),
|
||||||
|
container: null,
|
||||||
|
heightSum: null,
|
||||||
|
stickyNav: null,
|
||||||
|
limit: 25,
|
||||||
|
anchors: null,
|
||||||
|
items: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
accompanyingCourse() {
|
||||||
|
return this.$store.state.accompanyingCourse;
|
||||||
|
},
|
||||||
|
step() {
|
||||||
|
return this.accompanyingCourse.step;
|
||||||
|
},
|
||||||
|
top() {
|
||||||
|
return parseInt(window.getComputedStyle(this.stickyNav).getPropertyValue('top').slice(0, -2));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.ready();
|
||||||
|
window.addEventListener('scroll', this.handleScroll);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
window.removeEventListener('scroll', this.handleScroll);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
ready() {
|
||||||
|
|
||||||
|
// load datas DOM when mounted ready
|
||||||
|
this.container = document.querySelector("#content_conainter .container.content .container");
|
||||||
|
this.stickyNav = document.querySelector('#navmap');
|
||||||
|
this.anchors = document.querySelectorAll("h2 a[name^='section']");
|
||||||
|
this.initItemsMap();
|
||||||
|
|
||||||
|
// TODO resizeObserver not supports IE !
|
||||||
|
// Listen when elements change size, then recalculate heightSum and initItemsMap
|
||||||
|
const resizeObserver = new ResizeObserver(entries => {
|
||||||
|
this.refreshPos();
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserver.observe(this.header);
|
||||||
|
resizeObserver.observe(this.bannerName);
|
||||||
|
resizeObserver.observe(this.bannerDetails);
|
||||||
|
resizeObserver.observe(this.container);
|
||||||
|
},
|
||||||
|
initItemsMap() {
|
||||||
|
|
||||||
|
this.anchors.forEach(anchor => {
|
||||||
|
this.items.push({
|
||||||
|
pos: null,
|
||||||
|
active: false,
|
||||||
|
key: parseInt(anchor.name.slice(8).slice(0, -1)),
|
||||||
|
name: '#' + anchor.name
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
refreshPos() {
|
||||||
|
|
||||||
|
//console.log('refreshPos');
|
||||||
|
this.heightSum = this.header.offsetHeight + this.bannerName.offsetHeight + this.bannerDetails.offsetHeight;
|
||||||
|
|
||||||
|
this.anchors.forEach((anchor, i) => {
|
||||||
|
this.items[i].pos = this.findPos(anchor)['y'];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
findPos(element) {
|
||||||
|
|
||||||
|
let posX = 0, posY = 0;
|
||||||
|
do {
|
||||||
|
posX += element.offsetLeft;
|
||||||
|
posY += element.offsetTop;
|
||||||
|
element = element.offsetParent;
|
||||||
|
}
|
||||||
|
while( element != null );
|
||||||
|
|
||||||
|
let pos = [];
|
||||||
|
pos['x'] = posX;
|
||||||
|
pos['y'] = posY;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
},
|
||||||
|
handleScroll(event) {
|
||||||
|
|
||||||
|
let pos = this.findPos(this.stickyNav);
|
||||||
|
let top = this.heightSum + this.top - window.scrollY;
|
||||||
|
//console.log(window.scrollY);
|
||||||
|
|
||||||
|
if (top > this.limit) {
|
||||||
|
this.stickyNav.style.position = 'absolute';
|
||||||
|
this.stickyNav.style.left = '-60px';
|
||||||
|
} else {
|
||||||
|
this.stickyNav.style.position = 'fixed';
|
||||||
|
this.stickyNav.style.left = pos['x'] + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.switchActive();
|
||||||
|
},
|
||||||
|
switchActive() {
|
||||||
|
|
||||||
|
this.items.forEach((item, i) => {
|
||||||
|
let next = (this.items[i+1]) ? this.items[i+1].pos : '100000';
|
||||||
|
item.active =
|
||||||
|
(window.scrollY >= item.pos & window.scrollY < next) ? true : false;
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
// last item never switch active because scroll reach bottom of page
|
||||||
|
if (document.body.scrollHeight == window.scrollY + window.innerHeight) {
|
||||||
|
this.items[this.items.length-1].active = true;
|
||||||
|
this.items[this.items.length-2].active = false;
|
||||||
|
} else {
|
||||||
|
this.items[this.items.length-1].active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div#navmap {
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
left: -60px; //-10%;
|
||||||
|
nav {
|
||||||
|
font-size: small;
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: -3px;
|
||||||
|
color: #71859669;
|
||||||
|
&.top {
|
||||||
|
color: #718596;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
span {
|
||||||
|
display: inline;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: #718596b5;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
color: #e2793d; //orange
|
||||||
|
//color: #eec84a; //jaune
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
div.sticky-section {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<a
|
||||||
|
v-if="item.key <= 5"
|
||||||
|
:href="item.name"
|
||||||
|
:class="{ 'active': isActive }"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-square"></i>
|
||||||
|
<span>{{ item.key }}</span>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
v-else-if="step === 'DRAFT'"
|
||||||
|
:href="item.name"
|
||||||
|
:class="{ 'active': isActive }"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-square"></i>
|
||||||
|
<span>{{ item.key }}</span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Item",
|
||||||
|
props: ['item', 'step'],
|
||||||
|
computed: {
|
||||||
|
isActive() {
|
||||||
|
return this.item.active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vue-component">
|
||||||
|
<h2></a>Tests</h2>
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button class="sc-button bt-create" @click="modal1.showModal = true">
|
||||||
|
{{ $t('action.show_modal') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button class="sc-button bt-create" @click="modal2.showModal = true">
|
||||||
|
Ouvrir une seconde modale
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<teleport to="body">
|
||||||
|
<modal v-if="modal1.showModal" :modalDialogClass="modal1.modalDialogClass" @close="modal1.showModal = false">
|
||||||
|
<template v-slot:header>
|
||||||
|
<h2 class="modal-title">Le titre de ma modale</h2>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit. </p>
|
||||||
|
<p>Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.</p>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit. </p>
|
||||||
|
<p>Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.</p>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit. </p>
|
||||||
|
<p>Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.</p>
|
||||||
|
</template>
|
||||||
|
<template v-slot:footer>
|
||||||
|
<button class="sc-button green" @click="modal1.showModal = false; modal2.showModal = true">
|
||||||
|
{{ $t('action.next')}}</button>
|
||||||
|
</template>
|
||||||
|
</modal>
|
||||||
|
</teleport>
|
||||||
|
|
||||||
|
<teleport to="body">
|
||||||
|
<modal v-if="modal2.showModal" :modalDialogClass="modal2.modalDialogClass" @close="modal2.showModal = false">
|
||||||
|
<template v-slot:header>
|
||||||
|
<h2 class="modal-title">Une autre modale</h2>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body>
|
||||||
|
<p>modal 2</p>
|
||||||
|
</template>
|
||||||
|
<template v-slot:footer>
|
||||||
|
<button class="sc-button green" @click="modal2.showModal = false">
|
||||||
|
{{ $t('action.save')}}</button>
|
||||||
|
</template>
|
||||||
|
</modal>
|
||||||
|
</teleport>
|
||||||
|
<!-- END Modal -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Modal from 'ChillMainAssets/vuejs/_components/Modal'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Test',
|
||||||
|
components: {
|
||||||
|
Modal,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
modal1: {
|
||||||
|
showModal: false,
|
||||||
|
modalDialogClass: "modal-dialog-scrollable modal-xl" // modal-lg modal-md modal-sm
|
||||||
|
},
|
||||||
|
modal2: {
|
||||||
|
showModal: false,
|
||||||
|
modalDialogClass: "modal-dialog-centered modal-sm" // modal-lg modal-md modal-sm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user