mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +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 -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=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:
|
||||
expire_in: 1 day
|
||||
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",
|
||||
"mime": "^4.0.0",
|
||||
"pdfjs-dist": "^4.3.136",
|
||||
"swagger-ui": "^4.15.5",
|
||||
"vis-network": "^9.1.0",
|
||||
"vue": "^3.2.37",
|
||||
"vue-i18n": "^9.1.6",
|
||||
|
@ -68,7 +68,7 @@ final class ActivityController extends AbstractController
|
||||
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private readonly PaginatorFactory $paginatorFactory,
|
||||
private readonly ChillSecurity $security
|
||||
private readonly ChillSecurity $security,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
@ -28,7 +28,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
|
||||
public function __construct(
|
||||
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository,
|
||||
protected ActivityReasonRepository $activityReasonRepository,
|
||||
protected TranslatableStringHelper $translatableStringHelper
|
||||
protected TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -26,7 +26,7 @@ class ActivityUsersJobAggregator implements AggregatorInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -26,7 +26,7 @@ class ActivityUsersScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -26,7 +26,7 @@ class CreatorJobAggregator implements AggregatorInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -26,7 +26,7 @@ class CreatorScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepository $scopeRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -42,7 +42,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
|
||||
/**
|
||||
* The action for this report.
|
||||
*/
|
||||
protected string $action = 'sum'
|
||||
protected string $action = 'sum',
|
||||
) {
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class ListActivityHelper
|
||||
private readonly TranslatorInterface $translator,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private readonly TranslatableStringExportLabelHelper $translatableStringLabelHelper,
|
||||
private readonly UserHelper $userHelper
|
||||
private readonly UserHelper $userHelper,
|
||||
) {}
|
||||
|
||||
public function addSelect(QueryBuilder $qb): void
|
||||
|
@ -25,7 +25,7 @@ final readonly class ActivityPresenceFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private TranslatorInterface $translator
|
||||
private TranslatorInterface $translator,
|
||||
) {}
|
||||
|
||||
public function getTitle()
|
||||
|
@ -26,7 +26,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
|
||||
{
|
||||
public function __construct(
|
||||
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||
protected ActivityTypeRepositoryInterface $activityTypeRepository
|
||||
protected ActivityTypeRepositoryInterface $activityTypeRepository,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -29,7 +29,7 @@ class UsersJobFilter implements FilterInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private readonly UserJobRepositoryInterface $userJobRepository
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -29,7 +29,7 @@ class UsersScopeFilter implements FilterInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -59,7 +59,7 @@ class ActivityType extends AbstractType
|
||||
protected TranslatableStringHelper $translatableStringHelper,
|
||||
protected array $timeChoices,
|
||||
protected SocialIssueRender $socialIssueRender,
|
||||
protected SocialActionRender $socialActionRender
|
||||
protected SocialActionRender $socialActionRender,
|
||||
) {
|
||||
if (!$tokenStorage->getToken()->getUser() instanceof User) {
|
||||
throw new \RuntimeException('you should have a valid user');
|
||||
|
@ -27,7 +27,7 @@ class PickActivityReasonType extends AbstractType
|
||||
public function __construct(
|
||||
private readonly ActivityReasonRepository $activityReasonRepository,
|
||||
private readonly ActivityReasonRender $reasonRender,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
|
@ -32,7 +32,7 @@ final readonly class ActivityDocumentACLAwareRepository implements ActivityDocum
|
||||
private EntityManagerInterface $em,
|
||||
private CenterResolverManagerInterface $centerResolverManager,
|
||||
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser,
|
||||
private Security $security
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
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(
|
||||
ManagerRegistry $registry,
|
||||
private readonly RequestStack $requestStack
|
||||
private readonly RequestStack $requestStack,
|
||||
) {
|
||||
parent::__construct($registry, ActivityReason::class);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class ActivityStoredObjectVoter extends AbstractStoredObjectVoter
|
||||
public function __construct(
|
||||
private readonly ActivityRepository $repository,
|
||||
Security $security,
|
||||
WorkflowStoredObjectPermissionHelper $workflowDocumentService
|
||||
WorkflowStoredObjectPermissionHelper $workflowDocumentService,
|
||||
) {
|
||||
parent::__construct($security, $workflowDocumentService);
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
||||
|
||||
public function __construct(
|
||||
protected Security $security,
|
||||
VoterHelperFactoryInterface $voterHelperFactory
|
||||
VoterHelperFactoryInterface $voterHelperFactory,
|
||||
) {
|
||||
$this->voterHelper = $voterHelperFactory->generate(self::class)
|
||||
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||
|
@ -50,7 +50,7 @@ class ActivityContext implements
|
||||
private readonly TranslatorInterface $translator,
|
||||
private readonly BaseContextData $baseContextData,
|
||||
private readonly ThirdPartyRender $thirdPartyRender,
|
||||
private readonly ThirdPartyRepository $thirdPartyRepository
|
||||
private readonly ThirdPartyRepository $thirdPartyRepository,
|
||||
) {}
|
||||
|
||||
public function adminFormReverseTransform(array $data): array
|
||||
|
@ -56,7 +56,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
|
||||
private readonly SocialIssueRepository $socialIssueRepository,
|
||||
private readonly ThirdPartyRepository $thirdPartyRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private readonly UserRepository $userRepository
|
||||
private readonly UserRepository $userRepository,
|
||||
) {}
|
||||
|
||||
public function adminFormReverseTransform(array $data): array
|
||||
|
@ -76,7 +76,7 @@ final class TranslatableActivityReasonTest extends TypeTestCase
|
||||
*/
|
||||
protected function getTranslatableStringHelper(
|
||||
$locale = 'en',
|
||||
$fallbackLocale = 'en'
|
||||
$fallbackLocale = 'en',
|
||||
) {
|
||||
$prophet = new \Prophecy\Prophet();
|
||||
$requestStack = $prophet->prophesize();
|
||||
|
@ -138,7 +138,7 @@ final class ActivityVoterTest extends KernelTestCase
|
||||
Scope $scope,
|
||||
Center $center,
|
||||
$attribute,
|
||||
$message
|
||||
$message,
|
||||
) {
|
||||
$token = $this->prepareToken($user);
|
||||
$activity = $this->prepareActivity($scope, $this->preparePerson($center));
|
||||
|
@ -32,7 +32,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
||||
protected EntityManagerInterface $em,
|
||||
protected AuthorizationHelperInterface $helper,
|
||||
TokenStorageInterface $storage,
|
||||
protected ActivityACLAwareRepository $aclAwareRepository
|
||||
protected ActivityACLAwareRepository $aclAwareRepository,
|
||||
) {
|
||||
if (!$storage->getToken()->getUser() instanceof User) {
|
||||
throw new \RuntimeException('A user should be authenticated !');
|
||||
|
@ -25,7 +25,7 @@ final class AsideActivityController extends CRUDController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AsideActivityCategoryRepository $categoryRepository,
|
||||
private readonly Security $security
|
||||
private readonly Security $security,
|
||||
) {}
|
||||
|
||||
public function createEntity(string $action, Request $request): object
|
||||
@ -76,7 +76,7 @@ final class AsideActivityController extends CRUDController
|
||||
string $action,
|
||||
$query,
|
||||
Request $request,
|
||||
PaginatorInterface $paginator
|
||||
PaginatorInterface $paginator,
|
||||
) {
|
||||
if ('index' === $action) {
|
||||
return $query->orderBy('e.date', 'DESC');
|
||||
|
@ -22,7 +22,7 @@ class ByActivityTypeAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AsideActivityCategoryRepository $asideActivityCategoryRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -26,7 +26,7 @@ class ByUserJobAggregator implements AggregatorInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -26,7 +26,7 @@ class ByUserScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -41,7 +41,7 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn
|
||||
private AsideActivityCategoryRepository $asideActivityCategoryRepository,
|
||||
private CategoryRender $categoryRender,
|
||||
private LocationRepository $locationRepository,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
@ -27,7 +27,7 @@ class ByActivityTypeFilter implements FilterInterface
|
||||
public function __construct(
|
||||
private readonly CategoryRender $categoryRender,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private readonly AsideActivityCategoryRepository $asideActivityTypeRepository
|
||||
private readonly AsideActivityCategoryRepository $asideActivityTypeRepository,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -24,7 +24,7 @@ use Symfony\Component\Security\Core\Security;
|
||||
final readonly class ByLocationFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(
|
||||
private Security $security
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
public function getTitle(): string
|
||||
|
@ -29,7 +29,7 @@ class ByUserJobFilter implements FilterInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private readonly UserJobRepositoryInterface $userJobRepository
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -29,7 +29,7 @@ class ByUserScopeFilter implements FilterInterface
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -44,7 +44,7 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
CountNotificationTask $counter,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
TranslatorInterface $translator,
|
||||
AuthorizationCheckerInterface $authorizationChecker
|
||||
AuthorizationCheckerInterface $authorizationChecker,
|
||||
) {
|
||||
$this->counter = $counter;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
|
@ -26,7 +26,7 @@ class AsideActivityVoter extends AbstractChillVoter implements ProvideRoleHierar
|
||||
private readonly VoterHelperInterface $voterHelper;
|
||||
|
||||
public function __construct(
|
||||
VoterHelperFactoryInterface $voterHelperFactory
|
||||
VoterHelperFactoryInterface $voterHelperFactory,
|
||||
) {
|
||||
$this->voterHelper = $voterHelperFactory
|
||||
->generate(self::class)
|
||||
|
@ -47,7 +47,7 @@ class SendTestShortMessageOnCalendarCommand extends Command
|
||||
private readonly PhoneNumberHelperInterface $phoneNumberHelper,
|
||||
private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
|
||||
private readonly ShortMessageTransporterInterface $transporter,
|
||||
private readonly UserRepositoryInterface $userRepository
|
||||
private readonly UserRepositoryInterface $userRepository,
|
||||
) {
|
||||
parent::__construct('chill:calendar:test-send-short-message');
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class CalendarController extends AbstractController
|
||||
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
|
||||
private readonly UserRepositoryInterface $userRepository,
|
||||
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,
|
||||
#[ORM\ManyToOne(targetEntity: StoredObject::class, cascade: ['persist'])]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?StoredObject $storedObject
|
||||
private ?StoredObject $storedObject,
|
||||
) {
|
||||
$this->setCalendar($calendar);
|
||||
$this->datetimeVersion = $calendar->getDateTimeVersion();
|
||||
|
@ -26,7 +26,7 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
|
||||
public function __construct(
|
||||
private UserJobRepository $jobRepository,
|
||||
private TranslatableStringHelper $translatableStringHelper
|
||||
private TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -26,7 +26,7 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function __construct(
|
||||
private ScopeRepository $scopeRepository,
|
||||
private TranslatableStringHelper $translatableStringHelper
|
||||
private TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -28,7 +28,7 @@ final readonly class JobFilter implements FilterInterface
|
||||
|
||||
public function __construct(
|
||||
private TranslatableStringHelper $translatableStringHelper,
|
||||
private UserJobRepositoryInterface $userJobRepository
|
||||
private UserJobRepositoryInterface $userJobRepository,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -30,7 +30,7 @@ class ScopeFilter implements FilterInterface
|
||||
public function __construct(
|
||||
protected TranslatorInterface $translator,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
private readonly ScopeRepositoryInterface $scopeRepository
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
|
@ -37,7 +37,7 @@ class CalendarType extends AbstractType
|
||||
private readonly IdToUsersDataTransformer $idToUsersDataTransformer,
|
||||
private readonly IdToLocationDataTransformer $idToLocationDataTransformer,
|
||||
private readonly ThirdPartiesToIdDataTransformer $partiesToIdDataTransformer,
|
||||
private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer
|
||||
private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
|
@ -46,7 +46,7 @@ class CalendarMessage
|
||||
public function __construct(
|
||||
Calendar $calendar,
|
||||
private readonly string $action,
|
||||
User $byUser
|
||||
User $byUser,
|
||||
) {
|
||||
$this->calendarId = $calendar->getId();
|
||||
$this->byUserId = $byUser->getId();
|
||||
|
@ -59,7 +59,7 @@ final readonly class MSUserAbsenceReader implements MSUserAbsenceReaderInterface
|
||||
'alwaysEnabled' => true,
|
||||
'scheduled' => RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledStartDateTime']['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,
|
||||
int $expiration,
|
||||
?string $id = null,
|
||||
?string $secret = null
|
||||
?string $secret = null,
|
||||
): void {
|
||||
$user->setAttributeByDomain(self::METADATA_KEY, self::EXPIRATION_SUBSCRIPTION_EVENT, $expiration);
|
||||
|
||||
|
@ -57,7 +57,7 @@ class RemoteEventConverter
|
||||
private readonly LocationConverter $locationConverter,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly PersonRenderInterface $personRender,
|
||||
private readonly TranslatorInterface $translator
|
||||
private readonly TranslatorInterface $translator,
|
||||
) {
|
||||
$this->defaultDateTimeZone = (new \DateTimeImmutable())->getTimezone();
|
||||
$this->remoteDateTimeZone = self::getRemoteTimeZone();
|
||||
|
@ -351,7 +351,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
||||
[
|
||||
'id' => $id,
|
||||
'lastModifiedDateTime' => $lastModified,
|
||||
'changeKey' => $changeKey
|
||||
'changeKey' => $changeKey,
|
||||
] = $this->createOnRemote($eventData, $calendar->getMainUser(), 'calendar_'.$calendar->getId());
|
||||
|
||||
if (null === $id) {
|
||||
@ -427,7 +427,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
||||
[
|
||||
'id' => $id,
|
||||
'lastModifiedDateTime' => $lastModified,
|
||||
'changeKey' => $changeKey
|
||||
'changeKey' => $changeKey,
|
||||
] = $this->createOnRemote(
|
||||
$eventData,
|
||||
$calendarRange->getUser(),
|
||||
@ -564,7 +564,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
||||
[
|
||||
'id' => $id,
|
||||
'lastModifiedDateTime' => $lastModified,
|
||||
'changeKey' => $changeKey
|
||||
'changeKey' => $changeKey,
|
||||
] = $this->patchOnRemote(
|
||||
$calendar->getRemoteId(),
|
||||
$eventData,
|
||||
|
@ -33,6 +33,6 @@ class RemoteEvent
|
||||
#[Serializer\Groups(['read'])]
|
||||
public \DateTimeImmutable $endDate,
|
||||
#[Serializer\Groups(['read'])]
|
||||
public bool $isAllDay = false
|
||||
public bool $isAllDay = false,
|
||||
) {}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ class CalendarRangeRepository implements ObjectRepository
|
||||
\DateTimeImmutable $from,
|
||||
\DateTimeImmutable $to,
|
||||
?int $limit = null,
|
||||
?int $offset = null
|
||||
?int $offset = null,
|
||||
): array {
|
||||
$qb = $this->buildQueryAvailableRangesForUser($user, $from, $to);
|
||||
|
||||
|
@ -40,7 +40,7 @@ final readonly class CalendarContext implements CalendarContextInterface
|
||||
private PersonRepository $personRepository,
|
||||
private ThirdPartyRender $thirdPartyRender,
|
||||
private ThirdPartyRepository $thirdPartyRepository,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function adminFormReverseTransform(array $data): array
|
||||
|
@ -37,7 +37,7 @@ final readonly class AccompanyingPeriodCalendarGenericDocProvider implements Gen
|
||||
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private EntityManagerInterface $em
|
||||
private EntityManagerInterface $em,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
@ -36,7 +36,7 @@ final readonly class PersonCalendarGenericDocProvider implements GenericDocForPe
|
||||
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private EntityManagerInterface $em
|
||||
private EntityManagerInterface $em,
|
||||
) {}
|
||||
|
||||
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(
|
||||
string $classTransformer,
|
||||
string $objClass
|
||||
string $objClass,
|
||||
) {
|
||||
$transformer = $this->prophesize($classTransformer);
|
||||
$transformer->transform(Argument::type('array'))
|
||||
@ -195,7 +195,7 @@ final class CalendarTypeTest extends TypeTestCase
|
||||
|
||||
private function buildSingleToIdDataTransformer(
|
||||
string $classTransformer,
|
||||
string $class
|
||||
string $class,
|
||||
) {
|
||||
$transformer = $this->prophesize($classTransformer);
|
||||
$transformer->transform(Argument::type('object'))
|
||||
|
@ -203,7 +203,7 @@ final class CalendarContextTest extends TestCase
|
||||
|
||||
private function buildCalendarContext(
|
||||
?EntityManagerInterface $entityManager = null,
|
||||
?NormalizerInterface $normalizer = null
|
||||
?NormalizerInterface $normalizer = null,
|
||||
): CalendarContext {
|
||||
$baseContext = $this->prophesize(BaseContextData::class);
|
||||
$baseContext->getData(null)->willReturn(['base_context' => 'data']);
|
||||
|
@ -44,7 +44,7 @@ class CreateFieldsOnGroupCommand extends Command
|
||||
private readonly EntityManager $entityManager,
|
||||
private readonly ValidatorInterface $validator,
|
||||
private $availableLanguages,
|
||||
private $customizablesEntities
|
||||
private $customizablesEntities,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class CustomFieldsGroupController extends AbstractController
|
||||
public function __construct(
|
||||
private readonly CustomFieldProvider $customFieldProvider,
|
||||
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
|
||||
*/
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function allowOtherChoice(CustomField $cf)
|
||||
|
@ -44,7 +44,7 @@ class CustomFieldDate extends AbstractCustomField
|
||||
|
||||
public function __construct(
|
||||
private readonly Environment $templating,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
||||
|
@ -41,7 +41,7 @@ class CustomFieldNumber extends AbstractCustomField
|
||||
|
||||
public function __construct(
|
||||
private readonly Environment $templating,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
||||
|
@ -28,7 +28,7 @@ class CustomFieldText extends AbstractCustomField
|
||||
|
||||
public function __construct(
|
||||
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
|
||||
*/
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
||||
|
@ -26,7 +26,7 @@ class CustomFieldsGroupType extends AbstractType
|
||||
public function __construct(
|
||||
private readonly array $customizableEntities,
|
||||
// TODO : add comment about this variable
|
||||
private readonly TranslatorInterface $translator
|
||||
private readonly TranslatorInterface $translator,
|
||||
) {}
|
||||
|
||||
// TODO : details about the function
|
||||
|
@ -48,7 +48,7 @@ final class DocGeneratorTemplateController extends AbstractController
|
||||
private readonly PaginatorFactory $paginatorFactory,
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
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')]
|
||||
@ -56,7 +56,7 @@ final class DocGeneratorTemplateController extends AbstractController
|
||||
DocGeneratorTemplate $template,
|
||||
string $entityClassName,
|
||||
int $entityId,
|
||||
Request $request
|
||||
Request $request,
|
||||
): Response {
|
||||
return $this->generateDocFromTemplate(
|
||||
$template,
|
||||
@ -71,7 +71,7 @@ final class DocGeneratorTemplateController extends AbstractController
|
||||
DocGeneratorTemplate $template,
|
||||
string $entityClassName,
|
||||
int $entityId,
|
||||
Request $request
|
||||
Request $request,
|
||||
): Response {
|
||||
return $this->generateDocFromTemplate(
|
||||
$template,
|
||||
@ -137,7 +137,7 @@ final class DocGeneratorTemplateController extends AbstractController
|
||||
DocGeneratorTemplate $template,
|
||||
int $entityId,
|
||||
Request $request,
|
||||
bool $isTest
|
||||
bool $isTest,
|
||||
): Response {
|
||||
try {
|
||||
$context = $this->contextManager->getContextByDocGeneratorTemplate($template);
|
||||
|
@ -54,12 +54,15 @@ class LoadDocGeneratorTemplate extends AbstractFixture
|
||||
];
|
||||
|
||||
foreach ($templates as $template) {
|
||||
$newStoredObj = (new StoredObject())
|
||||
->setFilename($template['file']['filename'])
|
||||
->setKeyInfos(json_decode($template['file']['key'], true))
|
||||
->setIv(json_decode($template['file']['iv'], true))
|
||||
$newStoredObj = (new StoredObject());
|
||||
|
||||
$newStoredObj
|
||||
->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);
|
||||
|
||||
|
@ -28,7 +28,7 @@ final readonly class RelatorioDriver implements DriverInterface
|
||||
public function __construct(
|
||||
private HttpClientInterface $client,
|
||||
ParameterBagInterface $parameterBag,
|
||||
private LoggerInterface $logger
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
|
||||
|
||||
public function __construct(
|
||||
private readonly ClassMetadataFactoryInterface $classMetadataFactory,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {
|
||||
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class Generator implements GeneratorInterface
|
||||
private readonly DriverInterface $driver,
|
||||
private readonly ManagerRegistry $objectManagerRegistry,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly StoredObjectManagerInterface $storedObjectManager
|
||||
private readonly StoredObjectManagerInterface $storedObjectManager,
|
||||
) {}
|
||||
|
||||
public function generateDataDump(
|
||||
@ -134,13 +134,11 @@ class Generator implements GeneratorInterface
|
||||
$content = Yaml::dump($data, 6);
|
||||
/* @var StoredObject $destinationStoredObject */
|
||||
$destinationStoredObject
|
||||
->setType('application/yaml')
|
||||
->setFilename(sprintf('%s_yaml', uniqid('doc_', true)))
|
||||
->setStatus(StoredObject::STATUS_READY)
|
||||
;
|
||||
|
||||
try {
|
||||
$this->storedObjectManager->write($destinationStoredObject, $content);
|
||||
$this->storedObjectManager->write($destinationStoredObject, $content, 'application/yaml');
|
||||
} catch (StoredObjectManagerException $e) {
|
||||
$destinationStoredObject->addGenerationErrors($e->getMessage());
|
||||
|
||||
@ -174,13 +172,11 @@ class Generator implements GeneratorInterface
|
||||
|
||||
/* @var StoredObject $destinationStoredObject */
|
||||
$destinationStoredObject
|
||||
->setType($template->getFile()->getType())
|
||||
->setFilename(sprintf('%s_odt', uniqid('doc_', true)))
|
||||
->setStatus(StoredObject::STATUS_READY)
|
||||
;
|
||||
|
||||
try {
|
||||
$this->storedObjectManager->write($destinationStoredObject, $generatedResource);
|
||||
$this->storedObjectManager->write($destinationStoredObject, $generatedResource, $template->getFile()->getType());
|
||||
} catch (StoredObjectManagerException $e) {
|
||||
$destinationStoredObject->addGenerationErrors($e->getMessage());
|
||||
|
||||
|
@ -39,7 +39,7 @@ final readonly class OnGenerationFails implements EventSubscriberInterface
|
||||
private MailerInterface $mailer,
|
||||
private StoredObjectRepositoryInterface $storedObjectRepository,
|
||||
private TranslatorInterface $translator,
|
||||
private UserRepositoryInterface $userRepository
|
||||
private UserRepositoryInterface $userRepository,
|
||||
) {}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
|
@ -56,7 +56,7 @@ final class BaseContextDataTest extends KernelTestCase
|
||||
}
|
||||
|
||||
private function buildBaseContext(
|
||||
?NormalizerInterface $normalizer = null
|
||||
?NormalizerInterface $normalizer = null,
|
||||
): BaseContextData {
|
||||
return new BaseContextData(
|
||||
$normalizer ?? self::getContainer()->get(NormalizerInterface::class)
|
||||
|
@ -58,6 +58,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
|
||||
?int $expire_delay = null,
|
||||
?int $submit_delay = null,
|
||||
int $max_file_count = 1,
|
||||
?string $object_name = null,
|
||||
): SignedUrlPost {
|
||||
$delay = $expire_delay ?? $this->max_expire_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'));
|
||||
|
||||
$object_name = $this->generateObjectName();
|
||||
if (null === $object_name) {
|
||||
$object_name = $this->generateObjectName();
|
||||
}
|
||||
|
||||
$g = new SignedUrlPost(
|
||||
$url = $this->generateUrl($object_name),
|
||||
@ -141,7 +144,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
|
||||
{
|
||||
return match (str_ends_with($this->base_url, '/')) {
|
||||
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);
|
||||
}
|
||||
|
||||
private function generateSignature($method, $url, \DateTimeImmutable $expires)
|
||||
private function generateSignature(string $method, $url, \DateTimeImmutable $expires)
|
||||
{
|
||||
if ('POST' === $method) {
|
||||
return $this->generateSignaturePost($url, $expires);
|
||||
}
|
||||
|
||||
$path = \parse_url((string) $url, PHP_URL_PATH);
|
||||
|
||||
$body = sprintf(
|
||||
"%s\n%s\n%s",
|
||||
$method,
|
||||
strtoupper($method),
|
||||
$expires->format('U'),
|
||||
$path
|
||||
)
|
||||
;
|
||||
);
|
||||
|
||||
$this->logger->debug(
|
||||
'generate signature GET',
|
||||
|
@ -16,7 +16,8 @@ interface TempUrlGeneratorInterface
|
||||
public function generatePost(
|
||||
?int $expire_delay = null,
|
||||
?int $submit_delay = null,
|
||||
int $max_file_count = 1
|
||||
int $max_file_count = 1,
|
||||
?string $object_name = null,
|
||||
): SignedUrlPost;
|
||||
|
||||
public function generate(string $method, string $object_name, ?int $expire_delay = null): SignedUrl;
|
||||
|
@ -25,7 +25,7 @@ class AsyncUploadExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TempUrlGeneratorInterface $tempUrlGenerator,
|
||||
private readonly UrlGeneratorInterface $routingUrlGenerator
|
||||
private readonly UrlGeneratorInterface $routingUrlGenerator,
|
||||
) {}
|
||||
|
||||
public function getFilters()
|
||||
|
@ -11,9 +11,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\DocStoreBundle\Controller;
|
||||
|
||||
use Chill\DocStoreBundle\AsyncUpload\Exception\TempUrlGeneratorException;
|
||||
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 Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@ -30,62 +32,84 @@ final readonly class AsyncUploadController
|
||||
private TempUrlGeneratorInterface $tempUrlGenerator,
|
||||
private SerializerInterface $serializer,
|
||||
private Security $security,
|
||||
private LoggerInterface $logger,
|
||||
private LoggerInterface $chillLogger,
|
||||
) {}
|
||||
|
||||
#[Route(path: '/asyncupload/temp_url/generate/{method}', name: 'async_upload.generate_url')]
|
||||
public function getSignedUrl(string $method, Request $request): JsonResponse
|
||||
#[Route(path: '/api/1.0/doc-store/async-upload/temp_url/{uuid}/generate/post', name: 'chill_docstore_asyncupload_getsignedurlpost')]
|
||||
public function getSignedUrlPost(Request $request, StoredObject $storedObject): JsonResponse
|
||||
{
|
||||
try {
|
||||
switch (strtolower($method)) {
|
||||
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 (!$this->security->isGranted(StoredObjectRoleEnum::EDIT->value, $storedObject)) {
|
||||
throw new AccessDeniedHttpException('not able to edit the given stored object');
|
||||
}
|
||||
|
||||
if (null === $object_name) {
|
||||
return (new JsonResponse((object) [
|
||||
'message' => 'the object_name is null',
|
||||
]))
|
||||
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
|
||||
}
|
||||
$p = $this->tempUrlGenerator->generate(
|
||||
$method,
|
||||
$object_name,
|
||||
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return (new JsonResponse((object) ['message' => 'the method '
|
||||
."{$method} is not valid"]))
|
||||
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
|
||||
// we create a dummy version, to generate a filename
|
||||
$version = $storedObject->registerVersion();
|
||||
|
||||
$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,
|
||||
object_name: $version->getFilename()
|
||||
);
|
||||
|
||||
$this->chillLogger->notice('[Privacy Event] a request to upload a document has been generated', [
|
||||
'doc_uuid' => $storedObject->getUuid(),
|
||||
]);
|
||||
|
||||
return new JsonResponse(
|
||||
$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) {
|
||||
$this->logger->warning('The client requested a temp url'
|
||||
.' 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);
|
||||
} else {
|
||||
$filename = $storedObject->getCurrentVersion()->getFilename();
|
||||
}
|
||||
|
||||
if (!$this->security->isGranted(AsyncUploadVoter::GENERATE_SIGNATURE, $p)) {
|
||||
throw new AccessDeniedHttpException('not allowed to generate this signature');
|
||||
}
|
||||
$p = $this->tempUrlGenerator->generate(
|
||||
$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(
|
||||
$this->serializer->serialize($p, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
||||
|
@ -35,7 +35,7 @@ class DocumentAccompanyingCourseController extends AbstractController
|
||||
protected TranslatorInterface $translator,
|
||||
protected EventDispatcherInterface $eventDispatcher,
|
||||
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')]
|
||||
|
@ -44,7 +44,7 @@ class DocumentPersonController extends AbstractController
|
||||
protected AuthorizationHelper $authorizationHelper,
|
||||
protected PDFSignatureZoneParser $PDFSignatureZoneParser,
|
||||
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')]
|
||||
|
@ -11,6 +11,46 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\DocStoreBundle\Controller;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
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\Security\Authorization\StoredObjectRoleEnum;
|
||||
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
@ -42,6 +43,7 @@ final readonly class WebdavController
|
||||
private \Twig\Environment $engine,
|
||||
private StoredObjectManagerInterface $storedObjectManager,
|
||||
private Security $security,
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {
|
||||
$this->requestAnalyzer = new PropfindRequestAnalyzer();
|
||||
}
|
||||
@ -201,6 +203,8 @@ final readonly class WebdavController
|
||||
|
||||
$this->storedObjectManager->write($storedObject, $request->getContent());
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new DavResponse('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\DocStoreBundle\DependencyInjection;
|
||||
|
||||
use Chill\DocStoreBundle\Controller\StoredObjectApiController;
|
||||
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface;
|
||||
@ -19,7 +18,6 @@ use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Loader;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
|
||||
/**
|
||||
@ -53,29 +51,6 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf
|
||||
$this->prependRoute($container);
|
||||
$this->prependAuthorization($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)
|
||||
|
@ -42,7 +42,7 @@ class DocumentCategory
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, name: 'id_inside_bundle')]
|
||||
private $idInsideBundle
|
||||
private $idInsideBundle,
|
||||
) {}
|
||||
|
||||
public function getBundleId() // ::class BundleClass (FQDN)
|
||||
|
@ -16,10 +16,14 @@ use ChampsLibres\WopiLib\Contract\Entity\Document;
|
||||
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use Random\RandomException;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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\Table('chill_doc.stored_object')]
|
||||
#[AsyncFileExists(message: 'The file is not stored properly')]
|
||||
#[ORM\Table('stored_object', schema: 'chill_doc')]
|
||||
class StoredObject implements Document, TrackCreationInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
final public const STATUS_EMPTY = 'empty';
|
||||
final public const STATUS_READY = 'ready';
|
||||
final public const STATUS_PENDING = 'pending';
|
||||
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')]
|
||||
private array $datas = [];
|
||||
|
||||
#[Serializer\Groups(['write'])]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT)]
|
||||
private string $filename = '';
|
||||
/**
|
||||
* the prefix of each version.
|
||||
*/
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||
private string $prefix = '';
|
||||
|
||||
#[Serializer\Groups(['write'])]
|
||||
#[ORM\Id]
|
||||
@ -53,25 +62,10 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
#[Serializer\Groups(['write'])]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, name: 'iv')]
|
||||
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')]
|
||||
#[ORM\Column(name: 'title', type: \Doctrine\DBAL\Types\Types::TEXT, options: ['default' => ''])]
|
||||
private string $title = '';
|
||||
|
||||
#[Serializer\Groups(['write'])]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, name: 'type', options: ['default' => ''])]
|
||||
private string $type = '';
|
||||
|
||||
#[Serializer\Groups(['write'])]
|
||||
#[ORM\Column(type: 'uuid', unique: true)]
|
||||
private UuidInterface $uuid;
|
||||
@ -94,14 +88,22 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||
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
|
||||
*/
|
||||
public function __construct(
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, options: ['default' => 'ready'])]
|
||||
private string $status = 'ready'
|
||||
private string $status = 'empty',
|
||||
) {
|
||||
$this->uuid = Uuid::uuid4();
|
||||
$this->versions = new ArrayCollection();
|
||||
$this->prefix = self::generatePrefix();
|
||||
}
|
||||
|
||||
public function addGenerationTrial(): self
|
||||
@ -125,14 +127,34 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
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
|
||||
{
|
||||
return $this->datas;
|
||||
}
|
||||
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
public function getFilename(): string
|
||||
{
|
||||
return $this->filename;
|
||||
return $this->getCurrentVersion()?->getFilename() ?? '';
|
||||
}
|
||||
|
||||
public function getGenerationTrialsCounter(): int
|
||||
@ -145,14 +167,17 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
public function getIv(): array
|
||||
{
|
||||
return $this->iv;
|
||||
return $this->getCurrentVersion()?->getIv() ?? [];
|
||||
}
|
||||
|
||||
public function getKeyInfos(): array
|
||||
{
|
||||
return $this->keyInfos;
|
||||
return $this->getCurrentVersion()?->getKeyInfos() ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,14 +196,14 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getType()
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
return $this->getCurrentVersion()?->getType() ?? '';
|
||||
}
|
||||
|
||||
public function getUuid(): UuidInterface
|
||||
@ -209,27 +234,6 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
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
|
||||
*/
|
||||
@ -247,18 +251,16 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType(?string $type): self
|
||||
{
|
||||
$this->type = (string) $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTemplate(): ?DocGeneratorTemplate
|
||||
{
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
public function getVersions(): Collection
|
||||
{
|
||||
return $this->versions;
|
||||
}
|
||||
|
||||
public function hasTemplate(): bool
|
||||
{
|
||||
return null !== $this->template;
|
||||
@ -314,18 +316,65 @@ class StoredObject implements Document, TrackCreationInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function saveHistory(): void
|
||||
{
|
||||
if ('' === $this->getFilename()) {
|
||||
return;
|
||||
public function registerVersion(
|
||||
array $iv = [],
|
||||
array $keyInfos = [],
|
||||
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'][] = [
|
||||
'filename' => $this->getFilename(),
|
||||
'iv' => $this->getIv(),
|
||||
'key_infos' => $this->getKeyInfos(),
|
||||
'type' => $this->getType(),
|
||||
'before' => (new \DateTimeImmutable('now'))->getTimestamp(),
|
||||
];
|
||||
return $version;
|
||||
}
|
||||
|
||||
public function removeVersion(StoredObjectVersion $storedObjectVersion): void
|
||||
{
|
||||
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
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
|
@ -55,16 +55,8 @@ class StoredObjectDataMapper implements DataMapperInterface
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var StoredObject $viewData */
|
||||
if ($viewData->getFilename() !== $forms['stored_object']->getData()['filename']) {
|
||||
// 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']);
|
||||
/* @var StoredObject $viewData */
|
||||
$viewData = $forms['stored_object']->getData();
|
||||
|
||||
if (array_key_exists('title', $forms)) {
|
||||
$viewData->setTitle($forms['title']->getData());
|
||||
|
@ -12,7 +12,6 @@ declare(strict_types=1);
|
||||
namespace Chill\DocStoreBundle\Form\DataTransformer;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectNormalizer;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
@ -20,7 +19,7 @@ use Symfony\Component\Serializer\SerializerInterface;
|
||||
class StoredObjectDataTransformer implements DataTransformerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly SerializerInterface $serializer
|
||||
private readonly SerializerInterface $serializer,
|
||||
) {}
|
||||
|
||||
public function transform(mixed $value): mixed
|
||||
@ -30,11 +29,7 @@ class StoredObjectDataTransformer implements DataTransformerInterface
|
||||
}
|
||||
|
||||
if ($value instanceof StoredObject) {
|
||||
return $this->serializer->serialize($value, 'json', [
|
||||
'groups' => [
|
||||
StoredObjectNormalizer::ADD_DAV_EDIT_LINK_CONTEXT,
|
||||
],
|
||||
]);
|
||||
return $this->serializer->serialize($value, 'json');
|
||||
}
|
||||
|
||||
throw new UnexpectedTypeException($value, StoredObject::class);
|
||||
@ -46,6 +41,6 @@ class StoredObjectDataTransformer implements DataTransformerInterface
|
||||
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(
|
||||
FormView $view,
|
||||
FormInterface $form,
|
||||
array $options
|
||||
array $options,
|
||||
) {
|
||||
$view->vars['attr']['data-async-file-upload'] = true;
|
||||
$view->vars['attr']['data-generate-temp-url-post'] = $this
|
||||
|
@ -20,7 +20,7 @@ interface GenericDocForAccompanyingPeriodProviderInterface
|
||||
?\DateTimeImmutable $startDate = null,
|
||||
?\DateTimeImmutable $endDate = null,
|
||||
?string $content = null,
|
||||
?string $origin = null
|
||||
?string $origin = null,
|
||||
): FetchQueryInterface;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ interface GenericDocForPersonProviderInterface
|
||||
?\DateTimeImmutable $startDate = null,
|
||||
?\DateTimeImmutable $endDate = null,
|
||||
?string $content = null,
|
||||
?string $origin = null
|
||||
?string $origin = null,
|
||||
): FetchQueryInterface;
|
||||
|
||||
/**
|
||||
|
@ -46,7 +46,7 @@ final readonly class Manager
|
||||
?\DateTimeImmutable $startDate = null,
|
||||
?\DateTimeImmutable $endDate = null,
|
||||
?string $content = null,
|
||||
array $places = []
|
||||
array $places = [],
|
||||
): int {
|
||||
['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 $endDate = null,
|
||||
?string $content = null,
|
||||
array $places = []
|
||||
array $places = [],
|
||||
): int {
|
||||
['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 $endDate = null,
|
||||
?string $content = null,
|
||||
array $places = []
|
||||
array $places = [],
|
||||
): iterable {
|
||||
['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 $endDate = null,
|
||||
?string $content = null,
|
||||
array $places = []
|
||||
array $places = [],
|
||||
): iterable {
|
||||
['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 $endDate = null,
|
||||
?string $content = null,
|
||||
?string $origin = null
|
||||
?string $origin = null,
|
||||
): FetchQueryInterface {
|
||||
return $this->personDocumentACLAwareRepository->buildFetchQueryForPerson(
|
||||
$person,
|
||||
|
@ -31,13 +31,13 @@ interface PersonDocumentACLAwareRepositoryInterface
|
||||
Person $person,
|
||||
?\DateTimeImmutable $startDate = null,
|
||||
?\DateTimeImmutable $endDate = null,
|
||||
?string $content = null
|
||||
?string $content = null,
|
||||
): FetchQueryInterface;
|
||||
|
||||
public function buildFetchQueryForAccompanyingPeriod(
|
||||
AccompanyingPeriod $period,
|
||||
?\DateTimeImmutable $startDate = null,
|
||||
?\DateTimeImmutable $endDate = null,
|
||||
?string $content = null
|
||||
?string $content = null,
|
||||
): FetchQueryInterface;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ readonly class PersonDocumentRepository implements ObjectRepository, AssociatedE
|
||||
private EntityRepository $repository;
|
||||
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {
|
||||
$this->repository = $this->entityManager->getRepository($this->getClassName());
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace Chill\DocStoreBundle\Repository;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
|
||||
final readonly class StoredObjectRepository implements StoredObjectRepositoryInterface
|
||||
{
|
||||
@ -53,6 +54,21 @@ final readonly class StoredObjectRepository implements StoredObjectRepositoryInt
|
||||
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
|
||||
{
|
||||
return StoredObject::class;
|
||||
|
@ -17,4 +17,12 @@ use Doctrine\Persistence\ObjectRepository;
|
||||
/**
|
||||
* @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 {PostStoreObjectSignature} from "../../types";
|
||||
import {PostStoreObjectSignature, StoredObject} from "../../types";
|
||||
|
||||
const algo = 'AES-CBC';
|
||||
|
||||
@ -21,11 +21,22 @@ const createFilename = (): string => {
|
||||
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();
|
||||
params.append('expires_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 filename = asyncData.prefix + suffix;
|
||||
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]> => {
|
||||
console.log('encrypt', originalFile);
|
||||
const iv = crypto.getRandomValues(new Uint8Array(16));
|
||||
const key = await window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ]);
|
||||
const exportedKey = await window.crypto.subtle.exportKey('jwk', key);
|
@ -1,7 +1,7 @@
|
||||
import {CollectionEventPayload} from "../../../../../ChillMainBundle/Resources/public/module/collection";
|
||||
import {createApp} from "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";
|
||||
const i18n = _createI18n({});
|
||||
|
||||
@ -30,15 +30,17 @@ const startApp = (divElement: HTMLDivElement, collectionEntry: null|HTMLLIElemen
|
||||
DropFileWidget,
|
||||
},
|
||||
methods: {
|
||||
addDocument: function(object: StoredObjectCreated): void {
|
||||
console.log('object added', object);
|
||||
this.$data.existingDoc = object;
|
||||
input_stored_object.value = JSON.stringify(object);
|
||||
addDocument: function({stored_object, stored_object_version}: {stored_object: StoredObject, stored_object_version: StoredObjectVersion}): void {
|
||||
console.log('object added', stored_object);
|
||||
console.log('version added', stored_object_version);
|
||||
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 {
|
||||
console.log('catch remove document', object);
|
||||
input_stored_object.value = "";
|
||||
this.$data.existingDoc = null;
|
||||
this.$data.existingDoc = undefined;
|
||||
console.log('collectionEntry', 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 {
|
||||
id: number,
|
||||
|
||||
/**
|
||||
* filename of the object in the object storage
|
||||
*/
|
||||
filename: string,
|
||||
creationDate: DateTime,
|
||||
datas: object,
|
||||
iv: number[],
|
||||
keyInfos: object,
|
||||
title: string,
|
||||
type: string,
|
||||
uuid: string,
|
||||
status: StoredObjectStatus,
|
||||
id: number,
|
||||
title: string|null,
|
||||
uuid: string,
|
||||
prefix: string,
|
||||
status: StoredObjectStatus,
|
||||
currentVersion: null|StoredObjectVersionCreated|StoredObjectVersionPersisted,
|
||||
totalVersions: number,
|
||||
datas: object,
|
||||
/** @deprecated */
|
||||
creationDate: DateTime,
|
||||
createdAt: DateTime|null,
|
||||
createdBy: User|null,
|
||||
_permissions: {
|
||||
canEdit: boolean,
|
||||
canSee: boolean,
|
||||
},
|
||||
_links?: {
|
||||
dav_link?: {
|
||||
href: string
|
||||
expiration: number
|
||||
},
|
||||
}
|
||||
dav_link?: {
|
||||
href: string
|
||||
expiration: number
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export interface StoredObjectCreated {
|
||||
status: "stored_object_created",
|
||||
filename: string,
|
||||
iv: Uint8Array,
|
||||
keyInfos: object,
|
||||
type: string,
|
||||
export interface StoredObjectVersion {
|
||||
/**
|
||||
* filename of the object in the object storage
|
||||
*/
|
||||
filename: 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 {
|
||||
@ -82,4 +97,4 @@ export interface Signature {
|
||||
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