Compare commits

..

3 Commits

Author SHA1 Message Date
3e7c79d6ce test app async working 2021-04-23 12:10:37 +02:00
bc1f624e14 fix dummy controller 2021-04-23 10:49:15 +02:00
f73d3bf320 test app 2021-04-22 20:42:48 +02:00
164 changed files with 1395 additions and 7443 deletions

64
.env
View File

@@ -1,64 +0,0 @@
##
## Manually dump .env files in .env.local.php with
## `$ composer symfony:dump-env prod`
##
## Project environment
APP_ENV=dev
## Enable debug
APP_DEBUG=true
## Locale
LOCALE=fr
## Framework secret
APP_SECRET=ThisTokenIsNotSoSecretChangeIt
## Symfony/swiftmailer
MAILER_TRANSPORT=smtp
MAILER_HOST=smtp
MAILER_PORT=1025
MAILER_CRYPT=
MAILER_AUTH=
MAILER_USER=
MAILER_PASSWORD=
MAILER_URL=${MAILER_TRANSPORT}://${MAILER_HOST}:${MAILER_PORT}?encryption=${MAILER_CRYPT}&auth_mode=${MAILER_AUTH}&username=${MAILER_USER}&password=${MAILER_PASSWORD}
## Notifications
NOTIFICATION_HOST=localhost:8001
NOTIFICATION_FROM_EMAIL=admin@chill.social
NOTIFICATION_FROM_NAME=Chill
## Gelf
GELF_HOST=gelf
GELF_PORT=12201
## OVH OpenStack Storage User/Role
OS_USERNAME=
OS_PASSWORD=
OS_TENANT_ID=
OS_REGION_NAME=GRA
OS_AUTH_URL=https://auth.cloud.ovh.net/v2.0/
## OVH OpenStack Storage Container
ASYNC_UPLOAD_TEMP_URL_KEY=
ASYNC_UPLOAD_TEMP_URL_BASE_PATH=
ASYNC_UPLOAD_TEMP_URL_CONTAINER=
## Redis Cache
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
## Twilio
TWILIO_SID=~
TWILIO_SECRET=~
## DOCKER IMAGES REGISTRY
#IMAGE_PHP=
#IMAGE_NGINX=
## DOCKER IMAGES VERSION
#VERSION=test
VERSION=prod

View File

@@ -1,6 +0,0 @@
# variables for .env environement
# those variables suits for gitlab-ci
# Run tests from root to adapt your own environment
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres?serverVersion=12&charset=utf8

18
.gitignore vendored
View File

@@ -1,22 +1,4 @@
.composer/*
composer.phar
composer.lock
docs/build/
.php_cs.cache
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
/bin/
###< symfony/framework-bundle ###
###> phpunit/phpunit ###
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###

View File

@@ -1,41 +0,0 @@
---
image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4
# Select what we should cache between builds
cache:
paths:
- tests/app/vendor/
before_script:
# add extensions to postgres
- 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
- curl -sS https://getcomposer.org/installer | php
- php composer.phar install
- php tests/app/bin/console doctrine:migrations:migrate -n
- php tests/app/bin/console doctrine:fixtures:load -n
# 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.
services:
- name: postgres:12
alias: db
- name: redis
alias: redis
# Set any variables we need
variables:
# Configure postgres environment variables (https://hub.docker.com/r/_/postgres/)
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
# fetch the chill-app using git submodules
GIT_SUBMODULE_STRATEGY: recursive
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_URL: redis://redis:6379
# Run our tests
test:
script:
- bin/phpunit --colors=never

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "_exts/sphinx-php"]
path = _exts/sphinx-php
url = https://github.com/fabpot/sphinx-php.git
[submodule "tests/app"]
path = tests/app
url = https://gitlab.com/Chill-projet/chill-app.git

View File

@@ -1,9 +0,0 @@
# Chill framework
Documentation of the Chill software.
The online documentation can be found at http://docs.chill.social
See the [`docs`][1] directory for more.
[1]: docs/README.md

View File

@@ -19,11 +19,6 @@
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle"
}
},
"autoload-dev": {
"psr-4": {
"App\\": "tests/app/src/"
}
},
"require": {
"champs-libres/async-uploader-bundle": "dev-sf4",
"graylog2/gelf-php": "^1.5",
@@ -72,17 +67,6 @@
"symfony/stopwatch": "^5.1",
"symfony/web-profiler-bundle": "^5.0",
"symfony/var-dumper": "4.*",
"symfony/debug-bundle": "^5.1",
"symfony/phpunit-bridge": "^5.2"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
}
},
"config": {
"vendor-dir": "tests/app/vendor",
"bin-dir": "bin"
"symfony/debug-bundle": "^5.1"
}
}

View File

@@ -10,19 +10,10 @@ Compilation into HTML
To compile this documentation :
1. Install [sphinx-doc](http://sphinx-doc.org)
``` bash
$ virtualenv .venv # creation of the virtual env (only the first time)
$ source .venv/bin/activate # activate the virtual env
(.venv) $ pip install -r requirements.txt
```
1. Install [sphinx-doc](http://sphinx-doc.org) (eg. pip install sphinx & pip install sphinx_rtd_theme)
2. Install submodules : $ git submodule update --init;
3. run `make html` from the root directory
4. The base file is located on build/html/index.html
``` bash
$ cd build/html
$ python -m http.server 8888 # will serve the site on the port 8888
```
Contribute
===========

View File

@@ -1,453 +0,0 @@
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
.. _api:
API
###
Chill provides a basic framework to build REST api.
Configure a route
=================
Follow those steps to build a REST api:
1. Create your model;
2. Configure the API;
You can also:
* hook into the controller to customize some steps;
* add more route and steps
.. note::
Useful links:
* `How to use annotation to configure serialization <https://symfony.com/doc/current/serializer.html>`_
* `How to create your custom normalizer <https://symfony.com/doc/current/serializer/custom_normalizer.html>`_
Auto-loading the routes
***********************
Ensure that those lines are present in your file `app/config/routing.yml`:
.. code-block:: yaml
chill_cruds:
resource: 'chill_main_crud_route_loader:load'
type: service
Create your model
*****************
Create your model on the usual way:
.. code-block:: php
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\OriginRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=OriginRepository::class)
* @ORM\Table(name="chill_person_accompanying_period_origin")
*/
class Origin
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="json")
*/
private $label;
/**
* @ORM\Column(type="date_immutable", nullable=true)
*/
private $noActiveAfter;
// .. getters and setters
}
Configure api
*************
Configure the api using Yaml (see the full configuration: :ref:`api_full_configuration`):
.. code-block:: yaml
# config/packages/chill_main.yaml
chill_main:
apis:
accompanying_period_origin:
base_path: '/api/1.0/person/accompanying-period/origin'
class: 'Chill\PersonBundle\Entity\AccompanyingPeriod\Origin'
name: accompanying_period_origin
base_role: 'ROLE_USER'
actions:
_index:
methods:
GET: true
HEAD: true
_entity:
methods:
GET: true
HEAD: true
.. note::
If you are working on a shared bundle (aka "The chill bundles"), you should define your configuration inside the class :code:`ChillXXXXBundleExtension`, using the "prependConfig" feature:
.. code-block:: php
namespace Chill\PersonBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Class ChillPersonExtension
* Loads and manages your bundle configuration
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
* @package Chill\PersonBundle\DependencyInjection
*/
class ChillPersonExtension extends Extension implements PrependExtensionInterface
{
public function prepend(ContainerBuilder $container)
{
$this->prependCruds($container);
}
/**
* @param ContainerBuilder $container
*/
protected function prependCruds(ContainerBuilder $container)
{
$container->prependExtensionConfig('chill_main', [
'apis' => [
[
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod\Origin::class,
'name' => 'accompanying_period_origin',
'base_path' => '/api/1.0/person/accompanying-period/origin',
'controller' => \Chill\PersonBundle\Controller\OpeningApiController::class,
'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
]
],
]
]
]
]);
}
}
The :code:`_index` and :code:`_entity` action
=============================================
The :code:`_index` and :code:`_entity` action are default actions:
* they will call a specific method in the default controller;
* they will generate defined routes:
Index:
Name: :code:`chill_api_single_accompanying_period_origin__index`
Path: :code:`/api/1.0/person/accompanying-period/origin.{_format}`
Entity:
Name: :code:`chill_api_single_accompanying_period_origin__entity`
Path: :code:`/api/1.0/person/accompanying-period/origin/{id}.{_format}`
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.
For index action, the role will be called with :code:`NULL` as :code:`$subject`. The retrieved entity will be the subject for single queries.
You can also define a role for each method. In this case, this role is used for the given method, and, if any, the base role is taken into account.
.. code-block:: yaml
# config/packages/chill_main.yaml
chill_main:
apis:
accompanying_period_origin:
base_path: '/api/1.0/person/bla/bla'
class: 'Chill\PersonBundle\Entity\Blah'
name: bla
actions:
_entity:
methods:
GET: true
HEAD: true
roles:
GET: MY_ROLE_SEE
HEAD: MY ROLE_SEE
Customize the controller
========================
You can customize the controller by hooking into the default actions. Take care of extending :code:`Chill\MainBundle\CRUD\Controller\ApiController`.
.. code-block:: php
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class OpeningApiController extends ApiController
{
protected function customizeQuery(string $action, Request $request, $qb): void
{
$qb->where($qb->expr()->gt('e.noActiveAfter', ':now'))
->orWhere($qb->expr()->isNull('e.noActiveAfter'));
$qb->setParameter('now', new \DateTime('now'));
}
}
And set your controller in configuration:
.. code-block:: yaml
chill_main:
apis:
accompanying_period_origin:
base_path: '/api/1.0/person/accompanying-period/origin'
class: 'Chill\PersonBundle\Entity\AccompanyingPeriod\Origin'
name: accompanying_period_origin
# add a controller
controller: 'Chill\PersonBundle\Controller\OpeningApiController'
base_role: 'ROLE_USER'
actions:
_index:
methods:
GET: true
HEAD: true
_entity:
methods:
GET: true
HEAD: true
Create your own actions
=======================
You can add your own actions:
.. 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:
# add a custom participation:
participation:
methods:
POST: true
DELETE: true
GET: false
HEAD: false
PUT: false
roles:
POST: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
DELETE: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
GET: null
HEAD: null
PUT: null
single-collection: single
The key :code:`single-collection` with value :code:`single` will add a :code:`/{id}/ + "action name"` (in this example, :code:`/{id}/participation`) into the path, after the base path. If the value is :code:`collection`, no id will be set, but the action name will be append to the path.
Then, create the corresponding action into your controller:
.. 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\PersonBundle\Entity\AccompanyingPeriod;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
use Chill\PersonBundle\Entity\Person;
class AccompanyingCourseApiController extends ApiController
{
protected EventDispatcherInterface $eventDispatcher;
protected ValidatorInterface $validator;
public function __construct(EventDispatcherInterface $eventDispatcher, $validator)
{
$this->eventDispatcher = $eventDispatcher;
$this->validator = $validator;
}
public function participationApi($id, Request $request, $_format)
{
/** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = $this->getEntity('participation', $id, $request);
$person = $this->getSerializer()
->deserialize($request->getContent(), Person::class, $_format, []);
if (NULL === $person) {
throw new BadRequestException('person id not found');
}
$this->onPostCheckACL('participation', $request, $accompanyingPeriod, $_format);
switch ($request->getMethod()) {
case Request::METHOD_POST:
$participation = $accompanyingPeriod->addPerson($person);
break;
case Request::METHOD_DELETE:
$participation = $accompanyingPeriod->removePerson($person);
break;
default:
throw new BadRequestException("This method is not supported");
}
$errors = $this->validator->validate($accompanyingPeriod);
if ($errors->count() > 0) {
// only format accepted
return $this->json($errors);
}
$this->getDoctrine()->getManager()->flush();
return $this->json($participation);
}
}
Serialization for collection
============================
A specific model has been defined for returning collection:
.. code-block:: json
{
"count": 49,
"results": [
],
"pagination": {
"more": true,
"next": "/api/1.0/search.json&q=xxxx......&page=2",
"previous": null,
"first": 0,
"items_per_page": 1
}
}
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
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Chill\MainBundle\Pagination\PaginatorInterface;
class MyController extends AbstractController
{
protected function serializeCollection(PaginatorInterface $paginator, $entities): Response
{
$model = new Collection($entities, $paginator);
return $this->json($model, Response::HTTP_OK, [], $context);
}
}
.. _api_full_configuration:
Full configuration example
==========================
.. code-block:: yaml
apis:
-
class: Chill\PersonBundle\Entity\AccompanyingPeriod
name: accompanying_course
base_path: /api/1.0/person/accompanying-course
controller: Chill\PersonBundle\Controller\AccompanyingCourseApiController
actions:
_entity:
roles:
GET: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
HEAD: null
POST: null
DELETE: null
PUT: null
controller_action: null
path: null
single-collection: single
methods:
GET: true
HEAD: true
POST: false
DELETE: false
PUT: false
participation:
methods:
POST: true
DELETE: true
GET: false
HEAD: false
PUT: false
roles:
POST: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
DELETE: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
GET: null
HEAD: null
PUT: null
controller_action: null
# the requirements for the route. Will be set to `[ 'id' => '\d+' ]` if left empty.
requirements: []
path: null
single-collection: single
base_role: null

View File

@@ -16,7 +16,6 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
Instructions to create a new bundle <create-a-new-bundle.rst>
CRUD (Create - Update - Delete) for one entity <crud.rst>
Helpers for building a REST API <api.rst>
Routing <routing.rst>
Menus <menus.rst>
Forms <forms.rst>
@@ -30,7 +29,7 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
Timelines <timelines.rst>
Exports <exports.rst>
Embeddable comments <embeddable-comments.rst>
Run tests <run-tests.rst>
Testing <make-test-working.rst>
Useful snippets <useful-snippets.rst>
manual/index.rst
Assets <assets.rst>

View File

@@ -0,0 +1,231 @@
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
Make tests working
******************
Unit and functional tests are important to ensure that bundle may be deployed securely.
In reason of the Chill architecture, test should be runnable from the bundle's directory and works correctly: this will allow continuous integration tools to run tests automatically.
.. note::
Integration tools (i.e. `travis-ci <https://travis-ci.org>`_) works like this :
* they clone the bundle repository in a virtual machine, using git
* they optionnaly run `composer` to download and install depedencies
* they optionnaly run other command to prepare a database, insert fixtures, ...
* they run test
On the developer's machine test should be runnable with two or three commands **runned from the bundle directory** :
.. code-block:: bash
$ composer install --dev
$ // command to insert fixtures, ...
$ phpunit
This chapter has been inspired by `this useful blog post <http://blog.kevingomez.fr/2013/01/09/functional-testing-standalone-symfony2-bundles/>`_.
Bootstrap phpunit for a standalone bundle
==========================================
Unit tests should run after achieving this step.
phpunit.xml
-----------
A `phpunit.xml.dist` file should be present at the bundle root.
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./Tests/bootstrap.php" colors="true">
<!-- the file "./Tests/boostrap.php" will be created on the next step -->
<testsuites>
<testsuite name="ChillMain test suite">
<directory suffix="Test.php">./Tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
bootstrap.php
--------------
A file `boostrap.php`, located in the `Tests` directory, will allow phpunit to resolve class autoloading :
.. code-block:: php
<?php
if (!is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
throw new \LogicException('Could not find autoload.php in vendor/. Did you run "composer install --dev"?');
}
require $autoloadFile;
composer.json
-------------
The `composer.json` file **located at the bundle's root** should contains all depencies needed to run test (and to execute bundle functions).
Ensure that all dependencies are included in the `require` and `require-dev` sections.
Functional tests
================
If you want to access services, database, and run functional tests, you will have to bootstrap a symfony app, with the minimal configuration. Three files are required :
* a `config_test.yml` file (eventually with a `config.yml`);
* a `routing.yml` file
* an `AppKernel.php` file
Adapt phpunit.xml
-----------------
You should add reference to the new application within `phpunit.xml.dist`. The directive `<php>` should be added like this, if your `AppKernel.php` file is located in `Tests/Fixtures/App` directory:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./Tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="ChillMain test suite">
<directory suffix="Test.php">./Tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
<!-- the lines we added -->
<php>
<server name="KERNEL_DIR" value="./Tests/Fixtures/App/" />
</php>
</phpunit>
AppKernel.php
-------------
This file boostrap the app. It contains three functions. This is the file used in the ChillMain bundle :
.. code-block:: php
<?php
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;
class AppKernel extends Kernel
{
public function registerBundles()
{
return array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Chill\MainBundle\ChillMainBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new \Symfony\Bundle\AsseticBundle\AsseticBundle(),
#add here all the required bundle (some bundle are not required)
);
}
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}
/**
* @return string
*/
public function getCacheDir()
{
return sys_get_temp_dir().'/ChillMainBundle/cache';
}
/**
* @return string
*/
public function getLogDir()
{
return sys_get_temp_dir().'/ChillMainBundle/logs';
}
}
config_test.yml
---------------
There are only few parameters required for the config file. This is a basic version for ChillMain :
.. code-block:: yaml
# config/config_test.yml
imports:
- { resource: config.yml } #here we import a config.yml file, this is not required
framework:
test: ~
session:
storage_id: session.storage.filesystem
.. code-block:: yaml
# config/config.yml
framework:
secret: Not very secret
router: { resource: "%kernel.root_dir%/config/routing.yml" }
form: true
csrf_protection: true
session: ~
default_locale: fr
translator: { fallback: fr }
profiler: { only_exceptions: false }
templating: #required for assetic. Remove if not needed
engines: ['twig']
.. note::
You must adapt config.yml file according to your required bundle. Some options will be missing, other may be removed...
.. note::
If you would like to tests different environments, with differents configuration, you could create differents config_XXX.yml files.
routing.yml
------------
You should add there all routing information needed for your bundle.
.. code-block: yaml
chill_main_bundle:
resource: "@CLChillMainBundle/Resources/config/routing.yml"
That's it. Tests should pass.

View File

@@ -7,8 +7,6 @@
Free Documentation License".
.. _pagination-ref:
Pagination
##########
@@ -17,7 +15,7 @@ The Bundle :code:`Chill\MainBundle` provides a **Pagination** api which allow yo
A simple example
****************
In the controller, get the :code:`Chill\Main\Pagination\PaginatorFactory` from the `Container` and use this :code:`PaginatorFactory` to create a :code:`Paginator` instance.
In the controller, get the :class:`Chill\Main\Pagination\PaginatorFactory` from the `Container` and use this :code:`PaginatorFactory` to create a :code:`Paginator` instance.
.. literalinclude:: pagination/example.php

View File

@@ -1,68 +0,0 @@
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
Run tests
*********
In reason of the Chill architecture, test should be runnable from the bundle's directory and works correctly: this will allow continuous integration tools to run tests automatically.
From chill app
==============
This is the most convenient method for developer: run test for chill bundle from the main app.
.. code-block:: bash
# run into a container
docker-compose exec --user $(id -u) php bash
# execute all tests suites
bin/phpunit
# .. or execute a single test
bin/phpunit vendor/chill-project/chill-bundles/src/Bundle/ChillMainBundle/Tests/path/to/FileTest.php
You can also run tests in a single command:
.. code-block:: bash
docker-compose exec --user $(id -u) php bin/phpunit
Tests from a bundle (chill-bundles)
-----------------------------------
Those tests needs the whole symfony app to execute Application Tests (which test html page).
For ease, the app is cloned using a :code:`git submodule`, which clone the main app into :code:`tests/app`, and tests are bootstrapped to this app. The dependencies are also installed into `tests/app/vendor` to ensure compliance with relative path from this symfony application.
You may boostrap the tests fro the chill bundle this way:
.. code-block:: bash
# ensure to be located into the environement (provided by docker suits well)
docker-compose exec --user $(id -u) php bash
# go to chill subdirectory
cd vendor/chill-project/chill-bundles
# install submodule
git submodule init
git submodule update
# install composer and dependencies
curl -sS https://getcomposer.org/installer | php
# run tests
bin/phpunit
.. note::
If you are on a fresh install, you will need to migrate database schema.
The path to console tool must be adapted to the app. To load migration and add fixtures, one can execute the following commands:
.. code-block:: bash
tests/app/bin/console doctrine:migrations:migrate
tests/app/bin/console doctrine:fixtures:load

View File

@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/app/tests/bootstrap.php"
>
<php>
<ini name="error_reporting" value="-1" />
<server name="APP_ENV" value="test" force="true" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
<server name="SHELL_VERBOSITY" value="-1" />
</php>
<testsuites>
<testsuite name="MainBundle">
<directory suffix="Test.php">src/Bundle/ChillMainBundle/Tests/</directory>
</testsuite>
<!--
<testsuite name="PersonBundle">
<directory suffix="Test.php">src/Bundle/ChillPersonBundle/Tests/</directory>
</testsuite>
-->
</testsuites>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
</listeners>
<!-- Run `composer require symfony/panther` before enabling this extension -->
<!--
<extensions>
<extension class="Symfony\Component\Panther\ServerExtension" />
</extensions>
-->
</phpunit>

View File

@@ -1,77 +0,0 @@
<?php
/*
* Copyright (C) 2018 Champs Libres Cooperative <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\MainBundle\CRUD\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
use Chill\MainBundle\Routing\MenuComposer;
use Symfony\Component\DependencyInjection\Definition;
/**
*
*
*/
class CRUDControllerCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$crudConfig = $container->getParameter('chill_main_crud_route_loader_config');
$apiConfig = $container->getParameter('chill_main_api_route_loader_config');
foreach ($crudConfig as $crudEntry) {
$this->configureCrudController($container, $crudEntry, 'crud');
}
foreach ($apiConfig as $crudEntry) {
$this->configureCrudController($container, $crudEntry, 'api');
}
}
/**
* Add a controller for each definition, and add a methodCall to inject crud configuration to controller
*/
private function configureCrudController(ContainerBuilder $container, array $crudEntry, string $apiOrCrud): void
{
$controllerClass = $crudEntry['controller'];
$controllerServiceName = 'cs'.$apiOrCrud.'_'.$crudEntry['name'].'_controller';
if ($container->hasDefinition($controllerClass)) {
$controller = $container->getDefinition($controllerClass);
$container->removeDefinition($controllerClass);
$alreadyDefined = true;
} else {
$controller = new Definition($controllerClass);
$alreadyDefined = false;
}
$controller->addTag('controller.service_arguments');
if (FALSE === $alreadyDefined) {
$controller->setAutoconfigured(true);
$controller->setPublic(true);
}
$param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name'];
$container->setParameter($param, $crudEntry);
$controller->addMethodCall('setCrudConfig', ['%'.$param.'%']);
$container->setDefinition($controllerServiceName, $controller);
}
}

View File

@@ -1,225 +0,0 @@
<?php
namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Pagination\PaginatorInterface;
class AbstractCRUDController extends AbstractController
{
/**
* The crud configuration
*
* This configuration si defined by `chill_main['crud']` or `chill_main['apis']`
*
* @var array
*/
protected array $crudConfig = [];
/**
* get the instance of the entity with the given id
*
* @param string $id
* @return object
*/
protected function getEntity($action, $id, Request $request): ?object
{
return $this->getDoctrine()
->getRepository($this->getEntityClass())
->find($id);
}
/**
* Count the number of entities
*
* By default, count all entities. You can customize the query by
* using the method `customizeQuery`.
*
* @param string $action
* @param Request $request
* @return int
*/
protected function countEntities(string $action, Request $request, $_format): int
{
return $this->buildQueryEntities($action, $request)
->select('COUNT(e)')
->getQuery()
->getSingleScalarResult()
;
}
/**
* Query the entity.
*
* By default, get all entities. You can customize the query by using the
* method `customizeQuery`.
*
* The method `orderEntity` is called internally to order entities.
*
* It returns, by default, a query builder.
*
*/
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
{
$query = $this->buildQueryEntities($action, $request)
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
->setMaxResults($paginator->getItemsPerPage());
// allow to order queries and return the new query
return $this->orderQuery($action, $query, $request, $paginator, $_format);
}
/**
* Add ordering fields in the query build by self::queryEntities
*
*/
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
{
return $query;
}
/**
* Build the base query for listing all entities.
*
* This method is used internally by `countEntities` `queryEntities`
*
* This base query does not contains any `WHERE` or `SELECT` clauses. You
* can add some by using the method `customizeQuery`.
*
* The alias for the entity is "e".
*
* @param string $action
* @param Request $request
* @return QueryBuilder
*/
protected function buildQueryEntities(string $action, Request $request)
{
$qb = $this->getDoctrine()->getManager()
->createQueryBuilder()
->select('e')
->from($this->getEntityClass(), 'e')
;
$this->customizeQuery($action, $request, $qb);
return $qb;
}
protected function customizeQuery(string $action, Request $request, $query): void {}
/**
* Get the result of the query
*/
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
{
return $query->getQuery()->getResult();
}
protected function onPreIndex(string $action, Request $request, string $_format): ?Response
{
return null;
}
/**
* method used by indexAction
*/
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
{
return null;
}
/**
* method used by indexAction
*/
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response
{
return null;
}
/**
* method used by indexAction
*/
protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response
{
return null;
}
/**
* Get the complete FQDN of the class
*
* @return string the complete fqdn of the class
*/
protected function getEntityClass(): string
{
return $this->crudConfig['class'];
}
/**
* called on post fetch entity
*/
protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response
{
return null;
}
/**
* Called on post check ACL
*/
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
{
return null;
}
/**
* check the acl. Called by every action.
*
* By default, check the role given by `getRoleFor` for the value given in
* $entity.
*
* Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
* if not accessible.
*
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
*/
protected function checkACL(string $action, Request $request, string $_format, $entity = null)
{
$this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity);
}
/**
*
* @return string the crud name
*/
protected function getCrudName(): string
{
return $this->crudConfig['name'];
}
protected function getActionConfig(string $action)
{
return $this->crudConfig['actions'][$action];
}
/**
* Set the crud configuration
*
* Used by the container to inject configuration for this crud.
*/
public function setCrudConfig(array $config): void
{
$this->crudConfig = $config;
}
/**
* @return PaginatorFactory
*/
protected function getPaginatorFactory(): PaginatorFactory
{
return $this->container->get('chill_main.paginator_factory');
}
}

View File

@@ -1,220 +0,0 @@
<?php
namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\MainBundle\Pagination\PaginatorInterface;
class ApiController extends AbstractCRUDController
{
/**
* The view action.
*
* Some steps may be overriden during this process of rendering:
*
* This method:
*
* 1. fetch the entity, using `getEntity`
* 2. launch `onPostFetchEntity`. If postfetch is an instance of Response,
* this response is returned.
* 2. throw an HttpNotFoundException if entity is null
* 3. check ACL using `checkACL` ;
* 4. launch `onPostCheckACL`. If the result is an instance of Response,
* this response is returned ;
* 5. Serialize the entity and return the result. The serialization context is given by `getSerializationContext`
*
*/
protected function entityGet(string $action, Request $request, $id, $_format = 'html'): 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;
}
if ($_format === 'json') {
$context = $this->getContextForSerialization($action, $request, $_format, $entity);
return $this->json($entity, Response::HTTP_OK, [], $context);
} else {
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This format is not implemented");
}
}
public function onBeforeSerialize(string $action, Request $request, $_format, $entity): ?Response
{
return null;
}
/**
* Base method for handling api action
*
* @return void
*/
public function entityApi(Request $request, $id, $_format): Response
{
switch ($request->getMethod()) {
case Request::METHOD_GET:
case REQUEST::METHOD_HEAD:
return $this->entityGet('_entity', $request, $id, $_format);
default:
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This method is not implemented");
}
}
/**
* Base action for indexing entities
*/
public function indexApi(Request $request, string $_format)
{
switch ($request->getMethod()) {
case Request::METHOD_GET:
case REQUEST::METHOD_HEAD:
return $this->indexApiAction('_index', $request, $_format);
default:
throw $this->createNotFoundException("This method is not supported");
}
}
/**
* Build an index page.
*
* Some steps may be overriden during this process of rendering.
*
* This method:
*
* 1. Launch `onPreIndex`
* x. check acl. If it does return a response instance, return it
* x. launch `onPostCheckACL`. If it does return a response instance, return it
* 1. count the items, using `countEntities`
* 2. build a paginator element from the the number of entities ;
* 3. Launch `onPreIndexQuery`. If it does return a response instance, return it
* 3. build a query, using `queryEntities`
* x. fetch the results, using `getQueryResult`
* x. Launch `onPostIndexFetchQuery`. If it does return a response instance, return it
* 4. Serialize the entities in a Collection, using `SerializeCollection`
*
* @param string $action
* @param Request $request
* @return type
*/
protected function indexApiAction($action, Request $request, $_format)
{
$this->onPreIndex($action, $request, $_format);
$response = $this->checkACL($action, $request, $_format);
if ($response instanceof Response) {
return $response;
}
if (!isset($entity)) {
$entity = '';
}
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
}
$totalItems = $this->countEntities($action, $request, $_format);
$paginator = $this->getPaginatorFactory()->create($totalItems);
$response = $this->onPreIndexBuildQuery($action, $request, $_format, $totalItems,
$paginator);
if ($response instanceof Response) {
return $response;
}
$query = $this->queryEntities($action, $request, $_format, $paginator);
$response = $this->onPostIndexBuildQuery($action, $request, $_format, $totalItems,
$paginator, $query);
if ($response instanceof Response) {
return $response;
}
$entities = $this->getQueryResult($action, $request, $_format, $totalItems, $paginator, $query);
$response = $this->onPostIndexFetchQuery($action, $request, $_format, $totalItems,
$paginator, $entities);
if ($response instanceof Response) {
return $response;
}
return $this->serializeCollection($action, $request, $_format, $paginator, $entities);
}
/**
* Serialize collections
*
*/
protected function serializeCollection(string $action, Request $request, string $_format, PaginatorInterface $paginator, $entities): Response
{
$model = new Collection($entities, $paginator);
$context = $this->getContextForSerialization($action, $request, $_format, $entities);
return $this->json($model, Response::HTTP_OK, [], $context);
}
protected function getContextForSerialization(string $action, Request $request, string $_format, $entity): array
{
return [];
}
/**
* get the role given from the config.
*/
protected function getRoleFor(string $action, Request $request, $entity, $_format): string
{
$actionConfig = $this->getActionConfig($action);
if (NULL !== $actionConfig['roles'][$request->getMethod()]) {
return $actionConfig['roles'][$request->getMethod()];
}
if ($this->crudConfig['base_role']) {
return $this->crudConfig['base_role'];
}
throw new \RuntimeException(sprintf("the config does not have any role for the ".
"method %s nor a global role for the whole action. Add those to your ".
"configuration or override the required method", $request->getMethod()));
}
protected function getSerializer(): SerializerInterface
{
return $this->get('serializer');
}
}

View File

@@ -34,7 +34,6 @@ use Chill\MainBundle\CRUD\Form\CRUDDeleteEntityForm;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Class CRUDController
@@ -485,7 +484,7 @@ class CRUDController extends AbstractController
* @param mixed $id
* @return Response
*/
protected function viewAction(string $action, Request $request, $id, $_format = 'html'): Response
protected function viewAction(string $action, Request $request, $id)
{
$entity = $this->getEntity($action, $id, $request);
@@ -497,7 +496,7 @@ class CRUDController extends AbstractController
if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s "
. "is not found", $this->getCrudName(), $id));
. "is not found"), $this->getCrudName(), $id);
}
$response = $this->checkACL($action, $entity);
@@ -509,36 +508,17 @@ class CRUDController extends AbstractController
if ($response instanceof Response) {
return $response;
}
if ($_format === 'html') {
$defaultTemplateParameters = [
'entity' => $entity,
'crud_name' => $this->getCrudName()
];
return $this->render(
$this->getTemplateFor($action, $entity, $request),
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
$defaultTemplateParameters = [
'entity' => $entity,
'crud_name' => $this->getCrudName()
];
return $this->render(
$this->getTemplateFor($action, $entity, $request),
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
);
} elseif ($_format === 'json') {
$context = $this->getContextForSerialization($action, $request, $entity, $_format);
return $this->json($entity, Response::HTTP_OK, [], $context);
} else {
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This format is not implemented");
}
}
/**
* Get the context for the serialization
*/
public function getContextForSerialization(string $action, Request $request, $entity, string $_format): array
{
return [];
}
/**
* The edit action.
@@ -819,7 +799,7 @@ class CRUDController extends AbstractController
*/
protected function getRoleFor($action)
{
if (\array_key_exists('role', $this->getActionConfig($action))) {
if (NULL !== ($this->getActionConfig($action)['role'])) {
return $this->getActionConfig($action)['role'];
}
@@ -1201,7 +1181,6 @@ class CRUDController extends AbstractController
AuthorizationHelper::class => AuthorizationHelper::class,
EventDispatcherInterface::class => EventDispatcherInterface::class,
Resolver::class => Resolver::class,
SerializerInterface::class => SerializerInterface::class,
]
);
}

View File

@@ -23,9 +23,6 @@ namespace Chill\MainBundle\CRUD\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\HttpFoundation\Request;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\CRUD\Controller\CRUDController;
/**
* Class CRUDRoutesLoader
@@ -35,34 +32,24 @@ use Chill\MainBundle\CRUD\Controller\CRUDController;
*/
class CRUDRoutesLoader extends Loader
{
protected array $crudConfig = [];
protected array $apiCrudConfig = [];
/**
* @var array
*/
protected $config = [];
/**
* @var bool
*/
private $isLoaded = false;
private const ALL_SINGLE_METHODS = [
Request::METHOD_GET,
Request::METHOD_POST,
Request::METHOD_PUT,
Request::METHOD_DELETE
];
private const ALL_INDEX_METHODS = [ Request::METHOD_GET, Request::METHOD_HEAD ];
/**
* CRUDRoutesLoader constructor.
*
* @param $crudConfig the config from cruds
* @param $apicrudConfig the config from api_crud
* @param $config
*/
public function __construct(array $crudConfig, array $apiConfig)
public function __construct($config)
{
$this->crudConfig = $crudConfig;
$this->apiConfig = $apiConfig;
$this->config = $config;
}
/**
@@ -76,161 +63,53 @@ class CRUDRoutesLoader extends Loader
}
/**
* Load routes for CRUD and CRUD Api
* @return RouteCollection
*/
public function load($resource, $type = null): RouteCollection
public function load($resource, $type = null)
{
if (true === $this->isLoaded) {
throw new \RuntimeException('Do not add the "CRUD" loader twice');
}
$collection = new RouteCollection();
foreach ($this->crudConfig as $crudConfig) {
$collection->addCollection($this->loadCrudConfig($crudConfig));
foreach ($this->config as $config) {
$collection->addCollection($this->loadConfig($config));
}
foreach ($this->apiConfig as $crudConfig) {
$collection->addCollection($this->loadApi($crudConfig));
}
return $collection;
}
/**
* Load routes for CRUD (without api)
*
* @param $crudConfig
* @param $config
* @return RouteCollection
*/
protected function loadCrudConfig($crudConfig): RouteCollection
protected function loadConfig($config): RouteCollection
{
$collection = new RouteCollection();
$controller ='cscrud_'.$crudConfig['name'].'_controller';
foreach ($crudConfig['actions'] as $name => $action) {
// defaults (controller name)
foreach ($config['actions'] as $name => $action) {
$defaults = [
'_controller' => $controller.':'.($action['controller_action'] ?? $name)
'_controller' => 'cscrud_'.$config['name'].'_controller'.':'.($action['controller_action'] ?? $name)
];
if ($name === 'index') {
$path = "{_locale}".$crudConfig['base_path'];
$path = "{_locale}".$config['base_path'];
$route = new Route($path, $defaults);
} elseif ($name === 'new') {
$path = "{_locale}".$crudConfig['base_path'].'/'.$name;
$path = "{_locale}".$config['base_path'].'/'.$name;
$route = new Route($path, $defaults);
} else {
$path = "{_locale}".$crudConfig['base_path'].($action['path'] ?? '/{id}/'.$name);
$path = "{_locale}".$config['base_path'].($action['path'] ?? '/{id}/'.$name);
$requirements = $action['requirements'] ?? [
'{id}' => '\d+'
];
$route = new Route($path, $defaults, $requirements);
}
$collection->add('chill_crud_'.$crudConfig['name'].'_'.$name, $route);
$collection->add('chill_crud_'.$config['name'].'_'.$name, $route);
}
return $collection;
}
/**
* Load routes for api single
*
* @param $crudConfig
* @return RouteCollection
*/
protected function loadApi(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 === '_entity' ? 'single' : NULL;
if ('collection' === $singleCollection) {
// continue;
}
// compute default action
switch ($name) {
case '_entity':
$controllerAction = 'entityApi';
break;
case '_index':
$controllerAction = 'indexApi';
break;
default:
$controllerAction = $name.'Api';
break;
}
$defaults = [
'_controller' => $controller.':'.($action['controller_action'] ?? $controllerAction)
];
// path are rewritten
// if name === 'default', we rewrite it to nothing :-)
$localName = \in_array($name, [ '_entity', '_index' ]) ? '' : '/'.$name;
if ('collection' === $action['single-collection'] || '_index' === $name) {
$localPath = $action['path'] ?? $localName.'.{_format}';
} else {
$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->setMethods($methods);
$collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route);
}
return $collection;
}
/**
* 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;
}
$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->setMethods($methods);
$collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route);
}
return $collection;
}
}

View File

@@ -14,7 +14,6 @@ use Chill\MainBundle\DependencyInjection\CompilerPass\NotificationCounterCompile
use Chill\MainBundle\DependencyInjection\CompilerPass\MenuCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\ACLFlagsCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\GroupingCenterCompilerPass;
use Chill\MainBundle\CRUD\CompilerPass\CRUDControllerCompilerPass;
use Chill\MainBundle\Templating\Entity\CompilerPass as RenderEntityCompilerPass;
@@ -34,6 +33,5 @@ class ChillMainBundle extends Bundle
$container->addCompilerPass(new ACLFlagsCompilerPass());
$container->addCompilerPass(new GroupingCenterCompilerPass());
$container->addCompilerPass(new RenderEntityCompilerPass());
$container->addCompilerPass(new CRUDControllerCompilerPass());
}
}

View File

@@ -1,63 +0,0 @@
<?php
namespace Chill\MainBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\Routing\Annotation\Route;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\AddressReference;
/**
* Class AddressController
*
* @package Chill\MainBundle\Controller
*/
class AddressController extends AbstractController
{
/**
* Get API Data for showing endpoint
*
* @Route(
* "/{_locale}/main/api/1.0/address/{address_id}/show.{_format}",
* name="chill_main_address_api_show"
* )
* @ParamConverter("address", options={"id": "address_id"})
*/
public function showAddress(Address $address, $_format): Response
{
// TODO check ACL ?
switch ($_format) {
case 'json':
return $this->json($address);
default:
throw new BadRequestException('Unsupported format');
}
}
/**
* Get API Data for showing endpoint
*
* @Route(
* "/{_locale}/main/api/1.0/address-reference/{address_reference_id}/show.{_format}",
* name="chill_main_address_reference_api_show"
* )
* @ParamConverter("addressReference", options={"id": "address_reference_id"})
*/
public function showAddressReference(AddressReference $addressReference, $_format): Response
{
// TODO check ACL ?
switch ($_format) {
case 'json':
return $this->json($addressReference);
default:
throw new BadRequestException('Unsupported format');
}
}
}

View File

@@ -1,95 +0,0 @@
<?php
namespace Chill\MainBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
use Chill\MainBundle\Entity\AddressReference;
use Chill\MainBundle\Doctrine\Model\Point;
/**
* Load reference addresses into database
*
* @author Champs Libres
*/
class LoadAddressReferences extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface {
protected $faker;
public function __construct()
{
$this->faker = \Faker\Factory::create('fr_FR');
}
/**
*
* @var ContainerInterface
*/
private $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function getOrder() {
return 51;
}
/**
* Create a random point
*
* @return Point
*/
private function getRandomPoint()
{
$lonBrussels = 4.35243;
$latBrussels = 50.84676;
$lon = $lonBrussels + 0.01 * rand(-5, 5);
$lat = $latBrussels + 0.01 * rand(-5, 5);
return Point::fromLonLat($lon, $lat);
}
/**
* Create a random reference address
*
* @return AddressReference
*/
private function getRandomAddressReference()
{
$ar= new AddressReference();
$ar->setRefId($this->faker->numerify('ref-id-######'));
$ar->setStreet($this->faker->streetName);
$ar->setStreetNumber(rand(0,199));
$ar ->setPoint($this->getRandomPoint());
$ar->setPostcode($this->getReference(
LoadPostalCodes::$refs[array_rand(LoadPostalCodes::$refs)]
));
$ar->setMunicipalityCode($ar->getPostcode()->getCode());
return $ar
;
}
public function load(ObjectManager $manager) {
echo "loading some reference address... \n";
for ($i=0; $i<10; $i++) {
$ar = $this->getRandomAddressReference();
$manager->persist($ar);
}
$manager->flush();
}
}

View File

@@ -47,11 +47,11 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
{
/**
* widget factory
*
*
* @var WidgetFactoryInterface[]
*/
protected $widgetFactories = array();
/**
* @param WidgetFactoryInterface $factory
*/
@@ -59,7 +59,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
{
$this->widgetFactories[] = $factory;
}
/**
* @return WidgetFactoryInterface[]
*/
@@ -67,7 +67,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
{
return $this->widgetFactories;
}
/**
* {@inheritDoc}
* @param array $configs
@@ -79,31 +79,31 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
// configuration for main bundle
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('chill_main.installation_name',
$config['installation_name']);
$container->setParameter('chill_main.available_languages',
$config['available_languages']);
$container->setParameter('chill_main.routing.resources',
$config['routing']['resources']);
$container->setParameter('chill_main.routing.resources',
$config['routing']['resources']);
$container->setParameter('chill_main.pagination.item_per_page',
$config['pagination']['item_per_page']);
$container->setParameter('chill_main.notifications',
$container->setParameter('chill_main.notifications',
$config['notifications']);
$container->setParameter('chill_main.redis',
$container->setParameter('chill_main.redis',
$config['redis']);
$container->setParameter('chill_main.phone_helper',
$container->setParameter('chill_main.phone_helper',
$config['phone_helper'] ?? []);
// add the key 'widget' without the key 'enable'
$container->setParameter('chill_main.widgets',
isset($config['widgets']['homepage']) ?
$container->setParameter('chill_main.widgets',
isset($config['widgets']['homepage']) ?
array('homepage' => $config['widgets']['homepage']):
array()
);
@@ -131,11 +131,10 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
$loader->load('services/templating.yaml');
$loader->load('services/timeline.yaml');
$loader->load('services/search.yaml');
$loader->load('services/serializer.yaml');
$this->configureCruds($container, $config['cruds'], $config['apis'], $loader);
$this->configureCruds($container, $config['cruds'], $loader);
}
/**
* @param array $config
* @param ContainerBuilder $container
@@ -145,11 +144,11 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
{
return new Configuration($this->widgetFactories, $container);
}
/**
* @param ContainerBuilder $container
*/
public function prepend(ContainerBuilder $container)
public function prepend(ContainerBuilder $container)
{
//add installation_name and date_format to globals
$chillMainConfig = $container->getExtensionConfig($this->getAlias());
@@ -164,7 +163,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
'form_themes' => array('@ChillMain/Form/fields.html.twig')
);
$container->prependExtensionConfig('twig', $twigConfig);
//add DQL function to ORM (default entity_manager)
$container->prependExtensionConfig('doctrine', array(
'orm' => array(
@@ -183,56 +182,78 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
)
)
));
//add dbal types (default entity_manager)
$container->prependExtensionConfig('doctrine', array(
'dbal' => [
'types' => [
'dateinterval' => [
'class' => \Chill\MainBundle\Doctrine\Type\NativeDateIntervalType::class
],
'point' => [
'class' => \Chill\MainBundle\Doctrine\Type\PointType::class
]
'dateinterval' => \Chill\MainBundle\Doctrine\Type\NativeDateIntervalType::class
]
]
));
//add current route to chill main
$container->prependExtensionConfig('chill_main', array(
'routing' => array(
'resources' => array(
'@ChillMainBundle/config/routes.yaml'
)
)
));
//add a channel to log app events
$container->prependExtensionConfig('monolog', array(
'channels' => array('chill')
));
}
/**
* Load parameter for configuration and set parameters for api
* @param ContainerBuilder $container
* @param array $config the config under 'cruds' key
* @return null
*/
protected function configureCruds(
ContainerBuilder $container,
array $crudConfig,
array $apiConfig,
Loader\YamlFileLoader $loader
): void
protected function configureCruds(ContainerBuilder $container, $config, Loader\YamlFileLoader $loader)
{
if (count($crudConfig) === 0) {
if (count($config) === 0) {
return;
}
$loader->load('services/crud.yaml');
$container->setParameter('chill_main_crud_route_loader_config', $crudConfig);
$container->setParameter('chill_main_api_route_loader_config', $apiConfig);
// Note: the controller are loaded inside compiler pass
$container->setParameter('chill_main_crud_route_loader_config', $config);
$definition = new Definition();
$definition
->setClass(\Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader::class)
->addArgument('%chill_main_crud_route_loader_config%')
;
$container->setDefinition('chill_main_crud_route_loader', $definition);
$alreadyExistingNames = [];
foreach ($config as $crudEntry) {
$controller = $crudEntry['controller'];
$controllerServiceName = 'cscrud_'.$crudEntry['name'].'_controller';
$name = $crudEntry['name'];
// check for existing crud names
if (\in_array($name, $alreadyExistingNames)) {
throw new LogicException(sprintf("the name %s is defined twice in CRUD", $name));
}
if (!$container->has($controllerServiceName)) {
$controllerDefinition = new Definition($controller);
$controllerDefinition->addTag('controller.service_arguments');
$controllerDefinition->setAutoconfigured(true);
$controllerDefinition->setClass($crudEntry['controller']);
$container->setDefinition($controllerServiceName, $controllerDefinition);
}
$container->setParameter('chill_main_crud_config_'.$name, $crudEntry);
$container->getDefinition($controllerServiceName)
->addMethodCall('setCrudConfig', ['%chill_main_crud_config_'.$name.'%']);
}
}
}

View File

@@ -8,7 +8,6 @@ use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\Request;
/**
@@ -141,7 +140,7 @@ class Configuration implements ConfigurationInterface
->scalarNode('controller_action')
->defaultNull()
->info('the method name to call in the route. Will be set to the action name if left empty.')
->example("action")
->example("'action'")
->end()
->scalarNode('path')
->defaultNull()
@@ -169,78 +168,6 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->arrayNode('apis')
->defaultValue([])
->arrayPrototype()
->children()
->scalarNode('class')->cannotBeEmpty()->isRequired()->end()
->scalarNode('controller')
->cannotBeEmpty()
->defaultValue(\Chill\MainBundle\CRUD\Controller\ApiController::class)
->end()
->scalarNode('name')->cannotBeEmpty()->isRequired()->end()
->scalarNode('base_path')->cannotBeEmpty()->isRequired()->end()
->scalarNode('base_role')->defaultNull()->end()
->arrayNode('actions')
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->scalarNode('controller_action')
->defaultNull()
->info('the method name to call in the controller. Will be set to the concatenation '.
'of action name + \'Api\' if left empty.')
->example("showApi")
->end()
->scalarNode('path')
->defaultNull()
->info('the path that will be **appended** after the base path. Do not forget to add ' .
'arguments for the method. By default, will set to the action name, including an `{id}` '.
'parameter. A suffix of action name will be appended, except if the action name '.
'is "_entity".')
->example('/{id}/my-action')
->end()
->arrayNode('requirements')
->ignoreExtraKeys(false)
->info('the requirements for the route. Will be set to `[ \'id\' => \'\d+\' ]` if left empty.')
->end()
->enumNode('single-collection')
->values(['single', 'collection'])
->defaultValue('single')
->info('indicates if the returned object is a single element or a collection. '.
'If the action name is `_index`, this value will always be considered as '.
'`collection`')
->end()
->arrayNode('methods')
->addDefaultsIfNotSet()
->info('the allowed methods')
->children()
->booleanNode(Request::METHOD_GET)->defaultTrue()->end()
->booleanNode(Request::METHOD_HEAD)->defaultTrue()->end()
->booleanNode(Request::METHOD_POST)->defaultFalse()->end()
->booleanNode(Request::METHOD_DELETE)->defaultFalse()->end()
->booleanNode(Request::METHOD_PUT)->defaultFalse()->end()
->end()
->end()
->arrayNode('roles')
->addDefaultsIfNotSet()
->info("The role require for each http method")
->children()
->scalarNode(Request::METHOD_GET)->defaultNull()->end()
->scalarNode(Request::METHOD_HEAD)->defaultNull()->end()
->scalarNode(Request::METHOD_POST)->defaultNull()->end()
->scalarNode(Request::METHOD_DELETE)->defaultNull()->end()
->scalarNode(Request::METHOD_PUT)->defaultNull()->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end() // end of root/children
->end() // end of root
;

View File

@@ -1,103 +0,0 @@
<?php
namespace Chill\MainBundle\Doctrine\Model;
use \JsonSerializable;
/**
* Description of Point
*
*/
class Point implements JsonSerializable {
private float $lat;
private float $lon;
public static string $SRID = '4326';
private function __construct(float $lon, float $lat)
{
$this->lat = $lat;
$this->lon = $lon;
}
public function toGeoJson(): string
{
$array = $this->toArrayGeoJson();
return \json_encode($array);
}
public function jsonSerialize(): array
{
return $this->toArrayGeoJson();
}
public function toArrayGeoJson(): array
{
return [
"type" => "Point",
"coordinates" => [ $this->lon, $this->lat ]
];
}
/**
*
* @return string
*/
public function toWKT(): string
{
return 'SRID='.self::$SRID.';POINT('.$this->lon.' '.$this->lat.')';
}
/**
*
* @param type $geojson
* @return Point
*/
public static function fromGeoJson(string $geojson): Point
{
$a = json_decode($geojson);
//check if the geojson string is correct
if (NULL === $a or !isset($a->type) or !isset($a->coordinates)){
throw PointException::badJsonString($geojson);
}
if ($a->type != 'Point'){
throw PointException::badGeoType();
}
$lat = $a->coordinates[1];
$lon = $a->coordinates[0];
return Point::fromLonLat($lon, $lat);
}
public static function fromLonLat(float $lon, float $lat): Point
{
if (($lon > -180 && $lon < 180) && ($lat > -90 && $lat < 90))
{
return new Point($lon, $lat);
} else {
throw PointException::badCoordinates($lon, $lat);
}
}
public static function fromArrayGeoJson(array $array): Point
{
if ($array['type'] == 'Point' &&
isset($array['coordinates']))
{
return self::fromLonLat($array['coordinates'][0], $array['coordinates'][1]);
}
}
public function getLat(): float
{
return $this->lat;
}
public function getLon(): float
{
return $this->lon;
}
}

View File

@@ -1,27 +0,0 @@
<?php
namespace Chill\MainBundle\Doctrine\Model;
use \Exception;
/**
* Description of PointException
*
*/
class PointException extends Exception {
public static function badCoordinates($lon, $lat): self
{
return new self("Input coordinates are not valid in the used coordinate system (longitude = $lon , latitude = $lat)");
}
public static function badJsonString($str): self
{
return new self("The JSON string is not valid: $str");
}
public static function badGeoType(): self
{
return new self("The geoJSON object type is not valid");
}
}

View File

@@ -1,75 +0,0 @@
<?php
namespace Chill\MainBundle\Doctrine\Type;
use Chill\MainBundle\Doctrine\Model\Point;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Chill\MainBundle\Doctrine\Model\PointException;
/**
* A Type for Doctrine to implement the Geography Point type
* implemented by Postgis on postgis+postgresql databases
*
*/
class PointType extends Type {
const POINT = 'point';
/**
*
* @param array $fieldDeclaration
* @param AbstractPlatform $platform
* @return type
*/
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return 'geometry(POINT,'.Point::$SRID.')';
}
/**
*
* @param type $value
* @param AbstractPlatform $platform
* @return Point
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === NULL){
return NULL;
} else {
return Point::fromGeoJson($value);
}
}
public function getName()
{
return self::POINT;
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value === NULL){
return NULL;
} else {
return $value->toWKT();
}
}
public function canRequireSQLConversion()
{
return true;
}
public function convertToPHPValueSQL($sqlExpr, $platform)
{
return 'ST_AsGeoJSON('.$sqlExpr.') ';
}
public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform)
{
return $sqlExpr;
}
}

View File

@@ -4,8 +4,6 @@ namespace Chill\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Doctrine\Model\Point;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
/**
* Address
@@ -30,14 +28,14 @@ class Address
*
* @ORM\Column(type="string", length=255)
*/
private $street = '';
private $streetAddress1 = '';
/**
* @var string
*
* @ORM\Column(type="string", length=255)
*/
private $streetNumber = '';
private $streetAddress2 = '';
/**
* @var PostalCode
@@ -45,56 +43,7 @@ class Address
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\PostalCode")
*/
private $postcode;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $floor;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $corridor;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $steps;
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $buildingName;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $flat;
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $distribution;
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $extra;
/**
* Indicates when the address starts validation. Used to build an history
* of address. By default, the current date.
@@ -104,55 +53,27 @@ class Address
* @ORM\Column(type="date")
*/
private $validFrom;
/**
* Indicates when the address ends. Used to build an history
* of address.
*
* @var \DateTime|null
*
* @ORM\Column(type="date", nullable=true)
*/
private $validTo;
/**
* True if the address is a "no address", aka homeless person, ...
*
* @var bool
*/
private $isNoAddress = false;
/**
* A geospatial field storing the coordinates of the Address
*
* @var Point|null
*
* @ORM\Column(type="point", nullable=true)
*/
private $point;
/**
* A ThirdParty reference for person's addresses that are linked to a third party
*
* @var ThirdParty|null
*
* @ORM\ManyToOne(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
* @ORM\JoinColumn(nullable=true)
*/
private $linkedToThirdParty;
/**
* A list of metadata, added by customizable fields
*
*
* @var array
*/
private $customs = [];
public function __construct()
{
$this->validFrom = new \DateTime();
}
/**
* Get id
*
@@ -164,7 +85,7 @@ class Address
}
/**
* Set streetAddress1 (legacy function)
* Set streetAddress1
*
* @param string $streetAddress1
*
@@ -172,23 +93,23 @@ class Address
*/
public function setStreetAddress1($streetAddress1)
{
$this->street = $streetAddress1 === NULL ? '' : $streetAddress1;
$this->streetAddress1 = $streetAddress1 === NULL ? '' : $streetAddress1;
return $this;
}
/**
* Get streetAddress1 (legacy function)
* Get streetAddress1
*
* @return string
*/
public function getStreetAddress1()
{
return $this->street;
return $this->streetAddress1;
}
/**
* Set streetAddress2 (legacy function)
* Set streetAddress2
*
* @param string $streetAddress2
*
@@ -196,19 +117,19 @@ class Address
*/
public function setStreetAddress2($streetAddress2)
{
$this->streetNumber = $streetAddress2 === NULL ? '' : $streetAddress2;
$this->streetAddress2 = $streetAddress2 === NULL ? '' : $streetAddress2;
return $this;
}
/**
* Get streetAddress2 (legacy function)
* Get streetAddress2
*
* @return string
*/
public function getStreetAddress2()
{
return $this->streetNumber;
return $this->streetAddress2;
}
/**
@@ -234,7 +155,7 @@ class Address
{
return $this->postcode;
}
/**
* @return \DateTime
*/
@@ -252,19 +173,19 @@ class Address
$this->validFrom = $validFrom;
return $this;
}
/**
* Get IsNoAddress
*
*
* Indicate true if the address is a fake address (homeless, ...)
*
*
* @return bool
*/
public function getIsNoAddress(): bool
{
return $this->isNoAddress;
}
/**
* @return bool
*/
@@ -275,9 +196,9 @@ class Address
/**
* Set IsNoAddress
*
*
* Indicate true if the address is a fake address (homeless, ...)
*
*
* @param bool $isNoAddress
* @return $this
*/
@@ -286,10 +207,10 @@ class Address
$this->isNoAddress = $isNoAddress;
return $this;
}
/**
* Get customs informations in the address
*
*
* @return array
*/
public function getCustoms(): array
@@ -299,27 +220,27 @@ class Address
/**
* Store custom informations in the address
*
*
* @param array $customs
* @return $this
*/
public function setCustoms(array $customs): self
{
$this->customs = $customs;
return $this;
}
/**
* Validate the address.
*
*
* Check that:
*
*
* * if the address is not home address:
* * the postal code is present
* * the valid from is not null
* * the address street 1 is greater than 2
*
*
* @param ExecutionContextInterface $context
* @param array $payload
*/
@@ -331,18 +252,18 @@ class Address
->atPath('validFrom')
->addViolation();
}
if ($this->isNoAddress()) {
return;
}
if (empty($this->getStreetAddress1())) {
$context
->buildViolation("address.street1-should-be-set")
->atPath('streetAddress1')
->addViolation();
}
if (!$this->getPostcode() instanceof PostalCode) {
$context
->buildViolation("address.postcode-should-be-set")
@@ -350,7 +271,7 @@ class Address
->addViolation();
}
}
/**
* @param Address $original
* @return Address
@@ -365,149 +286,5 @@ class Address
;
}
public function getStreet(): ?string
{
return $this->street;
}
public function setStreet(string $street): self
{
$this->street = $street;
return $this;
}
public function getStreetNumber(): ?string
{
return $this->streetNumber;
}
public function setStreetNumber(string $streetNumber): self
{
$this->streetNumber = $streetNumber;
return $this;
}
public function getFloor(): ?string
{
return $this->floor;
}
public function setFloor(?string $floor): self
{
$this->floor = $floor;
return $this;
}
public function getCorridor(): ?string
{
return $this->corridor;
}
public function setCorridor(?string $corridor): self
{
$this->corridor = $corridor;
return $this;
}
public function getSteps(): ?string
{
return $this->steps;
}
public function setSteps(?string $steps): self
{
$this->steps = $steps;
return $this;
}
public function getBuildingName(): ?string
{
return $this->buildingName;
}
public function setBuildingName(?string $buildingName): self
{
$this->buildingName = $buildingName;
return $this;
}
public function getFlat(): ?string
{
return $this->flat;
}
public function setFlat(?string $flat): self
{
$this->flat = $flat;
return $this;
}
public function getDistribution(): ?string
{
return $this->distribution;
}
public function setDistribution(?string $distribution): self
{
$this->distribution = $distribution;
return $this;
}
public function getExtra(): ?string
{
return $this->extra;
}
public function setExtra(?string $extra): self
{
$this->extra = $extra;
return $this;
}
public function getValidTo(): ?\DateTimeInterface
{
return $this->validTo;
}
public function setValidTo(\DateTimeInterface $validTo): self
{
$this->validTo = $validTo;
return $this;
}
public function getPoint(): ?Point
{
return $this->point;
}
public function setPoint(?Point $point): self
{
$this->point = $point;
return $this;
}
public function getLinkedToThirdParty()
{
return $this->linkedToThirdParty;
}
public function setLinkedToThirdParty($linkedToThirdParty): self
{
$this->linkedToThirdParty = $linkedToThirdParty;
return $this;
}
}

View File

@@ -1,165 +0,0 @@
<?php
namespace Chill\MainBundle\Entity;
use Chill\MainBundle\Entity\AddressReferenceRepository;
use Doctrine\ORM\Mapping as ORM;
use Chill\MainBundle\Doctrine\Model\Point;
/**
* @ORM\Entity(repositoryClass=AddressReferenceRepository::class)
* @ORM\Table(name="chill_main_address_reference")
* @ORM\HasLifecycleCallbacks()
*/
class AddressReference
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $refId;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $street;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $streetNumber;
/**
* @var PostalCode
*
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\PostalCode")
*/
private $postcode;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $municipalityCode;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $source;
/**
* A geospatial field storing the coordinates of the Address
*
* @var Point
*
* @ORM\Column(type="point")
*/
private $point;
public function getId(): ?int
{
return $this->id;
}
public function getRefId(): ?string
{
return $this->refId;
}
public function setRefId(string $refId): self
{
$this->refId = $refId;
return $this;
}
public function getStreet(): ?string
{
return $this->street;
}
public function setStreet(?string $street): self
{
$this->street = $street;
return $this;
}
public function getStreetNumber(): ?string
{
return $this->streetNumber;
}
public function setStreetNumber(?string $streetNumber): self
{
$this->streetNumber = $streetNumber;
return $this;
}
/**
* Set postcode
*
* @param PostalCode $postcode
*
* @return Address
*/
public function setPostcode(PostalCode $postcode = null)
{
$this->postcode = $postcode;
return $this;
}
/**
* Get postcode
*
* @return PostalCode
*/
public function getPostcode()
{
return $this->postcode;
}
public function getMunicipalityCode(): ?string
{
return $this->municipalityCode;
}
public function setMunicipalityCode(?string $municipalityCode): self
{
$this->municipalityCode = $municipalityCode;
return $this;
}
public function getSource(): ?string
{
return $this->source;
}
public function setSource(?string $source): self
{
$this->source = $source;
return $this;
}
public function getPoint(): ?Point
{
return $this->point;
}
public function setPoint(?Point $point): self
{
$this->point = $point;
return $this;
}
}

View File

@@ -33,8 +33,8 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
* A type to create/update Address entity
*
* Options:
*
* - `has_valid_from` (boolean): show if an entry "has valid from" must be
*
* - `has_valid_from` (boolean): show if an entry "has valid from" must be
* shown.
* - `null_if_empty` (boolean): replace the address type by null if the street
* or the postCode is empty. This is useful when the address is not required and
@@ -45,10 +45,10 @@ class AddressType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('street', TextType::class, array(
->add('streetAddress1', TextType::class, array(
'required' => !$options['has_no_address'] // true if has no address is false
))
->add('streetNumber', TextType::class, array(
->add('streetAddress2', TextType::class, array(
'required' => false
))
->add('postCode', PostalCodeType::class, array(
@@ -57,7 +57,7 @@ class AddressType extends AbstractType
'required' => !$options['has_no_address'] // true if has no address is false
))
;
if ($options['has_valid_from']) {
$builder
->add('validFrom', DateType::class, array(
@@ -67,7 +67,7 @@ class AddressType extends AbstractType
)
);
}
if ($options['has_no_address']) {
$builder
->add('isNoAddress', ChoiceType::class, [
@@ -79,12 +79,12 @@ class AddressType extends AbstractType
'label' => 'address.address_homeless'
]);
}
if ($options['null_if_empty'] === TRUE) {
$builder->setDataMapper(new AddressDataMapper());
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver

View File

@@ -27,7 +27,6 @@ use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Form\Type\DataTransformer\ObjectToIdTransformer;
use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
/**
* Extends choice to allow adding select2 library on widget
@@ -42,26 +41,15 @@ class Select2CountryType extends AbstractType
*/
private $requestStack;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
* @var ObjectManager
*/
private $em;
public function __construct(
RequestStack $requestStack,
ObjectManager $em,
TranslatableStringHelper $translatableStringHelper
)
public function __construct(RequestStack $requestStack,ObjectManager $em)
{
$this->requestStack = $requestStack;
$this->em = $em;
$this->translatableStringHelper = $translatableStringHelper;
}
public function getBlockPrefix()
@@ -87,7 +75,7 @@ class Select2CountryType extends AbstractType
$choices = array();
foreach ($countries as $c) {
$choices[$c->getId()] = $this->translatableStringHelper->localize($c->getName());
$choices[$c->getId()] = $c->getName()[$locale];
}
asort($choices, SORT_STRING | SORT_FLAG_CASE);

View File

@@ -27,7 +27,6 @@ use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Form\Type\DataTransformer\MultipleObjectsToIdTransformer;
use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
/**
* Extends choice to allow adding select2 library on widget for languages (multiple)
@@ -44,21 +43,10 @@ class Select2LanguageType extends AbstractType
*/
private $em;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
public function __construct(
RequestStack $requestStack,
ObjectManager $em,
TranslatableStringHelper $translatableStringHelper
)
public function __construct(RequestStack $requestStack,ObjectManager $em)
{
$this->requestStack = $requestStack;
$this->em = $em;
$this->translatableStringHelper = $translatableStringHelper;
}
public function getBlockPrefix()
@@ -84,7 +72,7 @@ class Select2LanguageType extends AbstractType
$choices = array();
foreach ($languages as $l) {
$choices[$l->getId()] = $this->translatableStringHelper->localize($l->getName());
$choices[$l->getId()] = $l->getName()[$locale];
}
asort($choices, SORT_STRING | SORT_FLAG_CASE);

View File

@@ -104,7 +104,7 @@ class Mailer
* @param \User $to
* @param array $subject Subject of the message [ 0 => $message (required), 1 => $parameters (optional), 3 => $domain (optional) ]
* @param array $bodies The bodies. An array where keys are the contentType and values the bodies
* @param callable $callback a callback to customize the message (add attachment, etc.)
* @param \callable $callback a callback to customize the message (add attachment, etc.)
*/
public function sendNotification(
$recipient,

View File

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

View File

@@ -28,7 +28,7 @@
// @import "bootstrap/scss/card";
// @import "bootstrap/scss/breadcrumb";
// @import "bootstrap/scss/pagination";
@import "bootstrap/scss/badge";
// @import "bootstrap/scss/badge";
// @import "bootstrap/scss/jumbotron";
// @import "bootstrap/scss/alert";
// @import "bootstrap/scss/progress";
@@ -41,7 +41,7 @@
// @import "bootstrap/scss/popover";
// @import "bootstrap/scss/carousel";
// @import "bootstrap/scss/spinners";
@import "bootstrap/scss/utilities";
// @import "bootstrap/scss/utilities";
// @import "bootstrap/scss/print";
@import "custom";

View File

@@ -5,7 +5,7 @@
@include button($green, $white);
}
&.bt-reset, &.bt-delete, &.bt-remove {
&.bt-reset, &.bt-delete {
@include button($red, $white);
}
@@ -24,7 +24,6 @@
&.bt-save::before,
&.bt-new::before,
&.bt-delete::before,
&.bt-remove::before,
&.bt-update::before,
&.bt-edit::before,
&.bt-cancel::before,
@@ -57,12 +56,7 @@
// add a trash
content: "";
}
&.bt-remove::before {
// add a times
content: "";
}
&.bt-edit::before, &.bt-update::before {
// add a pencil
content: "";
@@ -100,7 +94,6 @@
&.bt-save::before,
&.bt-new::before,
&.bt-delete::before,
&.bt-remove::before,
&.bt-update::before,
&.bt-edit::before,
&.bt-cancel::before,
@@ -130,7 +123,6 @@
&.bt-save::before,
&.bt-new::before,
&.bt-delete::before,
&.bt-remove::before,
&.bt-update::before,
&.bt-edit::before,
&.bt-cancel::before,

View File

@@ -39,8 +39,6 @@ div.subheader {
height: 130px;
}
//// VUEJS ////
div.vue-component {
padding: 1.5em;
margin: 2em 0;
@@ -57,97 +55,3 @@ div.vue-component {
}
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;
}
}
}
}
.discret {
color: grey;
margin-right: 1em;
}

View File

@@ -1,45 +0,0 @@
<template>
<transition name="modal">
<div class="modal-mask">
<!-- :: styles bootstrap :: -->
<div class="modal-dialog" :class="modalDialogClass">
<div class="modal-content">
<div class="modal-header">
<slot name="header"></slot>
<button class="close sc-button grey" @click="$emit('close')">
<i class="fa fa-times" aria-hidden="true"></i></button>
</div>
<div class="modal-body up" style="overflow-y: unset;">
<slot name="body-fixed"></slot>
</div>
<div class="modal-body">
<slot name="body"></slot>
</div>
<div class="modal-footer">
<button class="sc-button cancel" @click="$emit('close')">{{ $t('action.close') }}</button>
<slot name="footer"></slot>
</div>
</div>
</div>
<!-- :: end styles bootstrap :: -->
</div>
</transition>
</template>
<script>
/*
* This Modal component is a mix between :
* - Vue3 modal implementation
* => with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden
* => with slot we can pass content from parent component
* => some classes are passed from parent component
* - Bootstrap 4.6 _modal.scss module
* => using bootstrap css classes, the modal have a responsive behaviour,
* => modal design can be configured using css classes (size, scroll)
*/
export default {
name: 'Modal',
props: ['modalDialogClass'],
emits: ['close']
}
</script>

View File

@@ -1,55 +0,0 @@
import { createI18n } from 'vue-i18n'
const datetimeFormats = {
fr: {
short: {
year: "numeric",
month: "numeric",
day: "numeric"
},
long: {
year: "numeric",
month: "short",
day: "numeric",
weekday: "short",
hour: "numeric",
minute: "numeric",
hour12: false
}
}
};
const messages = {
fr: {
action: {
actions: "Actions",
show: "Voir",
edit: "Modifier",
create: "Créer",
remove: "Enlever",
delete: "Supprimer",
save: "Enregistrer",
add: "Ajouter",
show_modal: "Ouvrir une modale",
ok: "OK",
cancel: "Annuler",
close: "Fermer",
next: "Suivant",
previous: "Précédent",
back: "Retour",
check_all: "cocher tout",
reset: "réinitialiser"
},
}
};
const _createI18n = (appMessages) => {
Object.assign(messages.fr, appMessages.fr);
return createI18n({
locale: 'fr',
fallbackLocale: 'fr',
datetimeFormats,
messages,
})
};
export { _createI18n }

View File

@@ -6,8 +6,8 @@
<div class="chill_address_is_noaddress">{{ 'address.consider homeless'|trans }}</div>
{% endif %}
<div class="chill_address_address">
{% if address.street is not empty %}<p class="street street1">{{ address.street }}</p>{% endif %}
{% if address.streetNumber is not empty %}<p class="street street2">{{ address.streetNumber }}</p>{% endif %}
{% if address.streetAddress1 is not empty %}<p class="street street1">{{ address.streetAddress1 }}</p>{% endif %}
{% if address.streetAddress2 is not empty %}<p class="street street2">{{ address.streetAddress2 }}</p>{% endif %}
{% if address.postCode is not empty %}
<p class="postalCode"><span class="code">{{ address.postCode.code }}</span> <span class="name">{{ address.postCode.name }}</span></p>
<p class="country">{{ address.postCode.country.name|localize_translatable_string }}</p>

View File

@@ -204,7 +204,7 @@ class SearchProvider
}
public function getResultByName($pattern, $name, $start = 0, $limit = 50,
array $options = array(), $format = 'html')
array $options = array(), $format)
{
$terms = $this->parse($pattern);
$search = $this->getByName($name);

View File

@@ -1,28 +0,0 @@
<?php
namespace Chill\MainBundle\Serializer\Model;
use Chill\MainBundle\Pagination\PaginatorInterface;
class Collection
{
private PaginatorInterface $paginator;
private $items;
public function __construct($items, PaginatorInterface $paginator)
{
$this->items = $items;
$this->paginator = $paginator;
}
public function getPaginator(): PaginatorInterface
{
return $this->paginator;
}
public function getItems()
{
return $this->items;
}
}

View File

@@ -1,44 +0,0 @@
<?php
/*
*
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Serializer\Normalizer;
use Chill\MainBundle\Entity\Center;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
*
*
*/
class CenterNormalizer implements NormalizerInterface
{
public function normalize($center, string $format = null, array $context = array())
{
/** @var Center $center */
return [
'id' => $center->getId(),
'name' => $center->getName()
];
}
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof Center;
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace Chill\MainBundle\Serializer\Normalizer;
use Chill\MainBundle\Serializer\Model\Collection;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
class CollectionNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof Collection;
}
public function normalize($collection, string $format = null, array $context = [])
{
/** @var $collection Collection */
/** @var $collection Chill\MainBundle\Pagination\PaginatorInterface */
$paginator = $collection->getPaginator();
$data['count'] = $paginator->getTotalItems();
$pagination['first'] = $paginator->getCurrentPageFirstItemNumber();
$pagination['items_per_page'] = $paginator->getItemsPerPage();
$pagination['next'] = $paginator->hasNextPage() ?
$paginator->getNextPage()->generateUrl() : null;
$pagination['previous'] = $paginator->hasPreviousPage() ?
$paginator->getPreviousPage()->generateUrl() : null;
$pagination['more'] = $paginator->hasNextPage();
$data['pagination'] = $pagination;
// normalize results
$data['results'] = $this->normalizer->normalize($collection->getItems(),
$format, $context);
return $data;
}
}

View File

@@ -1,39 +0,0 @@
<?php
/*
*
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Serializer\Normalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class DateNormalizer implements NormalizerInterface
{
public function normalize($date, string $format = null, array $context = array())
{
/** @var \DateTimeInterface $date */
return [
'datetime' => $date->format(\DateTimeInterface::ISO8601),
'u' => $date->getTimestamp()
];
}
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof \DateTimeInterface;
}
}

View File

@@ -1,44 +0,0 @@
<?php
/*
*
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Serializer\Normalizer;
use Chill\MainBundle\Entity\User;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
*
*
*/
class UserNormalizer implements NormalizerInterface
{
public function normalize($user, string $format = null, array $context = array())
{
/** @var User $user */
return [
'id' => $user->getId(),
'username' => $user->getUsername()
];
}
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof User;
}
}

View File

@@ -34,7 +34,7 @@ trait PrepareClientTrait
* @return \Symfony\Component\BrowserKit\Client
* @throws \LogicException
*/
public function getClientAuthenticated(
public function getClient(
$username = 'center a_social',
$password = 'password'
) {

View File

@@ -0,0 +1,13 @@
<?php
namespace Chill\MainBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DefaultControllerTest extends WebTestCase
{
public function testIndex()
{
}
}

View File

@@ -9,7 +9,15 @@ class LoginControllerTest extends WebTestCase
{
public function testLogin()
{
$client = static::createClient();
$client = static::createClient(array(
'framework' => array(
'default_locale' => 'en',
'translator' => array(
'fallback' => 'en'
)
),
));
//load login page and submit form
$crawler = $client->request('GET', '/login');
@@ -34,17 +42,17 @@ class LoginControllerTest extends WebTestCase
//on the home page, there must be a logout link
$client->followRedirects(true);
$crawler = $client->request('GET', '/');
$this->assertRegExp('/center a_social/', $client->getResponse()
->getContent());
$logoutLinkFilter = $crawler->filter('a:contains("Se déconnecter")');
$logoutLinkFilter = $crawler->filter('a:contains("Logout")');
//check there is > 0 logout link
$this->assertGreaterThan(0, $logoutLinkFilter->count(), 'check that a logout link is present');
//click on logout link
$client->followRedirects(false);
$client->click($crawler->selectLink('Se déconnecter')->link());
$client->click($crawler->selectLink('Logout')->link());
$this->assertTrue($client->getResponse()->isRedirect());
$client->followRedirect(); #redirect to login page

View File

@@ -32,7 +32,21 @@ use Chill\MainBundle\Search\SearchInterface;
*/
class SearchControllerTest extends WebTestCase
{
/*
public function setUp()
{
static::bootKernel();
//add a default service
$this->addSearchService(
$this->createDefaultSearchService('<p>I am default</p>', 10), 'default'
);
//add a domain service
$this->addSearchService(
$this->createDefaultSearchService('<p>I am domain bar</p>', 20), 'bar'
);
}
/**
* Test the behaviour when no domain is provided in the search pattern :
* the default search should be enabled
@@ -91,6 +105,29 @@ class SearchControllerTest extends WebTestCase
$this->assertTrue($client->getResponse()->isNotFound());
}
public function testSearchWithinSpecificSearchName()
{
/*
//add a search service which will be supported
$this->addSearchService(
$this->createNonDefaultDomainSearchService("<p>I am domain foo</p>", 100, TRUE), 'foo'
);
$client = $this->getAuthenticatedClient();
$crawler = $client->request('GET', '/fr/search',
array('q' => '@foo default search', 'name' => 'foo'));
//$this->markTestSkipped();
$this->assertEquals(0, $crawler->filter('p:contains("I am default")')->count(),
"The mocked default results are not shown");
$this->assertEquals(0, $crawler->filter('p:contains("I am domain bar")')->count(),
"The mocked non-default results are not shown");
$this->assertEquals(1, $crawler->filter('p:contains("I am domain foo")')->count(),
"The mocked nnon default results for foo are shown");
*/
}
private function getAuthenticatedClient()
{
return static::createClient(array(), array(

View File

@@ -37,12 +37,11 @@ class UserControllerTest extends WebTestCase
$username = 'Test_user'. uniqid();
$password = 'Password1234!';
dump($crawler->text());
// Fill in the form and submit it
$form = $crawler->selectButton('Créer')->form(array(
'chill_mainbundle_user[username]' => $username,
'chill_mainbundle_user[plainPassword][first]' => $password,
'chill_mainbundle_user[plainPassword][second]' => $password
'chill_mainbundle_user[plainPassword][password][first]' => $password,
'chill_mainbundle_user[plainPassword][password][second]' => $password
));
$this->client->submit($form);
@@ -120,8 +119,8 @@ class UserControllerTest extends WebTestCase
$crawler = $this->client->click($link);
$form = $crawler->selectButton('Changer le mot de passe')->form(array(
'chill_mainbundle_user_password[new_password][first]' => $newPassword,
'chill_mainbundle_user_password[new_password][second]' => $newPassword,
'chill_mainbundle_user_password[password][first]' => $newPassword,
'chill_mainbundle_user_password[password][second]' => $newPassword,
));
$this->client->submit($form);

View File

@@ -0,0 +1,179 @@
<?php
/*
* Chill is a software for social workers
* Copyright (C) 2015 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\MainBundle\Tests\DependencyInjection;
use Chill\MainBundle\DependencyInjection\ConfigConsistencyCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilderInterface;
/**
* Description of ConfigConsistencyCompilerPassTest
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ConfigConsistencyCompilerPassTest extends \PHPUnit\Framework\TestCase
{
/**
*
*
* @var \Chill\MainBundle\DependencyInjection\ConfigConsistencyCompilerPass
*/
private $configConsistencyCompilerPass;
public function setUp()
{
$this->configConsistencyCompilerPass = new ConfigConsistencyCompilerPass();
}
/**
* Test that everything is fine is configuration is correct
*
*/
public function testLanguagesArePresent()
{
try {
$this ->configConsistencyCompilerPass
->process(
$this->mockContainer(
$this->mockTranslatorDefinition(array('fr')),
array('fr', 'nl')
)
);
$this->assertTrue(TRUE, 'the config consistency can process');
} catch (\Exception $ex) {
$this->assertTrue(FALSE,
'the config consistency can process');
}
}
/**
* Test that everything is fine is configuration is correct
* if multiple fallback languages are present
*
*/
public function testMultiplesLanguagesArePresent()
{
try {
$this ->configConsistencyCompilerPass
->process(
$this->mockContainer(
$this->mockTranslatorDefinition(array('fr', 'nl')),
array('fr', 'nl', 'en')
)
);
$this->assertTrue(TRUE, 'the config consistency can process');
} catch (\Exception $ex) {
$this->assertTrue(FALSE,
'the config consistency can process');
}
}
/**
* Test that a runtime exception is throw if the available language does
* not contains the fallback locale
*
* @expectedException \RuntimeException
* @expectedExceptionMessageRegExp /The chill_main.available_languages parameter does not contains fallback locales./
*/
public function testLanguageNotPresent()
{
$container = $this->mockContainer(
$this->mockTranslatorDefinition(array('en')), array('fr')
);
$this->configConsistencyCompilerPass->process($container);
}
/**
* Test that a logic exception is thrown if the setFallbackLocale
* method is not defined in translator definition
*
* @expectedException \LogicException
*/
public function testSetFallbackNotDefined()
{
$container = $this->mockContainer(
$this->mockTranslatorDefinition(NULL), array('fr')
);
$this->configConsistencyCompilerPass->process($container);
}
/**
* @return ContainerBuilder
*/
private function mockContainer($definition, $availableLanguages)
{
$container = $this
->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')
->getMock();
$container->method('getParameter')
->will($this->returnCallback(
function($parameter) use ($availableLanguages) {
if ($parameter === 'chill_main.available_languages') {
return $availableLanguages;
} else {
throw new \LogicException("the parameter '$parameter' "
. "is not defined in stub test");
}
}
));
$container->method('findDefinition')
->will($this->returnCallback(
function($id) use ($definition) {
if (in_array($id, array('translator', 'translator.default'))) {
return $definition;
} else {
throw new \LogicException("the id $id is not defined in test");
}
}));
return $container;
}
/**
*
* @param type $languages
* @return 'Symfony\Component\DependencyInjection\Definition'
*/
private function mockTranslatorDefinition(array $languages = NULL)
{
$definition = $this
->getMockBuilder('Symfony\Component\DependencyInjection\Definition')
->getMock();
if (NULL !== $languages) {
$definition->method('getMethodCalls')
->willReturn(array(
['setFallbackLocales', array($languages)]
));
} else {
$definition->method('getMethodCalls')
->willReturn(array(['nothing', array()]));
}
return $definition;
}
}

View File

@@ -1,119 +0,0 @@
<?php
namespace Chill\MainBundle\Tests\Doctrine\Model;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Chill\MainBundle\Doctrine\Model\Point;
/**
* Test the point model methods
*
* @author Julien Minet <julien.minet@champs-libres.coop>
*/
class ExportControllerTest extends KernelTestCase
{
public function testToWKT()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);
$this->assertEquals($point->toWKT(),'SRID=4326;POINT(4.8634 50.47382)');
}
public function testToGeojson()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);
$this->assertEquals($point->toGeoJson(),'{"type":"Point","coordinates":[4.8634,50.47382]}');
}
public function testToArrayGeoJson()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);
$this->assertEquals(
$point->toArrayGeoJson(),
[
'type' => 'Point',
'coordinates' => [4.8634, 50.47382]
]
);
}
public function testJsonSerialize()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);
$this->assertEquals(
$point->jsonSerialize(),
[
'type' => 'Point',
'coordinates' => [4.8634, 50.47382]
]
);
}
public function testFromGeoJson()
{
$geojson = '{"type":"Point","coordinates":[4.8634,50.47382]}';
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($point, Point::fromGeoJson($geojson));
}
public function testFromLonLat()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($point, Point::fromLonLat($lon, $lat));
}
public function testFromArrayGeoJson()
{
$array = [
'type' => 'Point',
'coordinates' => [4.8634, 50.47382]
];
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($point, Point::fromArrayGeoJson($array));
}
public function testGetLat()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($lat, $point->getLat());
}
public function testGetLon()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($lon, $point->getLon());
}
private function preparePoint($lon, $lat)
{
return Point::fromLonLat($lon, $lat);
}
}

View File

@@ -45,6 +45,11 @@ class ExportManagerTest extends KernelTestCase
use \Chill\MainBundle\Test\PrepareUserTrait;
use \Chill\MainBundle\Test\PrepareScopeTrait;
/**
*
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;
/**
*
@@ -60,6 +65,8 @@ class ExportManagerTest extends KernelTestCase
{
self::bootKernel();
$this->container = self::$kernel->getContainer();
$this->prophet = new \Prophecy\Prophet;
}
@@ -90,7 +97,7 @@ class ExportManagerTest extends KernelTestCase
\Symfony\Component\Security\Core\User\UserInterface $user = null
)
{
$localUser = $user === NULL ? self::$container->get('doctrine.orm.entity_manager')
$localUser = $user === NULL ? $this->container->get('doctrine.orm.entity_manager')
->getRepository('ChillMainBundle:User')
->findOneBy(array('username' => 'center a_social')) :
$user;
@@ -99,10 +106,10 @@ class ExportManagerTest extends KernelTestCase
$tokenStorage->setToken($token);
return new ExportManager(
$logger === NULL ? self::$container->get('logger') : $logger,
$em === NULL ? self::$container->get('doctrine.orm.entity_manager') : $em,
$authorizationChecker === NULL ? self::$container->get('security.authorization_checker') : $authorizationChecker,
$authorizationHelper === NULL ? self::$container->get('chill.main.security.authorization.helper') : $authorizationHelper,
$logger === NULL ? $this->container->get('logger') : $logger,
$em === NULL ? $this->container->get('doctrine.orm.entity_manager') : $em,
$authorizationChecker === NULL ? $this->container->get('security.authorization_checker') : $authorizationChecker,
$authorizationHelper === NULL ? $this->container->get('chill.main.security.authorization.helper') : $authorizationHelper,
$tokenStorage)
;
}
@@ -537,7 +544,7 @@ class ExportManagerTest extends KernelTestCase
$export = $this->prophet->prophesize();
$export->willImplement(ExportInterface::class);
$em = self::$container->get('doctrine.orm.entity_manager');
$em = $this->container->get('doctrine.orm.entity_manager');
$export->initiateQuery(
Argument::is(array('foo')),
Argument::Type('array'),
@@ -620,13 +627,12 @@ class ExportManagerTest extends KernelTestCase
//add formatter interface
$formatter = new \Chill\MainBundle\Export\Formatter\SpreadSheetFormatter(
self::$container->get('translator'), $exportManager);
$this->container->get('translator'), $exportManager);
$exportManager->addFormatter($formatter, 'spreadsheet');
//ob_start();
$response = $exportManager->generate(
'dummy',
array($center),
$response = $exportManager->generate('dummy',
array(PickCenterType::CENTERS_IDENTIFIERS => array($center)),
array(
ExportType::FILTER_KEY => array(
'filter_foo' => array(

View File

@@ -54,20 +54,13 @@ class PageTest extends KernelTestCase
$number = 1,
$itemPerPage = 10,
$route = 'route',
array $routeParameters = array(),
$totalItems = 100
array $routeParameters = array()
) {
$urlGenerator = $this->prophet->prophesize();
$urlGenerator->willImplement(UrlGeneratorInterface::class);
return new Page(
$number,
$itemPerPage,
$urlGenerator->reveal(),
$route,
$routeParameters,
$totalItems
);
return new Page($number, $itemPerPage, $urlGenerator->reveal(), $route,
$routeParameters);
}
public function testPageNumber() {

View File

@@ -21,10 +21,9 @@ namespace Chill\MainBundle\Test\Search;
use Chill\MainBundle\Search\SearchProvider;
use Chill\MainBundle\Search\SearchInterface;
use PHPUnit\Framework\TestCase;
class SearchProviderTest extends TestCase
class SearchProviderTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -312,4 +311,4 @@ class SearchProviderTest extends TestCase
return $mock;
}
}
}

View File

@@ -50,7 +50,7 @@ class AuthorizationHelperTest extends KernelTestCase
*/
private function getAuthorizationHelper()
{
return static::$container
return static::$kernel->getContainer()
->get('chill.main.security.authorization.helper')
;
}

View File

@@ -39,7 +39,8 @@ class TokenManagerTest extends KernelTestCase
{
self::bootKernel();
$logger = self::$container
$logger = self::$kernel
->getContainer()
->get('logger');
$this->tokenManager = new TokenManager('secret', $logger);

View File

@@ -36,19 +36,53 @@ class ChillMenuTwigFunctionTest extends KernelTestCase
public static function setUpBeforeClass()
{
self::bootKernel(array('environment' => 'test'));
static::$templating = static::$container
->get('templating');
$pathToBundle = static::$container->getParameter('kernel.bundles_metadata')['ChillMainBundle']['path'];
static::$templating = static::$kernel
->getContainer()->get('templating');
//load templates in Tests/Resources/views
static::$container->get('twig.loader')
->addPath($pathToBundle.'/Resources/test/views/', $namespace = 'tests');
static::$kernel->getContainer()->get('twig.loader')
->addPath(static::$kernel->getContainer()->getParameter('kernel.root_dir')
.'/Resources/views/', $namespace = 'tests');
}
public function testNormalMenu()
{
$content = static::$templating->render('@tests/menus/normalMenu.html.twig');
$this->assertContains('ul', $content,
"test that the file contains an ul tag"
);
$crawler = new Crawler($content);
$ul = $crawler->filter('ul')->getNode(0);
$this->assertEquals( 'ul', $ul->tagName);
$lis = $crawler->filter('ul')->children();
$this->assertEquals(3, count($lis));
$lis->each(function(Crawler $node, $i) {
$this->assertEquals('li', $node->getNode(0)->tagName);
$a = $node->children()->getNode(0);
$this->assertEquals('a', $a->tagName);
switch($i) {
case 0:
$this->assertEquals('/dummy?param=fake', $a->getAttribute('href'));
$this->assertEquals('active', $a->getAttribute('class'));
$this->assertEquals('test0', $a->nodeValue);
break;
case 1:
$this->assertEquals('/dummy1?param=fake', $a->getAttribute('href'));
$this->assertEmpty($a->getAttribute('class'));
$this->assertEquals('test1', $a->nodeValue);
break;
case 3:
$this->assertEquals('/dummy2/fake', $a->getAttribute('href'));
$this->assertEmpty($a->getAttribute('class'));
$this->assertEquals('test2', $a->nodeValue);
}
});
}
public function testMenuOverrideTemplate()
{
$this->markTestSkipped("this hacks seems not working now");
$content = static::$templating->render('@tests/menus/overrideTemplate.html.twig');
$this->assertEquals('fake template', $content);
}
}

View File

@@ -28,7 +28,7 @@ class MenuComposerTest extends KernelTestCase
public function setUp()
{
self::bootKernel(array('environment' => 'test'));
$this->menuComposer = static::$container
$this->menuComposer = static::$kernel->getContainer()
->get('chill.main.menu_composer');
}
@@ -42,5 +42,50 @@ class MenuComposerTest extends KernelTestCase
$routes = $this->menuComposer->getRoutesFor('dummy0');
$this->assertInternalType('array', $routes);
$this->assertCount(3, $routes);
//check that the keys are sorted
$orders = array_keys($routes);
foreach ($orders as $key => $order){
if (array_key_exists($key + 1, $orders)) {
$this->assertGreaterThan($order, $orders[$key + 1],
'Failing to assert that routes are ordered');
}
}
//check that the array are identical, order is not important :
$expected = array(
50 => array(
'key' => 'chill_main_dummy_0',
'label' => 'test0',
'otherkey' => 'othervalue'
),
51 => array(
'key' => 'chill_main_dummy_1',
'label' => 'test1',
'helper'=> 'great helper'
),
52 => array(
'key' => 'chill_main_dummy_2',
'label' => 'test2'
));
foreach ($expected as $order => $route ){
}
//compare arrays
foreach($expected as $order => $route) {
//check the key are the one expected
$this->assertTrue(isset($routes[$order]));
if (isset($routes[$order])){ #avoid an exception if routes with order does not exists
//sort arrays. Order matters for phpunit::assertSame
ksort($route);
ksort($routes[$order]);
$this->assertSame($route, $routes[$order]);
}
}
}
}

View File

@@ -86,3 +86,7 @@ login_check:
logout:
path: /logout
chill_main_test:
path: /{_locale}/main/test
controller: Chill\MainBundle\Controller\DefaultController::testAction

View File

@@ -1,8 +1,7 @@
services:
Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader:
arguments:
$crudConfig: '%chill_main_crud_route_loader_config%'
$apiConfig: '%chill_main_api_route_loader_config%'
$config: '%chill_main_crud_route_loader_config%'
tags: [ routing.loader ]
Chill\MainBundle\CRUD\Resolver\Resolver:
@@ -14,4 +13,4 @@ services:
arguments:
$resolver: '@Chill\MainBundle\CRUD\Resolver\Resolver'
tags:
- { name: twig.extension }
- { name: twig.extension }

View File

@@ -23,7 +23,6 @@ services:
arguments:
- "@request_stack"
- "@doctrine.orm.entity_manager"
- "@chill.main.helper.translatable_string"
tags:
- { name: form.type, alias: select2_chill_country }
@@ -32,7 +31,6 @@ services:
arguments:
- "@request_stack"
- "@doctrine.orm.entity_manager"
- "@chill.main.helper.translatable_string"
tags:
- { name: form.type, alias: select2_chill_language }

View File

@@ -6,7 +6,6 @@ services:
- "@request_stack"
- "@router"
- "%chill_main.pagination.item_per_page%"
Chill\MainBundle\Pagination\PaginatorFactory: '@chill_main.paginator_factory'
chill_main.paginator.twig_extensions:

View File

@@ -1,17 +0,0 @@
---
services:
Chill\MainBundle\Serializer\Normalizer\CenterNormalizer:
tags:
- { name: 'serializer.normalizer', priority: 64 }
Chill\MainBundle\Serializer\Normalizer\DateNormalizer:
tags:
- { name: 'serializer.normalizer', priority: 64 }
Chill\MainBundle\Serializer\Normalizer\UserNormalizer:
tags:
- { name: 'serializer.normalizer', priority: 64 }
Chill\MainBundle\Serializer\Normalizer\CollectionNormalizer:
tags:
- { name: 'serializer.normalizer', priority: 64 }

View File

@@ -1,32 +0,0 @@
<?php declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Create the postgis extension
*/
final class Version20210414091001 extends AbstractMigration
{
public function up(Schema $schema) : void
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql('CREATE EXTENSION IF NOT EXISTS postgis;');
}
public function down(Schema $schema) : void
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql('DROP EXTENSION IF NOT EXISTS postgis;');
}
public function getDescription(): string
{
return "Enable the postgis extension in public schema";
}
}

View File

@@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add new fields to address, including a Point geometry field.
*/
final class Version20210420115006 extends AbstractMigration
{
public function getDescription() : string
{
return 'Add a Point data type and modify the Address entity';
}
public function up(Schema $schema) : void
{
$this->addSql('ALTER TABLE chill_main_address RENAME COLUMN streetaddress1 TO street;');
$this->addSql('ALTER TABLE chill_main_address RENAME COLUMN streetaddress2 TO streetNumber;');
$this->addSql('ALTER TABLE chill_main_address ADD floor VARCHAR(16) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD corridor VARCHAR(16) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD steps VARCHAR(16) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD buildingName VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD flat VARCHAR(16) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD distribution VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD extra VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD validTo DATE DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD point geometry(POINT,4326) DEFAULT NULL');
}
public function down(Schema $schema) : void
{
$this->addSql('ALTER TABLE chill_main_address RENAME COLUMN street TO streetaddress1;');
$this->addSql('ALTER TABLE chill_main_address RENAME COLUMN streetNumber TO streetaddress2;');
$this->addSql('ALTER TABLE chill_main_address DROP floor');
$this->addSql('ALTER TABLE chill_main_address DROP corridor');
$this->addSql('ALTER TABLE chill_main_address DROP steps');
$this->addSql('ALTER TABLE chill_main_address DROP buildingName');
$this->addSql('ALTER TABLE chill_main_address DROP flat');
$this->addSql('ALTER TABLE chill_main_address DROP distribution');
$this->addSql('ALTER TABLE chill_main_address DROP extra');
$this->addSql('ALTER TABLE chill_main_address DROP validTo');
$this->addSql('ALTER TABLE chill_main_address DROP point');
}
}

View File

@@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add a AddressReference table for storing authoritative address data
*/
final class Version20210503085107 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add a AddressReference table for storing authoritative address data';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE SEQUENCE chill_main_address_reference_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE chill_main_address_reference (id INT NOT NULL, postcode_id INT DEFAULT NULL, refId VARCHAR(255) NOT NULL, street VARCHAR(255) DEFAULT NULL, streetNumber VARCHAR(255) DEFAULT NULL, municipalityCode VARCHAR(255) DEFAULT NULL, source VARCHAR(255) DEFAULT NULL, point geometry(POINT,4326) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_CA6C1BD7EECBFDF1 ON chill_main_address_reference (postcode_id)');
$this->addSql('ALTER TABLE chill_main_address_reference ADD CONSTRAINT FK_CA6C1BD7EECBFDF1 FOREIGN KEY (postcode_id) REFERENCES chill_main_postal_code (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}
public function down(Schema $schema): void
{
$this->addSql('DROP SEQUENCE chill_main_address_reference_id_seq CASCADE');
$this->addSql('DROP TABLE chill_main_address_reference');
}
}

View File

@@ -1,58 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add linkedToThirdParty field to Address
*/
final class Version20210505153727 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add linkedToThirdParty field to Address';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_main_address ADD linkedToThirdParty_id INT DEFAULT NULL');
$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');
$this->addSql('CREATE INDEX IDX_165051F6114B8DD9 ON chill_main_address (linkedToThirdParty_id)');
$this->addSql('
CREATE TABLE chill_main_address_legacy AS
TABLE chill_main_address;
');
$this->addSql('
WITH hydrated_addresses AS (
SELECT *, rank() OVER (PARTITION BY pa_a.person_id ORDER BY validfrom)
FROM chill_main_address AS aa JOIN chill_person_persons_to_addresses AS pa_a ON aa.id = pa_a.address_id
)
UPDATE chill_main_address AS b
SET validto = (
SELECT validfrom - INTERVAL \'1 DAY\'
FROM hydrated_addresses
WHERE hydrated_addresses.id = (
SELECT a1.id
FROM hydrated_addresses AS a1 JOIN hydrated_addresses AS a2 ON a2.person_id = a1.person_id AND a2.rank = (a1.rank-1)
WHERE a2.id = b.id
)
);
');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_main_address DROP CONSTRAINT FK_165051F6114B8DD9');
$this->addSql('DROP INDEX IDX_165051F6114B8DD9');
$this->addSql('ALTER TABLE chill_main_address DROP linkedToThirdParty_id');
$this->addSql('DROP TABLE IF EXISTS chill_main_address_legacy');
$this->addSql('
UPDATE chill_main_address
SET validto = null;
');
}
}

View File

@@ -1,79 +0,0 @@
<?php
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
use Chill\PersonBundle\Entity\Person;
class AccompanyingCourseApiController extends ApiController
{
protected EventDispatcherInterface $eventDispatcher;
protected ValidatorInterface $validator;
public function __construct(EventDispatcherInterface $eventDispatcher, $validator)
{
$this->eventDispatcher = $eventDispatcher;
$this->validator = $validator;
}
public function participationApi($id, Request $request, $_format)
{
/** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = $this->getEntity('participation', $id, $request);
$person = $this->getSerializer()
->deserialize($request->getContent(), Person::class, $_format, []);
if (NULL === $person) {
throw new BadRequestException('person id not found');
}
// TODO add acl
//
$this->onPostCheckACL('participation', $request, $_format, $accompanyingPeriod);
switch ($request->getMethod()) {
case Request::METHOD_POST:
$participation = $accompanyingPeriod->addPerson($person);
break;
case Request::METHOD_DELETE:
$participation = $accompanyingPeriod->removePerson($person);
$participation->setEndDate(new \DateTimeImmutable('now'));
break;
default:
throw new BadRequestException("This method is not supported");
}
$errors = $this->validator->validate($accompanyingPeriod);
if ($errors->count() > 0) {
// only format accepted
return $this->json($errors);
}
$this->getDoctrine()->getManager()->flush();
return $this->json($participation);
}
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
{
$this->eventDispatcher->dispatch(
AccompanyingPeriodPrivacyEvent::ACCOMPANYING_PERIOD_PRIVACY_EVENT,
new AccompanyingPeriodPrivacyEvent($entity, [
'action' => $action,
'request' => $request->getMethod()
])
);
return null;
}
}

View File

@@ -4,21 +4,17 @@ namespace Chill\PersonBundle\Controller;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
use Chill\PersonBundle\Entity\Person;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Class AccompanyingCourseController
@@ -27,21 +23,6 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
*/
class AccompanyingCourseController extends Controller
{
protected SerializerInterface $serializer;
protected EventDispatcherInterface $dispatcher;
protected ValidatorInterface $validator;
public function __construct(
SerializerInterface $serializer,
EventDispatcherInterface $dispatcher,
ValidatorInterface $validator
) {
$this->serializer = $serializer;
$this->dispatcher = $dispatcher;
$this->validator = $validator;
}
/**
* Homepage of Accompanying Course section
*
@@ -87,77 +68,54 @@ class AccompanyingCourseController extends Controller
}
/**
* Get API Data for showing endpoint
*
* Sérialise temporairement quelques données pour donner à manger au composant vuejs
* @Route(
* "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/show.{_format}",
* name="chill_person_accompanying_course_api_show"
* )
* "/{_locale}/api/parcours/{accompanying_period_id}/show",
* name="chill_person_accompanying_course_api_show")
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
* @param SerializerInterface $serializer
*/
public function showAPI(AccompanyingPeriod $accompanyingCourse, $_format): Response
public function showAPI(AccompanyingPeriod $accompanyingCourse): 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');
$persons = [];
foreach ($accompanyingCourse->getParticipations() as $k => $participation ) {
/**
* @var AccompanyingPeriodParticipation $participation
* @var Person $person
*/
$person = $participation->getPerson();
$persons[$k] = [
'firstname' => $person->getFirstName(),
'lastname' => $person->getLastName(),
'email' => $person->getEmail(),
'phone' => $person->getPhonenumber(),
'startdate' => ($participation->getStartDate()) ? $participation->getStartDate()->format('Y-m-d') : null,
'enddate' => ($participation->getEndDate()) ? $participation->getEndDate()->format('Y-m-d') : null
];
}
$data = [
'id' => $accompanyingCourse->getId(),
'remark' => $accompanyingCourse->getRemark(),
'closing_motive' => $accompanyingCourse->getClosingMotive() ? $accompanyingCourse->getClosingMotive()->getName()['fr'] : null,
'opening_date' => ($accompanyingCourse->getOpeningDate()) ? $accompanyingCourse->getOpeningDate()->format('Y-m-d') : null,
'closing_date' => ($accompanyingCourse->getClosingDate()) ? $accompanyingCourse->getClosingDate()->format('Y-m-d') : null,
'persons' => $persons
];
/**
$normalizer = [new ObjectNormalizer()];
$encoder = [new JsonEncoder()];
$serializer = new Serializer($normalizer, $encoder);
$serialized = $serializer->serialize($data,'json', []);
*
*/
$serialized = \json_encode($data);
$response = new Response($serialized);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
/**
* 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);
}
}

View File

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

View File

@@ -1,17 +0,0 @@
<?php
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class OpeningApiController extends ApiController
{
protected function customizeQuery(string $action, Request $request, $qb): void
{
$qb->where($qb->expr()->gt('e.noActiveAfter', ':now'))
->orWhere($qb->expr()->isNull('e.noActiveAfter'));
$qb->setParameter('now', new \DateTime('now'));
}
}

View File

@@ -39,16 +39,22 @@ use Chill\MainBundle\Search\SearchProvider;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Doctrine\ORM\EntityManagerInterface;
final class PersonController extends AbstractController
/**
* Class PersonController
*
* @package Chill\PersonBundle\Controller
*/
class PersonController extends AbstractController
{
/**
*
* @var SimilarPersonMatcher
*/
protected $similarPersonMatcher;
/**
*
* @var TranslatorInterface
*/
protected $translator;
@@ -59,39 +65,35 @@ final class PersonController extends AbstractController
protected $eventDispatcher;
/**
*
* @var PersonRepository;
*/
protected $personRepository;
/**
*
* @var ConfigPersonAltNamesHelper
*/
protected $configPersonAltNameHelper;
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/**
* @var ValidatorInterface
*/
private $validator;
public function __construct(
public function __construct(
SimilarPersonMatcher $similarPersonMatcher,
TranslatorInterface $translator,
EventDispatcherInterface $eventDispatcher,
PersonRepository $personRepository,
ConfigPersonAltNamesHelper $configPersonAltNameHelper,
LoggerInterface $logger,
ValidatorInterface $validator,
EntityManagerInterface $em
ValidatorInterface $validator
) {
$this->similarPersonMatcher = $similarPersonMatcher;
$this->translator = $translator;
@@ -100,14 +102,14 @@ final class PersonController extends AbstractController
$this->personRepository = $personRepository;
$this->logger = $logger;
$this->validator = $validator;
$this->em = $em;
}
public function getCFGroup()
{
$cFGroup = null;
$cFDefaultGroup = $this->em->getRepository("ChillCustomFieldsBundle:CustomFieldsDefaultGroup")
$em = $this->getDoctrine()->getManager();
$cFDefaultGroup = $em->getRepository("ChillCustomFieldsBundle:CustomFieldsDefaultGroup")
->findOneByEntity("Chill\PersonBundle\Entity\Person");
if($cFDefaultGroup) {
@@ -196,7 +198,8 @@ final class PersonController extends AbstractController
->trans('The person data has been updated')
);
$this->em->flush();
$em = $this->getDoctrine()->getManager();
$em->flush();
$url = $this->generateUrl('chill_person_view', array(
'person_id' => $person->getId()
@@ -340,8 +343,6 @@ final class PersonController extends AbstractController
$this->logger->info('Person created without errors');
}
$this->em->persist($person);
$alternatePersons = $this->similarPersonMatcher
->matchPerson($person);
@@ -390,9 +391,11 @@ final class PersonController extends AbstractController
'You are not allowed to create this person');
if ($errors->count() === 0) {
$this->em->persist($person);
$em = $this->getDoctrine()->getManager();
$this->em->flush();
$em->persist($person);
$em->flush();
return $this->redirect($this->generateUrl('chill_person_general_edit',
array('person_id' => $person->getId())));

View File

@@ -1,88 +0,0 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <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\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
/**
* Description of LoadAccompanyingPeriod
*
* @author Champs-Libres Coop
*/
class LoadAccompanyingPeriod extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
public const ACCOMPANYING_PERIOD = 'parcours 1';
public function getOrder()
{
return 10004;
}
public static $references = array();
public function load(ObjectManager $manager)
{
$centerA = $this->getReference('centerA');
$centerAId = $centerA->getId();
$personIds = $this->container->get('doctrine.orm.entity_manager')
->createQueryBuilder()
->select('p.id')
->from('ChillPersonBundle:Person', 'p')
->where('p.center = :centerAId')
->orderBy('p.id', 'ASC')
->setParameter('centerAId', $centerAId)
->getQuery()
->getScalarResult();
$openingDate = new \DateTime('2020-04-01');
$person1 = $manager->getRepository(Person::class)->find($personIds[0]);
$person2 = $manager->getRepository(Person::class)->find($personIds[1]);
$socialScope = $this->getReference('scope_social');
$a = new AccompanyingPeriod($openingDate);
$a->addPerson($person1);
$a->addPerson($person2);
$a->addScope($socialScope);
$a->setStep(AccompanyingPeriod::STEP_CONFIRMED);
$manager->persist($a);
$this->addReference(self::ACCOMPANYING_PERIOD, $a);
echo "Adding one AccompanyingPeriod\n";
$manager->flush();
}
}

View File

@@ -1,62 +0,0 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <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\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
/**
* Description of LoadAccompanyingPeriodOrigin
*
* @author Champs-Libres Coop
*/
class LoadAccompanyingPeriodOrigin extends AbstractFixture implements OrderedFixtureInterface
{
public const ACCOMPANYING_PERIOD_ORIGIN = 'accompanying_period_origin';
public function getOrder()
{
return 10005;
}
private $phoneCall = ['en' => 'phone call', 'fr' => 'appel téléphonique'];
public static $references = array();
public function load(ObjectManager $manager)
{
$o = new Origin();
$o->setLabel(json_encode($this->phoneCall));
$manager->persist($o);
$this->addReference(self::ACCOMPANYING_PERIOD_ORIGIN, $o);
echo "Adding one AccompanyingPeriod Origin\n";
$manager->flush();
}
}

View File

@@ -29,7 +29,6 @@ use Chill\PersonBundle\Entity\Person;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Doctrine\Model\Point;
/**
* Load people into database
@@ -38,17 +37,17 @@ use Chill\MainBundle\Doctrine\Model\Point;
* @author Marc Ducobu <marc@champs-libres.coop>
*/
class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
protected $faker;
public function __construct()
{
$this->faker = \Faker\Factory::create('fr_FR');
}
public function prepare()
{
//prepare days, month, years
@@ -57,57 +56,57 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
$this->years[] = $y;
$y = $y +1;
} while ($y >= 1990);
$m = 1;
do {
$this->month[] = $m;
$m = $m +1;
} while ($m >= 12);
$d = 1;
do {
$this->day[] = $d;
$d = $d + 1;
} while ($d <= 28);
}
public function getOrder()
{
return 10000;
}
public function load(ObjectManager $manager)
{
$this->loadRandPeople($manager);
$this->loadExpectedPeople($manager);
$manager->flush();
}
public function loadExpectedPeople(ObjectManager $manager)
{
echo "loading expected people...\n";
foreach ($this->peoples as $person) {
$this->addAPerson($this->fillWithDefault($person), $manager);
}
}
public function loadRandPeople(ObjectManager $manager)
{
echo "loading rand people...\n";
$this->prepare();
$chooseLastNameOrTri = array('tri', 'tri', 'name', 'tri');
$i = 0;
do {
$i++;
$sex = $this->genders[array_rand($this->genders)];
if ($chooseLastNameOrTri[array_rand($chooseLastNameOrTri)] === 'tri' ) {
$length = rand(2, 3);
$lastName = '';
@@ -118,13 +117,13 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
} else {
$lastName = $this->lastNames[array_rand($this->lastNames)];
}
if ($sex === Person::MALE_GENDER) {
$firstName = $this->firstNamesMale[array_rand($this->firstNamesMale)];
} else {
$firstName = $this->firstNamesFemale[array_rand($this->firstNamesFemale)];
}
// add an address on 80% of the created people
if (rand(0,100) < 80) {
$address = $this->getRandomAddress();
@@ -138,7 +137,7 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
} else {
$address = null;
}
$person = array(
'FirstName' => $firstName,
'LastName' => $lastName,
@@ -148,15 +147,15 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
'Address' => $address,
'maritalStatus' => $this->maritalStatusRef[array_rand($this->maritalStatusRef)]
);
$this->addAPerson($this->fillWithDefault($person), $manager);
} while ($i <= 100);
}
/**
* fill a person array with default value
*
*
* @param string[] $specific
*/
private function fillWithDefault(array $specific)
@@ -172,10 +171,10 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
'Address' => null
), $specific);
}
/**
* create a new person from array data
*
*
* @param array $person
* @param ObjectManager $manager
* @throws \Exception
@@ -201,51 +200,35 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
$this->addAccompanyingPeriods($p, $value, $manager);
break;
}
//try to add the data using the setSomething function,
// if not possible, fallback to addSomething function
if (method_exists($p, 'set'.$key)) {
call_user_func(array($p, 'set'.$key), $value);
} elseif (method_exists($p, 'add'.$key)) {
// if we have a "addSomething", we may have multiple items to add
// so, we set the value in an array if it is not an array, and
// so, we set the value in an array if it is not an array, and
// will call the function addSomething multiple times
if (!is_array($value)) {
$value = array($value);
}
foreach($value as $v) {
if ($v !== NULL) {
call_user_func(array($p, 'add'.$key), $v);
}
}
}
}
}
$manager->persist($p);
echo "add person'".$p->__toString()."'\n";
}
/**
* Create a random point
*
* @return Point
*/
private function getRandomPoint()
{
$lonBrussels = 4.35243;
$latBrussels = 50.84676;
$lon = $lonBrussels + 0.01 * rand(-5, 5);
$lat = $latBrussels + 0.01 * rand(-5, 5);
return Point::fromLonLat($lon, $lat);
}
/**
* Create a random address
*
* Creata a random address
*
* @return Address
*/
private function getRandomAddress()
@@ -255,16 +238,13 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
->setStreetAddress2(
rand(0,9) > 5 ? $this->faker->streetAddress : ''
)
->setPoint(
rand(0,9) > 5 ? $this->getRandomPoint() : NULL
)
->setPostcode($this->getReference(
LoadPostalCodes::$refs[array_rand(LoadPostalCodes::$refs)]
))
->setValidFrom($this->faker->dateTimeBetween('-5 years'))
;
}
private function getCountry($countryCode)
{
if ($countryCode === NULL) {
@@ -277,30 +257,30 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
private $maritalStatusRef = ['ms_single', 'ms_married', 'ms_widow', 'ms_separat',
'ms_divorce', 'ms_legalco', 'ms_unknown'];
private $firstNamesMale = array("Jean", "Mohamed", "Alfred", "Robert", "Justin", "Brian",
"Compère", "Jean-de-Dieu", "Charles", "Pierre", "Luc", "Mathieu", "Alain", "Etienne", "Eric",
"Corentin", "Gaston", "Spirou", "Fantasio", "Mahmadou", "Mohamidou", "Vursuv", "Youssef" );
private $firstNamesFemale = array("Svedana", "Sevlatina", "Irène", "Marcelle",
"Corentine", "Alfonsine", "Caroline", "Solange", "Gostine", "Fatoumata", "Nicole",
"Groseille", "Chana", "Oxana", "Ivana", "Julie", "Tina", "Adèle" );
private $lastNames = array("Diallo", "Bah", "Gaillot", "Martin");
private $lastNamesTrigrams = array("fas", "tré", "hu", 'blart', 'van', 'der', 'lin', 'den',
'ta', 'mi', 'net', 'gna', 'bol', 'sac', 'ré', 'jo', 'du', 'pont', 'cas', 'tor', 'rob', 'al',
'ma', 'gone', 'car',"fu", "ka", "lot", "no", "va", "du", "bu", "su", "jau", "tte", 'sir',
"lo", 'to', "cho", "car", 'mo','zu', 'qi', 'mu');
private $genders = array(Person::MALE_GENDER, Person::FEMALE_GENDER);
private $years = array();
private $month = array();
private $day = array();
private $peoples = array(
array(
'LastName' => "Depardieu",
@@ -382,21 +362,21 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
'maritalStatus' => 'ms_legalco'
),
);
private function addAccompanyingPeriods(Person $person, array $periods, ObjectManager $manager)
{
foreach ($periods as $period) {
echo "adding new past Accompanying Period..\n";
/** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = new AccompanyingPeriod(new \DateTime($period['from']));
$accompanyingPeriod
->setClosingDate(new \DateTime($period['to']))
->setRemark($period['remark'])
;
$person->addAccompanyingPeriod($accompanyingPeriod);
}
}

View File

@@ -25,7 +25,6 @@ use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
use Chill\MainBundle\Entity\RoleScope;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
/**
* Add a role CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE for all groups except administrative,
@@ -45,7 +44,6 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface
{
foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) {
$permissionsGroup = $this->getReference($permissionsGroupRef);
$scopeSocial = $this->getReference('scope_social');
//create permission group
switch ($permissionsGroup->getName()) {
@@ -53,12 +51,6 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface
case 'direction':
printf("Adding CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE to %s permission group \n", $permissionsGroup->getName());
$permissionsGroup->addRoleScope(
(new RoleScope())
->setRole(AccompanyingPeriodVoter::SEE)
->setScope($scopeSocial)
);
$roleScopeUpdate = (new RoleScope())
->setRole('CHILL_PERSON_UPDATE')
->setScope(null);

View File

@@ -28,7 +28,6 @@ use Chill\MainBundle\DependencyInjection\MissingBundleException;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\MainBundle\Security\Authorization\ChillExportVoter;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Symfony\Component\HttpFoundation\Request;
/**
* Class ChillPersonExtension
@@ -39,7 +38,7 @@ use Symfony\Component\HttpFoundation\Request;
*/
class ChillPersonExtension extends Extension implements PrependExtensionInterface
{
/**
* {@inheritDoc}
* @param array $configs
@@ -50,14 +49,14 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// set configuration for validation
$container->setParameter('chill_person.validation.birtdate_not_before',
$config['validation']['birthdate_not_after']);
$this->handlePersonFieldsParameters($container, $config['person_fields']);
$this->handleAccompanyingPeriodsFieldsParameters($container, $config['accompanying_periods_fields']);
$container->setParameter('chill_person.allow_multiple_simultaneous_accompanying_periods',
$config['allow_multiple_simultaneous_accompanying_periods']);
@@ -76,21 +75,19 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/repository.yaml');
$loader->load('services/templating.yaml');
$loader->load('services/alt_names.yaml');
$loader->load('services/serializer.yaml');
$loader->load('services/security.yaml');
// load service advanced search only if configure
if ($config['search']['search_by_phone'] != 'never') {
$loader->load('services/search_by_phone.yaml');
$container->setParameter('chill_person.search.search_by_phone',
$config['search']['search_by_phone']);
}
if ($container->getParameter('chill_person.accompanying_period') !== 'hidden') {
$loader->load('services/exports_accompanying_period.yaml');
}
}
/**
* @param ContainerBuilder $container
* @param $config
@@ -100,9 +97,9 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
if (array_key_exists('enabled', $config)) {
unset($config['enabled']);
}
$container->setParameter('chill_person.person_fields', $config);
foreach ($config as $key => $value) {
switch($key) {
case 'accompanying_period':
@@ -114,7 +111,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
}
}
}
/**
* @param ContainerBuilder $container
* @param $config
@@ -122,7 +119,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
private function handleAccompanyingPeriodsFieldsParameters(ContainerBuilder $container, $config)
{
$container->setParameter('chill_person.accompanying_period_fields', $config);
foreach ($config as $key => $value) {
switch($key) {
case 'enabled':
@@ -133,7 +130,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
}
}
}
/**
* @param ContainerBuilder $container
* @throws MissingBundleException
@@ -153,7 +150,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
)
);
}
/**
* @param ContainerBuilder $container
* @throws MissingBundleException
@@ -164,7 +161,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$this->prependHomepageWidget($container);
$this->prependDoctrineDQL($container);
$this->prependCruds($container);
//add person_fields parameter as global
$chillPersonConfig = $container->getExtensionConfig($this->getAlias());
$config = $this->processConfiguration(new Configuration(), $chillPersonConfig);
@@ -182,7 +179,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$container->prependExtensionConfig('twig', $twigConfig);
$this-> declarePersonAsCustomizable($container);
//declare routes for person bundle
$container->prependExtensionConfig('chill_main', array(
'routing' => array(
@@ -192,7 +189,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
)
));
}
/**
* Add a widget "add a person" on the homepage, automatically
*
@@ -211,7 +208,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
)
));
}
/**
* Add role hierarchy.
*
@@ -228,7 +225,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
)
));
}
/**
* Add DQL function linked with person
*
@@ -237,7 +234,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
protected function prependDoctrineDQL(ContainerBuilder $container)
{
//add DQL function to ORM (default entity_manager)
$container->prependExtensionConfig('doctrine', array(
'orm' => array(
'dql' => array(
@@ -260,7 +257,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
)
));
}
/**
* @param ContainerBuilder $container
*/
@@ -309,55 +306,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
'template' => '@ChillPerson/MaritalStatus/edit.html.twig',
]
]
],
],
'apis' => [
[
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod::class,
'name' => 'accompanying_course',
'base_path' => '/api/1.0/person/accompanying-course',
'controller' => \Chill\PersonBundle\Controller\AccompanyingCourseApiController::class,
'actions' => [
'_entity' => [
'roles' => [
Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
]
],
'participation' => [
'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
]
]
]
],
[
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod\Origin::class,
'name' => 'accompanying_period_origin',
'base_path' => '/api/1.0/person/accompanying-period/origin',
'controller' => \Chill\PersonBundle\Controller\OpeningApiController::class,
'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
]
],
]
]
]
]);

View File

@@ -42,37 +42,7 @@ use Chill\MainBundle\Entity\User;
*/
class AccompanyingPeriod
{
/**
* Mark an accompanying period as "occasional"
*
* used in INTENSITY
*/
public const INTENSITY_OCCASIONAL = 'occasional';
/**
* Mark an accompanying period as "regular"
*
* used in INTENSITY
*/
public const INTENSITY_REGULAR = 'regular';
public const INTENSITIES = [self::INTENSITY_OCCASIONAL, self::INTENSITY_REGULAR];
/**
* Mark an accompanying period as "draft".
*
* This means that the accompanying period is not yet
* confirmed by the creator
*/
public const STEP_DRAFT = 'DRAFT';
/**
* Mark an accompanying period as "confirmed".
*
* This means that the accompanying period **is**
* confirmed by the creator
*/
public const STEP_CONFIRMED = 'CONFIRMED';
const INTENSITY = ['occasional', 'regular'];
/**
* @var integer
@@ -103,7 +73,7 @@ class AccompanyingPeriod
* @ORM\Column(type="text")
*/
private $remark = '';
/**
* @var Collection
*
@@ -112,16 +82,16 @@ class AccompanyingPeriod
* )
*/
private $comments;
/**
* @var Collection
*
* @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class,
* mappedBy="accompanyingPeriod",
* cascade={"persist", "refresh", "remove", "merge", "detach"})
* cascade={"persist", "remove", "merge", "detach"})
*/
private $participations;
/**
* @var AccompanyingPeriod\ClosingMotive
*
@@ -130,31 +100,31 @@ class AccompanyingPeriod
* @ORM\JoinColumn(nullable=true)
*/
private $closingMotive = null;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=true)
*/
private $user;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=true)
*/
private $createdBy;
/**
* @var string
* @ORM\Column(type="string", length=32, nullable=true)
*/
private $step = self::STEP_DRAFT;
private $step = 'DRAFT';
/**
* @ORM\ManyToOne(targetEntity=Origin::class)
* @ORM\JoinColumn(nullable=true)
*/
private $origin;
/**
* @var string
* @ORM\Column(type="string", nullable=true)
@@ -214,7 +184,7 @@ class AccompanyingPeriod
* )
*/
private $resources;
/**
* AccompanyingPeriod constructor.
*
@@ -246,7 +216,7 @@ class AccompanyingPeriod
public function setOpeningDate($openingDate)
{
$this->openingDate = $openingDate;
return $this;
}
@@ -272,7 +242,7 @@ class AccompanyingPeriod
public function setClosingDate($closingDate)
{
$this->closingDate = $closingDate;
return $this;
}
@@ -285,7 +255,7 @@ class AccompanyingPeriod
{
return $this->closingDate;
}
/**
* @return boolean
*/
@@ -298,43 +268,43 @@ class AccompanyingPeriod
if ($this->getClosingDate() === null) {
return true;
}
return false;
}
public function setRemark(string $remark): self
{
if ($remark === null) {
$remark = '';
}
$this->remark = $remark;
return $this;
}
public function getRemark(): string
{
return $this->remark;
}
public function getComments(): Collection
{
return $this->comments;
}
public function addComment(Comment $comment): self
{
$this->comments[] = $comment;
return $this;
}
public function removeComment(Comment $comment): void
{
$this->comments->removeElement($comment);
}
/**
* Get Participations Collection
*/
@@ -342,85 +312,67 @@ class AccompanyingPeriod
{
return $this->participations;
}
/**
* Get the participation containing a person
* This private function scan Participations Collection,
* searching for a given Person
*/
public function getParticipationsContainsPerson(Person $person): Collection
private function participationsContainsPerson(Person $person): ?AccompanyingPeriodParticipation
{
return $this->getParticipations($person)->filter(
function(AccompanyingPeriodParticipation $participation) use ($person) {
if ($person === $participation->getPerson()) {
return $participation;
}
});
foreach ($this->participations as $participation) {
/** @var AccompanyingPeriodParticipation $participation */
if ($person === $participation->getPerson()) {
return $participation;
}}
return null;
}
/**
* Get the opened participation containing a person
*
* "Open" means that the closed date is NULL
*/
public function getOpenParticipationContainsPerson(Person $person): ?AccompanyingPeriodParticipation
{
$collection = $this->getParticipationsContainsPerson($person)->filter(
function(AccompanyingPeriodParticipation $participation) use ($person) {
if (NULL === $participation->getEndDate()) {
return $participation;
}
});
return $collection->count() > 0 ? $collection->first() : NULL;
}
/**
* Return true if the accompanying period contains a person.
*
* **Note**: this participation can be opened or not.
* This public function is the same but return only true or false
*/
public function containsPerson(Person $person): bool
{
return $this->getParticipationsContainsPerson($person)->count() > 0;
return ($this->participationsContainsPerson($person) === null) ? false : true;
}
/**
* Add Person
*/
public function addPerson(Person $person = null): AccompanyingPeriodParticipation
public function addPerson(Person $person = null): self
{
$participation = new AccompanyingPeriodParticipation($this, $person);
$this->participations[] = $participation;
return $participation;
return $this;
}
/**
* Remove Person
*/
public function removePerson(Person $person): ?AccompanyingPeriodParticipation
public function removePerson(Person $person): void
{
$participation = $this->getOpenParticipationContainsPerson($person);
if ($participation instanceof AccompanyingPeriodParticipation) {
$participation = $this->participationsContainsPerson($person);
if (! null === $participation) {
$participation->setEndDate(new \DateTimeImmutable('now'));
$this->participations->removeElement($participation);
}
return $participation;
}
public function getClosingMotive(): ?ClosingMotive
{
return $this->closingMotive;
}
public function setClosingMotive(ClosingMotive $closingMotive = null): self
{
$this->closingMotive = $closingMotive;
return $this;
}
/**
* If the period can be reopened.
*
@@ -432,7 +384,7 @@ class AccompanyingPeriod
if ($this->isOpen() === true) {
return false;
}
$participation = $this->participationsContainsPerson($person);
if (!null === $participation)
{
@@ -440,10 +392,10 @@ class AccompanyingPeriod
$periods = $person->getAccompanyingPeriodsOrdered();
return end($periods) === $this;
}
return false;
}
/**
*/
public function reOpen(): void
@@ -460,14 +412,14 @@ class AccompanyingPeriod
if ($this->isOpen()) {
return;
}
if (! $this->isClosingAfterOpening()) {
$context->buildViolation('The date of closing is before the date of opening')
->atPath('dateClosing')
->addViolation();
}
}
/**
* Returns true if the closing date is after the opening date.
*
@@ -485,16 +437,16 @@ class AccompanyingPeriod
}
return false;
}
function getUser(): ?User
{
return $this->user;
}
function setUser(User $user): self
{
$this->user = $user;
return $this;
}
@@ -502,38 +454,38 @@ class AccompanyingPeriod
{
return $this->origin;
}
public function setOrigin(Origin $origin): self
{
$this->origin = $origin;
return $this;
}
public function getRequestorPerson(): ?Person
{
return $this->requestorPerson;
}
public function setRequestorPerson(Person $requestorPerson): self
{
$this->requestorPerson = ($this->requestorThirdParty === null) ? $requestorPerson : null;
return $this;
}
public function getRequestorThirdParty(): ?ThirdParty
{
return $this->requestorThirdParty;
}
public function setRequestorThirdParty(ThirdParty $requestorThirdParty): self
{
$this->requestorThirdParty = ($this->requestorPerson === null) ? $requestorThirdParty : null;
return $this;
}
/**
* @return Person|ThirdParty
*/
@@ -541,48 +493,48 @@ class AccompanyingPeriod
{
return $this->requestorPerson ?? $this->requestorThirdParty;
}
public function isRequestorAnonymous(): bool
{
return $this->requestorAnonymous;
}
public function setRequestorAnonymous(bool $requestorAnonymous): self
{
$this->requestorAnonymous = $requestorAnonymous;
return $this;
}
public function isEmergency(): bool
{
return $this->emergency;
}
public function setEmergency(bool $emergency): self
{
$this->emergency = $emergency;
return $this;
}
public function isConfidential(): bool
{
return $this->confidential;
}
public function setConfidential(bool $confidential): self
{
$this->confidential = $confidential;
return $this;
}
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
public function setCreatedBy(User $createdBy): self
{
$this->createdBy = $createdBy;
@@ -594,11 +546,11 @@ class AccompanyingPeriod
{
return $this->step;
}
public function setStep(string $step): self
{
$this->step = $step;
return $this;
}
@@ -606,57 +558,45 @@ class AccompanyingPeriod
{
return $this->intensity;
}
public function setIntensity(string $intensity): self
{
$this->intensity = $intensity;
return $this;
}
public function getScopes(): Collection
{
return $this->scopes;
}
public function addScope(Scope $scope): self
{
$this->scopes[] = $scope;
return $this;
}
public function removeScope(Scope $scope): void
{
$this->scopes->removeElement($scope);
}
public function getResources(): Collection
{
return $this->resources;
}
public function addResource(Resource $resource): self
{
$this->resources[] = $resource;
return $this;
}
public function removeResource(Resource $resource): void
{
$this->resources->removeElement($resource);
}
/**
* Get a list of all persons which are participating to this course
*/
public function getPersons(): Collection
{
return $this->participations->map(
function(AccompanyingPeriodParticipation $participation) {
return $participation->getPerson();
}
);
}
}

View File

@@ -1,309 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=AccompanyingPeriodWorkRepository::class)
* @ORM\Table(name="chill_person_accompanying_period_work")
*/
class AccompanyingPeriodWork
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="text")
*/
private $note;
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
*/
private $accompanyingPeriod;
/**
* @ORM\ManyToOne(targetEntity=SocialAction::class)
*/
private $socialAction;
/**
* @ORM\Column(type="datetime")
*/
private $createdAt;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=false)
*/
private $createdBy;
/**
* @ORM\Column(type="datetime")
*/
private $startDate;
/**
* @ORM\Column(type="datetime")
*/
private $endDate;
/**
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
*
* In schema : traitant
*/
private $handlingThierParty;
/**
* @ORM\Column(type="boolean")
*/
private $createdAutomatically;
/**
* @ORM\Column(type="text")
*/
private $createdAutomaticallyReason;
/**
* @ORM\OneToMany(targetEntity=AccompanyingPeriodWorkGoal::class, mappedBy="accompanyingPeriodWork")
*/
private $goals;
/**
* @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorks")
* @ORM\JoinTable(name="chill_person_accompanying_period_work_result")
*/
private $results;
/**
* @ORM\ManyToMany(targetEntity=ThirdParty::class)
* @ORM\JoinTable(name="chill_person_accompanying_period_work_third_party")
*
* In schema : intervenants
*/
private $thirdParties;
public function __construct()
{
$this->goals = new ArrayCollection();
$this->results = new ArrayCollection();
$this->thirdParties = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNote(): ?string
{
return $this->note;
}
public function setNote(string $note): self
{
$this->note = $note;
return $this;
}
public function getAccompanyingPeriod(): ?AccompanyingPeriod
{
return $this->accompanyingPeriod;
}
public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self
{
$this->accompanyingPeriod = $accompanyingPeriod;
return $this;
}
public function getSocialAction(): ?SocialAction
{
return $this->socialAction;
}
public function setSocialAction(?SocialAction $socialAction): self
{
$this->socialAction = $socialAction;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
public function setCreatedBy(?User $createdBy): self
{
$this->createdBy = $createdBy;
return $this;
}
public function getStartDate(): ?\DateTimeInterface
{
return $this->startDate;
}
public function setStartDate(\DateTimeInterface $startDate): self
{
$this->startDate = $startDate;
return $this;
}
public function getEndDate(): ?\DateTimeInterface
{
return $this->endDate;
}
public function setEndDate(\DateTimeInterface $endDate): self
{
$this->endDate = $endDate;
return $this;
}
public function getHandlingThierParty(): ?ThirdParty
{
return $this->handlingThierParty;
}
public function setHandlingThierParty(?ThirdParty $handlingThierParty): self
{
$this->handlingThierParty = $handlingThierParty;
return $this;
}
public function getCreatedAutomatically(): ?bool
{
return $this->createdAutomatically;
}
public function setCreatedAutomatically(bool $createdAutomatically): self
{
$this->createdAutomatically = $createdAutomatically;
return $this;
}
public function getCreatedAutomaticallyReason(): ?string
{
return $this->createdAutomaticallyReason;
}
public function setCreatedAutomaticallyReason(string $createdAutomaticallyReason): self
{
$this->createdAutomaticallyReason = $createdAutomaticallyReason;
return $this;
}
/**
* @return Collection|AccompanyingPeriodWorkGoal[]
*/
public function getGoals(): Collection
{
return $this->goals;
}
public function addGoal(AccompanyingPeriodWorkGoal $goal): self
{
if (!$this->goals->contains($goal)) {
$this->goals[] = $goal;
$goal->setAccompanyingPeriodWork2($this);
}
return $this;
}
public function removeGoal(AccompanyingPeriodWorkGoal $goal): self
{
if ($this->goals->removeElement($goal)) {
// set the owning side to null (unless already changed)
if ($goal->getAccompanyingPeriodWork2() === $this) {
$goal->setAccompanyingPeriodWork2(null);
}
}
return $this;
}
/**
* @return Collection|Result[]
*/
public function getResults(): Collection
{
return $this->results;
}
public function addResult(Result $result): self
{
if (!$this->results->contains($result)) {
$this->results[] = $result;
}
return $this;
}
public function removeResult(Result $result): self
{
$this->results->removeElement($result);
return $this;
}
/**
* @return Collection|ThirdParty[]
*/
public function getThirdParties(): Collection
{
return $this->thirdParties;
}
public function addThirdParty(ThirdParty $thirdParty): self
{
if (!$this->thirdParties->contains($thirdParty)) {
$this->thirdParties[] = $thirdParty;
}
return $this;
}
public function removeThirdParty(ThirdParty $thirdParty): self
{
$this->thirdParties->removeElement($thirdParty);
return $this;
}
}

View File

@@ -1,115 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\Goal;
use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkGoalRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=AccompanyingPeriodWorkGoalRepository::class)
* @ORM\Table(name="chill_person_accompanying_period_work_goal")
*/
class AccompanyingPeriodWorkGoal
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="text")
*/
private $note;
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriodWork::class, inversedBy="goals")
*/
private $accompanyingPeriodWork;
/**
* @ORM\ManyToOne(targetEntity=Goal::class)
*/
private $goal;
/**
* @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorkGoals")
* @ORM\JoinTable(name="chill_person_accompanying_period_work_goal_result")
*/
private $results;
public function __construct()
{
$this->results = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNote(): ?string
{
return $this->note;
}
public function setNote(string $note): self
{
$this->note = $note;
return $this;
}
public function getAccompanyingPeriodWork(): ?AccompanyingPeriodWork
{
return $this->accompanyingPeriodWork;
}
public function setAccompanyingPeriodWork(?AccompanyingPeriodWork $accompanyingPeriodWork): self
{
$this->accompanyingPeriodWork = $accompanyingPeriodWork;
return $this;
}
public function getGoal(): ?Goal
{
return $this->goal;
}
public function setGoal(?Goal $goal): self
{
$this->goal = $goal;
return $this;
}
/**
* @return Collection|Result[]
*/
public function getResults(): Collection
{
return $this->results;
}
public function addResult(Result $result): self
{
if (!$this->results->contains($result)) {
$this->results[] = $result;
}
return $this;
}
public function removeResult(Result $result): self
{
$this->results->removeElement($result);
return $this;
}
}

View File

@@ -47,7 +47,7 @@ class ClosingMotive
/**
* @var array
*
* @ORM\Column(type="json")
* @ORM\Column(type="json_array")
*/
private $name;

View File

@@ -22,7 +22,7 @@
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Repository\AccompanyingPeriod\OriginRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod\OriginRepository;
use Doctrine\ORM\Mapping as ORM;
/**
@@ -39,7 +39,7 @@ class Origin
private $id;
/**
* @ORM\Column(type="json")
* @ORM\Column(type="string", length=255)
*/
private $label;
@@ -53,7 +53,7 @@ class Origin
return $this->id;
}
public function getLabel()
public function getLabel(): ?string
{
return $this->label;
}

View File

@@ -50,7 +50,7 @@ class AccompanyingPeriodParticipation
private $person;
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class, inversedBy="participations", cascade={"persist"})
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class, inversedBy="participations")
* @ORM\JoinColumn(name="accompanyingperiod_id", referencedColumnName="id", nullable=false)
*/
private $accompanyingPeriod;

View File

@@ -1,70 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\Household;
use Chill\PersonBundle\Repository\Household\HouseholdRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Chill\MainBundle\Entity\Address;
/**
* @ORM\Entity(repositoryClass=HouseholdRepository::class)
*/
class Household
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
public function getId(): ?int
{
return $this->id;
}
/**
* Addresses
* @var Collection
*
* @ORM\ManyToMany(
* targetEntity="Chill\MainBundle\Entity\Address",
* cascade={"persist", "remove", "merge", "detach"})
* @ORM\JoinTable(name="chill_person_household_to_addresses")
* @ORM\OrderBy({"validFrom" = "DESC"})
*/
private $addresses;
/**
* @param Address $address
* @return $this
*/
public function addAddress(Address $address)
{
$this->addresses[] = $address;
return $this;
}
/**
* @param Address $address
*/
public function removeAddress(Address $address)
{
$this->addresses->removeElement($address);
}
/**
* By default, the addresses are ordered by date, descending (the most
* recent first)
*
* @return \Chill\MainBundle\Entity\Address[]
*/
public function getAddresses()
{
return $this->addresses;
}
}

View File

@@ -1,153 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\Household;
use Doctrine\ORM\Mapping as ORM;
use Chill\PersonBundle\Repository\Household\HouseholdMembersRepository;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Household\Household;
/**
* @ORM\Entity(repositoryClass=HouseholdMembersRepository::class)
*/
class HouseholdMembers
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $position;
/**
* @ORM\Column(type="date")
*/
private $startDate;
/**
* @ORM\Column(type="date")
*/
private $endDate;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $comment;
/**
* @ORM\Column(type="boolean")
*/
private $sharedHousehold;
/**
*
* @var Person
* @ORM\ManyToOne(
* targetEntity="\Chill\PersonBundle\Entity\Person"
* )
*/
private $person;
/**
*
* @var Household
* @ORM\ManyToOne(
* targetEntity="\Chill\PersonBundle\Entity\Household\Household"
* )
*/
private $household;
public function getId(): ?int
{
return $this->id;
}
public function getPosition(): ?string
{
return $this->position;
}
public function setPosition(?string $position): self
{
$this->position = $position;
return $this;
}
public function getStartDate(): ?\DateTimeInterface
{
return $this->startDate;
}
public function setStartDate(\DateTimeInterface $startDate): self
{
$this->startDate = $startDate;
return $this;
}
public function getEndDate(): ?\DateTimeInterface
{
return $this->endDate;
}
public function setEndDate(\DateTimeInterface $endDate): self
{
$this->endDate = $endDate;
return $this;
}
public function getComment(): ?string
{
return $this->comment;
}
public function setComment(?string $comment): self
{
$this->comment = $comment;
return $this;
}
public function getSharedHousehold(): ?bool
{
return $this->sharedHousehold;
}
public function setSharedHousehold(bool $sharedHousehold): self
{
$this->sharedHousehold = $sharedHousehold;
return $this;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
public function getHousehold(): ?Household
{
return $this->household;
}
public function setHousehold(?Household $household): self
{
$this->household = $household;
return $this;
}
}

View File

@@ -22,17 +22,15 @@ namespace Chill\PersonBundle\Entity;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use ArrayIterator;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Country;
use Chill\PersonBundle\Entity\MaritalStatus;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\Address;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
@@ -390,7 +388,7 @@ class Person implements HasCenterInterface
*
* @deprecated since 1.1 use `getOpenedAccompanyingPeriod instead
*/
public function getCurrentAccompanyingPeriod() : ?AccompanyingPeriod
public function getCurrentAccompanyingPeriod() : AccompanyingPeriod
{
return $this->getOpenedAccompanyingPeriod();
}
@@ -1010,28 +1008,32 @@ class Person implements HasCenterInterface
/**
* By default, the addresses are ordered by date, descending (the most
* recent first)
*
* @return \Chill\MainBundle\Entity\Address[]
*/
public function getAddresses(): Collection
public function getAddresses()
{
return $this->addresses;
}
public function getLastAddress(DateTime $from = null)
/**
* @param \DateTime|null $date
* @return null
*/
public function getLastAddress(\DateTime $date = null)
{
$from ??= new DateTime('now');
if ($date === null) {
$date = new \DateTime('now');
}
/** @var ArrayIterator $addressesIterator */
$addressesIterator = $this->getAddresses()
->filter(static fn (Address $address): bool => $address->getValidFrom() <= $from)
->getIterator();
$addresses = $this->getAddresses();
$addressesIterator->uasort(
static fn (Address $left, Address $right): int => $right->getValidFrom() <=> $left->getValidFrom()
);
if ($addresses == null) {
return [] === ($addresses = iterator_to_array($addressesIterator)) ?
null :
current($addresses);
return null;
}
return $addresses->first();
}
/**

View File

@@ -1,93 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=EvaluationRepository::class)
* @ORM\Table(name="chill_person_social_work_evaluation")
*/
class Evaluation
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="json")
*/
private $title = [];
/**
* @ORM\Column(type="dateinterval")
*/
private $delay;
/**
* @ORM\Column(type="dateinterval")
*/
private $notificationDelay;
/**
* @ORM\ManyToOne(targetEntity=SocialAction::class)
*/
private $socialAction;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): array
{
return $this->title;
}
public function setTitle(array $title): self
{
$this->title = $title;
return $this;
}
public function getDelay(): ?\DateInterval
{
return $this->delay;
}
public function setDelay(\DateInterval $delay): self
{
$this->delay = $delay;
return $this;
}
public function getNotificationDelay(): ?\DateInterval
{
return $this->notificationDelay;
}
public function setNotificationDelay(\DateInterval $notificationDelay): self
{
$this->notificationDelay = $notificationDelay;
return $this;
}
public function getSocialAction(): ?SocialAction
{
return $this->socialAction;
}
public function setSocialAction(?SocialAction $socialAction): self
{
$this->socialAction = $socialAction;
return $this;
}
}

View File

@@ -1,121 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Repository\SocialWork\GoalRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=GoalRepository::class)
* @ORM\Table(name="chill_person_social_work_goal")
*/
class Goal
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="json")
*/
private $title = [];
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $desactivationDate;
/**
* @ORM\ManyToMany(targetEntity=SocialAction::class, mappedBy="goals")
*/
private $socialActions;
/**
* @ORM\ManyToMany(targetEntity=Result::class, inversedBy="goals")
* @ORM\JoinTable(name="chill_person_social_work_goal_result")
*/
private $results;
public function __construct()
{
$this->socialActions = new ArrayCollection();
$this->results = new ArrayCollection();
}
public function getTitle(): array
{
return $this->title;
}
public function setTitle(array $title): self
{
$this->title = $title;
return $this;
}
public function getDesactivationDate(): ?\DateTimeInterface
{
return $this->desactivationDate;
}
public function setDesactivationDate(?\DateTimeInterface $desactivationDate): self
{
$this->desactivationDate = $desactivationDate;
return $this;
}
/**
* @return Collection|SocialAction[]
*/
public function getSocialActions(): Collection
{
return $this->socialActions;
}
public function addSocialAction(SocialAction $socialAction): self
{
if (!$this->socialActions->contains($socialAction)) {
$this->socialActions[] = $socialAction;
}
return $this;
}
public function removeSocialAction(SocialAction $socialAction): self
{
$this->socialActions->removeElement($socialAction);
return $this;
}
/**
* @return Collection|Result[]
*/
public function getResults(): Collection
{
return $this->results;
}
public function addResult(Result $result): self
{
if (!$this->results->contains($result)) {
$this->results[] = $result;
}
return $this;
}
public function removeResult(Result $result): self
{
$this->results->removeElement($result);
return $this;
}
}

View File

@@ -1,187 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
use Chill\PersonBundle\Repository\SocialWork\ResultRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=ResultRepository::class)
* @ORM\Table(name="chill_person_social_work_result")
*/
class Result
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="json")
*/
private $title = [];
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $desactivationDate;
/**
* @ORM\ManyToMany(targetEntity=SocialAction::class, mappedBy="results")
*/
private $socialActions;
/**
* @ORM\ManyToMany(targetEntity=Goal::class, mappedBy="results")
*/
private $goals;
/**
* @ORM\ManyToMany(targetEntity=AccompanyingPeriodWork::class, mappedBy="results")
*/
private $accompanyingPeriodWorks;
/**
* @ORM\ManyToMany(targetEntity=AccompanyingPeriodWorkGoal::class, mappedBy="results")
*/
private $accompanyingPeriodWorkGoals;
public function __construct()
{
$this->socialActions = new ArrayCollection();
$this->goals = new ArrayCollection();
$this->accompanyingPeriodWorks = new ArrayCollection();
$this->accompanyingPeriodWorkGoals = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): array
{
return $this->title;
}
public function setTitle(array $title): self
{
$this->title = $title;
return $this;
}
public function getDesactivationDate(): ?\DateTimeInterface
{
return $this->desactivationDate;
}
public function setDesactivationDate(?\DateTimeInterface $desactivationDate): self
{
$this->desactivationDate = $desactivationDate;
return $this;
}
/**
* @return Collection|SocialAction[]
*/
public function getSocialActions(): Collection
{
return $this->socialActions;
}
public function addSocialAction(SocialAction $socialAction): self
{
if (!$this->socialActions->contains($socialAction)) {
$this->socialActions[] = $socialAction;
}
return $this;
}
public function removeSocialAction(SocialAction $socialAction): self
{
$this->socialActions->removeElement($socialAction);
return $this;
}
/**
* @return Collection|Goal[]
*/
public function getGoals(): Collection
{
return $this->goals;
}
public function addGoal(Goal $goal): self
{
if (!$this->goals->contains($goal)) {
$this->goals[] = $goal;
}
return $this;
}
public function removeGoal(Goal $goal): self
{
$this->goals->removeElement($goal);
return $this;
}
/**
* @return Collection|AccompanyingPeriodWork[]
*/
public function getAccompanyingPeriodWorks(): Collection
{
return $this->accompanyingPeriodWorks;
}
public function addAccompanyingPeriodWork(AccompanyingPeriodWork $accompanyingPeriod): self
{
if (!$this->accompanyingPeriodWorks->contains($accompanyingPeriod)) {
$this->accompanyingPeriodWorks[] = $accompanyingPeriod;
}
return $this;
}
public function removeAccompanyingPeriodWork(AccompanyingPeriodWork $accompanyingPeriod): self
{
$this->accompanyingPeriodWorks->removeElement($accompanyingPeriod);
return $this;
}
/**
* @return Collection|AccompanyingPeriodWorkGoal[]
*/
public function getAccompanyingPeriodWorkGoals(): Collection
{
return $this->accompanyingPeriodWorkGoals;
}
public function addAccompanyingPeriodWorkGoal(AccompanyingPeriodWorkGoal $accompanyingPeriodWorkGoal): self
{
if (!$this->accompanyingPeriodWorkGoals->contains($accompanyingPeriodWorkGoal)) {
$this->accompanyingPeriodWorkGoals[] = $accompanyingPeriodWorkGoal;
}
return $this;
}
public function removeAccompanyingPeriodWorkGoal(AccompanyingPeriodWorkGoal $accompanyingPeriodWorkGoal): self
{
$this->accompanyingPeriodWorkGoals->removeElement($accompanyingPeriodWorkGoal);
return $this;
}
}

View File

@@ -1,214 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=SocialActionRepository::class)
* @ORM\Table(name="chill_person_social_action")
*/
class SocialAction
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $desactivationDate;
/**
* @ORM\ManyToOne(targetEntity=SocialIssue::class, inversedBy="socialActions")
*/
private $issue;
/**
* @ORM\ManyToOne(targetEntity=SocialAction::class, inversedBy="children")
*/
private $parent;
/**
* @ORM\OneToMany(targetEntity=SocialAction::class, mappedBy="parent")
*/
private $children;
/**
* @ORM\Column(type="dateinterval")
*/
private $defaultNotificationDelay;
/**
* @ORM\Column(type="json")
*/
private $title = [];
/**
* @ORM\ManyToMany(targetEntity=Goal::class, inversedBy="socialActions")
* @ORM\JoinTable(name="chill_person_social_action_goal")
*/
private $goals;
/**
* @ORM\ManyToMany(targetEntity=Result::class, inversedBy="socialActions")
* @ORM\JoinTable(name="chill_person_social_action_result")
*/
private $results;
public function __construct()
{
$this->children = new ArrayCollection();
$this->goals = new ArrayCollection();
$this->results = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getDesactivationDate(): ?\DateTimeInterface
{
return $this->desactivationDate;
}
public function setDesactivationDate(?\DateTimeInterface $desactivationDate): self
{
$this->desactivationDate = $desactivationDate;
return $this;
}
public function getIssue(): ?SocialIssue
{
return $this->issue;
}
public function setIssue(?SocialIssue $issue): self
{
$this->issue = $issue;
return $this;
}
public function getParent(): ?self
{
return $this->parent;
}
public function setParent(?self $parent): self
{
$this->parent = $parent;
return $this;
}
/**
* @return Collection|self[]
*/
public function getChildren(): Collection
{
return $this->children;
}
public function addChild(self $child): self
{
if (!$this->children->contains($child)) {
$this->children[] = $child;
$child->setParent($this);
}
return $this;
}
public function removeChild(self $child): self
{
if ($this->children->removeElement($child)) {
// set the owning side to null (unless already changed)
if ($child->getParent() === $this) {
$child->setParent(null);
}
}
return $this;
}
public function getDefaultNotificationDelay(): ?\DateInterval
{
return $this->defaultNotificationDelay;
}
public function setDefaultNotificationDelay(\DateInterval $defaultNotificationDelay): self
{
$this->defaultNotificationDelay = $defaultNotificationDelay;
return $this;
}
public function getTitle(): array
{
return $this->title;
}
public function setTitle(array $title): self
{
$this->title = $title;
return $this;
}
/**
* @return Collection|Goal[]
*/
public function getGoals(): Collection
{
return $this->goals;
}
public function addGoal(Goal $goal): self
{
if (!$this->goals->contains($goal)) {
$this->goals[] = $goal;
}
return $this;
}
public function removeGoal(Goal $goal): self
{
$this->goals->removeElement($goal);
return $this;
}
/**
* @return Collection|Result[]
*/
public function getResults(): Collection
{
return $this->results;
}
public function addResult(Result $result): self
{
if (!$this->results->contains($result)) {
$this->results[] = $result;
}
return $this;
}
public function removeResult(Result $result): self
{
$this->results->removeElement($result);
return $this;
}
}

View File

@@ -1,154 +0,0 @@
<?php
namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=SocialIssueRepository::class)
* @ORM\Table(name="chill_person_social_issue")
*/
class SocialIssue
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=SocialIssue::class, inversedBy="children")
*/
private $parent;
/**
* @ORM\OneToMany(targetEntity=SocialIssue::class, mappedBy="parent")
*/
private $children;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $desactivationDate;
/**
* @ORM\Column(type="json")
*/
private $title = [];
/**
* @ORM\OneToMany(targetEntity=SocialAction::class, mappedBy="issue")
*/
private $socialActions;
public function __construct()
{
$this->children = new ArrayCollection();
$this->socialActions = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getParent(): ?self
{
return $this->parent;
}
public function setParent(?self $parent): self
{
$this->parent = $parent;
return $this;
}
/**
* @return Collection|self[]
*/
public function getChildren(): Collection
{
return $this->children;
}
public function addChild(self $child): self
{
if (!$this->children->contains($child)) {
$this->children[] = $child;
$child->setParent($this);
}
return $this;
}
public function removeChild(self $child): self
{
if ($this->children->removeElement($child)) {
// set the owning side to null (unless already changed)
if ($child->getParent() === $this) {
$child->setParent(null);
}
}
return $this;
}
public function getDesactivationDate(): ?\DateTimeInterface
{
return $this->desactivationDate;
}
public function setDesactivationDate(?\DateTimeInterface $desactivationDate): self
{
$this->desactivationDate = $desactivationDate;
return $this;
}
public function getTitle(): array
{
return $this->title;
}
public function setTitle(array $title): self
{
$this->title = $title;
return $this;
}
/**
* @return Collection|SocialAction[]
*/
public function getSocialActions(): Collection
{
return $this->socialActions;
}
public function addSocialAction(SocialAction $socialAction): self
{
if (!$this->socialActions->contains($socialAction)) {
$this->socialActions[] = $socialAction;
$socialAction->setSocialIssue($this);
}
return $this;
}
public function removeSocialAction(SocialAction $socialAction): self
{
if ($this->socialActions->removeElement($socialAction)) {
// set the owning side to null (unless already changed)
if ($socialAction->getSocialIssue() === $this) {
$socialAction->setSocialIssue(null);
}
}
return $this;
}
}

View File

@@ -123,8 +123,7 @@ class PersonType extends AbstractType
'label' => false,
'delete_empty' => function(PersonPhone $pp = null) {
return NULL === $pp || $pp->isEmpty();
},
'error_bubbling' => false
}
]);
if ($this->config['email'] === 'visible') {

View File

@@ -1,52 +0,0 @@
<?php
namespace Chill\PersonBundle\Privacy;
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <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/>.
*/
use Symfony\Component\EventDispatcher\Event;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
class AccompanyingPeriodPrivacyEvent extends Event
{
public const ACCOMPANYING_PERIOD_PRIVACY_EVENT = 'chill_person.accompanying_period_privacy_event';
protected AccompanyingPeriod $period;
protected array $args;
public function __construct($period, $args = [])
{
$this->period = $period;
$this->args = $args;
}
public function getPeriod(): AccompanyingPeriod
{
return $this->period;
}
public function getArgs(): array
{
return $this->args;
}
}

View File

@@ -26,21 +26,20 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
class PrivacyEventSubscriber implements EventSubscriberInterface
{
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var TokenStorageInterface
*/
protected $token;
/**
* PrivacyEventSubscriber constructor.
*
@@ -51,64 +50,40 @@ class PrivacyEventSubscriber implements EventSubscriberInterface
$this->logger = $logger;
$this->token = $token;
}
public static function getSubscribedEvents()
{
return [
PrivacyEvent::PERSON_PRIVACY_EVENT => [
['onPrivacyEvent']
],
AccompanyingPeriodPrivacyEvent::ACCOMPANYING_PERIOD_PRIVACY_EVENT => [
['onAccompanyingPeriodPrivacyEvent']
]
];
return array(PrivacyEvent::PERSON_PRIVACY_EVENT => array(
array('onPrivacyEvent')
));
}
public function onAccompanyingPeriodPrivacyEvent(AccompanyingPeriodPrivacyEvent $event)
{
$involved = $this->getInvolved();
$involved['period_id'] = $event->getPeriod()->getId();
$involved['persons'] = $event->getPeriod()->getPersons()
->map(function(Person $p) { return $p->getId(); })
->toArray();
$this->logger->notice(
"[Privacy Event] An accompanying period has been viewed",
array_merge($involved, $event->getArgs())
);
}
public function onPrivacyEvent(PrivacyEvent $event)
{
$persons = [];
$persons = array();
if ($event->hasPersons() === true) {
foreach ($event->getPersons() as $person) {
$persons[] = $person->getId();
}
}
$involved = $this->getInvolved();
$involved['person_id'] = $event->getPerson()->getId();
$involved = array(
'by_user' => $this->token->getToken()->getUser()->getUsername(),
'by_user_id' => $this->token->getToken()->getUser()->getId(),
'person_id' => $event->getPerson()->getId(),
);
if ($event->hasPersons()) {
$involved['persons'] = \array_map(
function(Person $p) { return $p->getId(); },
function(Person $p) { return $p->getId(); },
$event->getPersons()
);
}
$this->logger->notice(
"[Privacy Event] A Person Folder has been viewed",
array_merge($involved, $event->getArgs())
);
}
protected function getInvolved(): array
{
return [
'by_user' => $this->token->getToken()->getUser()->getUsername(),
'by_user_id' => $this->token->getToken()->getUser()->getId(),
];
}
}
}

Some files were not shown because too many files have changed in this diff Show More