Skip to content

Commit d13e4d2

Browse files
committed
ZIP imports: Started actual import logic
1 parent 7b84558 commit d13e4d2

File tree

6 files changed

+124
-24
lines changed

6 files changed

+124
-24
lines changed

app/Entities/Tools/Cloner.php

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,12 @@
1818

1919
class Cloner
2020
{
21-
protected PageRepo $pageRepo;
22-
protected ChapterRepo $chapterRepo;
23-
protected BookRepo $bookRepo;
24-
protected ImageService $imageService;
25-
26-
public function __construct(PageRepo $pageRepo, ChapterRepo $chapterRepo, BookRepo $bookRepo, ImageService $imageService)
27-
{
28-
$this->pageRepo = $pageRepo;
29-
$this->chapterRepo = $chapterRepo;
30-
$this->bookRepo = $bookRepo;
31-
$this->imageService = $imageService;
21+
public function __construct(
22+
protected PageRepo $pageRepo,
23+
protected ChapterRepo $chapterRepo,
24+
protected BookRepo $bookRepo,
25+
protected ImageService $imageService,
26+
) {
3227
}
3328

3429
/**

app/Exports/ZipExports/Models/ZipExportAttachment.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,19 @@ class ZipExportAttachment extends ZipExportModel
1010
{
1111
public ?int $id = null;
1212
public string $name;
13-
public ?int $order = null;
1413
public ?string $link = null;
1514
public ?string $file = null;
1615

1716
public function metadataOnly(): void
1817
{
19-
$this->order = $this->link = $this->file = null;
18+
$this->link = $this->file = null;
2019
}
2120

2221
public static function fromModel(Attachment $model, ZipExportFiles $files): self
2322
{
2423
$instance = new self();
2524
$instance->id = $model->id;
2625
$instance->name = $model->name;
27-
$instance->order = $model->order;
2826

2927
if ($model->external) {
3028
$instance->link = $model->path;
@@ -47,7 +45,6 @@ public static function validate(ZipValidationHelper $context, array $data): arra
4745
$rules = [
4846
'id' => ['nullable', 'int'],
4947
'name' => ['required', 'string', 'min:1'],
50-
'order' => ['nullable', 'integer'],
5148
'link' => ['required_without:file', 'nullable', 'string'],
5249
'file' => ['required_without:link', 'nullable', 'string', $context->fileReferenceRule()],
5350
];
@@ -61,7 +58,6 @@ public static function fromArray(array $data): self
6158

6259
$model->id = $data['id'] ?? null;
6360
$model->name = $data['name'];
64-
$model->order = isset($data['order']) ? intval($data['order']) : null;
6561
$model->link = $data['link'] ?? null;
6662
$model->file = $data['file'] ?? null;
6763

app/Exports/ZipExports/Models/ZipExportTag.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,17 @@ class ZipExportTag extends ZipExportModel
99
{
1010
public string $name;
1111
public ?string $value = null;
12-
public ?int $order = null;
1312

1413
public function metadataOnly(): void
1514
{
16-
$this->value = $this->order = null;
15+
$this->value = null;
1716
}
1817

1918
public static function fromModel(Tag $model): self
2019
{
2120
$instance = new self();
2221
$instance->name = $model->name;
2322
$instance->value = $model->value;
24-
$instance->order = $model->order;
2523

2624
return $instance;
2725
}
@@ -36,7 +34,6 @@ public static function validate(ZipValidationHelper $context, array $data): arra
3634
$rules = [
3735
'name' => ['required', 'string', 'min:1'],
3836
'value' => ['nullable', 'string'],
39-
'order' => ['nullable', 'integer'],
4037
];
4138

4239
return $context->validateData($data, $rules);
@@ -48,7 +45,6 @@ public static function fromArray(array $data): self
4845

4946
$model->name = $data['name'];
5047
$model->value = $data['value'] ?? null;
51-
$model->order = isset($data['order']) ? intval($data['order']) : null;
5248

5349
return $model;
5450
}

app/Exports/ZipExports/ZipExportReader.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ public function fileExists(string $fileName): bool
7373
return $this->zip->statName("files/{$fileName}") !== false;
7474
}
7575

76+
/**
77+
* @return false|resource
78+
*/
79+
public function streamFile(string $fileName)
80+
{
81+
return $this->zip->getStream("files/{$fileName}");
82+
}
83+
7684
/**
7785
* @throws ZipExportException
7886
*/

app/Exports/ZipExports/ZipImportRunner.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,33 @@
55
use BookStack\Entities\Models\Book;
66
use BookStack\Entities\Models\Chapter;
77
use BookStack\Entities\Models\Entity;
8+
use BookStack\Entities\Models\Page;
9+
use BookStack\Entities\Repos\BookRepo;
10+
use BookStack\Entities\Repos\ChapterRepo;
11+
use BookStack\Entities\Repos\PageRepo;
812
use BookStack\Exceptions\ZipExportException;
913
use BookStack\Exceptions\ZipImportException;
1014
use BookStack\Exports\Import;
1115
use BookStack\Exports\ZipExports\Models\ZipExportBook;
1216
use BookStack\Exports\ZipExports\Models\ZipExportChapter;
1317
use BookStack\Exports\ZipExports\Models\ZipExportPage;
18+
use BookStack\Exports\ZipExports\Models\ZipExportTag;
1419
use BookStack\Uploads\FileStorage;
20+
use BookStack\Uploads\ImageService;
21+
use Illuminate\Http\UploadedFile;
1522

1623
class ZipImportRunner
1724
{
25+
protected array $tempFilesToCleanup = []; // TODO
26+
protected array $createdImages = []; // TODO
27+
protected array $createdAttachments = []; // TODO
28+
1829
public function __construct(
1930
protected FileStorage $storage,
31+
protected PageRepo $pageRepo,
32+
protected ChapterRepo $chapterRepo,
33+
protected BookRepo $bookRepo,
34+
protected ImageService $imageService,
2035
) {
2136
}
2237

@@ -51,6 +66,98 @@ public function run(Import $import, ?Entity $parent = null): void
5166
$this->ensurePermissionsPermitImport($exportModel);
5267

5368
// TODO - Run import
69+
// TODO - In transaction?
70+
// TODO - Revert uploaded files if goes wrong
71+
}
72+
73+
protected function importBook(ZipExportBook $exportBook, ZipExportReader $reader): Book
74+
{
75+
$book = $this->bookRepo->create([
76+
'name' => $exportBook->name,
77+
'description_html' => $exportBook->description_html ?? '',
78+
'image' => $exportBook->cover ? $this->zipFileToUploadedFile($exportBook->cover, $reader) : null,
79+
'tags' => $this->exportTagsToInputArray($exportBook->tags ?? []),
80+
]);
81+
82+
// TODO - Parse/format description_html references
83+
84+
if ($book->cover) {
85+
$this->createdImages[] = $book->cover;
86+
}
87+
88+
// TODO - Pages
89+
foreach ($exportBook->chapters as $exportChapter) {
90+
$this->importChapter($exportChapter, $book);
91+
}
92+
// TODO - Sort chapters/pages by order
93+
94+
return $book;
95+
}
96+
97+
protected function importChapter(ZipExportChapter $exportChapter, Book $parent, ZipExportReader $reader): Chapter
98+
{
99+
$chapter = $this->chapterRepo->create([
100+
'name' => $exportChapter->name,
101+
'description_html' => $exportChapter->description_html ?? '',
102+
'tags' => $this->exportTagsToInputArray($exportChapter->tags ?? []),
103+
], $parent);
104+
105+
// TODO - Parse/format description_html references
106+
107+
$exportPages = $exportChapter->pages;
108+
usort($exportPages, function (ZipExportPage $a, ZipExportPage $b) {
109+
return ($a->priority ?? 0) - ($b->priority ?? 0);
110+
});
111+
112+
foreach ($exportPages as $exportPage) {
113+
//
114+
}
115+
// TODO - Pages
116+
117+
return $chapter;
118+
}
119+
120+
protected function importPage(ZipExportPage $exportPage, Book|Chapter $parent, ZipExportReader $reader): Page
121+
{
122+
$page = $this->pageRepo->getNewDraftPage($parent);
123+
124+
// TODO - Import attachments
125+
// TODO - Import images
126+
// TODO - Parse/format HTML
127+
128+
$this->pageRepo->publishDraft($page, [
129+
'name' => $exportPage->name,
130+
'markdown' => $exportPage->markdown,
131+
'html' => $exportPage->html,
132+
'tags' => $this->exportTagsToInputArray($exportPage->tags ?? []),
133+
]);
134+
135+
return $page;
136+
}
137+
138+
protected function exportTagsToInputArray(array $exportTags): array
139+
{
140+
$tags = [];
141+
142+
/** @var ZipExportTag $tag */
143+
foreach ($exportTags as $tag) {
144+
$tags[] = ['name' => $tag->name, 'value' => $tag->value ?? ''];
145+
}
146+
147+
return $tags;
148+
}
149+
150+
protected function zipFileToUploadedFile(string $fileName, ZipExportReader $reader): UploadedFile
151+
{
152+
$tempPath = tempnam(sys_get_temp_dir(), 'bszipextract');
153+
$fileStream = $reader->streamFile($fileName);
154+
$tempStream = fopen($tempPath, 'wb');
155+
stream_copy_to_stream($fileStream, $tempStream);
156+
fclose($tempStream);
157+
158+
$this->tempFilesToCleanup[] = $tempPath;
159+
160+
return new UploadedFile($tempPath, $fileName);
54161
}
55162

56163
/**

dev/docs/portable-zip-file-format.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,10 @@ embedded within it.
135135
- `name` - String, required, name of attachment.
136136
- `link` - String, semi-optional, URL of attachment.
137137
- `file` - String reference, semi-optional, reference to attachment file.
138-
- `order` - Number, optional, integer order of the attachments (shown low to high).
139138

140139
Either `link` or `file` must be present, as that will determine the type of attachment.
141140

142141
#### Tag
143142

144143
- `name` - String, required, name of the tag.
145-
- `value` - String, optional, value of the tag (can be empty).
146-
- `order` - Number, optional, integer order of the tags (shown low to high).
144+
- `value` - String, optional, value of the tag (can be empty).

0 commit comments

Comments
 (0)