is_dir($path)) { return true; } if (!$createIfNotExist) { return false; } $view->mkdir($path); return true; } /** * Get view and path for changes * * @param string $userId - user id * @param FileInfo $fileInfo - file info * @param bool $createIfNotExist - create folder if not exist * * @return array */ private static function getView($userId, $fileInfo, $createIfNotExist = false) { $fileId = null; if ($fileInfo !== null) { $fileId = $fileInfo->getId(); if ($fileInfo->getStorage()->instanceOfStorage(\OCA\GroupFolders\Mount\GroupFolderStorage::class)) { $view = new View("/" . self::$groupFolderName); } else { $view = new View("/" . $userId); } } else { $view = new View("/" . $userId); } $path = self::$appName; if (!self::checkFolderExist($view, $path, $createIfNotExist)) { return [null, null]; } if ($fileId === null) { return [$view, $path]; } $path = $path . "/" . $fileId; if (!self::checkFolderExist($view, $path, $createIfNotExist)) { return [null, null]; } return [$view, $path]; } /** * Get changes from stored to history object * * @param string $ownerId - file owner id * @param FileInfo $fileInfo - file info * @param string $versionId - file version * @param string $prevVersion - previous version for check * * @return array */ public static function getHistoryData($ownerId, $fileInfo, $versionId, $prevVersion) { $logger = \OCP\Log\logger('onlyoffice'); if ($ownerId === null || $fileInfo === null) { return null; } $fileId = $fileInfo->getId(); list($view, $path) = self::getView($ownerId, $fileInfo); if ($view === null) { return null; } $historyPath = $path . "/" . $versionId . self::$historyExt; if (!$view->file_exists($historyPath)) { return null; } $historyDataString = $view->file_get_contents($historyPath); try { $historyData = json_decode($historyDataString, true); if ($historyData["prev"] !== $prevVersion) { $logger->debug("getHistoryData: previous $prevVersion != " . $historyData["prev"], ["app" => self::$appName]); $view->unlink($historyPath); $logger->debug("getHistoryData: delete $historyPath", ["app" => self::$appName]); $changesPath = $path . "/" . $versionId . self::$changesExt; if ($view->file_exists($changesPath)) { $view->unlink($changesPath); $logger->debug("getHistoryData: delete $changesPath", ["app" => self::$appName]); } return null; } return $historyData; } catch (\Exception $e) { $logger->error("getHistoryData: $fileId $versionId", ['exception' => $e]); return null; } } /** * Check if changes is stored * * @param string $ownerId - file owner id * @param FileInfo $fileInfo - file info * @param string $versionId - file version * * @return bool */ public static function hasChanges($ownerId, $fileInfo, $versionId) { if ($ownerId === null || $fileInfo === null) { return false; } list($view, $path) = self::getView($ownerId, $fileInfo); if ($view === null) { return false; } $changesPath = $path . "/" . $versionId . self::$changesExt; return $view->file_exists($changesPath); } /** * Get changes file * * @param string $ownerId - file owner id * @param FileInfo $fileInfo - file info * @param string $versionId - file version * * @return File */ public static function getChangesFile($ownerId, $fileInfo, $versionId) { if ($ownerId === null || $fileInfo === null) { return null; } $fileId = $fileInfo->getId(); list($view, $path) = self::getView($ownerId, $fileInfo); if ($view === null) { return null; } $changesPath = $path . "/" . $versionId . self::$changesExt; if (!$view->file_exists($changesPath)) { return null; } $changesInfo = $view->getFileInfo($changesPath); $rootView = \OCP\Server::get(View::class); $root = \OCP\Server::get(IRootFolder::class); $changes = new File($root, $rootView, $view->getAbsolutePath($changesPath), $changesInfo); \OCP\Log\logger('onlyoffice')->debug("getChangesFile: $fileId for $ownerId get changes $changesPath", ["app" => self::$appName]); return $changes; } /** * Save history to storage * * @param FileInfo $fileInfo - file info * @param array $history - file history * @param string $changes - file changes * @param string $prevVersion - previous version for check */ public static function saveHistory($fileInfo, $history, $changes, $prevVersion) { $logger = \OCP\Log\logger('onlyoffice'); if ($fileInfo === null) { return; } $owner = $fileInfo->getOwner(); if ($owner === null) { return; } if (empty($history) || empty($changes)) { return; } if ($fileInfo->getStorage()->instanceOfStorage(SharingExternalStorage::class)) { return; } $ownerId = $owner->getUID(); $fileId = $fileInfo->getId(); $versionId = $fileInfo->getMtime(); list($view, $path) = self::getView($ownerId, $fileInfo, true); try { $changesPath = $path . "/" . $versionId . self::$changesExt; $view->touch($changesPath); $view->file_put_contents($changesPath, $changes); $history["prev"] = $prevVersion; $historyPath = $path . "/" . $versionId . self::$historyExt; $view->touch($historyPath); $view->file_put_contents($historyPath, json_encode($history)); $logger->debug("saveHistory: $fileId for $ownerId stored changes $changesPath history $historyPath", ["app" => self::$appName]); } catch (\Exception $e) { $logger->error("saveHistory: save $fileId history error", ['exception' => $e]); } } /** * Delete all versions of file * * @param string $ownerId - file owner id * @param FileInfo $fileInfo - file info */ public static function deleteAllVersions($ownerId, $fileInfo = null) { $logger = \OCP\Log\logger('onlyoffice'); $fileId = null; if ($fileInfo !== null) { $fileId = $fileInfo->getId(); } $logger->debug("deleteAllVersions $ownerId $fileId", ["app" => self::$appName]); if ($ownerId === null) { return; } list($view, $path) = self::getView($ownerId, $fileInfo); if ($view === null) { return; } $view->unlink($path); } /** * Delete changes and history * * @param string $ownerId - file owner id * @param FileInfo $fileInfo - file info * @param string $versionId - file version */ public static function deleteVersion($ownerId, $fileInfo, $versionId) { if ($ownerId === null) { return; } if ($fileInfo === null || empty($versionId)) { return; } $logger = \OCP\Log\logger('onlyoffice'); $fileId = $fileInfo->getId(); $logger->debug("deleteVersion $fileId ($versionId)", ["app" => self::$appName]); list($view, $path) = self::getView($ownerId, $fileInfo); if ($view === null) { return null; } $historyPath = $path . "/" . $versionId . self::$historyExt; if ($view->file_exists($historyPath)) { $view->unlink($historyPath); $logger->debug("deleteVersion $historyPath", ["app" => self::$appName]); } $changesPath = $path . "/" . $versionId . self::$changesExt; if ($view->file_exists($changesPath)) { $view->unlink($changesPath); $logger->debug("deleteVersion $changesPath", ["app" => self::$appName]); } } /** * Clear all version history */ public static function clearHistory() { $logger = \OCP\Log\logger('onlyoffice'); $userDatabase = new Database(); $userIds = $userDatabase->getUsers(); $view = new View("/"); $groupFolderView = new View("/" . self::$groupFolderName); foreach ($userIds as $userId) { $path = $userId . "/" . self::$appName; if ($view->file_exists($path)) { $view->unlink($path); } if ($groupFolderView->file_exists($path)) { $groupFolderView->unlink($path); } } $logger->debug("clear all history", ["app" => self::$appName]); } /** * Save file author * * @param FileInfo $fileInfo - file info * @param IUser $author - version author */ public static function saveAuthor($fileInfo, $author) { $logger = \OCP\Log\logger('onlyoffice'); if ($fileInfo === null || $author === null) { return; } $owner = $fileInfo->getOwner(); if ($owner === null) { return; } if ($fileInfo->getStorage()->instanceOfStorage(SharingExternalStorage::class)) { return; } $ownerId = $owner->getUID(); $fileId = $fileInfo->getId(); $versionId = $fileInfo->getMtime(); list($view, $path) = self::getView($ownerId, $fileInfo, true); try { $authorPath = $path . "/" . $versionId . self::$authorExt; $view->touch($authorPath); $authorData = [ "id" => $author->getUID(), "name" => $author->getDisplayName() ]; $view->file_put_contents($authorPath, json_encode($authorData)); $logger->debug("saveAuthor: $fileId for $ownerId stored author $authorPath", ["app" => self::$appName]); } catch (\Exception $e) { $logger->error("saveAuthor: save $fileId author error", ['exception' => $e]); } } /** * Get version author id and name * * @param string $ownerId - file owner id * @param FileInfo $fileInfo - file info * @param string $versionId - file version * * @return array */ public static function getAuthor($ownerId, $fileInfo, $versionId) { if ($ownerId === null || $fileInfo === null) { return null; } $fileId = $fileInfo->getId(); list($view, $path) = self::getView($ownerId, $fileInfo); if ($view === null) { return null; } $authorPath = $path . "/" . $versionId . self::$authorExt; if (!$view->file_exists($authorPath)) { return null; } $authorDataString = $view->file_get_contents($authorPath); $author = json_decode($authorDataString, true); \OCP\Log\logger('onlyoffice')->debug("getAuthor: $fileId v.$versionId for $ownerId get author $authorPath", ["app" => self::$appName]); return $author; } /** * Delete version author info * * @param string $ownerId - file owner id * @param FileInfo $fileInfo - file info * @param string $versionId - file version */ public static function deleteAuthor($ownerId, $fileInfo, $versionId) { $logger = \OCP\Log\logger('onlyoffice'); $fileId = $fileInfo->getId(); $logger->debug("deleteAuthor $fileId ($versionId)", ["app" => self::$appName]); if ($ownerId === null) { return; } if ($fileInfo === null || empty($versionId)) { return; } list($view, $path) = self::getView($ownerId, $fileInfo); if ($view === null) { return null; } $authorPath = $path . "/" . $versionId . self::$authorExt; if ($view->file_exists($authorPath)) { $view->unlink($authorPath); $logger->debug("deleteAuthor $authorPath", ["app" => self::$appName]); } } /** * Get version compare with files_versions */ public static function getFilesVersionAppInfoCompareResult() { $filesVersionAppInfo = \OC::$server->getAppManager()->getAppInfo("files_versions"); return \version_compare($filesVersionAppInfo["version"], "1.19"); } /** * Reverese or not versions array * * @param array $versions - versions array */ public static function processVersionsArray($versions) { if (self::getFilesVersionAppInfoCompareResult() === -1) { return array_reverse($versions); } else { foreach ($versions as $key => $version) { if ($version->getRevisionId() === $version->getSourceFile()->getMTime()) { array_splice($versions, $key, 1); break; } } return $versions; } } }