mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-13 05:44:24 +00:00
Merge branch 'signature-app/object-version' into 'signature-app-master'
Add versioning to stored objects See merge request Chill-Projet/chill-bundles!710
This commit is contained in:
commit
b2042bd1e4
@ -122,7 +122,7 @@ unit_tests:
|
|||||||
- php tests/console chill:db:sync-views --env=test
|
- php tests/console chill:db:sync-views --env=test
|
||||||
- php -d memory_limit=2G tests/console cache:clear --env=test
|
- php -d memory_limit=2G tests/console cache:clear --env=test
|
||||||
- php -d memory_limit=3G tests/console doctrine:fixtures:load -n --env=test
|
- php -d memory_limit=3G tests/console doctrine:fixtures:load -n --env=test
|
||||||
- php -d memory_limit=4G bin/phpunit --colors=never --exclude-group dbIntensive
|
- php -d memory_limit=4G bin/phpunit --colors=never --exclude-group dbIntensive --exclude-group openstack-integration
|
||||||
artifacts:
|
artifacts:
|
||||||
expire_in: 1 day
|
expire_in: 1 day
|
||||||
paths:
|
paths:
|
||||||
|
125
docs/source/installation/enable-collabora-for-dev.rst
Normal file
125
docs/source/installation/enable-collabora-for-dev.rst
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
|
||||||
|
Enable CODE for development
|
||||||
|
===========================
|
||||||
|
|
||||||
|
For editing a document, there must be a way to communicate between the collabora server and the symfony server, in
|
||||||
|
both direction. The domain name should also be the same for collabora server and for the browser which access to the
|
||||||
|
online editor.
|
||||||
|
|
||||||
|
Using ngrok (or other http tunnel)
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
One can configure a tunnel server to expose your local install to the web, and access to your local server using the
|
||||||
|
tunnel url.
|
||||||
|
|
||||||
|
Start ngrok
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
This can be achieve using `ngrok <https://ngrok.com/>`_.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The configuration of ngrok is outside of the scope of this document. Refers to the ngrok's documentation.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# ensuring that your server is running through http and port 8000
|
||||||
|
ngrok http 8000
|
||||||
|
# then open the link given by the ngrok utility and you should reach your app
|
||||||
|
|
||||||
|
At this step, ensure that you can reach your local app using the ngrok url.
|
||||||
|
|
||||||
|
Configure Collabora
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The collabora server must be executed online and configure to access to your ngrok installation. Ensure that the aliasgroup
|
||||||
|
exists for your ngrok application (`See the CODE documentation: <https://sdk.collaboraonline.com/docs/installation/Configuration.html#multihost-configuration>`_).
|
||||||
|
|
||||||
|
Configure your app
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Set the :code:`EDITOR_SERVER` variable to point to your collabora server, this should be done in your :code:`.env.local` file.
|
||||||
|
|
||||||
|
At this point, everything must be fine. In case of errors, watch the log from your collabora server, use the `profiler <https://symfony.com/doc/current/profiler.html>`_
|
||||||
|
to debug the requests.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
In case of error while validating proof (you'll see those message in the collabora's logs), you can temporarily disable
|
||||||
|
the proof validation adding this code snippet in `config/services.yaml`:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
when@dev:
|
||||||
|
# add only in dev environment, to avoid security problems
|
||||||
|
services:
|
||||||
|
ChampsLibres\WopiLib\Contract\Service\ProofValidatorInterface:
|
||||||
|
# this class will always validate proof
|
||||||
|
alias: Chill\WopiBundle\Service\Wopi\NullProofValidator
|
||||||
|
|
||||||
|
With a local CODE image
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This configuration is not sure, and must be refined. The documentation does not seems to be entirely valid.
|
||||||
|
|
||||||
|
Use a local domain name and https for your app
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Use the proxy feature from embedded symfony server to run your app. `See the dedicated doc <https://symfony.com/doc/current/setup/symfony_server.html#local-domain-names>`
|
||||||
|
|
||||||
|
Configure also the `https certificate <https://symfony.com/doc/current/setup/symfony_server.html#enabling-tls>`_
|
||||||
|
|
||||||
|
In this example, your local domain name will be :code:`my-domain` and the url will be :code:`https://my-domain.wip`.
|
||||||
|
|
||||||
|
Ensure that the proxy is running.
|
||||||
|
|
||||||
|
Create a certificate database for collabora
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Collabora must validate your certificate generated by symfony console. For that, you need `to create a NSS database <https://sdk.collaboraonline.com/docs/installation/Configuration.html#validating-digital-signatures>`
|
||||||
|
and configure collabora to use it.
|
||||||
|
|
||||||
|
At first, export the certificate for symfony development. Use the graphical interface from your browser to get the
|
||||||
|
certificate as a PEM file.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# create your database in a custom directory
|
||||||
|
mkdir /path/to/your/directory
|
||||||
|
certutil -N -d /path/to/your/directory
|
||||||
|
cat /path/to/your/ca.crt | certutil -d . -A symfony -t -t C,P,C,u,w -a
|
||||||
|
|
||||||
|
Launch CODE properly configured
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
collabora:
|
||||||
|
image: collabora/code:latest
|
||||||
|
environment:
|
||||||
|
- SLEEPFORDEBUGGER=0
|
||||||
|
- DONT_GEN_SSL_CERT="True"
|
||||||
|
# add path to the database
|
||||||
|
- extra_params=--o:ssl.enable=false --o:ssl.termination=false --o:logging.level=7 -o:certificates.database_path=/etc/custom-certificates/nss-database
|
||||||
|
- username=admin
|
||||||
|
- password=admin
|
||||||
|
- dictionaries=en_US
|
||||||
|
- aliasgroup1=https://my-domain.wip
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:9980:9980"
|
||||||
|
volumes:
|
||||||
|
- "/path/to/your/directory/nss-database:/etc/custom-certificates/nss-database"
|
||||||
|
extra_hosts:
|
||||||
|
- "my-domain.wip:host-gateway"
|
||||||
|
|
||||||
|
Configure your app
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Into your :code:`.env.local` file:
|
||||||
|
|
||||||
|
.. code-block:: env
|
||||||
|
|
||||||
|
EDITOR_SERVER=http://${COLLABORA_HOST}:${COLLABORA_PORT}
|
||||||
|
|
||||||
|
At this step, you should be able to edit a document through collabora.
|
@ -54,7 +54,6 @@
|
|||||||
"masonry-layout": "^4.2.2",
|
"masonry-layout": "^4.2.2",
|
||||||
"mime": "^4.0.0",
|
"mime": "^4.0.0",
|
||||||
"pdfjs-dist": "^4.3.136",
|
"pdfjs-dist": "^4.3.136",
|
||||||
"swagger-ui": "^4.15.5",
|
|
||||||
"vis-network": "^9.1.0",
|
"vis-network": "^9.1.0",
|
||||||
"vue": "^3.2.37",
|
"vue": "^3.2.37",
|
||||||
"vue-i18n": "^9.1.6",
|
"vue-i18n": "^9.1.6",
|
||||||
|
@ -68,7 +68,7 @@ final class ActivityController extends AbstractController
|
|||||||
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
|
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
private readonly PaginatorFactory $paginatorFactory,
|
private readonly PaginatorFactory $paginatorFactory,
|
||||||
private readonly ChillSecurity $security
|
private readonly ChillSecurity $security,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,7 +28,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository,
|
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository,
|
||||||
protected ActivityReasonRepository $activityReasonRepository,
|
protected ActivityReasonRepository $activityReasonRepository,
|
||||||
protected TranslatableStringHelper $translatableStringHelper
|
protected TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -26,7 +26,7 @@ class ActivityUsersJobAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -26,7 +26,7 @@ class ActivityUsersScopeAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -26,7 +26,7 @@ class CreatorJobAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -26,7 +26,7 @@ class CreatorScopeAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ScopeRepository $scopeRepository,
|
private readonly ScopeRepository $scopeRepository,
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -42,7 +42,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
|
|||||||
/**
|
/**
|
||||||
* The action for this report.
|
* The action for this report.
|
||||||
*/
|
*/
|
||||||
protected string $action = 'sum'
|
protected string $action = 'sum',
|
||||||
) {
|
) {
|
||||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class ListActivityHelper
|
|||||||
private readonly TranslatorInterface $translator,
|
private readonly TranslatorInterface $translator,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
private readonly TranslatableStringExportLabelHelper $translatableStringLabelHelper,
|
private readonly TranslatableStringExportLabelHelper $translatableStringLabelHelper,
|
||||||
private readonly UserHelper $userHelper
|
private readonly UserHelper $userHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addSelect(QueryBuilder $qb): void
|
public function addSelect(QueryBuilder $qb): void
|
||||||
|
@ -25,7 +25,7 @@ final readonly class ActivityPresenceFilter implements FilterInterface
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
private TranslatorInterface $translator
|
private TranslatorInterface $translator,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function getTitle()
|
public function getTitle()
|
||||||
|
@ -26,7 +26,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected TranslatableStringHelperInterface $translatableStringHelper,
|
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
protected ActivityTypeRepositoryInterface $activityTypeRepository
|
protected ActivityTypeRepositoryInterface $activityTypeRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -29,7 +29,7 @@ class UsersJobFilter implements FilterInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
private readonly UserJobRepositoryInterface $userJobRepository
|
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -29,7 +29,7 @@ class UsersScopeFilter implements FilterInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -59,7 +59,7 @@ class ActivityType extends AbstractType
|
|||||||
protected TranslatableStringHelper $translatableStringHelper,
|
protected TranslatableStringHelper $translatableStringHelper,
|
||||||
protected array $timeChoices,
|
protected array $timeChoices,
|
||||||
protected SocialIssueRender $socialIssueRender,
|
protected SocialIssueRender $socialIssueRender,
|
||||||
protected SocialActionRender $socialActionRender
|
protected SocialActionRender $socialActionRender,
|
||||||
) {
|
) {
|
||||||
if (!$tokenStorage->getToken()->getUser() instanceof User) {
|
if (!$tokenStorage->getToken()->getUser() instanceof User) {
|
||||||
throw new \RuntimeException('you should have a valid user');
|
throw new \RuntimeException('you should have a valid user');
|
||||||
|
@ -27,7 +27,7 @@ class PickActivityReasonType extends AbstractType
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ActivityReasonRepository $activityReasonRepository,
|
private readonly ActivityReasonRepository $activityReasonRepository,
|
||||||
private readonly ActivityReasonRender $reasonRender,
|
private readonly ActivityReasonRender $reasonRender,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
@ -32,7 +32,7 @@ final readonly class ActivityDocumentACLAwareRepository implements ActivityDocum
|
|||||||
private EntityManagerInterface $em,
|
private EntityManagerInterface $em,
|
||||||
private CenterResolverManagerInterface $centerResolverManager,
|
private CenterResolverManagerInterface $centerResolverManager,
|
||||||
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser,
|
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser,
|
||||||
private Security $security
|
private Security $security,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function buildFetchQueryActivityDocumentLinkedToPersonFromPersonContext(Person $person, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface
|
public function buildFetchQueryActivityDocumentLinkedToPersonFromPersonContext(Person $person, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface
|
||||||
|
@ -25,7 +25,7 @@ class ActivityReasonRepository extends ServiceEntityRepository
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ManagerRegistry $registry,
|
ManagerRegistry $registry,
|
||||||
private readonly RequestStack $requestStack
|
private readonly RequestStack $requestStack,
|
||||||
) {
|
) {
|
||||||
parent::__construct($registry, ActivityReason::class);
|
parent::__construct($registry, ActivityReason::class);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class ActivityStoredObjectVoter extends AbstractStoredObjectVoter
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ActivityRepository $repository,
|
private readonly ActivityRepository $repository,
|
||||||
Security $security,
|
Security $security,
|
||||||
WorkflowStoredObjectPermissionHelper $workflowDocumentService
|
WorkflowStoredObjectPermissionHelper $workflowDocumentService,
|
||||||
) {
|
) {
|
||||||
parent::__construct($security, $workflowDocumentService);
|
parent::__construct($security, $workflowDocumentService);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected Security $security,
|
protected Security $security,
|
||||||
VoterHelperFactoryInterface $voterHelperFactory
|
VoterHelperFactoryInterface $voterHelperFactory,
|
||||||
) {
|
) {
|
||||||
$this->voterHelper = $voterHelperFactory->generate(self::class)
|
$this->voterHelper = $voterHelperFactory->generate(self::class)
|
||||||
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
@ -50,7 +50,7 @@ class ActivityContext implements
|
|||||||
private readonly TranslatorInterface $translator,
|
private readonly TranslatorInterface $translator,
|
||||||
private readonly BaseContextData $baseContextData,
|
private readonly BaseContextData $baseContextData,
|
||||||
private readonly ThirdPartyRender $thirdPartyRender,
|
private readonly ThirdPartyRender $thirdPartyRender,
|
||||||
private readonly ThirdPartyRepository $thirdPartyRepository
|
private readonly ThirdPartyRepository $thirdPartyRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function adminFormReverseTransform(array $data): array
|
public function adminFormReverseTransform(array $data): array
|
||||||
|
@ -56,7 +56,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
|
|||||||
private readonly SocialIssueRepository $socialIssueRepository,
|
private readonly SocialIssueRepository $socialIssueRepository,
|
||||||
private readonly ThirdPartyRepository $thirdPartyRepository,
|
private readonly ThirdPartyRepository $thirdPartyRepository,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
private readonly UserRepository $userRepository
|
private readonly UserRepository $userRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function adminFormReverseTransform(array $data): array
|
public function adminFormReverseTransform(array $data): array
|
||||||
|
@ -76,7 +76,7 @@ final class TranslatableActivityReasonTest extends TypeTestCase
|
|||||||
*/
|
*/
|
||||||
protected function getTranslatableStringHelper(
|
protected function getTranslatableStringHelper(
|
||||||
$locale = 'en',
|
$locale = 'en',
|
||||||
$fallbackLocale = 'en'
|
$fallbackLocale = 'en',
|
||||||
) {
|
) {
|
||||||
$prophet = new \Prophecy\Prophet();
|
$prophet = new \Prophecy\Prophet();
|
||||||
$requestStack = $prophet->prophesize();
|
$requestStack = $prophet->prophesize();
|
||||||
|
@ -138,7 +138,7 @@ final class ActivityVoterTest extends KernelTestCase
|
|||||||
Scope $scope,
|
Scope $scope,
|
||||||
Center $center,
|
Center $center,
|
||||||
$attribute,
|
$attribute,
|
||||||
$message
|
$message,
|
||||||
) {
|
) {
|
||||||
$token = $this->prepareToken($user);
|
$token = $this->prepareToken($user);
|
||||||
$activity = $this->prepareActivity($scope, $this->preparePerson($center));
|
$activity = $this->prepareActivity($scope, $this->preparePerson($center));
|
||||||
|
@ -32,7 +32,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
|||||||
protected EntityManagerInterface $em,
|
protected EntityManagerInterface $em,
|
||||||
protected AuthorizationHelperInterface $helper,
|
protected AuthorizationHelperInterface $helper,
|
||||||
TokenStorageInterface $storage,
|
TokenStorageInterface $storage,
|
||||||
protected ActivityACLAwareRepository $aclAwareRepository
|
protected ActivityACLAwareRepository $aclAwareRepository,
|
||||||
) {
|
) {
|
||||||
if (!$storage->getToken()->getUser() instanceof User) {
|
if (!$storage->getToken()->getUser() instanceof User) {
|
||||||
throw new \RuntimeException('A user should be authenticated !');
|
throw new \RuntimeException('A user should be authenticated !');
|
||||||
|
@ -25,7 +25,7 @@ final class AsideActivityController extends CRUDController
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly AsideActivityCategoryRepository $categoryRepository,
|
private readonly AsideActivityCategoryRepository $categoryRepository,
|
||||||
private readonly Security $security
|
private readonly Security $security,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function createEntity(string $action, Request $request): object
|
public function createEntity(string $action, Request $request): object
|
||||||
@ -76,7 +76,7 @@ final class AsideActivityController extends CRUDController
|
|||||||
string $action,
|
string $action,
|
||||||
$query,
|
$query,
|
||||||
Request $request,
|
Request $request,
|
||||||
PaginatorInterface $paginator
|
PaginatorInterface $paginator,
|
||||||
) {
|
) {
|
||||||
if ('index' === $action) {
|
if ('index' === $action) {
|
||||||
return $query->orderBy('e.date', 'DESC');
|
return $query->orderBy('e.date', 'DESC');
|
||||||
|
@ -22,7 +22,7 @@ class ByActivityTypeAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly AsideActivityCategoryRepository $asideActivityCategoryRepository,
|
private readonly AsideActivityCategoryRepository $asideActivityCategoryRepository,
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -26,7 +26,7 @@ class ByUserJobAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -26,7 +26,7 @@ class ByUserScopeAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -41,7 +41,7 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn
|
|||||||
private AsideActivityCategoryRepository $asideActivityCategoryRepository,
|
private AsideActivityCategoryRepository $asideActivityCategoryRepository,
|
||||||
private CategoryRender $categoryRender,
|
private CategoryRender $categoryRender,
|
||||||
private LocationRepository $locationRepository,
|
private LocationRepository $locationRepository,
|
||||||
private TranslatableStringHelperInterface $translatableStringHelper
|
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder) {}
|
public function buildForm(FormBuilderInterface $builder) {}
|
||||||
|
@ -27,7 +27,7 @@ class ByActivityTypeFilter implements FilterInterface
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly CategoryRender $categoryRender,
|
private readonly CategoryRender $categoryRender,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
private readonly AsideActivityCategoryRepository $asideActivityTypeRepository
|
private readonly AsideActivityCategoryRepository $asideActivityTypeRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -24,7 +24,7 @@ use Symfony\Component\Security\Core\Security;
|
|||||||
final readonly class ByLocationFilter implements FilterInterface
|
final readonly class ByLocationFilter implements FilterInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Security $security
|
private Security $security,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function getTitle(): string
|
public function getTitle(): string
|
||||||
|
@ -29,7 +29,7 @@ class ByUserJobFilter implements FilterInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
private readonly UserJobRepositoryInterface $userJobRepository
|
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -29,7 +29,7 @@ class ByUserScopeFilter implements FilterInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -44,7 +44,7 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
CountNotificationTask $counter,
|
CountNotificationTask $counter,
|
||||||
TokenStorageInterface $tokenStorage,
|
TokenStorageInterface $tokenStorage,
|
||||||
TranslatorInterface $translator,
|
TranslatorInterface $translator,
|
||||||
AuthorizationCheckerInterface $authorizationChecker
|
AuthorizationCheckerInterface $authorizationChecker,
|
||||||
) {
|
) {
|
||||||
$this->counter = $counter;
|
$this->counter = $counter;
|
||||||
$this->tokenStorage = $tokenStorage;
|
$this->tokenStorage = $tokenStorage;
|
||||||
|
@ -26,7 +26,7 @@ class AsideActivityVoter extends AbstractChillVoter implements ProvideRoleHierar
|
|||||||
private readonly VoterHelperInterface $voterHelper;
|
private readonly VoterHelperInterface $voterHelper;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
VoterHelperFactoryInterface $voterHelperFactory
|
VoterHelperFactoryInterface $voterHelperFactory,
|
||||||
) {
|
) {
|
||||||
$this->voterHelper = $voterHelperFactory
|
$this->voterHelper = $voterHelperFactory
|
||||||
->generate(self::class)
|
->generate(self::class)
|
||||||
|
@ -47,7 +47,7 @@ class SendTestShortMessageOnCalendarCommand extends Command
|
|||||||
private readonly PhoneNumberHelperInterface $phoneNumberHelper,
|
private readonly PhoneNumberHelperInterface $phoneNumberHelper,
|
||||||
private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
|
private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
|
||||||
private readonly ShortMessageTransporterInterface $transporter,
|
private readonly ShortMessageTransporterInterface $transporter,
|
||||||
private readonly UserRepositoryInterface $userRepository
|
private readonly UserRepositoryInterface $userRepository,
|
||||||
) {
|
) {
|
||||||
parent::__construct('chill:calendar:test-send-short-message');
|
parent::__construct('chill:calendar:test-send-short-message');
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ class CalendarController extends AbstractController
|
|||||||
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
|
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
|
||||||
private readonly UserRepositoryInterface $userRepository,
|
private readonly UserRepositoryInterface $userRepository,
|
||||||
private readonly TranslatorInterface $translator,
|
private readonly TranslatorInterface $translator,
|
||||||
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
|
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +47,7 @@ class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
Calendar $calendar,
|
Calendar $calendar,
|
||||||
#[ORM\ManyToOne(targetEntity: StoredObject::class, cascade: ['persist'])]
|
#[ORM\ManyToOne(targetEntity: StoredObject::class, cascade: ['persist'])]
|
||||||
#[ORM\JoinColumn(nullable: false)]
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
private ?StoredObject $storedObject
|
private ?StoredObject $storedObject,
|
||||||
) {
|
) {
|
||||||
$this->setCalendar($calendar);
|
$this->setCalendar($calendar);
|
||||||
$this->datetimeVersion = $calendar->getDateTimeVersion();
|
$this->datetimeVersion = $calendar->getDateTimeVersion();
|
||||||
|
@ -26,7 +26,7 @@ final readonly class JobAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private UserJobRepository $jobRepository,
|
private UserJobRepository $jobRepository,
|
||||||
private TranslatableStringHelper $translatableStringHelper
|
private TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -26,7 +26,7 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ScopeRepository $scopeRepository,
|
private ScopeRepository $scopeRepository,
|
||||||
private TranslatableStringHelper $translatableStringHelper
|
private TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -28,7 +28,7 @@ final readonly class JobFilter implements FilterInterface
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private TranslatableStringHelper $translatableStringHelper,
|
private TranslatableStringHelper $translatableStringHelper,
|
||||||
private UserJobRepositoryInterface $userJobRepository
|
private UserJobRepositoryInterface $userJobRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -30,7 +30,7 @@ class ScopeFilter implements FilterInterface
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
protected TranslatorInterface $translator,
|
protected TranslatorInterface $translator,
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
private readonly ScopeRepositoryInterface $scopeRepository
|
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
|
@ -37,7 +37,7 @@ class CalendarType extends AbstractType
|
|||||||
private readonly IdToUsersDataTransformer $idToUsersDataTransformer,
|
private readonly IdToUsersDataTransformer $idToUsersDataTransformer,
|
||||||
private readonly IdToLocationDataTransformer $idToLocationDataTransformer,
|
private readonly IdToLocationDataTransformer $idToLocationDataTransformer,
|
||||||
private readonly ThirdPartiesToIdDataTransformer $partiesToIdDataTransformer,
|
private readonly ThirdPartiesToIdDataTransformer $partiesToIdDataTransformer,
|
||||||
private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer
|
private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
@ -46,7 +46,7 @@ class CalendarMessage
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
Calendar $calendar,
|
Calendar $calendar,
|
||||||
private readonly string $action,
|
private readonly string $action,
|
||||||
User $byUser
|
User $byUser,
|
||||||
) {
|
) {
|
||||||
$this->calendarId = $calendar->getId();
|
$this->calendarId = $calendar->getId();
|
||||||
$this->byUserId = $byUser->getId();
|
$this->byUserId = $byUser->getId();
|
||||||
|
@ -59,7 +59,7 @@ final readonly class MSUserAbsenceReader implements MSUserAbsenceReaderInterface
|
|||||||
'alwaysEnabled' => true,
|
'alwaysEnabled' => true,
|
||||||
'scheduled' => RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledStartDateTime']['dateTime']) < $this->clock->now()
|
'scheduled' => RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledStartDateTime']['dateTime']) < $this->clock->now()
|
||||||
&& RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledEndDateTime']['dateTime']) > $this->clock->now(),
|
&& RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledEndDateTime']['dateTime']) > $this->clock->now(),
|
||||||
default => throw new UserAbsenceSyncException('this status is not documented by Microsoft')
|
default => throw new UserAbsenceSyncException('this status is not documented by Microsoft'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ class MapCalendarToUser
|
|||||||
User $user,
|
User $user,
|
||||||
int $expiration,
|
int $expiration,
|
||||||
?string $id = null,
|
?string $id = null,
|
||||||
?string $secret = null
|
?string $secret = null,
|
||||||
): void {
|
): void {
|
||||||
$user->setAttributeByDomain(self::METADATA_KEY, self::EXPIRATION_SUBSCRIPTION_EVENT, $expiration);
|
$user->setAttributeByDomain(self::METADATA_KEY, self::EXPIRATION_SUBSCRIPTION_EVENT, $expiration);
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ class RemoteEventConverter
|
|||||||
private readonly LocationConverter $locationConverter,
|
private readonly LocationConverter $locationConverter,
|
||||||
private readonly LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
private readonly PersonRenderInterface $personRender,
|
private readonly PersonRenderInterface $personRender,
|
||||||
private readonly TranslatorInterface $translator
|
private readonly TranslatorInterface $translator,
|
||||||
) {
|
) {
|
||||||
$this->defaultDateTimeZone = (new \DateTimeImmutable())->getTimezone();
|
$this->defaultDateTimeZone = (new \DateTimeImmutable())->getTimezone();
|
||||||
$this->remoteDateTimeZone = self::getRemoteTimeZone();
|
$this->remoteDateTimeZone = self::getRemoteTimeZone();
|
||||||
|
@ -351,7 +351,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
|||||||
[
|
[
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
'lastModifiedDateTime' => $lastModified,
|
'lastModifiedDateTime' => $lastModified,
|
||||||
'changeKey' => $changeKey
|
'changeKey' => $changeKey,
|
||||||
] = $this->createOnRemote($eventData, $calendar->getMainUser(), 'calendar_'.$calendar->getId());
|
] = $this->createOnRemote($eventData, $calendar->getMainUser(), 'calendar_'.$calendar->getId());
|
||||||
|
|
||||||
if (null === $id) {
|
if (null === $id) {
|
||||||
@ -427,7 +427,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
|||||||
[
|
[
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
'lastModifiedDateTime' => $lastModified,
|
'lastModifiedDateTime' => $lastModified,
|
||||||
'changeKey' => $changeKey
|
'changeKey' => $changeKey,
|
||||||
] = $this->createOnRemote(
|
] = $this->createOnRemote(
|
||||||
$eventData,
|
$eventData,
|
||||||
$calendarRange->getUser(),
|
$calendarRange->getUser(),
|
||||||
@ -564,7 +564,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
|||||||
[
|
[
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
'lastModifiedDateTime' => $lastModified,
|
'lastModifiedDateTime' => $lastModified,
|
||||||
'changeKey' => $changeKey
|
'changeKey' => $changeKey,
|
||||||
] = $this->patchOnRemote(
|
] = $this->patchOnRemote(
|
||||||
$calendar->getRemoteId(),
|
$calendar->getRemoteId(),
|
||||||
$eventData,
|
$eventData,
|
||||||
|
@ -33,6 +33,6 @@ class RemoteEvent
|
|||||||
#[Serializer\Groups(['read'])]
|
#[Serializer\Groups(['read'])]
|
||||||
public \DateTimeImmutable $endDate,
|
public \DateTimeImmutable $endDate,
|
||||||
#[Serializer\Groups(['read'])]
|
#[Serializer\Groups(['read'])]
|
||||||
public bool $isAllDay = false
|
public bool $isAllDay = false,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ class CalendarRangeRepository implements ObjectRepository
|
|||||||
\DateTimeImmutable $from,
|
\DateTimeImmutable $from,
|
||||||
\DateTimeImmutable $to,
|
\DateTimeImmutable $to,
|
||||||
?int $limit = null,
|
?int $limit = null,
|
||||||
?int $offset = null
|
?int $offset = null,
|
||||||
): array {
|
): array {
|
||||||
$qb = $this->buildQueryAvailableRangesForUser($user, $from, $to);
|
$qb = $this->buildQueryAvailableRangesForUser($user, $from, $to);
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ final readonly class CalendarContext implements CalendarContextInterface
|
|||||||
private PersonRepository $personRepository,
|
private PersonRepository $personRepository,
|
||||||
private ThirdPartyRender $thirdPartyRender,
|
private ThirdPartyRender $thirdPartyRender,
|
||||||
private ThirdPartyRepository $thirdPartyRepository,
|
private ThirdPartyRepository $thirdPartyRepository,
|
||||||
private TranslatableStringHelperInterface $translatableStringHelper
|
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function adminFormReverseTransform(array $data): array
|
public function adminFormReverseTransform(array $data): array
|
||||||
|
@ -37,7 +37,7 @@ final readonly class AccompanyingPeriodCalendarGenericDocProvider implements Gen
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Security $security,
|
private Security $security,
|
||||||
private EntityManagerInterface $em
|
private EntityManagerInterface $em,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,7 +36,7 @@ final readonly class PersonCalendarGenericDocProvider implements GenericDocForPe
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Security $security,
|
private Security $security,
|
||||||
private EntityManagerInterface $em
|
private EntityManagerInterface $em,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private function addWhereClausesToQuery(FetchQuery $query, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
private function addWhereClausesToQuery(FetchQuery $query, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
||||||
|
@ -156,7 +156,7 @@ final class CalendarTypeTest extends TypeTestCase
|
|||||||
|
|
||||||
private function buildMultiToIdDataTransformer(
|
private function buildMultiToIdDataTransformer(
|
||||||
string $classTransformer,
|
string $classTransformer,
|
||||||
string $objClass
|
string $objClass,
|
||||||
) {
|
) {
|
||||||
$transformer = $this->prophesize($classTransformer);
|
$transformer = $this->prophesize($classTransformer);
|
||||||
$transformer->transform(Argument::type('array'))
|
$transformer->transform(Argument::type('array'))
|
||||||
@ -195,7 +195,7 @@ final class CalendarTypeTest extends TypeTestCase
|
|||||||
|
|
||||||
private function buildSingleToIdDataTransformer(
|
private function buildSingleToIdDataTransformer(
|
||||||
string $classTransformer,
|
string $classTransformer,
|
||||||
string $class
|
string $class,
|
||||||
) {
|
) {
|
||||||
$transformer = $this->prophesize($classTransformer);
|
$transformer = $this->prophesize($classTransformer);
|
||||||
$transformer->transform(Argument::type('object'))
|
$transformer->transform(Argument::type('object'))
|
||||||
|
@ -203,7 +203,7 @@ final class CalendarContextTest extends TestCase
|
|||||||
|
|
||||||
private function buildCalendarContext(
|
private function buildCalendarContext(
|
||||||
?EntityManagerInterface $entityManager = null,
|
?EntityManagerInterface $entityManager = null,
|
||||||
?NormalizerInterface $normalizer = null
|
?NormalizerInterface $normalizer = null,
|
||||||
): CalendarContext {
|
): CalendarContext {
|
||||||
$baseContext = $this->prophesize(BaseContextData::class);
|
$baseContext = $this->prophesize(BaseContextData::class);
|
||||||
$baseContext->getData(null)->willReturn(['base_context' => 'data']);
|
$baseContext->getData(null)->willReturn(['base_context' => 'data']);
|
||||||
|
@ -44,7 +44,7 @@ class CreateFieldsOnGroupCommand extends Command
|
|||||||
private readonly EntityManager $entityManager,
|
private readonly EntityManager $entityManager,
|
||||||
private readonly ValidatorInterface $validator,
|
private readonly ValidatorInterface $validator,
|
||||||
private $availableLanguages,
|
private $availableLanguages,
|
||||||
private $customizablesEntities
|
private $customizablesEntities,
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class CustomFieldsGroupController extends AbstractController
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly CustomFieldProvider $customFieldProvider,
|
private readonly CustomFieldProvider $customFieldProvider,
|
||||||
private readonly TranslatorInterface $translator,
|
private readonly TranslatorInterface $translator,
|
||||||
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
|
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,7 +42,7 @@ class CustomFieldChoice extends AbstractCustomField
|
|||||||
/**
|
/**
|
||||||
* @var TranslatableStringHelper Helper that find the string in current locale from an array of translation
|
* @var TranslatableStringHelper Helper that find the string in current locale from an array of translation
|
||||||
*/
|
*/
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function allowOtherChoice(CustomField $cf)
|
public function allowOtherChoice(CustomField $cf)
|
||||||
|
@ -44,7 +44,7 @@ class CustomFieldDate extends AbstractCustomField
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Environment $templating,
|
private readonly Environment $templating,
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
||||||
|
@ -41,7 +41,7 @@ class CustomFieldNumber extends AbstractCustomField
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Environment $templating,
|
private readonly Environment $templating,
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
||||||
|
@ -28,7 +28,7 @@ class CustomFieldText extends AbstractCustomField
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Environment $templating,
|
private readonly Environment $templating,
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,7 @@ class CustomFieldTitle extends AbstractCustomField
|
|||||||
/**
|
/**
|
||||||
* @var TranslatableStringHelper Helper that find the string in current locale from an array of translation
|
* @var TranslatableStringHelper Helper that find the string in current locale from an array of translation
|
||||||
*/
|
*/
|
||||||
private readonly TranslatableStringHelper $translatableStringHelper
|
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
||||||
|
@ -26,7 +26,7 @@ class CustomFieldsGroupType extends AbstractType
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly array $customizableEntities,
|
private readonly array $customizableEntities,
|
||||||
// TODO : add comment about this variable
|
// TODO : add comment about this variable
|
||||||
private readonly TranslatorInterface $translator
|
private readonly TranslatorInterface $translator,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
// TODO : details about the function
|
// TODO : details about the function
|
||||||
|
@ -48,7 +48,7 @@ final class DocGeneratorTemplateController extends AbstractController
|
|||||||
private readonly PaginatorFactory $paginatorFactory,
|
private readonly PaginatorFactory $paginatorFactory,
|
||||||
private readonly EntityManagerInterface $entityManager,
|
private readonly EntityManagerInterface $entityManager,
|
||||||
private readonly ClockInterface $clock,
|
private readonly ClockInterface $clock,
|
||||||
private readonly ChillSecurity $security
|
private readonly ChillSecurity $security,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[Route(path: '{_locale}/admin/doc/gen/generate/test/from/{template}/for/{entityClassName}/{entityId}', name: 'chill_docgenerator_test_generate_from_template')]
|
#[Route(path: '{_locale}/admin/doc/gen/generate/test/from/{template}/for/{entityClassName}/{entityId}', name: 'chill_docgenerator_test_generate_from_template')]
|
||||||
@ -56,7 +56,7 @@ final class DocGeneratorTemplateController extends AbstractController
|
|||||||
DocGeneratorTemplate $template,
|
DocGeneratorTemplate $template,
|
||||||
string $entityClassName,
|
string $entityClassName,
|
||||||
int $entityId,
|
int $entityId,
|
||||||
Request $request
|
Request $request,
|
||||||
): Response {
|
): Response {
|
||||||
return $this->generateDocFromTemplate(
|
return $this->generateDocFromTemplate(
|
||||||
$template,
|
$template,
|
||||||
@ -71,7 +71,7 @@ final class DocGeneratorTemplateController extends AbstractController
|
|||||||
DocGeneratorTemplate $template,
|
DocGeneratorTemplate $template,
|
||||||
string $entityClassName,
|
string $entityClassName,
|
||||||
int $entityId,
|
int $entityId,
|
||||||
Request $request
|
Request $request,
|
||||||
): Response {
|
): Response {
|
||||||
return $this->generateDocFromTemplate(
|
return $this->generateDocFromTemplate(
|
||||||
$template,
|
$template,
|
||||||
@ -137,7 +137,7 @@ final class DocGeneratorTemplateController extends AbstractController
|
|||||||
DocGeneratorTemplate $template,
|
DocGeneratorTemplate $template,
|
||||||
int $entityId,
|
int $entityId,
|
||||||
Request $request,
|
Request $request,
|
||||||
bool $isTest
|
bool $isTest,
|
||||||
): Response {
|
): Response {
|
||||||
try {
|
try {
|
||||||
$context = $this->contextManager->getContextByDocGeneratorTemplate($template);
|
$context = $this->contextManager->getContextByDocGeneratorTemplate($template);
|
||||||
|
@ -54,12 +54,15 @@ class LoadDocGeneratorTemplate extends AbstractFixture
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($templates as $template) {
|
foreach ($templates as $template) {
|
||||||
$newStoredObj = (new StoredObject())
|
$newStoredObj = (new StoredObject());
|
||||||
->setFilename($template['file']['filename'])
|
|
||||||
->setKeyInfos(json_decode($template['file']['key'], true))
|
$newStoredObj
|
||||||
->setIv(json_decode($template['file']['iv'], true))
|
|
||||||
->setCreatedAt(new \DateTime('today'))
|
->setCreatedAt(new \DateTime('today'))
|
||||||
->setType($template['file']['type']);
|
->registerVersion(
|
||||||
|
json_decode($template['file']['key'], true),
|
||||||
|
json_decode($template['file']['iv'], true),
|
||||||
|
$template['file']['type'],
|
||||||
|
);
|
||||||
|
|
||||||
$manager->persist($newStoredObj);
|
$manager->persist($newStoredObj);
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ final readonly class RelatorioDriver implements DriverInterface
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private HttpClientInterface $client,
|
private HttpClientInterface $client,
|
||||||
ParameterBagInterface $parameterBag,
|
ParameterBagInterface $parameterBag,
|
||||||
private LoggerInterface $logger
|
private LoggerInterface $logger,
|
||||||
) {
|
) {
|
||||||
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
|
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ClassMetadataFactoryInterface $classMetadataFactory,
|
private readonly ClassMetadataFactoryInterface $classMetadataFactory,
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {
|
) {
|
||||||
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
|
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class Generator implements GeneratorInterface
|
|||||||
private readonly DriverInterface $driver,
|
private readonly DriverInterface $driver,
|
||||||
private readonly ManagerRegistry $objectManagerRegistry,
|
private readonly ManagerRegistry $objectManagerRegistry,
|
||||||
private readonly LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
private readonly StoredObjectManagerInterface $storedObjectManager
|
private readonly StoredObjectManagerInterface $storedObjectManager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function generateDataDump(
|
public function generateDataDump(
|
||||||
@ -134,13 +134,11 @@ class Generator implements GeneratorInterface
|
|||||||
$content = Yaml::dump($data, 6);
|
$content = Yaml::dump($data, 6);
|
||||||
/* @var StoredObject $destinationStoredObject */
|
/* @var StoredObject $destinationStoredObject */
|
||||||
$destinationStoredObject
|
$destinationStoredObject
|
||||||
->setType('application/yaml')
|
|
||||||
->setFilename(sprintf('%s_yaml', uniqid('doc_', true)))
|
|
||||||
->setStatus(StoredObject::STATUS_READY)
|
->setStatus(StoredObject::STATUS_READY)
|
||||||
;
|
;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->storedObjectManager->write($destinationStoredObject, $content);
|
$this->storedObjectManager->write($destinationStoredObject, $content, 'application/yaml');
|
||||||
} catch (StoredObjectManagerException $e) {
|
} catch (StoredObjectManagerException $e) {
|
||||||
$destinationStoredObject->addGenerationErrors($e->getMessage());
|
$destinationStoredObject->addGenerationErrors($e->getMessage());
|
||||||
|
|
||||||
@ -174,13 +172,11 @@ class Generator implements GeneratorInterface
|
|||||||
|
|
||||||
/* @var StoredObject $destinationStoredObject */
|
/* @var StoredObject $destinationStoredObject */
|
||||||
$destinationStoredObject
|
$destinationStoredObject
|
||||||
->setType($template->getFile()->getType())
|
|
||||||
->setFilename(sprintf('%s_odt', uniqid('doc_', true)))
|
|
||||||
->setStatus(StoredObject::STATUS_READY)
|
->setStatus(StoredObject::STATUS_READY)
|
||||||
;
|
;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->storedObjectManager->write($destinationStoredObject, $generatedResource);
|
$this->storedObjectManager->write($destinationStoredObject, $generatedResource, $template->getFile()->getType());
|
||||||
} catch (StoredObjectManagerException $e) {
|
} catch (StoredObjectManagerException $e) {
|
||||||
$destinationStoredObject->addGenerationErrors($e->getMessage());
|
$destinationStoredObject->addGenerationErrors($e->getMessage());
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ final readonly class OnGenerationFails implements EventSubscriberInterface
|
|||||||
private MailerInterface $mailer,
|
private MailerInterface $mailer,
|
||||||
private StoredObjectRepositoryInterface $storedObjectRepository,
|
private StoredObjectRepositoryInterface $storedObjectRepository,
|
||||||
private TranslatorInterface $translator,
|
private TranslatorInterface $translator,
|
||||||
private UserRepositoryInterface $userRepository
|
private UserRepositoryInterface $userRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function getSubscribedEvents()
|
public static function getSubscribedEvents()
|
||||||
|
@ -56,7 +56,7 @@ final class BaseContextDataTest extends KernelTestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function buildBaseContext(
|
private function buildBaseContext(
|
||||||
?NormalizerInterface $normalizer = null
|
?NormalizerInterface $normalizer = null,
|
||||||
): BaseContextData {
|
): BaseContextData {
|
||||||
return new BaseContextData(
|
return new BaseContextData(
|
||||||
$normalizer ?? self::getContainer()->get(NormalizerInterface::class)
|
$normalizer ?? self::getContainer()->get(NormalizerInterface::class)
|
||||||
|
@ -58,6 +58,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
|
|||||||
?int $expire_delay = null,
|
?int $expire_delay = null,
|
||||||
?int $submit_delay = null,
|
?int $submit_delay = null,
|
||||||
int $max_file_count = 1,
|
int $max_file_count = 1,
|
||||||
|
?string $object_name = null,
|
||||||
): SignedUrlPost {
|
): SignedUrlPost {
|
||||||
$delay = $expire_delay ?? $this->max_expire_delay;
|
$delay = $expire_delay ?? $this->max_expire_delay;
|
||||||
$submit_delay ??= $this->max_submit_delay;
|
$submit_delay ??= $this->max_submit_delay;
|
||||||
@ -84,7 +85,9 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
|
|||||||
|
|
||||||
$expires = $this->clock->now()->add(new \DateInterval('PT'.(string) $delay.'S'));
|
$expires = $this->clock->now()->add(new \DateInterval('PT'.(string) $delay.'S'));
|
||||||
|
|
||||||
$object_name = $this->generateObjectName();
|
if (null === $object_name) {
|
||||||
|
$object_name = $this->generateObjectName();
|
||||||
|
}
|
||||||
|
|
||||||
$g = new SignedUrlPost(
|
$g = new SignedUrlPost(
|
||||||
$url = $this->generateUrl($object_name),
|
$url = $this->generateUrl($object_name),
|
||||||
@ -141,7 +144,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
|
|||||||
{
|
{
|
||||||
return match (str_ends_with($this->base_url, '/')) {
|
return match (str_ends_with($this->base_url, '/')) {
|
||||||
true => $this->base_url.$relative_path,
|
true => $this->base_url.$relative_path,
|
||||||
false => $this->base_url.'/'.$relative_path
|
false => $this->base_url.'/'.$relative_path,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,21 +182,19 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
|
|||||||
return \hash_hmac('sha512', $body, $this->key, false);
|
return \hash_hmac('sha512', $body, $this->key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateSignature($method, $url, \DateTimeImmutable $expires)
|
private function generateSignature(string $method, $url, \DateTimeImmutable $expires)
|
||||||
{
|
{
|
||||||
if ('POST' === $method) {
|
if ('POST' === $method) {
|
||||||
return $this->generateSignaturePost($url, $expires);
|
return $this->generateSignaturePost($url, $expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = \parse_url((string) $url, PHP_URL_PATH);
|
$path = \parse_url((string) $url, PHP_URL_PATH);
|
||||||
|
|
||||||
$body = sprintf(
|
$body = sprintf(
|
||||||
"%s\n%s\n%s",
|
"%s\n%s\n%s",
|
||||||
$method,
|
strtoupper($method),
|
||||||
$expires->format('U'),
|
$expires->format('U'),
|
||||||
$path
|
$path
|
||||||
)
|
);
|
||||||
;
|
|
||||||
|
|
||||||
$this->logger->debug(
|
$this->logger->debug(
|
||||||
'generate signature GET',
|
'generate signature GET',
|
||||||
|
@ -16,7 +16,8 @@ interface TempUrlGeneratorInterface
|
|||||||
public function generatePost(
|
public function generatePost(
|
||||||
?int $expire_delay = null,
|
?int $expire_delay = null,
|
||||||
?int $submit_delay = null,
|
?int $submit_delay = null,
|
||||||
int $max_file_count = 1
|
int $max_file_count = 1,
|
||||||
|
?string $object_name = null,
|
||||||
): SignedUrlPost;
|
): SignedUrlPost;
|
||||||
|
|
||||||
public function generate(string $method, string $object_name, ?int $expire_delay = null): SignedUrl;
|
public function generate(string $method, string $object_name, ?int $expire_delay = null): SignedUrl;
|
||||||
|
@ -25,7 +25,7 @@ class AsyncUploadExtension extends AbstractExtension
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly TempUrlGeneratorInterface $tempUrlGenerator,
|
private readonly TempUrlGeneratorInterface $tempUrlGenerator,
|
||||||
private readonly UrlGeneratorInterface $routingUrlGenerator
|
private readonly UrlGeneratorInterface $routingUrlGenerator,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function getFilters()
|
public function getFilters()
|
||||||
|
@ -11,9 +11,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\DocStoreBundle\Controller;
|
namespace Chill\DocStoreBundle\Controller;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\AsyncUpload\Exception\TempUrlGeneratorException;
|
|
||||||
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
|
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
|
||||||
use Chill\DocStoreBundle\Security\Authorization\AsyncUploadVoter;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
@ -30,62 +32,84 @@ final readonly class AsyncUploadController
|
|||||||
private TempUrlGeneratorInterface $tempUrlGenerator,
|
private TempUrlGeneratorInterface $tempUrlGenerator,
|
||||||
private SerializerInterface $serializer,
|
private SerializerInterface $serializer,
|
||||||
private Security $security,
|
private Security $security,
|
||||||
private LoggerInterface $logger,
|
private LoggerInterface $chillLogger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[Route(path: '/asyncupload/temp_url/generate/{method}', name: 'async_upload.generate_url')]
|
#[Route(path: '/api/1.0/doc-store/async-upload/temp_url/{uuid}/generate/post', name: 'chill_docstore_asyncupload_getsignedurlpost')]
|
||||||
public function getSignedUrl(string $method, Request $request): JsonResponse
|
public function getSignedUrlPost(Request $request, StoredObject $storedObject): JsonResponse
|
||||||
{
|
{
|
||||||
try {
|
if (!$this->security->isGranted(StoredObjectRoleEnum::EDIT->value, $storedObject)) {
|
||||||
switch (strtolower($method)) {
|
throw new AccessDeniedHttpException('not able to edit the given stored object');
|
||||||
case 'post':
|
}
|
||||||
$p = $this->tempUrlGenerator
|
|
||||||
->generatePost(
|
|
||||||
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null,
|
|
||||||
$request->query->has('submit_delay') ? $request->query->getInt('submit_delay') : null
|
|
||||||
)
|
|
||||||
;
|
|
||||||
break;
|
|
||||||
case 'get':
|
|
||||||
case 'head':
|
|
||||||
$object_name = $request->query->get('object_name', null);
|
|
||||||
|
|
||||||
if (null === $object_name) {
|
// we create a dummy version, to generate a filename
|
||||||
return (new JsonResponse((object) [
|
$version = $storedObject->registerVersion();
|
||||||
'message' => 'the object_name is null',
|
|
||||||
]))
|
$p = $this->tempUrlGenerator
|
||||||
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
|
->generatePost(
|
||||||
}
|
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null,
|
||||||
$p = $this->tempUrlGenerator->generate(
|
$request->query->has('submit_delay') ? $request->query->getInt('submit_delay') : null,
|
||||||
$method,
|
object_name: $version->getFilename()
|
||||||
$object_name,
|
);
|
||||||
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null
|
|
||||||
);
|
$this->chillLogger->notice('[Privacy Event] a request to upload a document has been generated', [
|
||||||
break;
|
'doc_uuid' => $storedObject->getUuid(),
|
||||||
default:
|
]);
|
||||||
return (new JsonResponse((object) ['message' => 'the method '
|
|
||||||
."{$method} is not valid"]))
|
return new JsonResponse(
|
||||||
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
|
$this->serializer->serialize($p, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
||||||
|
Response::HTTP_OK,
|
||||||
|
[],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(path: '/api/1.0/doc-store/async-upload/temp_url/{uuid}/generate/{method}', name: 'chill_docstore_asyncupload_getsignedurlget', requirements: ['method' => 'get|head'])]
|
||||||
|
public function getSignedUrlGet(Request $request, StoredObject $storedObject, string $method): JsonResponse
|
||||||
|
{
|
||||||
|
if (!$this->security->isGranted(StoredObjectRoleEnum::SEE->value, $storedObject)) {
|
||||||
|
throw new AccessDeniedHttpException('not able to read the given stored object');
|
||||||
|
}
|
||||||
|
|
||||||
|
// we really want to be sure that there are no other method than get or head:
|
||||||
|
if (!in_array($method, ['get', 'head'], true)) {
|
||||||
|
throw new AccessDeniedHttpException('Only methods get and head are allowed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->query->has('version')) {
|
||||||
|
$filename = $request->query->get('version');
|
||||||
|
|
||||||
|
$storedObjectVersion = $storedObject->getVersions()->findFirst(fn (int $index, StoredObjectVersion $version): bool => $version->getFilename() === $filename);
|
||||||
|
|
||||||
|
if (null === $storedObjectVersion) {
|
||||||
|
// we are here in the case where the version is not stored into the database
|
||||||
|
// as the version is prefixed by the stored object prefix, we just have to check that this prefix
|
||||||
|
// is the same. It means that the user had previously the permission to "SEE_AND_EDIT" this stored
|
||||||
|
// object with same prefix that we checked before
|
||||||
|
if (!str_starts_with($filename, $storedObject->getPrefix())) {
|
||||||
|
throw new AccessDeniedHttpException('not able to match the version with the same filename');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (TempUrlGeneratorException $e) {
|
} else {
|
||||||
$this->logger->warning('The client requested a temp url'
|
$filename = $storedObject->getCurrentVersion()->getFilename();
|
||||||
.' which sparkle an error.', [
|
|
||||||
'message' => $e->getMessage(),
|
|
||||||
'expire_delay' => $request->query->getInt('expire_delay', 0),
|
|
||||||
'file_count' => $request->query->getInt('file_count', 1),
|
|
||||||
'method' => $method,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$p = new \stdClass();
|
|
||||||
$p->message = $e->getMessage();
|
|
||||||
$p->status = JsonResponse::HTTP_BAD_REQUEST;
|
|
||||||
|
|
||||||
return new JsonResponse($p, JsonResponse::HTTP_BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->security->isGranted(AsyncUploadVoter::GENERATE_SIGNATURE, $p)) {
|
$p = $this->tempUrlGenerator->generate(
|
||||||
throw new AccessDeniedHttpException('not allowed to generate this signature');
|
$method,
|
||||||
}
|
$filename,
|
||||||
|
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null
|
||||||
|
);
|
||||||
|
|
||||||
|
$user = $this->security->getUser();
|
||||||
|
$userId = match ($user instanceof User) {
|
||||||
|
true => $user->getId(),
|
||||||
|
false => $user->getUserIdentifier(),
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->chillLogger->notice('[Privacy Event] a request to see a document has been granted', [
|
||||||
|
'doc_uuid' => $storedObject->getUuid()->toString(),
|
||||||
|
'user_id' => $userId,
|
||||||
|
]);
|
||||||
|
|
||||||
return new JsonResponse(
|
return new JsonResponse(
|
||||||
$this->serializer->serialize($p, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
$this->serializer->serialize($p, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
||||||
|
@ -35,7 +35,7 @@ class DocumentAccompanyingCourseController extends AbstractController
|
|||||||
protected TranslatorInterface $translator,
|
protected TranslatorInterface $translator,
|
||||||
protected EventDispatcherInterface $eventDispatcher,
|
protected EventDispatcherInterface $eventDispatcher,
|
||||||
protected AuthorizationHelper $authorizationHelper,
|
protected AuthorizationHelper $authorizationHelper,
|
||||||
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
|
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[Route(path: '/{id}/delete', name: 'chill_docstore_accompanying_course_document_delete')]
|
#[Route(path: '/{id}/delete', name: 'chill_docstore_accompanying_course_document_delete')]
|
||||||
|
@ -44,7 +44,7 @@ class DocumentPersonController extends AbstractController
|
|||||||
protected AuthorizationHelper $authorizationHelper,
|
protected AuthorizationHelper $authorizationHelper,
|
||||||
protected PDFSignatureZoneParser $PDFSignatureZoneParser,
|
protected PDFSignatureZoneParser $PDFSignatureZoneParser,
|
||||||
protected StoredObjectManagerInterface $storedObjectManagerInterface,
|
protected StoredObjectManagerInterface $storedObjectManagerInterface,
|
||||||
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
|
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[Route(path: '/{id}/delete', name: 'chill_docstore_person_document_delete')]
|
#[Route(path: '/{id}/delete', name: 'chill_docstore_person_document_delete')]
|
||||||
|
@ -11,6 +11,46 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\DocStoreBundle\Controller;
|
namespace Chill\DocStoreBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
|
|
||||||
class StoredObjectApiController extends ApiController {}
|
class StoredObjectApiController extends ApiController
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly Security $security,
|
||||||
|
private readonly SerializerInterface $serializer,
|
||||||
|
private readonly EntityManagerInterface $entityManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new stored object.
|
||||||
|
*
|
||||||
|
* @return JsonResponse the response containing the serialized object in JSON format
|
||||||
|
*
|
||||||
|
* @throws AccessDeniedHttpException if the user does not have the necessary role to create a stored object
|
||||||
|
*/
|
||||||
|
#[Route('/api/1.0/doc-store/stored-object/create', methods: ['POST'])]
|
||||||
|
public function createStoredObject(): JsonResponse
|
||||||
|
{
|
||||||
|
if (!($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_USER'))) {
|
||||||
|
throw new AccessDeniedHttpException('Must be user or admin to create a stored object');
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = new StoredObject();
|
||||||
|
|
||||||
|
$this->entityManager->persist($object);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
return new JsonResponse(
|
||||||
|
$this->serializer->serialize($object, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
||||||
|
json: true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ use Chill\DocStoreBundle\Dav\Response\DavResponse;
|
|||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
@ -42,6 +43,7 @@ final readonly class WebdavController
|
|||||||
private \Twig\Environment $engine,
|
private \Twig\Environment $engine,
|
||||||
private StoredObjectManagerInterface $storedObjectManager,
|
private StoredObjectManagerInterface $storedObjectManager,
|
||||||
private Security $security,
|
private Security $security,
|
||||||
|
private EntityManagerInterface $entityManager,
|
||||||
) {
|
) {
|
||||||
$this->requestAnalyzer = new PropfindRequestAnalyzer();
|
$this->requestAnalyzer = new PropfindRequestAnalyzer();
|
||||||
}
|
}
|
||||||
@ -201,6 +203,8 @@ final readonly class WebdavController
|
|||||||
|
|
||||||
$this->storedObjectManager->write($storedObject, $request->getContent());
|
$this->storedObjectManager->write($storedObject, $request->getContent());
|
||||||
|
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
return new DavResponse('', Response::HTTP_NO_CONTENT);
|
return new DavResponse('', Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\DocStoreBundle\DependencyInjection;
|
namespace Chill\DocStoreBundle\DependencyInjection;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\Controller\StoredObjectApiController;
|
|
||||||
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
|
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
|
||||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||||
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface;
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface;
|
||||||
@ -19,7 +18,6 @@ use Symfony\Component\Config\FileLocator;
|
|||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||||
use Symfony\Component\DependencyInjection\Loader;
|
use Symfony\Component\DependencyInjection\Loader;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,29 +51,6 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf
|
|||||||
$this->prependRoute($container);
|
$this->prependRoute($container);
|
||||||
$this->prependAuthorization($container);
|
$this->prependAuthorization($container);
|
||||||
$this->prependTwig($container);
|
$this->prependTwig($container);
|
||||||
$this->prependApis($container);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function prependApis(ContainerBuilder $container)
|
|
||||||
{
|
|
||||||
$container->prependExtensionConfig('chill_main', [
|
|
||||||
'apis' => [
|
|
||||||
[
|
|
||||||
'class' => \Chill\DocStoreBundle\Entity\StoredObject::class,
|
|
||||||
'controller' => StoredObjectApiController::class,
|
|
||||||
'name' => 'stored_object',
|
|
||||||
'base_path' => '/api/1.0/docstore/stored-object',
|
|
||||||
'base_role' => 'ROLE_USER',
|
|
||||||
'actions' => [
|
|
||||||
'_entity' => [
|
|
||||||
'methods' => [
|
|
||||||
Request::METHOD_POST => true,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function prependAuthorization(ContainerBuilder $container)
|
protected function prependAuthorization(ContainerBuilder $container)
|
||||||
|
@ -42,7 +42,7 @@ class DocumentCategory
|
|||||||
*/
|
*/
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, name: 'id_inside_bundle')]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, name: 'id_inside_bundle')]
|
||||||
private $idInsideBundle
|
private $idInsideBundle,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function getBundleId() // ::class BundleClass (FQDN)
|
public function getBundleId() // ::class BundleClass (FQDN)
|
||||||
|
@ -16,10 +16,14 @@ use ChampsLibres\WopiLib\Contract\Entity\Document;
|
|||||||
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
use Ramsey\Uuid\UuidInterface;
|
use Ramsey\Uuid\UuidInterface;
|
||||||
|
use Random\RandomException;
|
||||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represent a document stored in an object store.
|
* Represent a document stored in an object store.
|
||||||
@ -28,13 +32,16 @@ use Symfony\Component\Serializer\Annotation as Serializer;
|
|||||||
*
|
*
|
||||||
* The property `$deleteAt` allow a deletion of the document after the given date. But this property should
|
* The property `$deleteAt` allow a deletion of the document after the given date. But this property should
|
||||||
* be set before the document is actually written by the StoredObjectManager.
|
* be set before the document is actually written by the StoredObjectManager.
|
||||||
|
*
|
||||||
|
* Each version is stored within a @see{StoredObjectVersion}, associated with this current's object. The creation
|
||||||
|
* of each new version should be done using the method @see{self::registerVersion}.
|
||||||
*/
|
*/
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table('chill_doc.stored_object')]
|
#[ORM\Table('stored_object', schema: 'chill_doc')]
|
||||||
#[AsyncFileExists(message: 'The file is not stored properly')]
|
|
||||||
class StoredObject implements Document, TrackCreationInterface
|
class StoredObject implements Document, TrackCreationInterface
|
||||||
{
|
{
|
||||||
use TrackCreationTrait;
|
use TrackCreationTrait;
|
||||||
|
final public const STATUS_EMPTY = 'empty';
|
||||||
final public const STATUS_READY = 'ready';
|
final public const STATUS_READY = 'ready';
|
||||||
final public const STATUS_PENDING = 'pending';
|
final public const STATUS_PENDING = 'pending';
|
||||||
final public const STATUS_FAILURE = 'failure';
|
final public const STATUS_FAILURE = 'failure';
|
||||||
@ -43,9 +50,11 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, name: 'datas')]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, name: 'datas')]
|
||||||
private array $datas = [];
|
private array $datas = [];
|
||||||
|
|
||||||
#[Serializer\Groups(['write'])]
|
/**
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT)]
|
* the prefix of each version.
|
||||||
private string $filename = '';
|
*/
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||||
|
private string $prefix = '';
|
||||||
|
|
||||||
#[Serializer\Groups(['write'])]
|
#[Serializer\Groups(['write'])]
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
@ -53,25 +62,10 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
||||||
private ?int $id = null;
|
private ?int $id = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int[]
|
|
||||||
*/
|
|
||||||
#[Serializer\Groups(['write'])]
|
#[Serializer\Groups(['write'])]
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, name: 'iv')]
|
#[ORM\Column(name: 'title', type: \Doctrine\DBAL\Types\Types::TEXT, options: ['default' => ''])]
|
||||||
private array $iv = [];
|
|
||||||
|
|
||||||
#[Serializer\Groups(['write'])]
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, name: 'key')]
|
|
||||||
private array $keyInfos = [];
|
|
||||||
|
|
||||||
#[Serializer\Groups(['write'])]
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, name: 'title')]
|
|
||||||
private string $title = '';
|
private string $title = '';
|
||||||
|
|
||||||
#[Serializer\Groups(['write'])]
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, name: 'type', options: ['default' => ''])]
|
|
||||||
private string $type = '';
|
|
||||||
|
|
||||||
#[Serializer\Groups(['write'])]
|
#[Serializer\Groups(['write'])]
|
||||||
#[ORM\Column(type: 'uuid', unique: true)]
|
#[ORM\Column(type: 'uuid', unique: true)]
|
||||||
private UuidInterface $uuid;
|
private UuidInterface $uuid;
|
||||||
@ -94,14 +88,22 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||||
private string $generationErrors = '';
|
private string $generationErrors = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Collection<int, StoredObjectVersion>
|
||||||
|
*/
|
||||||
|
#[ORM\OneToMany(mappedBy: 'storedObject', targetEntity: StoredObjectVersion::class, cascade: ['persist'], orphanRemoval: true)]
|
||||||
|
private Collection $versions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param StoredObject::STATUS_* $status
|
* @param StoredObject::STATUS_* $status
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, options: ['default' => 'ready'])]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, options: ['default' => 'ready'])]
|
||||||
private string $status = 'ready'
|
private string $status = 'empty',
|
||||||
) {
|
) {
|
||||||
$this->uuid = Uuid::uuid4();
|
$this->uuid = Uuid::uuid4();
|
||||||
|
$this->versions = new ArrayCollection();
|
||||||
|
$this->prefix = self::generatePrefix();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addGenerationTrial(): self
|
public function addGenerationTrial(): self
|
||||||
@ -125,14 +127,34 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
return \DateTime::createFromImmutable($this->createdAt);
|
return \DateTime::createFromImmutable($this->createdAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[AsyncFileExists(message: 'The file is not stored properly')]
|
||||||
|
#[Assert\NotNull(message: 'The store object version must be present')]
|
||||||
|
public function getCurrentVersion(): ?StoredObjectVersion
|
||||||
|
{
|
||||||
|
$maxVersion = null;
|
||||||
|
|
||||||
|
foreach ($this->versions as $v) {
|
||||||
|
if ($v->getVersion() > ($maxVersion?->getVersion() ?? -1)) {
|
||||||
|
$maxVersion = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $maxVersion;
|
||||||
|
}
|
||||||
|
|
||||||
public function getDatas(): array
|
public function getDatas(): array
|
||||||
{
|
{
|
||||||
return $this->datas;
|
return $this->datas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPrefix(): string
|
||||||
|
{
|
||||||
|
return $this->prefix;
|
||||||
|
}
|
||||||
|
|
||||||
public function getFilename(): string
|
public function getFilename(): string
|
||||||
{
|
{
|
||||||
return $this->filename;
|
return $this->getCurrentVersion()?->getFilename() ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGenerationTrialsCounter(): int
|
public function getGenerationTrialsCounter(): int
|
||||||
@ -145,14 +167,17 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<int>
|
||||||
|
*/
|
||||||
public function getIv(): array
|
public function getIv(): array
|
||||||
{
|
{
|
||||||
return $this->iv;
|
return $this->getCurrentVersion()?->getIv() ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getKeyInfos(): array
|
public function getKeyInfos(): array
|
||||||
{
|
{
|
||||||
return $this->keyInfos;
|
return $this->getCurrentVersion()?->getKeyInfos() ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,14 +196,14 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
return $this->status;
|
return $this->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle()
|
public function getTitle(): string
|
||||||
{
|
{
|
||||||
return $this->title;
|
return $this->title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getType()
|
public function getType(): string
|
||||||
{
|
{
|
||||||
return $this->type;
|
return $this->getCurrentVersion()?->getType() ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUuid(): UuidInterface
|
public function getUuid(): UuidInterface
|
||||||
@ -209,27 +234,6 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setFilename(?string $filename): self
|
|
||||||
{
|
|
||||||
$this->filename = (string) $filename;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setIv(?array $iv): self
|
|
||||||
{
|
|
||||||
$this->iv = (array) $iv;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setKeyInfos(?array $keyInfos): self
|
|
||||||
{
|
|
||||||
$this->keyInfos = (array) $keyInfos;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param StoredObject::STATUS_* $status
|
* @param StoredObject::STATUS_* $status
|
||||||
*/
|
*/
|
||||||
@ -247,18 +251,16 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setType(?string $type): self
|
|
||||||
{
|
|
||||||
$this->type = (string) $type;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTemplate(): ?DocGeneratorTemplate
|
public function getTemplate(): ?DocGeneratorTemplate
|
||||||
{
|
{
|
||||||
return $this->template;
|
return $this->template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getVersions(): Collection
|
||||||
|
{
|
||||||
|
return $this->versions;
|
||||||
|
}
|
||||||
|
|
||||||
public function hasTemplate(): bool
|
public function hasTemplate(): bool
|
||||||
{
|
{
|
||||||
return null !== $this->template;
|
return null !== $this->template;
|
||||||
@ -314,18 +316,65 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveHistory(): void
|
public function registerVersion(
|
||||||
{
|
array $iv = [],
|
||||||
if ('' === $this->getFilename()) {
|
array $keyInfos = [],
|
||||||
return;
|
string $type = '',
|
||||||
|
?string $filename = null,
|
||||||
|
): StoredObjectVersion {
|
||||||
|
$version = new StoredObjectVersion(
|
||||||
|
$this,
|
||||||
|
null === $this->getCurrentVersion() ? 0 : $this->getCurrentVersion()->getVersion() + 1,
|
||||||
|
$iv,
|
||||||
|
$keyInfos,
|
||||||
|
$type,
|
||||||
|
$filename
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->versions->add($version);
|
||||||
|
|
||||||
|
if ('empty' === $this->status) {
|
||||||
|
$this->status = self::STATUS_READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->datas['history'][] = [
|
return $version;
|
||||||
'filename' => $this->getFilename(),
|
}
|
||||||
'iv' => $this->getIv(),
|
|
||||||
'key_infos' => $this->getKeyInfos(),
|
public function removeVersion(StoredObjectVersion $storedObjectVersion): void
|
||||||
'type' => $this->getType(),
|
{
|
||||||
'before' => (new \DateTimeImmutable('now'))->getTimestamp(),
|
if (!$this->versions->contains($storedObjectVersion)) {
|
||||||
];
|
throw new \UnexpectedValueException('This stored object does not contains this version');
|
||||||
|
}
|
||||||
|
$this->versions->removeElement($storedObjectVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public function saveHistory(): void {}
|
||||||
|
|
||||||
|
public static function generatePrefix(): string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return base_convert(bin2hex(random_bytes(32)), 16, 36);
|
||||||
|
} catch (RandomException) {
|
||||||
|
return uniqid(more_entropy: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a stored object can be deleted.
|
||||||
|
*
|
||||||
|
* Currently, return true if the deletedAt date is below the current date, and the object
|
||||||
|
* does not contains any version (which must be removed first).
|
||||||
|
*
|
||||||
|
* @param \DateTimeImmutable $now the current date and time
|
||||||
|
* @param StoredObject $storedObject the stored object to check
|
||||||
|
*
|
||||||
|
* @return bool returns true if the stored object can be deleted, false otherwise
|
||||||
|
*/
|
||||||
|
public static function canBeDeleted(\DateTimeImmutable $now, StoredObject $storedObject): bool
|
||||||
|
{
|
||||||
|
return $storedObject->getDeleteAt() < $now && $storedObject->getVersions()->isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
127
src/Bundle/ChillDocStoreBundle/Entity/StoredObjectVersion.php
Normal file
127
src/Bundle/ChillDocStoreBundle/Entity/StoredObjectVersion.php
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Entity;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Random\RandomException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store each version of StoredObject's.
|
||||||
|
*
|
||||||
|
* A version should not be created manually: use the method @see{StoredObject::registerVersion} instead.
|
||||||
|
*/
|
||||||
|
#[ORM\Entity]
|
||||||
|
#[ORM\Table('chill_doc.stored_object_version')]
|
||||||
|
#[ORM\UniqueConstraint(name: 'chill_doc_stored_object_version_unique_by_object', columns: ['stored_object_id', 'version'])]
|
||||||
|
class StoredObjectVersion implements TrackCreationInterface
|
||||||
|
{
|
||||||
|
use TrackCreationTrait;
|
||||||
|
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\GeneratedValue]
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
||||||
|
private ?int $id = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filename of the version in the stored object.
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||||
|
private string $filename = '';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
/**
|
||||||
|
* The stored object associated with this version.
|
||||||
|
*/
|
||||||
|
#[ORM\ManyToOne(targetEntity: StoredObject::class, inversedBy: 'versions')]
|
||||||
|
#[ORM\JoinColumn(name: 'stored_object_id', nullable: false)]
|
||||||
|
private StoredObject $storedObject,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The incremental version.
|
||||||
|
*/
|
||||||
|
#[ORM\Column(name: 'version', type: \Doctrine\DBAL\Types\Types::INTEGER, options: ['default' => 0])]
|
||||||
|
private int $version = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vector for encryption.
|
||||||
|
*
|
||||||
|
* @var int[]
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, name: 'iv')]
|
||||||
|
private array $iv = [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key infos for document encryption.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, name: 'key')]
|
||||||
|
private array $keyInfos = [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* type of the document.
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, name: 'type', options: ['default' => ''])]
|
||||||
|
private string $type = '',
|
||||||
|
?string $filename = null,
|
||||||
|
) {
|
||||||
|
$this->filename = $filename ?? self::generateFilename($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function generateFilename(StoredObjectVersion $storedObjectVersion): string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$suffix = base_convert(bin2hex(random_bytes(8)), 16, 36);
|
||||||
|
} catch (RandomException) {
|
||||||
|
$suffix = uniqid(more_entropy: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $storedObjectVersion->getStoredObject()->getPrefix().'/'.$suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilename(): string
|
||||||
|
{
|
||||||
|
return $this->filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIv(): array
|
||||||
|
{
|
||||||
|
return $this->iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKeyInfos(): array
|
||||||
|
{
|
||||||
|
return $this->keyInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStoredObject(): StoredObject
|
||||||
|
{
|
||||||
|
return $this->storedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersion(): int
|
||||||
|
{
|
||||||
|
return $this->version;
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
|||||||
final class AccompanyingCourseDocumentType extends AbstractType
|
final class AccompanyingCourseDocumentType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
@ -55,16 +55,8 @@ class StoredObjectDataMapper implements DataMapperInterface
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var StoredObject $viewData */
|
/* @var StoredObject $viewData */
|
||||||
if ($viewData->getFilename() !== $forms['stored_object']->getData()['filename']) {
|
$viewData = $forms['stored_object']->getData();
|
||||||
// we want to keep the previous history
|
|
||||||
$viewData->saveHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
$viewData->setFilename($forms['stored_object']->getData()['filename']);
|
|
||||||
$viewData->setIv($forms['stored_object']->getData()['iv']);
|
|
||||||
$viewData->setKeyInfos($forms['stored_object']->getData()['keyInfos']);
|
|
||||||
$viewData->setType($forms['stored_object']->getData()['type']);
|
|
||||||
|
|
||||||
if (array_key_exists('title', $forms)) {
|
if (array_key_exists('title', $forms)) {
|
||||||
$viewData->setTitle($forms['title']->getData());
|
$viewData->setTitle($forms['title']->getData());
|
||||||
|
@ -12,7 +12,6 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle\Form\DataTransformer;
|
namespace Chill\DocStoreBundle\Form\DataTransformer;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectNormalizer;
|
|
||||||
use Symfony\Component\Form\DataTransformerInterface;
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
use Symfony\Component\Serializer\SerializerInterface;
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
@ -20,7 +19,7 @@ use Symfony\Component\Serializer\SerializerInterface;
|
|||||||
class StoredObjectDataTransformer implements DataTransformerInterface
|
class StoredObjectDataTransformer implements DataTransformerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly SerializerInterface $serializer
|
private readonly SerializerInterface $serializer,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function transform(mixed $value): mixed
|
public function transform(mixed $value): mixed
|
||||||
@ -30,11 +29,7 @@ class StoredObjectDataTransformer implements DataTransformerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($value instanceof StoredObject) {
|
if ($value instanceof StoredObject) {
|
||||||
return $this->serializer->serialize($value, 'json', [
|
return $this->serializer->serialize($value, 'json');
|
||||||
'groups' => [
|
|
||||||
StoredObjectNormalizer::ADD_DAV_EDIT_LINK_CONTEXT,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnexpectedTypeException($value, StoredObject::class);
|
throw new UnexpectedTypeException($value, StoredObject::class);
|
||||||
@ -46,6 +41,6 @@ class StoredObjectDataTransformer implements DataTransformerInterface
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return json_decode((string) $value, true, 10, JSON_THROW_ON_ERROR);
|
return $this->serializer->deserialize($value, StoredObject::class, 'json');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ class AsyncUploaderType extends AbstractType
|
|||||||
public function buildView(
|
public function buildView(
|
||||||
FormView $view,
|
FormView $view,
|
||||||
FormInterface $form,
|
FormInterface $form,
|
||||||
array $options
|
array $options,
|
||||||
) {
|
) {
|
||||||
$view->vars['attr']['data-async-file-upload'] = true;
|
$view->vars['attr']['data-async-file-upload'] = true;
|
||||||
$view->vars['attr']['data-generate-temp-url-post'] = $this
|
$view->vars['attr']['data-generate-temp-url-post'] = $this
|
||||||
|
@ -20,7 +20,7 @@ interface GenericDocForAccompanyingPeriodProviderInterface
|
|||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null,
|
?string $content = null,
|
||||||
?string $origin = null
|
?string $origin = null,
|
||||||
): FetchQueryInterface;
|
): FetchQueryInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,7 +20,7 @@ interface GenericDocForPersonProviderInterface
|
|||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null,
|
?string $content = null,
|
||||||
?string $origin = null
|
?string $origin = null,
|
||||||
): FetchQueryInterface;
|
): FetchQueryInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +46,7 @@ final readonly class Manager
|
|||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null,
|
?string $content = null,
|
||||||
array $places = []
|
array $places = [],
|
||||||
): int {
|
): int {
|
||||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ final readonly class Manager
|
|||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null,
|
?string $content = null,
|
||||||
array $places = []
|
array $places = [],
|
||||||
): int {
|
): int {
|
||||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places);
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places);
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ final readonly class Manager
|
|||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null,
|
?string $content = null,
|
||||||
array $places = []
|
array $places = [],
|
||||||
): iterable {
|
): iterable {
|
||||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ final readonly class Manager
|
|||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null,
|
?string $content = null,
|
||||||
array $places = []
|
array $places = [],
|
||||||
): iterable {
|
): iterable {
|
||||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places);
|
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ final readonly class PersonDocumentGenericDocProvider implements GenericDocForPe
|
|||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null,
|
?string $content = null,
|
||||||
?string $origin = null
|
?string $origin = null,
|
||||||
): FetchQueryInterface {
|
): FetchQueryInterface {
|
||||||
return $this->personDocumentACLAwareRepository->buildFetchQueryForPerson(
|
return $this->personDocumentACLAwareRepository->buildFetchQueryForPerson(
|
||||||
$person,
|
$person,
|
||||||
|
@ -31,13 +31,13 @@ interface PersonDocumentACLAwareRepositoryInterface
|
|||||||
Person $person,
|
Person $person,
|
||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null
|
?string $content = null,
|
||||||
): FetchQueryInterface;
|
): FetchQueryInterface;
|
||||||
|
|
||||||
public function buildFetchQueryForAccompanyingPeriod(
|
public function buildFetchQueryForAccompanyingPeriod(
|
||||||
AccompanyingPeriod $period,
|
AccompanyingPeriod $period,
|
||||||
?\DateTimeImmutable $startDate = null,
|
?\DateTimeImmutable $startDate = null,
|
||||||
?\DateTimeImmutable $endDate = null,
|
?\DateTimeImmutable $endDate = null,
|
||||||
?string $content = null
|
?string $content = null,
|
||||||
): FetchQueryInterface;
|
): FetchQueryInterface;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ readonly class PersonDocumentRepository implements ObjectRepository, AssociatedE
|
|||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private EntityManagerInterface $entityManager
|
private EntityManagerInterface $entityManager,
|
||||||
) {
|
) {
|
||||||
$this->repository = $this->entityManager->getRepository($this->getClassName());
|
$this->repository = $this->entityManager->getRepository($this->getClassName());
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace Chill\DocStoreBundle\Repository;
|
|||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
|
||||||
final readonly class StoredObjectRepository implements StoredObjectRepositoryInterface
|
final readonly class StoredObjectRepository implements StoredObjectRepositoryInterface
|
||||||
{
|
{
|
||||||
@ -53,6 +54,21 @@ final readonly class StoredObjectRepository implements StoredObjectRepositoryInt
|
|||||||
return $this->repository->findOneBy($criteria);
|
return $this->repository->findOneBy($criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByExpired(\DateTimeImmutable $expiredAtDate): iterable
|
||||||
|
{
|
||||||
|
$qb = $this->repository->createQueryBuilder('stored_object');
|
||||||
|
$qb
|
||||||
|
->where('stored_object.deleteAt <= :expiredAt')
|
||||||
|
->setParameter('expiredAt', $expiredAtDate);
|
||||||
|
|
||||||
|
return $qb->getQuery()->toIterable(hydrationMode: Query::HYDRATE_OBJECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneByUUID(string $uuid): ?StoredObject
|
||||||
|
{
|
||||||
|
return $this->repository->findOneBy(['uuid' => $uuid]);
|
||||||
|
}
|
||||||
|
|
||||||
public function getClassName(): string
|
public function getClassName(): string
|
||||||
{
|
{
|
||||||
return StoredObject::class;
|
return StoredObject::class;
|
||||||
|
@ -17,4 +17,12 @@ use Doctrine\Persistence\ObjectRepository;
|
|||||||
/**
|
/**
|
||||||
* @extends ObjectRepository<StoredObject>
|
* @extends ObjectRepository<StoredObject>
|
||||||
*/
|
*/
|
||||||
interface StoredObjectRepositoryInterface extends ObjectRepository {}
|
interface StoredObjectRepositoryInterface extends ObjectRepository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return iterable<StoredObject>
|
||||||
|
*/
|
||||||
|
public function findByExpired(\DateTimeImmutable $expiredAtDate): iterable;
|
||||||
|
|
||||||
|
public function findOneByUUID(string $uuid): ?StoredObject;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @implements ObjectRepository<StoredObjectVersion>
|
||||||
|
*/
|
||||||
|
class StoredObjectVersionRepository implements ObjectRepository
|
||||||
|
{
|
||||||
|
private readonly EntityRepository $repository;
|
||||||
|
|
||||||
|
private readonly Connection $connection;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
|
{
|
||||||
|
$this->repository = $entityManager->getRepository(StoredObjectVersion::class);
|
||||||
|
$this->connection = $entityManager->getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id): ?StoredObjectVersion
|
||||||
|
{
|
||||||
|
return $this->repository->find($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->repository->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||||
|
{
|
||||||
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria): ?StoredObjectVersion
|
||||||
|
{
|
||||||
|
return $this->repository->findOneBy($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the IDs of versions older than a given date and that are not the last version.
|
||||||
|
*
|
||||||
|
* Those version are good candidates for a deletion.
|
||||||
|
*
|
||||||
|
* @param \DateTimeImmutable $beforeDate the date to compare versions against
|
||||||
|
*
|
||||||
|
* @return iterable returns an iterable with the IDs of the versions
|
||||||
|
*/
|
||||||
|
public function findIdsByVersionsOlderThanDateAndNotLastVersion(\DateTimeImmutable $beforeDate): iterable
|
||||||
|
{
|
||||||
|
$results = $this->connection->executeQuery(
|
||||||
|
self::QUERY_FIND_IDS_BY_VERSIONS_OLDER_THAN_DATE_AND_NOT_LAST_VERSION,
|
||||||
|
[$beforeDate],
|
||||||
|
[Types::DATETIME_IMMUTABLE]
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($results->iterateAssociative() as $row) {
|
||||||
|
yield $row['sov_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const QUERY_FIND_IDS_BY_VERSIONS_OLDER_THAN_DATE_AND_NOT_LAST_VERSION = <<<'SQL'
|
||||||
|
SELECT
|
||||||
|
sov.id AS sov_id
|
||||||
|
FROM chill_doc.stored_object_version sov
|
||||||
|
WHERE
|
||||||
|
sov.createdat < ?::timestamp
|
||||||
|
AND
|
||||||
|
sov.version < (SELECT MAX(sub_sov.version) FROM chill_doc.stored_object_version sub_sov WHERE sub_sov.stored_object_id = sov.stored_object_id)
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
public function getClassName(): string
|
||||||
|
{
|
||||||
|
return StoredObjectVersion::class;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import {makeFetch} from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
import {makeFetch} from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||||
import {PostStoreObjectSignature} from "../../types";
|
import {PostStoreObjectSignature, StoredObject} from "../../types";
|
||||||
|
|
||||||
const algo = 'AES-CBC';
|
const algo = 'AES-CBC';
|
||||||
|
|
||||||
@ -21,11 +21,22 @@ const createFilename = (): string => {
|
|||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const uploadFile = async (uploadFile: ArrayBuffer): Promise<string> => {
|
/**
|
||||||
|
* Fetches a new stored object from the server.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @function fetchNewStoredObject
|
||||||
|
* @returns {Promise<StoredObject>} A Promise that resolves to the newly created StoredObject.
|
||||||
|
*/
|
||||||
|
export const fetchNewStoredObject = async (): Promise<StoredObject> => {
|
||||||
|
return makeFetch("POST", '/api/1.0/doc-store/stored-object/create', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const uploadVersion = async (uploadFile: ArrayBuffer, storedObject: StoredObject): Promise<string> => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.append('expires_delay', "180");
|
params.append('expires_delay', "180");
|
||||||
params.append('submit_delay', "180");
|
params.append('submit_delay', "180");
|
||||||
const asyncData: PostStoreObjectSignature = await makeFetch("GET", URL_POST + "?" + params.toString());
|
const asyncData: PostStoreObjectSignature = await makeFetch("GET", `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/post` + "?" + params.toString());
|
||||||
const suffix = createFilename();
|
const suffix = createFilename();
|
||||||
const filename = asyncData.prefix + suffix;
|
const filename = asyncData.prefix + suffix;
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@ -50,7 +61,6 @@ export const uploadFile = async (uploadFile: ArrayBuffer): Promise<string> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const encryptFile = async (originalFile: ArrayBuffer): Promise<[ArrayBuffer, Uint8Array, JsonWebKey]> => {
|
export const encryptFile = async (originalFile: ArrayBuffer): Promise<[ArrayBuffer, Uint8Array, JsonWebKey]> => {
|
||||||
console.log('encrypt', originalFile);
|
|
||||||
const iv = crypto.getRandomValues(new Uint8Array(16));
|
const iv = crypto.getRandomValues(new Uint8Array(16));
|
||||||
const key = await window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ]);
|
const key = await window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ]);
|
||||||
const exportedKey = await window.crypto.subtle.exportKey('jwk', key);
|
const exportedKey = await window.crypto.subtle.exportKey('jwk', key);
|
@ -1,7 +1,7 @@
|
|||||||
import {CollectionEventPayload} from "../../../../../ChillMainBundle/Resources/public/module/collection";
|
import {CollectionEventPayload} from "../../../../../ChillMainBundle/Resources/public/module/collection";
|
||||||
import {createApp} from "vue";
|
import {createApp} from "vue";
|
||||||
import DropFileWidget from "../../vuejs/DropFileWidget/DropFileWidget.vue"
|
import DropFileWidget from "../../vuejs/DropFileWidget/DropFileWidget.vue"
|
||||||
import {StoredObject, StoredObjectCreated} from "../../types";
|
import {StoredObject, StoredObjectVersion} from "../../types";
|
||||||
import {_createI18n} from "../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
|
import {_createI18n} from "../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
|
||||||
const i18n = _createI18n({});
|
const i18n = _createI18n({});
|
||||||
|
|
||||||
@ -30,15 +30,17 @@ const startApp = (divElement: HTMLDivElement, collectionEntry: null|HTMLLIElemen
|
|||||||
DropFileWidget,
|
DropFileWidget,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
addDocument: function(object: StoredObjectCreated): void {
|
addDocument: function({stored_object, stored_object_version}: {stored_object: StoredObject, stored_object_version: StoredObjectVersion}): void {
|
||||||
console.log('object added', object);
|
console.log('object added', stored_object);
|
||||||
this.$data.existingDoc = object;
|
console.log('version added', stored_object_version);
|
||||||
input_stored_object.value = JSON.stringify(object);
|
this.$data.existingDoc = stored_object;
|
||||||
|
this.$data.existingDoc.currentVersion = stored_object_version;
|
||||||
|
input_stored_object.value = JSON.stringify(this.$data.existingDoc);
|
||||||
},
|
},
|
||||||
removeDocument: function(object: StoredObject): void {
|
removeDocument: function(object: StoredObject): void {
|
||||||
console.log('catch remove document', object);
|
console.log('catch remove document', object);
|
||||||
input_stored_object.value = "";
|
input_stored_object.value = "";
|
||||||
this.$data.existingDoc = null;
|
this.$data.existingDoc = undefined;
|
||||||
console.log('collectionEntry', collectionEntry);
|
console.log('collectionEntry', collectionEntry);
|
||||||
|
|
||||||
if (null !== collectionEntry) {
|
if (null !== collectionEntry) {
|
||||||
|
@ -1,36 +1,51 @@
|
|||||||
import {DateTime} from "../../../ChillMainBundle/Resources/public/types";
|
import {DateTime, User} from "../../../ChillMainBundle/Resources/public/types";
|
||||||
|
|
||||||
export type StoredObjectStatus = "ready"|"failure"|"pending";
|
export type StoredObjectStatus = "empty"|"ready"|"failure"|"pending";
|
||||||
|
|
||||||
export interface StoredObject {
|
export interface StoredObject {
|
||||||
id: number,
|
id: number,
|
||||||
|
title: string|null,
|
||||||
/**
|
uuid: string,
|
||||||
* filename of the object in the object storage
|
prefix: string,
|
||||||
*/
|
status: StoredObjectStatus,
|
||||||
filename: string,
|
currentVersion: null|StoredObjectVersionCreated|StoredObjectVersionPersisted,
|
||||||
creationDate: DateTime,
|
totalVersions: number,
|
||||||
datas: object,
|
datas: object,
|
||||||
iv: number[],
|
/** @deprecated */
|
||||||
keyInfos: object,
|
creationDate: DateTime,
|
||||||
title: string,
|
createdAt: DateTime|null,
|
||||||
type: string,
|
createdBy: User|null,
|
||||||
uuid: string,
|
_permissions: {
|
||||||
status: StoredObjectStatus,
|
canEdit: boolean,
|
||||||
|
canSee: boolean,
|
||||||
|
},
|
||||||
_links?: {
|
_links?: {
|
||||||
dav_link?: {
|
dav_link?: {
|
||||||
href: string
|
href: string
|
||||||
expiration: number
|
expiration: number
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StoredObjectCreated {
|
export interface StoredObjectVersion {
|
||||||
status: "stored_object_created",
|
/**
|
||||||
filename: string,
|
* filename of the object in the object storage
|
||||||
iv: Uint8Array,
|
*/
|
||||||
keyInfos: object,
|
filename: string,
|
||||||
type: string,
|
iv: number[],
|
||||||
|
keyInfos: JsonWebKey,
|
||||||
|
type: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StoredObjectVersionCreated extends StoredObjectVersion {
|
||||||
|
persisted: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StoredObjectVersionPersisted extends StoredObjectVersionCreated {
|
||||||
|
version: number,
|
||||||
|
id: number,
|
||||||
|
createdAt: DateTime|null,
|
||||||
|
createdBy: User|null,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StoredObjectStatusChange {
|
export interface StoredObjectStatusChange {
|
||||||
@ -82,4 +97,4 @@ export interface Signature {
|
|||||||
zones: SignatureZone[],
|
zones: SignatureZone[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SignedState = 'pending' | 'signed' | 'rejected' | 'canceled' | 'error';
|
export type SignedState = 'pending' | 'signed' | 'rejected' | 'canceled' | 'error';
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user