3 namespace BookStack\Exports;
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;
28 public function __construct(
29 protected FileStorage $storage,
30 protected ZipImportRunner $importer,
31 protected EntityQueries $entityQueries,
36 * @return Collection<Import>
38 public function getVisibleImports(): Collection
40 return $this->queryVisible()->get();
44 * @return Builder<Import>
46 public function queryVisible(): Builder
48 $query = Import::query();
50 if (!userCan(Permission::SettingsManage)) {
51 $query->where('created_by', user()->id);
57 public function findVisible(int $id): Import
59 $query = Import::query();
61 if (!userCan(Permission::SettingsManage)) {
62 $query->where('created_by', user()->id);
65 return $query->findOrFail($id);
69 * @throws FileUploadException
70 * @throws ZipValidationException
71 * @throws ZipExportException
73 public function storeFromUpload(UploadedFile $file): Import
75 $zipPath = $file->getRealPath();
76 $reader = new ZipExportReader($zipPath);
78 $errors = (new ZipExportValidator($reader))->validate();
80 throw new ZipValidationException($errors);
83 $exportModel = $reader->decodeDataToExportModel();
85 $import = new Import();
86 $import->type = match (get_class($exportModel)) {
87 ZipExportPage::class => 'page',
88 ZipExportChapter::class => 'chapter',
89 ZipExportBook::class => 'book',
92 $import->name = $exportModel->name;
93 $import->created_by = user()->id;
94 $import->size = filesize($zipPath);
96 $exportModel->metadataOnly();
97 $import->metadata = json_encode($exportModel);
99 $path = $this->storage->uploadFile(
101 'uploads/files/imports/',
106 $import->path = $path;
109 Activity::add(ActivityType::IMPORT_CREATE, $import);
115 * @throws ZipImportException
117 public function runImport(Import $import, ?string $parent = null): Entity
120 if ($import->type === 'page' || $import->type === 'chapter') {
121 $parentModel = $parent ? $this->entityQueries->findVisibleByStringIdentifier($parent) : null;
124 DB::beginTransaction();
126 $model = $this->importer->run($import, $parentModel);
127 } catch (ZipImportException $e) {
129 $this->importer->revertStoredFiles();
134 $this->deleteImport($import);
135 Activity::add(ActivityType::IMPORT_RUN, $import);
140 public function deleteImport(Import $import): void
142 $this->storage->delete($import->path);
145 Activity::add(ActivityType::IMPORT_DELETE, $import);