]> BookStack Code Mirror - bookstack/blob - app/Exports/ZipExports/ZipExportReferences.php
ZIP Imports: Built out reference parsing/updating logic
[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             // We don't expect images to be part of book/chapter content
131             if (!($exportModel instanceof ZipExportPage)) {
132                 return null;
133             }
134
135             $page = $model->getPage();
136             if ($page && userCan('view', $page)) {
137                 if (!isset($this->images[$model->id])) {
138                     $exportImage = ZipExportImage::fromModel($model, $files);
139                     $this->images[$model->id] = $exportImage;
140                     $exportModel->images[] = $exportImage;
141                 }
142                 return "[[bsexport:image:{$model->id}]]";
143             }
144             return null;
145         }
146
147         // Handle entity references
148         if ($model instanceof Book && isset($this->books[$model->id])) {
149             return "[[bsexport:book:{$model->id}]]";
150         } else if ($model instanceof Chapter && isset($this->chapters[$model->id])) {
151             return "[[bsexport:chapter:{$model->id}]]";
152         } else if ($model instanceof Page && isset($this->pages[$model->id])) {
153             return "[[bsexport:page:{$model->id}]]";
154         }
155
156         return null;
157     }
158 }