]> BookStack Code Mirror - bookstack/blob - app/Services/AttachmentService.php
Corrected the keys for okta auth
[bookstack] / app / Services / AttachmentService.php
1 <?php namespace BookStack\Services;
2
3 use BookStack\Exceptions\FileUploadException;
4 use BookStack\Attachment;
5 use Exception;
6 use Symfony\Component\HttpFoundation\File\UploadedFile;
7
8 class AttachmentService extends UploadService
9 {
10
11     /**
12      * Get the storage that will be used for storing files.
13      * @return \Illuminate\Contracts\Filesystem\Filesystem
14      */
15     protected function getStorage()
16     {
17         if ($this->storageInstance !== null) {
18             return $this->storageInstance;
19         }
20
21         $storageType = config('filesystems.default');
22
23         // Override default location if set to local public to ensure not visible.
24         if ($storageType === 'local') {
25             $storageType = 'local_secure';
26         }
27
28         $this->storageInstance = $this->fileSystem->disk($storageType);
29
30         return $this->storageInstance;
31     }
32
33     /**
34      * Get an attachment from storage.
35      * @param Attachment $attachment
36      * @return string
37      * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
38      */
39     public function getAttachmentFromStorage(Attachment $attachment)
40     {
41         return $this->getStorage()->get($attachment->path);
42     }
43
44     /**
45      * Store a new attachment upon user upload.
46      * @param UploadedFile $uploadedFile
47      * @param int $page_id
48      * @return Attachment
49      * @throws FileUploadException
50      */
51     public function saveNewUpload(UploadedFile $uploadedFile, $page_id)
52     {
53         $attachmentName = $uploadedFile->getClientOriginalName();
54         $attachmentPath = $this->putFileInStorage($attachmentName, $uploadedFile);
55         $largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order');
56
57         $attachment = Attachment::forceCreate([
58             'name' => $attachmentName,
59             'path' => $attachmentPath,
60             'extension' => $uploadedFile->getClientOriginalExtension(),
61             'uploaded_to' => $page_id,
62             'created_by' => user()->id,
63             'updated_by' => user()->id,
64             'order' => $largestExistingOrder + 1
65         ]);
66
67         return $attachment;
68     }
69
70     /**
71      * Store a upload, saving to a file and deleting any existing uploads
72      * attached to that file.
73      * @param UploadedFile $uploadedFile
74      * @param Attachment $attachment
75      * @return Attachment
76      * @throws FileUploadException
77      */
78     public function saveUpdatedUpload(UploadedFile $uploadedFile, Attachment $attachment)
79     {
80         if (!$attachment->external) {
81             $this->deleteFileInStorage($attachment);
82         }
83
84         $attachmentName = $uploadedFile->getClientOriginalName();
85         $attachmentPath = $this->putFileInStorage($attachmentName, $uploadedFile);
86
87         $attachment->name = $attachmentName;
88         $attachment->path = $attachmentPath;
89         $attachment->external = false;
90         $attachment->extension = $uploadedFile->getClientOriginalExtension();
91         $attachment->save();
92         return $attachment;
93     }
94
95     /**
96      * Save a new File attachment from a given link and name.
97      * @param string $name
98      * @param string $link
99      * @param int $page_id
100      * @return Attachment
101      */
102     public function saveNewFromLink($name, $link, $page_id)
103     {
104         $largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order');
105         return Attachment::forceCreate([
106             'name' => $name,
107             'path' => $link,
108             'external' => true,
109             'extension' => '',
110             'uploaded_to' => $page_id,
111             'created_by' => user()->id,
112             'updated_by' => user()->id,
113             'order' => $largestExistingOrder + 1
114         ]);
115     }
116
117     /**
118      * Updates the file ordering for a listing of attached files.
119      * @param array $attachmentList
120      * @param $pageId
121      */
122     public function updateFileOrderWithinPage($attachmentList, $pageId)
123     {
124         foreach ($attachmentList as $index => $attachment) {
125             Attachment::where('uploaded_to', '=', $pageId)->where('id', '=', $attachment['id'])->update(['order' => $index]);
126         }
127     }
128
129
130     /**
131      * Update the details of a file.
132      * @param Attachment $attachment
133      * @param $requestData
134      * @return Attachment
135      */
136     public function updateFile(Attachment $attachment, $requestData)
137     {
138         $attachment->name = $requestData['name'];
139         if (isset($requestData['link']) && trim($requestData['link']) !== '') {
140             $attachment->path = $requestData['link'];
141             if (!$attachment->external) {
142                 $this->deleteFileInStorage($attachment);
143                 $attachment->external = true;
144             }
145         }
146         $attachment->save();
147         return $attachment;
148     }
149
150     /**
151      * Delete a File from the database and storage.
152      * @param Attachment $attachment
153      * @throws Exception
154      */
155     public function deleteFile(Attachment $attachment)
156     {
157         if ($attachment->external) {
158             $attachment->delete();
159             return;
160         }
161         
162         $this->deleteFileInStorage($attachment);
163         $attachment->delete();
164     }
165
166     /**
167      * Delete a file from the filesystem it sits on.
168      * Cleans any empty leftover folders.
169      * @param Attachment $attachment
170      */
171     protected function deleteFileInStorage(Attachment $attachment)
172     {
173         $storage = $this->getStorage();
174         $dirPath = dirname($attachment->path);
175
176         $storage->delete($attachment->path);
177         if (count($storage->allFiles($dirPath)) === 0) {
178             $storage->deleteDirectory($dirPath);
179         }
180     }
181
182     /**
183      * Store a file in storage with the given filename
184      * @param $attachmentName
185      * @param UploadedFile $uploadedFile
186      * @return string
187      * @throws FileUploadException
188      */
189     protected function putFileInStorage($attachmentName, UploadedFile $uploadedFile)
190     {
191         $attachmentData = file_get_contents($uploadedFile->getRealPath());
192
193         $storage = $this->getStorage();
194         $basePath = 'uploads/files/' . Date('Y-m-M') . '/';
195
196         $uploadFileName = $attachmentName;
197         while ($storage->exists($basePath . $uploadFileName)) {
198             $uploadFileName = str_random(3) . $uploadFileName;
199         }
200
201         $attachmentPath = $basePath . $uploadFileName;
202         try {
203             $storage->put($attachmentPath, $attachmentData);
204         } catch (Exception $e) {
205             throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentPath]));
206         }
207
208         return $attachmentPath;
209     }
210 }