3 namespace BookStack\Exports\ZipExports;
5 use BookStack\App\Model;
6 use BookStack\Entities\Models\Book;
7 use BookStack\Entities\Models\Chapter;
8 use BookStack\Entities\Models\Page;
9 use BookStack\Exports\ZipExports\Models\ZipExportAttachment;
10 use BookStack\Exports\ZipExports\Models\ZipExportBook;
11 use BookStack\Exports\ZipExports\Models\ZipExportChapter;
12 use BookStack\Exports\ZipExports\Models\ZipExportImage;
13 use BookStack\Exports\ZipExports\Models\ZipExportModel;
14 use BookStack\Exports\ZipExports\Models\ZipExportPage;
15 use BookStack\Uploads\Attachment;
16 use BookStack\Uploads\Image;
18 class ZipExportReferences
20 /** @var ZipExportPage[] */
21 protected array $pages = [];
22 /** @var ZipExportChapter[] */
23 protected array $chapters = [];
24 /** @var ZipExportBook[] */
25 protected array $books = [];
27 /** @var ZipExportAttachment[] */
28 protected array $attachments = [];
30 /** @var ZipExportImage[] */
31 protected array $images = [];
33 public function __construct(
34 protected ZipReferenceParser $parser,
38 public function addPage(ZipExportPage $page): void
41 $this->pages[$page->id] = $page;
44 foreach ($page->attachments as $attachment) {
45 if ($attachment->id) {
46 $this->attachments[$attachment->id] = $attachment;
51 public function addChapter(ZipExportChapter $chapter): void
54 $this->chapters[$chapter->id] = $chapter;
57 foreach ($chapter->pages as $page) {
58 $this->addPage($page);
62 public function addBook(ZipExportBook $book): void
65 $this->books[$book->id] = $book;
68 foreach ($book->pages as $page) {
69 $this->addPage($page);
72 foreach ($book->chapters as $chapter) {
73 $this->addChapter($chapter);
77 public function buildReferences(ZipExportFiles $files): void
79 $createHandler = function (ZipExportModel $zipModel) use ($files) {
80 return function (Model $model) use ($files, $zipModel) {
81 return $this->handleModelReference($model, $zipModel, $files);
85 // Parse page content first
86 foreach ($this->pages as $page) {
87 $handler = $createHandler($page);
88 $page->html = $this->parser->parseLinks($page->html ?? '', $handler);
89 if ($page->markdown) {
90 $page->markdown = $this->parser->parseLinks($page->markdown, $handler);
94 // Parse chapter description HTML
95 foreach ($this->chapters as $chapter) {
96 if ($chapter->description_html) {
97 $handler = $createHandler($chapter);
98 $chapter->description_html = $this->parser->parseLinks($chapter->description_html, $handler);
102 // Parse book description HTML
103 foreach ($this->books as $book) {
104 if ($book->description_html) {
105 $handler = $createHandler($book);
106 $book->description_html = $this->parser->parseLinks($book->description_html, $handler);
111 protected function handleModelReference(Model $model, ZipExportModel $exportModel, ZipExportFiles $files): ?string
113 // Handle attachment references
114 // No permission check needed here since they would only already exist in this
115 // reference context if already allowed via their entity access.
116 if ($model instanceof Attachment) {
117 if (isset($this->attachments[$model->id])) {
118 return "[[bsexport:attachment:{$model->id}]]";
123 // Handle image references
124 if ($model instanceof Image) {
125 // Only handle gallery and drawio images
126 if ($model->type !== 'gallery' && $model->type !== 'drawio') {
130 // Handle simple links outside of page content
131 if (!($exportModel instanceof ZipExportPage) && isset($this->images[$model->id])) {
132 return "[[bsexport:image:{$model->id}]]";
135 // Find and include images if in visibility
136 $page = $model->getPage();
137 if ($page && userCan('view', $page)) {
138 if (!isset($this->images[$model->id])) {
139 $exportImage = ZipExportImage::fromModel($model, $files);
140 $this->images[$model->id] = $exportImage;
141 $exportModel->images[] = $exportImage;
143 return "[[bsexport:image:{$model->id}]]";
148 // Handle entity references
149 if ($model instanceof Book && isset($this->books[$model->id])) {
150 return "[[bsexport:book:{$model->id}]]";
151 } else if ($model instanceof Chapter && isset($this->chapters[$model->id])) {
152 return "[[bsexport:chapter:{$model->id}]]";
153 } else if ($model instanceof Page && isset($this->pages[$model->id])) {
154 return "[[bsexport:page:{$model->id}]]";