Dan Brown

Attached images to pages and added restriction filtering

Closes #79
...@@ -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">
......