]> BookStack Code Mirror - bookstack/commitdiff
Added back-end attachments-in-browser support
authorDan Brown <redacted>
Sat, 5 Jun 2021 23:51:06 +0000 (00:51 +0100)
committerDan Brown <redacted>
Sat, 5 Jun 2021 23:51:06 +0000 (00:51 +0100)
A query string will cause attachments to be provided inline
with an appropriate mime type.
Remaining actions:
- Tests
- Front-end functionality
- Config option?

app/Http/Controllers/AttachmentController.php
app/Http/Controllers/Controller.php
app/Uploads/AttachmentService.php
composer.json

index 04e89ac5d1a0db18407398aabd3689951822eee8..74eae641b8e85785b69849d3c7663e2fc892b434 100644 (file)
@@ -14,16 +14,14 @@ use Illuminate\Validation\ValidationException;
 class AttachmentController extends Controller
 {
     protected $attachmentService;
-    protected $attachment;
     protected $pageRepo;
 
     /**
      * AttachmentController constructor.
      */
-    public function __construct(AttachmentService $attachmentService, Attachment $attachment, PageRepo $pageRepo)
+    public function __construct(AttachmentService $attachmentService, PageRepo $pageRepo)
     {
         $this->attachmentService = $attachmentService;
-        $this->attachment = $attachment;
         $this->pageRepo = $pageRepo;
     }
 
@@ -67,7 +65,7 @@ class AttachmentController extends Controller
             'file' => 'required|file'
         ]);
 
-        $attachment = $this->attachment->newQuery()->findOrFail($attachmentId);
+        $attachment = Attachment::query()->findOrFail($attachmentId);
         $this->checkOwnablePermission('view', $attachment->page);
         $this->checkOwnablePermission('page-update', $attachment->page);
         $this->checkOwnablePermission('attachment-create', $attachment);
@@ -89,7 +87,7 @@ class AttachmentController extends Controller
      */
     public function getUpdateForm(string $attachmentId)
     {
-        $attachment = $this->attachment->findOrFail($attachmentId);
+        $attachment = Attachment::query()->findOrFail($attachmentId);
 
         $this->checkOwnablePermission('page-update', $attachment->page);
         $this->checkOwnablePermission('attachment-create', $attachment);
@@ -202,9 +200,10 @@ class AttachmentController extends Controller
      * @throws FileNotFoundException
      * @throws NotFoundException
      */
-    public function get(string $attachmentId)
+    public function get(Request $request, string $attachmentId)
     {
-        $attachment = $this->attachment->findOrFail($attachmentId);
+        /** @var Attachment $attachment */
+        $attachment = Attachment::query()->findOrFail($attachmentId);
         try {
             $page = $this->pageRepo->getById($attachment->uploaded_to);
         } catch (NotFoundException $exception) {
@@ -217,8 +216,13 @@ class AttachmentController extends Controller
             return redirect($attachment->path);
         }
 
+        $fileName = $attachment->getFileName();
         $attachmentContents = $this->attachmentService->getAttachmentFromStorage($attachment);
-        return $this->downloadResponse($attachmentContents, $attachment->getFileName());
+
+        if ($request->get('open') === 'true') {
+            return $this->inlineDownloadResponse($attachmentContents, $fileName);
+        }
+        return $this->downloadResponse($attachmentContents, $fileName);
     }
 
     /**
@@ -227,7 +231,7 @@ class AttachmentController extends Controller
      */
     public function delete(string $attachmentId)
     {
-        $attachment = $this->attachment->findOrFail($attachmentId);
+        $attachment = Attachment::query()->findOrFail($attachmentId);
         $this->checkOwnablePermission('attachment-delete', $attachment);
         $this->attachmentService->deleteFile($attachment);
         return response()->json(['message' => trans('entities.attachments_deleted')]);
index 034dfa524192013b4766880ebb878176ef95d496..47b03b28d48aaa29610ef562635670af6adbb630 100644 (file)
@@ -6,6 +6,7 @@ use BookStack\Facades\Activity;
 use BookStack\Interfaces\Loggable;
 use BookStack\HasCreatorAndUpdater;
 use BookStack\Model;
+use finfo;
 use Illuminate\Foundation\Bus\DispatchesJobs;
 use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Http\Exceptions\HttpResponseException;
@@ -121,6 +122,20 @@ abstract class Controller extends BaseController
         ]);
     }
 
+    /**
+     * Create a file download response that provides the file with a content-type
+     * correct for the file, in a way so the browser can show the content in browser.
+     */
+    protected function inlineDownloadResponse(string $content, string $fileName): Response
+    {
+        $finfo = new finfo(FILEINFO_MIME_TYPE);
+        $mime = $finfo->buffer($content) ?: 'application/octet-stream';
+        return response()->make($content, 200, [
+            'Content-Type'        => $mime,
+            'Content-Disposition' => 'inline; filename="' . $fileName . '"'
+        ]);
+    }
+
     /**
      * Show a positive, successful notification to the user on next view load.
      */
index 4437897c711e49527035d78478e0a95625b2bcbb..37adb4f8368f0c355fcad8ae3bfd37037876c402 100644 (file)
@@ -3,8 +3,10 @@
 use BookStack\Exceptions\FileUploadException;
 use Exception;
 use Illuminate\Contracts\Filesystem\Factory as FileSystem;
+use Illuminate\Contracts\Filesystem\FileNotFoundException;
 use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
 use Illuminate\Support\Str;
+use Log;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 
 class AttachmentService
@@ -38,11 +40,9 @@ class AttachmentService
 
     /**
      * Get an attachment from storage.
-     * @param Attachment $attachment
-     * @return string
-     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+     * @throws FileNotFoundException
      */
-    public function getAttachmentFromStorage(Attachment $attachment)
+    public function getAttachmentFromStorage(Attachment $attachment): string
     {
         return $this->getStorage()->get($attachment->path);
     }
@@ -202,7 +202,7 @@ class AttachmentService
         try {
             $storage->put($attachmentPath, $attachmentData);
         } catch (Exception $e) {
-            \Log::error('Error when attempting file upload:' . $e->getMessage());
+            Log::error('Error when attempting file upload:' . $e->getMessage());
             throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentPath]));
         }
 
index 3e604b8fdfb29dd13cef6be681971ecdad0a9a9b..8450a2f9250205109ecaeebdca2ac3538c86fc69 100644 (file)
@@ -8,6 +8,7 @@
         "php": "^7.3|^8.0",
         "ext-curl": "*",
         "ext-dom": "*",
+        "ext-fileinfo": "*",
         "ext-gd": "*",
         "ext-json": "*",
         "ext-mbstring": "*",