Skip to content

Commit 48c101a

Browse files
committed
ZIP Imports: Finished off core import logic
1 parent 378f0d5 commit 48c101a

File tree

5 files changed

+113
-28
lines changed

5 files changed

+113
-28
lines changed

app/Exceptions/ZipImportException.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class ZipImportException extends \Exception
77
public function __construct(
88
public array $errors
99
) {
10-
parent::__construct();
10+
$message = "Import failed with errors:" . implode("\n", $this->errors);
11+
parent::__construct($message);
1112
}
1213
}

app/Exports/Controllers/ImportController.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,21 @@ public function run(int $id, Request $request)
7979
$import = $this->imports->findVisible($id);
8080
$parent = null;
8181

82-
if ($import->getType() === 'page' || $import->getType() === 'chapter') {
82+
if ($import->type === 'page' || $import->type === 'chapter') {
8383
$data = $this->validate($request, [
8484
'parent' => ['required', 'string']
8585
]);
8686
$parent = $data['parent'];
8787
}
8888

89-
// TODO - Run import
90-
// TODO - Validate again before
91-
// TODO - Check permissions before (create for main item, create for children, create for related items [image, attachments])
89+
$entity = $this->imports->runImport($import, $parent);
90+
if ($entity) {
91+
$this->logActivity(ActivityType::IMPORT_RUN, $import);
92+
return redirect($entity->getUrl());
93+
}
9294
// TODO - Redirect to result
9395
// TODO - Or redirect back with errors
96+
return 'failed';
9497
}
9598

9699
/**

app/Exports/ImportRepo.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace BookStack\Exports;
44

5+
use BookStack\Entities\Models\Entity;
56
use BookStack\Entities\Queries\EntityQueries;
67
use BookStack\Exceptions\FileUploadException;
78
use BookStack\Exceptions\ZipExportException;
9+
use BookStack\Exceptions\ZipImportException;
810
use BookStack\Exceptions\ZipValidationException;
911
use BookStack\Exports\ZipExports\Models\ZipExportBook;
1012
use BookStack\Exports\ZipExports\Models\ZipExportChapter;
@@ -95,9 +97,9 @@ public function storeFromUpload(UploadedFile $file): Import
9597
}
9698

9799
/**
98-
* @throws ZipValidationException
100+
* @throws ZipValidationException|ZipImportException
99101
*/
100-
public function runImport(Import $import, ?string $parent = null)
102+
public function runImport(Import $import, ?string $parent = null): ?Entity
101103
{
102104
$parentModel = null;
103105
if ($import->type === 'page' || $import->type === 'chapter') {

app/Exports/ZipExports/ZipImportReferences.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public function replaceReferences(): void
110110
{
111111
foreach ($this->books as $book) {
112112
$exportBook = $this->zipExportBookMap[$book->id];
113-
$content = $exportBook->description_html || '';
113+
$content = $exportBook->description_html ?? '';
114114
$parsed = $this->parser->parseReferences($content, $this->handleReference(...));
115115

116116
$this->baseRepo->update($book, [
@@ -120,7 +120,7 @@ public function replaceReferences(): void
120120

121121
foreach ($this->chapters as $chapter) {
122122
$exportChapter = $this->zipExportChapterMap[$chapter->id];
123-
$content = $exportChapter->description_html || '';
123+
$content = $exportChapter->description_html ?? '';
124124
$parsed = $this->parser->parseReferences($content, $this->handleReference(...));
125125

126126
$this->baseRepo->update($chapter, [

app/Exports/ZipExports/ZipImportRunner.php

Lines changed: 98 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,42 @@
1212
use BookStack\Exceptions\ZipExportException;
1313
use BookStack\Exceptions\ZipImportException;
1414
use BookStack\Exports\Import;
15+
use BookStack\Exports\ZipExports\Models\ZipExportAttachment;
1516
use BookStack\Exports\ZipExports\Models\ZipExportBook;
1617
use BookStack\Exports\ZipExports\Models\ZipExportChapter;
18+
use BookStack\Exports\ZipExports\Models\ZipExportImage;
1719
use BookStack\Exports\ZipExports\Models\ZipExportPage;
1820
use BookStack\Exports\ZipExports\Models\ZipExportTag;
21+
use BookStack\Uploads\Attachment;
22+
use BookStack\Uploads\AttachmentService;
1923
use BookStack\Uploads\FileStorage;
24+
use BookStack\Uploads\Image;
2025
use BookStack\Uploads\ImageService;
2126
use Illuminate\Http\UploadedFile;
2227

2328
class ZipImportRunner
2429
{
25-
protected array $tempFilesToCleanup = []; // TODO
30+
protected array $tempFilesToCleanup = [];
2631

2732
public function __construct(
2833
protected FileStorage $storage,
2934
protected PageRepo $pageRepo,
3035
protected ChapterRepo $chapterRepo,
3136
protected BookRepo $bookRepo,
3237
protected ImageService $imageService,
38+
protected AttachmentService $attachmentService,
3339
protected ZipImportReferences $references,
3440
) {
3541
}
3642

3743
/**
44+
* Run the import.
45+
* Performs re-validation on zip, validation on parent provided, and permissions for importing
46+
* the planned content, before running the import process.
47+
* Returns the top-level entity item which was imported.
3848
* @throws ZipImportException
3949
*/
40-
public function run(Import $import, ?Entity $parent = null): void
50+
public function run(Import $import, ?Entity $parent = null): ?Entity
4151
{
4252
$zipPath = $this->getZipPath($import);
4353
$reader = new ZipExportReader($zipPath);
@@ -63,15 +73,40 @@ public function run(Import $import, ?Entity $parent = null): void
6373
}
6474

6575
$this->ensurePermissionsPermitImport($exportModel);
76+
$entity = null;
77+
78+
if ($exportModel instanceof ZipExportBook) {
79+
$entity = $this->importBook($exportModel, $reader);
80+
} else if ($exportModel instanceof ZipExportChapter) {
81+
$entity = $this->importChapter($exportModel, $parent, $reader);
82+
} else if ($exportModel instanceof ZipExportPage) {
83+
$entity = $this->importPage($exportModel, $parent, $reader);
84+
}
6685

67-
// TODO - Run import
6886
// TODO - In transaction?
6987
// TODO - Revert uploaded files if goes wrong
7088
// TODO - Attachments
7189
// TODO - Images
7290
// (Both listed/stored in references)
7391

7492
$this->references->replaceReferences();
93+
94+
$reader->close();
95+
$this->cleanup();
96+
97+
dd('stop');
98+
99+
// TODO - Delete import/zip after import?
100+
// Do this in parent repo?
101+
102+
return $entity;
103+
}
104+
105+
protected function cleanup()
106+
{
107+
foreach ($this->tempFilesToCleanup as $file) {
108+
unlink($file);
109+
}
75110
}
76111

77112
protected function importBook(ZipExportBook $exportBook, ZipExportReader $reader): Book
@@ -83,17 +118,26 @@ protected function importBook(ZipExportBook $exportBook, ZipExportReader $reader
83118
'tags' => $this->exportTagsToInputArray($exportBook->tags ?? []),
84119
]);
85120

86-
// TODO - Parse/format description_html references
87-
88121
if ($book->cover) {
89122
$this->references->addImage($book->cover, null);
90123
}
91124

92-
// TODO - Pages
93-
foreach ($exportBook->chapters as $exportChapter) {
94-
$this->importChapter($exportChapter, $book, $reader);
125+
$children = [
126+
...$exportBook->chapters,
127+
...$exportBook->pages,
128+
];
129+
130+
usort($children, function (ZipExportPage|ZipExportChapter $a, ZipExportPage|ZipExportChapter $b) {
131+
return ($a->priority ?? 0) - ($b->priority ?? 0);
132+
});
133+
134+
foreach ($children as $child) {
135+
if ($child instanceof ZipExportChapter) {
136+
$this->importChapter($child, $book, $reader);
137+
} else if ($child instanceof ZipExportPage) {
138+
$this->importPage($child, $book, $reader);
139+
}
95140
}
96-
// TODO - Sort chapters/pages by order
97141

98142
$this->references->addBook($book, $exportBook);
99143

@@ -108,17 +152,14 @@ protected function importChapter(ZipExportChapter $exportChapter, Book $parent,
108152
'tags' => $this->exportTagsToInputArray($exportChapter->tags ?? []),
109153
], $parent);
110154

111-
// TODO - Parse/format description_html references
112-
113155
$exportPages = $exportChapter->pages;
114156
usort($exportPages, function (ZipExportPage $a, ZipExportPage $b) {
115157
return ($a->priority ?? 0) - ($b->priority ?? 0);
116158
});
117159

118160
foreach ($exportPages as $exportPage) {
119-
//
161+
$this->importPage($exportPage, $chapter, $reader);
120162
}
121-
// TODO - Pages
122163

123164
$this->references->addChapter($chapter, $exportChapter);
124165

@@ -129,11 +170,13 @@ protected function importPage(ZipExportPage $exportPage, Book|Chapter $parent, Z
129170
{
130171
$page = $this->pageRepo->getNewDraftPage($parent);
131172

132-
// TODO - Import attachments
133-
// TODO - Add attachment references
134-
// TODO - Import images
135-
// TODO - Add image references
136-
// TODO - Parse/format HTML
173+
foreach ($exportPage->attachments as $exportAttachment) {
174+
$this->importAttachment($exportAttachment, $page, $reader);
175+
}
176+
177+
foreach ($exportPage->images as $exportImage) {
178+
$this->importImage($exportImage, $page, $reader);
179+
}
137180

138181
$this->pageRepo->publishDraft($page, [
139182
'name' => $exportPage->name,
@@ -147,6 +190,40 @@ protected function importPage(ZipExportPage $exportPage, Book|Chapter $parent, Z
147190
return $page;
148191
}
149192

193+
protected function importAttachment(ZipExportAttachment $exportAttachment, Page $page, ZipExportReader $reader): Attachment
194+
{
195+
if ($exportAttachment->file) {
196+
$file = $this->zipFileToUploadedFile($exportAttachment->file, $reader);
197+
$attachment = $this->attachmentService->saveNewUpload($file, $page->id);
198+
$attachment->name = $exportAttachment->name;
199+
$attachment->save();
200+
} else {
201+
$attachment = $this->attachmentService->saveNewFromLink(
202+
$exportAttachment->name,
203+
$exportAttachment->link ?? '',
204+
$page->id,
205+
);
206+
}
207+
208+
$this->references->addAttachment($attachment, $exportAttachment->id);
209+
210+
return $attachment;
211+
}
212+
213+
protected function importImage(ZipExportImage $exportImage, Page $page, ZipExportReader $reader): Image
214+
{
215+
$file = $this->zipFileToUploadedFile($exportImage->file, $reader);
216+
$image = $this->imageService->saveNewFromUpload(
217+
$file,
218+
$exportImage->type,
219+
$page->id,
220+
);
221+
222+
$this->references->addImage($image, $exportImage->id);
223+
224+
return $image;
225+
}
226+
150227
protected function exportTagsToInputArray(array $exportTags): array
151228
{
152229
$tags = [];
@@ -235,7 +312,7 @@ protected function ensurePermissionsPermitImport(ZipExportPage|ZipExportChapter|
235312
}
236313

237314
if (count($attachments) > 0) {
238-
if (userCan('attachment-create-all')) {
315+
if (!userCan('attachment-create-all')) {
239316
$errors[] = 'You are lacking the required permissions to create attachments.';
240317
}
241318
}
@@ -257,6 +334,8 @@ protected function getZipPath(Import $import): string
257334
stream_copy_to_stream($stream, $tempFile);
258335
fclose($tempFile);
259336

337+
$this->tempFilesToCleanup[] = $tempFilePath;
338+
260339
return $tempFilePath;
261340
}
262341
}

0 commit comments

Comments
 (0)