use BookStack\Exports\ZipExportFiles;
use BookStack\Uploads\Attachment;
-class ZipExportAttachment implements ZipExportModel
+class ZipExportAttachment extends ZipExportModel
{
public ?int $id = null;
public string $name;
use BookStack\Activity\Models\Tag;
-class ZipExportImage implements ZipExportModel
+class ZipExportImage extends ZipExportModel
{
public string $name;
public string $file;
namespace BookStack\Exports\ZipExportModels;
-use BookStack\App\Model;
-use BookStack\Exports\ZipExportFiles;
+use JsonSerializable;
-interface ZipExportModel
+abstract class ZipExportModel implements JsonSerializable
{
-// public static function fromModel(Model $model, ZipExportFiles $files): self;
+ /**
+ * Handle the serialization to JSON.
+ * For these exports, we filter out optional (represented as nullable) fields
+ * just to clean things up and prevent confusion to avoid null states in the
+ * resulting export format itself.
+ */
+ public function jsonSerialize(): array
+ {
+ $publicProps = get_object_vars(...)->__invoke($this);
+ return array_filter($publicProps, fn ($value) => $value !== null);
+ }
}
use BookStack\Entities\Tools\PageContent;
use BookStack\Exports\ZipExportFiles;
-class ZipExportPage implements ZipExportModel
+class ZipExportPage extends ZipExportModel
{
public ?int $id = null;
public string $name;
use BookStack\Activity\Models\Tag;
-class ZipExportTag implements ZipExportModel
+class ZipExportTag extends ZipExportModel
{
public string $name;
public ?string $value = null;
// TODO - Handle found link to $model
// - Validate we can see/access $model, or/and that it's
// part of the export in progress.
+
+ // TODO - Add images after the above to files
return '[CAT]';
});
// TODO - markdown
}
+// dd('end');
// TODO - Parse chapter desc html
// TODO - Parse book desc html
}
use BookStack\App\Model;
use BookStack\Entities\Queries\EntityQueries;
+use BookStack\References\ModelResolvers\AttachmentModelResolver;
use BookStack\References\ModelResolvers\BookLinkModelResolver;
use BookStack\References\ModelResolvers\ChapterLinkModelResolver;
use BookStack\References\ModelResolvers\CrossLinkModelResolver;
+use BookStack\References\ModelResolvers\ImageModelResolver;
use BookStack\References\ModelResolvers\PageLinkModelResolver;
use BookStack\References\ModelResolvers\PagePermalinkModelResolver;
new PageLinkModelResolver($queries->pages),
new ChapterLinkModelResolver($queries->chapters),
new BookLinkModelResolver($queries->books),
- // TODO - Image
- // TODO - Attachment
+ new ImageModelResolver(),
+ new AttachmentModelResolver(),
];
}
--- /dev/null
+<?php
+
+namespace BookStack\References\ModelResolvers;
+
+use BookStack\Uploads\Attachment;
+
+class AttachmentModelResolver implements CrossLinkModelResolver
+{
+ public function resolve(string $link): ?Attachment
+ {
+ $pattern = '/^' . preg_quote(url('/attachments'), '/') . '\/(\d+)/';
+ $matches = [];
+ $match = preg_match($pattern, $link, $matches);
+ if (!$match) {
+ return null;
+ }
+
+ $id = intval($matches[1]);
+
+ return Attachment::query()->find($id);
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\References\ModelResolvers;
+
+use BookStack\Uploads\Image;
+
+class ImageModelResolver implements CrossLinkModelResolver
+{
+ public function resolve(string $link): ?Image
+ {
+ $pattern = '/^' . preg_quote(url('/uploads/images'), '/') . '\/(.+)/';
+ $matches = [];
+ $match = preg_match($pattern, $link, $matches);
+ if (!$match) {
+ return null;
+ }
+
+ $path = $matches[1];
+
+ // Strip thumbnail element from path if existing
+ $originalPathSplit = array_filter(explode('/', $path), function (string $part) {
+ $resizedDir = (str_starts_with($part, 'thumbs-') || str_starts_with($part, 'scaled-'));
+ $missingExtension = !str_contains($part, '.');
+
+ return !($resizedDir && $missingExtension);
+ });
+
+ // Build a database-format image path and search for the image entry
+ $fullPath = '/uploads/images/' . ltrim(implode('/', $originalPathSplit), '/');
+
+ return Image::query()->where('path', '=', $fullPath)->first();
+ }
+}