]> BookStack Code Mirror - bookstack/blob - app/Exports/ImportRepo.php
Permissions: Cleanup after review of enum implementation PR
[bookstack] / app / Exports / ImportRepo.php
1 <?php
2
3 namespace BookStack\Exports;
4
5 use BookStack\Activity\ActivityType;
6 use BookStack\Entities\Models\Entity;
7 use BookStack\Entities\Queries\EntityQueries;
8 use BookStack\Exceptions\FileUploadException;
9 use BookStack\Exceptions\ZipExportException;
10 use BookStack\Exceptions\ZipImportException;
11 use BookStack\Exceptions\ZipValidationException;
12 use BookStack\Exports\ZipExports\Models\ZipExportBook;
13 use BookStack\Exports\ZipExports\Models\ZipExportChapter;
14 use BookStack\Exports\ZipExports\Models\ZipExportPage;
15 use BookStack\Exports\ZipExports\ZipExportReader;
16 use BookStack\Exports\ZipExports\ZipExportValidator;
17 use BookStack\Exports\ZipExports\ZipImportRunner;
18 use BookStack\Facades\Activity;
19 use BookStack\Permissions\Permission;
20 use BookStack\Uploads\FileStorage;
21 use Illuminate\Database\Eloquent\Builder;
22 use Illuminate\Database\Eloquent\Collection;
23 use Illuminate\Support\Facades\DB;
24 use Symfony\Component\HttpFoundation\File\UploadedFile;
25
26 class ImportRepo
27 {
28     public function __construct(
29         protected FileStorage $storage,
30         protected ZipImportRunner $importer,
31         protected EntityQueries $entityQueries,
32     ) {
33     }
34
35     /**
36      * @return Collection<Import>
37      */
38     public function getVisibleImports(): Collection
39     {
40         return $this->queryVisible()->get();
41     }
42
43     /**
44      * @return Builder<Import>
45      */
46     public function queryVisible(): Builder
47     {
48         $query = Import::query();
49
50         if (!userCan(Permission::SettingsManage)) {
51             $query->where('created_by', user()->id);
52         }
53
54         return $query;
55     }
56
57     public function findVisible(int $id): Import
58     {
59         $query = Import::query();
60
61         if (!userCan(Permission::SettingsManage)) {
62             $query->where('created_by', user()->id);
63         }
64
65         return $query->findOrFail($id);
66     }
67
68     /**
69      * @throws FileUploadException
70      * @throws ZipValidationException
71      * @throws ZipExportException
72      */
73     public function storeFromUpload(UploadedFile $file): Import
74     {
75         $zipPath = $file->getRealPath();
76         $reader = new ZipExportReader($zipPath);
77
78         $errors = (new ZipExportValidator($reader))->validate();
79         if ($errors) {
80             throw new ZipValidationException($errors);
81         }
82
83         $exportModel = $reader->decodeDataToExportModel();
84
85         $import = new Import();
86         $import->type = match (get_class($exportModel)) {
87             ZipExportPage::class => 'page',
88             ZipExportChapter::class => 'chapter',
89             ZipExportBook::class => 'book',
90         };
91
92         $import->name = $exportModel->name;
93         $import->created_by = user()->id;
94         $import->size = filesize($zipPath);
95
96         $exportModel->metadataOnly();
97         $import->metadata = json_encode($exportModel);
98
99         $path = $this->storage->uploadFile(
100             $file,
101             'uploads/files/imports/',
102             '',
103             'zip'
104         );
105
106         $import->path = $path;
107         $import->save();
108
109         Activity::add(ActivityType::IMPORT_CREATE, $import);
110
111         return $import;
112     }
113
114     /**
115      * @throws ZipImportException
116      */
117     public function runImport(Import $import, ?string $parent = null): Entity
118     {
119         $parentModel = null;
120         if ($import->type === 'page' || $import->type === 'chapter') {
121             $parentModel = $parent ? $this->entityQueries->findVisibleByStringIdentifier($parent) : null;
122         }
123
124         DB::beginTransaction();
125         try {
126             $model = $this->importer->run($import, $parentModel);
127         } catch (ZipImportException $e) {
128             DB::rollBack();
129             $this->importer->revertStoredFiles();
130             throw $e;
131         }
132
133         DB::commit();
134         $this->deleteImport($import);
135         Activity::add(ActivityType::IMPORT_RUN, $import);
136
137         return $model;
138     }
139
140     public function deleteImport(Import $import): void
141     {
142         $this->storage->delete($import->path);
143         $import->delete();
144
145         Activity::add(ActivityType::IMPORT_DELETE, $import);
146     }
147 }