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 $fileData = file_get_contents($uploadedFile->getRealPath());
37 $storage = $this->getStorage();
38 $fileBasePath = 'uploads/files/' . Date('Y-m-M') . '/';
39 $storageBasePath = $this->getStorageBasePath() . $fileBasePath;
41 $uploadFileName = $fileName;
42 while ($storage->exists($storageBasePath . $uploadFileName)) {
43 $uploadFileName = str_random(3) . $uploadFileName;
46 $filePath = $fileBasePath . $uploadFileName;
47 $fileStoragePath = $this->getStorageBasePath() . $filePath;
50 $storage->put($fileStoragePath, $fileData);
51 } catch (Exception $e) {
52 throw new FileUploadException('File path ' . $fileStoragePath . ' could not be uploaded to. Ensure it is writable to the server.');
55 $largestExistingOrder = File::where('uploaded_to', '=', $page_id)->max('order');
57 $file = File::forceCreate([
60 'uploaded_to' => $page_id,
61 'created_by' => user()->id,
62 'updated_by' => user()->id,
63 'order' => $largestExistingOrder + 1
70 * Save a new File attachment from a given link and name.
76 public function saveNewFromLink($name, $link, $page_id)
78 $largestExistingOrder = File::where('uploaded_to', '=', $page_id)->max('order');
79 return File::forceCreate([
83 'uploaded_to' => $page_id,
84 'created_by' => user()->id,
85 'updated_by' => user()->id,
86 'order' => $largestExistingOrder + 1
91 * Get the file storage base path, amended for storage type.
92 * This allows us to keep a generic path in the database.
95 private function getStorageBasePath()
97 return $this->isLocal() ? 'storage/' : '';
101 * Updates the file ordering for a listing of attached files.
102 * @param array $fileList
105 public function updateFileOrderWithinPage($fileList, $pageId)
107 foreach ($fileList as $index => $file) {
108 File::where('uploaded_to', '=', $pageId)->where('id', '=', $file['id'])->update(['order' => $index]);
113 * Delete a file and any empty folders the deletion leaves.
116 public function deleteFile(File $file)
118 if ($file->external) {
123 $storedFilePath = $this->getStorageBasePath() . $file->path;
124 $storage = $this->getStorage();
125 $dirPath = dirname($storedFilePath);
127 $storage->delete($storedFilePath);
128 if (count($storage->allFiles($dirPath)) === 0) {
129 $storage->deleteDirectory($dirPath);