Still in testing.
Adds STORAGE_TYPE=local_secure option for setting images to be behind
auth. Stores images alongside attachments in /storage/uploads/images.
<?php namespace BookStack\Http\Controllers;
use BookStack\Exceptions\ImageUploadException;
+use BookStack\Exceptions\NotFoundException;
use BookStack\Repos\EntityRepo;
use BookStack\Repos\ImageRepo;
use Illuminate\Filesystem\Filesystem as File;
parent::__construct();
}
+ /**
+ * Provide an image file from storage.
+ * @param string $path
+ * @return mixed
+ */
+ public function showImage(string $path)
+ {
+ $path = storage_path('uploads/images/' . $path);
+ if (!file_exists($path)) {
+ abort(404);
+ }
+
+ return response()->file($path);
+ }
+
/**
* Get all images for a specific type, Paginated
* @param string $type
* Get the thumbnail for an image.
* If $keepRatio is true only the width will be used.
* Checks the cache then storage to avoid creating / accessing the filesystem on every check.
- *
* @param Image $image
* @param int $width
* @param int $height
{
try {
return $this->imageService->getThumbnail($image, $width, $height, $keepRatio);
- } catch (FileNotFoundException $exception) {
- $image->delete();
- return [];
+ } catch (\Exception $exception) {
+ dd($exception);
+ return null;
}
}
class AttachmentService extends UploadService
{
+ /**
+ * Get the storage that will be used for storing files.
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
+ */
+ protected function getStorage()
+ {
+ if ($this->storageInstance !== null) return $this->storageInstance;
+
+ $storageType = config('filesystems.default');
+
+ // Override default location if set to local public to ensure not visible.
+ if ($storageType === 'local') {
+ $storageType = 'local_secure';
+ }
+
+ $this->storageInstance = $this->fileSystem->disk($storageType);
+
+ return $this->storageInstance;
+ }
+
/**
* Get an attachment from storage.
* @param Attachment $attachment
* @return string
+ * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function getAttachmentFromStorage(Attachment $attachment)
{
- $attachmentPath = $this->getStorageBasePath() . $attachment->path;
- return $this->getStorage()->get($attachmentPath);
+ return $this->getStorage()->get($attachment->path);
}
/**
]);
}
- /**
- * Get the file storage base path, amended for storage type.
- * This allows us to keep a generic path in the database.
- * @return string
- */
- private function getStorageBasePath()
- {
- return $this->isLocal() ? 'storage/' : '';
- }
-
/**
* Updates the file ordering for a listing of attached files.
* @param array $attachmentList
/**
* Delete a File from the database and storage.
* @param Attachment $attachment
+ * @throws Exception
*/
public function deleteFile(Attachment $attachment)
{
*/
protected function deleteFileInStorage(Attachment $attachment)
{
- $storedFilePath = $this->getStorageBasePath() . $attachment->path;
$storage = $this->getStorage();
- $dirPath = dirname($storedFilePath);
+ $dirPath = dirname($attachment->path);
- $storage->delete($storedFilePath);
+ $storage->delete($attachment->path);
if (count($storage->allFiles($dirPath)) === 0) {
$storage->deleteDirectory($dirPath);
}
$attachmentData = file_get_contents($uploadedFile->getRealPath());
$storage = $this->getStorage();
- $attachmentBasePath = 'uploads/files/' . Date('Y-m-M') . '/';
- $storageBasePath = $this->getStorageBasePath() . $attachmentBasePath;
+ $basePath = 'uploads/files/' . Date('Y-m-M') . '/';
$uploadFileName = $attachmentName;
- while ($storage->exists($storageBasePath . $uploadFileName)) {
+ while ($storage->exists($basePath . $uploadFileName)) {
$uploadFileName = str_random(3) . $uploadFileName;
}
- $attachmentPath = $attachmentBasePath . $uploadFileName;
- $attachmentStoragePath = $this->getStorageBasePath() . $attachmentPath;
-
+ $attachmentPath = $basePath . $uploadFileName;
try {
- $storage->put($attachmentStoragePath, $attachmentData);
+ $storage->put($attachmentPath, $attachmentData);
} catch (Exception $e) {
- throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentStoragePath]));
+ throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentPath]));
}
+
return $attachmentPath;
}
return $this->saveNew($imageName, $imageData, $type, $uploadedTo);
}
-
/**
* Gets an image from url and saves it to the database.
* @param $url
$imagePath = '/uploads/images/' . $type . '/' . Date('Y-m-M') . '/';
- if ($this->isLocal()) $imagePath = '/public' . $imagePath;
-
while ($storage->exists($imagePath . $imageName)) {
$imageName = str_random(3) . $imageName;
}
throw new ImageUploadException(trans('errors.path_not_writable', ['filePath' => $fullPath]));
}
- if ($this->isLocal()) $fullPath = str_replace_first('/public', '', $fullPath);
-
$imageDetails = [
'name' => $imageName,
'path' => $fullPath,
$imageDetails['updated_by'] = $userId;
}
- $image = Image::forceCreate($imageDetails);
-
+ $image = (new Image());
+ $image->forceFill($imageDetails)->save();
return $image;
}
*/
protected function getPath(Image $image)
{
- return ($this->isLocal()) ? ('public/' . $image->path) : $image->path;
+ return $image->path;
}
/**
* Get the thumbnail for an image.
* If $keepRatio is true only the width will be used.
* Checks the cache then storage to avoid creating / accessing the filesystem on every check.
- *
* @param Image $image
* @param int $width
* @param int $height
}
$storage = $this->getStorage();
-
if ($storage->exists($thumbFilePath)) {
return $this->getPublicUrl($thumbFilePath);
}
} catch (Exception $e) {
if ($e instanceof \ErrorException || $e instanceof NotSupportedException) {
throw new ImageUploadException(trans('errors.cannot_create_thumbs'));
- } else {
- throw $e;
}
+ throw $e;
}
if ($keepRatio) {
$storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket'];
}
}
-
$this->storageUrl = $storageUrl;
}
- if ($this->isLocal()) $filePath = str_replace_first('public/', '', $filePath);
-
- return ($this->storageUrl == false ? rtrim(baseUrl(''), '/') : rtrim($this->storageUrl, '/')) . $filePath;
+ $basePath = ($this->storageUrl == false) ? baseUrl('/') : $this->storageUrl;
+ return rtrim($basePath, '/') . $filePath;
}
return $this->storageInstance;
}
-
/**
* Check whether or not a folder is empty.
* @param $path
'local' => [
'driver' => 'local',
- 'root' => base_path(),
+ 'root' => public_path(),
+ ],
+
+ 'local_secure' => [
+ 'driver' => 'local',
+ 'root' => storage_path(),
],
'ftp' => [
// Authenticated routes...
Route::group(['middleware' => 'auth'], function () {
+ Route::get('/uploads/images/{path}', 'ImageController@showImage')
+ ->where('path', '.*$');
+
Route::group(['prefix' => 'pages'], function() {
Route::get('/recently-created', 'PageController@showRecentlyCreated');
Route::get('/recently-updated', 'PageController@showRecentlyUpdated');
--- /dev/null
+*
+!.gitignore