]> BookStack Code Mirror - bookstack/blob - app/Exports/ZipExports/ZipExportBuilder.php
Merge branch 'development' of github.com:BookStackApp/BookStack into development
[bookstack] / app / Exports / ZipExports / ZipExportBuilder.php
1 <?php
2
3 namespace BookStack\Exports\ZipExports;
4
5 use BookStack\App\AppVersion;
6 use BookStack\Entities\Models\Book;
7 use BookStack\Entities\Models\Chapter;
8 use BookStack\Entities\Models\Page;
9 use BookStack\Exceptions\ZipExportException;
10 use BookStack\Exports\ZipExports\Models\ZipExportBook;
11 use BookStack\Exports\ZipExports\Models\ZipExportChapter;
12 use BookStack\Exports\ZipExports\Models\ZipExportPage;
13 use ZipArchive;
14
15 class ZipExportBuilder
16 {
17     protected array $data = [];
18
19     public function __construct(
20         protected ZipExportFiles $files,
21         protected ZipExportReferences $references,
22     ) {
23     }
24
25     /**
26      * @throws ZipExportException
27      */
28     public function buildForPage(Page $page): string
29     {
30         $exportPage = ZipExportPage::fromModel($page, $this->files);
31         $this->data['page'] = $exportPage;
32
33         $this->references->addPage($exportPage);
34
35         return $this->build();
36     }
37
38     /**
39      * @throws ZipExportException
40      */
41     public function buildForChapter(Chapter $chapter): string
42     {
43         $exportChapter = ZipExportChapter::fromModel($chapter, $this->files);
44         $this->data['chapter'] = $exportChapter;
45
46         $this->references->addChapter($exportChapter);
47
48         return $this->build();
49     }
50
51     /**
52      * @throws ZipExportException
53      */
54     public function buildForBook(Book $book): string
55     {
56         $exportBook = ZipExportBook::fromModel($book, $this->files);
57         $this->data['book'] = $exportBook;
58
59         $this->references->addBook($exportBook);
60
61         return $this->build();
62     }
63
64     /**
65      * @throws ZipExportException
66      */
67     protected function build(): string
68     {
69         $this->references->buildReferences($this->files);
70
71         $this->data['exported_at'] = date(DATE_ATOM);
72         $this->data['instance'] = [
73             'id'      => setting('instance-id', ''),
74             'version' => AppVersion::get(),
75         ];
76
77         $zipFile = tempnam(sys_get_temp_dir(), 'bszip-');
78         $zip = new ZipArchive();
79         $opened = $zip->open($zipFile, ZipArchive::CREATE);
80         if ($opened !== true) {
81             throw new ZipExportException('Failed to create zip file for export.');
82         }
83
84         $zip->addFromString('data.json', json_encode($this->data));
85         $zip->addEmptyDir('files');
86
87         $toRemove = [];
88         $addedNames = [];
89
90         try {
91             $this->files->extractEach(function ($filePath, $fileRef) use ($zip, &$toRemove, &$addedNames) {
92                 $entryName = "files/$fileRef";
93                 $zip->addFile($filePath, $entryName);
94                 $toRemove[] = $filePath;
95                 $addedNames[] = $entryName;
96             });
97         } catch (\Exception $exception) {
98             // Cleanup the files we've processed so far and respond back with error
99             foreach ($toRemove as $file) {
100                 unlink($file);
101             }
102             foreach ($addedNames as $name) {
103                 $zip->deleteName($name);
104             }
105             $zip->close();
106             unlink($zipFile);
107             throw new ZipExportException("Failed to add files for ZIP export, received error: " . $exception->getMessage());
108         }
109
110         $zip->close();
111
112         foreach ($toRemove as $file) {
113             unlink($file);
114         }
115
116         return $zipFile;
117     }
118 }