Compare commits
4 Commits
4c45d4da77
...
953a98afdc
| Author | SHA1 | Date | |
|---|---|---|---|
| 953a98afdc | |||
| ce246669d9 | |||
| 6b040ba19e | |||
| 33a06c6509 |
@@ -50,11 +50,7 @@ class TransferController extends ControllerAbstract
|
||||
*
|
||||
* GET /download/entity/{provider}/{service}/{collection}/{identifier}
|
||||
*/
|
||||
#[AuthenticatedRoute(
|
||||
'/download/entity/{provider}/{service}/{collection}/{identifier}',
|
||||
name: 'document_manager.download.entity',
|
||||
methods: ['GET']
|
||||
)]
|
||||
#[AuthenticatedRoute('/download/entity/{provider}/{service}/{collection}/{identifier}', name: 'document_manager.download.entity', methods: ['GET'])]
|
||||
public function downloadEntity(string $provider, string $service, string $collection, string $identifier): Response {
|
||||
$tenantId = $this->tenantContext->identifier();
|
||||
$userId = $this->identityContext->identifier();
|
||||
@@ -124,16 +120,44 @@ class TransferController extends ControllerAbstract
|
||||
}
|
||||
}
|
||||
|
||||
#[AuthenticatedRoute('/preview/entity/{provider}/{service}/{collection}/{identifier}', name: 'document_manager.preview.entity', methods: ['GET'])]
|
||||
public function previewEntity(string $provider, string $service, string $collection, string $identifier, string $variant = 'inline'): Response {
|
||||
try {
|
||||
$preview = $this->manager->entityPreview(
|
||||
$this->tenantContext->identifier(),
|
||||
$this->identityContext->identifier(),
|
||||
new EntityIdentifier($provider, $service, $collection, $identifier),
|
||||
$variant,
|
||||
);
|
||||
if ($preview === null) {
|
||||
return new JsonResponse([
|
||||
'status' => 'error',
|
||||
'error' => ['code' => 404, 'message' => 'Preview unavailable'],
|
||||
], Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response = new StreamedResponse($preview->resource->stream());
|
||||
$response->headers->set('Content-Type', $preview->resource->mimeType());
|
||||
$response->headers->set('Content-Length', (string) $preview->size);
|
||||
$response->headers->set('ETag', '"' . trim($preview->etag, '"') . '"');
|
||||
$response->headers->set('Cache-Control', 'private, max-age=3600');
|
||||
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
||||
return $response;
|
||||
} catch (Throwable $throwable) {
|
||||
$this->logger->error('Document preview failed', ['exception' => $throwable]);
|
||||
return new JsonResponse([
|
||||
'status' => 'error',
|
||||
'error' => ['code' => 500, 'message' => 'Preview unavailable'],
|
||||
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download multiple files as a ZIP archive
|
||||
*
|
||||
* GET /download/archive?provider=...&service=...&ids[]=...&ids[]=...
|
||||
*/
|
||||
#[AuthenticatedRoute(
|
||||
'/download/archive',
|
||||
name: 'manager.download.archive',
|
||||
methods: ['GET']
|
||||
)]
|
||||
#[AuthenticatedRoute('/download/archive', name: 'manager.download.archive', methods: ['GET'])]
|
||||
public function downloadArchive(string $provider, string $service, array $ids = [], ?string $collection = null, string $name = 'download'): Response {
|
||||
$tenantId = $this->tenantContext->identifier();
|
||||
$userId = $this->identityContext->identifier();
|
||||
@@ -215,11 +239,7 @@ class TransferController extends ControllerAbstract
|
||||
*
|
||||
* GET /download/collection/{provider}/{service}/{identifier}
|
||||
*/
|
||||
#[AuthenticatedRoute(
|
||||
'/download/collection/{provider}/{service}/{identifier}',
|
||||
name: 'manager.download.collection',
|
||||
methods: ['GET']
|
||||
)]
|
||||
#[AuthenticatedRoute('/download/collection/{provider}/{service}/{identifier}', name: 'manager.download.collection', methods: ['GET'])]
|
||||
public function downloadCollection(string $provider, string $service, string $identifier): Response {
|
||||
$tenantId = $this->tenantContext->identifier();
|
||||
$userId = $this->identityContext->identifier();
|
||||
|
||||
@@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace KTXM\DocumentsManager;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use KTXC\Preview\Preview;
|
||||
use KTXC\Preview\PreviewManager;
|
||||
use KTXC\Resource\ProviderManager;
|
||||
use KTXF\Documents\Collection\CollectionBaseInterface;
|
||||
use KTXF\Documents\Collection\CollectionPropertiesMutableInterface;
|
||||
@@ -18,6 +21,8 @@ use KTXF\Documents\Service\ServiceCollectionMutableInterface;
|
||||
use KTXF\Documents\Service\ServiceEntityMutableInterface;
|
||||
use KTXF\Documents\Service\ServiceMutableInterface;
|
||||
use KTXF\Resource\Filter\IFilter;
|
||||
use KTXF\Preview\PreviewSource;
|
||||
use KTXF\Resource\BinaryResource;
|
||||
use KTXF\Resource\Identifier\CollectionIdentifier;
|
||||
use KTXF\Resource\Identifier\EntityIdentifier;
|
||||
use KTXF\Resource\Identifier\ResourceIdentifiers;
|
||||
@@ -37,6 +42,7 @@ class Manager {
|
||||
public function __construct(
|
||||
private LoggerInterface $logger,
|
||||
private ProviderManager $providerManager,
|
||||
private PreviewManager $previewManager,
|
||||
) { }
|
||||
|
||||
/**
|
||||
@@ -1342,4 +1348,62 @@ class Manager {
|
||||
return $this->serviceForWrite($tenantId, $userId, $target)->entityWriteChunk($target, $offset, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an authorized entity and lazily generate or retrieve its preview.
|
||||
*/
|
||||
public function entityPreview(string $tenantId, string $userId, EntityIdentifier $target, string $variant = 'inline'): ?Preview {
|
||||
$entities = $this->entityFetchBulk($tenantId, $userId, $target);
|
||||
$entity = $entities[$target->entity()] ?? reset($entities);
|
||||
if (!$entity instanceof EntityBaseInterface) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$properties = $entity->getProperties();
|
||||
$mimeType = $properties->getMime();
|
||||
$size = $properties->size();
|
||||
$signature = $entity->signature() ?? hash('sha256', implode('|', [
|
||||
(string) $target,
|
||||
$entity->modified()?->format('U.u') ?? '',
|
||||
(string) $size,
|
||||
$mimeType,
|
||||
]));
|
||||
|
||||
$source = new PreviewSource(
|
||||
sourceType: 'document',
|
||||
identity: (string) $target,
|
||||
signature: $signature,
|
||||
mimeType: $mimeType,
|
||||
size: $size,
|
||||
resourceFactory: function () use ($tenantId, $userId, $target, $properties, $mimeType): BinaryResource {
|
||||
$stream = $this->entityReadStream($tenantId, $userId, $target);
|
||||
if (!is_resource($stream)) {
|
||||
throw new RuntimeException('Document content is unavailable');
|
||||
}
|
||||
|
||||
return new BinaryResource(
|
||||
$properties->getLabel(),
|
||||
$mimeType,
|
||||
(static function () use ($stream): \Generator {
|
||||
try {
|
||||
while (!feof($stream)) {
|
||||
$chunk = fread($stream, 65536);
|
||||
if ($chunk === false) {
|
||||
throw new RuntimeException('Failed to read document content');
|
||||
}
|
||||
if ($chunk !== '') {
|
||||
yield $chunk;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
fclose($stream);
|
||||
}
|
||||
})(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return $this->previewManager->fetch($tenantId, $source, $variant);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Generated
+62
-62
@@ -95,12 +95,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
||||
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
||||
"version": "7.29.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
|
||||
"integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.29.7"
|
||||
"@babel/types": "^7.29.8"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -110,9 +110,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
||||
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
||||
"version": "7.29.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
|
||||
"integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.29.7",
|
||||
@@ -918,39 +918,39 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.40.tgz",
|
||||
"integrity": "sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.42.tgz",
|
||||
"integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@vue/shared": "3.5.40",
|
||||
"@babel/parser": "^7.29.8",
|
||||
"@vue/shared": "3.5.42",
|
||||
"entities": "^7.0.1",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz",
|
||||
"integrity": "sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz",
|
||||
"integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
"@vue/compiler-core": "3.5.42",
|
||||
"@vue/shared": "3.5.42"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.40.tgz",
|
||||
"integrity": "sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz",
|
||||
"integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@vue/compiler-core": "3.5.40",
|
||||
"@vue/compiler-dom": "3.5.40",
|
||||
"@vue/compiler-ssr": "3.5.40",
|
||||
"@vue/shared": "3.5.40",
|
||||
"@babel/parser": "^7.29.8",
|
||||
"@vue/compiler-core": "3.5.42",
|
||||
"@vue/compiler-dom": "3.5.42",
|
||||
"@vue/compiler-ssr": "3.5.42",
|
||||
"@vue/shared": "3.5.42",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.21",
|
||||
"postcss": "^8.5.19",
|
||||
@@ -958,13 +958,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.40.tgz",
|
||||
"integrity": "sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz",
|
||||
"integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
"@vue/compiler-dom": "3.5.42",
|
||||
"@vue/shared": "3.5.42"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
@@ -1014,51 +1014,51 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.40.tgz",
|
||||
"integrity": "sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.42.tgz",
|
||||
"integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.40"
|
||||
"@vue/shared": "3.5.42"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.40.tgz",
|
||||
"integrity": "sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.42.tgz",
|
||||
"integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
"@vue/reactivity": "3.5.42",
|
||||
"@vue/shared": "3.5.42"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.40.tgz",
|
||||
"integrity": "sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz",
|
||||
"integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.40",
|
||||
"@vue/runtime-core": "3.5.40",
|
||||
"@vue/shared": "3.5.40",
|
||||
"@vue/reactivity": "3.5.42",
|
||||
"@vue/runtime-core": "3.5.42",
|
||||
"@vue/shared": "3.5.42",
|
||||
"csstype": "^3.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.40.tgz",
|
||||
"integrity": "sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.42.tgz",
|
||||
"integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.5.40",
|
||||
"@vue/runtime-dom": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
"@vue/compiler-ssr": "3.5.42",
|
||||
"@vue/runtime-dom": "3.5.42",
|
||||
"@vue/shared": "3.5.42"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.40.tgz",
|
||||
"integrity": "sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.42.tgz",
|
||||
"integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/tsconfig": {
|
||||
@@ -2320,16 +2320,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.40.tgz",
|
||||
"integrity": "sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==",
|
||||
"version": "3.5.42",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.42.tgz",
|
||||
"integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.40",
|
||||
"@vue/compiler-sfc": "3.5.40",
|
||||
"@vue/runtime-dom": "3.5.40",
|
||||
"@vue/server-renderer": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
"@vue/compiler-dom": "3.5.42",
|
||||
"@vue/compiler-sfc": "3.5.42",
|
||||
"@vue/runtime-dom": "3.5.42",
|
||||
"@vue/server-renderer": "3.5.42",
|
||||
"@vue/shared": "3.5.42"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
|
||||
@@ -203,6 +203,15 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
|
||||
return `${TRANSFER_BASE_URL}/download/entity/${encodeURIComponent(currentProviderId())}/${encodeURIComponent(currentServiceId())}/${encodeURIComponent(collection)}/${encodeURIComponent(entityId)}`
|
||||
}
|
||||
|
||||
const getEntityPreviewUrl = (
|
||||
entityId: string,
|
||||
collectionId?: string | null,
|
||||
variant: 'thumbnail' | 'inline' | 'fullscreen' = 'inline',
|
||||
): string => {
|
||||
const collection = collectionId ?? currentLocation.value
|
||||
return `${TRANSFER_BASE_URL}/preview/entity/${encodeURIComponent(currentProviderId())}/${encodeURIComponent(currentServiceId())}/${encodeURIComponent(collection)}/${encodeURIComponent(entityId)}?variant=${variant}`
|
||||
}
|
||||
|
||||
// Download a single file
|
||||
const downloadEntity = (entityId: string, collectionId?: string | null): void => {
|
||||
const collection = collectionId ?? currentLocation.value
|
||||
@@ -280,6 +289,7 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
|
||||
readFile,
|
||||
writeFile,
|
||||
getEntityUrl,
|
||||
getEntityPreviewUrl,
|
||||
downloadEntity,
|
||||
downloadCollection,
|
||||
downloadArchive,
|
||||
|
||||
Reference in New Issue
Block a user