Skip to content
Open
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
96 changes: 78 additions & 18 deletions lib/Service/InitialStateService.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

declare(strict_types=1);

namespace OCA\Richdocuments\Service;

use OCA\Richdocuments\AppConfig;
Expand All @@ -20,6 +21,7 @@

class InitialStateService {
private bool $hasProvidedCapabilities = false;
private bool $hasProvidedOptions = false;

public function __construct(
private IInitialState $initialState,
Expand All @@ -44,19 +46,55 @@ public function provideCapabilities(): void {
$this->initialState->provideInitialState('hasNextcloudBranding', $this->capabilitiesService->hasNextcloudBranding());
$this->initialState->provideInitialState('instanceId', $this->config->getSystemValue('instanceid'));
$this->initialState->provideInitialState('wopi_callback_url', $this->appConfig->getNextcloudUrl());

$this->provideOptions();

$this->hasProvidedCapabilities = true;
}

/**
* Provides the initial state for a document editing session.
*
* @param Wopi $wopi The WOPI session object containing access token and file metadata
* @param array $params Document parameters that override defaults. Required parameters:
* - urlsrc: WOPI source URL (will fail without it)
* - fileId: The file identifier (backend cannot locate file without it)
* - token: Access token for authentication (will fail without it)
*
* Parameters with safe fallbacks (possibly degraded functionality if omitted):
* - title: Document title/filename (default: '' - cosmetic only)
* - path: File path relative to user folder (default: '' - affects context)
* - permissions: File permissions bitmask (default: '' - read-only mode)
* - token_ttl: Token time-to-live in seconds (default: 0)
*
* Optional parameters:
* - isPublicShare: Whether accessed via public share (default: false)
* - directEdit: Whether in direct edit mode (default: false)
* - directGuest: Whether direct guest access (default: false)
* - hideCloseButton: Whether to hide close button
* - target: Optional bookmark/section target
* - userId: User identifier (may include remote server for federation)
* @return void
* @throws \InvalidArgumentException if critical parameters are missing
*/
public function provideDocument(Wopi $wopi, array $params): void {
$this->provideCapabilities();

// These keys are critical; the frontend will fail to load or authenticate properly if missing or empty.
if (!isset($params['urlsrc']) || $params['urlsrc'] === '') {
throw new \InvalidArgumentException('urlsrc is required for editor initialization');
}
if (!isset($params['fileId']) || $params['fileId'] === '') {
throw new \InvalidArgumentException('fileId is required to identify the document');
}
if (!isset($params['token']) || $params['token'] === '') {
throw new \InvalidArgumentException('token is required for authentication');
}

// merge provided parameters with default parameters
$this->initialState->provideInitialState('document', $this->prepareParams($params));

$this->initialState->provideInitialState('wopi', $wopi);

// Options may have been set already, but ensure anyway.
$this->provideOptions();
}

Expand All @@ -71,11 +109,16 @@ public function provideAdminSettings(): void {
]);
}

/**
* Parameters for a document editing session.
* Merges defaults with supplied params.
* Does not validate parameters; any validation should happen at the call site.
*/
public function prepareParams(array $params): array {
$defaults = [
'instanceId' => $this->config->getSystemValue('instanceid'),
'canonical_webroot' => $this->config->getAppValue(Application::APPNAME, 'canonical_webroot', ''),
'userId' => $this->userId,
'userId' => $this->userId, // @todo: confirm this is reasonable for all circumstances
'token' => '',
'token_ttl' => 0,
'directEdit' => false,
Expand All @@ -91,24 +134,41 @@ public function prepareParams(array $params): array {
return array_merge($defaults, $params);
}

/**
* Frontend options.
*/
private function provideOptions(): void {
if ($this->hasProvidedOptions) {
return;
}

$this->initialState->provideInitialState('loggedInUser', $this->userId ?? false);
$this->setThemeOptions();
$this->setLogoOptions();
$this->initialState->provideInitialState('open_local_editor', $this->config->getAppValue(Application::APPNAME, 'open_local_editor', 'yes') === 'yes');

$this->initialState->provideInitialState('theme', $this->config->getAppValue(Application::APPNAME, 'theme', 'nextcloud'));
$this->initialState->provideInitialState('uiDefaults', [
'UIMode' => $this->config->getAppValue(Application::APPNAME, 'uiDefaults-UIMode', 'notebookbar')
]);
$this->hasProvidedOptions = true;
}

$logoType = 'logoheader';
$logoSet = $this->imageManager->hasImage($logoType);
if (!$logoSet) {
private function setThemeOptions(): void {
$this->initialState->provideInitialState(
'theme',
$this->config->getAppValue(Application::APPNAME, 'theme', 'nextcloud')
);
$this->initialState->provideInitialState(
'uiDefaults',
['UIMode' => $this->config->getAppValue(Application::APPNAME, 'uiDefaults-UIMode', 'notebookbar')]
);
}

private function setLogoOptions(): void {
$logoType = false;
if ($this->imageManager->hasImage('logoheader')) { // prefer custom header logo
$logoType = 'logoheader';
} elseif ($this->imageManager->hasImage('logo')) {
$logoType = 'logo';
$logoSet = $this->imageManager->hasImage($logoType);
}

$logo = $logoSet ? $this->imageManager->getImageUrlAbsolute($logoType) : false;

$this->initialState->provideInitialState('theming-customLogo', $logo);
$this->initialState->provideInitialState('open_local_editor', $this->config->getAppValue(Application::APPNAME, 'open_local_editor', 'yes') === 'yes');
$logoUrl = $logoType ? $this->imageManager->getImageUrlAbsolute($logoType) : false;
$this->initialState->provideInitialState('theming-customLogo', $logoUrl);
}
}
Loading