Dan Brown

Added image user checking before deletion. Fixes #13.

...@@ -9,6 +9,7 @@ use Intervention\Image\Facades\Image as ImageTool; ...@@ -9,6 +9,7 @@ use Intervention\Image\Facades\Image as ImageTool;
9 use Illuminate\Support\Facades\DB; 9 use Illuminate\Support\Facades\DB;
10 use Oxbow\Http\Requests; 10 use Oxbow\Http\Requests;
11 use Oxbow\Image; 11 use Oxbow\Image;
12 +use Oxbow\Repos\PageRepo;
12 13
13 class ImageController extends Controller 14 class ImageController extends Controller
14 { 15 {
...@@ -27,42 +28,6 @@ class ImageController extends Controller ...@@ -27,42 +28,6 @@ class ImageController extends Controller
27 parent::__construct(); 28 parent::__construct();
28 } 29 }
29 30
30 - /**
31 - * Returns an image from behind the public-facing application.
32 - * @param Request $request
33 - * @return \Illuminate\Http\Response
34 - */
35 - public function getImage(Request $request)
36 - {
37 - $cacheTime = 60 * 60 * 24;
38 - $path = storage_path() . '/' . $request->path();
39 - $modifiedTime = $this->file->lastModified($path);
40 - $eTag = md5($modifiedTime . $path);
41 - $headerLastModified = gmdate('r', $modifiedTime);
42 - $headerExpires = gmdate('r', $modifiedTime + $cacheTime);
43 -
44 - $headers = [
45 - 'Last-Modified' => $headerLastModified,
46 - 'Cache-Control' => 'must-revalidate',
47 - 'Pragma' => 'public',
48 - 'Expires' => $headerExpires,
49 - 'Etag' => $eTag
50 - ];
51 -
52 - $browserModifiedSince = $request->header('If-Modified-Since');
53 - $browserNoneMatch = $request->header('If-None-Match');
54 - if ($browserModifiedSince !== null && file_exists($path) && ($browserModifiedSince == $headerLastModified || $browserNoneMatch == $eTag)) {
55 - return response()->make('', 304, $headers);
56 - }
57 -
58 - if (file_exists($path)) {
59 - return response()->make(file_get_contents($path), 200, array_merge($headers, [
60 - 'Content-Type' => $this->file->mimeType($path),
61 - 'Content-Length' => filesize($path),
62 - ]));
63 - }
64 - abort(404);
65 - }
66 31
67 /** 32 /**
68 * Get all images, Paginated 33 * Get all images, Paginated
...@@ -167,14 +132,23 @@ class ImageController extends Controller ...@@ -167,14 +132,23 @@ class ImageController extends Controller
167 132
168 /** 133 /**
169 * Deletes an image and all thumbnail/image files 134 * Deletes an image and all thumbnail/image files
170 - * @param $id 135 + * @param PageRepo $pageRepo
136 + * @param Request $request
137 + * @param int $id
171 * @return \Illuminate\Http\JsonResponse 138 * @return \Illuminate\Http\JsonResponse
172 */ 139 */
173 - public function destroy($id) 140 + public function destroy(PageRepo $pageRepo, Request $request, $id)
174 { 141 {
175 $this->checkPermission('image-delete'); 142 $this->checkPermission('image-delete');
176 $image = $this->image->findOrFail($id); 143 $image = $this->image->findOrFail($id);
177 144
145 + // Check if this image is used on any pages
146 + $pageSearch = $pageRepo->searchForImage($image->url);
147 + $isForced = ($request->has('force') && ($request->get('force') === 'true') || $request->get('force') === true);
148 + if ($pageSearch !== false && !$isForced) {
149 + return response()->json($pageSearch, 400);
150 + }
151 +
178 // Delete files 152 // Delete files
179 $folder = public_path() . dirname($image->url); 153 $folder = public_path() . dirname($image->url);
180 $fileName = basename($image->url); 154 $fileName = basename($image->url);
......
...@@ -59,7 +59,6 @@ Route::group(['middleware' => 'auth'], function () { ...@@ -59,7 +59,6 @@ Route::group(['middleware' => 'auth'], function () {
59 Route::put('/images/update/{imageId}', 'ImageController@update'); 59 Route::put('/images/update/{imageId}', 'ImageController@update');
60 Route::delete('/images/{imageId}', 'ImageController@destroy'); 60 Route::delete('/images/{imageId}', 'ImageController@destroy');
61 Route::get('/images/all/{page}', 'ImageController@getAll'); 61 Route::get('/images/all/{page}', 'ImageController@getAll');
62 - Route::get('/images/{any}', 'ImageController@getImage')->where('any', '.*');
63 62
64 // Links 63 // Links
65 Route::get('/link/{id}', 'PageController@redirectFromLink'); 64 Route::get('/link/{id}', 'PageController@redirectFromLink');
......
...@@ -93,6 +93,22 @@ class PageRepo ...@@ -93,6 +93,22 @@ class PageRepo
93 } 93 }
94 94
95 /** 95 /**
96 + * Search for image usage.
97 + * @param $imageString
98 + * @return mixed
99 + */
100 + public function searchForImage($imageString)
101 + {
102 + $pages = $this->page->where('html', 'like', '%'.$imageString.'%')->get();
103 + foreach($pages as $page) {
104 + $page->url = $page->getUrl();
105 + $page->html = '';
106 + $page->text = '';
107 + }
108 + return count($pages) > 0 ? $pages : false;
109 + }
110 +
111 + /**
96 * Updates a page with any fillable data and saves it into the database. 112 * Updates a page with any fillable data and saves it into the database.
97 * @param Page $page 113 * @param Page $page
98 * @param $book_id 114 * @param $book_id
......
...@@ -40,4 +40,8 @@ jQuery.fn.showFailure = function (messageMap) { ...@@ -40,4 +40,8 @@ jQuery.fn.showFailure = function (messageMap) {
40 }); 40 });
41 }); 41 });
42 42
43 +};
44 +
45 +jQuery.fn.submitForm = function() {
46 + $(this).closest('form').submit();
43 }; 47 };
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -15,9 +15,6 @@ ...@@ -15,9 +15,6 @@
15 left: 0; 15 left: 0;
16 z-index: 999; 16 z-index: 999;
17 display: flex; 17 display: flex;
18 - p, h1, h2, h3, h4, label, input {
19 - color: #444;
20 - }
21 h1, h2, h3 { 18 h1, h2, h3 {
22 font-weight: 300; 19 font-weight: 300;
23 } 20 }
......
...@@ -12,6 +12,6 @@ ...@@ -12,6 +12,6 @@
12 </div> 12 </div>
13 13
14 <div class="form-group"> 14 <div class="form-group">
15 - <a onclick="window.history.back();" class="button muted">Cancel</a> 15 + <a href="{{ back()->getTargetUrl() }}" class="button muted">Cancel</a>
16 <button type="submit" class="button pos">Save</button> 16 <button type="submit" class="button pos">Save</button>
17 </div> 17 </div>
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
14 </div> 14 </div>
15 <div class="col-md-8 faded"> 15 <div class="col-md-8 faded">
16 <div class="action-buttons"> 16 <div class="action-buttons">
17 - <a onclick="window.history.back();" class="text-primary"><i class="zmdi zmdi-close"></i>Cancel</a> 17 + <a href="{{ back()->getTargetUrl() }}" class="text-primary"><i class="zmdi zmdi-close"></i>Cancel</a>
18 - <a onclick="$(this).closest('form').submit();" type="submit" class="text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</a> 18 + <a onclick="$(this).submitForm();" type="submit" class="text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</a>
19 </div> 19 </div>
20 </div> 20 </div>
21 </div> 21 </div>
......
1 1
2 <div id="image-manager"> 2 <div id="image-manager">
3 - <div class="overlay" v-el="overlay" v-on="click: overlayClick" style="display:none;"> 3 + <div class="overlay" v-el="overlay" v-on="click: overlayClick" >
4 <div class="image-manager-body"> 4 <div class="image-manager-body">
5 <div class="image-manager-content"> 5 <div class="image-manager-content">
6 <div class="image-manager-list"> 6 <div class="image-manager-list">
...@@ -31,8 +31,19 @@ ...@@ -31,8 +31,19 @@
31 </div> 31 </div>
32 </form> 32 </form>
33 <hr class="even"> 33 <hr class="even">
34 + <div v-show="dependantPages">
35 + <p class="text-neg text-small">
36 + This image is used in the pages below, Click delete again to confirm you want to delete this image.
37 + </p>
38 + <ul class="text-neg">
39 + <li v-repeat="page: dependantPages">
40 + <a v-attr="href: page.url" target="_blank" class="text-neg">@{{ page.name }}</a>
41 + </li>
42 + </ul>
43 + </div>
44 +
34 <form v-on="submit: deleteImage" v-el="imageDeleteForm"> 45 <form v-on="submit: deleteImage" v-el="imageDeleteForm">
35 - {{ csrf_field() }} 46 + <input type="hidden" v-model="deleteForm._token" value="{{ csrf_token() }}">
36 <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button> 47 <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
37 </form> 48 </form>
38 </div> 49 </div>
......