Skip to content

Commit 964f015

Browse files
committed
fix(attachments): Adjust path to attachments folder when copying a node
Signed-off-by: Jonas <jonas@freesources.org>
1 parent ee530a2 commit 964f015

3 files changed

Lines changed: 113 additions & 6 deletions

File tree

lib/Listeners/NodeCopiedListener.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCP\EventDispatcher\IEventListener;
1414
use OCP\Files\Events\Node\NodeCopiedEvent;
1515
use OCP\Files\File;
16+
use OCP\Lock\ILockingProvider;
1617

1718
/**
1819
* @template-implements IEventListener<Event|NodeCopiedEvent>
@@ -36,6 +37,9 @@ public function handle(Event $event): void {
3637
&& $target->getMimeType() === 'text/markdown'
3738
) {
3839
$this->attachmentService->copyAttachments($source, $target);
40+
$target->unlock(ILockingProvider::LOCK_SHARED);
41+
AttachmentService::replaceAttachmentFolderId($source, $target);
42+
$target->lock(ILockingProvider::LOCK_SHARED);
3943
}
4044
}
4145
}

lib/Service/AttachmentService.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,4 +715,24 @@ public function copyAttachments(File $source, File $target): void {
715715
}
716716
}
717717
}
718+
719+
public static function replaceAttachmentFolderId(File $source, File $target): void {
720+
$sourceId = $source->getId();
721+
$targetId = $target->getId();
722+
$patterns = [
723+
// Replace `[title](.attachments.1/file.png)` with `[title](attachments.2/file.png)`
724+
// '/(\[[^]]+\]\(\s*\<?\.attachments\.)' . $sourceId . '(\/[^)]+\>?\s*\))/',
725+
'/(\[(?:\\\]|[^]])+\]\(\s*\<?\.attachments\.)' . $sourceId . '(\/[^)]+\>?\s*\))/',
726+
// Replace `[ref]: .attachments.1/file.png` with `[ref]: .attachments.2/file.png`
727+
'/(\[(?:\\\]|[^]])+\]:\s+.attachments\.)' . $sourceId . '(\/[^\s]+)/',
728+
];
729+
$replacements = [
730+
'${1}' . $targetId . '${2}',
731+
'${1}' . $targetId . '${2}',
732+
];
733+
$content = preg_replace($patterns, $replacements, $target->getContent());
734+
if ($content !== null) {
735+
$target->putContent($content);
736+
}
737+
}
718738
}

tests/unit/Service/AttachmentServiceTest.php

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
use OCA\Text\AppInfo\Application;
66
use OCA\Text\Service\AttachmentService;
7+
use OCP\Files\File;
78
use OCP\Files\Folder;
9+
use PHPUnit\Framework\TestCase;
810

9-
class AttachmentServiceTest extends \PHPUnit\Framework\TestCase {
10-
private static $attachmentNames = [
11+
class AttachmentServiceTest extends TestCase {
12+
private static array $attachmentNames = [
1113
'aaa.png',
1214
'aaa (2).png',
1315
'aaa 2).png',
@@ -21,12 +23,12 @@ class AttachmentServiceTest extends \PHPUnit\Framework\TestCase {
2123
'a`a`.png',
2224
];
2325

24-
public function testDummy() {
26+
public function testDummy(): void {
2527
$app = new Application();
2628
$this->assertEquals('text', $app::APP_NAME);
2729
}
2830

29-
public function testGetAttachmentNamesFromContent() {
31+
public function testGetAttachmentNamesFromContent(): void {
3032
$content = "some content\n";
3133
foreach (self::$attachmentNames as $name) {
3234
// this is how it's generated in MenuBar.vue
@@ -42,7 +44,7 @@ public function testGetAttachmentNamesFromContent() {
4244
}
4345
}
4446

45-
public function testGetAttachmentIdsFromContent() {
47+
public function testGetAttachmentIdsFromContent(): void {
4648
$urls = [
4749
'www.example.com',
4850
'http://example.com',
@@ -69,7 +71,7 @@ public function testGetAttachmentIdsFromContent() {
6971
$this->assertEquals(range(1, $id - 1), $computedIds);
7072
}
7173

72-
public function testGetUniqueFileName() {
74+
public function testGetUniqueFileName(): void {
7375
$fileNameList = [
7476
'foo.png',
7577
'bar',
@@ -104,4 +106,85 @@ public function testGetUniqueFileName() {
104106
$this->assertEquals('yay (4).png', AttachmentService::getUniqueFileName($folder, 'yay.png'));
105107
$this->assertEquals('file.ext (2).ext', AttachmentService::getUniqueFileName($folder, 'file.ext.ext'));
106108
}
109+
110+
// Replacement expected
111+
public static function contentReplacedProvider(): array {
112+
return [
113+
['![image.png](.attachments.1/image.png)', '![image.png](.attachments.2/image.png)'],
114+
// Spaces surrounding link URL
115+
['![image.png]( .attachments.1/image%20%282%29.png )', '![image.png]( .attachments.2/image%20%282%29.png )'],
116+
// Escaped square brackets in title
117+
['![title \[#1\]](.attachments.1/image.png)', '![title \[#1\]](.attachments.2/image.png)'],
118+
// Angle brackets surrounding link URL
119+
['![image.png](<.attachments.1/image.png>)', '![image.png](<.attachments.2/image.png>)'],
120+
// Spaces and angle brackets surrounding link URL
121+
['![image.png]( <.attachments.1/image.png> )', '![image.png]( <.attachments.2/image.png> )'],
122+
// Spaces in title
123+
['![title with space](.attachments.1/image.png)', '![title with space](.attachments.2/image.png)'],
124+
// Several links in a row
125+
['Some text\n\n![image.png](.attachments.1/image.png)\n\nMore text. [file.tar.gz](.attachments.1/file.tar.gz) ...', 'Some text\n\n![image.png](.attachments.2/image.png)\n\nMore text. [file.tar.gz](.attachments.2/file.tar.gz) ...'],
126+
// Reference link syntax
127+
['[reference]: .attachments.1/image.png', '[reference]: .attachments.2/image.png'],
128+
// Reference link syntax with title
129+
['[reference]: .attachments.1/image.png "title"', '[reference]: .attachments.2/image.png "title"'],
130+
// Reference link syntax with escaped square brackets in reference name
131+
['[reference \[#1\]]: .attachments.1/image.png "title"', '[reference \[#1\]]: .attachments.2/image.png "title"'],
132+
];
133+
}
134+
135+
// No replacement expected
136+
public static function contentNotReplacedProvider(): array {
137+
return [
138+
// Empty title
139+
[ '![](.attachments.1/image.png)' ],
140+
// Empty filename
141+
[ '![title](.attachments.1/)' ],
142+
// Wrong fileId #1
143+
[ '![image.png](.attachments.01/image.png)' ],
144+
// Wrong fileId #2
145+
[ '![image.png](.attachments.12/image.png)' ],
146+
// Normal brackets around title
147+
[ '!(image.png)(.attachments.1/image.png)' ],
148+
// Square brackets in title
149+
['![title [#1]](.attachments.1/image.png)' ],
150+
// Reference link syntax with square brackets in reference name
151+
['[reference [#1]]: .attachments.1/image.png' ],
152+
];
153+
}
154+
155+
/**
156+
* @dataProvider contentReplacedProvider
157+
*/
158+
public function testReplaceAttachmentFolderId(string $sourceContent, string $targetContent): void {
159+
$source = $this->createMock(File::class);
160+
$source->method('getId')->willReturn(1);
161+
$target = $this->createMock(File::class);
162+
$target->method('getId')->willReturn(2);
163+
$target->method('getContent')->willReturn($sourceContent);
164+
$replacedContent = '';
165+
$target->method('putContent')->willReturnCallback(function (string $content) use (&$replacedContent) {
166+
$replacedContent = $content;
167+
});
168+
169+
AttachmentService::replaceAttachmentFolderId($source, $target);
170+
$this->assertEquals($targetContent, $replacedContent);
171+
}
172+
173+
/**
174+
* @dataProvider contentNotReplacedProvider
175+
*/
176+
public function testReplaceAttachmentFolderIdNoReplace(string $sourceContent): void {
177+
$source = $this->createMock(File::class);
178+
$source->method('getId')->willReturn(1);
179+
$target = $this->createMock(File::class);
180+
$target->method('getId')->willReturn(2);
181+
$target->method('getContent')->willReturn($sourceContent);
182+
$replacedContent = '';
183+
$target->method('putContent')->willReturnCallback(function (string $content) use (&$replacedContent) {
184+
$replacedContent = $content;
185+
});
186+
187+
AttachmentService::replaceAttachmentFolderId($source, $target);
188+
$this->assertEquals($sourceContent, $replacedContent);
189+
}
107190
}

0 commit comments

Comments
 (0)