1 <?php namespace BookStack\Services;
4 use BookStack\Exceptions\FileUploadException;
7 use Illuminate\Contracts\Filesystem\FileNotFoundException;
8 use Illuminate\Support\Collection;
9 use Symfony\Component\HttpFoundation\File\UploadedFile;
11 class FileService extends UploadService
15 * Get a file from storage.
19 public function getFile(File $file)
21 $filePath = $this->getStorageBasePath() . $file->path;
22 return $this->getStorage()->get($filePath);
26 * Store a new file upon user upload.
27 * @param UploadedFile $uploadedFile
30 * @throws FileUploadException
32 public function saveNewUpload(UploadedFile $uploadedFile, $page_id)
34 $fileName = $uploadedFile->getClientOriginalName();
35 $filePath = $this->putFileInStorage($fileName, $uploadedFile);
36 $largestExistingOrder = File::where('uploaded_to', '=', $page_id)->max('order');
38 $file = File::forceCreate([
41 'extension' => $uploadedFile->getClientOriginalExtension(),
42 'uploaded_to' => $page_id,
43 'created_by' => user()->id,
44 'updated_by' => user()->id,
45 'order' => $largestExistingOrder + 1
52 * Store a upload, saving to a file and deleting any existing uploads
53 * attached to that file.
54 * @param UploadedFile $uploadedFile
57 * @throws FileUploadException
59 public function saveUpdatedUpload(UploadedFile $uploadedFile, File $file)
61 if (!$file->external) {
62 $this->deleteFileInStorage($file);
65 $fileName = $uploadedFile->getClientOriginalName();
66 $filePath = $this->putFileInStorage($fileName, $uploadedFile);
68 $file->name = $fileName;
69 $file->path = $filePath;
70 $file->external = false;
71 $file->extension = $uploadedFile->getClientOriginalExtension();
77 * Save a new File attachment from a given link and name.
83 public function saveNewFromLink($name, $link, $page_id)
85 $largestExistingOrder = File::where('uploaded_to', '=', $page_id)->max('order');
86 return File::forceCreate([
91 'uploaded_to' => $page_id,
92 'created_by' => user()->id,
93 'updated_by' => user()->id,
94 'order' => $largestExistingOrder + 1
99 * Get the file storage base path, amended for storage type.
100 * This allows us to keep a generic path in the database.
103 private function getStorageBasePath()
105 return $this->isLocal() ? 'storage/' : '';
109 * Updates the file ordering for a listing of attached files.
110 * @param array $fileList
113 public function updateFileOrderWithinPage($fileList, $pageId)
115 foreach ($fileList as $index => $file) {
116 File::where('uploaded_to', '=', $pageId)->where('id', '=', $file['id'])->update(['order' => $index]);
122 * Update the details of a file.
124 * @param $requestData
127 public function updateFile(File $file, $requestData)
129 $file->name = $requestData['name'];
130 if (isset($requestData['link']) && trim($requestData['link']) !== '') {
131 $file->path = $requestData['link'];
132 if (!$file->external) {
133 $this->deleteFileInStorage($file);
134 $file->external = true;
142 * Delete a File from the database and storage.
145 public function deleteFile(File $file)
147 if ($file->external) {
152 $this->deleteFileInStorage($file);
157 * Delete a file from the filesystem it sits on.
158 * Cleans any empty leftover folders.
161 protected function deleteFileInStorage(File $file)
163 $storedFilePath = $this->getStorageBasePath() . $file->path;
164 $storage = $this->getStorage();
165 $dirPath = dirname($storedFilePath);
167 $storage->delete($storedFilePath);
168 if (count($storage->allFiles($dirPath)) === 0) {
169 $storage->deleteDirectory($dirPath);
174 * Store a file in storage with the given filename
176 * @param UploadedFile $uploadedFile
178 * @throws FileUploadException
180 protected function putFileInStorage($fileName, UploadedFile $uploadedFile)
182 $fileData = file_get_contents($uploadedFile->getRealPath());
184 $storage = $this->getStorage();
185 $fileBasePath = 'uploads/files/' . Date('Y-m-M') . '/';
186 $storageBasePath = $this->getStorageBasePath() . $fileBasePath;
188 $uploadFileName = $fileName;
189 while ($storage->exists($storageBasePath . $uploadFileName)) {
190 $uploadFileName = str_random(3) . $uploadFileName;
193 $filePath = $fileBasePath . $uploadFileName;
194 $fileStoragePath = $this->getStorageBasePath() . $filePath;
197 $storage->put($fileStoragePath, $fileData);
198 } catch (Exception $e) {
199 throw new FileUploadException('File path ' . $fileStoragePath . ' could not be uploaded to. Ensure it is writable to the server.');