mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-04-22 11:52:30 +00:00
ZIP Export: Started building link/ref handling
This commit is contained in:
parent
21ccfa97dd
commit
7c39dd5cba
9 changed files with 266 additions and 46 deletions
|
@ -2,10 +2,9 @@
|
|||
|
||||
namespace BookStack\Exports;
|
||||
|
||||
use BookStack\Activity\Models\Tag;
|
||||
use BookStack\Entities\Models\Page;
|
||||
use BookStack\Exceptions\ZipExportException;
|
||||
use BookStack\Uploads\Attachment;
|
||||
use BookStack\Exports\ZipExportModels\ZipExportPage;
|
||||
use ZipArchive;
|
||||
|
||||
class ZipExportBuilder
|
||||
|
@ -13,7 +12,8 @@ class ZipExportBuilder
|
|||
protected array $data = [];
|
||||
|
||||
public function __construct(
|
||||
protected ZipExportFiles $files
|
||||
protected ZipExportFiles $files,
|
||||
protected ZipExportReferences $references,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -22,57 +22,21 @@ class ZipExportBuilder
|
|||
*/
|
||||
public function buildForPage(Page $page): string
|
||||
{
|
||||
$this->data['page'] = $this->convertPage($page);
|
||||
$exportPage = ZipExportPage::fromModel($page, $this->files);
|
||||
$this->data['page'] = $exportPage;
|
||||
|
||||
$this->references->addPage($exportPage);
|
||||
|
||||
return $this->build();
|
||||
}
|
||||
|
||||
protected function convertPage(Page $page): array
|
||||
{
|
||||
$tags = array_map($this->convertTag(...), $page->tags()->get()->all());
|
||||
$attachments = array_map($this->convertAttachment(...), $page->attachments()->get()->all());
|
||||
|
||||
return [
|
||||
'id' => $page->id,
|
||||
'name' => $page->name,
|
||||
'html' => '', // TODO
|
||||
'markdown' => '', // TODO
|
||||
'priority' => $page->priority,
|
||||
'attachments' => $attachments,
|
||||
'images' => [], // TODO
|
||||
'tags' => $tags,
|
||||
];
|
||||
}
|
||||
|
||||
protected function convertAttachment(Attachment $attachment): array
|
||||
{
|
||||
$data = [
|
||||
'name' => $attachment->name,
|
||||
'order' => $attachment->order,
|
||||
];
|
||||
|
||||
if ($attachment->external) {
|
||||
$data['link'] = $attachment->path;
|
||||
} else {
|
||||
$data['file'] = $this->files->referenceForAttachment($attachment);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function convertTag(Tag $tag): array
|
||||
{
|
||||
return [
|
||||
'name' => $tag->name,
|
||||
'value' => $tag->value,
|
||||
'order' => $tag->order,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ZipExportException
|
||||
*/
|
||||
protected function build(): string
|
||||
{
|
||||
$this->references->buildReferences();
|
||||
|
||||
$this->data['exported_at'] = date(DATE_ATOM);
|
||||
$this->data['instance'] = [
|
||||
'version' => trim(file_get_contents(base_path('version'))),
|
||||
|
|
37
app/Exports/ZipExportModels/ZipExportAttachment.php
Normal file
37
app/Exports/ZipExportModels/ZipExportAttachment.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Exports\ZipExportModels;
|
||||
|
||||
use BookStack\Exports\ZipExportFiles;
|
||||
use BookStack\Uploads\Attachment;
|
||||
|
||||
class ZipExportAttachment implements ZipExportModel
|
||||
{
|
||||
public ?int $id = null;
|
||||
public string $name;
|
||||
public ?int $order = null;
|
||||
public ?string $link = null;
|
||||
public ?string $file = null;
|
||||
|
||||
public static function fromModel(Attachment $model, ZipExportFiles $files): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->id = $model->id;
|
||||
$instance->name = $model->name;
|
||||
|
||||
if ($model->external) {
|
||||
$instance->link = $model->path;
|
||||
} else {
|
||||
$instance->file = $files->referenceForAttachment($model);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public static function fromModelArray(array $attachmentArray, ZipExportFiles $files): array
|
||||
{
|
||||
return array_values(array_map(function (Attachment $attachment) use ($files) {
|
||||
return self::fromModel($attachment, $files);
|
||||
}, $attachmentArray));
|
||||
}
|
||||
}
|
11
app/Exports/ZipExportModels/ZipExportImage.php
Normal file
11
app/Exports/ZipExportModels/ZipExportImage.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Exports\ZipExportModels;
|
||||
|
||||
use BookStack\Activity\Models\Tag;
|
||||
|
||||
class ZipExportImage implements ZipExportModel
|
||||
{
|
||||
public string $name;
|
||||
public string $file;
|
||||
}
|
11
app/Exports/ZipExportModels/ZipExportModel.php
Normal file
11
app/Exports/ZipExportModels/ZipExportModel.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Exports\ZipExportModels;
|
||||
|
||||
use BookStack\App\Model;
|
||||
use BookStack\Exports\ZipExportFiles;
|
||||
|
||||
interface ZipExportModel
|
||||
{
|
||||
// public static function fromModel(Model $model, ZipExportFiles $files): self;
|
||||
}
|
39
app/Exports/ZipExportModels/ZipExportPage.php
Normal file
39
app/Exports/ZipExportModels/ZipExportPage.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Exports\ZipExportModels;
|
||||
|
||||
use BookStack\Entities\Models\Page;
|
||||
use BookStack\Entities\Tools\PageContent;
|
||||
use BookStack\Exports\ZipExportFiles;
|
||||
|
||||
class ZipExportPage implements ZipExportModel
|
||||
{
|
||||
public ?int $id = null;
|
||||
public string $name;
|
||||
public ?string $html = null;
|
||||
public ?string $markdown = null;
|
||||
public ?int $priority = null;
|
||||
/** @var ZipExportAttachment[] */
|
||||
public array $attachments = [];
|
||||
/** @var ZipExportImage[] */
|
||||
public array $images = [];
|
||||
/** @var ZipExportTag[] */
|
||||
public array $tags = [];
|
||||
|
||||
public static function fromModel(Page $model, ZipExportFiles $files): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->id = $model->id;
|
||||
$instance->name = $model->name;
|
||||
$instance->html = (new PageContent($model))->render();
|
||||
|
||||
if (!empty($model->markdown)) {
|
||||
$instance->markdown = $model->markdown;
|
||||
}
|
||||
|
||||
$instance->tags = ZipExportTag::fromModelArray($model->tags()->get()->all());
|
||||
$instance->attachments = ZipExportAttachment::fromModelArray($model->attachments()->get()->all(), $files);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
27
app/Exports/ZipExportModels/ZipExportTag.php
Normal file
27
app/Exports/ZipExportModels/ZipExportTag.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Exports\ZipExportModels;
|
||||
|
||||
use BookStack\Activity\Models\Tag;
|
||||
|
||||
class ZipExportTag implements ZipExportModel
|
||||
{
|
||||
public string $name;
|
||||
public ?string $value = null;
|
||||
public ?int $order = 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;
|
||||
}
|
||||
|
||||
public static function fromModelArray(array $tagArray): array
|
||||
{
|
||||
return array_values(array_map(self::fromModel(...), $tagArray));
|
||||
}
|
||||
}
|
55
app/Exports/ZipExportReferences.php
Normal file
55
app/Exports/ZipExportReferences.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Exports;
|
||||
|
||||
use BookStack\App\Model;
|
||||
use BookStack\Exports\ZipExportModels\ZipExportAttachment;
|
||||
use BookStack\Exports\ZipExportModels\ZipExportPage;
|
||||
|
||||
class ZipExportReferences
|
||||
{
|
||||
/** @var ZipExportPage[] */
|
||||
protected array $pages = [];
|
||||
protected array $books = [];
|
||||
protected array $chapters = [];
|
||||
|
||||
/** @var ZipExportAttachment[] */
|
||||
protected array $attachments = [];
|
||||
|
||||
public function __construct(
|
||||
protected ZipReferenceParser $parser,
|
||||
) {
|
||||
}
|
||||
|
||||
public function addPage(ZipExportPage $page): void
|
||||
{
|
||||
if ($page->id) {
|
||||
$this->pages[$page->id] = $page;
|
||||
}
|
||||
|
||||
foreach ($page->attachments as $attachment) {
|
||||
if ($attachment->id) {
|
||||
$this->attachments[$attachment->id] = $attachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function buildReferences(): void
|
||||
{
|
||||
// TODO - References to images, attachments, other entities
|
||||
|
||||
// TODO - Parse page MD & HTML
|
||||
foreach ($this->pages as $page) {
|
||||
$page->html = $this->parser->parse($page->html ?? '', function (Model $model): ?string {
|
||||
// TODO - Handle found link to $model
|
||||
// - Validate we can see/access $model, or/and that it's
|
||||
// part of the export in progress.
|
||||
return '[CAT]';
|
||||
});
|
||||
// TODO - markdown
|
||||
}
|
||||
|
||||
// TODO - Parse chapter desc html
|
||||
// TODO - Parse book desc html
|
||||
}
|
||||
}
|
75
app/Exports/ZipReferenceParser.php
Normal file
75
app/Exports/ZipReferenceParser.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Exports;
|
||||
|
||||
use BookStack\App\Model;
|
||||
use BookStack\Entities\Queries\EntityQueries;
|
||||
use BookStack\References\ModelResolvers\BookLinkModelResolver;
|
||||
use BookStack\References\ModelResolvers\ChapterLinkModelResolver;
|
||||
use BookStack\References\ModelResolvers\CrossLinkModelResolver;
|
||||
use BookStack\References\ModelResolvers\PageLinkModelResolver;
|
||||
use BookStack\References\ModelResolvers\PagePermalinkModelResolver;
|
||||
|
||||
class ZipReferenceParser
|
||||
{
|
||||
/**
|
||||
* @var CrossLinkModelResolver[]
|
||||
*/
|
||||
protected array $modelResolvers;
|
||||
|
||||
public function __construct(EntityQueries $queries)
|
||||
{
|
||||
$this->modelResolvers = [
|
||||
new PagePermalinkModelResolver($queries->pages),
|
||||
new PageLinkModelResolver($queries->pages),
|
||||
new ChapterLinkModelResolver($queries->chapters),
|
||||
new BookLinkModelResolver($queries->books),
|
||||
// TODO - Image
|
||||
// TODO - Attachment
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and replace references in the given content.
|
||||
* @param callable(Model):(string|null) $handler
|
||||
*/
|
||||
public function parse(string $content, callable $handler): string
|
||||
{
|
||||
$escapedBase = preg_quote(url('/'), '/');
|
||||
$linkRegex = "/({$escapedBase}.*?)[\\t\\n\\f>\"'=?#]/";
|
||||
$matches = [];
|
||||
preg_match_all($linkRegex, $content, $matches);
|
||||
|
||||
if (count($matches) < 2) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ($matches[1] as $link) {
|
||||
$model = $this->linkToModel($link);
|
||||
if ($model) {
|
||||
$result = $handler($model);
|
||||
if ($result !== null) {
|
||||
$content = str_replace($link, $result, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt to resolve the given link to a model using the instance model resolvers.
|
||||
*/
|
||||
protected function linkToModel(string $link): ?Model
|
||||
{
|
||||
foreach ($this->modelResolvers as $resolver) {
|
||||
$model = $resolver->resolve($link);
|
||||
if (!is_null($model)) {
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -128,6 +128,7 @@ File must be an image type accepted by BookStack (png, jpg, gif, webp)
|
|||
|
||||
#### Attachment
|
||||
|
||||
- `id` - Number, optional, original ID for the attachment from exported system.
|
||||
- `name` - String, required, name of attachment.
|
||||
- `link` - String, semi-optional, URL of attachment.
|
||||
- `file` - String reference, semi-optional, reference to attachment file.
|
||||
|
|
Loading…
Add table
Reference in a new issue