mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Added image user checking before deletion. Fixes #13.
This commit is contained in:
parent
03f5f9e9b9
commit
69eff86ff5
@ -9,6 +9,7 @@ use Intervention\Image\Facades\Image as ImageTool;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Oxbow\Http\Requests;
|
use Oxbow\Http\Requests;
|
||||||
use Oxbow\Image;
|
use Oxbow\Image;
|
||||||
|
use Oxbow\Repos\PageRepo;
|
||||||
|
|
||||||
class ImageController extends Controller
|
class ImageController extends Controller
|
||||||
{
|
{
|
||||||
@ -27,42 +28,6 @@ class ImageController extends Controller
|
|||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an image from behind the public-facing application.
|
|
||||||
* @param Request $request
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function getImage(Request $request)
|
|
||||||
{
|
|
||||||
$cacheTime = 60 * 60 * 24;
|
|
||||||
$path = storage_path() . '/' . $request->path();
|
|
||||||
$modifiedTime = $this->file->lastModified($path);
|
|
||||||
$eTag = md5($modifiedTime . $path);
|
|
||||||
$headerLastModified = gmdate('r', $modifiedTime);
|
|
||||||
$headerExpires = gmdate('r', $modifiedTime + $cacheTime);
|
|
||||||
|
|
||||||
$headers = [
|
|
||||||
'Last-Modified' => $headerLastModified,
|
|
||||||
'Cache-Control' => 'must-revalidate',
|
|
||||||
'Pragma' => 'public',
|
|
||||||
'Expires' => $headerExpires,
|
|
||||||
'Etag' => $eTag
|
|
||||||
];
|
|
||||||
|
|
||||||
$browserModifiedSince = $request->header('If-Modified-Since');
|
|
||||||
$browserNoneMatch = $request->header('If-None-Match');
|
|
||||||
if ($browserModifiedSince !== null && file_exists($path) && ($browserModifiedSince == $headerLastModified || $browserNoneMatch == $eTag)) {
|
|
||||||
return response()->make('', 304, $headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_exists($path)) {
|
|
||||||
return response()->make(file_get_contents($path), 200, array_merge($headers, [
|
|
||||||
'Content-Type' => $this->file->mimeType($path),
|
|
||||||
'Content-Length' => filesize($path),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all images, Paginated
|
* Get all images, Paginated
|
||||||
@ -167,14 +132,23 @@ class ImageController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes an image and all thumbnail/image files
|
* Deletes an image and all thumbnail/image files
|
||||||
* @param $id
|
* @param PageRepo $pageRepo
|
||||||
|
* @param Request $request
|
||||||
|
* @param int $id
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function destroy($id)
|
public function destroy(PageRepo $pageRepo, Request $request, $id)
|
||||||
{
|
{
|
||||||
$this->checkPermission('image-delete');
|
$this->checkPermission('image-delete');
|
||||||
$image = $this->image->findOrFail($id);
|
$image = $this->image->findOrFail($id);
|
||||||
|
|
||||||
|
// Check if this image is used on any pages
|
||||||
|
$pageSearch = $pageRepo->searchForImage($image->url);
|
||||||
|
$isForced = ($request->has('force') && ($request->get('force') === 'true') || $request->get('force') === true);
|
||||||
|
if ($pageSearch !== false && !$isForced) {
|
||||||
|
return response()->json($pageSearch, 400);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete files
|
// Delete files
|
||||||
$folder = public_path() . dirname($image->url);
|
$folder = public_path() . dirname($image->url);
|
||||||
$fileName = basename($image->url);
|
$fileName = basename($image->url);
|
||||||
|
@ -59,7 +59,6 @@ Route::group(['middleware' => 'auth'], function () {
|
|||||||
Route::put('/images/update/{imageId}', 'ImageController@update');
|
Route::put('/images/update/{imageId}', 'ImageController@update');
|
||||||
Route::delete('/images/{imageId}', 'ImageController@destroy');
|
Route::delete('/images/{imageId}', 'ImageController@destroy');
|
||||||
Route::get('/images/all/{page}', 'ImageController@getAll');
|
Route::get('/images/all/{page}', 'ImageController@getAll');
|
||||||
Route::get('/images/{any}', 'ImageController@getImage')->where('any', '.*');
|
|
||||||
|
|
||||||
// Links
|
// Links
|
||||||
Route::get('/link/{id}', 'PageController@redirectFromLink');
|
Route::get('/link/{id}', 'PageController@redirectFromLink');
|
||||||
|
@ -92,6 +92,22 @@ class PageRepo
|
|||||||
return $pages;
|
return $pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for image usage.
|
||||||
|
* @param $imageString
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function searchForImage($imageString)
|
||||||
|
{
|
||||||
|
$pages = $this->page->where('html', 'like', '%'.$imageString.'%')->get();
|
||||||
|
foreach($pages as $page) {
|
||||||
|
$page->url = $page->getUrl();
|
||||||
|
$page->html = '';
|
||||||
|
$page->text = '';
|
||||||
|
}
|
||||||
|
return count($pages) > 0 ? $pages : false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a page with any fillable data and saves it into the database.
|
* Updates a page with any fillable data and saves it into the database.
|
||||||
* @param Page $page
|
* @param Page $page
|
||||||
|
4
resources/assets/js/jquery-extensions.js
vendored
4
resources/assets/js/jquery-extensions.js
vendored
@ -41,3 +41,7 @@ jQuery.fn.showFailure = function (messageMap) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jQuery.fn.submitForm = function() {
|
||||||
|
$(this).closest('form').submit();
|
||||||
|
};
|
@ -15,9 +15,6 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
display: flex;
|
display: flex;
|
||||||
p, h1, h2, h3, h4, label, input {
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
h1, h2, h3 {
|
h1, h2, h3 {
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<a onclick="window.history.back();" class="button muted">Cancel</a>
|
<a href="{{ back()->getTargetUrl() }}" class="button muted">Cancel</a>
|
||||||
<button type="submit" class="button pos">Save</button>
|
<button type="submit" class="button pos">Save</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-8 faded">
|
<div class="col-md-8 faded">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a onclick="window.history.back();" class="text-primary"><i class="zmdi zmdi-close"></i>Cancel</a>
|
<a href="{{ back()->getTargetUrl() }}" class="text-primary"><i class="zmdi zmdi-close"></i>Cancel</a>
|
||||||
<a onclick="$(this).closest('form').submit();" type="submit" class="text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</a>
|
<a onclick="$(this).submitForm();" type="submit" class="text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
<div id="image-manager">
|
<div id="image-manager">
|
||||||
<div class="overlay" v-el="overlay" v-on="click: overlayClick" style="display:none;">
|
<div class="overlay" v-el="overlay" v-on="click: overlayClick" >
|
||||||
<div class="image-manager-body">
|
<div class="image-manager-body">
|
||||||
<div class="image-manager-content">
|
<div class="image-manager-content">
|
||||||
<div class="image-manager-list">
|
<div class="image-manager-list">
|
||||||
@ -31,8 +31,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<hr class="even">
|
<hr class="even">
|
||||||
|
<div v-show="dependantPages">
|
||||||
|
<p class="text-neg text-small">
|
||||||
|
This image is used in the pages below, Click delete again to confirm you want to delete this image.
|
||||||
|
</p>
|
||||||
|
<ul class="text-neg">
|
||||||
|
<li v-repeat="page: dependantPages">
|
||||||
|
<a v-attr="href: page.url" target="_blank" class="text-neg">@{{ page.name }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form v-on="submit: deleteImage" v-el="imageDeleteForm">
|
<form v-on="submit: deleteImage" v-el="imageDeleteForm">
|
||||||
{{ csrf_field() }}
|
<input type="hidden" v-model="deleteForm._token" value="{{ csrf_token() }}">
|
||||||
<button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
|
<button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user