Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/text.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/AppInfo/Application.php10
-rw-r--r--lib/Listeners/BeforeNodeDeletedListener.php50
-rw-r--r--lib/Listeners/BeforeNodeRenamedListener.php53
-rw-r--r--lib/Listeners/NodeCopiedListener.php55
-rw-r--r--lib/Service/ImageService.php116
5 files changed, 273 insertions, 11 deletions
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index d15b783fa..a370e8056 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -27,6 +27,9 @@ namespace OCA\Text\AppInfo;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
+use OCA\Text\Listeners\BeforeNodeDeletedListener;
+use OCA\Text\Listeners\BeforeNodeRenamedListener;
+use OCA\Text\Listeners\NodeCopiedListener;
use OCA\Text\Listeners\FilesLoadAdditionalScriptsListener;
use OCA\Text\Listeners\FilesSharingLoadAdditionalScriptsListener;
use OCA\Text\Listeners\LoadViewerListener;
@@ -37,6 +40,9 @@ use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\DirectEditing\RegisterDirectEditorEvent;
+use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
+use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
+use OCP\Files\Events\Node\NodeCopiedEvent;
use OCP\Files\Template\ITemplateManager;
use OCP\Files\Template\TemplateFileCreator;
use OCP\IL10N;
@@ -53,6 +59,10 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(LoadViewer::class, LoadViewerListener::class);
$context->registerEventListener(LoadAdditionalScriptsEvent::class, FilesLoadAdditionalScriptsListener::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, FilesSharingLoadAdditionalScriptsListener::class);
+ // for image attachments
+ $context->registerEventListener(NodeCopiedEvent::class, NodeCopiedListener::class);
+ $context->registerEventListener(BeforeNodeRenamedEvent::class, BeforeNodeRenamedListener::class);
+ $context->registerEventListener(BeforeNodeDeletedEvent::class, BeforeNodeDeletedListener::class);
}
public function boot(IBootContext $context): void {
diff --git a/lib/Listeners/BeforeNodeDeletedListener.php b/lib/Listeners/BeforeNodeDeletedListener.php
new file mode 100644
index 000000000..eac7b28d6
--- /dev/null
+++ b/lib/Listeners/BeforeNodeDeletedListener.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021 Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @author Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Text\Listeners;
+
+use OCA\Text\Service\ImageService;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
+use OCP\Files\File;
+
+class BeforeNodeDeletedListener implements IEventListener {
+
+ private ImageService $imageService;
+
+ public function __construct(ImageService $imageService) {
+ $this->imageService = $imageService;
+ }
+
+ public function handle(Event $event): void {
+ if (!$event instanceof BeforeNodeDeletedEvent) {
+ return;
+ }
+ if ($event->getNode() instanceof File && $event->getNode()->getMimeType() === 'text/markdown') {
+ $this->imageService->deleteAttachments($event->getNode());
+ }
+ }
+}
diff --git a/lib/Listeners/BeforeNodeRenamedListener.php b/lib/Listeners/BeforeNodeRenamedListener.php
new file mode 100644
index 000000000..483c2f9a6
--- /dev/null
+++ b/lib/Listeners/BeforeNodeRenamedListener.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021 Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @author Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Text\Listeners;
+
+use OCA\Text\Service\ImageService;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
+use OCP\Files\File;
+
+class BeforeNodeRenamedListener implements IEventListener {
+
+ private ImageService $imageService;
+
+ public function __construct(ImageService $imageService) {
+ $this->imageService = $imageService;
+ }
+
+ public function handle(Event $event): void {
+ if (!$event instanceof BeforeNodeRenamedEvent) {
+ return;
+ }
+ if ($event->getSource() instanceof File
+ && $event->getSource()->getMimeType() === 'text/markdown'
+ && $event->getTarget() instanceof File
+ ) {
+ $this->imageService->moveAttachments($event->getSource(), $event->getTarget());
+ }
+ }
+}
diff --git a/lib/Listeners/NodeCopiedListener.php b/lib/Listeners/NodeCopiedListener.php
new file mode 100644
index 000000000..64b66c166
--- /dev/null
+++ b/lib/Listeners/NodeCopiedListener.php
@@ -0,0 +1,55 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021 Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @author Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Text\Listeners;
+
+use OCA\Text\Service\ImageService;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Files\Events\Node\NodeCopiedEvent;
+use OCP\Files\File;
+
+class NodeCopiedListener implements IEventListener {
+
+ private ImageService $imageService;
+
+ public function __construct(ImageService $imageService) {
+ $this->imageService = $imageService;
+ }
+
+ public function handle(Event $event): void {
+ if (!$event instanceof NodeCopiedEvent) {
+ return;
+ }
+ error_log('COPIED source ' . $event->getSource()->getId() . ' target '. $event->getTarget()->getId());
+ if ($event->getSource() instanceof File
+ && $event->getSource()->getMimeType() === 'text/markdown'
+ && $event->getTarget() instanceof File
+ && $event->getTarget()->getMimeType() === 'text/markdown'
+ ) {
+ $this->imageService->copyAttachments($event->getSource(), $event->getTarget());
+ }
+ }
+}
diff --git a/lib/Service/ImageService.php b/lib/Service/ImageService.php
index ae68a08a0..e9defe1a8 100644
--- a/lib/Service/ImageService.php
+++ b/lib/Service/ImageService.php
@@ -432,6 +432,25 @@ class ImageService {
return null;
}
+ private function getAttachmentDirectoryForFile(File $textFile): ?Folder {
+ $owner = $textFile->getOwner();
+ $ownerId = $owner->getUID();
+ $ownerUserFolder = $this->rootFolder->getUserFolder($ownerId);
+ $ownerTextFile = $ownerUserFolder->getById($textFile->getId());
+ if (count($ownerTextFile) > 0) {
+ $ownerTextFile = $ownerTextFile[0];
+ $ownerParentFolder = $ownerTextFile->getParent();
+ $attachmentFolderName = '.attachments.' . $textFile->getId();
+ if ($ownerParentFolder->nodeExists($attachmentFolderName)) {
+ $attachmentFolder = $ownerParentFolder->get($attachmentFolderName);
+ if ($attachmentFolder instanceof Folder) {
+ return $attachmentFolder;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Get a user file from file ID
* @param string $filePath
@@ -574,17 +593,7 @@ class ImageService {
$attachmentsById[$attNode->getId()] = $attNode;
}
- // get IDs of attachment listed in the markdown file content
- $matches = [];
- preg_match_all(
- '/\!\[[^\[\]]+\]\(text:\/\/image\?[^)]*imageFileId=([\d]+)[^)]*\)/',
- $textFile->getContent(),
- $matches,
- PREG_SET_ORDER
- );
- $contentAttachmentIds = array_map(function (array $match) {
- return $match[1] ?? null;
- }, $matches);
+ $contentAttachmentIds = $this->getAttachmentIdsFromContent($textFile->getContent());
$toDelete = array_diff(array_keys($attachmentsById), $contentAttachmentIds);
foreach ($toDelete as $id) {
@@ -595,4 +604,89 @@ class ImageService {
}
return 0;
}
+
+
+ /**
+ * Get IDs of attachment listed in the markdown file content
+ *
+ * @param string $content
+ * @return array
+ */
+ private function getAttachmentIdsFromContent(string $content): array {
+ $matches = [];
+ preg_match_all(
+ '/\!\[[^\[\]]+\]\(text:\/\/image\?[^)]*imageFileId=([\d]+)[^)]*\)/',
+ $content,
+ $matches,
+ PREG_SET_ORDER
+ );
+ return array_map(function (array $match) {
+ return $match[1] ?? null;
+ }, $matches);
+ }
+
+ /**
+ * @param File $source
+ * @param File $target
+ * @throws NotFoundException
+ * @throws \OCP\Files\InvalidPathException
+ * @throws \OCP\Files\NotPermittedException
+ * @throws \OCP\Lock\LockedException
+ */
+ public function moveAttachments(File $source, File $target): void {
+ // if the parent directory has changed
+ if ($source->getParent()->getPath() !== $target->getParent()->getPath()) {
+ // if there is an attachment dir for this file
+ $sourceAttachmentDir = $this->getAttachmentDirectoryForFile($source);
+ if ($sourceAttachmentDir !== null) {
+ $sourceAttachmentDir->move($target->getParent()->getPath() . '/' . $sourceAttachmentDir->getName());
+ }
+ }
+ }
+
+ /**
+ * @param File $source
+ * @throws NotFoundException
+ * @throws \OCP\Files\InvalidPathException
+ * @throws \OCP\Files\NotPermittedException
+ */
+ public function deleteAttachments(File $source): void {
+ // if there is an attachment dir for this file
+ $sourceAttachmentDir = $this->getAttachmentDirectoryForFile($source);
+ if ($sourceAttachmentDir !== null) {
+ $sourceAttachmentDir->delete();
+ }
+ }
+
+ public function copyAttachments(File $source, File $target): void {
+ $sourceAttachmentDir = $this->getAttachmentDirectoryForFile($source);
+ if ($sourceAttachmentDir !== null) {
+ // create a new attachment dir next to the new file
+ $targetAttachmentDir = $this->getOrCreateAttachmentDirectoryForFile($target);
+ $markdownContent = $source->getContent();
+ $sourceAttachmentIds = $this->getAttachmentIdsFromContent($markdownContent);
+ // replace the text file ID in each attachment link in the markdown content
+ $markdownContent = preg_replace(
+ '/\!\[([^\[\]]+)\]\(text:\/\/image\?textFileId=' . $source->getId() . '&imageFileId=([\d]+[^)]*)\)/',
+ '![$1](text://image?textFileId=' . $target->getId() . '&imageFileId=$2)',
+ $markdownContent
+ );
+ // copy the attachments and replace attachment file IDs in the markdown content
+ foreach ($sourceAttachmentIds as $sourceAttachmentId) {
+ $sourceAttachment = $sourceAttachmentDir->getById($sourceAttachmentId);
+ if (count($sourceAttachment) > 0 && $sourceAttachment[0] instanceof File) {
+ $sourceAttachment = $sourceAttachment[0];
+ $copied = $targetAttachmentDir->newFile($sourceAttachment->getName(), $sourceAttachment->getContent());
+ $copiedId = $copied->getId();
+ $markdownContent = preg_replace(
+ '/\!\[([^\[\]]+)\]\(text:\/\/image\?([^)]*)imageFileId=' . $sourceAttachmentId . '([^)]*)\)/',
+ '![$1](text://image?$2imageFileId=' . $copiedId . '$3)',
+ $markdownContent
+ );
+ }
+ }
+ // FIXME this is not possible because the target file is locked at this point
+ $target->putContent($markdownContent);
+ }
+ }
}