]> BookStack Code Mirror - bookstack/blob - app/Exports/ZipExports/ZipExportReferences.php
Merge branch 'docker-simplify' into development
[bookstack] / app / Exports / ZipExports / ZipExportReferences.php
1 <?php
2
3 namespace BookStack\Exports\ZipExports;
4
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;
17
18 class ZipExportReferences
19 {
20     /** @var ZipExportPage[] */
21     protected array $pages = [];
22     /** @var ZipExportChapter[] */
23     protected array $chapters = [];
24     /** @var ZipExportBook[] */
25     protected array $books = [];
26
27     /** @var ZipExportAttachment[] */
28     protected array $attachments = [];
29
30     /** @var ZipExportImage[] */
31     protected array $images = [];
32
33     public function __construct(
34         protected ZipReferenceParser $parser,
35     ) {
36     }
37
38     public function addPage(ZipExportPage $page): void
39     {
40         if ($page->id) {
41             $this->pages[$page->id] = $page;
42         }
43
44         foreach ($page->attachments as $attachment) {
45             if ($attachment->id) {
46                 $this->attachments[$attachment->id] = $attachment;
47             }
48         }
49     }
50
51     public function addChapter(ZipExportChapter $chapter): void
52     {
53         if ($chapter->id) {
54             $this->chapters[$chapter->id] = $chapter;
55         }
56
57         foreach ($chapter->pages as $page) {
58             $this->addPage($page);
59         }
60     }
61
62     public function addBook(ZipExportBook $book): void
63     {
64         if ($book->id) {
65             $this->books[$book->id] = $book;
66         }
67
68         foreach ($book->pages as $page) {
69             $this->addPage($page);
70         }
71
72         foreach ($book->chapters as $chapter) {
73             $this->addChapter($chapter);
74         }
75     }
76
77     public function buildReferences(ZipExportFiles $files): void
78     {
79         $createHandler = function (ZipExportModel $zipModel) use ($files) {
80             return function (Model $model) use ($files, $zipModel) {
81                 return $this->handleModelReference($model, $zipModel, $files);
82             };
83         };
84
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);
91             }
92         }
93
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);
99             }
100         }
101
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);
107             }
108         }
109     }
110
111     protected function handleModelReference(Model $model, ZipExportModel $exportModel, ZipExportFiles $files): ?string
112     {
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}]]";
119             }
120             return null;
121         }
122
123         // Handle image references
124         if ($model instanceof Image) {
125             // Only handle gallery and drawio images
126             if ($model->type !== 'gallery' && $model->type !== 'drawio') {
127                 return null;
128             }
129
130             // Handle simple links outside of page content
131             if (!($exportModel instanceof ZipExportPage) && isset($this->images[$model->id])) {
132                 return "[[bsexport:image:{$model->id}]]";
133             }
134
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;
142                 }
143                 return "[[bsexport:image:{$model->id}]]";
144             }
145             return null;
146         }
147
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}]]";
155         }
156
157         return null;
158     }
159 }