mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 14:43:49 +00:00
Merge branch 'master' into VSR-issues
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class RegroupmentController extends CRUDController
|
||||
{
|
||||
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
|
||||
{
|
||||
$query->addOrderBy('e.id', 'ASC');
|
||||
|
||||
return parent::orderQuery($action, $query, $request, $paginator);
|
||||
}
|
||||
}
|
@@ -85,7 +85,6 @@ class WorkflowController extends AbstractController
|
||||
->setRelatedEntityClass($request->query->get('entityClass'))
|
||||
->setRelatedEntityId($request->query->getInt('entityId'))
|
||||
->setWorkflowName($request->query->get('workflow'))
|
||||
->addSubscriberToStep($this->getUser())
|
||||
->addSubscriberToFinal($this->getUser());
|
||||
|
||||
$errors = $this->validator->validate($entityWorkflow, null, ['creation']);
|
||||
|
@@ -44,7 +44,7 @@ class CronManager implements CronManagerInterface
|
||||
|
||||
private const UPDATE_AFTER_EXEC = 'UPDATE ' . CronJobExecution::class . ' cr SET cr.lastEnd = :now, cr.lastStatus = :status WHERE cr.key = :key';
|
||||
|
||||
private const UPDATE_BEFORE_EXEC = 'UPDATE ' . CronJobExecution::class . ' cr SET cr.lastExecution = :now WHERE cr.key = :key';
|
||||
private const UPDATE_BEFORE_EXEC = 'UPDATE ' . CronJobExecution::class . ' cr SET cr.lastStart = :now WHERE cr.key = :key';
|
||||
|
||||
private CronJobExecutionRepositoryInterface $cronJobExecutionRepository;
|
||||
|
||||
@@ -90,7 +90,8 @@ class CronManager implements CronManagerInterface
|
||||
->setParameters([
|
||||
'now' => new DateTimeImmutable('now'),
|
||||
'key' => $job->getKey(),
|
||||
]);
|
||||
])
|
||||
->execute();
|
||||
} else {
|
||||
$execution = new CronJobExecution($job->getKey());
|
||||
$this->entityManager->persist($execution);
|
||||
|
@@ -18,6 +18,7 @@ use Chill\MainBundle\Controller\CountryController;
|
||||
use Chill\MainBundle\Controller\LanguageController;
|
||||
use Chill\MainBundle\Controller\LocationController;
|
||||
use Chill\MainBundle\Controller\LocationTypeController;
|
||||
use Chill\MainBundle\Controller\RegroupmentController;
|
||||
use Chill\MainBundle\Controller\UserController;
|
||||
use Chill\MainBundle\Controller\UserJobApiController;
|
||||
use Chill\MainBundle\Controller\UserJobController;
|
||||
@@ -48,6 +49,7 @@ use Chill\MainBundle\Entity\Country;
|
||||
use Chill\MainBundle\Entity\Language;
|
||||
use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\LocationType;
|
||||
use Chill\MainBundle\Entity\Regroupment;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Form\CivilityType;
|
||||
@@ -55,6 +57,7 @@ use Chill\MainBundle\Form\CountryType;
|
||||
use Chill\MainBundle\Form\LanguageType;
|
||||
use Chill\MainBundle\Form\LocationFormType;
|
||||
use Chill\MainBundle\Form\LocationTypeType;
|
||||
use Chill\MainBundle\Form\RegroupmentType;
|
||||
use Chill\MainBundle\Form\UserJobType;
|
||||
use Chill\MainBundle\Form\UserType;
|
||||
use Exception;
|
||||
@@ -148,6 +151,11 @@ class ChillMainExtension extends Extension implements
|
||||
$config['access_permissions_group_list']
|
||||
);
|
||||
|
||||
$container->setParameter(
|
||||
'chill_main.add_address',
|
||||
$config['add_address']
|
||||
);
|
||||
|
||||
$container->setParameter(
|
||||
'chill_main.routing.resources',
|
||||
$config['routing']['resources']
|
||||
@@ -223,6 +231,7 @@ class ChillMainExtension extends Extension implements
|
||||
'installation' => [
|
||||
'name' => $config['installation_name'], ],
|
||||
'available_languages' => $config['available_languages'],
|
||||
'add_address' => $config['add_address'],
|
||||
],
|
||||
'form_themes' => ['@ChillMain/Form/fields.html.twig'],
|
||||
];
|
||||
@@ -493,6 +502,27 @@ class ChillMainExtension extends Extension implements
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'class' => Regroupment::class,
|
||||
'name' => 'regroupment',
|
||||
'base_path' => '/admin/regroupment',
|
||||
'form_class' => RegroupmentType::class,
|
||||
'controller' => RegroupmentController::class,
|
||||
'actions' => [
|
||||
'index' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/Admin/Regroupment/index.html.twig',
|
||||
],
|
||||
'new' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/Admin/Regroupment/new.html.twig',
|
||||
],
|
||||
'edit' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/Admin/Regroupment/edit.html.twig',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'apis' => [
|
||||
[
|
||||
|
@@ -276,6 +276,16 @@ class Configuration implements ConfigurationInterface
|
||||
->end() // end of root
|
||||
;
|
||||
|
||||
$rootNode->children()
|
||||
->arrayNode('add_address')->addDefaultsIfNotSet()->children()
|
||||
->scalarNode('default_country')->cannotBeEmpty()->defaultValue('BE')->end()
|
||||
->arrayNode('map_center')->children()
|
||||
->scalarNode('x')->cannotBeEmpty()->defaultValue(50.8443)->end()
|
||||
->scalarNode('y')->cannotBeEmpty()->defaultValue(4.3523)->end()
|
||||
->scalarNode('z')->cannotBeEmpty()->defaultValue(15)->end()
|
||||
->end()
|
||||
->end();
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
|
@@ -135,12 +135,8 @@ trait AddWidgetConfigurationTrait
|
||||
|
||||
/**
|
||||
* add configuration nodes for the widget at the given place.
|
||||
*
|
||||
* @param type $place
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
protected function addWidgetsConfiguration($place, ContainerBuilder $containerBuilder)
|
||||
protected function addWidgetsConfiguration(string $place, ContainerBuilder $containerBuilder)
|
||||
{
|
||||
$treeBuilder = new TreeBuilder($place);
|
||||
$root = $treeBuilder->getRootNode($place)
|
||||
|
95
src/Bundle/ChillMainBundle/Entity/Regroupment.php
Normal file
95
src/Bundle/ChillMainBundle/Entity/Regroupment.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="regroupment")
|
||||
*/
|
||||
class Regroupment
|
||||
{
|
||||
/**
|
||||
* @var Center
|
||||
* @ORM\ManyToMany(
|
||||
* targetEntity="Chill\MainBundle\Entity\Center"
|
||||
* )
|
||||
* @ORM\Id
|
||||
*/
|
||||
private Collection $centers;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean")
|
||||
*/
|
||||
private bool $isActive = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=15, options={"default": ""}, nullable=false)
|
||||
*/
|
||||
private string $name = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->centers = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getCenters(): ?Collection
|
||||
{
|
||||
return $this->centers;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getIsActive(): bool
|
||||
{
|
||||
return $this->isActive;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setCenters(?Collection $centers): self
|
||||
{
|
||||
$this->centers = $centers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setIsActive(bool $isActive): self
|
||||
{
|
||||
$this->isActive = $isActive;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setName(string $name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -15,7 +15,7 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
@@ -31,7 +31,7 @@ use function in_array;
|
||||
* "user": User::class
|
||||
* })
|
||||
*/
|
||||
class User implements AdvancedUserInterface
|
||||
class User implements UserInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
@@ -58,8 +58,6 @@ class User implements AdvancedUserInterface
|
||||
private ?Location $currentLocation = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(type="string", length=150, nullable=true)
|
||||
*/
|
||||
private ?string $email = null;
|
||||
@@ -216,7 +214,7 @@ class User implements AdvancedUserInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GroupCenter
|
||||
* @return Collection<GroupCenter>
|
||||
*/
|
||||
public function getGroupCenters()
|
||||
{
|
||||
@@ -225,10 +223,8 @@ class User implements AdvancedUserInterface
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
@@ -487,7 +483,7 @@ class User implements AdvancedUserInterface
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Agent
|
||||
* @return User
|
||||
*/
|
||||
public function setUsername($name)
|
||||
{
|
||||
|
@@ -132,8 +132,6 @@ class EntityWorkflowStep
|
||||
{
|
||||
if (!$this->destUser->contains($user)) {
|
||||
$this->destUser[] = $user;
|
||||
$this->getEntityWorkflow()
|
||||
->addSubscriberToFinal($user);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -143,8 +141,6 @@ class EntityWorkflowStep
|
||||
{
|
||||
if (!$this->destUserByAccessKey->contains($user) && !$this->destUser->contains($user)) {
|
||||
$this->destUserByAccessKey[] = $user;
|
||||
$this->getEntityWorkflow()
|
||||
->addSubscriberToFinal($user);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
47
src/Bundle/ChillMainBundle/Form/RegroupmentType.php
Normal file
47
src/Bundle/ChillMainBundle/Form/RegroupmentType.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Regroupment;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class RegroupmentType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('name', TextType::class, [
|
||||
'label' => 'Nom',
|
||||
])
|
||||
->add('centers', EntityType::class, [
|
||||
'class' => Center::class,
|
||||
'multiple' => true,
|
||||
'attr' => ['class' => 'select2'],
|
||||
])
|
||||
->add('isActive', CheckboxType::class, [
|
||||
'label' => 'Actif ?',
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setDefault('class', Regroupment::class);
|
||||
}
|
||||
}
|
@@ -27,7 +27,6 @@ class ChillDateTimeType extends AbstractType
|
||||
{
|
||||
$resolver
|
||||
->setDefault('date_widget', 'single_text')
|
||||
->setDefault('date_format', 'dd-MM-yyyy')
|
||||
->setDefault('time_widget', 'choice')
|
||||
->setDefault('minutes', range(0, 59, 5))
|
||||
->setDefault('hours', range(8, 22))
|
||||
|
@@ -67,13 +67,18 @@ export const makeFetch = <Input, Output>(method: 'POST'|'GET'|'PUT'|'PATCH'|'DEL
|
||||
},
|
||||
};
|
||||
|
||||
if (body !== null || typeof body !== 'undefined') {
|
||||
console.log('for url '+url, body);
|
||||
console.log('for url '+url, body !== null);
|
||||
|
||||
if (body !== null && typeof body !== 'undefined') {
|
||||
Object.assign(opts, {body: JSON.stringify(body)})
|
||||
}
|
||||
|
||||
if (typeof options !== 'undefined') {
|
||||
opts = Object.assign(opts, options);
|
||||
}
|
||||
console.log('will fetch', url);
|
||||
console.log('content for ' + url, opts);
|
||||
|
||||
return fetch(url, opts)
|
||||
.then(response => {
|
||||
|
@@ -315,8 +315,11 @@ export default {
|
||||
addressMap: {
|
||||
// Note: LeafletJs demands [lat, lon]
|
||||
// cfr https://macwright.com/lonlat/
|
||||
center : [48.8589, 2.3469],
|
||||
zoom: 12
|
||||
center : [
|
||||
this.context.defaults.map_center.x,
|
||||
this.context.defaults.map_center.y,
|
||||
],
|
||||
zoom: this.context.defaults.map_center.z
|
||||
},
|
||||
},
|
||||
errorMsg: []
|
||||
|
@@ -8,8 +8,12 @@ import L from 'leaflet';
|
||||
import markerIconPng from 'leaflet/dist/images/marker-icon.png'
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
|
||||
const lonLatForLeaflet = (coordinates) => {
|
||||
return [coordinates[1], coordinates[0]];
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'AddressMap',
|
||||
name: 'AddressMap',
|
||||
props: ['entity'],
|
||||
data() {
|
||||
return {
|
||||
@@ -21,10 +25,47 @@ export default {
|
||||
center() {
|
||||
return this.entity.addressMap.center;
|
||||
},
|
||||
hasAddressPoint() {
|
||||
if (Object.keys(this.entity.address).length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (null !== this.entity.address.addressReference) {
|
||||
return true;
|
||||
}
|
||||
if (null !== this.entity.address.postcode && null !== this.entity.address.postcode.center) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @returns {coordinates: [float, float], type: "Point"}
|
||||
*/
|
||||
addressPoint() {
|
||||
if (Object.keys(this.entity.address).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null !== this.entity.address.addressReference) {
|
||||
return this.entity.address.addressReference.point;
|
||||
}
|
||||
|
||||
if (null !== this.entity.address.postcode && null !== this.entity.address.postcode.center) {
|
||||
return this.entity.address.postcode.center;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
},
|
||||
methods:{
|
||||
init() {
|
||||
this.map = L.map('address_map').setView([46.67059, -1.42683], 12);
|
||||
this.map = L.map('address_map');
|
||||
|
||||
if (!this.hasAddressPoint) {
|
||||
this.map.setView(lonLatForLeaflet(this.entity.addressMap.center), this.entity.addressMap.zoom);
|
||||
} else {
|
||||
this.map.setView(lonLatForLeaflet(this.addressPoint.coordinates), 15);
|
||||
}
|
||||
|
||||
this.map.scrollWheelZoom.disable();
|
||||
|
||||
@@ -37,20 +78,22 @@ export default {
|
||||
iconAnchor: [12, 41],
|
||||
});
|
||||
|
||||
this.marker = L.marker([48.8589, 2.3469], {icon: markerIcon});
|
||||
if (!this.hasAddressPoint) {
|
||||
this.marker = L.marker(lonLatForLeaflet(this.entity.addressMap.center), {icon: markerIcon});
|
||||
} else {
|
||||
this.marker = L.marker(lonLatForLeaflet(this.addressPoint.coordinates), {icon: markerIcon});
|
||||
}
|
||||
this.marker.addTo(this.map);
|
||||
},
|
||||
update() {
|
||||
console.log('update map with : ', this.center)
|
||||
if (this.marker && this.center) {
|
||||
this.marker.setLatLng(this.center);
|
||||
this.map.setView(this.center, 15);
|
||||
if (this.marker && this.entity.addressMap.center) {
|
||||
this.marker.setLatLng(lonLatForLeaflet(this.entity.addressMap.center));
|
||||
this.map.panTo(lonLatForLeaflet(this.entity.addressMap.center));
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
mounted() {
|
||||
this.init();
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@@ -30,7 +30,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
value: this.selectCountryByCode(
|
||||
this.context.edit ? this.entity.selected.country.code : 'FR'
|
||||
this.context.edit ? this.entity.selected.country.code : this.context.defaults.default_country
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -45,14 +45,12 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
console.log('country selection mounted', this.value);
|
||||
if (this.value !== undefined) {
|
||||
this.selectCountry(this.value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
if (this.value !== undefined) {
|
||||
this.selectCountry(this.value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectCountryByCode(countryCode) {
|
||||
return this.entity.loaded.countries.filter(c => c.countryCode === countryCode)[0];
|
||||
},
|
||||
|
@@ -174,8 +174,8 @@ export default {
|
||||
},
|
||||
updateMapCenter(point) {
|
||||
console.log('point', point);
|
||||
this.addressMap.center[0] = point.coordinates[1]; // TODO use reverse()
|
||||
this.addressMap.center[1] = point.coordinates[0];
|
||||
this.addressMap.center[0] = point.coordinates[0];
|
||||
this.addressMap.center[1] = point.coordinates[1];
|
||||
this.$refs.addressMap.update(); // cast child methods
|
||||
}
|
||||
}
|
||||
|
@@ -93,7 +93,7 @@ export default {
|
||||
],
|
||||
emits: ['openEditPane'],
|
||||
mounted() {
|
||||
console.log('context', this.context)
|
||||
//console.log('context', this.context)
|
||||
},
|
||||
computed: {
|
||||
address() {
|
||||
|
@@ -20,7 +20,8 @@ containers.forEach((container) => {
|
||||
},
|
||||
edit: container.dataset.mode === 'edit', //boolean
|
||||
addressId: parseInt(container.dataset.addressId) || null,
|
||||
backUrl: container.dataset.backUrl || null
|
||||
backUrl: container.dataset.backUrl || null,
|
||||
defaults: JSON.parse(container.dataset.addressDefaults)
|
||||
},
|
||||
options: {
|
||||
/// Options override default.
|
||||
|
@@ -19,7 +19,6 @@ const addAddressInput = (inputs) => {
|
||||
if (container === null) {
|
||||
throw Error("no container");
|
||||
}
|
||||
console.log('useValidFrom', el.dataset.useValidFrom === '1');
|
||||
|
||||
const app = createApp({
|
||||
template: `<app v-bind:addAddress="this.addAddress" @address-created="associateToInput"></app>`,
|
||||
@@ -34,6 +33,7 @@ const addAddressInput = (inputs) => {
|
||||
},
|
||||
edit: isEdit,
|
||||
addressId: addressIdInt,
|
||||
defaults: window.addaddress,
|
||||
},
|
||||
options: {
|
||||
/// Options override default.
|
||||
|
@@ -72,6 +72,8 @@
|
||||
{% if onlyButton is defined and onlyButton == 1 %}
|
||||
data-hide-address="true"
|
||||
{% endif %}
|
||||
|
||||
data-address-defaults="{{ add_address|json_encode|e('html') }}"
|
||||
></div>
|
||||
|
||||
{{ encore_entry_script_tags('vue_address') }}
|
||||
|
@@ -0,0 +1,11 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_edit_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock admin_content %}
|
@@ -0,0 +1,39 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_index.html.twig' %}
|
||||
{% block table_entities_thead_tr %}
|
||||
<th>{{ 'Label'|trans }}</th>
|
||||
<th>{{ 'Active'|trans }}</th>
|
||||
<th> </th>
|
||||
{% endblock %}
|
||||
|
||||
{% block table_entities_tbody %}
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.name }}</td>
|
||||
<td style="text-align:center">
|
||||
{% if entity.isActive %}
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
{% else %}
|
||||
<i class="fa fa-square-o"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_crud_regroupment_edit', { 'id': entity.id }) }}" class="btn btn-edit"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block actions_before %}
|
||||
<li class='cancel'>
|
||||
<a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans}}</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
@@ -0,0 +1,11 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock admin_content %}
|
@@ -9,6 +9,11 @@
|
||||
{% block head_custom %}{% endblock %}
|
||||
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
|
||||
|
||||
<script type="application/javascript">
|
||||
{# this is global data, in use for all js app #}
|
||||
window.addaddress = {{ add_address|json_encode|raw }};
|
||||
</script>
|
||||
|
||||
{{ encore_entry_link_tags('mod_bootstrap') }}
|
||||
{{ encore_entry_link_tags('mod_forkawesome') }}
|
||||
{{ encore_entry_link_tags('mod_ckeditor5') }}
|
||||
|
@@ -53,6 +53,10 @@ class AdminUserMenuBuilder implements LocalMenuBuilderInterface
|
||||
'route' => 'admin_center',
|
||||
])->setExtras(['order' => 1010]);
|
||||
|
||||
$menu->addChild('Regroupements des centres', [
|
||||
'route' => 'chill_crud_regroupment_index',
|
||||
])->setExtras(['order' => 1015]);
|
||||
|
||||
$menu->addChild('List circles', [
|
||||
'route' => 'admin_scope',
|
||||
])->setExtras(['order' => 1020]);
|
||||
|
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class AuthorizationHelperForCurrentUser implements AuthorizationHelperForCurrentUserInterface
|
||||
{
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(AuthorizationHelperInterface $authorizationHelper, Security $security)
|
||||
{
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
public function getReachableCenters(string $role, ?Scope $scope = null): array
|
||||
{
|
||||
if (!$this->security->getUser() instanceof User) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->authorizationHelper->getReachableCenters($this->security->getUser(), $role, $scope);
|
||||
}
|
||||
|
||||
public function getReachableScopes(string $role, $center): array
|
||||
{
|
||||
if (!$this->security->getUser() instanceof User) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->authorizationHelper->getReachableScopes($this->security->getUser(), $role, $center);
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
|
||||
interface AuthorizationHelperForCurrentUserInterface
|
||||
{
|
||||
/**
|
||||
* Get reachable Centers for the given user, role,
|
||||
* and optionnaly Scope.
|
||||
*
|
||||
* @return Center[]
|
||||
*/
|
||||
public function getReachableCenters(string $role, ?Scope $scope = null): array;
|
||||
|
||||
/**
|
||||
* @param array|Center|Center[] $center
|
||||
*/
|
||||
public function getReachableScopes(string $role, $center): array;
|
||||
}
|
@@ -31,6 +31,10 @@ class CenterNormalizer implements DenormalizerInterface, NormalizerInterface
|
||||
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
if (null === $data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (false === array_key_exists('type', $data)) {
|
||||
throw new InvalidArgumentException('missing "type" key in data');
|
||||
}
|
||||
|
@@ -17,12 +17,9 @@ use DateInterval;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use UnexpectedValueException;
|
||||
use function in_array;
|
||||
|
||||
class RefreshAddressToGeographicalUnitMaterializedViewCronJob implements CronJobInterface
|
||||
{
|
||||
private const ACCEPTED_HOURS = ['0', '1', '2', '3', '4', '5'];
|
||||
|
||||
private Connection $connection;
|
||||
|
||||
public function __construct(Connection $connection)
|
||||
@@ -43,9 +40,8 @@ class RefreshAddressToGeographicalUnitMaterializedViewCronJob implements CronJob
|
||||
$now = new DateTimeImmutable('now');
|
||||
|
||||
return $cronJobExecution->getLastStart() < $now->sub(new DateInterval('P1D'))
|
||||
&& in_array($now->format('H'), self::ACCEPTED_HOURS, true)
|
||||
// introduce a random component to ensure a roll when multiple instances are hosted on same machines
|
||||
&& mt_rand(0, 5) === 0;
|
||||
&& mt_rand(0, 10) === 0;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
|
@@ -30,6 +30,56 @@ final class RefreshAddressToGeographicalUnitMaterializedViewCronJobTest extends
|
||||
$this->connection = self::$container->get(Connection::class);
|
||||
}
|
||||
|
||||
public function testCanRun(): void
|
||||
{
|
||||
// As the can run is executed one of ten, this should be executed at least one after
|
||||
// 10 + 5 executions
|
||||
$job = new \Chill\MainBundle\Service\AddressGeographicalUnit\RefreshAddressToGeographicalUnitMaterializedViewCronJob(
|
||||
$this->connection
|
||||
);
|
||||
|
||||
$lastExecution = new CronJobExecution($job->getKey());
|
||||
$lastExecution->setLastStart(new DateTimeImmutable('2 days ago'));
|
||||
|
||||
$executedForFirstTime = 0;
|
||||
$executedAfterPreviousExecution = 0;
|
||||
|
||||
for ($round = 0; 20 > $round; ++$round) {
|
||||
if ($job->canRun(null)) {
|
||||
++$executedForFirstTime;
|
||||
}
|
||||
|
||||
if ($job->canRun($lastExecution)) {
|
||||
++$executedAfterPreviousExecution;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertGreaterThan(0, $executedForFirstTime);
|
||||
$this->assertGreaterThan(0, $executedAfterPreviousExecution);
|
||||
}
|
||||
|
||||
public function testCanRunShouldReturnFalse(): void
|
||||
{
|
||||
// As the can run is executed one of ten, this should be executed at least one after
|
||||
// 10 + 5 executions
|
||||
$job = new \Chill\MainBundle\Service\AddressGeographicalUnit\RefreshAddressToGeographicalUnitMaterializedViewCronJob(
|
||||
$this->connection
|
||||
);
|
||||
|
||||
$lastExecution = new CronJobExecution($job->getKey());
|
||||
$lastExecution->setLastStart(new DateTimeImmutable('2 hours ago'));
|
||||
|
||||
$executedAfterPreviousExecution = 0;
|
||||
|
||||
for ($round = 0; 20 > $round; ++$round) {
|
||||
if ($job->canRun($lastExecution)) {
|
||||
++$executedAfterPreviousExecution;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertEquals(0, $executedAfterPreviousExecution);
|
||||
}
|
||||
|
||||
public function testFullRun(): void
|
||||
{
|
||||
$job = new \Chill\MainBundle\Service\AddressGeographicalUnit\RefreshAddressToGeographicalUnitMaterializedViewCronJob(
|
||||
|
@@ -33,3 +33,7 @@ services:
|
||||
arguments:
|
||||
$security: '@Symfony\Component\Security\Core\Security'
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
Chill\MainBundle\Controller\RegroupmentController:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
@@ -138,6 +138,10 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Form\RegroupmentType:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Form\DataTransformer\IdToLocationDataTransformer: ~
|
||||
Chill\MainBundle\Form\DataTransformer\IdToUserDataTransformer: ~
|
||||
Chill\MainBundle\Form\DataTransformer\IdToUsersDataTransformer: ~
|
||||
|
@@ -3,6 +3,11 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Security\:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
resource: '../../Security'
|
||||
|
||||
Chill\MainBundle\Security\Resolver\CenterResolverDispatcher:
|
||||
arguments:
|
||||
- !tagged_iterator chill_main.center_resolver
|
||||
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20230111104315 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP SEQUENCE regroupment_id_seq CASCADE');
|
||||
$this->addSql('ALTER TABLE regroupment_center DROP CONSTRAINT FK_2BCCE2F9EC6D1029');
|
||||
$this->addSql('ALTER TABLE regroupment_center DROP CONSTRAINT FK_2BCCE2F95932F377');
|
||||
$this->addSql('DROP TABLE regroupment');
|
||||
$this->addSql('DROP TABLE regroupment_center');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add regroupment admin entity';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE SEQUENCE regroupment_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE regroupment (id INT NOT NULL, name VARCHAR(15) DEFAULT \'\' NOT NULL, isActive BOOLEAN NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE TABLE regroupment_center (regroupment_id INT NOT NULL, center_id INT NOT NULL, PRIMARY KEY(regroupment_id, center_id))');
|
||||
$this->addSql('CREATE INDEX IDX_2BCCE2F9EC6D1029 ON regroupment_center (regroupment_id)');
|
||||
$this->addSql('CREATE INDEX IDX_2BCCE2F95932F377 ON regroupment_center (center_id)');
|
||||
$this->addSql('ALTER TABLE regroupment_center ADD CONSTRAINT FK_2BCCE2F9EC6D1029 FOREIGN KEY (regroupment_id) REFERENCES regroupment (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE regroupment_center ADD CONSTRAINT FK_2BCCE2F95932F377 FOREIGN KEY (center_id) REFERENCES centers (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
}
|
@@ -42,6 +42,8 @@ by_user: "par "
|
||||
lifecycleUpdate: Evenements de création et mise à jour
|
||||
address_fields: Données liées à l'adresse
|
||||
|
||||
inactive: inactif
|
||||
|
||||
Edit: Modifier
|
||||
Update: Mettre à jour
|
||||
Back to the list: Retour à la liste
|
||||
@@ -408,6 +410,12 @@ crud:
|
||||
add_new: Ajouter une civilité
|
||||
title_new: Nouvelle civilité
|
||||
title_edit: Modifier une civilité
|
||||
regroupment:
|
||||
index:
|
||||
title: Liste des regroupements
|
||||
add_new: Ajouter un regroupement
|
||||
title_new: Nouveau regroupement
|
||||
title_edit: Modifier un regroupement
|
||||
|
||||
No entities: Aucun élément
|
||||
|
||||
|
Reference in New Issue
Block a user