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.
* @param Image $image
* @param File $file
* @param Image $image
* @param File $file
* @param 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
* @param string $type
* @param int $page
* @return \Illuminate\Http\JsonResponse
*/
@ -56,7 +57,7 @@ class ImageController extends Controller
/**
* Handles image uploads for use on pages.
* @param string $type
* @param string $type
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
@ -64,13 +65,15 @@ class ImageController extends Controller
{
$this->checkPermission('image-create-all');
$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');
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) {
return response($e->getMessage(), 500);
}
@ -96,7 +99,7 @@ class ImageController extends Controller
/**
* Update image details
* @param $imageId
* @param integer $imageId
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
@ -114,8 +117,8 @@ class ImageController extends Controller
/**
* Deletes an image and all thumbnail/image files
* @param PageRepo $pageRepo
* @param Request $request
* @param int $id
* @param Request $request
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function destroy(PageRepo $pageRepo, Request $request, $id)

View File

@ -3,6 +3,7 @@
use BookStack\Image;
use BookStack\Services\ImageService;
use BookStack\Services\RestrictionService;
use Setting;
use Symfony\Component\HttpFoundation\File\UploadedFile;
@ -11,16 +12,19 @@ class ImageRepo
protected $image;
protected $imageService;
protected $restictionService;
/**
* ImageRepo constructor.
* @param Image $image
* @param Image $image
* @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->imageService = $imageService;
$this->restictionService = $restrictionService;
}
@ -34,13 +38,12 @@ class ImageRepo
return $this->image->findOrFail($id);
}
/**
* Gets a load images paginated, filtered by image type.
* @param string $type
* @param int $page
* @param int $pageSize
* @param bool|int $userFilter
* @param int $page
* @param int $pageSize
* @param bool|int $userFilter
* @return array
*/
public function getPaginatedByType($type, $page = 0, $pageSize = 24, $userFilter = false)
@ -51,6 +54,7 @@ class ImageRepo
$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();
$hasMore = count($images) > $pageSize;
@ -68,12 +72,13 @@ class ImageRepo
/**
* Save a new image into storage and return the new image.
* @param UploadedFile $uploadFile
* @param string $type
* @param string $type
* @param int $uploadedTo
* @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);
return $image;
}
@ -123,9 +128,9 @@ class ImageRepo
* Checks the cache then storage to avoid creating / accessing the filesystem on every check.
*
* @param Image $image
* @param int $width
* @param int $height
* @param bool $keepRatio
* @param int $width
* @param int $height
* @param bool $keepRatio
* @return string
*/
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.
* @param UploadedFile $uploadedFile
* @param string $type
* @param string $type
* @param int $uploadedTo
* @return mixed
* @throws ImageUploadException
*/
public function saveNewFromUpload(UploadedFile $uploadedFile, $type)
public function saveNewFromUpload(UploadedFile $uploadedFile, $type, $uploadedTo = 0)
{
$imageName = $uploadedFile->getClientOriginalName();
$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 $imageData
* @param string $type
* @param int $uploadedTo
* @return Image
* @throws ImageUploadException
*/
private function saveNew($imageName, $imageData, $type)
private function saveNew($imageName, $imageData, $type, $uploadedTo = 0)
{
$storage = $this->getStorage();
$secureUploads = setting('app-secure-images');
@ -100,7 +103,8 @@ class ImageService
'name' => $imageName,
'path' => $fullPath,
'url' => $this->getPublicUrl($fullPath),
'type' => $type
'type' => $type,
'uploaded_to' => $uploadedTo
];
if (auth()->user() && auth()->user()->id !== 0) {

View File

@ -50,10 +50,10 @@ class RestrictionService
public function enforcePageRestrictions($query, $action = 'view')
{
// Prevent drafts being visible to others.
$query = $query->where(function($query) {
$query = $query->where(function ($query) {
$query->where('draft', '=', false);
if ($this->currentUser) {
$query->orWhere(function($query) {
$query->orWhere(function ($query) {
$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.
* @param $query

View File

@ -3,7 +3,7 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class ImageEntitiesAndPageDrafts extends Migration
class AddPageDrafts extends Migration
{
/**
* Run the migrations.
@ -12,12 +12,6 @@ class ImageEntitiesAndPageDrafts extends Migration
*/
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) {
$table->boolean('draft')->default(false);
$table->index('draft');
@ -31,12 +25,6 @@ class ImageEntitiesAndPageDrafts extends Migration
*/
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) {
$table->dropColumn('draft');
});

View File

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

View File

@ -110,7 +110,8 @@ module.exports = function (ngApp, events) {
scope: {
uploadUrl: '@',
eventSuccess: '=',
eventError: '='
eventError: '=',
uploadedTo: '@'
},
link: function (scope, element, attrs) {
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) {
var token = window.document.querySelector('meta[name=token]').getAttribute('content');
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);
dz.on('success', function (file, data) {

View File

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

View File

@ -14,6 +14,6 @@
@include('pages/form', ['model' => $page])
</form>
</div>
@include('partials/image-manager', ['imageType' => 'gallery'])
@include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
@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="image-manager-body" ng-click="$event.stopPropagation()">
@ -22,7 +22,7 @@
<div class="image-manager-sidebar">
<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">
<hr class="even">