]> BookStack Code Mirror - bookstack/blob - app/Exports/ZipExports/ZipExportFiles.php
Merge branch 'development' of github.com:BookStackApp/BookStack into development
[bookstack] / app / Exports / ZipExports / ZipExportFiles.php
1 <?php
2
3 namespace BookStack\Exports\ZipExports;
4
5 use BookStack\Uploads\Attachment;
6 use BookStack\Uploads\AttachmentService;
7 use BookStack\Uploads\Image;
8 use BookStack\Uploads\ImageService;
9 use Illuminate\Support\Str;
10
11 class ZipExportFiles
12 {
13     /**
14      * References for attachments by attachment ID.
15      * @var array<int, string>
16      */
17     protected array $attachmentRefsById = [];
18
19     /**
20      * References for images by image ID.
21      * @var array<int, string>
22      */
23     protected array $imageRefsById = [];
24
25     public function __construct(
26         protected AttachmentService $attachmentService,
27         protected ImageService $imageService,
28     ) {
29     }
30
31     /**
32      * Gain a reference to the given attachment instance.
33      * This is expected to be a file-based attachment that the user
34      * has visibility of, no permission/access checks are performed here.
35      */
36     public function referenceForAttachment(Attachment $attachment): string
37     {
38         if (isset($this->attachmentRefsById[$attachment->id])) {
39             return $this->attachmentRefsById[$attachment->id];
40         }
41
42         $existingFiles = $this->getAllFileNames();
43         do {
44             $fileName = Str::random(20) . '.' . $attachment->extension;
45         } while (in_array($fileName, $existingFiles));
46
47         $this->attachmentRefsById[$attachment->id] = $fileName;
48
49         return $fileName;
50     }
51
52     /**
53      * Gain a reference to the given image instance.
54      * This is expected to be an image that the user has visibility of,
55      * no permission/access checks are performed here.
56      */
57     public function referenceForImage(Image $image): string
58     {
59         if (isset($this->imageRefsById[$image->id])) {
60             return $this->imageRefsById[$image->id];
61         }
62
63         $existingFiles = $this->getAllFileNames();
64         $extension = pathinfo($image->path, PATHINFO_EXTENSION);
65         do {
66             $fileName = Str::random(20) . '.' . $extension;
67         } while (in_array($fileName, $existingFiles));
68
69         $this->imageRefsById[$image->id] = $fileName;
70
71         return $fileName;
72     }
73
74     protected function getAllFileNames(): array
75     {
76         return array_merge(
77             array_values($this->attachmentRefsById),
78             array_values($this->imageRefsById),
79         );
80     }
81
82     /**
83      * Extract each of the ZIP export tracked files.
84      * Calls the given callback for each tracked file, passing a temporary
85      * file reference of the file contents, and the zip-local tracked reference.
86      */
87     public function extractEach(callable $callback): void
88     {
89         foreach ($this->attachmentRefsById as $attachmentId => $ref) {
90             $attachment = Attachment::query()->find($attachmentId);
91             $stream = $this->attachmentService->streamAttachmentFromStorage($attachment);
92             $tmpFile = tempnam(sys_get_temp_dir(), 'bszipfile-');
93             $tmpFileStream = fopen($tmpFile, 'w');
94             stream_copy_to_stream($stream, $tmpFileStream);
95             $callback($tmpFile, $ref);
96         }
97
98         foreach ($this->imageRefsById as $imageId => $ref) {
99             $image = Image::query()->find($imageId);
100             $stream = $this->imageService->getImageStream($image);
101             $tmpFile = tempnam(sys_get_temp_dir(), 'bszipimage-');
102             $tmpFileStream = fopen($tmpFile, 'w');
103             stream_copy_to_stream($stream, $tmpFileStream);
104             $callback($tmpFile, $ref);
105         }
106     }
107 }