Added image name editing & deleting

This commit is contained in:
Dan Brown 2015-08-16 00:18:22 +01:00
parent b0808a1c24
commit e5a372ffbd
10 changed files with 201 additions and 64 deletions

View file

@ -10,6 +10,9 @@ use Intervention\Image\Facades\Image as ImageTool;
use Illuminate\Support\Facades\DB;
use Oxbow\Http\Requests;
use Oxbow\Image;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RegexIterator;
class ImageController extends Controller
{
@ -71,7 +74,7 @@ class ImageController extends Controller
*/
public function getAll($page = 0)
{
$pageSize = 25;
$pageSize = 30;
$images = DB::table('images')->orderBy('created_at', 'desc')
->skip($page*$pageSize)->take($pageSize)->get();
foreach($images as $image) {
@ -146,5 +149,44 @@ class ImageController extends Controller
return response()->json($this->image);
}
/**
* Update image details
* @param $imageId
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function update($imageId, Request $request)
{
$this->validate($request, [
'name' => 'required|min:2|string'
]);
$image = $this->image->findOrFail($imageId);
$image->fill($request->all());
$image->save();
return response()->json($this->image);
}
/**
* Deletes an image and all thumbnail/image files
* @param $id
* @return \Illuminate\Http\JsonResponse
*/
public function destroy($id)
{
$image = $this->image->findOrFail($id);
// Delete files
$folder = public_path() . dirname($image->url);
$pattern = '/' . preg_quote(basename($image->url)). '/';
$dir = new RecursiveDirectoryIterator($folder);
$ite = new RecursiveIteratorIterator($dir);
$files = new RegexIterator($ite, $pattern, RegexIterator::ALL_MATCHES);
foreach($files as $path => $file) {
unlink($path);
}
$image->delete();
return response()->json('Image Deleted');
}
}

View file

@ -68,6 +68,8 @@ Route::group(['middleware' => 'auth'], function() {
// Image routes
Route::get('/images/all', 'ImageController@getAll');
Route::put('/images/update/{imageId}', 'ImageController@update');
Route::delete('/images/{imageId}', 'ImageController@destroy');
Route::get('/images/all/{page}', 'ImageController@getAll');
Route::get('/images/{any}', 'ImageController@getImage')->where('any', '.*');

View file

@ -6,6 +6,9 @@ use Illuminate\Database\Eloquent\Model;
class Image extends Model
{
protected $fillable = ['name'];
public function getFilePath()
{
return storage_path() . $this->url;

View file

@ -1,4 +1,30 @@
jQuery.fn.showSuccess = function(message) {
var elem = $(this);
var success = $('<div class="text-pos" style="display:none;"><i class="zmdi zmdi-check-circle"></i>'+message+'</div>');
elem.after(success);
success.slideDown(400, function() {
setTimeout(function() {success.slideUp(400, function() {
success.remove();
})}, 2000);
});
};
jQuery.fn.showFailure = function(messageMap) {
var elem = $(this);
$.each(messageMap, function(key, messages) {
var input = elem.find('[name="'+key+'"]').last();
var fail = $('<div class="text-neg" style="display:none;"><i class="zmdi zmdi-alert-circle"></i>'+messages.join("\n")+'</div>');
input.after(fail);
fail.slideDown(400, function() {
setTimeout(function() {fail.slideUp(400, function() {
fail.remove();
})}, 2000);
});
});
};
(function() {
var ImageManager = new Vue({
@ -56,7 +82,7 @@
var dblClickTime = 380;
var cTime = (new Date()).getTime();
var timeDiff = cTime - this.cClickTime;
if(this.cClickTime !== 0 && timeDiff < dblClickTime) {
if(this.cClickTime !== 0 && timeDiff < dblClickTime && this.selectedImage === image) {
// DoubleClick
if(this.callback) {
this.callback(image);
@ -68,6 +94,13 @@
this.cClickTime = cTime;
},
selectButtonClick: function() {
if(this.callback) {
this.callback(this.selectedImage);
}
this.hide();
},
show: function(callback) {
this.callback = callback;
this.$$.overlay.style.display = 'block';
@ -81,6 +114,34 @@
hide: function() {
this.$$.overlay.style.display = 'none';
},
saveImageDetails: function(e) {
e.preventDefault();
var _this = this;
var form = $(_this.$$.imageForm);
$.ajax('/images/update/' + _this.selectedImage.id, {
method: 'PUT',
data: form.serialize()
}).done(function() {
form.showSuccess('Image name updated');
}).fail(function(jqXHR) {
form.showFailure(jqXHR.responseJSON);
})
},
deleteImage: function(e) {
e.preventDefault();
var _this = this;
var form = $(_this.$$.imageDeleteForm);
$.ajax('/images/' + _this.selectedImage.id, {
method: 'DELETE',
data: form.serialize()
}).done(function() {
_this.images.splice(_this.images.indexOf(_this.selectedImage), 1);
_this.selectedImage = false;
$(_this.$$.imageTitle).showSuccess('Image Deleted');
})
}
}

View file

@ -0,0 +1,17 @@
.anim.fadeIn {
opacity: 0;
animation-name: fadeIn;
animation-duration: 160ms;
animation-timing-function: ease-in-out;
animation-fill-mode: forwards;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View file

@ -45,6 +45,12 @@ input[type="text"], input[type="number"], input[type="email"], input[type="searc
margin-bottom: $-s;
}
.form-group {
.text-pos, .text-neg {
padding: $-xs 0;
}
}
.inline-input-style {
border: 2px dotted #BBB;
display: block;

View file

@ -82,7 +82,7 @@ hr {
&.faded {
background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF);
}
&.margin-top {
&.margin-top, &.even {
margin-top: $-l;
}
}
@ -227,4 +227,11 @@ ul {
.list > * {
display: block;
}
/**
* Icons
*/
i {
padding-right: $-xs;
}

View file

@ -1,6 +1,5 @@
.image-manager-body {
background-color: rgb(37, 37, 37);
max-width: 90%;
background-color: #FFF;
max-height: 90%;
width: 90%;
height: 90%;
@ -9,18 +8,7 @@
border-radius: 4px;
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.3);
overflow: hidden;
.image-manager-list img {
border-radius: 0;
float: left;
margin: 0;
cursor: pointer;
width: 150px;
height: 150px;
border: 1px solid transparent;
&.selected {
border: 3px solid #EEE;
}
}
max-width: 1340px;
position: fixed;
top: 0;
bottom: 0;
@ -28,34 +16,48 @@
z-index: 999;
display: flex;
p, h1, h2, h3, h4, label, input {
color: #EEE;
color: #444;
}
h1, h2, h3 {
font-weight: 300;
}
}
#image-manager .dropzone-container {
height: 100px;
position: relative;
border: 3px dashed #DDD;
}
#container {
height: 90vh;
.image-manager-bottom {
position: absolute;
bottom: 0;
right: 0;
}
.image-manager-list img {
display: block;
border-radius: 0;
float: left;
margin: 0;
cursor: pointer;
width: (100%/6);
height: auto;
border: 1px solid #FFF;
transition: all cubic-bezier(.4,0,1,1) 160ms;
&.selected {
transform: scale3d(0.92, 0.92, 0.92);
}
}
#image-manager .load-more {
width: 150px;
height: 150px;
display: block;
float: left;
text-align: center;
background-color: #404040;
margin: 1px;
color: #FFF;
line-height: 140px;
background-color: #EEE;
padding: $-s $-m;
color: #AAA;
clear: both;
font-size: 20px;
cursor: pointer;
font-style: italic;
}
.image-manager-sidebar {
@ -75,6 +77,7 @@
.image-manager-list {
overflow-y: scroll;
flex: 1;
border-top: 1px solid #ddd;
}
.image-manager-content {
@ -93,19 +96,13 @@
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
.dz-message {
font-size: 1.6em;
font-size: 1.4em;
font-style: italic;
color: #aaa;
text-align: center;
line-height: 90px;
cursor: pointer;
padding: $-xl $-m;
transition: all ease-in-out 120ms;
position: absolute;
top: 0;
left: 50%;
max-width: 400px;
width: 400px;
margin-left: -200px;
}
.dz-drag-hover .dz-message {
background-color: rgb(16, 126, 210);
@ -131,22 +128,10 @@
transform: translateY(0px); } }
@keyframes pulse {
0% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1); }
10% {
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
-ms-transform: scale(1.1);
-o-transform: scale(1.1);
transform: scale(1.1); }
20% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1); } }
.dropzone, .dropzone * {
box-sizing: border-box; }
@ -171,10 +156,6 @@
.dz-preview.dz-image-preview {
background: white; }
.dz-preview.dz-image-preview .dz-details {
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-ms-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear; }
.dz-preview .dz-remove {
font-size: 14px;
@ -217,12 +198,6 @@
padding: 0 0.4em;
border-radius: 3px; }
.dz-preview:hover .dz-image img {
-webkit-transform: scale(1.05, 1.05);
-moz-transform: scale(1.05, 1.05);
-ms-transform: scale(1.05, 1.05);
-o-transform: scale(1.05, 1.05);
transform: scale(1.05, 1.05);
-webkit-filter: blur(8px);
filter: blur(8px); }
.dz-preview .dz-image {
border-radius: 4px;

View file

@ -8,6 +8,7 @@
@import "buttons";
@import "forms";
@import "tables";
@import "animations";
@import "tinymce";
@import "image-manager";

View file

@ -3,19 +3,42 @@
<div class="overlay" v-el="overlay" v-on="click: overlayClick" style="display:none;">
<div class="image-manager-body">
<div class="image-manager-content">
<div class="dropzone-container" v-el="dropZone">
<div class="dz-message">Drop files or click here to upload</div>
</div>
<div class="image-manager-list">
<div v-repeat="image: images">
<img v-class="selected: (image==selectedImage)" v-attr="src: image.thumbnail" v-on="click: imageClick(image)" alt="@{{image.name}}">
<img class="anim fadeIn"
v-class="selected: (image==selectedImage)"
v-attr="src: image.thumbnail, alt: image.name, title: image.name"
v-on="click: imageClick(image)"
v-style="animation-delay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'">
</div>
<div class="load-more" v-show="hasMore" v-on="click: fetchData">Load More</div>
</div>
</div>
<button class="neg button image-manager-close" v-on="click: hide()">x</button>
<div class="image-manager-sidebar">
<button class="neg button image-manager-close" v-on="click: hide()">x</button>
<h2>Images</h2>
<h2 v-el="imageTitle">Images</h2>
<hr class="even">
<div class="dropzone-container" v-el="dropZone">
<div class="dz-message">Drop files or click here to upload</div>
</div>
<div class="image-manager-details anim fadeIn" v-show="selectedImage">
<hr class="even">
<form v-on="submit: saveImageDetails" v-el="imageForm">
{{ csrf_field() }}
<div class="form-group">
<label for="name">Image Name</label>
<input type="text" id="name" name="name" v-model="selectedImage.name">
</div>
</form>
<hr class="even">
<form v-on="submit: deleteImage" v-el="imageDeleteForm">
{{ csrf_field() }}
<button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
</form>
</div>
<div class="image-manager-bottom">
<button class="button pos anim fadeIn" v-show="selectedImage" v-on="click:selectButtonClick"><i class="zmdi zmdi-square-right"></i>Select Image</button>
</div>
</div>
</div>
</div>