userSession = $userSession; $this->userManager = $userManager; $this->root = $root; $this->urlGenerator = $urlGenerator; $this->trans = $trans; $this->logger = $logger; $this->config = $config; $this->crypt = $crypt; if (\OC::$server->getAppManager()->isInstalled("files_versions")) { try { $this->versionManager = \OC::$server->query(IVersionManager::class); } catch (QueryException $e) { $this->logger->logException($e, ["message" => "VersionManager init error", "app" => $this->appName]); } } $this->fileUtility = new FileUtility($AppName, $trans, $logger, $config, $shareManager, $session); } /** * Create new file in folder * * @param string $name - file name * @param string $dir - folder path * @param string $shareToken - access token * * @return array * * @NoAdminRequired * @PublicPage */ public function create($name, $dir, $shareToken = null) { $this->logger->debug("Create: $name", ["app" => $this->appName]); if (empty($shareToken) && !$this->config->isUserAllowedToUse()) { return ["error" => $this->trans->t("Not permitted")]; } if (empty($shareToken)) { $userId = $this->userSession->getUser()->getUID(); $userFolder = $this->root->getUserFolder($userId); } else { list ($userFolder, $error, $share) = $this->fileUtility->getNodeByToken($shareToken); if (isset($error)) { $this->logger->error("Create: $error", ["app" => $this->appName]); return ["error" => $error]; } if ($userFolder instanceof File) { return ["error" => $this->trans->t("You don't have enough permission to create")]; } if (!empty($shareToken) && ($share->getPermissions() & Constants::PERMISSION_CREATE) === 0) { $this->logger->error("Create in public folder without access", ["app" => $this->appName]); return ["error" => $this->trans->t("You do not have enough permissions to view the file")]; } } $folder = $userFolder->get($dir); if ($folder === null) { $this->logger->error("Folder for file creation was not found: $dir", ["app" => $this->appName]); return ["error" => $this->trans->t("The required folder was not found")]; } if (!$folder->isCreatable()) { $this->logger->error("Folder for file creation without permission: $dir", ["app" => $this->appName]); return ["error" => $this->trans->t("You don't have enough permission to create")]; } $template = TemplateManager::GetTemplate($name); if (!$template) { $this->logger->error("Template for file creation not found: $name", ["app" => $this->appName]); return ["error" => $this->trans->t("Template not found")]; } $name = $folder->getNonExistingName($name); try { if (\version_compare(\implode(".", \OCP\Util::getVersion()), "19", "<")) { $file = $folder->newFile($name); $file->putContent($template); } else { $file = $folder->newFile($name, $template); } } catch (NotPermittedException $e) { $this->logger->logException($e, ["message" => "Can't create file: $name", "app" => $this->appName]); return ["error" => $this->trans->t("Can't create file")]; } $fileInfo = $file->getFileInfo(); $result = Helper::formatFileInfo($fileInfo); return $result; } /** * Conversion file to Office Open XML format * * @param integer $fileId - file identifier * @param string $shareToken - access token * * @return array * * @NoAdminRequired * @PublicPage */ public function convert($fileId, $shareToken = null) { $this->logger->debug("Convert: $fileId", ["app" => $this->appName]); if (empty($shareToken) && !$this->config->isUserAllowedToUse()) { return ["error" => $this->trans->t("Not permitted")]; } $user = $this->userSession->getUser(); $userId = null; if (!empty($user)) { $userId = $user->getUID(); } list ($file, $error, $share) = empty($shareToken) ? $this->getFile($userId, $fileId) : $this->fileUtility->getFileByToken($fileId, $shareToken); if (isset($error)) { $this->logger->error("Convertion: $fileId $error", ["app" => $this->appName]); return ["error" => $error]; } if (!empty($shareToken) && ($share->getPermissions() & Constants::PERMISSION_CREATE) === 0) { $this->logger->error("Convertion in public folder without access: $fileId", ["app" => $this->appName]); return ["error" => $this->trans->t("You do not have enough permissions to view the file")]; } $fileName = $file->getName(); $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); $format = $this->config->FormatsSetting()[$ext]; if (!isset($format)) { $this->logger->info("Format for convertion not supported: $fileName", ["app" => $this->appName]); return ["error" => $this->trans->t("Format is not supported")]; } if (!isset($format["conv"]) || $format["conv"] !== true) { $this->logger->info("Conversion is not required: $fileName", ["app" => $this->appName]); return ["error" => $this->trans->t("Conversion is not required")]; } $internalExtension = "docx"; switch ($format["type"]) { case "spreadsheet": $internalExtension = "xlsx"; break; case "presentation": $internalExtension = "pptx"; break; } $newFileUri = null; $documentService = new DocumentService($this->trans, $this->config); $key = $this->fileUtility->getKey($file); $fileUrl = $this->getUrl($file, $user, $shareToken); try { $newFileUri = $documentService->GetConvertedUri($fileUrl, $ext, $internalExtension, $key); } catch (\Exception $e) { $this->logger->logException($e, ["message" => "GetConvertedUri: " . $file->getId(), "app" => $this->appName]); return ["error" => $e->getMessage()]; } $folder = $file->getParent(); if (!$folder->isCreatable()) { $folder = $this->root->getUserFolder($userId); } try { $newData = $documentService->Request($newFileUri); } catch (\Exception $e) { $this->logger->logException($e, ["message" => "Failed to download converted file", "app" => $this->appName]); return ["error" => $this->trans->t("Failed to download converted file")]; } $fileNameWithoutExt = substr($fileName, 0, strlen($fileName) - strlen($ext) - 1); $newFileName = $folder->getNonExistingName($fileNameWithoutExt . "." . $internalExtension); try { $file = $folder->newFile($newFileName); $file->putContent($newData); } catch (NotPermittedException $e) { $this->logger->logException($e, ["message" => "Can't create file: $newFileName", "app" => $this->appName]); return ["error" => $this->trans->t("Can't create file")]; } $fileInfo = $file->getFileInfo(); $result = Helper::formatFileInfo($fileInfo); return $result; } /** * Save file to folder * * @param string $name - file name * @param string $dir - folder path * @param string $url - file url * * @return array * * @NoAdminRequired */ public function save($name, $dir, $url) { $this->logger->debug("Save: $name", ["app" => $this->appName]); if (!$this->config->isUserAllowedToUse()) { return ["error" => $this->trans->t("Not permitted")]; } $userId = $this->userSession->getUser()->getUID(); $userFolder = $this->root->getUserFolder($userId); $folder = $userFolder->get($dir); if ($folder === null) { $this->logger->error("Folder for saving file was not found: $dir", ["app" => $this->appName]); return ["error" => $this->trans->t("The required folder was not found")]; } if (!$folder->isCreatable()) { $this->logger->error("Folder for saving file without permission: $dir", ["app" => $this->appName]); return ["error" => $this->trans->t("You don't have enough permission to create")]; } $url = $this->config->ReplaceDocumentServerUrlToInternal($url); try { $documentService = new DocumentService($this->trans, $this->config); $newData = $documentService->Request($url); } catch (\Exception $e) { $this->logger->logException($e, ["message" => "Failed to download file for saving: $url", "app" => $this->appName]); return ["error" => $this->trans->t("Download failed")]; } $name = $folder->getNonExistingName($name); try { $file = $folder->newFile($name); $file->putContent($newData); } catch (NotPermittedException $e) { $this->logger->logException($e, ["message" => "Can't save file: $name", "app" => $this->appName]); return ["error" => $this->trans->t("Can't create file")]; } $fileInfo = $file->getFileInfo(); $result = Helper::formatFileInfo($fileInfo); return $result; } /** * Get versions history for file * * @param integer $fileId - file identifier * @param string $shareToken - access token * * @return array * * @NoAdminRequired * @PublicPage */ public function history($fileId, $shareToken = null) { $this->logger->debug("Request history for: $fileId", ["app" => $this->appName]); $history = []; $user = $this->userSession->getUser(); $userId = null; if (!empty($user)) { $userId = $user->getUID(); } list ($file, $error, $share) = empty($shareToken) ? $this->getFile($userId, $fileId) : $this->fileUtility->getFileByToken($fileId, $shareToken); if (isset($error)) { $this->logger->error("History: $fileId $error", ["app" => $this->appName]); return ["error" => $error]; } if ($fileId === 0) { $fileId = $file->getId(); } $ownerId = null; $owner = $file->getFileInfo()->getOwner(); if ($owner !== null) { $ownerId = $owner->getUID(); } $versions = array(); if ($this->versionManager !== null && $owner !== null) { $versions = array_reverse($this->versionManager->getVersionsForFile($owner, $file->getFileInfo())); } $prevVersion = ""; $versionNum = 0; foreach ($versions as $version) { $versionNum = $versionNum + 1; $key = $this->fileUtility->getVersionKey($version); $key = DocumentService::GenerateRevisionId($key); $historyItem = [ "created" => $version->getTimestamp(), "key" => $key, "version" => $versionNum ]; $versionId = $version->getRevisionId(); $author = FileVersions::getAuthor($ownerId, $fileId, $versionId); $authorId = $author !== null ? $author["id"] : $ownerId; $authorName = $author !== null ? $author["name"] : $owner->getDisplayName(); $historyItem["user"] = [ "id" => $this->buildUserId($authorId), "name" => $authorName ]; $historyData = FileVersions::getHistoryData($ownerId, $fileId, $versionId, $prevVersion); if ($historyData !== null) { $historyItem["changes"] = $historyData["changes"]; $historyItem["serverVersion"] = $historyData["serverVersion"]; } $prevVersion = $versionId; array_push($history, $historyItem); } $key = $this->fileUtility->getKey($file, true); $key = DocumentService::GenerateRevisionId($key); $historyItem = [ "created" => $file->getMTime(), "key" => $key, "version" => $versionNum + 1 ]; $versionId = $file->getFileInfo()->getMtime(); $author = FileVersions::getAuthor($ownerId, $fileId, $versionId); if ($author !== null) { $historyItem["user"] = [ "id" => $this->buildUserId($author["id"]), "name" => $author["name"] ]; } else if ($owner !== null) { $historyItem["user"] = [ "id" => $this->buildUserId($ownerId), "name" => $owner->getDisplayName() ]; } $historyData = FileVersions::getHistoryData($ownerId, $fileId, $versionId, $prevVersion); if ($historyData !== null) { $historyItem["changes"] = $historyData["changes"]; $historyItem["serverVersion"] = $historyData["serverVersion"]; } array_push($history, $historyItem); return $history; } /** * Get file attributes of specific version * * @param integer $fileId - file identifier * @param integer $version - file version * @param string $shareToken - access token * * @return array * * @NoAdminRequired * @PublicPage */ public function version($fileId, $version, $shareToken = null) { $this->logger->debug("Request version for: $fileId ($version)", ["app" => $this->appName]); $version = empty($version) ? null : $version; $user = $this->userSession->getUser(); $userId = null; if (!empty($user)) { $userId = $user->getUID(); } list ($file, $error, $share) = empty($shareToken) ? $this->getFile($userId, $fileId) : $this->fileUtility->getFileByToken($fileId, $shareToken); if (isset($error)) { $this->logger->error("History: $fileId $error", ["app" => $this->appName]); return ["error" => $error]; } if ($fileId === 0) { $fileId = $file->getId(); } $owner = null; $ownerId = null; $versions = array(); if ($this->versionManager !== null) { $owner = $file->getFileInfo()->getOwner(); if ($owner !== null) { $ownerId = $owner->getUID(); $versions = array_reverse($this->versionManager->getVersionsForFile($owner, $file->getFileInfo())); } } $key = null; $fileUrl = null; $versionId = null; if ($version > count($versions)) { $key = $this->fileUtility->getKey($file, true); $versionId = $file->getFileInfo()->getMtime(); $fileUrl = $this->getUrl($file, $user, $shareToken); } else { $fileVersion = array_values($versions)[$version - 1]; $key = $this->fileUtility->getVersionKey($fileVersion); $versionId = $fileVersion->getRevisionId(); $fileUrl = $this->getUrl($file, $user, $shareToken, $version); } $key = DocumentService::GenerateRevisionId($key); $result = [ "url" => $fileUrl, "version" => $version, "key" => $key ]; if ($version > 1 && count($versions) >= $version - 1 && FileVersions::hasChanges($ownerId, $fileId, $versionId)) { $changesUrl = $this->getUrl($file, $user, $shareToken, $version, true); $result["changesUrl"] = $changesUrl; $prevVersion = array_values($versions)[$version - 2]; $prevVersionKey = $this->fileUtility->getVersionKey($prevVersion); $prevVersionKey = DocumentService::GenerateRevisionId($prevVersionKey); $prevVersionUrl = $this->getUrl($file, $user, $shareToken, $version - 1); $result["previous"] = [ "key" => $prevVersionKey, "url" => $prevVersionUrl ]; } if (!empty($this->config->GetDocumentServerSecret())) { $token = \Firebase\JWT\JWT::encode($result, $this->config->GetDocumentServerSecret()); $result["token"] = $token; } return $result; } /** * Get presigned url to file * * @param string $filePath - file path * * @return array * * @NoAdminRequired */ public function url($filePath) { $this->logger->debug("Request url for: $filePath", ["app" => $this->appName]); if (!$this->config->isUserAllowedToUse()) { return ["error" => $this->trans->t("Not permitted")]; } $user = $this->userSession->getUser(); $userId = $user->getUID(); $userFolder = $this->root->getUserFolder($userId); $file = $userFolder->get($filePath); if ($file === null) { $this->logger->error("File for generate presigned url was not found: $filePath", ["app" => $this->appName]); return ["error" => $this->trans->t("File not found")]; } if (!$file->isReadable()) { $this->logger->error("Folder for saving file without permission: $filePath", ["app" => $this->appName]); return ["error" => $this->trans->t("You do not have enough permissions to view the file")]; } $fileName = $file->getName(); $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); $fileUrl = $this->getUrl($file, $user); $result = [ "fileType" => $ext, "url" => $fileUrl ]; if (!empty($this->config->GetDocumentServerSecret())) { $token = \Firebase\JWT\JWT::encode($result, $this->config->GetDocumentServerSecret()); $result["token"] = $token; } return $result; } /** * Print editor section * * @param integer $fileId - file identifier * @param string $filePath - file path * @param string $shareToken - access token * @param integer $version - file version * @param bool $inframe - open in frame * * @return TemplateResponse|RedirectResponse * * @NoAdminRequired * @NoCSRFRequired */ public function index($fileId, $filePath = null, $shareToken = null, $version = 0, $inframe = false) { $this->logger->debug("Open: $fileId ($version) $filePath ", ["app" => $this->appName]); $isLoggedIn = $this->userSession->isLoggedIn(); if (empty($shareToken) && !$isLoggedIn) { $redirectUrl = $this->urlGenerator->linkToRoute("core.login.showLoginForm", [ "redirect_url" => $this->request->getRequestUri() ]); return new RedirectResponse($redirectUrl); } if (empty($shareToken) && !$this->config->isUserAllowedToUse()) { return $this->renderError($this->trans->t("Not permitted")); } $documentServerUrl = $this->config->GetDocumentServerUrl(); if (empty($documentServerUrl)) { $this->logger->error("documentServerUrl is empty", ["app" => $this->appName]); return $this->renderError($this->trans->t("ONLYOFFICE app is not configured. Please contact admin")); } $params = [ "documentServerUrl" => $documentServerUrl, "fileId" => $fileId, "filePath" => $filePath, "shareToken" => $shareToken, "directToken" => null, "version" => $version, "inframe" => false ]; $response = null; if ($inframe === true) { $params["inframe"] = true; $response = new TemplateResponse($this->appName, "editor", $params, "base"); } else { if ($isLoggedIn) { $response = new TemplateResponse($this->appName, "editor", $params); } else { $response = new PublicTemplateResponse($this->appName, "editor", $params); list ($file, $error, $share) = $this->fileUtility->getFileByToken($fileId, $shareToken); if (!isset($error)) { $response->setHeaderTitle($file->getName()); } } } $csp = new ContentSecurityPolicy(); $csp->allowInlineScript(true); if (preg_match("/^https?:\/\//i", $documentServerUrl)) { $csp->addAllowedScriptDomain($documentServerUrl); $csp->addAllowedFrameDomain($documentServerUrl); } else { $csp->addAllowedFrameDomain("'self'"); } $response->setContentSecurityPolicy($csp); return $response; } /** * Print public editor section * * @param integer $fileId - file identifier * @param string $shareToken - access token * @param integer $version - file version * @param bool $inframe - open in frame * * @return TemplateResponse * * @NoAdminRequired * @NoCSRFRequired * @PublicPage */ public function PublicPage($fileId, $shareToken, $version = 0, $inframe = false) { return $this->index($fileId, null, $shareToken, $version, $inframe); } /** * Get template loader Onlyoffice * * @return TemplateResponse * * @NoAdminRequired * @NoCSRFRequired * @PublicPage */ public function loader() { return new TemplateResponse($this->appName, "loader", [], "plain"); } /** * Collecting the file parameters for the document service * * @param integer $fileId - file identifier * @param string $filePath - file path * @param string $shareToken - access token * @param string $directToken - direct token * @param integer $version - file version * @param integer $inframe - open in frame. 0 - no, 1 - yes, 2 - without goback for old editor (5.4) * @param bool $desktop - desktop label * @param string $guestName - nickname not logged user * * @return array * * @NoAdminRequired * @PublicPage */ public function config($fileId, $filePath = null, $shareToken = null, $directToken = null, $version = 0, $inframe = 0, $desktop = false, $guestName = null) { if (!empty($directToken)) { list ($directData, $error) = $this->crypt->ReadHash($directToken); if ($directData === null) { $this->logger->error("Config for directEditor with empty or not correct hash: $error", ["app" => $this->appName]); return ["error" => $this->trans->t("Not permitted")]; } if ($directData->action !== "direct") { $this->logger->error("Config for directEditor with other data", ["app" => $this->appName]); return ["error" => $this->trans->t("Invalid request")]; } $fileId = $directData->fileId; $userId = $directData->userId; if ($this->userSession->isLoggedIn() && $userId === $this->userSession->getUser()->getUID()) { $redirectUrl = $this->urlGenerator->linkToRouteAbsolute($this->appName . ".editor.index", [ "fileId" => $fileId, "filePath" => $filePath ]); return ["redirectUrl" => $redirectUrl]; } $user = $this->userManager->get($userId); } else { if (empty($shareToken) && !$this->config->isUserAllowedToUse()) { return ["error" => $this->trans->t("Not permitted")]; } $user = $this->userSession->getUser(); $userId = null; if (!empty($user)) { $userId = $user->getUID(); } } list ($file, $error, $share) = empty($shareToken) ? $this->getFile($userId, $fileId, $filePath) : $this->fileUtility->getFileByToken($fileId, $shareToken); if (isset($error)) { $this->logger->error("Config: $fileId $error", ["app" => $this->appName]); return ["error" => $error]; } $fileName = $file->getName(); $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); $format = $this->config->FormatsSetting()[$ext]; if (!isset($format)) { $this->logger->info("Format is not supported for editing: $fileName", ["app" => $this->appName]); return ["error" => $this->trans->t("Format is not supported")]; } $fileUrl = $this->getUrl($file, $user, $shareToken, $version); $key = null; if ($version > 0 && $this->versionManager !== null) { $owner = $file->getFileInfo()->getOwner(); if ($owner !== null) { $versions = array_reverse($this->versionManager->getVersionsForFile($owner, $file->getFileInfo())); if ($version <= count($versions)) { $fileVersion = array_values($versions)[$version - 1]; $key = $this->fileUtility->getVersionKey($fileVersion); } } } if ($key === null) { $key = $this->fileUtility->getKey($file, true); } $key = DocumentService::GenerateRevisionId($key); $params = [ "document" => [ "fileType" => $ext, "key" => $key, "permissions" => [], "title" => $fileName, "url" => $fileUrl, ], "documentType" => $format["type"], "editorConfig" => [ "lang" => str_replace("_", "-", \OC::$server->getL10NFactory("")->get("")->getLanguageCode()), "region" => str_replace("_", "-", \OC::$server->getL10NFactory("")->findLocale()) ] ]; $permissions_modifyFilter = $this->config->GetSystemValue($this->config->_permissions_modifyFilter); if (isset($permissions_modifyFilter)) { $params["document"]["permissions"]["modifyFilter"] = $permissions_modifyFilter; } $canEdit = isset($format["edit"]) && $format["edit"]; $editable = $version < 1 && $file->isUpdateable() && (empty($shareToken) || ($share->getPermissions() & Constants::PERMISSION_UPDATE) === Constants::PERMISSION_UPDATE); $params["document"]["permissions"]["edit"] = $editable; if ($editable && $canEdit) { $hashCallback = $this->crypt->GetHash(["userId" => $userId, "fileId" => $file->getId(), "filePath" => $filePath, "shareToken" => $shareToken, "action" => "track"]); $callback = $this->urlGenerator->linkToRouteAbsolute($this->appName . ".callback.track", ["doc" => $hashCallback]); if (!empty($this->config->GetStorageUrl())) { $callback = str_replace($this->urlGenerator->getAbsoluteURL("/"), $this->config->GetStorageUrl(), $callback); } $params["editorConfig"]["callbackUrl"] = $callback; } else { $params["editorConfig"]["mode"] = "view"; } if (\OC::$server->getRequest()->isUserAgent([$this::USER_AGENT_MOBILE])) { $params["type"] = "mobile"; } if (!empty($userId)) { $params["editorConfig"]["user"] = [ "id" => $this->buildUserId($userId), "name" => $user->getDisplayName() ]; } else if (!empty($guestName)) { $params["editorConfig"]["user"] = [ "name" => $guestName ]; } $folderLink = null; if (!empty($shareToken)) { if (method_exists($share, "getHideDownload") && $share->getHideDownload()) { $params["document"]["permissions"]["download"] = false; $params["document"]["permissions"]["print"] = false; } $node = $share->getNode(); if ($node instanceof Folder) { $sharedFolder = $node; $folderPath = $sharedFolder->getRelativePath($file->getParent()->getPath()); if (!empty($folderPath)) { $linkAttr = [ "path" => $folderPath, "scrollto" => $file->getName(), "token" => $shareToken ]; $folderLink = $this->urlGenerator->linkToRouteAbsolute("files_sharing.sharecontroller.showShare", $linkAttr); } } } else if (!empty($userId)) { $userFolder = $this->root->getUserFolder($userId); $folderPath = $userFolder->getRelativePath($file->getParent()->getPath()); if (!empty($folderPath)) { $linkAttr = [ "dir" => $folderPath, "scrollto" => $file->getName() ]; $folderLink = $this->urlGenerator->linkToRouteAbsolute("files.view.index", $linkAttr); } } if ($folderLink !== null && $inframe !== 2) { $params["editorConfig"]["customization"]["goback"] = [ "url" => $folderLink ]; if (!$desktop) { if ($this->config->GetSameTab()) { $params["editorConfig"]["customization"]["goback"]["blank"] = false; } if ($inframe === 1 || !empty($directToken)) { $params["editorConfig"]["customization"]["goback"]["requestClose"] = true; } } } if ($inframe === 1) { $params["_files_sharing"] = \OC::$server->getAppManager()->isInstalled("files_sharing"); } $params = $this->setCustomization($params); if ($file->getStorage()->instanceOfStorage(SharingExternalStorage::class)) { //otherwise forcesave will delete the key $params["editorConfig"]["customization"]["forcesave"] = false; } $params = $this->setWatermark($params, !empty($shareToken), $userId, $file); if ($this->config->UseDemo()) { $params["editorConfig"]["tenant"] = $this->config->GetSystemValue("instanceid", true); } if (!empty($this->config->GetDocumentServerSecret())) { $token = \Firebase\JWT\JWT::encode($params, $this->config->GetDocumentServerSecret()); $params["token"] = $token; } $this->logger->debug("Config is generated for: $fileId ($version) with key $key", ["app" => $this->appName]); return $params; } /** * Getting file by identifier * * @param string $userId - user identifier * @param integer $fileId - file identifier * @param string $filePath - file path * * @return array */ private function getFile($userId, $fileId, $filePath = null) { if (empty($fileId)) { return [null, $this->trans->t("FileId is empty"), null]; } try { $files = $this->root->getUserFolder($userId)->getById($fileId); } catch (\Exception $e) { $this->logger->logException($e, ["message" => "getFile: $fileId", "app" => $this->appName]); return [null, $this->trans->t("Invalid request"), null]; } if (empty($files)) { $this->logger->info("Files not found: $fileId", ["app" => $this->appName]); return [null, $this->trans->t("File not found"), null]; } $file = $files[0]; if (count($files) > 1 && !empty($filePath)) { $filePath = "/" . $userId . "/files" . $filePath; foreach ($files as $curFile) { if ($curFile->getPath() === $filePath) { $file = $curFile; break; } } } if (!$file->isReadable()) { return [null, $this->trans->t("You do not have enough permissions to view the file"), null]; } return [$file, null, null]; } /** * Generate secure link to download document * * @param File $file - file * @param IUser $user - user with access * @param string $shareToken - access token * @param integer $version - file version * @param bool $changes - is required url to file changes * * @return string */ private function getUrl($file, $user = null, $shareToken = null, $version = 0, $changes = false) { $data = [ "action" => "download", "fileId" => $file->getId() ]; $userId = null; if (!empty($user)) { $userId = $user->getUID(); $data["userId"] = $userId; } if (!empty($shareToken)) { $data["shareToken"] = $shareToken; } if ($version > 0) { $data["version"] = $version; } if ($changes) { $data["changes"] = true; } $hashUrl = $this->crypt->GetHash($data); $fileUrl = $this->urlGenerator->linkToRouteAbsolute($this->appName . ".callback.download", ["doc" => $hashUrl]); if (!empty($this->config->GetStorageUrl()) && !$changes) { $fileUrl = str_replace($this->urlGenerator->getAbsoluteURL("/"), $this->config->GetStorageUrl(), $fileUrl); } return $fileUrl; } /** * Generate unique user identifier * * @param string $userId - current user identifier * * @return string */ private function buildUserId($userId) { $instanceId = $this->config->GetSystemValue("instanceid", true); $userId = $instanceId . "_" . $userId; return $userId; } /** * Set customization parameters * * @param array params - file parameters * * @return array */ private function setCustomization($params) { //default is true if ($this->config->GetCustomizationChat() === false) { $params["editorConfig"]["customization"]["chat"] = false; } //default is false if ($this->config->GetCustomizationCompactHeader() === true) { $params["editorConfig"]["customization"]["compactHeader"] = true; } //default is false if ($this->config->GetCustomizationFeedback() === true) { $params["editorConfig"]["customization"]["feedback"] = true; } //default is false if ($this->config->GetCustomizationForcesave() === true) { $params["editorConfig"]["customization"]["forcesave"] = true; } //default is true if ($this->config->GetCustomizationHelp() === false) { $params["editorConfig"]["customization"]["help"] = false; } //default is original $reviewDisplay = $this->config->GetCustomizationReviewDisplay(); if ($reviewDisplay !== "original") { $params["editorConfig"]["customization"]["reviewDisplay"] = $reviewDisplay; } //default is false if ($this->config->GetCustomizationToolbarNoTabs() === true) { $params["editorConfig"]["customization"]["toolbarNoTabs"] = true; } /* from system config */ $autosave = $this->config->GetSystemValue($this->config->_customization_autosave); if (isset($autosave)) { $params["editorConfig"]["customization"]["autosave"] = $autosave; } $customer = $this->config->GetSystemValue($this->config->_customization_customer); if (isset($customer)) { $params["editorConfig"]["customization"]["customer"] = $customer; } $loaderLogo = $this->config->GetSystemValue($this->config->_customization_loaderLogo); if (isset($loaderLogo)) { $params["editorConfig"]["customization"]["loaderLogo"] = $loaderLogo; } $loaderName = $this->config->GetSystemValue($this->config->_customization_loaderName); if (isset($loaderName)) { $params["editorConfig"]["customization"]["loaderName"] = $loaderName; } $logo = $this->config->GetSystemValue($this->config->_customization_logo); if (isset($logo)) { $params["editorConfig"]["customization"]["logo"] = $logo; } $zoom = $this->config->GetSystemValue($this->config->_customization_zoom); if (isset($zoom)) { $params["editorConfig"]["customization"]["zoom"] = $zoom; } return $params; } /** * Set watermark parameters * * @param array params - file parameters * @param bool isPublic - with access token * @param string userId - user identifier * @param string file - file * * @return array */ private function setWatermark($params, $isPublic, $userId, $file) { $watermarkTemplate = $this->getWatermarkText($isPublic, $userId, $file, $params["document"]["permissions"]["edit"] !== false, !array_key_exists("download", $params["document"]["permissions"]) || $params["document"]["permissions"]["download"] !== false); if ($watermarkTemplate !== false) { $replacements = [ "userId" => $userId, "date" => (new \DateTime())->format("Y-m-d H:i:s"), "themingName" => \OC::$server->getThemingDefaults()->getName() ]; $watermarkTemplate = preg_replace_callback("/{(.+?)}/", function ($matches) use ($replacements) { return $replacements[$matches[1]]; }, $watermarkTemplate); $params["document"]["options"] = [ "watermark_on_draw" => [ "align" => 1, "height" => 100, "paragraphs" => array([ "align" => 2, "runs" => array([ "fill" => [182, 182, 182], "font-size" => 70, "text" => $watermarkTemplate, ]) ]), "rotate" => -45, "width" => 250, ] ]; } return $params; } /** * Should watermark * * @param bool isPublic - with access token * @param string userId - user identifier * @param string file - file * @param bool canEdit - edit permission * @param bool canDownload - download permission * * @return bool|string */ private function getWatermarkText($isPublic, $userId, $file, $canEdit, $canDownload) { $watermarkSettings = $this->config->GetWatermarkSettings(); if (!$watermarkSettings["enabled"]) { return false; } $watermarkText = $watermarkSettings["text"]; $fileId = $file->getId(); if ($isPublic) { if ($watermarkSettings["linkAll"]) { return $watermarkText; } if ($watermarkSettings["linkRead"] && !$canEdit) { return $watermarkText; } if ($watermarkSettings["linkSecure"] && !$canDownload) { return $watermarkText; } if ($watermarkSettings["linkTags"]) { $tags = $watermarkSettings["linkTagsList"]; $fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], "files")[$fileId]; foreach ($fileTags as $tagId) { if (in_array($tagId, $tags, true)) { return $watermarkText; } } } } else { if ($watermarkSettings["shareAll"] && ($file->getOwner() === null || $file->getOwner()->getUID() !== $userId)) { return $watermarkText; } if ($watermarkSettings["shareRead"] && !$canEdit) { return $watermarkText; } } if ($watermarkSettings["allGroups"]) { $groups = $watermarkSettings["allGroupsList"]; foreach ($groups as $group) { if (\OC::$server->getGroupManager()->isInGroup($userId, $group)) { return $watermarkText; } } } if ($watermarkSettings["allTags"]) { $tags = $watermarkSettings["allTagsList"]; $fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], "files")[$fileId]; foreach ($fileTags as $tagId) { if (in_array($tagId, $tags, true)) { return $watermarkText; } } } return false; } /** * Print error page * * @param string $error - error message * @param string $hint - error hint * * @return TemplateResponse */ private function renderError($error, $hint = "") { return new TemplateResponse("", "error", [ "errors" => [ [ "error" => $error, "hint" => $hint ] ] ], "error"); } }