]> BookStack Code Mirror - bookstack/blob - app/Exports/ImportRepo.php
ZIP Imports: Added parent and permission check pre-import
[bookstack] / app / Exports / ImportRepo.php
1 <?php
2
3 namespace BookStack\Exports;
4
5 use BookStack\Entities\Queries\EntityQueries;
6 use BookStack\Exceptions\FileUploadException;
7 use BookStack\Exceptions\ZipExportException;
8 use BookStack\Exceptions\ZipValidationException;
9 use BookStack\Exports\ZipExports\Models\ZipExportBook;
10 use BookStack\Exports\ZipExports\Models\ZipExportChapter;
11 use BookStack\Exports\ZipExports\Models\ZipExportPage;
12 use BookStack\Exports\ZipExports\ZipExportReader;
13 use BookStack\Exports\ZipExports\ZipExportValidator;
14 use BookStack\Exports\ZipExports\ZipImportRunner;
15 use BookStack\Uploads\FileStorage;
16 use Illuminate\Database\Eloquent\Collection;
17 use Symfony\Component\HttpFoundation\File\UploadedFile;
18
19 class ImportRepo
20 {
21     public function __construct(
22         protected FileStorage $storage,
23         protected ZipImportRunner $importer,
24         protected EntityQueries $entityQueries,
25     ) {
26     }
27
28     /**
29      * @return Collection<Import>
30      */
31     public function getVisibleImports(): Collection
32     {
33         $query = Import::query();
34
35         if (!userCan('settings-manage')) {
36             $query->where('created_by', user()->id);
37         }
38
39         return $query->get();
40     }
41
42     public function findVisible(int $id): Import
43     {
44         $query = Import::query();
45
46         if (!userCan('settings-manage')) {
47             $query->where('created_by', user()->id);
48         }
49
50         return $query->findOrFail($id);
51     }
52
53     /**
54      * @throws FileUploadException
55      * @throws ZipValidationException
56      * @throws ZipExportException
57      */
58     public function storeFromUpload(UploadedFile $file): Import
59     {
60         $zipPath = $file->getRealPath();
61         $reader = new ZipExportReader($zipPath);
62
63         $errors = (new ZipExportValidator($reader))->validate();
64         if ($errors) {
65             throw new ZipValidationException($errors);
66         }
67
68         $exportModel = $reader->decodeDataToExportModel();
69
70         $import = new Import();
71         $import->type = match (get_class($exportModel)) {
72             ZipExportPage::class => 'page',
73             ZipExportChapter::class => 'chapter',
74             ZipExportBook::class => 'book',
75         };
76
77         $import->name = $exportModel->name;
78         $import->created_by = user()->id;
79         $import->size = filesize($zipPath);
80
81         $exportModel->metadataOnly();
82         $import->metadata = json_encode($exportModel);
83
84         $path = $this->storage->uploadFile(
85             $file,
86             'uploads/files/imports/',
87             '',
88             'zip'
89         );
90
91         $import->path = $path;
92         $import->save();
93
94         return $import;
95     }
96
97     /**
98      * @throws ZipValidationException
99      */
100     public function runImport(Import $import, ?string $parent = null)
101     {
102         $parentModel = null;
103         if ($import->type === 'page' || $import->type === 'chapter') {
104             $parentModel = $parent ? $this->entityQueries->findVisibleByStringIdentifier($parent) : null;
105         }
106
107         return $this->importer->run($import, $parentModel);
108     }
109
110     public function deleteImport(Import $import): void
111     {
112         $this->storage->delete($import->path);
113         $import->delete();
114     }
115 }