Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c45d4da77 |
@@ -50,7 +50,11 @@ class TransferController extends ControllerAbstract
|
|||||||
*
|
*
|
||||||
* GET /download/entity/{provider}/{service}/{collection}/{identifier}
|
* 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 {
|
public function downloadEntity(string $provider, string $service, string $collection, string $identifier): Response {
|
||||||
$tenantId = $this->tenantContext->identifier();
|
$tenantId = $this->tenantContext->identifier();
|
||||||
$userId = $this->identityContext->identifier();
|
$userId = $this->identityContext->identifier();
|
||||||
@@ -120,44 +124,16 @@ 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
|
* Download multiple files as a ZIP archive
|
||||||
*
|
*
|
||||||
* GET /download/archive?provider=...&service=...&ids[]=...&ids[]=...
|
* 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 {
|
public function downloadArchive(string $provider, string $service, array $ids = [], ?string $collection = null, string $name = 'download'): Response {
|
||||||
$tenantId = $this->tenantContext->identifier();
|
$tenantId = $this->tenantContext->identifier();
|
||||||
$userId = $this->identityContext->identifier();
|
$userId = $this->identityContext->identifier();
|
||||||
@@ -239,7 +215,11 @@ class TransferController extends ControllerAbstract
|
|||||||
*
|
*
|
||||||
* GET /download/collection/{provider}/{service}/{identifier}
|
* 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 {
|
public function downloadCollection(string $provider, string $service, string $identifier): Response {
|
||||||
$tenantId = $this->tenantContext->identifier();
|
$tenantId = $this->tenantContext->identifier();
|
||||||
$userId = $this->identityContext->identifier();
|
$userId = $this->identityContext->identifier();
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace KTXM\DocumentsManager;
|
namespace KTXM\DocumentsManager;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
|
||||||
use KTXC\Preview\Preview;
|
|
||||||
use KTXC\Preview\PreviewManager;
|
|
||||||
use KTXC\Resource\ProviderManager;
|
use KTXC\Resource\ProviderManager;
|
||||||
use KTXF\Documents\Collection\CollectionBaseInterface;
|
use KTXF\Documents\Collection\CollectionBaseInterface;
|
||||||
use KTXF\Documents\Collection\CollectionPropertiesMutableInterface;
|
use KTXF\Documents\Collection\CollectionPropertiesMutableInterface;
|
||||||
@@ -21,8 +18,6 @@ use KTXF\Documents\Service\ServiceCollectionMutableInterface;
|
|||||||
use KTXF\Documents\Service\ServiceEntityMutableInterface;
|
use KTXF\Documents\Service\ServiceEntityMutableInterface;
|
||||||
use KTXF\Documents\Service\ServiceMutableInterface;
|
use KTXF\Documents\Service\ServiceMutableInterface;
|
||||||
use KTXF\Resource\Filter\IFilter;
|
use KTXF\Resource\Filter\IFilter;
|
||||||
use KTXF\Preview\PreviewSource;
|
|
||||||
use KTXF\Resource\BinaryResource;
|
|
||||||
use KTXF\Resource\Identifier\CollectionIdentifier;
|
use KTXF\Resource\Identifier\CollectionIdentifier;
|
||||||
use KTXF\Resource\Identifier\EntityIdentifier;
|
use KTXF\Resource\Identifier\EntityIdentifier;
|
||||||
use KTXF\Resource\Identifier\ResourceIdentifiers;
|
use KTXF\Resource\Identifier\ResourceIdentifiers;
|
||||||
@@ -42,7 +37,6 @@ class Manager {
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private LoggerInterface $logger,
|
private LoggerInterface $logger,
|
||||||
private ProviderManager $providerManager,
|
private ProviderManager $providerManager,
|
||||||
private PreviewManager $previewManager,
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1348,62 +1342,4 @@ class Manager {
|
|||||||
return $this->serviceForWrite($tenantId, $userId, $target)->entityWriteChunk($target, $offset, $data);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,15 +203,6 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
|
|||||||
return `${TRANSFER_BASE_URL}/download/entity/${encodeURIComponent(currentProviderId())}/${encodeURIComponent(currentServiceId())}/${encodeURIComponent(collection)}/${encodeURIComponent(entityId)}`
|
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
|
// Download a single file
|
||||||
const downloadEntity = (entityId: string, collectionId?: string | null): void => {
|
const downloadEntity = (entityId: string, collectionId?: string | null): void => {
|
||||||
const collection = collectionId ?? currentLocation.value
|
const collection = collectionId ?? currentLocation.value
|
||||||
@@ -289,7 +280,6 @@ export function useDocumentManager(options: UseDocumentManagerOptions) {
|
|||||||
readFile,
|
readFile,
|
||||||
writeFile,
|
writeFile,
|
||||||
getEntityUrl,
|
getEntityUrl,
|
||||||
getEntityPreviewUrl,
|
|
||||||
downloadEntity,
|
downloadEntity,
|
||||||
downloadCollection,
|
downloadCollection,
|
||||||
downloadArchive,
|
downloadArchive,
|
||||||
|
|||||||
Reference in New Issue
Block a user