1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-01-31 12:11:37 +01:00

Attached images to pages and added restriction filtering

Closes #79
This commit is contained in:
Dan Brown 2016-03-13 13:30:47 +00:00
parent 75ecf1c44d
commit dbe11c1360
10 changed files with 73 additions and 45 deletions

View File

@ -20,8 +20,8 @@ class ImageController extends Controller
/** /**
* ImageController constructor. * ImageController constructor.
* @param Image $image * @param Image $image
* @param File $file * @param File $file
* @param ImageRepo $imageRepo * @param ImageRepo $imageRepo
*/ */
public function __construct(Image $image, File $file, ImageRepo $imageRepo) public function __construct(Image $image, File $file, ImageRepo $imageRepo)
@ -34,6 +34,7 @@ class ImageController extends Controller
/** /**
* Get all images for a specific type, Paginated * Get all images for a specific type, Paginated
* @param string $type
* @param int $page * @param int $page
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
@ -56,7 +57,7 @@ class ImageController extends Controller
/** /**
* Handles image uploads for use on pages. * Handles image uploads for use on pages.
* @param string $type * @param string $type
* @param Request $request * @param Request $request
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
@ -64,13 +65,15 @@ class ImageController extends Controller
{ {
$this->checkPermission('image-create-all'); $this->checkPermission('image-create-all');
$this->validate($request, [ $this->validate($request, [
'file' => 'image|mimes:jpeg,gif,png' 'file' => 'image|mimes:jpeg,gif,png',
'uploaded_to' => 'integer|exists:pages,id'
]); ]);
$imageUpload = $request->file('file'); $imageUpload = $request->file('file');
try { try {
$image = $this->imageRepo->saveNew($imageUpload, $type); $uploadedTo = $request->has('uploaded_to') ? $request->get('uploaded_to') : 0;
$image = $this->imageRepo->saveNew($imageUpload, $type, $uploadedTo);
} catch (ImageUploadException $e) { } catch (ImageUploadException $e) {
return response($e->getMessage(), 500); return response($e->getMessage(), 500);
} }
@ -96,7 +99,7 @@ class ImageController extends Controller
/** /**
* Update image details * Update image details
* @param $imageId * @param integer $imageId
* @param Request $request * @param Request $request
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
@ -114,8 +117,8 @@ class ImageController extends Controller
/** /**
* Deletes an image and all thumbnail/image files * Deletes an image and all thumbnail/image files
* @param PageRepo $pageRepo * @param PageRepo $pageRepo
* @param Request $request * @param Request $request
* @param int $id * @param int $id
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function destroy(PageRepo $pageRepo, Request $request, $id) public function destroy(PageRepo $pageRepo, Request $request, $id)

View File

@ -3,6 +3,7 @@
use BookStack\Image; use BookStack\Image;
use BookStack\Services\ImageService; use BookStack\Services\ImageService;
use BookStack\Services\RestrictionService;
use Setting; use Setting;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
@ -11,16 +12,19 @@ class ImageRepo
protected $image; protected $image;
protected $imageService; protected $imageService;
protected $restictionService;
/** /**
* ImageRepo constructor. * ImageRepo constructor.
* @param Image $image * @param Image $image
* @param ImageService $imageService * @param ImageService $imageService
* @param RestrictionService $restrictionService
*/ */
public function __construct(Image $image, ImageService $imageService) public function __construct(Image $image, ImageService $imageService, RestrictionService $restrictionService)
{ {
$this->image = $image; $this->image = $image;
$this->imageService = $imageService; $this->imageService = $imageService;
$this->restictionService = $restrictionService;
} }
@ -34,13 +38,12 @@ class ImageRepo
return $this->image->findOrFail($id); return $this->image->findOrFail($id);
} }
/** /**
* Gets a load images paginated, filtered by image type. * Gets a load images paginated, filtered by image type.
* @param string $type * @param string $type
* @param int $page * @param int $page
* @param int $pageSize * @param int $pageSize
* @param bool|int $userFilter * @param bool|int $userFilter
* @return array * @return array
*/ */
public function getPaginatedByType($type, $page = 0, $pageSize = 24, $userFilter = false) public function getPaginatedByType($type, $page = 0, $pageSize = 24, $userFilter = false)
@ -51,6 +54,7 @@ class ImageRepo
$images = $images->where('created_by', '=', $userFilter); $images = $images->where('created_by', '=', $userFilter);
} }
$images = $this->restictionService->filterRelatedPages($images, 'images', 'uploaded_to');
$images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get(); $images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get();
$hasMore = count($images) > $pageSize; $hasMore = count($images) > $pageSize;
@ -68,12 +72,13 @@ class ImageRepo
/** /**
* Save a new image into storage and return the new image. * Save a new image into storage and return the new image.
* @param UploadedFile $uploadFile * @param UploadedFile $uploadFile
* @param string $type * @param string $type
* @param int $uploadedTo
* @return Image * @return Image
*/ */
public function saveNew(UploadedFile $uploadFile, $type) public function saveNew(UploadedFile $uploadFile, $type, $uploadedTo = 0)
{ {
$image = $this->imageService->saveNewFromUpload($uploadFile, $type); $image = $this->imageService->saveNewFromUpload($uploadFile, $type, $uploadedTo);
$this->loadThumbs($image); $this->loadThumbs($image);
return $image; return $image;
} }
@ -123,9 +128,9 @@ class ImageRepo
* Checks the cache then storage to avoid creating / accessing the filesystem on every check. * Checks the cache then storage to avoid creating / accessing the filesystem on every check.
* *
* @param Image $image * @param Image $image
* @param int $width * @param int $width
* @param int $height * @param int $height
* @param bool $keepRatio * @param bool $keepRatio
* @return string * @return string
*/ */
public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false) public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)

View File

@ -41,14 +41,16 @@ class ImageService
/** /**
* Saves a new image from an upload. * Saves a new image from an upload.
* @param UploadedFile $uploadedFile * @param UploadedFile $uploadedFile
* @param string $type * @param string $type
* @param int $uploadedTo
* @return mixed * @return mixed
* @throws ImageUploadException
*/ */
public function saveNewFromUpload(UploadedFile $uploadedFile, $type) public function saveNewFromUpload(UploadedFile $uploadedFile, $type, $uploadedTo = 0)
{ {
$imageName = $uploadedFile->getClientOriginalName(); $imageName = $uploadedFile->getClientOriginalName();
$imageData = file_get_contents($uploadedFile->getRealPath()); $imageData = file_get_contents($uploadedFile->getRealPath());
return $this->saveNew($imageName, $imageData, $type); return $this->saveNew($imageName, $imageData, $type, $uploadedTo);
} }
@ -73,10 +75,11 @@ class ImageService
* @param string $imageName * @param string $imageName
* @param string $imageData * @param string $imageData
* @param string $type * @param string $type
* @param int $uploadedTo
* @return Image * @return Image
* @throws ImageUploadException * @throws ImageUploadException
*/ */
private function saveNew($imageName, $imageData, $type) private function saveNew($imageName, $imageData, $type, $uploadedTo = 0)
{ {
$storage = $this->getStorage(); $storage = $this->getStorage();
$secureUploads = setting('app-secure-images'); $secureUploads = setting('app-secure-images');
@ -100,7 +103,8 @@ class ImageService
'name' => $imageName, 'name' => $imageName,
'path' => $fullPath, 'path' => $fullPath,
'url' => $this->getPublicUrl($fullPath), 'url' => $this->getPublicUrl($fullPath),
'type' => $type 'type' => $type,
'uploaded_to' => $uploadedTo
]; ];
if (auth()->user() && auth()->user()->id !== 0) { if (auth()->user() && auth()->user()->id !== 0) {

View File

@ -50,10 +50,10 @@ class RestrictionService
public function enforcePageRestrictions($query, $action = 'view') public function enforcePageRestrictions($query, $action = 'view')
{ {
// Prevent drafts being visible to others. // Prevent drafts being visible to others.
$query = $query->where(function($query) { $query = $query->where(function ($query) {
$query->where('draft', '=', false); $query->where('draft', '=', false);
if ($this->currentUser) { if ($this->currentUser) {
$query->orWhere(function($query) { $query->orWhere(function ($query) {
$query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id); $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id);
}); });
} }
@ -264,6 +264,30 @@ class RestrictionService
}); });
} }
/**
* Filters pages that are a direct relation to another item.
* @param $query
* @param $tableName
* @param $entityIdColumn
* @return mixed
*/
public function filterRelatedPages($query, $tableName, $entityIdColumn)
{
if ($this->isAdmin) return $query;
$this->currentAction = 'view';
$tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn];
return $query->where(function ($query) use (&$tableDetails) {
$query->where(function ($query) use (&$tableDetails) {
$query->whereExists(function ($query) use (&$tableDetails) {
$query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
->where(function ($query) {
$this->pageRestrictionQuery($query);
});
})->orWhere($tableDetails['entityIdColumn'], '=', 0);
});
});
}
/** /**
* The query to check the restrictions on an entity. * The query to check the restrictions on an entity.
* @param $query * @param $query

View File

@ -3,7 +3,7 @@
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
class ImageEntitiesAndPageDrafts extends Migration class AddPageDrafts extends Migration
{ {
/** /**
* Run the migrations. * Run the migrations.
@ -12,12 +12,6 @@ class ImageEntitiesAndPageDrafts extends Migration
*/ */
public function up() public function up()
{ {
Schema::table('images', function (Blueprint $table) {
$table->string('entity_type', 100);
$table->integer('entity_id');
$table->index(['entity_type', 'entity_id']);
});
Schema::table('pages', function(Blueprint $table) { Schema::table('pages', function(Blueprint $table) {
$table->boolean('draft')->default(false); $table->boolean('draft')->default(false);
$table->index('draft'); $table->index('draft');
@ -31,12 +25,6 @@ class ImageEntitiesAndPageDrafts extends Migration
*/ */
public function down() public function down()
{ {
Schema::table('images', function (Blueprint $table) {
$table->dropIndex(['entity_type', 'entity_id']);
$table->dropColumn('entity_type');
$table->dropColumn('entity_id');
});
Schema::table('pages', function (Blueprint $table) { Schema::table('pages', function (Blueprint $table) {
$table->dropColumn('draft'); $table->dropColumn('draft');
}); });

View File

@ -13,6 +13,7 @@ module.exports = function (ngApp, events) {
$scope.hasMore = false; $scope.hasMore = false;
$scope.imageUpdateSuccess = false; $scope.imageUpdateSuccess = false;
$scope.imageDeleteSuccess = false; $scope.imageDeleteSuccess = false;
$scope.uploadedTo = $attrs.uploadedTo;
var page = 0; var page = 0;
var previousClickTime = 0; var previousClickTime = 0;

View File

@ -110,7 +110,8 @@ module.exports = function (ngApp, events) {
scope: { scope: {
uploadUrl: '@', uploadUrl: '@',
eventSuccess: '=', eventSuccess: '=',
eventError: '=' eventError: '=',
uploadedTo: '@'
}, },
link: function (scope, element, attrs) { link: function (scope, element, attrs) {
var dropZone = new DropZone(element[0].querySelector('.dropzone-container'), { var dropZone = new DropZone(element[0].querySelector('.dropzone-container'), {
@ -120,6 +121,8 @@ module.exports = function (ngApp, events) {
dz.on('sending', function (file, xhr, data) { dz.on('sending', function (file, xhr, data) {
var token = window.document.querySelector('meta[name=token]').getAttribute('content'); var token = window.document.querySelector('meta[name=token]').getAttribute('content');
data.append('_token', token); data.append('_token', token);
var uploadedTo = typeof scope.uploadedTo === 'undefined' ? 0 : scope.uploadedTo;
data.append('uploaded_to', uploadedTo);
}); });
if (typeof scope.eventSuccess !== 'undefined') dz.on('success', scope.eventSuccess); if (typeof scope.eventSuccess !== 'undefined') dz.on('success', scope.eventSuccess);
dz.on('success', function (file, data) { dz.on('success', function (file, data) {

View File

@ -13,5 +13,5 @@
@include('pages/form', ['model' => $draft]) @include('pages/form', ['model' => $draft])
</form> </form>
</div> </div>
@include('partials/image-manager', ['imageType' => 'gallery']) @include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $draft->id])
@stop @stop

View File

@ -14,6 +14,6 @@
@include('pages/form', ['model' => $page]) @include('pages/form', ['model' => $page])
</form> </form>
</div> </div>
@include('partials/image-manager', ['imageType' => 'gallery']) @include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
@stop @stop

View File

@ -1,4 +1,4 @@
<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController"> <div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController" uploaded-to="{{ $uploaded_to or 0 }}">
<div class="overlay anim-slide" ng-show="showing" ng-cloak ng-click="hide()"> <div class="overlay anim-slide" ng-show="showing" ng-cloak ng-click="hide()">
<div class="image-manager-body" ng-click="$event.stopPropagation()"> <div class="image-manager-body" ng-click="$event.stopPropagation()">
@ -22,7 +22,7 @@
<div class="image-manager-sidebar"> <div class="image-manager-sidebar">
<h2>Images</h2> <h2>Images</h2>
<drop-zone upload-url="@{{getUploadUrl()}}" event-success="uploadSuccess"></drop-zone> <drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
<div class="image-manager-details anim fadeIn" ng-show="selectedImage"> <div class="image-manager-details anim fadeIn" ng-show="selectedImage">
<hr class="even"> <hr class="even">