mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-25 09:42:10 +08:00
ZIP Exports: Added working image handling/inclusion
Some checks failed
analyse-php / build (push) Has been cancelled
lint-php / build (push) Has been cancelled
test-migrations / build (8.1) (push) Has been cancelled
test-migrations / build (8.2) (push) Has been cancelled
test-migrations / build (8.3) (push) Has been cancelled
test-php / build (8.1) (push) Has been cancelled
test-php / build (8.2) (push) Has been cancelled
test-php / build (8.3) (push) Has been cancelled
Some checks failed
analyse-php / build (push) Has been cancelled
lint-php / build (push) Has been cancelled
test-migrations / build (8.1) (push) Has been cancelled
test-migrations / build (8.2) (push) Has been cancelled
test-migrations / build (8.3) (push) Has been cancelled
test-php / build (8.1) (push) Has been cancelled
test-php / build (8.2) (push) Has been cancelled
test-php / build (8.3) (push) Has been cancelled
This commit is contained in:
parent
06ffd8ee72
commit
4fb4fe0931
|
@ -35,7 +35,7 @@ class ZipExportBuilder
|
|||
*/
|
||||
protected function build(): string
|
||||
{
|
||||
$this->references->buildReferences();
|
||||
$this->references->buildReferences($this->files);
|
||||
|
||||
$this->data['exported_at'] = date(DATE_ATOM);
|
||||
$this->data['instance'] = [
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace BookStack\Exports;
|
|||
|
||||
use BookStack\Uploads\Attachment;
|
||||
use BookStack\Uploads\AttachmentService;
|
||||
use BookStack\Uploads\Image;
|
||||
use BookStack\Uploads\ImageService;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ZipExportFiles
|
||||
|
@ -14,8 +16,15 @@ class ZipExportFiles
|
|||
*/
|
||||
protected array $attachmentRefsById = [];
|
||||
|
||||
/**
|
||||
* References for images by image ID.
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected array $imageRefsById = [];
|
||||
|
||||
public function __construct(
|
||||
protected AttachmentService $attachmentService,
|
||||
protected ImageService $imageService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -30,15 +39,46 @@ class ZipExportFiles
|
|||
return $this->attachmentRefsById[$attachment->id];
|
||||
}
|
||||
|
||||
$existingFiles = $this->getAllFileNames();
|
||||
do {
|
||||
$fileName = Str::random(20) . '.' . $attachment->extension;
|
||||
} while (in_array($fileName, $this->attachmentRefsById));
|
||||
} while (in_array($fileName, $existingFiles));
|
||||
|
||||
$this->attachmentRefsById[$attachment->id] = $fileName;
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gain a reference to the given image instance.
|
||||
* This is expected to be an image that the user has visibility of,
|
||||
* no permission/access checks are performed here.
|
||||
*/
|
||||
public function referenceForImage(Image $image): string
|
||||
{
|
||||
if (isset($this->imageRefsById[$image->id])) {
|
||||
return $this->imageRefsById[$image->id];
|
||||
}
|
||||
|
||||
$existingFiles = $this->getAllFileNames();
|
||||
$extension = pathinfo($image->path, PATHINFO_EXTENSION);
|
||||
do {
|
||||
$fileName = Str::random(20) . '.' . $extension;
|
||||
} while (in_array($fileName, $existingFiles));
|
||||
|
||||
$this->imageRefsById[$image->id] = $fileName;
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function getAllFileNames(): array
|
||||
{
|
||||
return array_merge(
|
||||
array_values($this->attachmentRefsById),
|
||||
array_values($this->imageRefsById),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract each of the ZIP export tracked files.
|
||||
* Calls the given callback for each tracked file, passing a temporary
|
||||
|
@ -54,5 +94,14 @@ class ZipExportFiles
|
|||
stream_copy_to_stream($stream, $tmpFileStream);
|
||||
$callback($tmpFile, $ref);
|
||||
}
|
||||
|
||||
foreach ($this->imageRefsById as $imageId => $ref) {
|
||||
$image = Image::query()->find($imageId);
|
||||
$stream = $this->imageService->getImageStream($image);
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'bszipimage-');
|
||||
$tmpFileStream = fopen($tmpFile, 'w');
|
||||
stream_copy_to_stream($stream, $tmpFileStream);
|
||||
$callback($tmpFile, $ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,24 @@
|
|||
|
||||
namespace BookStack\Exports\ZipExportModels;
|
||||
|
||||
use BookStack\Activity\Models\Tag;
|
||||
use BookStack\Exports\ZipExportFiles;
|
||||
use BookStack\Uploads\Image;
|
||||
|
||||
class ZipExportImage extends ZipExportModel
|
||||
{
|
||||
public ?int $id = null;
|
||||
public string $name;
|
||||
public string $file;
|
||||
public string $type;
|
||||
|
||||
public static function fromModel(Image $model, ZipExportFiles $files): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->id = $model->id;
|
||||
$instance->name = $model->name;
|
||||
$instance->type = $model->type;
|
||||
$instance->file = $files->referenceForImage($model);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,13 @@
|
|||
namespace BookStack\Exports;
|
||||
|
||||
use BookStack\App\Model;
|
||||
use BookStack\Entities\Models\Page;
|
||||
use BookStack\Exports\ZipExportModels\ZipExportAttachment;
|
||||
use BookStack\Exports\ZipExportModels\ZipExportImage;
|
||||
use BookStack\Exports\ZipExportModels\ZipExportModel;
|
||||
use BookStack\Exports\ZipExportModels\ZipExportPage;
|
||||
use BookStack\Uploads\Attachment;
|
||||
use BookStack\Uploads\Image;
|
||||
|
||||
class ZipExportReferences
|
||||
{
|
||||
|
@ -16,6 +21,9 @@ class ZipExportReferences
|
|||
/** @var ZipExportAttachment[] */
|
||||
protected array $attachments = [];
|
||||
|
||||
/** @var ZipExportImage[] */
|
||||
protected array $images = [];
|
||||
|
||||
public function __construct(
|
||||
protected ZipReferenceParser $parser,
|
||||
) {
|
||||
|
@ -34,19 +42,12 @@ class ZipExportReferences
|
|||
}
|
||||
}
|
||||
|
||||
public function buildReferences(): void
|
||||
public function buildReferences(ZipExportFiles $files): 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.
|
||||
|
||||
// TODO - Add images after the above to files
|
||||
return '[CAT]';
|
||||
$page->html = $this->parser->parse($page->html ?? '', function (Model $model) use ($files, $page) {
|
||||
return $this->handleModelReference($model, $page, $files);
|
||||
});
|
||||
// TODO - markdown
|
||||
}
|
||||
|
@ -55,4 +56,45 @@ class ZipExportReferences
|
|||
// TODO - Parse chapter desc html
|
||||
// TODO - Parse book desc html
|
||||
}
|
||||
|
||||
protected function handleModelReference(Model $model, ZipExportModel $exportModel, ZipExportFiles $files): ?string
|
||||
{
|
||||
// TODO - References to other entities
|
||||
|
||||
// Handle attachment references
|
||||
// No permission check needed here since they would only already exist in this
|
||||
// reference context if already allowed via their entity access.
|
||||
if ($model instanceof Attachment) {
|
||||
if (isset($this->attachments[$model->id])) {
|
||||
return "[[bsexport:attachment:{$model->id}]]";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Handle image references
|
||||
if ($model instanceof Image) {
|
||||
// Only handle gallery and drawio images
|
||||
if ($model->type !== 'gallery' && $model->type !== 'drawio') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// We don't expect images to be part of book/chapter content
|
||||
if (!($exportModel instanceof ZipExportPage)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$page = $model->getPage();
|
||||
if ($page && userCan('view', $page)) {
|
||||
if (!isset($this->images[$model->id])) {
|
||||
$exportImage = ZipExportImage::fromModel($model, $files);
|
||||
$this->images[$model->id] = $exportImage;
|
||||
$exportModel->images[] = $exportImage;
|
||||
}
|
||||
return "[[bsexport:image:{$model->id}]]";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,6 +133,19 @@ class ImageService
|
|||
return $disk->get($image->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw data content from an image.
|
||||
*
|
||||
* @throws Exception
|
||||
* @returns ?resource
|
||||
*/
|
||||
public function getImageStream(Image $image): mixed
|
||||
{
|
||||
$disk = $this->storage->getDisk();
|
||||
|
||||
return $disk->stream($image->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an image along with its revisions, thumbnails and remaining folders.
|
||||
*
|
||||
|
|
|
@ -55,6 +55,15 @@ class ImageStorageDisk
|
|||
return $this->filesystem->get($this->adjustPathForDisk($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a stream to the file at the given path.
|
||||
* @returns ?resource
|
||||
*/
|
||||
public function stream(string $path): mixed
|
||||
{
|
||||
return $this->filesystem->readStream($this->adjustPathForDisk($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the given image data at the given path. Can choose to set
|
||||
* the image as public which will update its visibility after saving.
|
||||
|
|
|
@ -46,13 +46,12 @@ This can be done using the following format:
|
|||
[[bsexport:<object>:<reference>]]
|
||||
```
|
||||
|
||||
Images and attachments are referenced via their file name within the `files/` directory.
|
||||
Otherwise, other content types are referenced by `id`.
|
||||
References are to the `id` for data objects.
|
||||
Here's an example of each type of such reference that could be used:
|
||||
|
||||
```
|
||||
[[bsexport:image:an-image-path.png]]
|
||||
[[bsexport:attachment:an-image-path.png]]
|
||||
[[bsexport:image:22]]
|
||||
[[bsexport:attachment:55]]
|
||||
[[bsexport:page:40]]
|
||||
[[bsexport:chapter:2]]
|
||||
[[bsexport:book:8]]
|
||||
|
@ -121,10 +120,14 @@ The page editor type, and edit content will be determined by what content is pro
|
|||
|
||||
#### Image
|
||||
|
||||
- `id` - Number, optional, original ID for the page from exported system.
|
||||
- `name` - String, required, name of image.
|
||||
- `file` - String reference, required, reference to image file.
|
||||
- `type` - String, required, must be 'gallery' or 'drawio'
|
||||
|
||||
File must be an image type accepted by BookStack (png, jpg, gif, webp)
|
||||
File must be an image type accepted by BookStack (png, jpg, gif, webp).
|
||||
Images of type 'drawio' are expected to be png with draw.io drawing data
|
||||
embedded within it.
|
||||
|
||||
#### Attachment
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user