From d9e5efbf47a666d4982dcc38800609eb5827854d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Mon, 11 Nov 2019 14:23:42 +0100 Subject: [PATCH 1/3] Add dav plugin for rich workspaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- appinfo/info.xml | 10 ++- lib/Controller/WorkspaceController.php | 29 +++----- lib/DAV/WorkspacePlugin.php | 97 ++++++++++++++++++++++++++ lib/Service/WorkspaceService.php | 31 ++++++++ 4 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 lib/DAV/WorkspacePlugin.php create mode 100644 lib/Service/WorkspaceService.php diff --git a/appinfo/info.xml b/appinfo/info.xml index 8c857f725ba..c26f7847497 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -11,11 +11,14 @@ - **💾 Open format:** Files are saved as [Markdown](https://en.wikipedia.org/wiki/Markdown), so you can edit them from any other text app too. - **✊ Strong foundation:** We use [🐈 tiptap](https://tiptap.scrumpy.io) which is based on [🦉 ProseMirror](https://prosemirror.net) – huge thanks to them! ]]> - 1.2.0 + 1.2.3 agpl Julius Härtl Text + + + office https://github.com/nextcloud/text https://github.com/nextcloud/text/issues @@ -29,4 +32,9 @@ OCA\Text\Cron\Cleanup + + + OCA\Text\DAV\WorkspacePlugin + + diff --git a/lib/Controller/WorkspaceController.php b/lib/Controller/WorkspaceController.php index 47f7cb6eaf6..adeb0a76f89 100644 --- a/lib/Controller/WorkspaceController.php +++ b/lib/Controller/WorkspaceController.php @@ -48,6 +48,7 @@ use OCA\Text\AppInfo\Application; use OCA\Text\Service\SessionService; +use OCA\Text\Service\WorkspaceService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -68,17 +69,18 @@ class WorkspaceController extends OCSController { /** @var IManager */ private $shareManager; - private const SUPPORTED_FILENAMES = [ - 'README.md', - 'Readme.md', - 'readme.md' - ]; + /** @var WorkspaceService */ + private $workspaceService; + /** @var string */ + private $userId; - public function __construct($appName, IRequest $request, IRootFolder $rootFolder, IManager $shareManager, $userId) { + + public function __construct($appName, IRequest $request, IRootFolder $rootFolder, IManager $shareManager, WorkspaceService $workspaceService, $userId) { parent::__construct($appName, $request); $this->rootFolder = $rootFolder; $this->shareManager = $shareManager; + $this->workspaceService = $workspaceService; $this->userId = $userId; } @@ -92,7 +94,7 @@ public function folder(string $path = '/'): DataResponse { try { $folder = $this->rootFolder->getUserFolder($this->userId)->get($path); if ($folder instanceof Folder) { - $file = $this->getFile($folder); + $file = $this->workspaceService->getFile($folder); if ($file === null) { return new DataResponse(['message' => 'No workspace file found'], Http::STATUS_NOT_FOUND); } @@ -122,7 +124,7 @@ public function publicFolder(string $shareToken, string $path = '/'): DataRespon $share = $this->shareManager->getShareByToken($shareToken); $folder = $share->getNode()->get($path); if ($folder instanceof Folder) { - $file = $this->getFile($folder); + $file = $this->workspaceService->getFile($folder); if ($file === null) { return new DataResponse(['message' => 'No workspace file found'], Http::STATUS_NOT_FOUND); } @@ -143,15 +145,4 @@ public function publicFolder(string $shareToken, string $path = '/'): DataRespon } } - private function getFile(Folder $folder) { - $file = null; - foreach (self::SUPPORTED_FILENAMES as $filename) { - if ($folder->nodeExists($filename)) { - $file = $folder->get($filename); - continue; - } - } - return $file; - } - } diff --git a/lib/DAV/WorkspacePlugin.php b/lib/DAV/WorkspacePlugin.php new file mode 100644 index 00000000000..a566838a8c4 --- /dev/null +++ b/lib/DAV/WorkspacePlugin.php @@ -0,0 +1,97 @@ + + * + * @author Julius Härtl + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Text\DAV; + +use OC\Files\Node\File; +use OC\Files\Node\Folder; +use OCA\DAV\Connector\Sabre\Directory; +use OCA\DAV\Files\FilesHome; +use OCA\Text\Service\WorkspaceService; +use OCP\Files\IRootFolder; +use Sabre\DAV\INode; +use Sabre\DAV\PropFind; +use Sabre\DAV\Server; +use Sabre\DAV\ServerPlugin; + +class WorkspacePlugin extends ServerPlugin { + + public const WORKSPACE = '{http://nextcloud.org/ns}rich-workspace'; + + /** @var Server */ + private $server; + + /** @var WorkspaceService */ + private $workspaceService; + + /** @var IRootFolder */ + private $rootFolder; + + /** @var string|null */ + private $userId; + + public function __construct(WorkspaceService $workspaceService, IRootFolder $rootFolder, $userId) { + $this->workspaceService = $workspaceService; + $this->rootFolder = $rootFolder; + $this->userId = $userId; + } + + /** + * This initializes the plugin. + * + * This function is called by Sabre\DAV\Server, after + * addPlugin is called. + * + * This method should set up the required event subscriptions. + * + * @param Server $server + * @return void + */ + function initialize(Server $server) { + $this->server = $server; + + $this->server->on('propFind', [$this, 'propFind']); + } + + + public function propFind(PropFind $propFind, INode $node) { + if (!$node instanceof Directory && !$node instanceof FilesHome) { + return; + } + + $propFind->handle(self::WORKSPACE, function () use ($node) { + /** @var Folder[] $nodes */ + $nodes = $this->rootFolder->getUserFolder($this->userId)->getById($node->getId()); + if (count($nodes) > 0) { + /** @var File $file */ + $file = $this->workspaceService->getFile($nodes[0]); + if ($file instanceof File) { + return $file->getContent(); + } + } + }); + + } + +} diff --git a/lib/Service/WorkspaceService.php b/lib/Service/WorkspaceService.php new file mode 100644 index 00000000000..7613708148b --- /dev/null +++ b/lib/Service/WorkspaceService.php @@ -0,0 +1,31 @@ +nodeExists($filename)) { + try { + $file = $folder->get($filename); + } catch (NotFoundException $e) { + } + continue; + } + } + return $file; + } +} From a669c37a2e7ec51fa626f35af1f56c04b329f7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Mon, 18 Nov 2019 14:07:18 +0100 Subject: [PATCH 2/3] Only show partial content of unfocused workspace on mobile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- src/components/EditorWrapper.vue | 8 +++++++- src/views/RichWorkspace.vue | 27 +++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/EditorWrapper.vue b/src/components/EditorWrapper.vue index f68b0de6965..5c8cf17e3f6 100644 --- a/src/components/EditorWrapper.vue +++ b/src/components/EditorWrapper.vue @@ -46,7 +46,7 @@ -
+
{ + this.$emit('focus') + }) + this.tiptap.on('blur', () => { + this.$emit('blur') + }) this.syncService.state = this.tiptap.state }) }) diff --git a/src/views/RichWorkspace.vue b/src/views/RichWorkspace.vue index 76f9977865b..5de4a105bf4 100644 --- a/src/views/RichWorkspace.vue +++ b/src/views/RichWorkspace.vue @@ -21,7 +21,7 @@ --> @@ -62,6 +64,7 @@ export default { }, data() { return { + focus: false, file: null, loaded: false, ready: false, @@ -158,4 +161,24 @@ export default { #rich-workspace::v-deep .editor__content { margin: 0; } + + @media only screen and (max-width: 1024px) { + #rich-workspace:not(.focus) { + max-height: 30vh; + position: relative; + overflow: hidden; + } + #rich-workspace:not(.focus):not(.icon-loading):after { + content: ''; + position: absolute; + z-index: 1; + bottom: 0; + left: 0; + pointer-events: none; + background-image: linear-gradient(to bottom, rgba(0,0,0, 0), var(--color-main-background)); + width: 100%; + height: 4em; + } + } + From da1ef876909c482fc6d0fa5f98c5c088cf62f3de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Mon, 18 Nov 2019 14:08:05 +0100 Subject: [PATCH 3/3] Use translatable readme.md name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- lib/Service/WorkspaceService.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/Service/WorkspaceService.php b/lib/Service/WorkspaceService.php index 7613708148b..14b4bef905d 100644 --- a/lib/Service/WorkspaceService.php +++ b/lib/Service/WorkspaceService.php @@ -6,18 +6,26 @@ use OCP\Files\Folder; use OCP\Files\NotFoundException; +use OCP\IL10N; class WorkspaceService { - private const SUPPORTED_FILENAMES = [ + private const SUPPORTED_STATIC_FILENAMES = [ 'README.md', 'Readme.md', 'readme.md' ]; + /** @var IL10N */ + private $l10n; + + public function __construct(IL10N $l10n) { + $this->l10n = $l10n; + } + public function getFile(Folder $folder) { $file = null; - foreach (self::SUPPORTED_FILENAMES as $filename) { + foreach ($this->getSupportedFilenames() as $filename) { if ($folder->nodeExists($filename)) { try { $file = $folder->get($filename); @@ -28,4 +36,10 @@ public function getFile(Folder $folder) { } return $file; } + + private function getSupportedFilenames() { + return array_merge([ + $this->l10n->t('Readme') . '.md' + ], self::SUPPORTED_STATIC_FILENAMES); + } }