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