userSession = $userSession; $this->root = $root; $this->urlGenerator = $urlGenerator; $this->trans = $trans; $this->logger = $logger; $this->config = $config; $this->crypt = $crypt; } /** * Create new file in folder * * @param string $name - file name * @param string $dir - folder path * * @return array * * @NoAdminRequired */ public function create($name, $dir) { $this->logger->debug("Create: " . $name, array("app" => $this->appName)); $userId = $this->userSession->getUser()->getUID(); $userFolder = $this->root->getUserFolder($userId); $folder = $userFolder->get($dir); if ($folder === NULL) { $this->logger->info("Folder for file creation was not found: " . $dir, array("app" => $this->appName)); return ["error" => $this->trans->t("The required folder was not found")]; } if (!$folder->isCreatable()) { $this->logger->info("Folder for file creation without permission: " . $dir, array("app" => $this->appName)); return ["error" => $this->trans->t("You don't have enough permission to create")]; } $name = $folder->getNonExistingName($name); $filePath = $dir . DIRECTORY_SEPARATOR . $name; $ext = strtolower("." . pathinfo($filePath, PATHINFO_EXTENSION)); $lang = \OC::$server->getL10NFactory("")->get("")->getLanguageCode(); $templatePath = $this->getTemplatePath($lang, $ext); if (!file_exists($templatePath)) { $lang = "en"; $templatePath = $this->getTemplatePath($lang, $ext); } $template = file_get_contents($templatePath); if (!$template) { $this->logger->info("Template for file creation not found: " . $templatePath, array("app" => $this->appName)); return ["error" => $this->trans->t("Template not found")]; } $view = Filesystem::getView(); if (!$view->file_put_contents($filePath, $template)) { $this->logger->error("Can't create file: " . $filePath, array("app" => $this->appName)); return ["error" => $this->trans->t("Can't create file")]; } $fileInfo = $view->getFileInfo($filePath); if ($fileInfo === false) { $this->logger->info("File not found: " . $filePath, array("app" => $this->appName)); return ["error" => $this->trans->t("File not found")]; } $result = Helper::formatFileInfo($fileInfo); return $result; } private function getTemplatePath($lang, $ext) { return dirname(__DIR__) . DIRECTORY_SEPARATOR . "assets" . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . "new" . $ext; } /** * Conversion file to Office Open XML format * * @param integer $fileId - file identifier * * @return array * * @NoAdminRequired */ public function convert($fileId) { $this->logger->debug("Convert: " . $fileId, array("app" => $this->appName)); list ($file, $error) = $this->getFile($fileId); if (isset($error)) { $this->logger->error("Convertion: " . $fileId . " " . $error, array("app" => $this->appName)); return ["error" => $error]; } $fileName = $file->getName(); $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); $format = $this->config->formats[$ext]; if (!isset($format)) { $this->logger->info("Format for convertion not supported: " . $fileName, array("app" => $this->appName)); return ["error" => $this->trans->t("Format is not supported")]; } if (!isset($format["conv"]) || $format["conv"] !== TRUE) { $this->logger->debug("Conversion is not required: " . $fileName, array("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; $documentService = new DocumentService($this->trans, $this->config); $key = $this->getKey($file); $fileUrl = $this->getUrl($file); try { $newFileUri = $documentService->GetConvertedUri($fileUrl, $ext, $internalExtension, $key); } catch (\Exception $e) { $this->logger->error("GetConvertedUri: " . $fileId . " " . $e->getMessage(), array("app" => $this->appName)); return ["error" => $e->getMessage()]; } $userId = $this->userSession->getUser()->getUID(); $folder = $file->getParent(); if (!$folder->isCreatable()) { $folder = $this->root->getUserFolder($userId); } $pattern = "/^\\" . DIRECTORY_SEPARATOR . $userId . "\\" . DIRECTORY_SEPARATOR . "files/"; $newFolderPath = preg_replace($pattern, "", $folder->getPath()); $fileNameWithoutExt = substr($fileName, 0, strlen($fileName) - strlen($ext) - 1); $newFileName = $folder->getNonExistingName($fileNameWithoutExt . "." . $internalExtension); $newFilePath = $newFolderPath . DIRECTORY_SEPARATOR . $newFileName; if (($newData = $documentService->Request($newFileUri)) === FALSE) { $this->logger->error("Failed to download converted file: " . $newFileUri, array("app" => $this->appName)); return ["error" => $this->trans->t("Failed to download converted file")]; } $view = Filesystem::getView(); if (!$view->file_put_contents($newFilePath, $newData)) { $this->logger->error("Can't create file after convertion: " . $newFilePath, array("app" => $this->appName)); return ["error" => $this->trans->t("Can't create file")]; } $fileInfo = $view->getFileInfo($newFilePath); if ($fileInfo === false) { $this->logger->info("File not found: " . $newFilePath, array("app" => $this->appName)); return ["error" => $this->trans->t("File not found")]; } $result = Helper::formatFileInfo($fileInfo); return $result; } /** * Print editor section * * @param integer $fileId - file identifier * * @return TemplateResponse * * @NoAdminRequired * @NoCSRFRequired */ public function index($fileId) { $this->logger->debug("Open: " . $fileId, array("app" => $this->appName)); $documentServerUrl = $this->config->GetDocumentServerUrl(); if (empty($documentServerUrl)) { $this->logger->error("documentServerUrl is empty", array("app" => $this->appName)); return ["error" => $this->trans->t("ONLYOFFICE app is not configured. Please contact admin")]; } $params = [ "documentServerUrl" => $documentServerUrl, "fileId" => $fileId ]; $response = new TemplateResponse($this->appName, "editor", $params); $csp = new ContentSecurityPolicy(); $csp->allowInlineScript(true); if (preg_match("/^https?:\/\//i", $documentServerUrl)) { $csp->addAllowedScriptDomain($documentServerUrl); $csp->addAllowedFrameDomain($documentServerUrl); } else { $csp->addAllowedFrameDomain($this->urlGenerator->getAbsoluteURL("/")); } $response->setContentSecurityPolicy($csp); return $response; } /** * Collecting the file parameters for the document service * * @param integer $fileId - file identifier * * @return array * * @NoAdminRequired */ public function config($fileId) { list ($file, $error) = $this->getFile($fileId); if (isset($error)) { $this->logger->error("Convertion: " . $fileId . " " . $error, array("app" => $this->appName)); return ["error" => $error]; } $fileName = $file->getName(); $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); $format = $this->config->formats[$ext]; if (!isset($format)) { $this->logger->info("Format is not supported for editing: " . $fileName, array("app" => $this->appName)); return ["error" => $this->trans->t("Format is not supported")]; } $userId = $this->userSession->getUser()->getUID(); $userFolder = $this->root->getUserFolder($userId); $folderPath = $userFolder->getRelativePath($file->getParent()->getPath()); $folderLink = $this->urlGenerator->linkToRouteAbsolute("files.view.index", [ "dir" => $folderPath, "scrollto" => $file->getName() ]); $fileId = $file->getId(); $hashCallback = $this->crypt->GetHash(["fileId" => $fileId, "userId" => $userId, "action" => "track"]); $fileUrl = $this->getUrl($file); $key = $this->getKey($file); $canEdit = isset($format["edit"]) && $format["edit"]; $callback = ($file->isUpdateable() && $canEdit ? $this->urlGenerator->linkToRouteAbsolute($this->appName . ".callback.track", ["doc" => $hashCallback]) : ""); if (!empty($this->config->GetStorageUrl())) { $callback = str_replace($this->urlGenerator->getAbsoluteURL("/"), $this->config->GetStorageUrl(), $callback); } $type = "desktop"; if (\OC::$server->getRequest()->isUserAgent([$this::USER_AGENT_MOBILE])) { $type = "mobile"; } $params = [ "document" => [ "fileType" => $ext, "key" => DocumentService::GenerateRevisionId($key), "title" => $fileName, "url" => $fileUrl, ], "documentType" => $format["type"], "editorConfig" => [ "callbackUrl" => $callback, "customization" => [ "goback" => [ "url" => $folderLink ] ], "lang" => str_replace("_", "-", \OC::$server->getL10NFactory("")->get("")->getLanguageCode()), "mode" => (empty($callback) ? "view" : "edit"), "user" => [ "id" => $userId, "name" => $this->userSession->getUser()->getDisplayName() ] ], "type" => $type ]; $params = $this->setCustomization($params); 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 . " with key " . $key, array("app" => $this->appName)); return $params; } /** * Getting file by identifier * * @param integer $fileId - file identifier * * @return array */ private function getFile($fileId) { if (empty($fileId)) { return [NULL, $this->trans->t("FileId is empty")]; } $files = $this->root->getById($fileId); if (empty($files)) { return [NULL, $this->trans->t("File not found")]; } $file = $files[0]; if (!$file->isReadable()) { return [NULL, $this->trans->t("You do not have enough permissions to view the file")]; } return [$file, NULL]; } /** * Generate unique document identifier * * @param \OCP\Files\File - file * * @return string */ private function getKey($file) { $fileId = $file->getId(); $key = $fileId . "_" . $file->getMtime(); $ownerId = $file->getOwner()->getUID(); try { $this->root->getUserFolder($ownerId); } catch (NoUserException $e) { $ownerId = $this->userSession->getUser()->getUID(); } $ownerView = new View("/" . $ownerId . "/files"); $filePath = $ownerView->getPath($fileId); $versions = []; if (App::isEnabled("files_versions")) { $versions = Storage::getVersions($ownerId, $filePath); } $countVersions = count($versions); if ($countVersions > 0) { $key = $key . "_" . $countVersions; } return $key; } /** * Generate secure link to download document * * @param \OCP\Files\File - file * * @return string */ private function getUrl($file) { $fileId = $file->getId(); $userId = $this->userSession->getUser()->getUID(); $hashUrl = $this->crypt->GetHash(["fileId" => $fileId, "userId" => $userId, "action" => "download"]); $fileUrl = $this->urlGenerator->linkToRouteAbsolute($this->appName . ".callback.download", ["doc" => $hashUrl]); if (!empty($this->config->GetStorageUrl())) { $fileUrl = str_replace($this->urlGenerator->getAbsoluteURL("/"), $this->config->GetStorageUrl(), $fileUrl); } return $fileUrl; } /** * Set customization parameters * * @param array params - file parameters * * @return array */ private function setCustomization($params) { $customer = $this->config->getSystemValue($this->config->_customization_customer); if (isset($customer)) { $params["editorConfig"]["customization"]["customer"] = $customer; } $feedback = $this->config->getSystemValue($this->config->_customization_feedback); if (isset($feedback)) { $params["editorConfig"]["customization"]["feedback"] = $feedback; } $logo = $this->config->getSystemValue($this->config->_customization_logo); if (isset($logo)) { $params["editorConfig"]["customization"]["logo"] = $logo; } return $params; } }