]> BookStack Code Mirror - bookstack/blob - app/Exports/ZipExports/ZipImportRunner.php
ZIP Imports: Added parent and permission check pre-import
[bookstack] / app / Exports / ZipExports / ZipImportRunner.php
1 <?php
2
3 namespace BookStack\Exports\ZipExports;
4
5 use BookStack\Entities\Models\Book;
6 use BookStack\Entities\Models\Chapter;
7 use BookStack\Entities\Models\Entity;
8 use BookStack\Exceptions\ZipExportException;
9 use BookStack\Exceptions\ZipImportException;
10 use BookStack\Exports\Import;
11 use BookStack\Exports\ZipExports\Models\ZipExportBook;
12 use BookStack\Exports\ZipExports\Models\ZipExportChapter;
13 use BookStack\Exports\ZipExports\Models\ZipExportPage;
14 use BookStack\Uploads\FileStorage;
15
16 class ZipImportRunner
17 {
18     public function __construct(
19         protected FileStorage $storage,
20     ) {
21     }
22
23     /**
24      * @throws ZipImportException
25      */
26     public function run(Import $import, ?Entity $parent = null): void
27     {
28         $zipPath = $this->getZipPath($import);
29         $reader = new ZipExportReader($zipPath);
30
31         $errors = (new ZipExportValidator($reader))->validate();
32         if ($errors) {
33             throw new ZipImportException(["ZIP failed to validate"]);
34         }
35
36         try {
37             $exportModel = $reader->decodeDataToExportModel();
38         } catch (ZipExportException $e) {
39             throw new ZipImportException([$e->getMessage()]);
40         }
41
42         // Validate parent type
43         if ($exportModel instanceof ZipExportBook && ($parent !== null)) {
44             throw new ZipImportException(["Must not have a parent set for a Book import"]);
45         } else if ($exportModel instanceof ZipExportChapter && (!$parent instanceof Book)) {
46             throw new ZipImportException(["Parent book required for chapter import"]);
47         } else if ($exportModel instanceof ZipExportPage && !($parent instanceof Book || $parent instanceof Chapter)) {
48             throw new ZipImportException(["Parent book or chapter required for page import"]);
49         }
50
51         $this->ensurePermissionsPermitImport($exportModel);
52
53         // TODO - Run import
54     }
55
56     /**
57      * @throws ZipImportException
58      */
59     protected function ensurePermissionsPermitImport(ZipExportPage|ZipExportChapter|ZipExportBook $exportModel, Book|Chapter|null $parent = null): void
60     {
61         $errors = [];
62
63         // TODO - Extract messages to language files
64         // TODO - Ensure these are shown to users on failure
65
66         $chapters = [];
67         $pages = [];
68         $images = [];
69         $attachments = [];
70
71         if ($exportModel instanceof ZipExportBook) {
72             if (!userCan('book-create-all')) {
73                 $errors[] = 'You are lacking the required permission to create books.';
74             }
75             array_push($pages, ...$exportModel->pages);
76             array_push($chapters, ...$exportModel->chapters);
77         } else if ($exportModel instanceof ZipExportChapter) {
78             $chapters[] = $exportModel;
79         } else if ($exportModel instanceof ZipExportPage) {
80             $pages[] = $exportModel;
81         }
82
83         foreach ($chapters as $chapter) {
84             array_push($pages, ...$chapter->pages);
85         }
86
87         if (count($chapters) > 0) {
88             $permission = 'chapter-create' . ($parent ? '' : '-all');
89             if (!userCan($permission, $parent)) {
90                 $errors[] = 'You are lacking the required permission to create chapters.';
91             }
92         }
93
94         foreach ($pages as $page) {
95             array_push($attachments, ...$page->attachments);
96             array_push($images, ...$page->images);
97         }
98
99         if (count($pages) > 0) {
100             if ($parent) {
101                 if (!userCan('page-create', $parent)) {
102                     $errors[] = 'You are lacking the required permission to create pages.';
103                 }
104             } else {
105                 $hasPermission = userCan('page-create-all') || userCan('page-create-own');
106                 if (!$hasPermission) {
107                     $errors[] = 'You are lacking the required permission to create pages.';
108                 }
109             }
110         }
111
112         if (count($images) > 0) {
113             if (!userCan('image-create-all')) {
114                 $errors[] = 'You are lacking the required permissions to create images.';
115             }
116         }
117
118         if (count($attachments) > 0) {
119             if (userCan('attachment-create-all')) {
120                 $errors[] = 'You are lacking the required permissions to create attachments.';
121             }
122         }
123
124         if (count($errors)) {
125             throw new ZipImportException($errors);
126         }
127     }
128
129     protected function getZipPath(Import $import): string
130     {
131         if (!$this->storage->isRemote()) {
132             return $this->storage->getSystemPath($import->path);
133         }
134
135         $tempFilePath = tempnam(sys_get_temp_dir(), 'bszip-import-');
136         $tempFile = fopen($tempFilePath, 'wb');
137         $stream = $this->storage->getReadStream($import->path);
138         stream_copy_to_stream($stream, $tempFile);
139         fclose($tempFile);
140
141         return $tempFilePath;
142     }
143 }