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