Permalink
Browse files

Gallery Photos has an Image Cropper

  • Loading branch information...
bpocallaghan committed Jan 15, 2018
1 parent b0698a2 commit 746286c9a5811441953f629ae1767673c801802b
@@ -1,6 +1,6 @@
##Admin Starter - TODO
- Add cropper for Gallery photos
- ~~Add cropper for Gallery photos~~
- Update bootstrap 4 to latest beta version
- Move components to seperate packages for more mudular structure
- ~~[Locations](https://github.com/bpocallaghan/locations)~~
@@ -0,0 +1,109 @@
<?php
namespace App\Http\Controllers\Admin\Photos;
use Image;
use App\Http\Requests;
use App\Models\Article;
use App\Models\News;
use App\Models\Photo;
use App\Models\Product;
use App\Models\PhotoAlbum;
use App\Http\Controllers\Admin\AdminController;
class CropperController extends AdminController
{
private $LARGE_SIZE = [800, 800];
private $THUMB_SIZE = [400, 400];
/**
* @param $photoable
* @param Photo $photo
* @return this
*/
private function showCropper($photoable, Photo $photo)
{
return $this->view('photos.cropper')->with('photoable', $photoable)->with('photo', $photo);
}
/**
* @param News $news
* @param Photo $photo
* @return this
*/
public function showNewsPhoto(News $news, Photo $photo)
{
return $this->showCropper($news, $photo);
}
/**
* @param PhotoAlbum $album
* @param Photo $photo
* @return this
*/
public function showAlbumsPhoto(PhotoAlbum $album, Photo $photo)
{
return $this->showCropper($album, $photo);
}
/**
* @param Article $article
* @param Photo $photo
* @return this
*/
public function showArticlesPhoto(Article $article, Photo $photo)
{
return $this->showCropper($article, $photo);
}
/**
* Crop a photo
* @param Photo $photo
* @return \Illuminate\Http\JsonResponse
*/
public function cropPhoto(Photo $photo)
{
$photoable = input('photoable_type')::find(input('photoable_id'));
// if relationship not found
if (!$photoable) {
return json_response_error('Whoops', 'We could not find the photoable.');
}
// get the large and thumb sizes
if (isset($photoable::$LARGE_SIZE)) {
$largeSize = $photoable::$LARGE_SIZE;
$thumbSize = $photoable::$THUMB_SIZE;
}
else {
$largeSize = $this->LARGE_SIZE;
$thumbSize = $this->THUMB_SIZE;
}
// open file image resource
$path = upload_path('photos');
$originalImage = Image::make($photo->original_url);
// get the crop data
$x = intval(input('x'));
$y = intval(input('y'));
$width = intval(input('width'));
$height = intval(input('height'));
// crop image on cropped area
$imageTmp = $originalImage->crop($width, $height, $x, $y);
// resize the image to large size
$imageTmp->resize($largeSize[0], null, function ($constraint) {
$constraint->aspectRatio();
})->save($path . $photo->filename);
// resize the image to thumb size
$imageTmp->resize($thumbSize[0], null, function ($constraint) {
$constraint->aspectRatio();
})->save($path . $photo->thumb);
return json_response('success');
}
}
@@ -22,7 +22,7 @@ class Photo extends TitanCMSModel
protected $guarded = ['id'];
protected $appends = ['thumb', 'original', 'url'];
protected $appends = ['thumb', 'original_filename', 'url'];
static public $rules = [
'file' => 'required|image|max:5000|mimes:jpg,jpeg,png,bmp'
@@ -54,11 +54,20 @@ public function getThumbAttribute()
* Get the thumb path (append -tn at the end)
* @return mixed
*/
public function getOriginalAttribute()
public function getOriginalFilenameAttribute()
{
return $this->appendBeforeExtension(self::$originalAppend);
}
/**
* Get the extension
* @return bool|string
*/
public function getExtensionAttribute()
{
return substr($this->filename, strpos($this->filename, '.'));
}
/**
* Get the url to the photo
* @return string
@@ -68,11 +77,24 @@ public function getUrlAttribute()
return $this->urlForName($this->filename);
}
/**
* Get the thumb url
* @return string
*/
public function getThumbUrlAttribute()
{
return $this->urlForName($this->thumb);
}
/**
* Get the original url
* @return string
*/
public function getOriginalUrlAttribute()
{
return $this->urlForName($this->original_filename);
}
/**
* Get the url for the file name (specify thumb, default, original)
* @param $name
@@ -0,0 +1,160 @@
@extends('layouts.admin')
@section('styles')
<style>
.cropper-container {
text-align: center;
}
.cropper-box {
margin: auto;
min-height: 500px;
}
.preview {
width: 100%;
margin: auto;
overflow: hidden;
min-height: 200px;
/*border: 1px solid #444444;*/
}
</style>
@endsection
@section('content')
<div class="row cropper-container">
<div class="col-xs-12">
<div class="box box-primary box-solid">
<div class="box-header with-border">
<h3 class="box-title">
<span><i class="fa fa-crop"></i></span>
<span>Crop {{ $photo->name }}</span>
</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-3">
<h4>Current Thumb</h4>
<img src="{{ $photo->thumb_url }}" title="{{ $photo->name }}" style="max-width: 100%;">
</div>
<div class="col-md-6">
<div class="cropper-box">
<h4>Crop</h4>
<img id="image-cropper" src="{{ $photo->original_url }}" title="{{ $photo->name }}" style="max-width: 100%;">
</div>
</div>
<div class="col-md-3">
<h4>Preview</h4>
<div class="preview"></div>
</div>
</div>
<div class="form-footer">
<button id="btn-crop-photo" class="btn btn-labeled btn-primary btn-ajax-submit">
<span class="btn-label"><i class="fa fa-fw fa-save"></i></span>
Update Photo
</button>
<a href="javascript:window.history.back();" class="btn btn-labeled btn-default">
<span class="btn-label"><i class="fa fa-fw fa-chevron-left"></i></span>Back
</a>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
@parent
<script type="text/javascript" charset="utf-8">
$(function () {
var cropperData = false;
var $image = $('#image-cropper');
var minCropWidth = 0;
var minCropHeight = 0;
var minWidth = {{ isset($photoable::$THUMB_SIZE)? $photoable::$THUMB_SIZE[0]:400 }};
var minHeight = {{ isset($photoable::$THUMB_SIZE)? $photoable::$THUMB_SIZE[1]:200 }};
$image.cropper({
// viewMode: 1, // restrict the crop box to not exceed the size of the canvas.
preview: '.preview',
aspectRatio: minWidth / minHeight,
ready: function () {
cropperData = $(this).cropper('getData', true);
// add restrictions when natural image is bigger than minimum allowed
if (cropperData.width > minWidth && cropperData.height > minHeight) {
// get the original cropbox data
var originalBoxData = $image.cropper('getCropBoxData');
// set the crop area to minimum image size
$image.cropper('setData', {
width: minWidth,
height: minHeight
});
// set the minimum cropbox width and height
var cropBoxData = $image.cropper('getCropBoxData');
minCropWidth = cropBoxData.width;
minCropHeight = cropBoxData.height;
// reset the cropbox area to initialize
$image.cropper('setCropBoxData', {
width: originalBoxData.width,
height: originalBoxData.height,
});
} else {
notifyError("Image Size", "The image uploaded is smaller than the minimum required size.");
}
},
crop: function (e) {
// console.log(e.x);
// console.log(e.y);
// console.log(e.width);
// console.log(e.height);
// get crop data - round to integer
cropperData = $(this).cropper('getData', true);
},
cropmove: function (e) {
if (minCropWidth > 0) {
var imageData = $image.cropper('getData', true);
// if image width is less than minimum
if (imageData.width < minWidth) {
$image.cropper('setCropBoxData', {
width: minCropWidth
});
}
// if image height is less than minimum
if (imageData.height < minHeight) {
$image.cropper('setCropBoxData', {
height: minCropHeight
});
}
}
}
});
$('#btn-crop-photo').click(function (e) {
e.preventDefault();
cropperData['photoable_id'] = "{{ $photoable->id }}";
cropperData['photoable_type'] = "{{ str_replace('\\','\\\\',get_class($photoable)) }}";
cropperData['photoable_type_name'] = "{{ (new \ReflectionClass($photoable))->getShortName() }}";
doAjax("/admin/photos/crop/{{ $photo->id }}", cropperData, function (response) {
BUTTON.reset('.btn-ajax-submit');
if (response.error) {
notifyError(response.error.title, response.error.content);
} else {
notify('Cropped', 'The photo was successfully cropped.', 'success', 'fa fa-smile-o bounce animated', 5000);
}
});
})
})
</script>
@endsection
@@ -25,7 +25,7 @@
<span id="image-row-title-span-{{ $photo->id }}" class="image-row-title-span">{{ $photo->name }}</span>
</a>
</td>
<td>
<td style="min-width: 55px;">
<form id="form-delete-row{{ $photo->id }}" method="POST" action="/admin/photos/{{ $photo->id }}" class="dt-titan">
<input name="_method" type="hidden" value="DELETE">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
@@ -36,6 +36,10 @@
<i class="fa fa-times"></i>
</a>
</form>
<a href="{{ request()->url() }}/crop/{{ $photo->id }}" class="btn btn-info btn-xs" data-toggle="tooltip" title="Crop {{ $photo->name }}"
style="float:right; padding: 0px 6px; margin-right: 3px;">
<i class="fa fa-crop"></i>
</a>
</td>
</tr>
</tbody>
@@ -178,6 +178,12 @@ function () {
Route::resource('/albums', 'AlbumsController', ['except' => 'show']);
Route::get('/albums/{album}', 'PhotosController@showAlbumPhotos');
// croppers
Route::post('/crop/{photo}', 'CropperController@cropPhoto');
Route::get('/news/{news}/crop/{photo}', 'CropperController@showNewsPhoto');
Route::get('/albums/{album}/crop/{photo}', 'CropperController@showAlbumsPhoto');
Route::get('/articles/{article}/crop/{photo}', 'CropperController@showArticlesPhoto');
});
// corporate

0 comments on commit 746286c

Please sign in to comment.