class Cloner
{
- protected PageRepo $pageRepo;
- protected ChapterRepo $chapterRepo;
- protected BookRepo $bookRepo;
- protected ImageService $imageService;
-
- public function __construct(PageRepo $pageRepo, ChapterRepo $chapterRepo, BookRepo $bookRepo, ImageService $imageService)
- {
- $this->pageRepo = $pageRepo;
- $this->chapterRepo = $chapterRepo;
- $this->bookRepo = $bookRepo;
- $this->imageService = $imageService;
+ public function __construct(
+ protected PageRepo $pageRepo,
+ protected ChapterRepo $chapterRepo,
+ protected BookRepo $bookRepo,
+ protected ImageService $imageService,
+ ) {
}
/**
{
public ?int $id = null;
public string $name;
- public ?int $order = null;
public ?string $link = null;
public ?string $file = null;
public function metadataOnly(): void
{
- $this->order = $this->link = $this->file = null;
+ $this->link = $this->file = null;
}
public static function fromModel(Attachment $model, ZipExportFiles $files): self
$instance = new self();
$instance->id = $model->id;
$instance->name = $model->name;
- $instance->order = $model->order;
if ($model->external) {
$instance->link = $model->path;
$rules = [
'id' => ['nullable', 'int'],
'name' => ['required', 'string', 'min:1'],
- 'order' => ['nullable', 'integer'],
'link' => ['required_without:file', 'nullable', 'string'],
'file' => ['required_without:link', 'nullable', 'string', $context->fileReferenceRule()],
];
$model->id = $data['id'] ?? null;
$model->name = $data['name'];
- $model->order = isset($data['order']) ? intval($data['order']) : null;
$model->link = $data['link'] ?? null;
$model->file = $data['file'] ?? null;
{
public string $name;
public ?string $value = null;
- public ?int $order = null;
public function metadataOnly(): void
{
- $this->value = $this->order = null;
+ $this->value = null;
}
public static function fromModel(Tag $model): self
$instance = new self();
$instance->name = $model->name;
$instance->value = $model->value;
- $instance->order = $model->order;
return $instance;
}
$rules = [
'name' => ['required', 'string', 'min:1'],
'value' => ['nullable', 'string'],
- 'order' => ['nullable', 'integer'],
];
return $context->validateData($data, $rules);
$model->name = $data['name'];
$model->value = $data['value'] ?? null;
- $model->order = isset($data['order']) ? intval($data['order']) : null;
return $model;
}
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Repos\BookRepo;
+use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Entities\Repos\PageRepo;
use BookStack\Exceptions\ZipExportException;
use BookStack\Exceptions\ZipImportException;
use BookStack\Exports\Import;
use BookStack\Exports\ZipExports\Models\ZipExportBook;
use BookStack\Exports\ZipExports\Models\ZipExportChapter;
use BookStack\Exports\ZipExports\Models\ZipExportPage;
+use BookStack\Exports\ZipExports\Models\ZipExportTag;
use BookStack\Uploads\FileStorage;
+use BookStack\Uploads\ImageService;
+use Illuminate\Http\UploadedFile;
class ZipImportRunner
{
+ protected array $tempFilesToCleanup = []; // TODO
+ protected array $createdImages = []; // TODO
+ protected array $createdAttachments = []; // TODO
+
public function __construct(
protected FileStorage $storage,
+ protected PageRepo $pageRepo,
+ protected ChapterRepo $chapterRepo,
+ protected BookRepo $bookRepo,
+ protected ImageService $imageService,
) {
}
$this->ensurePermissionsPermitImport($exportModel);
// TODO - Run import
+ // TODO - In transaction?
+ // TODO - Revert uploaded files if goes wrong
+ }
+
+ protected function importBook(ZipExportBook $exportBook, ZipExportReader $reader): Book
+ {
+ $book = $this->bookRepo->create([
+ 'name' => $exportBook->name,
+ 'description_html' => $exportBook->description_html ?? '',
+ 'image' => $exportBook->cover ? $this->zipFileToUploadedFile($exportBook->cover, $reader) : null,
+ 'tags' => $this->exportTagsToInputArray($exportBook->tags ?? []),
+ ]);
+
+ // TODO - Parse/format description_html references
+
+ if ($book->cover) {
+ $this->createdImages[] = $book->cover;
+ }
+
+ // TODO - Pages
+ foreach ($exportBook->chapters as $exportChapter) {
+ $this->importChapter($exportChapter, $book);
+ }
+ // TODO - Sort chapters/pages by order
+
+ return $book;
+ }
+
+ protected function importChapter(ZipExportChapter $exportChapter, Book $parent, ZipExportReader $reader): Chapter
+ {
+ $chapter = $this->chapterRepo->create([
+ 'name' => $exportChapter->name,
+ 'description_html' => $exportChapter->description_html ?? '',
+ 'tags' => $this->exportTagsToInputArray($exportChapter->tags ?? []),
+ ], $parent);
+
+ // TODO - Parse/format description_html references
+
+ $exportPages = $exportChapter->pages;
+ usort($exportPages, function (ZipExportPage $a, ZipExportPage $b) {
+ return ($a->priority ?? 0) - ($b->priority ?? 0);
+ });
+
+ foreach ($exportPages as $exportPage) {
+ //
+ }
+ // TODO - Pages
+
+ return $chapter;
+ }
+
+ protected function importPage(ZipExportPage $exportPage, Book|Chapter $parent, ZipExportReader $reader): Page
+ {
+ $page = $this->pageRepo->getNewDraftPage($parent);
+
+ // TODO - Import attachments
+ // TODO - Import images
+ // TODO - Parse/format HTML
+
+ $this->pageRepo->publishDraft($page, [
+ 'name' => $exportPage->name,
+ 'markdown' => $exportPage->markdown,
+ 'html' => $exportPage->html,
+ 'tags' => $this->exportTagsToInputArray($exportPage->tags ?? []),
+ ]);
+
+ return $page;
+ }
+
+ protected function exportTagsToInputArray(array $exportTags): array
+ {
+ $tags = [];
+
+ /** @var ZipExportTag $tag */
+ foreach ($exportTags as $tag) {
+ $tags[] = ['name' => $tag->name, 'value' => $tag->value ?? ''];
+ }
+
+ return $tags;
+ }
+
+ protected function zipFileToUploadedFile(string $fileName, ZipExportReader $reader): UploadedFile
+ {
+ $tempPath = tempnam(sys_get_temp_dir(), 'bszipextract');
+ $fileStream = $reader->streamFile($fileName);
+ $tempStream = fopen($tempPath, 'wb');
+ stream_copy_to_stream($fileStream, $tempStream);
+ fclose($tempStream);
+
+ $this->tempFilesToCleanup[] = $tempPath;
+
+ return new UploadedFile($tempPath, $fileName);
}
/**
- `name` - String, required, name of attachment.
- `link` - String, semi-optional, URL of attachment.
- `file` - String reference, semi-optional, reference to attachment file.
-- `order` - Number, optional, integer order of the attachments (shown low to high).
Either `link` or `file` must be present, as that will determine the type of attachment.
#### Tag
- `name` - String, required, name of the tag.
-- `value` - String, optional, value of the tag (can be empty).
-- `order` - Number, optional, integer order of the tags (shown low to high).
\ No newline at end of file
+- `value` - String, optional, value of the tag (can be empty).
\ No newline at end of file