refs #6: added the ability to flip photos horizontally, vertically and both

This commit is contained in:
Andy Heathershaw 2016-10-03 15:57:58 +01:00
parent 7783af00b0
commit fe0b4c2108
9 changed files with 128 additions and 5 deletions

View File

@ -7,6 +7,26 @@ use App\Photo;
class ImageHelper class ImageHelper
{ {
public function flipImage($imageResource, $flipHorizontal, $flipVertical)
{
imagesetinterpolation($imageResource, $this->getInterpolationMethod());
if ($flipHorizontal && $flipVertical)
{
return imageflip($imageResource, IMG_FLIP_BOTH);
}
else if ($flipHorizontal)
{
return imageflip($imageResource, IMG_FLIP_HORIZONTAL);
}
else if ($flipVertical)
{
return imageflip($imageResource, IMG_FLIP_VERTICAL);
}
return false;
}
public function generateThumbnail($gdImageResource, Photo $photo, $thumbnailInfo) public function generateThumbnail($gdImageResource, Photo $photo, $thumbnailInfo)
{ {
$thumbnailWidth = intval($thumbnailInfo['width']); $thumbnailWidth = intval($thumbnailInfo['width']);

View File

@ -161,6 +161,10 @@ class AlbumController extends Controller
'rotate_left' => trans('admin.photo_actions.rotate_left'), 'rotate_left' => trans('admin.photo_actions.rotate_left'),
'rotate_right' => trans('admin.photo_actions.rotate_right'), 'rotate_right' => trans('admin.photo_actions.rotate_right'),
'-' => '-----', '-' => '-----',
'flip_horizontal' => trans('admin.photo_actions.flip_horizontal'),
'flip_vertical' => trans('admin.photo_actions.flip_vertical'),
'flip_both' => trans('admin.photo_actions.flip_both'),
'--' => '-----',
'refresh_thumbnails' => trans('admin.photo_actions.refresh_thumbnails'), 'refresh_thumbnails' => trans('admin.photo_actions.refresh_thumbnails'),
'delete' => trans('admin.photo_actions.delete') 'delete' => trans('admin.photo_actions.delete')
], ],

View File

@ -105,6 +105,23 @@ class PhotoController extends Controller
return back(); return back();
} }
public function flip($photoId, $horizontal, $vertical)
{
$this->authorize('admin-access');
settype($direction, 'boolean');
$photo = Photo::where('id', intval($photoId))->first();
if (is_null($photo))
{
App::abort(404);
return null;
}
$photoService = new PhotoService($photo);
$photoService->flip($horizontal, $vertical);
}
public function regenerateThumbnails($photoId) public function regenerateThumbnails($photoId)
{ {
$this->authorize('admin-access'); $this->authorize('admin-access');

View File

@ -18,6 +18,7 @@ class CheckMaxPostSizeExceeded
protected $exclude = [ protected $exclude = [
'/admin/photos/analyse/*', '/admin/photos/analyse/*',
'/admin/photos/flip/*',
'/admin/photos/regenerate-thumbnails/*', '/admin/photos/regenerate-thumbnails/*',
'/admin/photos/rotate/*' '/admin/photos/rotate/*'
]; ];

View File

@ -137,6 +137,21 @@ class PhotoService
$this->photo->delete(); $this->photo->delete();
} }
public function flip($horizontal, $vertical)
{
$imageInfo = array();
$photoPath = $this->albumSource->getPathToPhoto($this->photo);
$originalPhotoImage = $this->imageHelper->openImage($photoPath, $imageInfo);
if ($this->imageHelper->flipImage($originalPhotoImage, boolval($horizontal), boolval($vertical)))
{
$this->imageHelper->saveImage($originalPhotoImage, $photoPath, $imageInfo);
$this->regenerateThumbnails($originalPhotoImage);
$this->photo->save();
}
}
public function regenerateThumbnails($originalPhotoResource = null) public function regenerateThumbnails($originalPhotoResource = null)
{ {
if (is_null($originalPhotoResource)) if (is_null($originalPhotoResource))

View File

@ -50,7 +50,10 @@ return [
'no_storages_title' => 'No storage locations defined', 'no_storages_title' => 'No storage locations defined',
'open_album' => 'Open album', 'open_album' => 'Open album',
'photo_actions' => [ 'photo_actions' => [
'delete' => 'Delete', 'delete' => 'Delete permanently',
'flip_both' => 'Flip both',
'flip_horizontal' => 'Flip horizontally',
'flip_vertical' => 'Flip vertically',
'refresh_thumbnails' => 'Refresh thumbnails', 'refresh_thumbnails' => 'Refresh thumbnails',
'rotate_left' => 'Rotate left', 'rotate_left' => 'Rotate left',
'rotate_right' => 'Rotate right' 'rotate_right' => 'Rotate right'

View File

@ -191,6 +191,40 @@
}); });
} }
function flipPhoto(photo_id, horizontal, vertical, parent)
{
var url = '{{ route('photos.flip', ['id' => 0, 'horizontal' => -1, 'vertical' => -2]) }}';
url = url.replace('/0/', '/' + photo_id + '/');
if (horizontal)
{
url = url.replace(/\/-1\//, '/1/');
}
else
{
url = url.replace(/\/-1\//, '/0/');
}
if (vertical)
{
url = url.replace(/\/-2$/, '/1');
}
else
{
url = url.replace(/\/-2$/, '/0');
}
$('.loading', parent).show();
$.post(url, function()
{
var image = $('img.photo-thumbnail', parent);
var originalUrl = image.data('original-src');
image.attr('src', originalUrl + "&_=" + new Date().getTime());
$('.loading', parent).hide();
});
}
function regenerateThumbnails(photo_id, parent) function regenerateThumbnails(photo_id, parent)
{ {
var url = '{{ route('photos.regenerateThumbnails', ['id' => 0]) }}'; var url = '{{ route('photos.regenerateThumbnails', ['id' => 0]) }}';
@ -256,6 +290,30 @@
return false; return false;
}); });
$('a.flip-photo-both').click(function() {
var parent = $(this).parents('.photo');
var photo_id = $(parent).data('photo-id');
flipPhoto(photo_id, true, true, parent);
$(this).dropdown('toggle');
return false;
});
$('a.flip-photo-horizontal').click(function() {
var parent = $(this).parents('.photo');
var photo_id = $(parent).data('photo-id');
flipPhoto(photo_id, true, false, parent);
$(this).dropdown('toggle');
return false;
});
$('a.flip-photo-vertical').click(function() {
var parent = $(this).parents('.photo');
var photo_id = $(parent).data('photo-id');
flipPhoto(photo_id, false, true, parent);
$(this).dropdown('toggle');
return false;
});
$('a.regenerate-thumbnails').click(function() { $('a.regenerate-thumbnails').click(function() {
var parent = $(this).parents('.photo'); var parent = $(this).parents('.photo');
var photo_id = $(parent).data('photo-id'); var photo_id = $(parent).data('photo-id');

View File

@ -15,8 +15,12 @@
<i class="fa fa-fw fa-pencil"></i> <span class="caret"></span> <i class="fa fa-fw fa-pencil"></i> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="#" class="rotate-photo-left"><i class="fa fa-fw fa-rotate-left"></i> Rotate left</a></li> <li><a href="#" class="rotate-photo-left"><i class="fa fa-fw fa-rotate-left"></i> @lang('admin.photo_actions.rotate_left')</a></li>
<li><a href="#" class="rotate-photo-right"><i class="fa fa-fw fa-rotate-right"></i> Rotate right</a></li> <li><a href="#" class="rotate-photo-right"><i class="fa fa-fw fa-rotate-right"></i> @lang('admin.photo_actions.rotate_right')</a></li>
<li class="divider"></li>
<li><a href="#" class="flip-photo-horizontal"><i class="fa fa-fw fa-arrows-h"></i> @lang('admin.photo_actions.flip_horizontal')</a></li>
<li><a href="#" class="flip-photo-vertical"><i class="fa fa-fw fa-arrows-v"></i> @lang('admin.photo_actions.flip_vertical')</a></li>
<li><a href="#" class="flip-photo-both"><i class="fa fa-fw fa-retweet"></i> @lang('admin.photo_actions.flip_both')</a></li>
</ul> </ul>
</div> </div>
@ -25,8 +29,8 @@
<i class="fa fa-fw fa-cog"></i> <span class="caret"></span> <i class="fa fa-fw fa-cog"></i> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="#" class="regenerate-thumbnails"><i class="fa fa-fw fa-picture-o"></i> Refresh thumbnails</a></li> <li><a href="#" class="regenerate-thumbnails"><i class="fa fa-fw fa-picture-o"></i> @lang('admin.photo_actions.refresh_thumbnails')</a></li>
<li><a href="#" class="delete-photo"><i class="fa fa-fw fa-trash text-danger"></i> <span class="text-danger">Delete photo</span></a></li> <li><a href="#" class="delete-photo"><i class="fa fa-fw fa-trash text-danger"></i> <span class="text-danger">@lang('admin.photo_actions.delete')</span></a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -27,6 +27,7 @@ Route::group(['prefix' => 'admin'], function () {
// Photo management // Photo management
Route::post('photos/analyse/{id}', 'Admin\PhotoController@analyse')->name('photos.analyse'); Route::post('photos/analyse/{id}', 'Admin\PhotoController@analyse')->name('photos.analyse');
Route::post('photos/flip/{photoId}/{horizontal}/{vertical}', 'Admin\PhotoController@flip')->name('photos.flip');
Route::post('photos/regenerate-thumbnails/{id}', 'Admin\PhotoController@regenerateThumbnails')->name('photos.regenerateThumbnails'); Route::post('photos/regenerate-thumbnails/{id}', 'Admin\PhotoController@regenerateThumbnails')->name('photos.regenerateThumbnails');
Route::post('photos/rotate/{photoId}/{angle}', 'Admin\PhotoController@rotate')->name('photos.rotate'); Route::post('photos/rotate/{photoId}/{angle}', 'Admin\PhotoController@rotate')->name('photos.rotate');
Route::post('photos/store-bulk', 'Admin\PhotoController@storeBulk')->name('photos.storeBulk'); Route::post('photos/store-bulk', 'Admin\PhotoController@storeBulk')->name('photos.storeBulk');