Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use OCA\Richdocuments\Listener\LoadViewerListener;
use OCA\Richdocuments\Listener\OverwritePublicSharePropertiesListener;
use OCA\Richdocuments\Listener\ReferenceListener;
use OCA\Richdocuments\Listener\RegisterDirectEditorListener;
use OCA\Richdocuments\Listener\RegisterTemplateFileCreatorListener;
use OCA\Richdocuments\Listener\ShareLinkListener;
use OCA\Richdocuments\Middleware\WOPIMiddleware;
Expand Down Expand Up @@ -56,6 +57,7 @@
use OCP\BeforeSabrePubliclyLoadedEvent;
use OCP\Collaboration\Reference\RenderReferenceEvent;
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent;
use OCP\DirectEditing\RegisterDirectEditorEvent;
use OCP\Files\Storage\IStorage;
use OCP\Files\Template\BeforeGetTemplatesEvent;
use OCP\Files\Template\FileCreatedFromTemplateEvent;
Expand All @@ -82,6 +84,7 @@ public function register(IRegistrationContext $context): void {
$context->registerMiddleWare(WOPIMiddleware::class);
$context->registerEventListener(RegisterTemplateCreatorEvent::class, RegisterTemplateFileCreatorListener::class);
$context->registerEventListener(FileCreatedFromTemplateEvent::class, FileCreatedFromTemplateListener::class);
$context->registerEventListener(RegisterDirectEditorEvent::class, RegisterDirectEditorListener::class);
$context->registerEventListener(AddContentSecurityPolicyEvent::class, AddContentSecurityPolicyListener::class);
$context->registerEventListener(AddFeaturePolicyEvent::class, AddFeaturePolicyListener::class);
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalListener::class);
Expand Down
54 changes: 37 additions & 17 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,32 +102,18 @@ public function __construct(
#[\Override]
public function getCapabilities() {
// Only expose capabilities for users with enabled office or guests (where it depends on the share owner if they have access)
if (!$this->permissionManager->isEnabledForUser() && $this->userId !== null) {
if (!$this->permissionManager->isEnabledForUser($this->userId) && $this->userId !== null) {
return [];
}

if (!$this->capabilities) {
$collaboraCapabilities = $this->capabilitiesService->getCapabilities();

$defaultMimetypes = self::MIMETYPES;
$optionalMimetypes = self::MIMETYPES_OPTIONAL;

if (!$this->capabilitiesService->hasOtherOOXMLApps()) {
array_push($defaultMimetypes, ...self::MIMETYPES_MSOFFICE);
} else {
array_push($optionalMimetypes, ...self::MIMETYPES_MSOFFICE);
}

if (!$this->appManager->isEnabledForUser('files_pdfviewer')) {
$defaultMimetypes[] = 'application/pdf';
$optionalMimetypes = array_diff($optionalMimetypes, ['application/pdf']);
}

$this->capabilities = [
'richdocuments' => [
'version' => $this->appManager->getAppVersion('richdocuments'),
'mimetypes' => $defaultMimetypes,
'mimetypesNoDefaultOpen' => array_values($optionalMimetypes),
'mimetypes' => $this->getDefaultMimetypes(),
'mimetypesNoDefaultOpen' => $this->getOptionalMimetypes(),
'mimetypesSecureView' => $this->config->useSecureViewAdditionalMimes() ? self::SECURE_VIEW_ADDITIONAL_MIMES : [],
'collabora' => $collaboraCapabilities,
'direct_editing' => ($collaboraCapabilities['hasMobileSupport'] ?? false) && $this->config->getAppValue('mobile_editing', 'yes') === 'yes',
Expand All @@ -150,4 +136,38 @@ public function getCapabilities() {
}
return $this->capabilities;
}

/**
* @return list<string>
*/
public function getDefaultMimetypes(): array {
$defaultMimetypes = self::MIMETYPES;

if (!$this->capabilitiesService->hasOtherOOXMLApps()) {
array_push($defaultMimetypes, ...self::MIMETYPES_MSOFFICE);
}

if (!$this->appManager->isEnabledForUser('files_pdfviewer')) {
$defaultMimetypes[] = 'application/pdf';
}

return $defaultMimetypes;
}

/**
* @return list<string>
*/
public function getOptionalMimetypes(): array {
$optionalMimetypes = self::MIMETYPES_OPTIONAL;

if ($this->capabilitiesService->hasOtherOOXMLApps()) {
array_push($optionalMimetypes, ...self::MIMETYPES_MSOFFICE);
}

if (!$this->appManager->isEnabledForUser('files_pdfviewer')) {
$optionalMimetypes = array_diff($optionalMimetypes, ['application/pdf']);
}

return $optionalMimetypes;
}
}
51 changes: 10 additions & 41 deletions lib/Controller/DirectViewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@
use OCA\Richdocuments\AppConfig;
use OCA\Richdocuments\Db\Direct;
use OCA\Richdocuments\Db\DirectMapper;
use OCA\Richdocuments\Service\DirectEditingViewService;
use OCA\Richdocuments\Service\FederationService;
use OCA\Richdocuments\Service\InitialStateService;
use OCA\Richdocuments\Service\UserScopeService;
use OCA\Richdocuments\TemplateManager;
use OCA\Richdocuments\TokenManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Files\File;
use OCP\Files\Folder;
Expand All @@ -44,8 +45,8 @@ public function __construct(
private InitialStateService $initialState,
private IConfig $config,
private AppConfig $appConfig,
private TemplateManager $templateManager,
private FederationService $federationService,
private DirectEditingViewService $viewService,
private LoggerInterface $logger,
) {
parent::__construct($appName, $request);
Expand All @@ -57,7 +58,7 @@ public function __construct(
* @PublicPage
*
* @param string $token
* @return JSONResponse|RedirectResponse|TemplateResponse
* @return JSONResponse|RedirectResponse|TemplateResponse|Response
* @throws NotFoundException
*/
public function show($token) {
Expand Down Expand Up @@ -88,50 +89,18 @@ public function show($token) {
throw new \Exception();
}

/** Open file from remote collabora */
$federatedUrl = $this->federationService->getRemoteRedirectURL($item, $direct);
if ($federatedUrl !== null) {
$response = new RedirectResponse($federatedUrl);
return $response;
}

$wopi = null;
$template = $direct->getTemplateId() ? $this->templateManager->get($direct->getTemplateId()) : null;

if ($template !== null) {
$wopi = $this->tokenManager->generateWopiTokenForTemplate($template, $item->getId(), $direct->getUid(), false, true);
}

if ($wopi === null) {
$wopi = $this->tokenManager->generateWopiToken((string)$item->getId(), null, $direct->getUid(), true);
// Mirror the legacy "template-id carried on the direct token" flow:
// hand the association to the shared view service so the next render
// picks up the template via `richdocuments_template`.
if ($direct->getTemplateId()) {
$this->viewService->primeTemplateSource($item->getId(), $direct->getTemplateId(), $direct->getUid());
}

$urlSrc = $this->tokenManager->getUrlSrc($item);
return $this->viewService->render($item, $direct->getUid());
} catch (\Exception $e) {
$this->logger->error('Failed to generate token for existing file on direct editing', ['exception' => $e]);
return $this->renderErrorPage('Failed to open the requested file.');
}

$relativePath = $folder->getRelativePath($item->getPath());

try {
$params = [
'permissions' => $item->getPermissions(),
'title' => basename($relativePath),
'fileId' => $wopi->getFileid() . '_' . $this->config->getSystemValue('instanceid'),
'token' => $wopi->getToken(),
'token_ttl' => $wopi->getExpiry(),
'urlsrc' => $urlSrc,
'path' => $relativePath,
'direct' => true,
'userId' => $direct->getUid(),
];

return $this->documentTemplateResponse($wopi, $params);
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
return $this->renderErrorPage('Failed to open the requested file.');
}
}

public function showPublicShare(Direct $direct) {
Expand Down
60 changes: 50 additions & 10 deletions lib/Controller/OCSController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

use Exception;
use GuzzleHttp\Exception\BadResponseException;
use OCA\Richdocuments\AppInfo\Application;
use OCA\Richdocuments\Db\DirectMapper;
use OCA\Richdocuments\Exceptions\ExpiredTokenException;
use OCA\Richdocuments\Exceptions\UnknownTokenException;
Expand All @@ -21,6 +22,9 @@
use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\Constants;
use OCP\DirectEditing\IManager as IDirectEditingManager;
use OCP\DirectEditing\RegisterDirectEditorEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
Expand All @@ -47,6 +51,8 @@ public function __construct(
private TokenManager $tokenManager,
private IManager $shareManager,
private FederationService $federationService,
private IDirectEditingManager $directEditingManager,
private IEventDispatcher $eventDispatcher,
private LoggerInterface $logger,
) {
parent::__construct($appName, $request);
Expand All @@ -55,7 +61,13 @@ public function __construct(
/**
* @NoAdminRequired
*
* Init a direct editing session
* Init a direct editing session.
*
* @deprecated Use the server's direct editing API at
* POST /ocs/v2.php/apps/files/api/v1/directEditing/open with
* editorId=richdocuments. This endpoint is kept for
* backwards compatibility with older clients and now delegates
* to {@see \OCP\DirectEditing\IManager}.
*
* @param int $fileId
* @return DataResponse
Expand All @@ -74,12 +86,16 @@ public function createDirect($fileId) {
throw new OCSBadRequestException('Cannot view folder');
}

$direct = $this->directMapper->newDirect($this->userId, $fileId);
$path = $userFolder->getRelativePath($node->getPath());

$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
/** @psalm-suppress UndefinedInterfaceMethod IManager does not expose open() but the concrete Manager does, same pattern as files-app DirectEditingController */
$token = $this->directEditingManager->open($path, Application::APPNAME, $node->getId());

return new DataResponse([
'url' => $this->urlGenerator->linkToRouteAbsolute('richdocuments.directView.show', [
'token' => $direct->getToken()
])
'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', [
'token' => $token,
]),
]);
} catch (NotFoundException) {
throw new OCSNotFoundException();
Expand Down Expand Up @@ -246,6 +262,12 @@ public function updateGuestName(string $access_token, string $guestName): DataRe
* @NoAdminRequired
* @PublicPage
*
* @deprecated Use the server's direct editing API at
* GET /ocs/v2.php/apps/files/api/v1/directEditing/templates/richdocuments/{creatorId}.
* This endpoint is kept for backwards compatibility and reads
* from the same {@see \OCA\Richdocuments\TemplateManager} as
* the new flow.
*
* @param string $type The template type
* @return DataResponse
* @throws OCSBadRequestException
Expand All @@ -261,6 +283,12 @@ public function getTemplates($type) {
/**
* @NoAdminRequired
*
* @deprecated Use the server's direct editing API at
* POST /ocs/v2.php/apps/files/api/v1/directEditing/create with
* editorId=richdocuments. This endpoint is kept for
* backwards compatibility with older clients and now delegates
* to {@see \OCP\DirectEditing\IManager}.
*
* @param string $path Where to create the document
* @param int $template The template id
*/
Expand All @@ -279,17 +307,29 @@ public function createFromTemplate($path, $template) {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
$folder = isset($info['dirname']) ? $userFolder->get($info['dirname']) : $userFolder;
$name = $folder->getNonExistingName($info['basename']);
$file = $folder->newFile($name);

$direct = $this->directMapper->newDirect($this->userId, $file->getId(), $template);
$dirPath = isset($info['dirname']) ? rtrim($info['dirname'], '/') : '';
$targetPath = $dirPath === '' ? '/' . $name : $dirPath . '/' . $name;

$extension = $info['extension'] ?? '';
$creatorId = $this->manager->getTemplateTypeForExtension($extension);
if ($creatorId === null) {
throw new OCSBadRequestException('Unsupported file extension');
}

$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
/** @psalm-suppress InvalidArgument IManager::create accepts mixed templateId despite an outdated nullable docblock */
$token = $this->directEditingManager->create($targetPath, Application::APPNAME, $creatorId, (string)$template);

return new DataResponse([
'url' => $this->urlGenerator->linkToRouteAbsolute('richdocuments.directView.show', [
'token' => $direct->getToken()
])
'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', [
'token' => $token,
]),
]);
} catch (NotFoundException) {
throw new OCSNotFoundException();
} catch (OCSBadRequestException $e) {
throw $e;
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new OCSException('Failed to create new file from template.');
Expand Down
Loading
Loading