Attached images to pages and added restriction filtering
Closes #79
Showing
10 changed files
with
59 additions
and
31 deletions
| ... | @@ -34,6 +34,7 @@ class ImageController extends Controller | ... | @@ -34,6 +34,7 @@ class ImageController extends Controller |
| 34 | 34 | ||
| 35 | /** | 35 | /** |
| 36 | * Get all images for a specific type, Paginated | 36 | * Get all images for a specific type, Paginated |
| 37 | + * @param string $type | ||
| 37 | * @param int $page | 38 | * @param int $page |
| 38 | * @return \Illuminate\Http\JsonResponse | 39 | * @return \Illuminate\Http\JsonResponse |
| 39 | */ | 40 | */ |
| ... | @@ -64,13 +65,15 @@ class ImageController extends Controller | ... | @@ -64,13 +65,15 @@ class ImageController extends Controller |
| 64 | { | 65 | { |
| 65 | $this->checkPermission('image-create-all'); | 66 | $this->checkPermission('image-create-all'); |
| 66 | $this->validate($request, [ | 67 | $this->validate($request, [ |
| 67 | - 'file' => 'image|mimes:jpeg,gif,png' | 68 | + 'file' => 'image|mimes:jpeg,gif,png', |
| 69 | + 'uploaded_to' => 'integer|exists:pages,id' | ||
| 68 | ]); | 70 | ]); |
| 69 | 71 | ||
| 70 | $imageUpload = $request->file('file'); | 72 | $imageUpload = $request->file('file'); |
| 71 | 73 | ||
| 72 | try { | 74 | try { |
| 73 | - $image = $this->imageRepo->saveNew($imageUpload, $type); | 75 | + $uploadedTo = $request->has('uploaded_to') ? $request->get('uploaded_to') : 0; |
| 76 | + $image = $this->imageRepo->saveNew($imageUpload, $type, $uploadedTo); | ||
| 74 | } catch (ImageUploadException $e) { | 77 | } catch (ImageUploadException $e) { |
| 75 | return response($e->getMessage(), 500); | 78 | return response($e->getMessage(), 500); |
| 76 | } | 79 | } |
| ... | @@ -96,7 +99,7 @@ class ImageController extends Controller | ... | @@ -96,7 +99,7 @@ class ImageController extends Controller |
| 96 | 99 | ||
| 97 | /** | 100 | /** |
| 98 | * Update image details | 101 | * Update image details |
| 99 | - * @param $imageId | 102 | + * @param integer $imageId |
| 100 | * @param Request $request | 103 | * @param Request $request |
| 101 | * @return \Illuminate\Http\JsonResponse | 104 | * @return \Illuminate\Http\JsonResponse |
| 102 | */ | 105 | */ | ... | ... |
| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | ||
| 4 | use BookStack\Image; | 4 | use BookStack\Image; |
| 5 | use BookStack\Services\ImageService; | 5 | use BookStack\Services\ImageService; |
| 6 | +use BookStack\Services\RestrictionService; | ||
| 6 | use Setting; | 7 | use Setting; |
| 7 | use Symfony\Component\HttpFoundation\File\UploadedFile; | 8 | use Symfony\Component\HttpFoundation\File\UploadedFile; |
| 8 | 9 | ||
| ... | @@ -11,16 +12,19 @@ class ImageRepo | ... | @@ -11,16 +12,19 @@ class ImageRepo |
| 11 | 12 | ||
| 12 | protected $image; | 13 | protected $image; |
| 13 | protected $imageService; | 14 | protected $imageService; |
| 15 | + protected $restictionService; | ||
| 14 | 16 | ||
| 15 | /** | 17 | /** |
| 16 | * ImageRepo constructor. | 18 | * ImageRepo constructor. |
| 17 | * @param Image $image | 19 | * @param Image $image |
| 18 | * @param ImageService $imageService | 20 | * @param ImageService $imageService |
| 21 | + * @param RestrictionService $restrictionService | ||
| 19 | */ | 22 | */ |
| 20 | - public function __construct(Image $image, ImageService $imageService) | 23 | + public function __construct(Image $image, ImageService $imageService, RestrictionService $restrictionService) |
| 21 | { | 24 | { |
| 22 | $this->image = $image; | 25 | $this->image = $image; |
| 23 | $this->imageService = $imageService; | 26 | $this->imageService = $imageService; |
| 27 | + $this->restictionService = $restrictionService; | ||
| 24 | } | 28 | } |
| 25 | 29 | ||
| 26 | 30 | ||
| ... | @@ -34,7 +38,6 @@ class ImageRepo | ... | @@ -34,7 +38,6 @@ class ImageRepo |
| 34 | return $this->image->findOrFail($id); | 38 | return $this->image->findOrFail($id); |
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | - | ||
| 38 | /** | 41 | /** |
| 39 | * Gets a load images paginated, filtered by image type. | 42 | * Gets a load images paginated, filtered by image type. |
| 40 | * @param string $type | 43 | * @param string $type |
| ... | @@ -51,6 +54,7 @@ class ImageRepo | ... | @@ -51,6 +54,7 @@ class ImageRepo |
| 51 | $images = $images->where('created_by', '=', $userFilter); | 54 | $images = $images->where('created_by', '=', $userFilter); |
| 52 | } | 55 | } |
| 53 | 56 | ||
| 57 | + $images = $this->restictionService->filterRelatedPages($images, 'images', 'uploaded_to'); | ||
| 54 | $images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get(); | 58 | $images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get(); |
| 55 | $hasMore = count($images) > $pageSize; | 59 | $hasMore = count($images) > $pageSize; |
| 56 | 60 | ||
| ... | @@ -69,11 +73,12 @@ class ImageRepo | ... | @@ -69,11 +73,12 @@ class ImageRepo |
| 69 | * Save a new image into storage and return the new image. | 73 | * Save a new image into storage and return the new image. |
| 70 | * @param UploadedFile $uploadFile | 74 | * @param UploadedFile $uploadFile |
| 71 | * @param string $type | 75 | * @param string $type |
| 76 | + * @param int $uploadedTo | ||
| 72 | * @return Image | 77 | * @return Image |
| 73 | */ | 78 | */ |
| 74 | - public function saveNew(UploadedFile $uploadFile, $type) | 79 | + public function saveNew(UploadedFile $uploadFile, $type, $uploadedTo = 0) |
| 75 | { | 80 | { |
| 76 | - $image = $this->imageService->saveNewFromUpload($uploadFile, $type); | 81 | + $image = $this->imageService->saveNewFromUpload($uploadFile, $type, $uploadedTo); |
| 77 | $this->loadThumbs($image); | 82 | $this->loadThumbs($image); |
| 78 | return $image; | 83 | return $image; |
| 79 | } | 84 | } | ... | ... |
| ... | @@ -42,13 +42,15 @@ class ImageService | ... | @@ -42,13 +42,15 @@ class ImageService |
| 42 | * Saves a new image from an upload. | 42 | * Saves a new image from an upload. |
| 43 | * @param UploadedFile $uploadedFile | 43 | * @param UploadedFile $uploadedFile |
| 44 | * @param string $type | 44 | * @param string $type |
| 45 | + * @param int $uploadedTo | ||
| 45 | * @return mixed | 46 | * @return mixed |
| 47 | + * @throws ImageUploadException | ||
| 46 | */ | 48 | */ |
| 47 | - public function saveNewFromUpload(UploadedFile $uploadedFile, $type) | 49 | + public function saveNewFromUpload(UploadedFile $uploadedFile, $type, $uploadedTo = 0) |
| 48 | { | 50 | { |
| 49 | $imageName = $uploadedFile->getClientOriginalName(); | 51 | $imageName = $uploadedFile->getClientOriginalName(); |
| 50 | $imageData = file_get_contents($uploadedFile->getRealPath()); | 52 | $imageData = file_get_contents($uploadedFile->getRealPath()); |
| 51 | - return $this->saveNew($imageName, $imageData, $type); | 53 | + return $this->saveNew($imageName, $imageData, $type, $uploadedTo); |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | 56 | ||
| ... | @@ -73,10 +75,11 @@ class ImageService | ... | @@ -73,10 +75,11 @@ class ImageService |
| 73 | * @param string $imageName | 75 | * @param string $imageName |
| 74 | * @param string $imageData | 76 | * @param string $imageData |
| 75 | * @param string $type | 77 | * @param string $type |
| 78 | + * @param int $uploadedTo | ||
| 76 | * @return Image | 79 | * @return Image |
| 77 | * @throws ImageUploadException | 80 | * @throws ImageUploadException |
| 78 | */ | 81 | */ |
| 79 | - private function saveNew($imageName, $imageData, $type) | 82 | + private function saveNew($imageName, $imageData, $type, $uploadedTo = 0) |
| 80 | { | 83 | { |
| 81 | $storage = $this->getStorage(); | 84 | $storage = $this->getStorage(); |
| 82 | $secureUploads = setting('app-secure-images'); | 85 | $secureUploads = setting('app-secure-images'); |
| ... | @@ -100,7 +103,8 @@ class ImageService | ... | @@ -100,7 +103,8 @@ class ImageService |
| 100 | 'name' => $imageName, | 103 | 'name' => $imageName, |
| 101 | 'path' => $fullPath, | 104 | 'path' => $fullPath, |
| 102 | 'url' => $this->getPublicUrl($fullPath), | 105 | 'url' => $this->getPublicUrl($fullPath), |
| 103 | - 'type' => $type | 106 | + 'type' => $type, |
| 107 | + 'uploaded_to' => $uploadedTo | ||
| 104 | ]; | 108 | ]; |
| 105 | 109 | ||
| 106 | if (auth()->user() && auth()->user()->id !== 0) { | 110 | if (auth()->user() && auth()->user()->id !== 0) { | ... | ... |
| ... | @@ -50,10 +50,10 @@ class RestrictionService | ... | @@ -50,10 +50,10 @@ class RestrictionService |
| 50 | public function enforcePageRestrictions($query, $action = 'view') | 50 | public function enforcePageRestrictions($query, $action = 'view') |
| 51 | { | 51 | { |
| 52 | // Prevent drafts being visible to others. | 52 | // Prevent drafts being visible to others. |
| 53 | - $query = $query->where(function($query) { | 53 | + $query = $query->where(function ($query) { |
| 54 | $query->where('draft', '=', false); | 54 | $query->where('draft', '=', false); |
| 55 | if ($this->currentUser) { | 55 | if ($this->currentUser) { |
| 56 | - $query->orWhere(function($query) { | 56 | + $query->orWhere(function ($query) { |
| 57 | $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id); | 57 | $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id); |
| 58 | }); | 58 | }); |
| 59 | } | 59 | } |
| ... | @@ -265,6 +265,30 @@ class RestrictionService | ... | @@ -265,6 +265,30 @@ class RestrictionService |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | /** | 267 | /** |
| 268 | + * Filters pages that are a direct relation to another item. | ||
| 269 | + * @param $query | ||
| 270 | + * @param $tableName | ||
| 271 | + * @param $entityIdColumn | ||
| 272 | + * @return mixed | ||
| 273 | + */ | ||
| 274 | + public function filterRelatedPages($query, $tableName, $entityIdColumn) | ||
| 275 | + { | ||
| 276 | + if ($this->isAdmin) return $query; | ||
| 277 | + $this->currentAction = 'view'; | ||
| 278 | + $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn]; | ||
| 279 | + return $query->where(function ($query) use (&$tableDetails) { | ||
| 280 | + $query->where(function ($query) use (&$tableDetails) { | ||
| 281 | + $query->whereExists(function ($query) use (&$tableDetails) { | ||
| 282 | + $query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn']) | ||
| 283 | + ->where(function ($query) { | ||
| 284 | + $this->pageRestrictionQuery($query); | ||
| 285 | + }); | ||
| 286 | + })->orWhere($tableDetails['entityIdColumn'], '=', 0); | ||
| 287 | + }); | ||
| 288 | + }); | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + /** | ||
| 268 | * The query to check the restrictions on an entity. | 292 | * The query to check the restrictions on an entity. |
| 269 | * @param $query | 293 | * @param $query |
| 270 | * @param $tableName | 294 | * @param $tableName | ... | ... |
| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | use Illuminate\Database\Schema\Blueprint; | 3 | use Illuminate\Database\Schema\Blueprint; |
| 4 | use Illuminate\Database\Migrations\Migration; | 4 | use Illuminate\Database\Migrations\Migration; |
| 5 | 5 | ||
| 6 | -class ImageEntitiesAndPageDrafts extends Migration | 6 | +class AddPageDrafts extends Migration |
| 7 | { | 7 | { |
| 8 | /** | 8 | /** |
| 9 | * Run the migrations. | 9 | * Run the migrations. |
| ... | @@ -12,12 +12,6 @@ class ImageEntitiesAndPageDrafts extends Migration | ... | @@ -12,12 +12,6 @@ class ImageEntitiesAndPageDrafts extends Migration |
| 12 | */ | 12 | */ |
| 13 | public function up() | 13 | public function up() |
| 14 | { | 14 | { |
| 15 | - Schema::table('images', function (Blueprint $table) { | ||
| 16 | - $table->string('entity_type', 100); | ||
| 17 | - $table->integer('entity_id'); | ||
| 18 | - $table->index(['entity_type', 'entity_id']); | ||
| 19 | - }); | ||
| 20 | - | ||
| 21 | Schema::table('pages', function(Blueprint $table) { | 15 | Schema::table('pages', function(Blueprint $table) { |
| 22 | $table->boolean('draft')->default(false); | 16 | $table->boolean('draft')->default(false); |
| 23 | $table->index('draft'); | 17 | $table->index('draft'); |
| ... | @@ -31,12 +25,6 @@ class ImageEntitiesAndPageDrafts extends Migration | ... | @@ -31,12 +25,6 @@ class ImageEntitiesAndPageDrafts extends Migration |
| 31 | */ | 25 | */ |
| 32 | public function down() | 26 | public function down() |
| 33 | { | 27 | { |
| 34 | - Schema::table('images', function (Blueprint $table) { | ||
| 35 | - $table->dropIndex(['entity_type', 'entity_id']); | ||
| 36 | - $table->dropColumn('entity_type'); | ||
| 37 | - $table->dropColumn('entity_id'); | ||
| 38 | - }); | ||
| 39 | - | ||
| 40 | Schema::table('pages', function (Blueprint $table) { | 28 | Schema::table('pages', function (Blueprint $table) { |
| 41 | $table->dropColumn('draft'); | 29 | $table->dropColumn('draft'); |
| 42 | }); | 30 | }); | ... | ... |
| ... | @@ -13,6 +13,7 @@ module.exports = function (ngApp, events) { | ... | @@ -13,6 +13,7 @@ module.exports = function (ngApp, events) { |
| 13 | $scope.hasMore = false; | 13 | $scope.hasMore = false; |
| 14 | $scope.imageUpdateSuccess = false; | 14 | $scope.imageUpdateSuccess = false; |
| 15 | $scope.imageDeleteSuccess = false; | 15 | $scope.imageDeleteSuccess = false; |
| 16 | + $scope.uploadedTo = $attrs.uploadedTo; | ||
| 16 | 17 | ||
| 17 | var page = 0; | 18 | var page = 0; |
| 18 | var previousClickTime = 0; | 19 | var previousClickTime = 0; | ... | ... |
| ... | @@ -110,7 +110,8 @@ module.exports = function (ngApp, events) { | ... | @@ -110,7 +110,8 @@ module.exports = function (ngApp, events) { |
| 110 | scope: { | 110 | scope: { |
| 111 | uploadUrl: '@', | 111 | uploadUrl: '@', |
| 112 | eventSuccess: '=', | 112 | eventSuccess: '=', |
| 113 | - eventError: '=' | 113 | + eventError: '=', |
| 114 | + uploadedTo: '@' | ||
| 114 | }, | 115 | }, |
| 115 | link: function (scope, element, attrs) { | 116 | link: function (scope, element, attrs) { |
| 116 | var dropZone = new DropZone(element[0].querySelector('.dropzone-container'), { | 117 | var dropZone = new DropZone(element[0].querySelector('.dropzone-container'), { |
| ... | @@ -120,6 +121,8 @@ module.exports = function (ngApp, events) { | ... | @@ -120,6 +121,8 @@ module.exports = function (ngApp, events) { |
| 120 | dz.on('sending', function (file, xhr, data) { | 121 | dz.on('sending', function (file, xhr, data) { |
| 121 | var token = window.document.querySelector('meta[name=token]').getAttribute('content'); | 122 | var token = window.document.querySelector('meta[name=token]').getAttribute('content'); |
| 122 | data.append('_token', token); | 123 | data.append('_token', token); |
| 124 | + var uploadedTo = typeof scope.uploadedTo === 'undefined' ? 0 : scope.uploadedTo; | ||
| 125 | + data.append('uploaded_to', uploadedTo); | ||
| 123 | }); | 126 | }); |
| 124 | if (typeof scope.eventSuccess !== 'undefined') dz.on('success', scope.eventSuccess); | 127 | if (typeof scope.eventSuccess !== 'undefined') dz.on('success', scope.eventSuccess); |
| 125 | dz.on('success', function (file, data) { | 128 | dz.on('success', function (file, data) { | ... | ... |
| ... | @@ -13,5 +13,5 @@ | ... | @@ -13,5 +13,5 @@ |
| 13 | @include('pages/form', ['model' => $draft]) | 13 | @include('pages/form', ['model' => $draft]) |
| 14 | </form> | 14 | </form> |
| 15 | </div> | 15 | </div> |
| 16 | - @include('partials/image-manager', ['imageType' => 'gallery']) | 16 | + @include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $draft->id]) |
| 17 | @stop | 17 | @stop |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -14,6 +14,6 @@ | ... | @@ -14,6 +14,6 @@ |
| 14 | @include('pages/form', ['model' => $page]) | 14 | @include('pages/form', ['model' => $page]) |
| 15 | </form> | 15 | </form> |
| 16 | </div> | 16 | </div> |
| 17 | - @include('partials/image-manager', ['imageType' => 'gallery']) | 17 | + @include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id]) |
| 18 | 18 | ||
| 19 | @stop | 19 | @stop |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | -<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController"> | 1 | +<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController" uploaded-to="{{ $uploaded_to or 0 }}"> |
| 2 | <div class="overlay anim-slide" ng-show="showing" ng-cloak ng-click="hide()"> | 2 | <div class="overlay anim-slide" ng-show="showing" ng-cloak ng-click="hide()"> |
| 3 | <div class="image-manager-body" ng-click="$event.stopPropagation()"> | 3 | <div class="image-manager-body" ng-click="$event.stopPropagation()"> |
| 4 | 4 | ||
| ... | @@ -22,7 +22,7 @@ | ... | @@ -22,7 +22,7 @@ |
| 22 | 22 | ||
| 23 | <div class="image-manager-sidebar"> | 23 | <div class="image-manager-sidebar"> |
| 24 | <h2>Images</h2> | 24 | <h2>Images</h2> |
| 25 | - <drop-zone upload-url="@{{getUploadUrl()}}" event-success="uploadSuccess"></drop-zone> | 25 | + <drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone> |
| 26 | <div class="image-manager-details anim fadeIn" ng-show="selectedImage"> | 26 | <div class="image-manager-details anim fadeIn" ng-show="selectedImage"> |
| 27 | 27 | ||
| 28 | <hr class="even"> | 28 | <hr class="even"> | ... | ... |
-
Please register or sign in to post a comment