mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-13 13:54:23 +00:00
Merge branch 'onTheFly' into thirdparty
This commit is contained in:
commit
14539f6f88
@ -7,6 +7,7 @@ charset = utf-8
|
|||||||
end_of_line = LF
|
end_of_line = LF
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
[*.{php,html,twig}]
|
[*.{php,html,twig}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
18
CHANGELOG.md
Normal file
18
CHANGELOG.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to
|
||||||
|
|
||||||
|
* [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for stable releases;
|
||||||
|
* date versioning for test releases
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
* [Household editor][UI] Update how household suggestion and addresses are picked;
|
||||||
|
* [AddAddress] Handle address suggestion;
|
||||||
|
|
||||||
|
## Test release yyyy-mm-dd
|
||||||
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
|||||||
"league/csv": "^9.7.1",
|
"league/csv": "^9.7.1",
|
||||||
"nyholm/psr7": "^1.4",
|
"nyholm/psr7": "^1.4",
|
||||||
"phpoffice/phpspreadsheet": "^1.16",
|
"phpoffice/phpspreadsheet": "^1.16",
|
||||||
|
"ramsey/uuid-doctrine": "^1.7",
|
||||||
"sensio/framework-extra-bundle": "^5.5",
|
"sensio/framework-extra-bundle": "^5.5",
|
||||||
"symfony/asset": "4.*",
|
"symfony/asset": "4.*",
|
||||||
"symfony/browser-kit": "^5.2",
|
"symfony/browser-kit": "^5.2",
|
||||||
@ -29,6 +30,7 @@
|
|||||||
"symfony/expression-language": "4.*",
|
"symfony/expression-language": "4.*",
|
||||||
"symfony/form": "4.*",
|
"symfony/form": "4.*",
|
||||||
"symfony/intl": "4.*",
|
"symfony/intl": "4.*",
|
||||||
|
"symfony/mime": "^4 || ^5",
|
||||||
"symfony/monolog-bundle": "^3.5",
|
"symfony/monolog-bundle": "^3.5",
|
||||||
"symfony/security-bundle": "4.*",
|
"symfony/security-bundle": "4.*",
|
||||||
"symfony/serializer": "^5.2",
|
"symfony/serializer": "^5.2",
|
||||||
|
@ -29,17 +29,19 @@ class ListenToActivityCreate
|
|||||||
$activityData = $request->query->get('activityData');
|
$activityData = $request->query->get('activityData');
|
||||||
if (array_key_exists('calendarId', $activityData)) {
|
if (array_key_exists('calendarId', $activityData)) {
|
||||||
$calendarId = $activityData['calendarId'];
|
$calendarId = $activityData['calendarId'];
|
||||||
|
|
||||||
|
// Attach the activity to the calendar
|
||||||
|
$em = $event->getObjectManager();
|
||||||
|
|
||||||
|
$calendar = $em->getRepository(\Chill\CalendarBundle\Entity\Calendar::class)->find($calendarId);
|
||||||
|
$calendar->setActivity($activity);
|
||||||
|
|
||||||
|
$em->persist($calendar);
|
||||||
|
$em->flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach the activity to the calendar
|
|
||||||
$em = $event->getObjectManager();
|
|
||||||
|
|
||||||
$calendar = $em->getRepository(\Chill\CalendarBundle\Entity\Calendar::class)->find($calendarId);
|
|
||||||
$calendar->setActivity($activity);
|
|
||||||
|
|
||||||
$em->persist($calendar);
|
|
||||||
$em->flush();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
'accompanying_period_id' => $period->getId(),
|
'accompanying_period_id' => $period->getId(),
|
||||||
]])
|
]])
|
||||||
->setExtras(['order' => 41]);
|
->setExtras(['order' => 35]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ class DocGeneratorTemplateController extends AbstractController
|
|||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
return $this->redirectToRoute('chill_wopi_file_edit', [
|
return $this->redirectToRoute('chill_wopi_file_edit', [
|
||||||
'fileId' => $genDocName,
|
'fileId' => $storedObject->getUuid(),
|
||||||
'returnPath' => $request->query->get('returnPath', "/")
|
'returnPath' => $request->query->get('returnPath', "/")
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@ namespace Chill\DocStoreBundle\Entity;
|
|||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface;
|
use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface;
|
||||||
use ChampsLibres\AsyncUploaderBundle\Validator\Constraints\AsyncFileExists;
|
use ChampsLibres\AsyncUploaderBundle\Validator\Constraints\AsyncFileExists;
|
||||||
|
use ChampsLibres\WopiLib\Contract\Entity\Document;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
|
use Ramsey\Uuid\UuidInterface;
|
||||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,7 +22,7 @@ use Symfony\Component\Serializer\Annotation as Serializer;
|
|||||||
* message="The file is not stored properly"
|
* message="The file is not stored properly"
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
class StoredObject implements AsyncFileInterface
|
class StoredObject implements AsyncFileInterface, Document
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @ORM\Id()
|
* @ORM\Id()
|
||||||
@ -47,6 +50,12 @@ class StoredObject implements AsyncFileInterface
|
|||||||
*/
|
*/
|
||||||
private array $iv = [];
|
private array $iv = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="uuid", unique=true)
|
||||||
|
* @Serializer\Groups({"read"})
|
||||||
|
*/
|
||||||
|
private UuidInterface $uuid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="datetime", name="creation_date")
|
* @ORM\Column(type="datetime", name="creation_date")
|
||||||
* @Serializer\Groups({"read"})
|
* @Serializer\Groups({"read"})
|
||||||
@ -68,6 +77,7 @@ class StoredObject implements AsyncFileInterface
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->creationDate = new \DateTime();
|
$this->creationDate = new \DateTime();
|
||||||
|
$this->uuid = Uuid::uuid4();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId()
|
public function getId()
|
||||||
@ -155,5 +165,13 @@ class StoredObject implements AsyncFileInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUuid(): UuidInterface
|
||||||
|
{
|
||||||
|
return $this->uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWopiDocId(): string
|
||||||
|
{
|
||||||
|
return (string) $this->uuid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,12 @@
|
|||||||
"description": "A Chill bundle to store documents",
|
"description": "A Chill bundle to store documents",
|
||||||
"type": "symfony-bundle",
|
"type": "symfony-bundle",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": { "Chill\\DocStoreBundle\\" : "" }
|
"psr-4": {
|
||||||
|
"Chill\\DocStoreBundle\\": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
"symfony/mime": "^4 || ^5"
|
||||||
},
|
},
|
||||||
"license": "AGPL-3.0"
|
"license": "AGPL-3.0"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\DocStore;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20210928182542 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Create UUID column on StoredObject table.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"');
|
||||||
|
$this->addSql('ALTER TABLE chill_doc.stored_object ADD uuid UUID DEFAULT NULL');
|
||||||
|
$this->addSql('UPDATE chill_doc.stored_object SET uuid=uuid_generate_v4()');
|
||||||
|
$this->addSql('ALTER TABLE chill_doc.stored_object ALTER uuid SET NOT NULL');
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX UNIQ_49604E36D17F50A6 ON chill_doc.stored_object (uuid)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE chill_doc.stored_object DROP uuid');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||||
|
use Chill\MainBundle\Entity\Address;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
|
||||||
|
class AddressApiController extends ApiController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Duplicate an existing address
|
||||||
|
*
|
||||||
|
* @Route("/api/1.0/main/address/{id}/duplicate.json", name="chill_api_main_address_duplicate",
|
||||||
|
* methods={"POST"})
|
||||||
|
*
|
||||||
|
* @param Address $address
|
||||||
|
*/
|
||||||
|
public function duplicate(Address $address): JsonResponse
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||||
|
$new = Address::createFromAddress($address);
|
||||||
|
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$em->persist($new);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->json($new, Response::HTTP_OK, [], [
|
||||||
|
AbstractNormalizer::GROUPS => ['read']
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
namespace Chill\MainBundle\DependencyInjection;
|
namespace Chill\MainBundle\DependencyInjection;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Controller\AddressApiController;
|
||||||
use Chill\MainBundle\Controller\UserController;
|
use Chill\MainBundle\Controller\UserController;
|
||||||
use Chill\MainBundle\Doctrine\DQL\STContains;
|
use Chill\MainBundle\Doctrine\DQL\STContains;
|
||||||
use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS;
|
use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS;
|
||||||
@ -319,6 +320,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
|||||||
'apis' => [
|
'apis' => [
|
||||||
[
|
[
|
||||||
'class' => \Chill\MainBundle\Entity\Address::class,
|
'class' => \Chill\MainBundle\Entity\Address::class,
|
||||||
|
'controller' => AddressApiController::class,
|
||||||
'name' => 'address',
|
'name' => 'address',
|
||||||
'base_path' => '/api/1.0/main/address',
|
'base_path' => '/api/1.0/main/address',
|
||||||
'base_role' => 'ROLE_USER',
|
'base_role' => 'ROLE_USER',
|
||||||
|
@ -376,10 +376,22 @@ class Address
|
|||||||
public static function createFromAddress(Address $original) : Address
|
public static function createFromAddress(Address $original) : Address
|
||||||
{
|
{
|
||||||
return (new Address())
|
return (new Address())
|
||||||
|
->setBuildingName($original->getBuildingName())
|
||||||
|
->setCorridor($original->getCorridor())
|
||||||
|
->setCustoms($original->getCustoms())
|
||||||
|
->setDistribution($original->getDistribution())
|
||||||
|
->setExtra($original->getExtra())
|
||||||
|
->setFlat($original->getFlat())
|
||||||
|
->setFloor($original->getFloor())
|
||||||
|
->setIsNoAddress($original->getIsNoAddress())
|
||||||
|
->setLinkedToThirdParty($original->getLinkedToThirdParty())
|
||||||
|
->setPoint($original->getPoint())
|
||||||
->setPostcode($original->getPostcode())
|
->setPostcode($original->getPostcode())
|
||||||
->setStreetAddress1($original->getStreetAddress1())
|
->setSteps($original->getSteps())
|
||||||
->setStreetAddress2($original->getStreetAddress2())
|
->setStreet($original->getStreet())
|
||||||
|
->setStreetNumber($original->getStreetNumber())
|
||||||
->setValidFrom($original->getValidFrom())
|
->setValidFrom($original->getValidFrom())
|
||||||
|
->setValidTo($original->getValidTo())
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,7 +518,7 @@ class Address
|
|||||||
return $this->validTo;
|
return $this->validTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setValidTo(\DateTimeInterface $validTo): self
|
public function setValidTo(?\DateTimeInterface $validTo = null): self
|
||||||
{
|
{
|
||||||
$this->validTo = $validTo;
|
$this->validTo = $validTo;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ namespace Chill\MainBundle\Repository;
|
|||||||
use Chill\MainBundle\Entity\Address;
|
use Chill\MainBundle\Entity\Address;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Doctrine\Persistence\ObjectRepository;
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
final class AddressRepository implements ObjectRepository
|
final class AddressRepository implements ObjectRepository
|
||||||
@ -47,4 +48,9 @@ final class AddressRepository implements ObjectRepository
|
|||||||
public function getClassName() {
|
public function getClassName() {
|
||||||
return Address::class;
|
return Address::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
|
||||||
|
{
|
||||||
|
return $this->repository->createQueryBuilder($alias, $indexBy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ const fetchScopes = () => {
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
console.log(data);
|
//console.log(data);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log(data);
|
//console.log(data);
|
||||||
resolve(data.results);
|
resolve(data.results);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Endpoint chill_api_single_country__index
|
* Endpoint chill_api_single_country__index
|
||||||
* method GET, get Country Object
|
* method GET, get Country Object
|
||||||
* @returns {Promise} a promise containing all Country object
|
* @returns {Promise} a promise containing all Country object
|
||||||
@ -14,7 +14,7 @@ const fetchCountries = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Endpoint chill_api_single_postal_code__index
|
* Endpoint chill_api_single_postal_code__index
|
||||||
* method GET, get Country Object
|
* method GET, get Country Object
|
||||||
* @returns {Promise} a promise containing all Postal Code objects filtered with country
|
* @returns {Promise} a promise containing all Postal Code objects filtered with country
|
||||||
@ -29,7 +29,7 @@ const fetchCities = (country) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Endpoint chill_api_single_address_reference__index
|
* Endpoint chill_api_single_address_reference__index
|
||||||
* method GET, get AddressReference Object
|
* method GET, get AddressReference Object
|
||||||
* @returns {Promise} a promise containing all AddressReference objects filtered with postal code
|
* @returns {Promise} a promise containing all AddressReference objects filtered with postal code
|
||||||
@ -44,7 +44,7 @@ const fetchReferenceAddresses = (postalCode) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Endpoint chill_api_single_address_reference__index
|
* Endpoint chill_api_single_address_reference__index
|
||||||
* method GET, get AddressReference Object
|
* method GET, get AddressReference Object
|
||||||
* @returns {Promise} a promise containing all AddressReference objects filtered with postal code
|
* @returns {Promise} a promise containing all AddressReference objects filtered with postal code
|
||||||
@ -60,7 +60,7 @@ const fetchAddresses = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Endpoint chill_api_single_address__entity__create
|
* Endpoint chill_api_single_address__entity__create
|
||||||
* method POST, post Address Object
|
* method POST, post Address Object
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
@ -81,8 +81,28 @@ const postAddress = (address) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param address
|
||||||
|
* @returns {Promise<Response>}
|
||||||
|
*/
|
||||||
|
const duplicateAddress = (address) => {
|
||||||
|
const url = `/api/1.0/main/address/${address.address_id}/duplicate.json`;
|
||||||
|
return fetch(url, {
|
||||||
|
'method': 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
|
},
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
|
/**
|
||||||
* Endpoint chill_api_single_address__entity__create
|
* Endpoint chill_api_single_address__entity__create
|
||||||
* method PATCH, patch Address Instance
|
* method PATCH, patch Address Instance
|
||||||
*
|
*
|
||||||
@ -142,6 +162,7 @@ const getAddress = (id) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
duplicateAddress,
|
||||||
fetchCountries,
|
fetchCountries,
|
||||||
fetchCities,
|
fetchCities,
|
||||||
fetchReferenceAddresses,
|
fetchReferenceAddresses,
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul class="record_actions"
|
<ul class="record_actions" v-if="!options.onlyButton"
|
||||||
:class="{ 'sticky-form-buttons': isStickyForm }">
|
:class="{ 'sticky-form-buttons': isStickyForm }">
|
||||||
|
|
||||||
<li v-if="isStickyForm" class="cancel">
|
<li v-if="isStickyForm" class="cancel">
|
||||||
<slot name="before"></slot>
|
<slot name="before"></slot>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
<slot name="action"></slot>
|
<slot name="action"></slot>
|
||||||
|
</li>
|
||||||
<li v-if="isStickyForm">
|
<li v-if="isStickyForm">
|
||||||
<slot name="after"></slot>
|
<slot name="after"></slot>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<slot v-else name="action"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -23,9 +24,6 @@ export default {
|
|||||||
return (typeof this.options.stickyActions !== 'undefined') ?
|
return (typeof this.options.stickyActions !== 'undefined') ?
|
||||||
this.options.stickyActions : this.defaultz.stickyActions;
|
this.options.stickyActions : this.defaultz.stickyActions;
|
||||||
},
|
},
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
v-bind:defaultz="this.defaultz"
|
v-bind:defaultz="this.defaultz"
|
||||||
v-bind:entity="this.entity"
|
v-bind:entity="this.entity"
|
||||||
v-bind:flag="this.flag"
|
v-bind:flag="this.flag"
|
||||||
|
@pick-address="this.pickAddress"
|
||||||
ref="suggestAddress">
|
ref="suggestAddress">
|
||||||
</suggest-pane>
|
</suggest-pane>
|
||||||
</template>
|
</template>
|
||||||
@ -55,6 +56,7 @@
|
|||||||
v-bind:entity="this.entity"
|
v-bind:entity="this.entity"
|
||||||
v-bind:flag="this.flag"
|
v-bind:flag="this.flag"
|
||||||
v-bind:insideModal="false"
|
v-bind:insideModal="false"
|
||||||
|
@pick-address="this.pickAddress"
|
||||||
ref="suggestAddress">
|
ref="suggestAddress">
|
||||||
|
|
||||||
<template v-slot:before v-if="!bypassFirstStep">
|
<template v-slot:before v-if="!bypassFirstStep">
|
||||||
@ -217,7 +219,16 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
|
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
|
||||||
import { getAddress, fetchCountries, fetchCities, fetchReferenceAddresses, patchAddress, postAddress, postPostalCode } from '../api';
|
import {
|
||||||
|
duplicateAddress,
|
||||||
|
fetchCountries,
|
||||||
|
fetchCities,
|
||||||
|
fetchReferenceAddresses,
|
||||||
|
getAddress,
|
||||||
|
patchAddress,
|
||||||
|
postAddress,
|
||||||
|
postPostalCode,
|
||||||
|
} from '../api';
|
||||||
import { postAddressToPerson, postAddressToHousehold } from "ChillPersonAssets/vuejs/_api/AddAddress.js";
|
import { postAddressToPerson, postAddressToHousehold } from "ChillPersonAssets/vuejs/_api/AddAddress.js";
|
||||||
import ShowPane from './ShowPane.vue';
|
import ShowPane from './ShowPane.vue';
|
||||||
import SuggestPane from './SuggestPane.vue';
|
import SuggestPane from './SuggestPane.vue';
|
||||||
@ -234,6 +245,9 @@ export default {
|
|||||||
EditPane,
|
EditPane,
|
||||||
DatePane
|
DatePane
|
||||||
},
|
},
|
||||||
|
emits: {
|
||||||
|
pickAddress: null
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
flag: {
|
flag: {
|
||||||
@ -257,7 +271,7 @@ export default {
|
|||||||
validFrom: false,
|
validFrom: false,
|
||||||
validTo: false
|
validTo: false
|
||||||
},
|
},
|
||||||
hideAddress: false
|
onlyButton: false
|
||||||
},
|
},
|
||||||
entity: {
|
entity: {
|
||||||
address: {}, // <== loaded and returned
|
address: {}, // <== loaded and returned
|
||||||
@ -311,8 +325,10 @@ export default {
|
|||||||
return (this.validFrom || this.validTo) ? true : false;
|
return (this.validFrom || this.validTo) ? true : false;
|
||||||
},
|
},
|
||||||
hasSuggestions() {
|
hasSuggestions() {
|
||||||
// TODO
|
console.log(this.context.suggestions);
|
||||||
//return addressSuggestions.length > 0
|
if (typeof(this.context.suggestions) !== 'undefined') {
|
||||||
|
return this.context.suggestions.length > 0;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
displaySuggestions() {
|
displaySuggestions() {
|
||||||
@ -647,9 +663,12 @@ export default {
|
|||||||
this.flag.loading = false;
|
this.flag.loading = false;
|
||||||
this.flag.success = true;
|
this.flag.success = true;
|
||||||
resolve({
|
resolve({
|
||||||
|
address,
|
||||||
|
targetOrigin: this.context.target,
|
||||||
|
// for "legacy" use:
|
||||||
target: this.context.target.name,
|
target: this.context.target.name,
|
||||||
targetId: this.context.target.id,
|
targetId: this.context.target.id,
|
||||||
addressId: this.entity.address.address_id
|
addressId: this.entity.address.address_id,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}))
|
}))
|
||||||
@ -695,6 +714,9 @@ export default {
|
|||||||
this.flag.loading = false;
|
this.flag.loading = false;
|
||||||
this.flag.success = true;
|
this.flag.success = true;
|
||||||
return resolve({
|
return resolve({
|
||||||
|
address,
|
||||||
|
targetOrigin: this.context.target,
|
||||||
|
// for "legacy" use:
|
||||||
target: this.context.target.name,
|
target: this.context.target.name,
|
||||||
targetId: this.context.target.id,
|
targetId: this.context.target.id,
|
||||||
addressId: this.entity.address.address_id
|
addressId: this.entity.address.address_id
|
||||||
@ -707,18 +729,28 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Method just add closing pane to the callback method
|
*
|
||||||
* (get out step1 show pane, submit button)
|
* @param address the address selected
|
||||||
closePaneAndCallbackSubmit(payload)
|
|
||||||
{
|
|
||||||
//this.initForm();
|
|
||||||
//this.resetPane(); // because parent callback will cast afterLastPaneAction()
|
|
||||||
console.log('will call parent callback method', payload);
|
|
||||||
// callback props method from parent
|
|
||||||
this.addressChangedCallback(payload);
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
pickAddress(address) {
|
||||||
|
console.log('pickAddress', address);
|
||||||
|
duplicateAddress(address).then(newAddress => {
|
||||||
|
this.entity.address = newAddress;
|
||||||
|
this.flag.loading = false;
|
||||||
|
this.flag.success = true;
|
||||||
|
let payload = {
|
||||||
|
address: newAddress,
|
||||||
|
targetOrigin: this.context.target,
|
||||||
|
// for "legacy" use:
|
||||||
|
target: this.context.target.name,
|
||||||
|
targetId: this.context.target.id,
|
||||||
|
addressId: this.entity.address.address_id
|
||||||
|
};
|
||||||
|
this.addressChangedCallback(payload);
|
||||||
|
this.closeSuggestPane();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div v-if="!hideAddress">
|
<div v-if="!onlyButton">
|
||||||
<div class="loading">
|
<div class="loading">
|
||||||
<i v-if="flag.loading" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"></i>
|
<i v-if="flag.loading" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"></i>
|
||||||
<span class="sr-only">{{ $t('loading') }}</span>
|
<span class="sr-only">{{ $t('loading') }}</span>
|
||||||
@ -28,13 +28,11 @@
|
|||||||
:options="this.options"
|
:options="this.options"
|
||||||
:defaultz="this.defaultz">
|
:defaultz="this.defaultz">
|
||||||
<template v-slot:action>
|
<template v-slot:action>
|
||||||
<li>
|
<button @click.prevent="$emit('openEditPane')"
|
||||||
<button @click.prevent="$emit('openEditPane')"
|
class="btn" :class="getClassButton"
|
||||||
class="btn" :class="getClassButton"
|
type="button" name="button" :title="$t(getTextButton)">
|
||||||
type="button" name="button" :title="$t(getTextButton)">
|
<span v-if="displayTextButton">{{ $t(getTextButton) }}</span>
|
||||||
<span v-if="displayTextButton">{{ $t(getTextButton) }}</span>
|
</button>
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</template>
|
</template>
|
||||||
</action-buttons>
|
</action-buttons>
|
||||||
|
|
||||||
@ -86,9 +84,9 @@ export default {
|
|||||||
getSuccessText() {
|
getSuccessText() {
|
||||||
return (this.context.edit) ? 'address_edit_success' : 'address_new_success';
|
return (this.context.edit) ? 'address_edit_success' : 'address_new_success';
|
||||||
},
|
},
|
||||||
hideAddress() {
|
onlyButton() {
|
||||||
return (typeof this.options.hideAddress !== 'undefined') ?
|
return (typeof this.options.onlyButton !== 'undefined') ?
|
||||||
this.options.hideAddress : this.defaultz.hideAddress;
|
this.options.onlyButton : this.defaultz.onlyButton;
|
||||||
},
|
},
|
||||||
forceRedirect() {
|
forceRedirect() {
|
||||||
return (!(this.context.backUrl === null || typeof this.context.backUrl === 'undefined'));
|
return (!(this.context.backUrl === null || typeof this.context.backUrl === 'undefined'));
|
||||||
|
@ -10,13 +10,14 @@
|
|||||||
<h4 class="h3">{{ $t('address_suggestions') }}</h4>
|
<h4 class="h3">{{ $t('address_suggestions') }}</h4>
|
||||||
|
|
||||||
<div class="flex-table AddressSuggestionList">
|
<div class="flex-table AddressSuggestionList">
|
||||||
<div class="item-bloc">
|
<div v-for="a in context.suggestions" class="item-bloc">
|
||||||
<div class="float-button bottom">
|
<div class="float-button bottom">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="action">
|
<div class="action">
|
||||||
|
<!-- QUESTION normal que ça vienne avant l'adresse ? pourquoi pas après avoir affiché le address-render-box ? -->
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li>
|
<li>
|
||||||
<button class="btn btn-sm btn-choose">
|
<button class="btn btn-sm btn-choose" @click="this.pickAddress(a)">
|
||||||
{{ $t('use_this_address') }}
|
{{ $t('use_this_address') }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
@ -25,9 +26,7 @@
|
|||||||
<ul class="list-content fa-ul">
|
<ul class="list-content fa-ul">
|
||||||
<li>
|
<li>
|
||||||
<i class="fa fa-li fa-map-marker"></i>
|
<i class="fa fa-li fa-map-marker"></i>
|
||||||
<!--
|
<address-render-box :address="a"></address-render-box>
|
||||||
<address-render-box></address-render-box>
|
|
||||||
-->
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -68,9 +67,14 @@ export default {
|
|||||||
'flag',
|
'flag',
|
||||||
'entity',
|
'entity',
|
||||||
'errorMsg',
|
'errorMsg',
|
||||||
'insideModal'
|
'insideModal',
|
||||||
],
|
],
|
||||||
computed: {},
|
computed: {},
|
||||||
methods: {}
|
methods: {
|
||||||
|
pickAddress(address) {
|
||||||
|
console.log('pickAddress in suggest pane', address);
|
||||||
|
this.$emit('pickAddress', address);
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -53,7 +53,7 @@ containers.forEach((container) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// Don't display show renderbox Address: showPane display only a button
|
/// Don't display show renderbox Address: showPane display only a button
|
||||||
hideAddress: container.dataset.hideAddress === 'true' //boolean, default: false
|
onlyButton: container.dataset.onlyButton === 'true' //boolean, default: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,16 +42,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:footer>
|
<template v-slot:footer>
|
||||||
<button v-if="action === 'show'"
|
<a v-if="action === 'show'"
|
||||||
@click="goToLocation(id, type)"
|
:href="buildLocation(id, type)"
|
||||||
:title="$t(titleMessage)"
|
:title="$t(titleMessage)"
|
||||||
class="btn btn-show">{{ $t(buttonMessage) }}
|
class="btn btn-show">{{ $t(buttonMessage) }}
|
||||||
</button>
|
</a>
|
||||||
<button v-else
|
<a v-else
|
||||||
class="btn btn-save"
|
class="btn btn-save"
|
||||||
@click="saveAction">
|
@click="saveAction">
|
||||||
{{ $t('action.save')}}
|
{{ $t('action.save')}}
|
||||||
</button>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</modal>
|
</modal>
|
||||||
@ -134,6 +134,8 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openModal() {
|
openModal() {
|
||||||
|
console.log('## OPEN ON THE FLY MODAL');
|
||||||
|
console.log('## type:', this.type, ', action:', this.action);
|
||||||
this.modal.showModal = true;
|
this.modal.showModal = true;
|
||||||
this.$nextTick(function() {
|
this.$nextTick(function() {
|
||||||
//this.$refs.search.focus();
|
//this.$refs.search.focus();
|
||||||
@ -144,7 +146,6 @@ export default {
|
|||||||
},
|
},
|
||||||
saveAction() {
|
saveAction() {
|
||||||
console.log('saveAction button: create/edit action with', this.type);
|
console.log('saveAction button: create/edit action with', this.type);
|
||||||
|
|
||||||
let
|
let
|
||||||
type = this.type,
|
type = this.type,
|
||||||
data = {} ;
|
data = {} ;
|
||||||
@ -173,11 +174,12 @@ export default {
|
|||||||
|
|
||||||
this.modal.showModal = false;
|
this.modal.showModal = false;
|
||||||
},
|
},
|
||||||
goToLocation(id, type){
|
buildLocation(id, type) {
|
||||||
if(type == 'person'){
|
if (type == 'person') {
|
||||||
window.location = `../../person/${id}/general`
|
// TODO i18n
|
||||||
} else if(type == 'thirdparty') {
|
return `/fr/person/${id}/general`;
|
||||||
window.location = `../../thirdparty/thirdparty/${id}/show`
|
} else if (type == 'thirdparty') {
|
||||||
|
return `/fr/thirdparty/thirdparty/${id}/show`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" :class="{ active: isActive('person') }">
|
<a class="nav-link" :class="{ active: isActive('person') }">
|
||||||
<label for="person">
|
<label for="person">
|
||||||
<input type="radio" name="person" v-model="radioType" value="person">
|
<input type="radio" name="person" id="person" v-model="radioType" value="person">
|
||||||
{{ $t('onthefly.create.person') }}
|
{{ $t('onthefly.create.person') }}
|
||||||
</label>
|
</label>
|
||||||
</a>
|
</a>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" :class="{ active: isActive('thirdparty') }">
|
<a class="nav-link" :class="{ active: isActive('thirdparty') }">
|
||||||
<label for="thirdparty">
|
<label for="thirdparty">
|
||||||
<input type="radio" name="thirdparty" v-model="radioType" value="thirdparty">
|
<input type="radio" name="thirdparty" id="thirdparty" v-model="radioType" value="thirdparty">
|
||||||
{{ $t('onthefly.create.thirdparty') }}
|
{{ $t('onthefly.create.thirdparty') }}
|
||||||
</label>
|
</label>
|
||||||
</a>
|
</a>
|
||||||
@ -56,6 +56,7 @@ export default {
|
|||||||
radioType: {
|
radioType: {
|
||||||
set(type) {
|
set(type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
console.log('## type:', type, ', action:', this.action);
|
||||||
},
|
},
|
||||||
get() {
|
get() {
|
||||||
return this.type;
|
return this.type;
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* stickyActions bool (default: false)
|
* stickyActions bool (default: false)
|
||||||
* useValidFrom bool (default: false)
|
* useValidFrom bool (default: false)
|
||||||
* useValidTo bool (default: false)
|
* useValidTo bool (default: false)
|
||||||
* hideAddress bool (default: false)
|
* onlyButton bool (default: false)
|
||||||
#}
|
#}
|
||||||
<div class="address-container"
|
<div class="address-container"
|
||||||
|
|
||||||
@ -69,7 +69,7 @@
|
|||||||
data-use-valid-to="true"
|
data-use-valid-to="true"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if hideAddress is defined and hideAddress == 1 %}
|
{% if onlyButton is defined and onlyButton == 1 %}
|
||||||
data-hide-address="true"
|
data-hide-address="true"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
></div>
|
></div>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block table_entities_tbody %}
|
{% block table_entities_tbody %}
|
||||||
{% for entity in entities %}
|
{% for entity in entities %}
|
||||||
<tr>
|
<tr data-username="{{ entity.username|e('html_attr') }}">
|
||||||
<td>
|
<td>
|
||||||
{% if entity.isEnabled %}
|
{% if entity.isEnabled %}
|
||||||
<i class="fa fa-check chill-green"></i>
|
<i class="fa fa-check chill-green"></i>
|
||||||
|
@ -17,12 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
namespace Chill\MainBundle\Test;
|
namespace Chill\MainBundle\Test;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare a client authenticated with a user
|
* Prepare a client authenticated with a user
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
*/
|
||||||
trait PrepareClientTrait
|
trait PrepareClientTrait
|
||||||
{
|
{
|
||||||
@ -37,7 +36,7 @@ trait PrepareClientTrait
|
|||||||
public function getClientAuthenticated(
|
public function getClientAuthenticated(
|
||||||
$username = 'center a_social',
|
$username = 'center a_social',
|
||||||
$password = 'password'
|
$password = 'password'
|
||||||
) {
|
): KernelBrowser {
|
||||||
if (!$this instanceof WebTestCase) {
|
if (!$this instanceof WebTestCase) {
|
||||||
throw new \LogicException(sprintf("The current class does not "
|
throw new \LogicException(sprintf("The current class does not "
|
||||||
. "implements %s", WebTestCase::class));
|
. "implements %s", WebTestCase::class));
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Address;
|
||||||
|
use Chill\MainBundle\Repository\AddressRepository;
|
||||||
|
use Chill\MainBundle\Test\PrepareClientTrait;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||||
|
|
||||||
|
class AddressControllerTest extends \Symfony\Bundle\FrameworkBundle\Test\WebTestCase
|
||||||
|
{
|
||||||
|
private KernelBrowser $client;
|
||||||
|
|
||||||
|
use PrepareClientTrait;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->client = $this->getClientAuthenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider generateAddressIds
|
||||||
|
* @param int $addressId
|
||||||
|
*/
|
||||||
|
public function testDuplicate(int $addressId)
|
||||||
|
{
|
||||||
|
$this->client->request('POST', "/api/1.0/main/address/$addressId/duplicate.json");
|
||||||
|
|
||||||
|
$this->assertResponseIsSuccessful('test that duplicate is successful');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateAddressIds()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$qb = $em->createQueryBuilder();
|
||||||
|
$addresses = $qb->select('a')->from(Address::class, 'a')
|
||||||
|
->setMaxResults(2)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
foreach ($addresses as $a) {
|
||||||
|
yield [ $a->getId() ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,17 @@
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Tests\Controller;
|
namespace Chill\MainBundle\Tests\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||||
|
|
||||||
class UserControllerTest extends WebTestCase
|
class UserControllerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
private $client;
|
private $client;
|
||||||
|
|
||||||
|
private array $toDelete = [];
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
self::bootKernel();
|
self::bootKernel();
|
||||||
@ -22,18 +27,14 @@ class UserControllerTest extends WebTestCase
|
|||||||
public function testList()
|
public function testList()
|
||||||
{
|
{
|
||||||
// get the list
|
// get the list
|
||||||
$crawler = $this->client->request('GET', '/fr/admin/user/');
|
$crawler = $this->client->request('GET', '/fr/admin/main/user');
|
||||||
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(),
|
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(),
|
||||||
"Unexpected HTTP status code for GET /admin/user/");
|
"Unexpected HTTP status code for GET /admin/main/user");
|
||||||
|
|
||||||
$link = $crawler->selectLink('Ajouter un nouvel utilisateur')->link();
|
|
||||||
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $link);
|
|
||||||
$this->assertRegExp('|/fr/admin/user/new$|', $link->getUri());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNew()
|
public function testNew()
|
||||||
{
|
{
|
||||||
$crawler = $this->client->request('GET', '/fr/admin/user/new');
|
$crawler = $this->client->request('GET', '/fr/admin/main/user/new');
|
||||||
|
|
||||||
$username = 'Test_user'. uniqid();
|
$username = 'Test_user'. uniqid();
|
||||||
$password = 'Password1234!';
|
$password = 'Password1234!';
|
||||||
@ -54,22 +55,15 @@ class UserControllerTest extends WebTestCase
|
|||||||
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test_user")')->count(),
|
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test_user")')->count(),
|
||||||
'Missing element td:contains("Test user")');
|
'Missing element td:contains("Test user")');
|
||||||
|
|
||||||
$update = $crawler->selectLink('Modifier')->link();
|
|
||||||
|
|
||||||
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $update);
|
|
||||||
$this->assertRegExp('|/fr/admin/user/[0-9]{1,}/edit$|', $update->getUri());
|
|
||||||
|
|
||||||
//test the auth of the new client
|
//test the auth of the new client
|
||||||
$this->isPasswordValid($username, $password);
|
$this->isPasswordValid($username, $password);
|
||||||
|
|
||||||
return $update;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isPasswordValid($username, $password)
|
protected function isPasswordValid($username, $password)
|
||||||
{
|
{
|
||||||
/* @var $passwordEncoder \Symfony\Component\Security\Core\Encoder\UserPasswordEncoder */
|
/* @var $passwordEncoder \Symfony\Component\Security\Core\Encoder\UserPasswordEncoder */
|
||||||
$passwordEncoder = self::$kernel->getContainer()
|
$passwordEncoder = self::$container
|
||||||
->get('security.password_encoder');
|
->get(UserPasswordEncoderInterface::class);
|
||||||
|
|
||||||
$user = self::$kernel->getContainer()
|
$user = self::$kernel->getContainer()
|
||||||
->get('doctrine.orm.entity_manager')
|
->get('doctrine.orm.entity_manager')
|
||||||
@ -81,46 +75,33 @@ class UserControllerTest extends WebTestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param \Symfony\Component\DomCrawler\Link $update
|
* @dataProvider dataGenerateUserId
|
||||||
* @depends testNew
|
|
||||||
*/
|
*/
|
||||||
public function testUpdate(\Symfony\Component\DomCrawler\Link $update)
|
public function testUpdate(int $userId, string $username)
|
||||||
{
|
{
|
||||||
$crawler = $this->client->click($update);
|
$crawler = $this->client->request('GET', "/fr/admin/main/user/$userId/edit");
|
||||||
|
|
||||||
$username = 'Foo bar '.uniqid();
|
$username = 'Foo bar '.uniqid();
|
||||||
$form = $crawler->selectButton('Mettre à jour')->form(array(
|
$form = $crawler->selectButton('Enregistrer & fermer')->form(array(
|
||||||
'chill_mainbundle_user[username]' => $username,
|
'chill_mainbundle_user[username]' => $username,
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->client->submit($form);
|
$this->client->submit($form);
|
||||||
$crawler = $this->client->followRedirect();
|
$crawler = $this->client->followRedirect();
|
||||||
// Check the element contains an attribute with value equals "Foo"
|
// Check the element contains an attribute with value equals "Foo"
|
||||||
$this->assertGreaterThan(0, $crawler->filter('[value="'.$username.'"]')->count(),
|
$this->assertGreaterThan(0, $crawler->filter('[data-username="'.$username.'"]')->count(),
|
||||||
'Missing element [value="Foo bar"]');
|
'Missing element [data-username="Foo bar"]');
|
||||||
|
|
||||||
$updatePassword = $crawler->selectLink('Modifier le mot de passe')->link();
|
|
||||||
|
|
||||||
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $updatePassword);
|
|
||||||
$this->assertRegExp('|/fr/admin/user/[0-9]{1,}/edit_password$|',
|
|
||||||
$updatePassword->getUri());
|
|
||||||
|
|
||||||
return array('link' => $updatePassword, 'username' => $username);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param \Symfony\Component\DomCrawler\Link $updatePassword
|
* @dataProvider dataGenerateUserId
|
||||||
* @depends testUpdate
|
|
||||||
*/
|
*/
|
||||||
public function testUpdatePassword(array $params)
|
public function testUpdatePassword(int $userId, $username)
|
||||||
{
|
{
|
||||||
$link = $params['link'];
|
$crawler = $this->client->request('GET', "/fr/admin/user/$userId/edit_password");
|
||||||
$username = $params['username'];
|
|
||||||
$newPassword = '1234Password!';
|
$newPassword = '1234Password!';
|
||||||
|
|
||||||
$crawler = $this->client->click($link);
|
|
||||||
|
|
||||||
$form = $crawler->selectButton('Changer le mot de passe')->form(array(
|
$form = $crawler->selectButton('Changer le mot de passe')->form(array(
|
||||||
'chill_mainbundle_user_password[new_password][first]' => $newPassword,
|
'chill_mainbundle_user_password[new_password][first]' => $newPassword,
|
||||||
'chill_mainbundle_user_password[new_password][second]' => $newPassword,
|
'chill_mainbundle_user_password[new_password][second]' => $newPassword,
|
||||||
@ -130,10 +111,38 @@ class UserControllerTest extends WebTestCase
|
|||||||
|
|
||||||
$this->assertTrue($this->client->getResponse()->isRedirect(),
|
$this->assertTrue($this->client->getResponse()->isRedirect(),
|
||||||
"the response is a redirection");
|
"the response is a redirection");
|
||||||
$this->client->followRedirect();
|
|
||||||
|
|
||||||
$this->isPasswordValid($username, $newPassword);
|
$this->isPasswordValid($username, $newPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function tearDown()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
foreach ($this->toDelete as list($class, $id)) {
|
||||||
|
$obj = $em->getRepository($class)->find($id);
|
||||||
|
$em->remove($obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
$em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataGenerateUserId()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$em = self::$container->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$user = new User();
|
||||||
|
$user->setUsername('Test_user '.uniqid());
|
||||||
|
$user->setPassword(self::$container->get(UserPasswordEncoderInterface::class)->encodePassword($user,
|
||||||
|
'password'));
|
||||||
|
|
||||||
|
$em->persist($user);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$this->toDelete[] = [User::class, $user->getId()];
|
||||||
|
|
||||||
|
yield [ $user->getId(), $user->getUsername() ];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,6 +293,32 @@ paths:
|
|||||||
401:
|
401:
|
||||||
description: "Unauthorized"
|
description: "Unauthorized"
|
||||||
|
|
||||||
|
/1.0/main/address/{id}/duplicate.json:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- address
|
||||||
|
summary: Duplicate an existing address
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The address id that will be duplicated
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: integer
|
||||||
|
minimum: 1
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: "ok"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Address'
|
||||||
|
404:
|
||||||
|
description: "not found"
|
||||||
|
401:
|
||||||
|
description: "Unauthorized"
|
||||||
|
|
||||||
/1.0/main/address-reference.json:
|
/1.0/main/address-reference.json:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
|
@ -45,9 +45,9 @@ class PersonApiController extends ApiController
|
|||||||
$person = parent::createEntity($action, $request);
|
$person = parent::createEntity($action, $request);
|
||||||
|
|
||||||
// TODO temporary hack to allow creation of person with fake center
|
// TODO temporary hack to allow creation of person with fake center
|
||||||
$centers = $this->authorizationHelper->getReachableCenters($this->getUser(),
|
/* $centers = $this->authorizationHelper->getReachableCenters($this->getUser(),
|
||||||
new Role(PersonVoter::CREATE));
|
new Role(PersonVoter::CREATE));
|
||||||
$person->setCenter($centers[0]);
|
$person->setCenter($centers[0]); */
|
||||||
|
|
||||||
return $person;
|
return $person;
|
||||||
}
|
}
|
||||||
|
@ -247,7 +247,6 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
|
|||||||
if (\random_int(0, 10) > 3) {
|
if (\random_int(0, 10) > 3) {
|
||||||
// always add social scope:
|
// always add social scope:
|
||||||
$accompanyingPeriod->addScope($this->getReference('scope_social'));
|
$accompanyingPeriod->addScope($this->getReference('scope_social'));
|
||||||
var_dump(count($accompanyingPeriod->getScopes()));
|
|
||||||
|
|
||||||
$accompanyingPeriod->setAddressLocation($this->createAddress());
|
$accompanyingPeriod->setAddressLocation($this->createAddress());
|
||||||
$manager->persist($accompanyingPeriod->getAddressLocation());
|
$manager->persist($accompanyingPeriod->getAddressLocation());
|
||||||
|
@ -640,12 +640,13 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
|||||||
Request::METHOD_GET => true,
|
Request::METHOD_GET => true,
|
||||||
Request::METHOD_HEAD => true,
|
Request::METHOD_HEAD => true,
|
||||||
Request::METHOD_POST=> true,
|
Request::METHOD_POST=> true,
|
||||||
|
Request::METHOD_PATCH => true
|
||||||
],
|
],
|
||||||
'roles' => [
|
'roles' => [
|
||||||
Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
|
Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
|
||||||
Request::METHOD_HEAD => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
|
Request::METHOD_HEAD => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
|
||||||
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\PersonVoter::CREATE,
|
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\PersonVoter::CREATE,
|
||||||
|
Request::METHOD_PATCH => \Chill\PersonBundle\Security\Authorization\PersonVoter::CREATE,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'address' => [
|
'address' => [
|
||||||
|
@ -229,7 +229,7 @@ class Household
|
|||||||
))
|
))
|
||||||
->andWhere($expr->orX(
|
->andWhere($expr->orX(
|
||||||
$expr->isNull('endDate'),
|
$expr->isNull('endDate'),
|
||||||
$expr->gte('endDate', $date)
|
$expr->gt('endDate', $date)
|
||||||
));
|
));
|
||||||
|
|
||||||
return $criteria;
|
return $criteria;
|
||||||
@ -306,7 +306,7 @@ class Household
|
|||||||
)
|
)
|
||||||
->orWhere(
|
->orWhere(
|
||||||
$expr->andX(
|
$expr->andX(
|
||||||
$expr->lt('endDate', $date),
|
$expr->lte('endDate', $date),
|
||||||
$expr->neq('endDate', null)
|
$expr->neq('endDate', null)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -199,7 +199,7 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (NULL !== $birthdate) {
|
if (NULL !== $birthdate) {
|
||||||
$qb->andWhere($qb->expr()->eq('s.birthdate', ':birthdate'))
|
$qb->andWhere($qb->expr()->eq('p.birthdate', ':birthdate'))
|
||||||
->setParameter('birthdate', $birthdate);
|
->setParameter('birthdate', $birthdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,8 +87,8 @@ export default {
|
|||||||
}
|
}
|
||||||
padding: 0em 0em;
|
padding: 0em 0em;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
border: 1px dotted tint-color($chill-accourse-context, 10%);
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
border: 1px dotted tint-color($chill-accourse-context, 10%);
|
||||||
border-left: 1px dotted tint-color($chill-accourse-context, 10%);
|
border-left: 1px dotted tint-color($chill-accourse-context, 10%);
|
||||||
border-right: 1px dotted tint-color($chill-accourse-context, 10%);
|
border-right: 1px dotted tint-color($chill-accourse-context, 10%);
|
||||||
dd {
|
dd {
|
||||||
@ -96,10 +96,15 @@ export default {
|
|||||||
}
|
}
|
||||||
& > div {
|
& > div {
|
||||||
margin: 1em 3em 0;
|
margin: 1em 3em 0;
|
||||||
|
|
||||||
&.flex-table,
|
&.flex-table,
|
||||||
&.flex-bloc {
|
&.flex-bloc {
|
||||||
margin: 1em 0 0;
|
margin: 1em 0 0;
|
||||||
}
|
}
|
||||||
|
&.alert.to-confirm {
|
||||||
|
margin: 1em 0 0;
|
||||||
|
padding: 1em 3em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.flex-table {
|
div.flex-table {
|
||||||
|
@ -91,7 +91,7 @@ export default {
|
|||||||
},
|
},
|
||||||
scopes: {
|
scopes: {
|
||||||
msg: 'confirm.set_a_scope',
|
msg: 'confirm.set_a_scope',
|
||||||
anchor: '#section-65'
|
anchor: '#section-60'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,19 +27,34 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isTemporaryAddress" class="alert alert-warning separator">
|
<div v-if="isTemporaryAddress" class="alert alert-warning separator">
|
||||||
<p>{{ $t('courselocation.temporary_address_must_be_changed') }}</p>
|
<p>
|
||||||
|
{{ $t('courselocation.temporary_address_must_be_changed') }}
|
||||||
|
<i class="fa fa-fw fa-map-marker"></i>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="hasNoPersonLocation" class="alert alert-danger no-person-location">
|
||||||
|
<i class="fa fa-warning fa-2x"></i>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ $t('courselocation.associate_at_least_one_person_with_one_household_with_address') }}
|
||||||
|
<a href="#section-10">
|
||||||
|
<i class="fa fa-level-up fa-fw"></i>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li>
|
<li>
|
||||||
<add-address
|
<add-address
|
||||||
v-if="!isPersonLocation"
|
v-if="!isPersonLocation"
|
||||||
|
:key="key"
|
||||||
:context="context"
|
:context="context"
|
||||||
:key="addAddress.type"
|
:options="options"
|
||||||
:options="addAddress.options"
|
|
||||||
:addressChangedCallback="submitTemporaryAddress"
|
:addressChangedCallback="submitTemporaryAddress"
|
||||||
ref="addAddress">
|
ref="addAddress">
|
||||||
</add-address>
|
</add-address>
|
||||||
@ -55,11 +70,15 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!isLocationValid" class="alert alert-warning to-confirm">
|
||||||
|
{{ $t('courselocation.not_valid') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import {mapGetters, mapState} from "vuex";
|
||||||
import AddAddress from 'ChillMainAssets/vuejs/Address/components/AddAddress.vue';
|
import AddAddress from 'ChillMainAssets/vuejs/Address/components/AddAddress.vue';
|
||||||
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
|
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
|
||||||
|
|
||||||
@ -72,10 +91,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
addAddress: {
|
addAddress: {
|
||||||
type: 'accompanying_course_location',
|
|
||||||
options: {
|
options: {
|
||||||
/// Options override default.
|
|
||||||
/// null value take default component value
|
|
||||||
button: {
|
button: {
|
||||||
text: {
|
text: {
|
||||||
create: 'courselocation.add_temporary_address',
|
create: 'courselocation.add_temporary_address',
|
||||||
@ -86,13 +102,7 @@ export default {
|
|||||||
create: 'courselocation.add_temporary_address',
|
create: 'courselocation.add_temporary_address',
|
||||||
edit: 'courselocation.edit_temporary_address'
|
edit: 'courselocation.edit_temporary_address'
|
||||||
},
|
},
|
||||||
/// Display each step in page or Modal
|
onlyButton: true
|
||||||
openPanesInModal: true,
|
|
||||||
// Use Date fields
|
|
||||||
//useDate: {
|
|
||||||
// validFrom: true
|
|
||||||
//},
|
|
||||||
hideAddress: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,6 +112,16 @@ export default {
|
|||||||
accompanyingCourse: state => state.accompanyingCourse,
|
accompanyingCourse: state => state.accompanyingCourse,
|
||||||
context: state => state.addressContext
|
context: state => state.addressContext
|
||||||
}),
|
}),
|
||||||
|
...mapGetters([
|
||||||
|
'isLocationValid'
|
||||||
|
]),
|
||||||
|
options() {
|
||||||
|
return this.addAddress.options;
|
||||||
|
},
|
||||||
|
key() {
|
||||||
|
return (this.context.edit) ? 'address_' + this.context.addressId
|
||||||
|
: this.accompanyingCourse.type + '_' + this.accompanyingCourse.id ;
|
||||||
|
},
|
||||||
isTemporaryAddress() {
|
isTemporaryAddress() {
|
||||||
return this.accompanyingCourse.locationStatus === 'address';
|
return this.accompanyingCourse.locationStatus === 'address';
|
||||||
},
|
},
|
||||||
@ -111,11 +131,40 @@ export default {
|
|||||||
hasNoLocation() {
|
hasNoLocation() {
|
||||||
return this.accompanyingCourse.locationStatus === 'none';
|
return this.accompanyingCourse.locationStatus === 'none';
|
||||||
},
|
},
|
||||||
|
currentParticipations() {
|
||||||
|
return this.accompanyingCourse.participations.filter(p => p.enddate !== null);
|
||||||
|
},
|
||||||
|
hasNoPersonLocation() {
|
||||||
|
|
||||||
|
let addressInParticipations_ = []
|
||||||
|
this.currentParticipations.forEach(p => {
|
||||||
|
addressInParticipations_.push(this.checkHouseholdAddressForParticipation(p));
|
||||||
|
});
|
||||||
|
|
||||||
|
const booleanReducer = (previousValue, currentValue) => previousValue || currentValue;
|
||||||
|
|
||||||
|
let addressInParticipations = (addressInParticipations_.length > 0) ?
|
||||||
|
addressInParticipations_.reduce(booleanReducer) : false;
|
||||||
|
|
||||||
|
//console.log(addressInParticipations_, addressInParticipations);
|
||||||
|
return (
|
||||||
|
this.accompanyingCourse.step !== 'DRAFT'
|
||||||
|
&& this.isTemporaryAddress
|
||||||
|
&& !addressInParticipations
|
||||||
|
)
|
||||||
|
;
|
||||||
|
},
|
||||||
isContextEdit() {
|
isContextEdit() {
|
||||||
return this.context.edit;
|
return this.context.edit;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
checkHouseholdAddressForParticipation(participation) {
|
||||||
|
if (participation.person.current_household_id === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return participation.person.current_household_address !== null;
|
||||||
|
},
|
||||||
initAddressContext() {
|
initAddressContext() {
|
||||||
let context = {
|
let context = {
|
||||||
target: {
|
target: {
|
||||||
@ -153,20 +202,44 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
this.initAddressContext();
|
this.initAddressContext();
|
||||||
|
|
||||||
console.log('ac.locationStatus', this.accompanyingCourse.locationStatus);
|
//console.log('ac.locationStatus', this.accompanyingCourse.locationStatus);
|
||||||
console.log('ac.location (temporary location)', this.accompanyingCourse.location);
|
//console.log('ac.location (temporary location)', this.accompanyingCourse.location);
|
||||||
console.log('ac.personLocation', this.accompanyingCourse.personLocation);
|
//console.log('ac.personLocation', this.accompanyingCourse.personLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
div.flex-table {
|
div#accompanying-course {
|
||||||
div.item-bloc {
|
div.vue-component {
|
||||||
div.alert {
|
& > div.alert.no-person-location {
|
||||||
margin: 0 -0.9em -1em;
|
margin: 1px 0 0;
|
||||||
|
}
|
||||||
|
div.no-person-location {
|
||||||
|
padding-top: 1.5em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
& > i {
|
||||||
|
flex-basis: 1.5em; flex-grow: 0; flex-shrink: 0;
|
||||||
|
padding-top: 0.2em;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
& > div {
|
||||||
|
flex-basis: auto;
|
||||||
|
div.action {
|
||||||
|
button.btn-update {
|
||||||
|
margin-right: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div.flex-table {
|
||||||
|
div.item-bloc {
|
||||||
|
div.alert {
|
||||||
|
margin: 0 -0.9em -1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
:id="p.person.id"
|
:id="p.person.id"
|
||||||
:value="p.person.id"
|
:value="p.person.id"
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" for="hasNoHousehold">
|
<label class="form-check-label">
|
||||||
{{ p.person.text }}
|
{{ p.person.text }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -58,11 +58,14 @@
|
|||||||
</add-persons>
|
</add-persons>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!isParticipationValid" class="alert alert-warning to-confirm">
|
||||||
|
{{ $t('persons_associated.participation_not_valid') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex';
|
import {mapGetters, mapState} from 'vuex';
|
||||||
import ParticipationItem from "./PersonsAssociated/ParticipationItem.vue"
|
import ParticipationItem from "./PersonsAssociated/ParticipationItem.vue"
|
||||||
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'
|
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'
|
||||||
|
|
||||||
@ -89,6 +92,9 @@ export default {
|
|||||||
courseId: state => state.accompanyingCourse.id,
|
courseId: state => state.accompanyingCourse.id,
|
||||||
participations: state => state.accompanyingCourse.participations
|
participations: state => state.accompanyingCourse.participations
|
||||||
}),
|
}),
|
||||||
|
...mapGetters([
|
||||||
|
'isParticipationValid'
|
||||||
|
]),
|
||||||
currentParticipations() {
|
currentParticipations() {
|
||||||
return this.participations.filter(p => p.endDate === null)
|
return this.participations.filter(p => p.endDate === null)
|
||||||
},
|
},
|
||||||
@ -126,7 +132,7 @@ export default {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
div#accompanying-course {
|
div#accompanying-course {
|
||||||
div.vue-component {
|
div.vue-component {
|
||||||
& > div.alert {
|
& > div.alert.no-household {
|
||||||
margin: 0 0 -1em;
|
margin: 0 0 -1em;
|
||||||
}
|
}
|
||||||
div.no-household {
|
div.no-household {
|
||||||
|
@ -19,20 +19,8 @@
|
|||||||
v-if="hasCurrentHouseholdAddress"
|
v-if="hasCurrentHouseholdAddress"
|
||||||
v-bind:person="participation.person">
|
v-bind:person="participation.person">
|
||||||
</button-location>
|
</button-location>
|
||||||
<li>
|
<li><on-the-fly :type="participation.person.type" :id="participation.person.id" action="show"></on-the-fly></li>
|
||||||
<on-the-fly
|
<li><on-the-fly :type="participation.person.type" :id="participation.person.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||||
v-bind:type="participation.person.type"
|
|
||||||
v-bind:id="participation.person.id"
|
|
||||||
action="show">
|
|
||||||
</on-the-fly>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<on-the-fly
|
|
||||||
v-bind:type="participation.person.type"
|
|
||||||
v-bind:id="participation.person.id"
|
|
||||||
action="edit">
|
|
||||||
</on-the-fly>
|
|
||||||
</li>
|
|
||||||
<!-- <li>
|
<!-- <li>
|
||||||
<button class="btn btn-delete"
|
<button class="btn btn-delete"
|
||||||
:title="$t('action.delete')"
|
:title="$t('action.delete')"
|
||||||
@ -112,22 +100,13 @@ export default {
|
|||||||
getAccompanyingCourseReturnPath() {
|
getAccompanyingCourseReturnPath() {
|
||||||
return `fr/parcours/${this.$store.state.accompanyingCourse.id}/edit#section-10`;
|
return `fr/parcours/${this.$store.state.accompanyingCourse.id}/edit#section-10`;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
saveFormOnTheFly(payload) {
|
||||||
|
console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
|
||||||
|
payload.target = 'participation';
|
||||||
|
this.$store.dispatch('patchOnTheFly', payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* dates of participation
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* <tr>
|
|
||||||
* <td><span v-if="participation.startDate">
|
|
||||||
* {{ $d(participation.startDate.datetime, 'short') }}</span>
|
|
||||||
* </td>
|
|
||||||
* <td><span v-if="participation.endDate">
|
|
||||||
* {{ $d(participation.endDate.datetime, 'short') }}</span>
|
|
||||||
* </td>
|
|
||||||
* </tr>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
{{ $t('requestor.is_anonymous') }}
|
{{ $t('requestor.is_anonymous') }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<third-party-render-box v-if="accompanyingCourse.requestor.type == 'thirdparty'"
|
<third-party-render-box v-if="accompanyingCourse.requestor.type === 'thirdparty'"
|
||||||
:thirdparty="accompanyingCourse.requestor"
|
:thirdparty="accompanyingCourse.requestor"
|
||||||
:options="{
|
:options="{
|
||||||
addLink: false,
|
addLink: false,
|
||||||
@ -23,14 +23,13 @@
|
|||||||
>
|
>
|
||||||
<template v-slot:record-actions>
|
<template v-slot:record-actions>
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<button-location v-if="hasCurrentHouseholdAddress" :thirdparty="accompanyingCourse.requestor"></button-location>
|
|
||||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="show"></on-the-fly></li>
|
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="show"></on-the-fly></li>
|
||||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit"></on-the-fly></li>
|
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
</third-party-render-box>
|
</third-party-render-box>
|
||||||
|
|
||||||
<person-render-box render="bloc" v-else-if="accompanyingCourse.requestor.type == 'person'"
|
<person-render-box render="bloc" v-else-if="accompanyingCourse.requestor.type === 'person'"
|
||||||
:person="accompanyingCourse.requestor"
|
:person="accompanyingCourse.requestor"
|
||||||
:options="{
|
:options="{
|
||||||
addLink: false,
|
addLink: false,
|
||||||
@ -44,9 +43,8 @@
|
|||||||
>
|
>
|
||||||
<template v-slot:record-actions>
|
<template v-slot:record-actions>
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<button-location v-if="hasCurrentHouseholdAddress" :person="accompanyingCourse.requestor"></button-location>
|
|
||||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="show"></on-the-fly></li>
|
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="show"></on-the-fly></li>
|
||||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit"></on-the-fly></li>
|
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
</person-render-box>
|
</person-render-box>
|
||||||
@ -129,6 +127,11 @@ export default {
|
|||||||
this.$store.dispatch('addRequestor', selected.shift());
|
this.$store.dispatch('addRequestor', selected.shift());
|
||||||
this.$refs.addPersons.resetSearch(); // to cast child method
|
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||||
modal.showModal = false;
|
modal.showModal = false;
|
||||||
|
},
|
||||||
|
saveFormOnTheFly(payload) {
|
||||||
|
console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
|
||||||
|
payload.target = 'requestor';
|
||||||
|
this.$store.dispatch('patchOnTheFly', payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
>
|
>
|
||||||
<template v-slot:record-actions>
|
<template v-slot:record-actions>
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
|
<!--
|
||||||
<button-location v-if="hasCurrentHouseholdAddress" :person="resource.resource"></button-location>
|
<button-location v-if="hasCurrentHouseholdAddress" :person="resource.resource"></button-location>
|
||||||
|
-->
|
||||||
<li><on-the-fly :type="resource.resource.type" :id="resource.resource.id" action="show"></on-the-fly></li>
|
<li><on-the-fly :type="resource.resource.type" :id="resource.resource.id" action="show"></on-the-fly></li>
|
||||||
<li><on-the-fly :type="resource.resource.type" :id="resource.resource.id" action="edit"></on-the-fly></li>
|
<li><on-the-fly :type="resource.resource.type" :id="resource.resource.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||||
<li><button class="btn btn-sm btn-remove" :title="$t('action.remove')" @click.prevent="$emit('remove', resource)"></button></li>
|
<li><button class="btn btn-sm btn-remove" :title="$t('action.remove')" @click.prevent="$emit('remove', resource)"></button></li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
@ -22,7 +24,7 @@
|
|||||||
<template v-slot:record-actions>
|
<template v-slot:record-actions>
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li><on-the-fly :type="resource.resource.type" :id="resource.resource.id" action="show"></on-the-fly></li>
|
<li><on-the-fly :type="resource.resource.type" :id="resource.resource.id" action="show"></on-the-fly></li>
|
||||||
<li><on-the-fly :type="resource.resource.type" :id="resource.resource.id" action="edit"></on-the-fly></li>
|
<li><on-the-fly :type="resource.resource.type" :id="resource.resource.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||||
<li><button class="btn btn-sm btn-remove" :title="$t('action.remove')" @click.prevent="$emit('remove', resource)"></button></li>
|
<li><button class="btn btn-sm btn-remove" :title="$t('action.remove')" @click.prevent="$emit('remove', resource)"></button></li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
@ -53,6 +55,13 @@ export default {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
saveFormOnTheFly(payload) {
|
||||||
|
console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
|
||||||
|
payload.target = 'resource';
|
||||||
|
this.$store.dispatch('patchOnTheFly', payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vue-component">
|
<div class="vue-component">
|
||||||
<h2><a id="section-60"></a>{{ $t('scopes.title') }}</h2>
|
<h2><a id="section-60"></a>{{ $t('scopes.title') }}</h2>
|
||||||
|
|
||||||
<ul>
|
<div class="mb-4">
|
||||||
<li v-for="s in scopes">
|
<div class="form-check" v-for="s in scopes">
|
||||||
<input type="checkbox" v-model="checkedScopes" :value="s" />
|
<input class="form-check-input" type="checkbox" v-model="checkedScopes" :value="s" />
|
||||||
{{ s.name.fr }}
|
<label class="form-check-label">
|
||||||
</li>
|
{{ s.name.fr }}
|
||||||
</ul>
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="!isScopeValid" class="alert alert-warning separator">
|
<div v-if="!isScopeValid" class="alert alert-warning to-confirm">
|
||||||
{{ $t('scopes.add_at_least_one') }}
|
{{ $t('scopes.add_at_least_one') }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -21,13 +21,17 @@
|
|||||||
</VueMultiselect>
|
</VueMultiselect>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!isSocialIssueValid" class="alert alert-warning to-confirm">
|
||||||
|
{{ $t('social_issue.not_valid') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import VueMultiselect from 'vue-multiselect';
|
import VueMultiselect from 'vue-multiselect';
|
||||||
import { getSocialIssues } from '../api';
|
import { getSocialIssues } from '../api';
|
||||||
import { mapState } from 'vuex';
|
import {mapGetters, mapState} from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "SocialIssue",
|
name: "SocialIssue",
|
||||||
@ -41,6 +45,9 @@ export default {
|
|||||||
...mapState({
|
...mapState({
|
||||||
value: state => state.accompanyingCourse.socialIssues,
|
value: state => state.accompanyingCourse.socialIssues,
|
||||||
}),
|
}),
|
||||||
|
...mapGetters([
|
||||||
|
'isSocialIssueValid'
|
||||||
|
])
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getOptions();
|
this.getOptions();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n'
|
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n';
|
||||||
|
import { thirdpartyMessages } from 'ChillThirdPartyAssets/vuejs/_js/i18n';
|
||||||
import { addressMessages } from 'ChillMainAssets/vuejs/Address/i18n';
|
import { addressMessages } from 'ChillMainAssets/vuejs/Address/i18n';
|
||||||
|
|
||||||
const appMessages = {
|
const appMessages = {
|
||||||
@ -48,8 +49,9 @@ const appMessages = {
|
|||||||
ok: "Oui, l'usager quitte le parcours",
|
ok: "Oui, l'usager quitte le parcours",
|
||||||
show_household_number: "Voir le ménage (n° {id})",
|
show_household_number: "Voir le ménage (n° {id})",
|
||||||
show_household: "Voir le ménage",
|
show_household: "Voir le ménage",
|
||||||
person_without_household_warning: "Certaines personnes n'appartiennent à aucun ménage actuellement. Renseignez leur appartenance à un ménage dès que possible.",
|
person_without_household_warning: "Certaines usagers n'appartiennent actuellement à aucun ménage. Renseignez leur appartenance dès que possible.",
|
||||||
update_household: "Modifier l'appartenance",
|
update_household: "Modifier l'appartenance",
|
||||||
|
participation_not_valid: "Sélectionnez ou créez au minimum 1 usager",
|
||||||
},
|
},
|
||||||
requestor: {
|
requestor: {
|
||||||
title: "Demandeur",
|
title: "Demandeur",
|
||||||
@ -72,6 +74,7 @@ const appMessages = {
|
|||||||
social_issue: {
|
social_issue: {
|
||||||
title: "Problématiques sociales",
|
title: "Problématiques sociales",
|
||||||
label: "Choisir les problématiques sociales",
|
label: "Choisir les problématiques sociales",
|
||||||
|
not_valid: "Sélectionnez au minimum une problématique sociale",
|
||||||
},
|
},
|
||||||
courselocation: {
|
courselocation: {
|
||||||
title: "Localisation du parcours",
|
title: "Localisation du parcours",
|
||||||
@ -79,11 +82,13 @@ const appMessages = {
|
|||||||
edit_temporary_address: "Modifier l'adresse temporaire",
|
edit_temporary_address: "Modifier l'adresse temporaire",
|
||||||
assign_course_address: "Désigner comme l'adresse du parcours",
|
assign_course_address: "Désigner comme l'adresse du parcours",
|
||||||
remove_button: "Enlever l'adresse",
|
remove_button: "Enlever l'adresse",
|
||||||
temporary_address_must_be_changed: "Cette adresse est temporaire et devrait être remplacée par celle d'un usager de référence.",
|
temporary_address_must_be_changed: "Cette adresse est temporaire. Le parcours devrait être localisé auprès d'un usager concerné.",
|
||||||
|
associate_at_least_one_person_with_one_household_with_address: "Commencez d'abord par associer un membre du parcours à un ménage, et indiquez une adresse à ce ménage.",
|
||||||
sure: "Êtes-vous sûr ?",
|
sure: "Êtes-vous sûr ?",
|
||||||
sure_description: "Voulez-vous faire de cette adresse l'adresse du parcours ?",
|
sure_description: "Voulez-vous faire de cette adresse l'adresse du parcours ?",
|
||||||
ok: "Désigner comme adresse du parcours",
|
ok: "Désigner comme adresse du parcours",
|
||||||
person_locator: "Parcours localisé auprès de {0}",
|
person_locator: "Parcours localisé auprès de {0}",
|
||||||
|
not_valid: "Indiquez au minimum une localisation temporaire du parcours",
|
||||||
no_address: "Il n'y a pas d'adresse associée au parcours"
|
no_address: "Il n'y a pas d'adresse associée au parcours"
|
||||||
},
|
},
|
||||||
scopes: {
|
scopes: {
|
||||||
@ -137,7 +142,7 @@ const appMessages = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(appMessages.fr, personMessages.fr, addressMessages.fr);
|
Object.assign(appMessages.fr, personMessages.fr, thirdpartyMessages.fr, addressMessages.fr);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
appMessages
|
appMessages
|
||||||
|
@ -11,6 +11,8 @@ import { getAccompanyingCourse,
|
|||||||
addScope,
|
addScope,
|
||||||
removeScope,
|
removeScope,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
|
import { patchPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly";
|
||||||
|
import { patchThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly";
|
||||||
|
|
||||||
|
|
||||||
const debug = process.env.NODE_ENV !== 'production';
|
const debug = process.env.NODE_ENV !== 'production';
|
||||||
@ -48,7 +50,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
|||||||
return state.accompanyingCourse.location !== null;
|
return state.accompanyingCourse.location !== null;
|
||||||
},
|
},
|
||||||
isScopeValid(state) {
|
isScopeValid(state) {
|
||||||
console.log('is scope valid', state.accompanyingCourse.scopes.length > 0);
|
//console.log('is scope valid', state.accompanyingCourse.scopes.length > 0);
|
||||||
return state.accompanyingCourse.scopes.length > 0;
|
return state.accompanyingCourse.scopes.length > 0;
|
||||||
},
|
},
|
||||||
validationKeys(state, getters) {
|
validationKeys(state, getters) {
|
||||||
@ -107,6 +109,36 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
|||||||
//console.log('### mutation: addResource', resource);
|
//console.log('### mutation: addResource', resource);
|
||||||
state.accompanyingCourse.resources.push(resource);
|
state.accompanyingCourse.resources.push(resource);
|
||||||
},
|
},
|
||||||
|
updatePerson(state, payload) {
|
||||||
|
console.log('### mutation: updatePerson', payload);
|
||||||
|
let i = null;
|
||||||
|
switch (payload.target) {
|
||||||
|
case 'participation':
|
||||||
|
i = state.accompanyingCourse.participations.findIndex(e => e.person.id === payload.person.id );
|
||||||
|
state.accompanyingCourse.participations[i].person = payload.person;
|
||||||
|
break;
|
||||||
|
case 'requestor':
|
||||||
|
state.accompanyingCourse.requestor = payload.person;
|
||||||
|
break;
|
||||||
|
case 'resource':
|
||||||
|
i = state.accompanyingCourse.resources.findIndex(e => e.resource.id === payload.person.id );
|
||||||
|
state.accompanyingCourse.resources[i].resource = payload.person;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateThirdparty(state, payload) {
|
||||||
|
console.log('### mutation: updateThirdparty', payload);
|
||||||
|
let i = null;
|
||||||
|
switch (payload.target) {
|
||||||
|
case 'requestor':
|
||||||
|
state.accompanyingCourse.requestor = payload.thirdparty;
|
||||||
|
break;
|
||||||
|
case 'resource':
|
||||||
|
i = state.accompanyingCourse.resources.findIndex(e => e.resource.id === payload.thirdparty.id );
|
||||||
|
state.accompanyingCourse.resources[i].resource = payload.thirdparty;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
toggleIntensity(state, value) {
|
toggleIntensity(state, value) {
|
||||||
state.accompanyingCourse.intensity = value;
|
state.accompanyingCourse.intensity = value;
|
||||||
},
|
},
|
||||||
@ -239,6 +271,38 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
|||||||
resolve();
|
resolve();
|
||||||
})).catch((error) => { commit('catchError', error) });
|
})).catch((error) => { commit('catchError', error) });
|
||||||
},
|
},
|
||||||
|
patchOnTheFly({ commit }, payload) {
|
||||||
|
console.log('## action: patch OnTheFly', payload);
|
||||||
|
let body = { type: payload.type };
|
||||||
|
if (payload.type === 'person') {
|
||||||
|
body.firstName = payload.data.firstName;
|
||||||
|
body.lastName = payload.data.lastName;
|
||||||
|
if (payload.data.birthdate !== null) { body.birthdate = payload.data.birthdate; }
|
||||||
|
body.phonenumber = payload.data.phonenumber;
|
||||||
|
body.mobilenumber = payload.data.mobilenumber;
|
||||||
|
body.gender = payload.data.gender;
|
||||||
|
console.log('id', payload.data.id, 'and body', body);
|
||||||
|
patchPerson(payload.data.id, body)
|
||||||
|
.then(person => new Promise((resolve, reject) => {
|
||||||
|
console.log('patch person', person);
|
||||||
|
commit('updatePerson', { target: payload.target, person: person });
|
||||||
|
resolve();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else if (payload.type === 'thirdparty') {
|
||||||
|
body.name = payload.data.text;
|
||||||
|
body.email = payload.data.email;
|
||||||
|
body.telephone = payload.data.phonenumber;
|
||||||
|
body.address = { id: payload.data.address.address_id };
|
||||||
|
console.log('id', payload.data.id, 'and body', body);
|
||||||
|
patchThirdparty(payload.data.id, body)
|
||||||
|
.then(thirdparty => new Promise((resolve, reject) => {
|
||||||
|
console.log('patch thirdparty', thirdparty);
|
||||||
|
commit('updateThirdparty', { target: payload.target, thirdparty: thirdparty });
|
||||||
|
resolve();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
toggleIntensity({ commit }, payload) {
|
toggleIntensity({ commit }, payload) {
|
||||||
//console.log(payload);
|
//console.log(payload);
|
||||||
patchAccompanyingCourse(id, { type: "accompanying_period", intensity: payload })
|
patchAccompanyingCourse(id, { type: "accompanying_period", intensity: payload })
|
||||||
|
@ -109,7 +109,7 @@ export default {
|
|||||||
this.toggleEditEvaluation();
|
this.toggleEditEvaluation();
|
||||||
},
|
},
|
||||||
buildEditLink(storedObject) {
|
buildEditLink(storedObject) {
|
||||||
return `/fr/chill_wopi/edit/${storedObject.filename}?returnPath=` + encodeURIComponent(
|
return `/edit/${storedObject.uuid}?returnPath=` + encodeURIComponent(
|
||||||
window.location.pathname + window.location.search + window.location.hash);
|
window.location.pathname + window.location.search + window.location.hash);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -2,171 +2,126 @@
|
|||||||
|
|
||||||
<h2 class="mt-4">{{ $t('household_members_editor.household_part') }}</h2>
|
<h2 class="mt-4">{{ $t('household_members_editor.household_part') }}</h2>
|
||||||
|
|
||||||
<div v-if="hasHousehold">
|
<div v-if="mode == null">
|
||||||
|
|
||||||
<div class="flex-table">
|
<div class="alert alert-info">{{ $t('household_members_editor.household.no_household_choose_one') }}</div>
|
||||||
<div class="item-bloc">
|
|
||||||
<household-render-box :household="household" :isAddressMultiline="true"></household-render-box>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isHouseholdNew && !hasHouseholdAddress">
|
<div class="flex-table householdSuggestionList">
|
||||||
|
<div v-if="isModeNewAllowed" class="item-bloc">
|
||||||
<div v-if="hasAddressSuggestion" class="householdAddressSuggestion my-5">
|
<div>
|
||||||
<h4 class="mb-3">
|
<section>
|
||||||
{{ $t('household_members_editor.household.where_live_the_household') }}
|
<div class="item-row">
|
||||||
</h4>
|
<div class="item-col">
|
||||||
<div class="accordion" id="addressSuggestions">
|
<div class="h4">
|
||||||
<div class="accordion-item">
|
<i class="fa fa-home"></i> {{ $t('household_members_editor.household.new_household') }}
|
||||||
<h2 class="accordion-header" id="heading_address_suggestions">
|
|
||||||
<button v-if="!showAddressSuggestion"
|
|
||||||
class="accordion-button collapsed"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="collapse"
|
|
||||||
aria-expanded="false"
|
|
||||||
@click="toggleAddressSuggestion">
|
|
||||||
{{ $tc('household_members_editor.show_household_suggestion', countAddressSuggestion) }}
|
|
||||||
</button>
|
|
||||||
<button v-if="showAddressSuggestion"
|
|
||||||
class="accordion-button"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="collapse"
|
|
||||||
aria-expanded="true"
|
|
||||||
@click="toggleAddressSuggestion">
|
|
||||||
{{ $t('household_members_editor.hide_household_suggestion') }}
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div class="accordion-collapse" id="collapse_address_suggestions"
|
|
||||||
aria-labelledby="heading_address_suggestions" data-bs-parent="#addressSuggestions">
|
|
||||||
<div v-if="showAddressSuggestion">
|
|
||||||
<div class="flex-table householdAddressSuggestionList">
|
|
||||||
<div v-for="a in filterAddressesSuggestion" class="item-bloc">
|
|
||||||
<div class="float-button bottom">
|
|
||||||
<div class="box">
|
|
||||||
<div class="action">
|
|
||||||
<ul class="record_actions">
|
|
||||||
<li>
|
|
||||||
<button class="btn btn-sm btn-choose" @click="setHouseholdAddress(a)">
|
|
||||||
{{ $t('household_members_editor.household.household_live_to_this_address') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<ul class="list-content fa-ul">
|
|
||||||
<li>
|
|
||||||
<i class="fa fa-li fa-map-marker"></i>
|
|
||||||
<address-render-box :address="a"></address-render-box>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button @click="setModeNew" class="btn btn-sm btn-create">{{ $t('household_members_editor.household.create_household') }}</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- if allow leave household -->
|
||||||
|
<div v-if="isModeLeaveAllowed" class="item-bloc">
|
||||||
|
<div>
|
||||||
|
<section>
|
||||||
|
<div class="item-row">
|
||||||
|
<div class="item-col">
|
||||||
|
<div class="h4">
|
||||||
|
<span class="fa-stack fa-lg">
|
||||||
|
<i class="fa fa-home fa-stack-1x"></i>
|
||||||
|
<i class="fa fa-ban fa-stack-2x text-danger"></i>
|
||||||
|
</span>
|
||||||
|
{{ $t('household_members_editor.household.leave_without_household') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item-row">
|
||||||
|
{{ $t('household_members_editor.household.will_leave_any_household_explanation')}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button @click="setModeLeave" class="btn btn-sm">
|
||||||
|
<i class="fa fa-sign-out"></i>
|
||||||
|
{{ $t('household_members_editor.household.leave') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="item in getSuggestions">
|
||||||
|
<div class="item-bloc">
|
||||||
|
<household-render-box :household="item.household"></household-render-box>
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button class="btn btn-sm btn-choose" @click="selectHousehold(item.household)">
|
||||||
|
{{ $t('household_members_editor.select_household') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ul class="record_actions">
|
<div v-else>
|
||||||
<li >
|
<div class="flex-table">
|
||||||
<add-address
|
<div class="item-bloc">
|
||||||
:context="addAddress.context"
|
<template v-if="isModeLeave">
|
||||||
|
<section>
|
||||||
|
<div class="item-row">
|
||||||
|
<div class="item-col">
|
||||||
|
<div class="h4">
|
||||||
|
<span class="fa-stack fa-lg">
|
||||||
|
<i class="fa fa-home fa-stack-1x"></i>
|
||||||
|
<i class="fa fa-ban fa-stack-2x text-danger"></i>
|
||||||
|
</span>
|
||||||
|
{{ $t('household_members_editor.household.leave_without_household') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item-row">
|
||||||
|
{{ $t('household_members_editor.household.will_leave_any_household_explanation')}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<household-render-box :household="household" :isAddressMultiline="true"></household-render-box>
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<add-address
|
||||||
|
:context="getAddressContext"
|
||||||
:key="addAddress.key"
|
:key="addAddress.key"
|
||||||
:options="addAddress.options"
|
:options="addAddress.options"
|
||||||
:result="addAddress.result"
|
:addressChangedCallback="addressChanged"
|
||||||
@addressChangedCallback="setHouseholdCreatedAddress"
|
></add-address>
|
||||||
ref="addAddress">
|
</li>
|
||||||
</add-address>
|
<li v-if="hasHouseholdAddress">
|
||||||
</li>
|
<button class="btn btn-remove"
|
||||||
</ul>
|
@click="removeHouseholdAddress">
|
||||||
</div>
|
{{ $t('household_members_editor.household.remove_address') }}
|
||||||
|
</button>
|
||||||
<div v-if="isHouseholdNew && hasHouseholdAddress">
|
</li>
|
||||||
<ul class="record_actions">
|
</ul>
|
||||||
<li >
|
</template>
|
||||||
<button class="btn btn-misc" @click="removeHouseholdAddress">
|
|
||||||
{{ $t('household_members_editor.household.delete_this_address') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div v-else-if="isForceLeaveWithoutHousehold">
|
|
||||||
{{ $t('household_members_editor.household.will_leave_any_household') }}
|
|
||||||
</div>
|
|
||||||
<div v-else class="alert alert-info">{{ $t('household_members_editor.household.no_household_choose_one') }}</div>
|
|
||||||
|
|
||||||
<ul v-if="allowChangeHousehold" class="record_actions">
|
|
||||||
<li v-if="allowHouseholdCreate">
|
|
||||||
<button class="btn btn-create" @click="createHousehold">
|
|
||||||
{{ $t('household_members_editor.household.create_household') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li v-if="allowHouseholdSearch">
|
|
||||||
<button class="btn btn-misc">
|
|
||||||
<i class="fa fa-search"></i>{{ $t('household_members_editor.household.search_household') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li v-if="allowLeaveWithoutHousehold" >
|
|
||||||
<button @click="forceLeaveWithoutHousehold" class="btn btn-orange">
|
|
||||||
<i class="fa fa-sign-out"></i>{{ $t('household_members_editor.household.leave_without_household') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li v-if="allowRemoveHousehold">
|
|
||||||
<button @click="removeHousehold" class="btn">
|
|
||||||
{{ $t('household_members_editor.household.change') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div v-if="hasHouseholdSuggestion" class="householdSuggestions my-5">
|
|
||||||
<h4 class="mb-3">
|
|
||||||
{{ $t('household_members_editor.household_for_participants_accompanying_period') }} :
|
|
||||||
</h4>
|
|
||||||
<div class="accordion" id="householdSuggestions">
|
|
||||||
<div class="accordion-item">
|
|
||||||
<h2 class="accordion-header" id="heading_household_suggestions">
|
|
||||||
<button v-if="!showHouseholdSuggestion"
|
|
||||||
class="accordion-button collapsed"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="collapse"
|
|
||||||
aria-expanded="false"
|
|
||||||
@click="toggleHouseholdSuggestion">
|
|
||||||
{{ $tc('household_members_editor.show_household_suggestion', countHouseholdSuggestion) }}
|
|
||||||
</button>
|
|
||||||
<button v-if="showHouseholdSuggestion"
|
|
||||||
class="accordion-button"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="collapse"
|
|
||||||
aria-expanded="true"
|
|
||||||
@click="toggleHouseholdSuggestion">
|
|
||||||
{{ $t('household_members_editor.hide_household_suggestion') }}
|
|
||||||
</button>
|
|
||||||
<!-- disabled bootstrap behaviour: data-bs-target="#collapse_household_suggestions" aria-controls="collapse_household_suggestions" -->
|
|
||||||
</h2>
|
|
||||||
<div class="accordion-collapse" id="collapse_household_suggestions"
|
|
||||||
aria-labelledby="heading_household_suggestions" data-bs-parent="#householdSuggestions">
|
|
||||||
<div v-if="showHouseholdSuggestion">
|
|
||||||
<div class="flex-table householdSuggestionList">
|
|
||||||
<div v-for="h in filterHouseholdSuggestionByAccompanyingPeriod" class="item-bloc">
|
|
||||||
<household-render-box :household="h"></household-render-box>
|
|
||||||
<ul class="record_actions">
|
|
||||||
<li>
|
|
||||||
<button class="btn btn-sm btn-choose" @click="selectHousehold(h)">
|
|
||||||
{{ $t('household_members_editor.select_household') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<ul v-if="isModeNewAllowed || isModeLeaveAllowed || getModeSuggestions.length > 0" class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button class="btn btn-sm btn-chill-beige" @click="resetMode">
|
||||||
|
{{ $t('household_members_editor.household.reset_mode') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -185,35 +140,32 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
addAddress: {
|
addAddress: {
|
||||||
context: {
|
|
||||||
target: {
|
|
||||||
name: 'household_create',
|
|
||||||
id: 0
|
|
||||||
},
|
|
||||||
edit: false,
|
|
||||||
addressId: null
|
|
||||||
},
|
|
||||||
key: 'household_new',
|
key: 'household_new',
|
||||||
options: {
|
options: {
|
||||||
useDate: {
|
useDate: {
|
||||||
validFrom: true
|
validFrom: false,
|
||||||
|
validTo: false,
|
||||||
},
|
},
|
||||||
|
onlyButton: true,
|
||||||
button: {
|
button: {
|
||||||
text: {
|
text: {
|
||||||
create: 'household_members_editor.household.or_create_new_address',
|
create: 'household_members_editor.household.set_address',
|
||||||
edit: null,
|
edit: 'household_members_editor.household.update_address',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
create: 'household_members_editor.household.create_new_address',
|
create: 'household_members_editor.household.create_new_address',
|
||||||
edit: null,
|
edit: 'household_members_editor.household.update_address_title',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
|
'isModeNewAllowed',
|
||||||
|
'isModeLeaveAllowed',
|
||||||
|
'getSuggestions',
|
||||||
'hasHousehold',
|
'hasHousehold',
|
||||||
'isHouseholdNew',
|
'isHouseholdNew',
|
||||||
'hasHouseholdSuggestion',
|
'hasHouseholdSuggestion',
|
||||||
@ -223,68 +175,38 @@ export default {
|
|||||||
'countAddressSuggestion',
|
'countAddressSuggestion',
|
||||||
'filterAddressesSuggestion',
|
'filterAddressesSuggestion',
|
||||||
'hasHouseholdAddress',
|
'hasHouseholdAddress',
|
||||||
|
'isModeLeave',
|
||||||
|
'getAddressContext',
|
||||||
]),
|
]),
|
||||||
...mapState([
|
...mapState([
|
||||||
'showHouseholdSuggestion',
|
'showHouseholdSuggestion',
|
||||||
'showAddressSuggestion'
|
'showAddressSuggestion',
|
||||||
|
'mode',
|
||||||
]),
|
]),
|
||||||
household() {
|
household() {
|
||||||
return this.$store.state.household;
|
return this.$store.state.household;
|
||||||
},
|
},
|
||||||
allowHouseholdCreate() {
|
|
||||||
return this.$store.state.allowHouseholdCreate && !this.$store.getters.hasHousehold;
|
|
||||||
},
|
|
||||||
allowHouseholdSearch() {
|
allowHouseholdSearch() {
|
||||||
return false;
|
return false;
|
||||||
return this.$store.state.allowHouseholdSearch && !this.$store.getters.hasHousehold;
|
return this.$store.state.allowHouseholdSearch && !this.$store.getters.hasHousehold;
|
||||||
},
|
},
|
||||||
allowLeaveWithoutHousehold() {
|
|
||||||
return this.$store.state.allowLeaveWithoutHousehold && !this.$store.getters.hasHousehold;
|
|
||||||
},
|
|
||||||
allowRemoveHousehold() {
|
|
||||||
return this.$store.getters.hasHousehold &&
|
|
||||||
(
|
|
||||||
this.allowHouseholdCreate || this.allowHouseholdSearch ||
|
|
||||||
this.allowLeaveWithoutHousehold
|
|
||||||
)
|
|
||||||
;
|
|
||||||
},
|
|
||||||
allowChangeHousehold() {
|
|
||||||
return this.allowHouseholdCreate || this.allowHouseholdSearch ||
|
|
||||||
this.allowLeaveWithoutHousehold;
|
|
||||||
},
|
|
||||||
isForceLeaveWithoutHousehold() {
|
|
||||||
return this.$store.state.forceLeaveWithoutHousehold;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
createHousehold() {
|
setModeNew() {
|
||||||
this.$store.dispatch('createHousehold');
|
this.$store.dispatch('createHousehold');
|
||||||
},
|
},
|
||||||
forceLeaveWithoutHousehold() {
|
setModeLeave() {
|
||||||
this.$store.dispatch('forceLeaveWithoutHousehold');
|
this.$store.dispatch('forceLeaveWithoutHousehold');
|
||||||
},
|
},
|
||||||
toggleHouseholdSuggestion() {
|
resetMode() {
|
||||||
this.$store.commit('toggleHouseholdSuggestion');
|
this.$store.commit('resetMode');
|
||||||
},
|
},
|
||||||
toggleAddressSuggestion() {
|
addressChanged(payload) {
|
||||||
this.$store.commit('toggleAddressSuggestion');
|
console.log("addressChanged", payload);
|
||||||
|
this.$store.dispatch('setHouseholdNewAddress', payload.address);
|
||||||
},
|
},
|
||||||
selectHousehold(h) {
|
selectHousehold(h) {
|
||||||
this.$store.dispatch('selectHousehold', h);
|
this.$store.dispatch('selectHousehold', h);
|
||||||
this.toggleHouseholdSuggestion();
|
|
||||||
},
|
|
||||||
removeHousehold() {
|
|
||||||
this.$store.dispatch('removeHousehold');
|
|
||||||
},
|
|
||||||
setHouseholdAddress(a) {
|
|
||||||
let payload = this.$refs.addAddress.submitNewAddress();
|
|
||||||
console.log('setHouseholdAddress', a);
|
|
||||||
this.$store.commit('setHouseholdAddress', a);
|
|
||||||
},
|
|
||||||
setHouseholdCreatedAddress(payload) {
|
|
||||||
console.log('setHouseholdAddress', payload);
|
|
||||||
this.$store.dispatch('setHouseholdNewAddress', payload);
|
|
||||||
},
|
},
|
||||||
removeHouseholdAddress() {
|
removeHouseholdAddress() {
|
||||||
this.$store.commit('removeHouseholdAddress');
|
this.$store.commit('removeHouseholdAddress');
|
||||||
@ -310,28 +232,4 @@ div.householdSuggestionList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
div.householdAddressSuggestionList {
|
|
||||||
display: flex;
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
& > li {}
|
|
||||||
}
|
|
||||||
.householdSuggestionList {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
& > .item {
|
|
||||||
margin-bottom: 0.8rem;
|
|
||||||
width: calc(50% - 1rem);
|
|
||||||
border: 1px solid var(--chill-light-gray);
|
|
||||||
padding: 0.5rem 0.5rem 0 0.5rem;
|
|
||||||
ul.record_actions {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -7,16 +7,27 @@ const appMessages = {
|
|||||||
household: {
|
household: {
|
||||||
no_household_choose_one: "Aucun ménage de destination. Choisissez un ménage. Les usagers concernés par la modification apparaitront ensuite.",
|
no_household_choose_one: "Aucun ménage de destination. Choisissez un ménage. Les usagers concernés par la modification apparaitront ensuite.",
|
||||||
new_household: "Nouveau ménage",
|
new_household: "Nouveau ménage",
|
||||||
create_household: "Créer un nouveau ménage de destination",
|
create_household: "Créer",
|
||||||
search_household: "Chercher un ménage",
|
search_household: "Chercher un ménage",
|
||||||
will_leave_any_household: "Ne rejoignent pas de ménage",
|
will_leave_any_household: "Les usagers ne rejoignent pas de ménage",
|
||||||
|
leave: "Quitter",
|
||||||
|
will_leave_any_household_explanation: "Les usagers quitteront leur ménage actuel, et ne seront pas associés à un autre ménage. Par ailleurs, ils seront enregistrés comme étant sans adresse connue.",
|
||||||
leave_without_household: "Sans nouveau ménage",
|
leave_without_household: "Sans nouveau ménage",
|
||||||
|
set_address: "Indiquer une adresse",
|
||||||
|
reset_mode: "Modifier la destination",
|
||||||
|
remove_address: "Supprimer l'adresse",
|
||||||
|
update_address: "Mettre à jour l'adresse",
|
||||||
|
// remove ?
|
||||||
|
/*
|
||||||
where_live_the_household: "À quelle adresse habite ce ménage ?",
|
where_live_the_household: "À quelle adresse habite ce ménage ?",
|
||||||
household_live_to_this_address: "Sélectionner l'adresse",
|
household_live_to_this_address: "Sélectionner l'adresse",
|
||||||
no_suggestions: "Aucune adresse à suggérer",
|
no_suggestions: "Aucune adresse à suggérer",
|
||||||
delete_this_address: "Supprimer cette adresse",
|
delete_this_address: "Supprimer cette adresse",
|
||||||
create_new_address: "Créer une nouvelle adresse",
|
create_new_address: "Créer une nouvelle adresse",
|
||||||
or_create_new_address: "Ou créer une nouvelle adresse",
|
or_create_new_address: "Ou créer une nouvelle adresse",
|
||||||
|
|
||||||
|
*/
|
||||||
|
// end remove ?
|
||||||
},
|
},
|
||||||
concerned: {
|
concerned: {
|
||||||
title: "Nouveaux membres du ménage",
|
title: "Nouveaux membres du ménage",
|
||||||
@ -34,7 +45,7 @@ const appMessages = {
|
|||||||
is_not_holder: "N'est pas titulaire",
|
is_not_holder: "N'est pas titulaire",
|
||||||
remove_position: "Retirer des {position}",
|
remove_position: "Retirer des {position}",
|
||||||
remove_concerned: "Ne plus transférer",
|
remove_concerned: "Ne plus transférer",
|
||||||
household_part: "Ménage de destination",
|
household_part: "Destination",
|
||||||
suggestions: "Suggestions",
|
suggestions: "Suggestions",
|
||||||
hide_household_suggestion: "Masquer les suggestions",
|
hide_household_suggestion: "Masquer les suggestions",
|
||||||
show_household_suggestion: 'Aucune suggestion | Afficher une suggestion | Afficher {count} suggestions',
|
show_household_suggestion: 'Aucune suggestion | Afficher une suggestion | Afficher {count} suggestions',
|
||||||
|
@ -29,6 +29,15 @@ const store = createStore({
|
|||||||
return 0;
|
return 0;
|
||||||
}),
|
}),
|
||||||
startDate: new Date(),
|
startDate: new Date(),
|
||||||
|
/**
|
||||||
|
* Indicates if the destination is:
|
||||||
|
*
|
||||||
|
* * "new" => a new household
|
||||||
|
* * "existing" => an existing household
|
||||||
|
* * "leave" => leave without household
|
||||||
|
* * null if not set
|
||||||
|
*/
|
||||||
|
mode: window.household_members_editor_data.household === null ? null : "existing",
|
||||||
allowHouseholdCreate: window.household_members_editor_data.allowHouseholdCreate,
|
allowHouseholdCreate: window.household_members_editor_data.allowHouseholdCreate,
|
||||||
allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch,
|
allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch,
|
||||||
allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold,
|
allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold,
|
||||||
@ -41,11 +50,70 @@ const store = createStore({
|
|||||||
errors: []
|
errors: []
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
/**
|
||||||
|
* return true if this page allow to create a new household
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isModeNewAllowed(state) {
|
||||||
|
return state.allowHouseholdCreate;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* return true if this page allow to "leave without household"
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isModeLeaveAllowed(state) {
|
||||||
|
return state.allowLeaveWithoutHousehold;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* return true if the mode "leave" is selected
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isModeLeave(state) {
|
||||||
|
return state.mode === "leave";
|
||||||
|
},
|
||||||
|
getSuggestions(state) {
|
||||||
|
let suggestions = [];
|
||||||
|
state.householdSuggestionByAccompanyingPeriod.forEach(h => {
|
||||||
|
console.log(h);
|
||||||
|
suggestions.push({household: h});
|
||||||
|
});
|
||||||
|
|
||||||
|
return suggestions;
|
||||||
|
},
|
||||||
isHouseholdNew(state) {
|
isHouseholdNew(state) {
|
||||||
|
return state.mode === "new";
|
||||||
|
/*
|
||||||
if (state.household === null) {
|
if (state.household === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !Number.isInteger(state.household.id);
|
return !Number.isInteger(state.household.id);
|
||||||
|
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
getAddressContext(state, getters) {
|
||||||
|
if (!getters.hasHouseholdAddress) {
|
||||||
|
return {
|
||||||
|
edit: false,
|
||||||
|
addressId: null,
|
||||||
|
target: {
|
||||||
|
name: state.household.type,
|
||||||
|
id: state.household.id
|
||||||
|
},
|
||||||
|
suggestions: state.addressesSuggestion
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
edit: true,
|
||||||
|
addressId: state.household.current_address.id,
|
||||||
|
target: {
|
||||||
|
name: state.household.type,
|
||||||
|
id: state.household.id
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
hasHouseholdAddress(state) {
|
hasHouseholdAddress(state) {
|
||||||
if (null === state.household) {
|
if (null === state.household) {
|
||||||
@ -180,6 +248,11 @@ const store = createStore({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
resetMode(state) {
|
||||||
|
state.mode = null;
|
||||||
|
state.household = null;
|
||||||
|
state.forceLeaveWithoutHousehold = false;
|
||||||
|
},
|
||||||
addConcerned(state, person) {
|
addConcerned(state, person) {
|
||||||
let persons = state.concerned.map(conc => conc.person.id);
|
let persons = state.concerned.map(conc => conc.person.id);
|
||||||
if (!persons.includes(person.id)) {
|
if (!persons.includes(person.id)) {
|
||||||
@ -222,6 +295,7 @@ const store = createStore({
|
|||||||
current_address: null,
|
current_address: null,
|
||||||
current_members_id: []
|
current_members_id: []
|
||||||
};
|
};
|
||||||
|
state.mode = "new";
|
||||||
state.forceLeaveWithoutHousehold = false;
|
state.forceLeaveWithoutHousehold = false;
|
||||||
},
|
},
|
||||||
removeHousehold(state) {
|
removeHousehold(state) {
|
||||||
@ -229,6 +303,7 @@ const store = createStore({
|
|||||||
state.forceLeaveWithoutHousehold = false;
|
state.forceLeaveWithoutHousehold = false;
|
||||||
},
|
},
|
||||||
setHouseholdAddress(state, address) {
|
setHouseholdAddress(state, address) {
|
||||||
|
console.log('setHouseholdAddress commit', address);
|
||||||
if (null === state.household) {
|
if (null === state.household) {
|
||||||
console.error("no household");
|
console.error("no household");
|
||||||
throw new Error("No household");
|
throw new Error("No household");
|
||||||
@ -246,10 +321,12 @@ const store = createStore({
|
|||||||
},
|
},
|
||||||
forceLeaveWithoutHousehold(state) {
|
forceLeaveWithoutHousehold(state) {
|
||||||
state.household = null;
|
state.household = null;
|
||||||
|
state.mode = "leave";
|
||||||
state.forceLeaveWithoutHousehold = true;
|
state.forceLeaveWithoutHousehold = true;
|
||||||
},
|
},
|
||||||
selectHousehold(state, household) {
|
selectHousehold(state, household) {
|
||||||
state.household = household;
|
state.household = household;
|
||||||
|
state.mode = "existing";
|
||||||
state.forceLeaveWithoutHousehold = false;
|
state.forceLeaveWithoutHousehold = false;
|
||||||
},
|
},
|
||||||
setHouseholdSuggestionByAccompanyingPeriod(state, households) {
|
setHouseholdSuggestionByAccompanyingPeriod(state, households) {
|
||||||
@ -320,18 +397,8 @@ const store = createStore({
|
|||||||
commit('createHousehold');
|
commit('createHousehold');
|
||||||
dispatch('computeWarnings');
|
dispatch('computeWarnings');
|
||||||
},
|
},
|
||||||
setHouseholdNewAddress({ commit }, payload) {
|
setHouseholdNewAddress({ commit }, address) {
|
||||||
let url = `/api/1.0/main/address/${payload.addressId}.json`;
|
commit('setHouseholdAddress', address);
|
||||||
window.fetch(url).then(r => {
|
|
||||||
if (r.ok) {
|
|
||||||
return r.json();
|
|
||||||
}
|
|
||||||
throw new Error("error while fetch address");
|
|
||||||
}).then(data => {
|
|
||||||
commit('setHouseholdAddress', data);
|
|
||||||
}).catch(e => {
|
|
||||||
console.error(e);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
forceLeaveWithoutHousehold({ commit, dispatch }) {
|
forceLeaveWithoutHousehold({ commit, dispatch }) {
|
||||||
commit('forceLeaveWithoutHousehold');
|
commit('forceLeaveWithoutHousehold');
|
||||||
|
@ -28,7 +28,26 @@ const postPerson = (body) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PATCH an existing person
|
||||||
|
*/
|
||||||
|
const patchPerson = (id, body) => {
|
||||||
|
const url = `/api/1.0/person/person/${id}.json`;
|
||||||
|
return fetch(url, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getPerson,
|
getPerson,
|
||||||
postPerson
|
postPerson,
|
||||||
|
patchPerson
|
||||||
};
|
};
|
||||||
|
@ -92,6 +92,7 @@ import OnTheFly from 'ChillMainAssets/vuejs/_components/OnTheFly.vue';
|
|||||||
import PersonSuggestion from './AddPersons/PersonSuggestion';
|
import PersonSuggestion from './AddPersons/PersonSuggestion';
|
||||||
import { searchPersons, searchPersons_2 } from 'ChillPersonAssets/vuejs/_api/AddPersons';
|
import { searchPersons, searchPersons_2 } from 'ChillPersonAssets/vuejs/_api/AddPersons';
|
||||||
import { postPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly";
|
import { postPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly";
|
||||||
|
import { postThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AddPersons',
|
name: 'AddPersons',
|
||||||
@ -229,7 +230,7 @@ export default {
|
|||||||
return item.result.type + item.result.id;
|
return item.result.type + item.result.id;
|
||||||
},
|
},
|
||||||
addPriorSuggestion() {
|
addPriorSuggestion() {
|
||||||
console.log('echo', this.hasPriorSuggestion);
|
//console.log('addPriorSuggestion', this.hasPriorSuggestion);
|
||||||
if (this.hasPriorSuggestion) {
|
if (this.hasPriorSuggestion) {
|
||||||
console.log('addPriorSuggestion',);
|
console.log('addPriorSuggestion',);
|
||||||
this.suggested.unshift(this.priorSuggestion);
|
this.suggested.unshift(this.priorSuggestion);
|
||||||
@ -248,31 +249,31 @@ export default {
|
|||||||
result: entity
|
result: entity
|
||||||
}
|
}
|
||||||
this.search.priorSuggestion = suggestion;
|
this.search.priorSuggestion = suggestion;
|
||||||
console.log('ici', this.search.priorSuggestion);
|
console.log('search priorSuggestion', this.search.priorSuggestion);
|
||||||
} else {
|
} else {
|
||||||
this.search.priorSuggestion = {};
|
this.search.priorSuggestion = {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
saveFormOnTheFly({ type, data }) {
|
saveFormOnTheFly({ type, data }) {
|
||||||
console.log('saveFormOnTheFly from addPersons', { type, data });
|
console.log('saveFormOnTheFly from addPersons, type', type, ', data', data);
|
||||||
|
|
||||||
// create/edit person
|
|
||||||
if (type === 'person') {
|
if (type === 'person') {
|
||||||
|
|
||||||
console.log('type person with', data);
|
console.log('type person with', data);
|
||||||
postPerson(data)
|
postPerson(data)
|
||||||
.then(person => new Promise((resolve, reject) => {
|
.then(person => new Promise((resolve, reject) => {
|
||||||
//this.person = person;
|
console.log('onthefly create: post person', person);
|
||||||
this.newPriorSuggestion(person);
|
this.newPriorSuggestion(person);
|
||||||
resolve();
|
resolve();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// create/edit thirdparty
|
|
||||||
else if (type === 'thirdparty') {
|
else if (type === 'thirdparty') {
|
||||||
console.log('not yet implemented: type thirdparty with', type, data);
|
console.log('type thirdparty with', data);
|
||||||
|
postThirdparty(data)
|
||||||
|
.then(thirdparty => new Promise((resolve, reject) => {
|
||||||
|
console.log('onthefly create: post thirdparty', thirdparty);
|
||||||
|
this.newPriorSuggestion(thirdparty);
|
||||||
|
resolve();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="options.addInfo == true" class="moreinfo">
|
<p v-if="options.addInfo === true" class="moreinfo">
|
||||||
<i :class="'fa fa-fw ' + getGenderIcon" title="{{ getGender }}"></i>
|
<i :class="'fa fa-fw ' + getGenderIcon" title="{{ getGender }}"></i>
|
||||||
<time v-if="person.birthdate && !person.deathdate" datetime="{{ person.birthdate }}" title="{{ birthdate }}">
|
<time v-if="person.birthdate && !person.deathdate" datetime="{{ person.birthdate }}" title="{{ birthdate }}">
|
||||||
{{ $t(getGenderTranslation) + ' ' + $d(birthdate, 'text') }}
|
{{ $t(getGenderTranslation) + ' ' + $d(birthdate, 'text') }}
|
||||||
@ -142,7 +142,7 @@ export default {
|
|||||||
props: ['person', 'options', 'render', 'returnPath'],
|
props: ['person', 'options', 'render', 'returnPath'],
|
||||||
computed: {
|
computed: {
|
||||||
getGenderTranslation: function() {
|
getGenderTranslation: function() {
|
||||||
return this.person.gender == 'woman' ? 'renderbox.birthday.woman' : 'renderbox.birthday.man';
|
return this.person.gender === 'woman' ? 'renderbox.birthday.woman' : 'renderbox.birthday.man';
|
||||||
},
|
},
|
||||||
isMultiline: function() {
|
isMultiline: function() {
|
||||||
if(this.options.isMultiline){
|
if(this.options.isMultiline){
|
||||||
@ -152,7 +152,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getGenderIcon: function() {
|
getGenderIcon: function() {
|
||||||
return this.person.gender == 'woman' ? 'fa-venus' : this.person.gender == 'man' ? 'fa-mars' : 'fa-neuter';
|
return this.person.gender === 'woman' ? 'fa-venus' : this.person.gender === 'man' ? 'fa-mars' : 'fa-neuter';
|
||||||
},
|
},
|
||||||
birthdate: function(){
|
birthdate: function(){
|
||||||
if(this.person.birthdate !== null){
|
if(this.person.birthdate !== null){
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<option value="man">{{ $t('person.gender.man') }}</option>
|
<option value="man">{{ $t('person.gender.man') }}</option>
|
||||||
<option value="neuter">{{ $t('person.gender.neuter') }}</option>
|
<option value="neuter">{{ $t('person.gender.neuter') }}</option>
|
||||||
</select>
|
</select>
|
||||||
<label for="gender">{{ $t('person.gender.title') }}</label>
|
<label>{{ $t('person.gender.title') }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
@ -75,7 +75,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getPerson, postPerson } from '../../_api/OnTheFly';
|
import { getPerson } from '../../_api/OnTheFly';
|
||||||
import PersonRenderBox from '../Entity/PersonRenderBox.vue';
|
import PersonRenderBox from '../Entity/PersonRenderBox.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -159,7 +159,7 @@ export default {
|
|||||||
getPerson(this.id)
|
getPerson(this.id)
|
||||||
.then(person => new Promise((resolve, reject) => {
|
.then(person => new Promise((resolve, reject) => {
|
||||||
this.person = person;
|
this.person = person;
|
||||||
//console.log('get person', this.person);
|
console.log('get person', this.person);
|
||||||
resolve();
|
resolve();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
{% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
|
{% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
|
||||||
targetEntity: { name: 'household', id: household.id },
|
targetEntity: { name: 'household', id: household.id },
|
||||||
backUrl: path('chill_person_household_summary', { 'household_id': household.id }),
|
backUrl: path('chill_person_household_summary', { 'household_id': household.id }),
|
||||||
hideAddress: true,
|
onlyButton: true,
|
||||||
mode: 'new',
|
mode: 'new',
|
||||||
buttonSize: 'btn-sm',
|
buttonSize: 'btn-sm',
|
||||||
buttonText: 'Move household',
|
buttonText: 'Move household',
|
||||||
|
@ -17,11 +17,19 @@
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-md-end">
|
<div class="text-md-end">
|
||||||
{% if person|chill_resolve_center is not null%}
|
{% if person|chill_resolve_center is not null %}
|
||||||
<span class="open_sansbold">
|
<span class="open_sansbold">
|
||||||
{{ 'Center'|trans|upper}} :
|
{{ 'Center'|trans|upper}} :
|
||||||
</span>
|
</span>
|
||||||
{{ person|chill_resolve_center.name|upper }}
|
|
||||||
|
{% if person|chill_resolve_center is iterable %}
|
||||||
|
{% for c in person|chill_resolve_center %}
|
||||||
|
{{ c.name|upper }}{% if not loop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{ person|chill_resolve_center.name|upper }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -298,6 +298,35 @@ paths:
|
|||||||
$ref: "#/components/schemas/Person"
|
$ref: "#/components/schemas/Person"
|
||||||
403:
|
403:
|
||||||
description: "Unauthorized"
|
description: "Unauthorized"
|
||||||
|
patch:
|
||||||
|
tags:
|
||||||
|
- person
|
||||||
|
summary: "Alter a person"
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The person's id
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: integer
|
||||||
|
minimum: 1
|
||||||
|
requestBody:
|
||||||
|
description: "A person"
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Person"
|
||||||
|
responses:
|
||||||
|
401:
|
||||||
|
description: "Unauthorized"
|
||||||
|
404:
|
||||||
|
description: "Not found"
|
||||||
|
200:
|
||||||
|
description: "OK"
|
||||||
|
422:
|
||||||
|
description: "Object with validation errors"
|
||||||
|
|
||||||
/1.0/person/person.json:
|
/1.0/person/person.json:
|
||||||
post:
|
post:
|
||||||
|
@ -61,20 +61,10 @@ class ThirdPartyController extends Controller
|
|||||||
$repository = $this->getDoctrine()->getManager()
|
$repository = $this->getDoctrine()->getManager()
|
||||||
->getRepository(ThirdParty::class);
|
->getRepository(ThirdParty::class);
|
||||||
|
|
||||||
$centers = $this->authorizationHelper
|
$nbThirdParties = $repository->count([]); //$repository->countByMemberOfCenters($centers);
|
||||||
->getReachableCenters(
|
|
||||||
$this->getUser(),
|
|
||||||
new Role(ThirdPartyVoter::SHOW)
|
|
||||||
);
|
|
||||||
|
|
||||||
$nbThirdParties = $repository->countByMemberOfCenters($centers); //
|
|
||||||
$pagination = $this->paginatorFactory->create($nbThirdParties);
|
$pagination = $this->paginatorFactory->create($nbThirdParties);
|
||||||
|
|
||||||
$thirdParties = $repository->findByMemberOfCenters(
|
$thirdParties = $repository->findAll();
|
||||||
$centers,
|
|
||||||
$pagination->getCurrentPage()->getFirstItemNumber(),
|
|
||||||
$pagination->getItemsPerPage()
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->render('ChillThirdPartyBundle:ThirdParty:index.html.twig', array(
|
return $this->render('ChillThirdPartyBundle:ThirdParty:index.html.twig', array(
|
||||||
'third_parties' => $thirdParties,
|
'third_parties' => $thirdParties,
|
||||||
@ -89,16 +79,7 @@ class ThirdPartyController extends Controller
|
|||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted(ThirdPartyVoter::CREATE);
|
$this->denyAccessUnlessGranted(ThirdPartyVoter::CREATE);
|
||||||
|
|
||||||
/* $centers = $this->authorizationHelper
|
$centers = [];
|
||||||
->getReachableCenters(
|
|
||||||
$this->getUser(),
|
|
||||||
new Role(ThirdPartyVoter::CREATE)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($centers === []) { //
|
|
||||||
throw new \LogicException("There should be at least one center reachable "
|
|
||||||
. "if role ".ThirdPartyVoter::CREATE." is granted");
|
|
||||||
} */
|
|
||||||
|
|
||||||
$thirdParty = new ThirdParty();
|
$thirdParty = new ThirdParty();
|
||||||
$thirdParty->setCenters(new ArrayCollection($centers));
|
$thirdParty->setCenters(new ArrayCollection($centers));
|
||||||
@ -142,16 +123,10 @@ class ThirdPartyController extends Controller
|
|||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted(ThirdPartyVoter::CREATE);
|
$this->denyAccessUnlessGranted(ThirdPartyVoter::CREATE);
|
||||||
|
|
||||||
/* $centers = $this->authorizationHelper
|
$repository = $this->getDoctrine()->getManager()
|
||||||
->getReachableCenters(
|
->getRepository(ThirdParty::class);
|
||||||
$this->getUser(),
|
|
||||||
new Role(ThirdPartyVoter::CREATE)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($centers === []) {
|
$centers = $repository->findAll();
|
||||||
throw new \LogicException("There should be at least one center reachable "
|
|
||||||
. "if role ".ThirdPartyVoter::CREATE." is granted");
|
|
||||||
} */
|
|
||||||
|
|
||||||
// we want to keep centers the users has no access to. So we will add them
|
// we want to keep centers the users has no access to. So we will add them
|
||||||
// later if they are removed. (this is a ugly hack but it will works
|
// later if they are removed. (this is a ugly hack but it will works
|
||||||
|
@ -59,27 +59,23 @@ class ChillThirdPartyExtension extends Extension implements PrependExtensionInte
|
|||||||
'class' => \Chill\ThirdPartyBundle\Entity\ThirdParty::class,
|
'class' => \Chill\ThirdPartyBundle\Entity\ThirdParty::class,
|
||||||
'name' => 'thirdparty',
|
'name' => 'thirdparty',
|
||||||
'base_path' => '/api/1.0/thirdparty/thirdparty',
|
'base_path' => '/api/1.0/thirdparty/thirdparty',
|
||||||
'base_role' => \Chill\ThirdPartyBundle\Security\Authorization\ThirdPartyVoter::class,
|
//'base_role' => \Chill\ThirdPartyBundle\Security\Authorization\ThirdPartyVoter::SHOW,
|
||||||
//'controller' => \Chill\ThirdPartyBundle\Controller\ThirdPartyApiController::class,
|
//'controller' => \Chill\ThirdPartyBundle\Controller\ThirdPartyApiController::class,
|
||||||
'actions' => [
|
'actions' => [
|
||||||
'_index' => [
|
|
||||||
'methods' => [
|
|
||||||
Request::METHOD_GET => true,
|
|
||||||
Request::METHOD_HEAD => true,
|
|
||||||
Request::METHOD_POST => true,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'_entity' => [
|
'_entity' => [
|
||||||
'methods' => [
|
'methods' => [
|
||||||
Request::METHOD_GET => true,
|
Request::METHOD_GET => true,
|
||||||
Request::METHOD_HEAD => true,
|
Request::METHOD_HEAD => true,
|
||||||
Request::METHOD_POST=> true,
|
Request::METHOD_POST => true,
|
||||||
|
Request::METHOD_PUT => true,
|
||||||
|
Request::METHOD_PATCH => true
|
||||||
],
|
],
|
||||||
'roles' => [
|
'roles' => [
|
||||||
Request::METHOD_GET => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::SHOW,
|
Request::METHOD_GET => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::SHOW,
|
||||||
Request::METHOD_HEAD => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::SHOW,
|
Request::METHOD_HEAD => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::SHOW,
|
||||||
Request::METHOD_POST => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::CREATE,
|
Request::METHOD_POST => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::CREATE,
|
||||||
|
Request::METHOD_PUT => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::CREATE,
|
||||||
|
Request::METHOD_PATCH => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::CREATE
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -60,7 +60,7 @@ class ThirdParty
|
|||||||
* @var string
|
* @var string
|
||||||
* @ORM\Column(name="name", type="string", length=255)
|
* @ORM\Column(name="name", type="string", length=255)
|
||||||
* @Assert\Length(min="2")
|
* @Assert\Length(min="2")
|
||||||
* @Groups({"read"})
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $name;
|
private $name;
|
||||||
|
|
||||||
@ -69,6 +69,7 @@ class ThirdParty
|
|||||||
* @var string
|
* @var string
|
||||||
* @ORM\Column(name="name_company", type="string", length=255, nullable=true)
|
* @ORM\Column(name="name_company", type="string", length=255, nullable=true)
|
||||||
* @Assert\Length(min="3")
|
* @Assert\Length(min="3")
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $nameCompany;
|
private $nameCompany;
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ class ThirdParty
|
|||||||
* @var string
|
* @var string
|
||||||
* @ORM\Column(name="acronym", type="string", length=64, nullable=true)
|
* @ORM\Column(name="acronym", type="string", length=64, nullable=true)
|
||||||
* @Assert\Length(min="2")
|
* @Assert\Length(min="2")
|
||||||
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $acronym;
|
private $acronym;
|
||||||
|
|
||||||
@ -94,7 +96,7 @@ class ThirdParty
|
|||||||
* @ORM\Column(name="types", type="json", nullable=true)
|
* @ORM\Column(name="types", type="json", nullable=true)
|
||||||
* @Assert\Count(min=1)
|
* @Assert\Count(min=1)
|
||||||
*/
|
*/
|
||||||
private $type;
|
private $types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contact Persons: One Institutional ThirdParty has Many Contact Persons
|
* Contact Persons: One Institutional ThirdParty has Many Contact Persons
|
||||||
@ -130,7 +132,7 @@ class ThirdParty
|
|||||||
* @Assert\Regex("/^([\+{1}])([0-9\s*]{4,20})$/",
|
* @Assert\Regex("/^([\+{1}])([0-9\s*]{4,20})$/",
|
||||||
* message="Invalid phone number: it should begin with the international prefix starting with ""+"", hold only digits and be smaller than 20 characters. Ex: +33123456789"
|
* message="Invalid phone number: it should begin with the international prefix starting with ""+"", hold only digits and be smaller than 20 characters. Ex: +33123456789"
|
||||||
* )
|
* )
|
||||||
* @Groups({"read"})
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $telephone;
|
private $telephone;
|
||||||
|
|
||||||
@ -138,7 +140,7 @@ class ThirdParty
|
|||||||
* @var string|null
|
* @var string|null
|
||||||
* @ORM\Column(name="email", type="string", length=255, nullable=true)
|
* @ORM\Column(name="email", type="string", length=255, nullable=true)
|
||||||
* @Assert\Email(checkMX=false)
|
* @Assert\Email(checkMX=false)
|
||||||
* @Groups({"read"})
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $email;
|
private $email;
|
||||||
|
|
||||||
@ -147,7 +149,7 @@ class ThirdParty
|
|||||||
* @ORM\ManyToOne(targetEntity="\Chill\MainBundle\Entity\Address",
|
* @ORM\ManyToOne(targetEntity="\Chill\MainBundle\Entity\Address",
|
||||||
* cascade={"persist", "remove"})
|
* cascade={"persist", "remove"})
|
||||||
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
|
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
|
||||||
* @Groups({"read"})
|
* @Groups({"read", "write"})
|
||||||
*/
|
*/
|
||||||
private $address;
|
private $address;
|
||||||
|
|
||||||
@ -168,7 +170,6 @@ class ThirdParty
|
|||||||
* @var Collection
|
* @var Collection
|
||||||
* @ORM\ManyToMany(targetEntity="\Chill\MainBundle\Entity\Center")
|
* @ORM\ManyToMany(targetEntity="\Chill\MainBundle\Entity\Center")
|
||||||
* @ORM\JoinTable(name="chill_3party.party_center")
|
* @ORM\JoinTable(name="chill_3party.party_center")
|
||||||
* @Assert\Count(min=1)
|
|
||||||
*/
|
*/
|
||||||
private $centers;
|
private $centers;
|
||||||
|
|
||||||
@ -325,7 +326,7 @@ class ThirdParty
|
|||||||
* @param array|null $type
|
* @param array|null $type
|
||||||
* @return ThirdParty
|
* @return ThirdParty
|
||||||
*/
|
*/
|
||||||
public function setType(array $type = null)
|
public function setTypes(array $type = null)
|
||||||
{
|
{
|
||||||
// remove all keys from the input data
|
// remove all keys from the input data
|
||||||
$this->type = \array_values($type);
|
$this->type = \array_values($type);
|
||||||
@ -338,9 +339,9 @@ class ThirdParty
|
|||||||
*
|
*
|
||||||
* @return array|null
|
* @return array|null
|
||||||
*/
|
*/
|
||||||
public function getType()
|
public function getTypes()
|
||||||
{
|
{
|
||||||
return $this->type;
|
return $this->types;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,10 +64,10 @@ class ThirdPartyType extends AbstractType
|
|||||||
}
|
}
|
||||||
if (count($types) === 1) {
|
if (count($types) === 1) {
|
||||||
$builder
|
$builder
|
||||||
->add('type', HiddenType::class, [
|
->add('types', HiddenType::class, [
|
||||||
'data' => array_values($types)
|
'data' => array_values($types)
|
||||||
])
|
])
|
||||||
->get('type')
|
->get('types')
|
||||||
->addModelTransformer(new CallbackTransformer(
|
->addModelTransformer(new CallbackTransformer(
|
||||||
function (?array $typeArray): ?string {
|
function (?array $typeArray): ?string {
|
||||||
if (null === $typeArray) {
|
if (null === $typeArray) {
|
||||||
@ -84,7 +84,7 @@ class ThirdPartyType extends AbstractType
|
|||||||
))
|
))
|
||||||
;
|
;
|
||||||
} else {
|
} else {
|
||||||
$builder->add('type', ChoiceType::class, [
|
$builder->add('types', ChoiceType::class, [
|
||||||
'choices' => $types,
|
'choices' => $types,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
|
@ -13,7 +13,7 @@ const getThirdparty = (id) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* POST a new person
|
* POST a new thirdparty
|
||||||
*/
|
*/
|
||||||
const postThirdparty = (body) => {
|
const postThirdparty = (body) => {
|
||||||
const url = `/api/1.0/thirdparty/thirdparty.json`;
|
const url = `/api/1.0/thirdparty/thirdparty.json`;
|
||||||
@ -30,7 +30,26 @@ const postThirdparty = (body) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PATCH an existing thirdparty
|
||||||
|
*/
|
||||||
|
const patchThirdparty = (id, body) => {
|
||||||
|
const url = `/api/1.0/thirdparty/thirdparty/${id}.json`;
|
||||||
|
return fetch(url, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) { return response.json(); }
|
||||||
|
throw Error('Error with request resource response');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getThirdparty,
|
getThirdparty,
|
||||||
postThirdparty
|
postThirdparty,
|
||||||
|
patchThirdparty
|
||||||
};
|
};
|
@ -8,25 +8,17 @@
|
|||||||
|
|
||||||
<div :class="'denomination h' + options.hLevel">
|
<div :class="'denomination h' + options.hLevel">
|
||||||
|
|
||||||
<a v-if="this.options.addLink == true" href="#">
|
<a v-if="this.options.addLink === true" href="#">
|
||||||
<span class="name">{{ thirdparty.text }}</span>
|
<span class="name">{{ thirdparty.text }}</span>
|
||||||
</a>
|
</a>
|
||||||
<span class="name">{{ thirdparty.text }}</span>
|
<span class="name">{{ thirdparty.text }}</span>
|
||||||
|
|
||||||
<span v-if="options.addId == true" class="id-number" :title="'n° ' + thirdparty.id">{{ thirdparty.id }}</span>
|
<span v-if="options.addId == true" class="id-number" :title="'n° ' + thirdparty.id">{{ thirdparty.id }}</span>
|
||||||
<span v-if="options.addEntity == true && thirdparty.type == 'thirdparty'" class="badge rounded-pill bg-secondary">{{ $t('renderbox.type.thirdparty') }}</span>
|
<span v-if="options.addEntity == true && thirdparty.type === 'thirdparty'" class="badge rounded-pill bg-secondary">{{ $t('renderbox.type.thirdparty') }}</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="this.options.addInfo == true" class="moreinfo">
|
<p v-if="this.options.addInfo === true" class="moreinfo">
|
||||||
<i v-if="thirdparty.birthdate" :class="'fa fa-fw ' + getGenderIcon" title="{{ getGender }}"></i>
|
|
||||||
<time v-if="thirdparty.birthdate" datetime="{{ thirdparty.birthdate.datetime }}" title="{{ birthdate }}">
|
|
||||||
{{ $t(getGender) + ' ' + $d(birthdate, 'short') }}
|
|
||||||
</time>
|
|
||||||
<time v-else-if="thirdparty.deathdate" datetime="{{ thirdparty.deathdate.datetime }}" title="{{ thirdparty.deathdate }}">
|
|
||||||
{{ birthdate }} - {{ deathdate }}
|
|
||||||
</time>
|
|
||||||
<span v-if="options.addAge == true" class="age">{{ thirdparty.age }}</span>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -42,9 +34,9 @@
|
|||||||
<i class="fa fa-li fa-map-marker"></i>
|
<i class="fa fa-li fa-map-marker"></i>
|
||||||
<address-render-box :address="thirdparty.address" :isMultiline="isMultiline"></address-render-box>
|
<address-render-box :address="thirdparty.address" :isMultiline="isMultiline"></address-render-box>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="thirdparty.telephone">
|
<li v-if="thirdparty.phonenumber">
|
||||||
<i class="fa fa-li fa-mobile"></i>
|
<i class="fa fa-li fa-mobile"></i>
|
||||||
<a :href="'tel: ' + thirdparty.telephone">{{ thirdparty.telephone }}</a>
|
<a :href="'tel: ' + thirdparty.phonenumber">{{ thirdparty.phonenumber }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="thirdparty.email">
|
<li v-if="thirdparty.email">
|
||||||
<i class="fa fa-li fa-envelope-o"></i>
|
<i class="fa fa-li fa-envelope-o"></i>
|
||||||
@ -78,21 +70,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
getGender: function() {
|
|
||||||
return this.thirdparty.gender == 'woman' ? 'renderbox.birthday.woman' : 'renderbox.birthday.man';
|
|
||||||
},
|
|
||||||
getGenderIcon: function() {
|
|
||||||
return this.thirdparty.gender == 'woman' ? 'fa-venus' : this.thirdparty.gender == 'man' ? 'fa-mars' : 'fa-neuter';
|
|
||||||
},
|
|
||||||
birthdate: function(){
|
|
||||||
var date = new Date(this.thirdparty.birthdate.datetime);
|
|
||||||
return dateToISO(date);
|
|
||||||
},
|
|
||||||
deathdate: function(){
|
|
||||||
var date = new Date(this.thirdparty.deathdate.datetime);
|
|
||||||
return dateToISO(date);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="action === 'show'">
|
<div v-if="action === 'show'">
|
||||||
<div class="flex-table">
|
<div class="flex-table">
|
||||||
<third-party-render-box
|
<third-party-render-box
|
||||||
:thirdparty="thirdparty"
|
:thirdparty="thirdparty"
|
||||||
@ -21,13 +21,8 @@
|
|||||||
<div v-else-if="action === 'edit' || action === 'create'">
|
<div v-else-if="action === 'edit' || action === 'create'">
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input class="form-control form-control-lg" id="firstname" v-model="thirdparty.firstName" v-bind:placeholder="$t('thirdparty.firstname')" />
|
<input class="form-control form-control-lg" id="name" v-model="thirdparty.text" v-bind:placeholder="$t('thirdparty.name')" />
|
||||||
<label for="firstname">{{ $t('thirdparty.firstname') }}</label>
|
<label for="name">{{ $t('thirdparty.name') }}</label>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
|
||||||
<input class="form-control form-control-lg" id="lastname" v-model="thirdparty.lastName" v-bind:placeholder="$t('thirdparty.lastname')" />
|
|
||||||
<label for="lastname">{{ $t('thirdparty.lastname') }}</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
@ -47,44 +42,82 @@
|
|||||||
v-bind:aria-label="$t('thirdparty.phonenumber')"
|
v-bind:aria-label="$t('thirdparty.phonenumber')"
|
||||||
aria-describedby="phonenumber" />
|
aria-describedby="phonenumber" />
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
|
<add-address
|
||||||
|
:context="this.context"
|
||||||
|
:options="this.addAddress.options"
|
||||||
|
:address-changed-callback="submitAddress">
|
||||||
|
</add-address>
|
||||||
|
-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ThirdPartyRenderBox from '../Entity/ThirdPartyRenderBox.vue';
|
import ThirdPartyRenderBox from '../Entity/ThirdPartyRenderBox.vue';
|
||||||
import { getThirdparty, postThirdparty } from '../../_api/OnTheFly';
|
import AddAddress from 'ChillMainAssets/vuejs/Address/components/AddAddress';
|
||||||
|
import { getThirdparty } from '../../_api/OnTheFly';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "OnTheFlyThirdParty",
|
name: "OnTheFlyThirdParty",
|
||||||
props: ['id', 'type', 'action'],
|
props: ['id', 'type', 'action'],
|
||||||
components: {
|
components: {
|
||||||
ThirdPartyRenderBox,
|
ThirdPartyRenderBox,
|
||||||
|
AddAddress
|
||||||
},
|
},
|
||||||
data: function() {
|
data() {
|
||||||
return {
|
return {
|
||||||
thirdparty: {
|
thirdparty: {
|
||||||
type: 'thirdparty'
|
type: 'thirdparty'
|
||||||
|
},
|
||||||
|
addAddress: {
|
||||||
|
options: {
|
||||||
|
openPanesInModal: true,
|
||||||
|
onlyButton: false,
|
||||||
|
/*
|
||||||
|
button: {
|
||||||
|
text: {
|
||||||
|
create: 'courselocation.add_temporary_address',
|
||||||
|
edit: 'courselocation.edit_temporary_address'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
create: 'courselocation.add_temporary_address',
|
||||||
|
edit: 'courselocation.edit_temporary_address'
|
||||||
|
} */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
context() {
|
||||||
|
let context = {
|
||||||
|
target: {
|
||||||
|
name: this.type,
|
||||||
|
id: this.id
|
||||||
|
},
|
||||||
|
edit: false,
|
||||||
|
addressId: null
|
||||||
|
};
|
||||||
|
console.log('context', context);
|
||||||
|
return context;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadThirdparty(){
|
loadData(){
|
||||||
getThirdparty(this.id).then(thirdparty => new Promise((resolve, reject) => {
|
getThirdparty(this.id).then(thirdparty => new Promise((resolve, reject) => {
|
||||||
this.thirdparty = thirdparty;
|
this.thirdparty = thirdparty;
|
||||||
|
console.log('get thirdparty', thirdparty);
|
||||||
resolve();
|
resolve();
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
postData() {
|
submitAddress(payload) {
|
||||||
postThirdparty(this.thirdparty).then(thirdparty => new Promise((resolve, reject) => {
|
console.log('submitAddress', payload);
|
||||||
this.thirdparty = thirdparty;
|
|
||||||
resolve();
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.action !== 'create'){
|
if (this.action !== 'create') {
|
||||||
this.loadThirdparty();
|
this.loadData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
const thirdpartyMessages = {
|
||||||
|
fr: {
|
||||||
|
thirdparty: {
|
||||||
|
name: "Dénomination",
|
||||||
|
email: "Courriel",
|
||||||
|
phonenumber: "Téléphone",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { thirdpartyMessages };
|
@ -51,7 +51,7 @@
|
|||||||
<th>{{ (tp.active ? '<i class="fa fa-check chill-green">' : '<i class="fa fa-times chill-red">')|raw }}</th>
|
<th>{{ (tp.active ? '<i class="fa fa-check chill-green">' : '<i class="fa fa-times chill-red">')|raw }}</th>
|
||||||
<td>{{ tp.name }}</td>
|
<td>{{ tp.name }}</td>
|
||||||
{% set types = [] %}
|
{% set types = [] %}
|
||||||
{% for t in tp.type %}
|
{% for t in tp.types %}
|
||||||
{% set types = types|merge( [ ('chill_3party.key_label.'~t)|trans ] ) %}
|
{% set types = types|merge( [ ('chill_3party.key_label.'~t)|trans ] ) %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<td>{{ types|join(', ') }}</td>
|
<td>{{ types|join(', ') }}</td>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
{{ form_row(form.profession) }}
|
{{ form_row(form.profession) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{{ form_row(form.type) }}
|
{{ form_row(form.types) }}
|
||||||
{{ form_row(form.categories) }}
|
{{ form_row(form.categories) }}
|
||||||
|
|
||||||
{{ form_row(form.telephone) }}
|
{{ form_row(form.telephone) }}
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
<dt>{{ 'Type'|trans }}</dt>
|
<dt>{{ 'Type'|trans }}</dt>
|
||||||
{% set types = [] %}
|
{% set types = [] %}
|
||||||
{% for t in thirdParty.type %}
|
{% for t in thirdParty.types %}
|
||||||
{% set types = types|merge( [ ('chill_3party.key_label.'~t)|trans ] ) %}
|
{% set types = types|merge( [ ('chill_3party.key_label.'~t)|trans ] ) %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<dd>
|
<dd>
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
{{ form_row(form.profession) }}
|
{{ form_row(form.profession) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{{ form_row(form.type) }}
|
{{ form_row(form.types) }}
|
||||||
{{ form_row(form.categories) }}
|
{{ form_row(form.categories) }}
|
||||||
|
|
||||||
{{ form_row(form.telephone) }}
|
{{ form_row(form.telephone) }}
|
||||||
|
@ -64,6 +64,8 @@ class ThirdPartyVoter extends AbstractChillVoter implements ProvideRoleHierarchy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
$centers = $this->authorizationHelper
|
$centers = $this->authorizationHelper
|
||||||
->getReachableCenters($user, new Role($attribute));
|
->getReachableCenters($user, new Role($attribute));
|
||||||
|
|
||||||
|
@ -8,17 +8,57 @@ servers:
|
|||||||
- url: "/api"
|
- url: "/api"
|
||||||
description: "Your current dev server"
|
description: "Your current dev server"
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Thirdparty:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
readOnly: true
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- "thirdparty"
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
telephone:
|
||||||
|
type: string
|
||||||
|
address:
|
||||||
|
$ref: "#/components/schemas/Address"
|
||||||
|
Address:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
/1.0/thirdparty/thirdparty.json:
|
/1.0/thirdparty/thirdparty.json:
|
||||||
get:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- thirdparty
|
- thirdparty
|
||||||
summary: Return a list of all thirdparty items
|
summary: Create a single thirdparty
|
||||||
|
requestBody:
|
||||||
|
description: "A thirdparty"
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Thirdparty"
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: "ok"
|
description: "OK"
|
||||||
401:
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Thirdparty"
|
||||||
|
403:
|
||||||
description: "Unauthorized"
|
description: "Unauthorized"
|
||||||
|
422:
|
||||||
|
description: "Invalid data"
|
||||||
|
|
||||||
/1.0/thirdparty/thirdparty/{id}.json:
|
/1.0/thirdparty/thirdparty/{id}.json:
|
||||||
get:
|
get:
|
||||||
@ -41,3 +81,32 @@ paths:
|
|||||||
description: "not found"
|
description: "not found"
|
||||||
401:
|
401:
|
||||||
description: "Unauthorized"
|
description: "Unauthorized"
|
||||||
|
patch:
|
||||||
|
tags:
|
||||||
|
- thirdparty
|
||||||
|
summary: "Alter a thirdparty"
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The thirdparty's id
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: integer
|
||||||
|
minimum: 1
|
||||||
|
requestBody:
|
||||||
|
description: "A thirdparty"
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Thirdparty"
|
||||||
|
responses:
|
||||||
|
401:
|
||||||
|
description: "Unauthorized"
|
||||||
|
404:
|
||||||
|
description: "Not found"
|
||||||
|
200:
|
||||||
|
description: "OK"
|
||||||
|
422:
|
||||||
|
description: "Object with validation errors"
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"php": ">= 7.4",
|
"php": ">= 7.4",
|
||||||
"champs-libres/wopi-bundle": "dev-master",
|
"champs-libres/wopi-bundle": "dev-master",
|
||||||
"nyholm/psr7": "^1.4",
|
"nyholm/psr7": "^1.4",
|
||||||
"php-opencloud/openstack": "^3.2.1"
|
"symfony/mime": "^4 || ^5"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@ -9,9 +9,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\WopiBundle\Controller;
|
namespace Chill\WopiBundle\Controller;
|
||||||
|
|
||||||
use ChampsLibres\WopiLib\Configuration\WopiConfigurationInterface;
|
use ChampsLibres\WopiLib\Contract\Service\Configuration\ConfigurationInterface;
|
||||||
use ChampsLibres\WopiLib\Discovery\WopiDiscoveryInterface;
|
use ChampsLibres\WopiLib\Contract\Service\Discovery\DiscoveryInterface;
|
||||||
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
|
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Chill\WopiBundle\Service\Controller\ResponderInterface;
|
use Chill\WopiBundle\Service\Controller\ResponderInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
use loophp\psr17\Psr17Interface;
|
use loophp\psr17\Psr17Interface;
|
||||||
@ -23,11 +24,11 @@ use Symfony\Component\Security\Core\Security;
|
|||||||
|
|
||||||
final class Test
|
final class Test
|
||||||
{
|
{
|
||||||
private StoredObjectRepository $storedObjectRepository;
|
private DiscoveryInterface $wopiDiscovery;
|
||||||
|
|
||||||
private WopiDiscoveryInterface $wopiDiscovery;
|
private DocumentManagerInterface $documentManager;
|
||||||
|
|
||||||
private WopiConfigurationInterface $wopiConfiguration;
|
private ConfigurationInterface $wopiConfiguration;
|
||||||
|
|
||||||
private ResponderInterface $responder;
|
private ResponderInterface $responder;
|
||||||
|
|
||||||
@ -38,15 +39,15 @@ final class Test
|
|||||||
private RouterInterface $router;
|
private RouterInterface $router;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
StoredObjectRepository $storedObjectRepository,
|
ConfigurationInterface $wopiConfiguration,
|
||||||
WopiConfigurationInterface $wopiConfiguration,
|
DiscoveryInterface $wopiDiscovery,
|
||||||
WopiDiscoveryInterface $wopiDiscovery,
|
DocumentManagerInterface $documentManager,
|
||||||
ResponderInterface $responder,
|
ResponderInterface $responder,
|
||||||
Security $security,
|
Security $security,
|
||||||
Psr17Interface $psr17,
|
Psr17Interface $psr17,
|
||||||
RouterInterface $router
|
RouterInterface $router
|
||||||
) {
|
) {
|
||||||
$this->storedObjectRepository = $storedObjectRepository;
|
$this->documentManager = $documentManager;
|
||||||
$this->wopiConfiguration = $wopiConfiguration;
|
$this->wopiConfiguration = $wopiConfiguration;
|
||||||
$this->wopiDiscovery = $wopiDiscovery;
|
$this->wopiDiscovery = $wopiDiscovery;
|
||||||
$this->responder = $responder;
|
$this->responder = $responder;
|
||||||
@ -58,11 +59,11 @@ final class Test
|
|||||||
public function __invoke(string $fileId): Response
|
public function __invoke(string $fileId): Response
|
||||||
{
|
{
|
||||||
$configuration = $this->wopiConfiguration->jsonSerialize();
|
$configuration = $this->wopiConfiguration->jsonSerialize();
|
||||||
|
/** @var StoredObject $storedObject */
|
||||||
$storedObject = $this->storedObjectRepository->findOneBy(['filename' => $fileId]);
|
$storedObject = $this->documentManager->findByDocumentId($fileId);
|
||||||
|
|
||||||
if (null === $storedObject) {
|
if (null === $storedObject) {
|
||||||
throw new NotFoundHttpException(sprintf('Unable to find object named %s', $fileId));
|
throw new NotFoundHttpException(sprintf('Unable to find object %s', $fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([] === $discoverExtension = $this->wopiDiscovery->discoverMimeType($storedObject->getType())) {
|
if ([] === $discoverExtension = $this->wopiDiscovery->discoverMimeType($storedObject->getType())) {
|
||||||
@ -83,7 +84,7 @@ final class Test
|
|||||||
->generate(
|
->generate(
|
||||||
'checkFileInfo',
|
'checkFileInfo',
|
||||||
[
|
[
|
||||||
'fileId' => $storedObject->getFilename(),
|
'fileId' => $this->documentManager->getDocumentId($storedObject),
|
||||||
],
|
],
|
||||||
UrlGeneratorInterface::ABSOLUTE_URL
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
),
|
),
|
||||||
|
@ -10,8 +10,10 @@ declare(strict_types=1);
|
|||||||
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
||||||
|
|
||||||
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
|
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
|
||||||
use ChampsLibres\WopiLib\Service\Contract\WopiInterface;
|
|
||||||
use Chill\WopiBundle\Service\Wopi\ChillWopi;
|
use Chill\WopiBundle\Service\Wopi\ChillWopi;
|
||||||
|
use ChampsLibres\WopiBundle\Service\Wopi as CLWopi;
|
||||||
|
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
||||||
|
use Chill\WopiBundle\Service\Wopi\ChillDocumentManager;
|
||||||
|
|
||||||
return static function (ContainerConfigurator $container) {
|
return static function (ContainerConfigurator $container) {
|
||||||
$services = $container
|
$services = $container
|
||||||
@ -30,8 +32,14 @@ return static function (ContainerConfigurator $container) {
|
|||||||
->tag('controller.service_arguments');
|
->tag('controller.service_arguments');
|
||||||
|
|
||||||
$services
|
$services
|
||||||
->alias(WopiInterface::class, ChillWopi::class);
|
->set(ChillWopi::class)
|
||||||
|
->decorate(CLWopi::class)
|
||||||
|
->arg('$wopi', service('.inner'));
|
||||||
|
|
||||||
|
$services
|
||||||
|
->alias(DocumentManagerInterface::class, ChillDocumentManager::class);
|
||||||
|
|
||||||
|
// TODO: Move this into the async bundle (low priority)
|
||||||
$services
|
$services
|
||||||
->alias(TempUrlGeneratorInterface::class, 'async_uploader.temp_url_generator');
|
->alias(TempUrlGeneratorInterface::class, 'async_uploader.temp_url_generator');
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,244 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\WopiBundle\Service\Wopi;
|
||||||
|
|
||||||
|
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
|
||||||
|
use ChampsLibres\WopiLib\Contract\Entity\Document;
|
||||||
|
use ChampsLibres\WopiLib\Contract\Service\DocumentLockManagerInterface;
|
||||||
|
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Error;
|
||||||
|
use loophp\psr17\Psr17Interface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
|
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\Mime\MimeTypes;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
final class ChillDocumentManager implements DocumentManagerInterface
|
||||||
|
{
|
||||||
|
private DocumentLockManagerInterface $documentLockManager;
|
||||||
|
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
private HttpClientInterface $httpClient;
|
||||||
|
|
||||||
|
private Psr17Interface $psr17;
|
||||||
|
|
||||||
|
private RequestInterface $request;
|
||||||
|
|
||||||
|
private StoredObjectRepository $storedObjectRepository;
|
||||||
|
|
||||||
|
private TempUrlGeneratorInterface $tempUrlGenerator;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
DocumentLockManagerInterface $documentLockManager,
|
||||||
|
EntityManagerInterface $entityManager,
|
||||||
|
HttpClientInterface $httpClient,
|
||||||
|
Psr17Interface $psr17,
|
||||||
|
StoredObjectRepository $storedObjectRepository,
|
||||||
|
TempUrlGeneratorInterface $tempUrlGenerator,
|
||||||
|
HttpMessageFactoryInterface $httpMessageFactory,
|
||||||
|
RequestStack $requestStack
|
||||||
|
) {
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
|
$this->psr17 = $psr17;
|
||||||
|
$this->storedObjectRepository = $storedObjectRepository;
|
||||||
|
$this->documentLockManager = $documentLockManager;
|
||||||
|
$this->tempUrlGenerator = $tempUrlGenerator;
|
||||||
|
$this->httpClient = $httpClient;
|
||||||
|
$this->request = $httpMessageFactory->createRequest($requestStack->getCurrentRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(array $data): Document
|
||||||
|
{
|
||||||
|
/** @var StoredObject $document */
|
||||||
|
$document = (new ObjectNormalizer())->denormalize([], StoredObject::class);
|
||||||
|
|
||||||
|
// Mime types / extension handling.
|
||||||
|
$mimeTypes = new MimeTypes();
|
||||||
|
$mimeTypes->getMimeTypes($data['extension']);
|
||||||
|
$document->setType(reset($mimeTypes));
|
||||||
|
|
||||||
|
$document->setFilename($data['name']);
|
||||||
|
|
||||||
|
$this->entityManager->persist($document);
|
||||||
|
$this->entityManager->flush($document);
|
||||||
|
|
||||||
|
// TODO : Ask proper mapping.
|
||||||
|
// Available: basename, name, extension, content, size
|
||||||
|
$this->setContent($document, $data['content']);
|
||||||
|
|
||||||
|
return $document;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteLock(Document $document): void {
|
||||||
|
$this->documentLockManager->deleteLock($document, $this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $documentFilename without extension !
|
||||||
|
*/
|
||||||
|
public function findByDocumentFilename(string $documentFilename): ?Document {
|
||||||
|
return $this->storedObjectRepository->findOneBy(
|
||||||
|
[
|
||||||
|
'filename' => $documentFilename,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByDocumentId(string $documentId): ?Document {
|
||||||
|
return $this->storedObjectRepository->findOneBy(
|
||||||
|
[
|
||||||
|
'uuid' => Uuid::fromString($documentId),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StoredObject $document
|
||||||
|
*/
|
||||||
|
public function getCreationDate(Document $document): DateTimeInterface
|
||||||
|
{
|
||||||
|
return $document->getCreationDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StoredObject $document
|
||||||
|
*/
|
||||||
|
public function getLastModifiedDate(Document $document): DateTimeInterface
|
||||||
|
{
|
||||||
|
// TODO: Add column 'LastModifiedDate' in StoredObject entity
|
||||||
|
return $document->getCreationDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLock(Document $document): string {
|
||||||
|
return $this->documentLockManager->getLock($document, $this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersion(Document $document): string {
|
||||||
|
// TODO ?
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasLock(Document $document): bool {
|
||||||
|
return $this->documentLockManager->hasLock($document, $this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lock(Document $document, string $lock): void {
|
||||||
|
$this->documentLockManager->setLock($document, $lock, $this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove(Document $document): void {
|
||||||
|
$entityIsDeleted = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->entityManager->remove($document);
|
||||||
|
$entityIsDeleted = true;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$entityIsDeleted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entityIsDeleted === true) {
|
||||||
|
$this->deleteContent($document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write(Document $document, array $properties = []): void
|
||||||
|
{
|
||||||
|
$this->setContent($document, $properties['content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StoredObject $document
|
||||||
|
*
|
||||||
|
* @return string The document filename with its extension.
|
||||||
|
*/
|
||||||
|
public function getBasename(Document $document): string {
|
||||||
|
$exts = (new MimeTypes())->getExtensions($document->getType());
|
||||||
|
|
||||||
|
if ([] === $exts) {
|
||||||
|
throw new Error('Unknown mimetype for stored document.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('%s.%s', $document->getFilename(), reset($exts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StoredObject $document
|
||||||
|
*/
|
||||||
|
public function getDocumentId(Document $document): string {
|
||||||
|
return (string) $document->getUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSha256(Document $document): string {
|
||||||
|
return base64_encode(hash('sha256', $this->getContent($document)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize(Document $document): int {
|
||||||
|
return strlen($this->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read(Document $document): StreamInterface {
|
||||||
|
return $this
|
||||||
|
->psr17
|
||||||
|
->createStream($this->getContent($document));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function deleteContent(StoredObject $storedObject): void
|
||||||
|
{
|
||||||
|
/** @var StdClass $object */
|
||||||
|
$object = $this->tempUrlGenerator->generate('DELETE', $storedObject->getFilename());
|
||||||
|
|
||||||
|
$response = $this->httpClient->request('DELETE', $object->url);
|
||||||
|
|
||||||
|
if (200 !== $response->getStatusCode())
|
||||||
|
{
|
||||||
|
throw new Error('Unable to delete stored object.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getContent(StoredObject $storedObject): string
|
||||||
|
{
|
||||||
|
/** @var StdClass $object */
|
||||||
|
$object = $this->tempUrlGenerator->generate('GET', $storedObject->getFilename());
|
||||||
|
|
||||||
|
$response = $this->httpClient->request('GET', $object->url);
|
||||||
|
|
||||||
|
if (200 !== $response->getStatusCode())
|
||||||
|
{
|
||||||
|
throw new Error('Unable to retrieve stored object.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setContent(StoredObject $storedObject, string $content): void
|
||||||
|
{
|
||||||
|
// TODO: Add strict typing in champs-libres/async-uploader-bundle
|
||||||
|
/** @var StdClass $object */
|
||||||
|
$object = $this->tempUrlGenerator->generate('PUT', $storedObject->getFilename());
|
||||||
|
|
||||||
|
$response = $this->httpClient->request('PUT', $object->url, ['body' => $content]);
|
||||||
|
|
||||||
|
if (201 !== $response->getStatusCode())
|
||||||
|
{
|
||||||
|
throw new Error('Unable to save stored object.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -9,13 +9,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\WopiBundle\Service\Wopi;
|
namespace Chill\WopiBundle\Service\Wopi;
|
||||||
|
|
||||||
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
|
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
|
||||||
use ChampsLibres\WopiLib\Discovery\WopiDiscoveryInterface;
|
use ChampsLibres\WopiLib\Contract\Service\WopiInterface;
|
||||||
use ChampsLibres\WopiLib\Service\Contract\WopiInterface;
|
|
||||||
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
|
|
||||||
use Exception;
|
|
||||||
use loophp\psr17\Psr17Interface;
|
use loophp\psr17\Psr17Interface;
|
||||||
use Psr\Http\Client\ClientInterface;
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||||
@ -23,32 +20,28 @@ use Symfony\Component\Security\Core\User\UserProviderInterface;
|
|||||||
|
|
||||||
final class ChillWopi implements WopiInterface
|
final class ChillWopi implements WopiInterface
|
||||||
{
|
{
|
||||||
|
private CacheItemPoolInterface $cache;
|
||||||
|
|
||||||
|
private DocumentManagerInterface $documentManager;
|
||||||
|
|
||||||
private Psr17Interface $psr17;
|
private Psr17Interface $psr17;
|
||||||
|
|
||||||
private WopiDiscoveryInterface $wopiDiscovery;
|
|
||||||
|
|
||||||
private StoredObjectRepository $storedObjectRepository;
|
|
||||||
|
|
||||||
private ClientInterface $httpClient;
|
|
||||||
|
|
||||||
private TempUrlGeneratorInterface $tempUrlGenerator;
|
|
||||||
|
|
||||||
private UserProviderInterface $userProvider;
|
private UserProviderInterface $userProvider;
|
||||||
|
|
||||||
|
private WopiInterface $wopi;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
CacheItemPoolInterface $cache,
|
||||||
|
DocumentManagerInterface $documentManager,
|
||||||
Psr17Interface $psr17,
|
Psr17Interface $psr17,
|
||||||
WopiDiscoveryInterface $wopiDiscovery,
|
UserProviderInterface $userProvider,
|
||||||
StoredObjectRepository $storedObjectRepository,
|
WopiInterface $wopi
|
||||||
ClientInterface $httpClient,
|
|
||||||
TempUrlGeneratorInterface $tempUrlGenerator,
|
|
||||||
UserProviderInterface $userProvider
|
|
||||||
) {
|
) {
|
||||||
|
$this->cache = $cache;
|
||||||
|
$this->documentManager = $documentManager;
|
||||||
$this->psr17 = $psr17;
|
$this->psr17 = $psr17;
|
||||||
$this->wopiDiscovery = $wopiDiscovery;
|
|
||||||
$this->storedObjectRepository = $storedObjectRepository;
|
|
||||||
$this->httpClient = $httpClient;
|
|
||||||
$this->tempUrlGenerator = $tempUrlGenerator;
|
|
||||||
$this->userProvider = $userProvider;
|
$this->userProvider = $userProvider;
|
||||||
|
$this->wopi = $wopi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkFileInfo(
|
public function checkFileInfo(
|
||||||
@ -64,17 +57,10 @@ final class ChillWopi implements WopiInterface
|
|||||||
->createResponse(401);
|
->createResponse(401);
|
||||||
}
|
}
|
||||||
|
|
||||||
$storedObject = $this->storedObjectRepository->findOneBy(['filename' => $fileId]);
|
// @ TODO : Replace this with a call to decorated object once authentication is done.
|
||||||
|
$document = $this->documentManager->findByDocumentId($fileId);
|
||||||
if (null === $storedObject) {
|
$userIdentifier = $user->getUsername();
|
||||||
throw new Exception(sprintf('Unable to find object named %s', $fileId));
|
$userCacheKey = sprintf('wopi_putUserInfo_%s', $userIdentifier);
|
||||||
}
|
|
||||||
|
|
||||||
$mimeType = $storedObject->getType();
|
|
||||||
|
|
||||||
if ([] === $this->wopiDiscovery->discoverMimeType($mimeType)) {
|
|
||||||
throw new Exception(sprintf('Unable to find mime type %s', $mimeType));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->psr17
|
->psr17
|
||||||
@ -82,33 +68,38 @@ final class ChillWopi implements WopiInterface
|
|||||||
->withHeader('Content-Type', 'application/json')
|
->withHeader('Content-Type', 'application/json')
|
||||||
->withBody($this->psr17->createStream((string) json_encode(
|
->withBody($this->psr17->createStream((string) json_encode(
|
||||||
[
|
[
|
||||||
'BaseFileName' => $storedObject->getFilename(),
|
'BaseFileName' => $this->documentManager->getBasename($document),
|
||||||
'OwnerId' => uniqid(),
|
'OwnerId' => 'Symfony',
|
||||||
'Size' => 0,
|
'Size' => $this->documentManager->getSize($document),
|
||||||
'UserId' => uniqid(),
|
'UserId' => $userIdentifier,
|
||||||
// 'Version' => 'v' . uniqid(),
|
|
||||||
'ReadOnly' => false,
|
'ReadOnly' => false,
|
||||||
|
'UserCanAttend' => true,
|
||||||
|
'UserCanPresent' => true,
|
||||||
|
'UserCanRename' => true,
|
||||||
'UserCanWrite' => true,
|
'UserCanWrite' => true,
|
||||||
'UserCanNotWriteRelative' => true,
|
'UserCanNotWriteRelative' => false,
|
||||||
'SupportsLocks' => false,
|
'SupportsUserInfo' => true,
|
||||||
'UserFriendlyName' => sprintf('User %s', $user->getUsername()),
|
'SupportsDeleteFile' => true,
|
||||||
'UserExtraInfo' => [],
|
'SupportsLocks' => true,
|
||||||
'LastModifiedTime' => date('Y-m-d\TH:i:s.u\Z', $storedObject->getCreationDate()->getTimestamp()),
|
'SupportsGetLock' => true,
|
||||||
'CloseButtonClosesWindow' => true,
|
'SupportsExtendedLockLength' => true,
|
||||||
'EnableInsertRemoteImage' => true,
|
'UserFriendlyName' => $userIdentifier,
|
||||||
'EnableShare' => false,
|
|
||||||
'SupportsUpdate' => true,
|
'SupportsUpdate' => true,
|
||||||
'SupportsRename' => false,
|
'SupportsRename' => true,
|
||||||
'DisablePrint' => false,
|
'DisablePrint' => false,
|
||||||
'DisableExport' => false,
|
'AllowExternalMarketplace' => true,
|
||||||
'DisableCopy' => false,
|
'SupportedShareUrlTypes' => [
|
||||||
|
'ReadOnly',
|
||||||
|
],
|
||||||
|
'SHA256' => $this->documentManager->getSha256($document),
|
||||||
|
'UserInfo' => (string) $this->cache->getItem($userCacheKey)->get(),
|
||||||
]
|
]
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
public function deleteFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->deleteFile($fileId, $accessToken, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enumerateAncestors(
|
public function enumerateAncestors(
|
||||||
@ -116,57 +107,17 @@ final class ChillWopi implements WopiInterface
|
|||||||
?string $accessToken,
|
?string $accessToken,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->enumerateAncestors($fileId, $accessToken, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
public function getFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
try {
|
return $this->wopi->getFile($fileId, $accessToken, $request);
|
||||||
$user = $this->userProvider->loadUserByUsername($accessToken);
|
|
||||||
} catch (UsernameNotFoundException $e) {
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
$storedObject = $this->storedObjectRepository->findOneBy(['filename' => $fileId]);
|
|
||||||
|
|
||||||
if (null === $storedObject) {
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add strict typing in champs-libres/async-uploader-bundle
|
|
||||||
/** @var StdClass $object */
|
|
||||||
$object = $this->tempUrlGenerator->generate('GET', $storedObject->getFilename());
|
|
||||||
|
|
||||||
$response = $this->httpClient->sendRequest($this->psr17->createRequest('GET', $object->url));
|
|
||||||
|
|
||||||
if (200 !== $response->getStatusCode())
|
|
||||||
{
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse()
|
|
||||||
->withHeader(
|
|
||||||
'Content-Type',
|
|
||||||
'application/octet-stream',
|
|
||||||
)
|
|
||||||
->withHeader(
|
|
||||||
'Content-Disposition',
|
|
||||||
sprintf('attachment; filename=%s', $storedObject->getFilename())
|
|
||||||
)
|
|
||||||
->withBody($response->getBody());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLock(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
public function getLock(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->getLock($fileId, $accessToken, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShareUrl(
|
public function getShareUrl(
|
||||||
@ -174,7 +125,7 @@ final class ChillWopi implements WopiInterface
|
|||||||
?string $accessToken,
|
?string $accessToken,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->getShareUrl($fileId, $accessToken, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function lock(
|
public function lock(
|
||||||
@ -183,7 +134,7 @@ final class ChillWopi implements WopiInterface
|
|||||||
string $xWopiLock,
|
string $xWopiLock,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->lock($fileId, $accessToken, $xWopiLock, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function putFile(
|
public function putFile(
|
||||||
@ -193,49 +144,17 @@ final class ChillWopi implements WopiInterface
|
|||||||
string $xWopiEditors,
|
string $xWopiEditors,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
try {
|
return $this->wopi->putFile($fileId, $accessToken, $xWopiLock, $xWopiEditors, $request);
|
||||||
$user = $this->userProvider->loadUserByUsername($accessToken);
|
|
||||||
} catch (UsernameNotFoundException $e) {
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
$storedObject = $this->storedObjectRepository->findOneBy(['filename' => $fileId]);
|
|
||||||
|
|
||||||
if (null === $storedObject) {
|
|
||||||
throw new Exception(sprintf('Unable to find object named %s', $fileId));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add strict typing in champs-libres/async-uploader-bundle
|
|
||||||
/** @var StdClass $object */
|
|
||||||
$object = $this->tempUrlGenerator->generate('PUT', $storedObject->getFilename());
|
|
||||||
|
|
||||||
$response = $this->httpClient->sendRequest($this->psr17->createRequest('PUT', $object->url)->withBody($request->getBody()));
|
|
||||||
|
|
||||||
if (201 !== $response->getStatusCode())
|
|
||||||
{
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse()
|
|
||||||
->withHeader('Content-Type', 'application/json')
|
|
||||||
->withAddedHeader('X-WOPI-Lock', $xWopiLock)
|
|
||||||
->withBody($this->psr17->createStream((string) json_encode([])));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function putRelativeFile(string $fileId, string $accessToken, ?string $suggestedTarget, ?string $relativeTarget, bool $overwriteRelativeTarget, int $size, RequestInterface $request): ResponseInterface
|
public function putRelativeFile(string $fileId, string $accessToken, ?string $suggestedTarget, ?string $relativeTarget, bool $overwriteRelativeTarget, int $size, RequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->putRelativeFile($fileId, $accessToken, $suggestedTarget, $relativeTarget, $overwriteRelativeTarget, $size, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function putUserInfo(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
public function putUserInfo(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->putUserInfo($fileId, $accessToken, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refreshLock(
|
public function refreshLock(
|
||||||
@ -244,7 +163,7 @@ final class ChillWopi implements WopiInterface
|
|||||||
string $xWopiLock,
|
string $xWopiLock,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->refreshLock($fileId, $accessToken, $xWopiLock, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renameFile(
|
public function renameFile(
|
||||||
@ -254,7 +173,7 @@ final class ChillWopi implements WopiInterface
|
|||||||
string $xWopiRequestedName,
|
string $xWopiRequestedName,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->renameFile($fileId, $accessToken, $xWopiLock, $xWopiRequestedName, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unlock(
|
public function unlock(
|
||||||
@ -263,7 +182,7 @@ final class ChillWopi implements WopiInterface
|
|||||||
string $xWopiLock,
|
string $xWopiLock,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->unlock($fileId, $accessToken, $xWopiLock, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unlockAndRelock(
|
public function unlockAndRelock(
|
||||||
@ -273,33 +192,6 @@ final class ChillWopi implements WopiInterface
|
|||||||
string $xWopiOldLock,
|
string $xWopiOldLock,
|
||||||
RequestInterface $request
|
RequestInterface $request
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
return $this->getDebugResponse(__FUNCTION__, $request);
|
return $this->wopi->unlockAndRelock($fileId, $accessToken, $xWopiLock, $xWopiOldLock, $request);
|
||||||
}
|
|
||||||
|
|
||||||
private function getDebugResponse(string $method, RequestInterface $request): ResponseInterface
|
|
||||||
{
|
|
||||||
$params = [];
|
|
||||||
parse_str($request->getUri()->getQuery(), $params);
|
|
||||||
|
|
||||||
$data = (string) json_encode(array_merge(
|
|
||||||
['method' => $method],
|
|
||||||
$params,
|
|
||||||
$request->getHeaders()
|
|
||||||
));
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->psr17
|
|
||||||
->createResponse()
|
|
||||||
->withHeader('content', 'application/json')
|
|
||||||
->withBody($this->psr17->createStream($data));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getLockFilepath(string $fileId): string
|
|
||||||
{
|
|
||||||
return sprintf(
|
|
||||||
'%s/%s.lock',
|
|
||||||
$this->filesRepository,
|
|
||||||
$fileId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user