Implemented a better multi-file upload for browsers that support it. Started adding support for bulk modifying photos (e.g. rotating)

This commit is contained in:
Andy Heathershaw 2016-09-11 09:04:07 +01:00
parent 08f13b28cb
commit 63e341199b
6 changed files with 163 additions and 13 deletions

View File

@ -127,6 +127,13 @@ class AlbumController extends Controller
return Theme::render('admin.show_album', [ return Theme::render('admin.show_album', [
'album' => $album, 'album' => $album,
'bulk_actions' => [
'rotate_left' => trans('admin.photo_actions.rotate_left'),
'rotate_right' => trans('admin.photo_actions.rotate_right'),
'-' => '-----',
'refresh_thumbnails' => trans('admin.photo_actions.refresh_thumbnails'),
'delete' => trans('admin.photo_actions.delete')
],
'error' => $request->session()->get('error'), 'error' => $request->session()->get('error'),
'photos' => $photos 'photos' => $photos
]); ]);

View File

@ -184,10 +184,17 @@ class PhotoController extends Controller
$photo->save(); $photo->save();
} }
if ($request->isXmlHttpRequest())
{
return response()->json(['is_successful' => true]);
}
else
{
return redirect(route('albums.analyse', [ return redirect(route('albums.analyse', [
'id' => $album->id 'id' => $album->id
])); ]));
} }
}
public function storeBulk(Request $request) public function storeBulk(Request $request)
{ {
@ -303,6 +310,9 @@ class PhotoController extends Controller
{ {
$photos = $request->get('photo'); $photos = $request->get('photo');
dump($request->all());
exit();
/** @var Album $album */ /** @var Album $album */
$album = Album::where('id', intval($albumId))->first(); $album = Album::where('id', intval($albumId))->first();

View File

@ -19,12 +19,21 @@ return [
'edit_album' => 'Edit photo album: :album_name', 'edit_album' => 'Edit photo album: :album_name',
'edit_album_intro' => 'Photo albums contain individual photographs together in the same way as a physical photo album or memory book.', 'edit_album_intro' => 'Photo albums contain individual photographs together in the same way as a physical photo album or memory book.',
'edit_album_intro2' => 'Complete the form below to edit the properties of the album: :album_name.', 'edit_album_intro2' => 'Complete the form below to edit the properties of the album: :album_name.',
'upload_file_not_image_messages' => 'The file ":file_name" is not recognised as an image and won\'t be uploaded.',
'upload_file_status_failed' => ':file_name failed to upload',
'upload_file_status_success' => ':file_name uploaded successfully',
'manage_widget' => [ 'manage_widget' => [
'panel_header' => 'Manage' 'panel_header' => 'Manage'
], ],
'no_albums_text' => 'You have no photo albums yet. Click the button below to create one.', 'no_albums_text' => 'You have no photo albums yet. Click the button below to create one.',
'no_albums_title' => 'No Photo Albums', 'no_albums_title' => 'No Photo Albums',
'open_album' => 'Open album', 'open_album' => 'Open album',
'photo_actions' => [
'delete' => 'Delete',
'refresh_thumbnails' => 'Refresh thumbnails',
'rotate_left' => 'Rotate left',
'rotate_right' => 'Rotate right'
],
'settings_image_protection' => 'Image Protection', 'settings_image_protection' => 'Image Protection',
'settings_link' => 'Settings', 'settings_link' => 'Settings',
'settings_recaptcha' => 'reCAPTCHA settings', 'settings_recaptcha' => 'reCAPTCHA settings',

View File

@ -1,5 +1,8 @@
<?php <?php
return [ return [
'apply_action' => 'Apply',
'bulk_edit_photos_label' => 'Bulk edit selected photos:',
'bulk_edit_photos_placeholder' => 'Select an action',
'cancel_action' => 'Cancel', 'cancel_action' => 'Cancel',
'create_action' => 'Create', 'create_action' => 'Create',
'delete_action' => 'Delete', 'delete_action' => 'Delete',
@ -14,6 +17,7 @@ return [
'realname_label' => 'Your name:', 'realname_label' => 'Your name:',
'register_action' => 'Create account', 'register_action' => 'Create account',
'remember_me_label' => 'Remember me', 'remember_me_label' => 'Remember me',
'select' => 'Select',
'settings_hotlink_protection' => 'Prevent hot-linking to images', 'settings_hotlink_protection' => 'Prevent hot-linking to images',
'settings_hotlink_protection_help' => 'With this option enabled, direct linking to images is not allowed. Photos can only be viewed through Blue Twilight.', 'settings_hotlink_protection_help' => 'With this option enabled, direct linking to images is not allowed. Photos can only be viewed through Blue Twilight.',
'settings_restrict_originals_download' => 'Restrict access to original images', 'settings_restrict_originals_download' => 'Restrict access to original images',

View File

@ -47,6 +47,11 @@
@include (Theme::viewName('partials.single_photo_admin')) @include (Theme::viewName('partials.single_photo_admin'))
@endforeach @endforeach
<div class="pull-left">
<p>{!! Form::label('bulk-action', trans('forms.bulk_edit_photos_label'), ['class' => 'control-label']) !!}</p>
{!! Form::select('bulk-action', $bulk_actions, null, ['placeholder' => trans('forms.bulk_edit_photos_placeholder'), 'id' => 'bulk-action-apply']) !!}
<button type="submit" class="btn btn-sm btn-success" name="bulk-apply" value="clicked">@lang('forms.apply_action')</button>
</div>
<div class="pull-right"> <div class="pull-right">
<button type="submit" class="btn btn-success">@lang('forms.save_action')</button> <button type="submit" class="btn btn-success">@lang('forms.save_action')</button>
</div> </div>
@ -62,19 +67,36 @@
{{-- Upload --}} {{-- Upload --}}
<div role="tabpanel" class="tab-pane" id="upload-tab"> <div role="tabpanel" class="tab-pane" id="upload-tab">
<h4>Upload single images</h4> <h4>Upload images</h4>
{!! Form::open(['route' => 'photos.store', 'method' => 'POST', 'files' => true]) !!} <div class="row">
<div class="col-sm-5">
{!! Form::open(['route' => 'photos.store', 'method' => 'POST', 'files' => true, 'id' => 'single-upload-form']) !!}
{!! Form::hidden('album_id', $album->id) !!} {!! Form::hidden('album_id', $album->id) !!}
<div class="form-group"> <div class="form-group">
{!! Form::file('photo[]', ['class' => 'control-label', 'multiple' => 'multiple']) !!} {!! Form::file('photo[]', ['class' => 'control-label', 'multiple' => 'multiple', 'id' => 'single-upload-files']) !!}
</div> </div>
<div> <div>
{!! Form::submit(trans('forms.upload_action'), ['class' => 'btn btn-success']) !!} <button type="submit" class="btn btn-success">@lang('forms.upload_action')</button>
</div> </div>
{!! Form::close() !!} {!! Form::close() !!}
</div>
<div class="col-sm-5 text-center" id="upload-progress-bar" style="display: none;">
<p><b>Uploading...</b></p>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 0%">
<span class="sr-only"><span class="percentage-success">0%</span> Complete (success)</span>
</div>
<div class="progress-bar progress-bar-danger" style="width: 0%">
<span class="sr-only"><span class="percentage-danger">0%</span> Complete (warning)</span>
</div>
</div>
<p id="upload-status-text"></p>
</div>
</div>
<hr/> <hr/>
<h4>Bulk upload</h4> <h4>Bulk upload</h4>
@ -108,6 +130,10 @@
@push('scripts') @push('scripts')
<script type="text/javascript"> <script type="text/javascript">
var images_failed = 0;
var images_uploaded = 0;
var images_total = 0;
function deletePhoto(photo_id, parent) function deletePhoto(photo_id, parent)
{ {
var url = '{{ route('photos.destroy', ['id' => 0]) }}'; var url = '{{ route('photos.destroy', ['id' => 0]) }}';
@ -154,6 +180,51 @@
}); });
} }
function updateProgressBar()
{
if ((images_uploaded > 0 && images_uploaded + images_failed) == images_total)
{
window.location = '{{ route('albums.analyse', ['id' => $album->id]) }}';
}
var failed_percentage = ((images_failed / images_total) * 100).toFixed(2);
var success_percentage = ((images_uploaded / images_total) * 100).toFixed(2);
$('#upload-progress-bar').show();
$('.progress-bar-success', '#upload-progress-bar').css('width', success_percentage + '%');
$('.progress-bar-danger', '#upload-progress-bar').css('width', failed_percentage + '%');
$('.percentage-success', '#upload-progress-bar').html(success_percentage);
$('.percentage-danger', '#upload-progress-bar').html(failed_percentage);
}
function uploadImageFile(formObject, imageFile)
{
var formData = new FormData();
formData.append('album_id', '{{ $album->id }}');
formData.append('photo[]', imageFile, imageFile.name);
$.ajax(
{
complete: function() {
updateProgressBar();
},
contentType: false,
data: formData,
error: function() {
images_failed++;
$('#upload-status-text').html('@lang('admin.upload_file_status_failed')'.replace(':file_name', imageFile.name));
},
method: $(formObject).attr('method'),
processData: false,
success: function() {
images_uploaded++;
$('#upload-status-text').html('@lang('admin.upload_file_status_success')'.replace(':file_name', imageFile.name));
},
url: $(formObject).attr('action')
}
);
}
$(document).ready(function() { $(document).ready(function() {
$('#upload-button').click(function() { $('#upload-button').click(function() {
$('.nav-tabs a[href="#upload-tab"]').tab('show'); $('.nav-tabs a[href="#upload-tab"]').tab('show');
@ -210,6 +281,51 @@
$(this).dropdown('toggle'); $(this).dropdown('toggle');
return false; return false;
}); });
{{-- Photo uploads using AJAX --}}
if (window.FormData)
{
$('#single-upload-form').submit(function(event) {
var fileSelect = $('#single-upload-files', this);
var uploadButton = $('button[type=submit]', this);
var currentText = uploadButton.html();
// Update button text
uploadButton.attr('disabled', 'disabled');
uploadButton.html('Uploading...');
// Get the selected files
var notImageString = '{!! addslashes(trans('admin.upload_file_not_image_messages')) !!}';
var files = fileSelect[0].files;
// Reset statistics
images_total = files.length;
images_failed = 0;
images_uploaded = 0;
updateProgressBar();
// Loop through each of the selected files and upload them individually
for (var i = 0; i < files.length; i++)
{
var file = files[i];
// We're only interested in image files
if (!file.type.match('image.*'))
{
alert(notImageString.replace(':file_name', file.name));
continue;
}
// Upload the file
uploadImageFile($(this), file);
}
// Prevent standard form upload
event.preventDefault();
return false;
})
}
}) })
</script> </script>
@endpush @endpush

View File

@ -30,6 +30,10 @@
</ul> </ul>
</div> </div>
</div> </div>
<p style="margin-top: 10px;">
<input type="checkbox" id="select-photo-{{ $photo->id }}" name="select-photo[]" value="{{ $photo->id }}" /> <label for="select-photo-{{ $photo->id }}">@lang('forms.select')</label>
</p>
</div> </div>
<div class="col-xs-12 col-sm-10"> <div class="col-xs-12 col-sm-10">
<div class="form-group"> <div class="form-group">