Dan Brown

Added image name editing & deleting

...@@ -10,6 +10,9 @@ use Intervention\Image\Facades\Image as ImageTool; ...@@ -10,6 +10,9 @@ use Intervention\Image\Facades\Image as ImageTool;
10 use Illuminate\Support\Facades\DB; 10 use Illuminate\Support\Facades\DB;
11 use Oxbow\Http\Requests; 11 use Oxbow\Http\Requests;
12 use Oxbow\Image; 12 use Oxbow\Image;
13 +use RecursiveDirectoryIterator;
14 +use RecursiveIteratorIterator;
15 +use RegexIterator;
13 16
14 class ImageController extends Controller 17 class ImageController extends Controller
15 { 18 {
...@@ -71,7 +74,7 @@ class ImageController extends Controller ...@@ -71,7 +74,7 @@ class ImageController extends Controller
71 */ 74 */
72 public function getAll($page = 0) 75 public function getAll($page = 0)
73 { 76 {
74 - $pageSize = 25; 77 + $pageSize = 30;
75 $images = DB::table('images')->orderBy('created_at', 'desc') 78 $images = DB::table('images')->orderBy('created_at', 'desc')
76 ->skip($page*$pageSize)->take($pageSize)->get(); 79 ->skip($page*$pageSize)->take($pageSize)->get();
77 foreach($images as $image) { 80 foreach($images as $image) {
...@@ -146,5 +149,44 @@ class ImageController extends Controller ...@@ -146,5 +149,44 @@ class ImageController extends Controller
146 return response()->json($this->image); 149 return response()->json($this->image);
147 } 150 }
148 151
152 + /**
153 + * Update image details
154 + * @param $imageId
155 + * @param Request $request
156 + * @return \Illuminate\Http\JsonResponse
157 + */
158 + public function update($imageId, Request $request)
159 + {
160 + $this->validate($request, [
161 + 'name' => 'required|min:2|string'
162 + ]);
163 + $image = $this->image->findOrFail($imageId);
164 + $image->fill($request->all());
165 + $image->save();
166 + return response()->json($this->image);
167 + }
168 +
169 + /**
170 + * Deletes an image and all thumbnail/image files
171 + * @param $id
172 + * @return \Illuminate\Http\JsonResponse
173 + */
174 + public function destroy($id)
175 + {
176 + $image = $this->image->findOrFail($id);
177 +
178 + // Delete files
179 + $folder = public_path() . dirname($image->url);
180 + $pattern = '/' . preg_quote(basename($image->url)). '/';
181 + $dir = new RecursiveDirectoryIterator($folder);
182 + $ite = new RecursiveIteratorIterator($dir);
183 + $files = new RegexIterator($ite, $pattern, RegexIterator::ALL_MATCHES);
184 + foreach($files as $path => $file) {
185 + unlink($path);
186 + }
187 + $image->delete();
188 + return response()->json('Image Deleted');
189 + }
190 +
149 191
150 } 192 }
......
...@@ -68,6 +68,8 @@ Route::group(['middleware' => 'auth'], function() { ...@@ -68,6 +68,8 @@ Route::group(['middleware' => 'auth'], function() {
68 68
69 // Image routes 69 // Image routes
70 Route::get('/images/all', 'ImageController@getAll'); 70 Route::get('/images/all', 'ImageController@getAll');
71 + Route::put('/images/update/{imageId}', 'ImageController@update');
72 + Route::delete('/images/{imageId}', 'ImageController@destroy');
71 Route::get('/images/all/{page}', 'ImageController@getAll'); 73 Route::get('/images/all/{page}', 'ImageController@getAll');
72 Route::get('/images/{any}', 'ImageController@getImage')->where('any', '.*'); 74 Route::get('/images/{any}', 'ImageController@getImage')->where('any', '.*');
73 75
......
...@@ -6,6 +6,9 @@ use Illuminate\Database\Eloquent\Model; ...@@ -6,6 +6,9 @@ use Illuminate\Database\Eloquent\Model;
6 6
7 class Image extends Model 7 class Image extends Model
8 { 8 {
9 +
10 + protected $fillable = ['name'];
11 +
9 public function getFilePath() 12 public function getFilePath()
10 { 13 {
11 return storage_path() . $this->url; 14 return storage_path() . $this->url;
......
1 1
2 +jQuery.fn.showSuccess = function(message) {
3 + var elem = $(this);
4 + var success = $('<div class="text-pos" style="display:none;"><i class="zmdi zmdi-check-circle"></i>'+message+'</div>');
5 + elem.after(success);
6 + success.slideDown(400, function() {
7 + setTimeout(function() {success.slideUp(400, function() {
8 + success.remove();
9 + })}, 2000);
10 + });
11 +};
12 +
13 +jQuery.fn.showFailure = function(messageMap) {
14 + var elem = $(this);
15 + $.each(messageMap, function(key, messages) {
16 + var input = elem.find('[name="'+key+'"]').last();
17 + var fail = $('<div class="text-neg" style="display:none;"><i class="zmdi zmdi-alert-circle"></i>'+messages.join("\n")+'</div>');
18 + input.after(fail);
19 + fail.slideDown(400, function() {
20 + setTimeout(function() {fail.slideUp(400, function() {
21 + fail.remove();
22 + })}, 2000);
23 + });
24 + });
25 +
26 +};
27 +
2 (function() { 28 (function() {
3 29
4 var ImageManager = new Vue({ 30 var ImageManager = new Vue({
...@@ -56,7 +82,7 @@ ...@@ -56,7 +82,7 @@
56 var dblClickTime = 380; 82 var dblClickTime = 380;
57 var cTime = (new Date()).getTime(); 83 var cTime = (new Date()).getTime();
58 var timeDiff = cTime - this.cClickTime; 84 var timeDiff = cTime - this.cClickTime;
59 - if(this.cClickTime !== 0 && timeDiff < dblClickTime) { 85 + if(this.cClickTime !== 0 && timeDiff < dblClickTime && this.selectedImage === image) {
60 // DoubleClick 86 // DoubleClick
61 if(this.callback) { 87 if(this.callback) {
62 this.callback(image); 88 this.callback(image);
...@@ -68,6 +94,13 @@ ...@@ -68,6 +94,13 @@
68 this.cClickTime = cTime; 94 this.cClickTime = cTime;
69 }, 95 },
70 96
97 + selectButtonClick: function() {
98 + if(this.callback) {
99 + this.callback(this.selectedImage);
100 + }
101 + this.hide();
102 + },
103 +
71 show: function(callback) { 104 show: function(callback) {
72 this.callback = callback; 105 this.callback = callback;
73 this.$$.overlay.style.display = 'block'; 106 this.$$.overlay.style.display = 'block';
...@@ -81,6 +114,34 @@ ...@@ -81,6 +114,34 @@
81 114
82 hide: function() { 115 hide: function() {
83 this.$$.overlay.style.display = 'none'; 116 this.$$.overlay.style.display = 'none';
117 + },
118 +
119 + saveImageDetails: function(e) {
120 + e.preventDefault();
121 + var _this = this;
122 + var form = $(_this.$$.imageForm);
123 + $.ajax('/images/update/' + _this.selectedImage.id, {
124 + method: 'PUT',
125 + data: form.serialize()
126 + }).done(function() {
127 + form.showSuccess('Image name updated');
128 + }).fail(function(jqXHR) {
129 + form.showFailure(jqXHR.responseJSON);
130 + })
131 + },
132 +
133 + deleteImage: function(e) {
134 + e.preventDefault();
135 + var _this = this;
136 + var form = $(_this.$$.imageDeleteForm);
137 + $.ajax('/images/' + _this.selectedImage.id, {
138 + method: 'DELETE',
139 + data: form.serialize()
140 + }).done(function() {
141 + _this.images.splice(_this.images.indexOf(_this.selectedImage), 1);
142 + _this.selectedImage = false;
143 + $(_this.$$.imageTitle).showSuccess('Image Deleted');
144 + })
84 } 145 }
85 146
86 } 147 }
......
1 +
2 +.anim.fadeIn {
3 + opacity: 0;
4 + animation-name: fadeIn;
5 + animation-duration: 160ms;
6 + animation-timing-function: ease-in-out;
7 + animation-fill-mode: forwards;
8 +}
9 +
10 +@keyframes fadeIn {
11 + 0% {
12 + opacity: 0;
13 + }
14 + 100% {
15 + opacity: 1;
16 + }
17 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -45,6 +45,12 @@ input[type="text"], input[type="number"], input[type="email"], input[type="searc ...@@ -45,6 +45,12 @@ input[type="text"], input[type="number"], input[type="email"], input[type="searc
45 margin-bottom: $-s; 45 margin-bottom: $-s;
46 } 46 }
47 47
48 +.form-group {
49 + .text-pos, .text-neg {
50 + padding: $-xs 0;
51 + }
52 +}
53 +
48 .inline-input-style { 54 .inline-input-style {
49 border: 2px dotted #BBB; 55 border: 2px dotted #BBB;
50 display: block; 56 display: block;
......
...@@ -82,7 +82,7 @@ hr { ...@@ -82,7 +82,7 @@ hr {
82 &.faded { 82 &.faded {
83 background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF); 83 background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF);
84 } 84 }
85 - &.margin-top { 85 + &.margin-top, &.even {
86 margin-top: $-l; 86 margin-top: $-l;
87 } 87 }
88 } 88 }
...@@ -227,4 +227,11 @@ ul { ...@@ -227,4 +227,11 @@ ul {
227 227
228 .list > * { 228 .list > * {
229 display: block; 229 display: block;
230 +}
231 +
232 +/**
233 + * Icons
234 + */
235 +i {
236 + padding-right: $-xs;
230 } 237 }
...\ No newline at end of file ...\ No newline at end of file
......
1 .image-manager-body { 1 .image-manager-body {
2 - background-color: rgb(37, 37, 37); 2 + background-color: #FFF;
3 - max-width: 90%;
4 max-height: 90%; 3 max-height: 90%;
5 width: 90%; 4 width: 90%;
6 height: 90%; 5 height: 90%;
...@@ -9,18 +8,7 @@ ...@@ -9,18 +8,7 @@
9 border-radius: 4px; 8 border-radius: 4px;
10 box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.3); 9 box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.3);
11 overflow: hidden; 10 overflow: hidden;
12 - .image-manager-list img { 11 + max-width: 1340px;
13 - border-radius: 0;
14 - float: left;
15 - margin: 0;
16 - cursor: pointer;
17 - width: 150px;
18 - height: 150px;
19 - border: 1px solid transparent;
20 - &.selected {
21 - border: 3px solid #EEE;
22 - }
23 - }
24 position: fixed; 12 position: fixed;
25 top: 0; 13 top: 0;
26 bottom: 0; 14 bottom: 0;
...@@ -28,34 +16,48 @@ ...@@ -28,34 +16,48 @@
28 z-index: 999; 16 z-index: 999;
29 display: flex; 17 display: flex;
30 p, h1, h2, h3, h4, label, input { 18 p, h1, h2, h3, h4, label, input {
31 - color: #EEE; 19 + color: #444;
32 } 20 }
33 h1, h2, h3 { 21 h1, h2, h3 {
34 font-weight: 300; 22 font-weight: 300;
35 } 23 }
36 } 24 }
37 #image-manager .dropzone-container { 25 #image-manager .dropzone-container {
38 - height: 100px;
39 position: relative; 26 position: relative;
27 + border: 3px dashed #DDD;
40 } 28 }
41 29
42 -#container { 30 +.image-manager-bottom {
43 - height: 90vh; 31 + position: absolute;
32 + bottom: 0;
33 + right: 0;
44 } 34 }
45 35
36 +.image-manager-list img {
37 + display: block;
38 + border-radius: 0;
39 + float: left;
40 + margin: 0;
41 + cursor: pointer;
42 + width: (100%/6);
43 + height: auto;
44 + border: 1px solid #FFF;
45 + transition: all cubic-bezier(.4,0,1,1) 160ms;
46 + &.selected {
47 + transform: scale3d(0.92, 0.92, 0.92);
48 + }
49 +}
46 50
47 #image-manager .load-more { 51 #image-manager .load-more {
48 - width: 150px;
49 - height: 150px;
50 display: block; 52 display: block;
51 - float: left;
52 text-align: center; 53 text-align: center;
53 - background-color: #404040; 54 + background-color: #EEE;
54 - margin: 1px; 55 + padding: $-s $-m;
55 - color: #FFF; 56 + color: #AAA;
56 - line-height: 140px; 57 + clear: both;
57 font-size: 20px; 58 font-size: 20px;
58 cursor: pointer; 59 cursor: pointer;
60 + font-style: italic;
59 } 61 }
60 62
61 .image-manager-sidebar { 63 .image-manager-sidebar {
...@@ -75,6 +77,7 @@ ...@@ -75,6 +77,7 @@
75 .image-manager-list { 77 .image-manager-list {
76 overflow-y: scroll; 78 overflow-y: scroll;
77 flex: 1; 79 flex: 1;
80 + border-top: 1px solid #ddd;
78 } 81 }
79 82
80 .image-manager-content { 83 .image-manager-content {
...@@ -93,19 +96,13 @@ ...@@ -93,19 +96,13 @@
93 * Copyright (c) 2012 Matias Meno <m@tias.me> 96 * Copyright (c) 2012 Matias Meno <m@tias.me>
94 */ 97 */
95 .dz-message { 98 .dz-message {
96 - font-size: 1.6em; 99 + font-size: 1.4em;
97 font-style: italic; 100 font-style: italic;
98 color: #aaa; 101 color: #aaa;
99 text-align: center; 102 text-align: center;
100 - line-height: 90px;
101 cursor: pointer; 103 cursor: pointer;
104 + padding: $-xl $-m;
102 transition: all ease-in-out 120ms; 105 transition: all ease-in-out 120ms;
103 - position: absolute;
104 - top: 0;
105 - left: 50%;
106 - max-width: 400px;
107 - width: 400px;
108 - margin-left: -200px;
109 } 106 }
110 .dz-drag-hover .dz-message { 107 .dz-drag-hover .dz-message {
111 background-color: rgb(16, 126, 210); 108 background-color: rgb(16, 126, 210);
...@@ -131,22 +128,10 @@ ...@@ -131,22 +128,10 @@
131 transform: translateY(0px); } } 128 transform: translateY(0px); } }
132 @keyframes pulse { 129 @keyframes pulse {
133 0% { 130 0% {
134 - -webkit-transform: scale(1);
135 - -moz-transform: scale(1);
136 - -ms-transform: scale(1);
137 - -o-transform: scale(1);
138 transform: scale(1); } 131 transform: scale(1); }
139 10% { 132 10% {
140 - -webkit-transform: scale(1.1);
141 - -moz-transform: scale(1.1);
142 - -ms-transform: scale(1.1);
143 - -o-transform: scale(1.1);
144 transform: scale(1.1); } 133 transform: scale(1.1); }
145 20% { 134 20% {
146 - -webkit-transform: scale(1);
147 - -moz-transform: scale(1);
148 - -ms-transform: scale(1);
149 - -o-transform: scale(1);
150 transform: scale(1); } } 135 transform: scale(1); } }
151 .dropzone, .dropzone * { 136 .dropzone, .dropzone * {
152 box-sizing: border-box; } 137 box-sizing: border-box; }
...@@ -171,10 +156,6 @@ ...@@ -171,10 +156,6 @@
171 .dz-preview.dz-image-preview { 156 .dz-preview.dz-image-preview {
172 background: white; } 157 background: white; }
173 .dz-preview.dz-image-preview .dz-details { 158 .dz-preview.dz-image-preview .dz-details {
174 - -webkit-transition: opacity 0.2s linear;
175 - -moz-transition: opacity 0.2s linear;
176 - -ms-transition: opacity 0.2s linear;
177 - -o-transition: opacity 0.2s linear;
178 transition: opacity 0.2s linear; } 159 transition: opacity 0.2s linear; }
179 .dz-preview .dz-remove { 160 .dz-preview .dz-remove {
180 font-size: 14px; 161 font-size: 14px;
...@@ -217,12 +198,6 @@ ...@@ -217,12 +198,6 @@
217 padding: 0 0.4em; 198 padding: 0 0.4em;
218 border-radius: 3px; } 199 border-radius: 3px; }
219 .dz-preview:hover .dz-image img { 200 .dz-preview:hover .dz-image img {
220 - -webkit-transform: scale(1.05, 1.05);
221 - -moz-transform: scale(1.05, 1.05);
222 - -ms-transform: scale(1.05, 1.05);
223 - -o-transform: scale(1.05, 1.05);
224 - transform: scale(1.05, 1.05);
225 - -webkit-filter: blur(8px);
226 filter: blur(8px); } 201 filter: blur(8px); }
227 .dz-preview .dz-image { 202 .dz-preview .dz-image {
228 border-radius: 4px; 203 border-radius: 4px;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
8 @import "buttons"; 8 @import "buttons";
9 @import "forms"; 9 @import "forms";
10 @import "tables"; 10 @import "tables";
11 +@import "animations";
11 @import "tinymce"; 12 @import "tinymce";
12 @import "image-manager"; 13 @import "image-manager";
13 14
......
...@@ -3,19 +3,42 @@ ...@@ -3,19 +3,42 @@
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" style="display:none;">
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="dropzone-container" v-el="dropZone">
7 - <div class="dz-message">Drop files or click here to upload</div>
8 - </div>
9 <div class="image-manager-list"> 6 <div class="image-manager-list">
10 <div v-repeat="image: images"> 7 <div v-repeat="image: images">
11 - <img v-class="selected: (image==selectedImage)" v-attr="src: image.thumbnail" v-on="click: imageClick(image)" alt="@{{image.name}}"> 8 + <img class="anim fadeIn"
9 + v-class="selected: (image==selectedImage)"
10 + v-attr="src: image.thumbnail, alt: image.name, title: image.name"
11 + v-on="click: imageClick(image)"
12 + v-style="animation-delay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'">
12 </div> 13 </div>
13 <div class="load-more" v-show="hasMore" v-on="click: fetchData">Load More</div> 14 <div class="load-more" v-show="hasMore" v-on="click: fetchData">Load More</div>
14 </div> 15 </div>
15 </div> 16 </div>
17 + <button class="neg button image-manager-close" v-on="click: hide()">x</button>
16 <div class="image-manager-sidebar"> 18 <div class="image-manager-sidebar">
17 - <button class="neg button image-manager-close" v-on="click: hide()">x</button> 19 + <h2 v-el="imageTitle">Images</h2>
18 - <h2>Images</h2> 20 + <hr class="even">
21 + <div class="dropzone-container" v-el="dropZone">
22 + <div class="dz-message">Drop files or click here to upload</div>
23 + </div>
24 + <div class="image-manager-details anim fadeIn" v-show="selectedImage">
25 + <hr class="even">
26 + <form v-on="submit: saveImageDetails" v-el="imageForm">
27 + {{ csrf_field() }}
28 + <div class="form-group">
29 + <label for="name">Image Name</label>
30 + <input type="text" id="name" name="name" v-model="selectedImage.name">
31 + </div>
32 + </form>
33 + <hr class="even">
34 + <form v-on="submit: deleteImage" v-el="imageDeleteForm">
35 + {{ csrf_field() }}
36 + <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
37 + </form>
38 + </div>
39 + <div class="image-manager-bottom">
40 + <button class="button pos anim fadeIn" v-show="selectedImage" v-on="click:selectButtonClick"><i class="zmdi zmdi-square-right"></i>Select Image</button>
41 + </div>
19 </div> 42 </div>
20 </div> 43 </div>
21 </div> 44 </div>
......