diff --git a/app/Album.php b/app/Album.php index ec7fde5..3b9559c 100644 --- a/app/Album.php +++ b/app/Album.php @@ -19,7 +19,7 @@ class Album extends Model * @var array */ protected $fillable = [ - 'name', 'description', 'url_alias', 'is_private', 'user_id', 'storage_id' + 'name', 'description', 'url_alias', 'is_private', 'user_id', 'storage_id', 'default_view' ]; /** diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 21c9784..e8186fb 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -5,6 +5,7 @@ namespace App\Exceptions; use Exception; use Illuminate\Auth\AuthenticationException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; +use Illuminate\Session\TokenMismatchException; class Handler extends ExceptionHandler { @@ -44,6 +45,11 @@ class Handler extends ExceptionHandler */ public function render($request, Exception $exception) { + if ($exception instanceof TokenMismatchException) + { + return redirect()->back()->withInput()->with('error', 'Your session has expired. Please try the request again.'); + } + return parent::render($request, $exception); } diff --git a/app/Helpers/ConfigHelper.php b/app/Helpers/ConfigHelper.php index bea26fa..43ec37a 100644 --- a/app/Helpers/ConfigHelper.php +++ b/app/Helpers/ConfigHelper.php @@ -9,6 +9,11 @@ use App\Configuration; class ConfigHelper { + public function allowedAlbumViews() + { + return ['default', 'slideshow']; + } + public function allowedDateFormats() { return [ @@ -78,6 +83,7 @@ class ConfigHelper 'allow_self_registration' => true, 'app_name' => trans('global.app_name'), 'date_format' => $this->allowedDateFormats()[0], + 'default_album_view' => $this->allowedAlbumViews()[0], 'hotlink_protection' => false, 'items_per_page' => 12, 'items_per_page_admin' => 10, diff --git a/app/Http/Controllers/Admin/AlbumController.php b/app/Http/Controllers/Admin/AlbumController.php index bc538f1..92e05e0 100644 --- a/app/Http/Controllers/Admin/AlbumController.php +++ b/app/Http/Controllers/Admin/AlbumController.php @@ -155,8 +155,15 @@ class AlbumController extends Controller $postLimit = MiscHelper::convertToBytes(ini_get('post_max_size')) / (1024*1024); $fileUploadOrPostLowerLimit = ($postLimit < $fileUploadLimit) ? $postLimit : $fileUploadLimit; + $allowedAlbumViews = []; + foreach (UserConfig::allowedAlbumViews() as $view) + { + $allowedAlbumViews[$view] = trans(sprintf('gallery.album_views.%s', $view)); + } + return Theme::render('admin.show_album', [ 'album' => $album, + 'allowed_views' => $allowedAlbumViews, 'bulk_actions' => [ 'rotate_left' => trans('admin.photo_actions.rotate_left'), 'rotate_right' => trans('admin.photo_actions.rotate_right'), @@ -193,6 +200,7 @@ class AlbumController extends Controller $album = new Album(); $album->fill($request->only(['name', 'description', 'storage_id'])); + $album->default_view = UserConfig::get('default_album_view'); $album->is_private = (strtolower($request->get('is_private')) == 'on'); $album->user_id = Auth::user()->id; @@ -214,10 +222,22 @@ class AlbumController extends Controller $this->authorize('admin-access'); $album = $this->loadAlbum($id); - $album->fill($request->only(['name', 'description'])); - $album->save(); + $album->fill(['name', 'description']); + $album->is_private = (strtolower($request->get('is_private')) == 'on'); - return Theme::render('admin.show_album', ['album' => $album]); + // These keys are optional and may or may not be in the request, depending on the page requesting it + foreach (['storage_id', 'default_view'] as $key) + { + if ($request->has($key)) + { + $album->$key = $request->get($key); + } + } + + $album->save(); + $request->session()->flash('success', trans('admin.album_saved_successfully', ['name' => $album->name])); + + return redirect(route('albums.show', ['id' => $id])); } /** diff --git a/app/Http/Controllers/Admin/PhotoController.php b/app/Http/Controllers/Admin/PhotoController.php index eb31fb8..49b271e 100644 --- a/app/Http/Controllers/Admin/PhotoController.php +++ b/app/Http/Controllers/Admin/PhotoController.php @@ -377,7 +377,7 @@ class PhotoController extends Controller public function updateBulk(Request $request, $albumId) { - $photoIds = $request->get('select-photo'); + $this->authorize('admin-access'); /** @var Album $album */ $album = Album::where('id', intval($albumId))->first(); @@ -388,6 +388,31 @@ class PhotoController extends Controller return null; } + $numberChanged = 0; + + if ($request->has('bulk-apply')) + { + $numberChanged = $this->applyBulkActions($request, $album); + } + else + { + $numberChanged = $this->updatePhotoDetails($request, $album); + } + + $request->session()->flash('success', trans_choice('admin.bulk_photos_changed', $numberChanged, ['number' => $numberChanged])); + + return redirect(route('albums.show', array('id' => $albumId))); + } + + private function applyBulkActions(Request $request, Album $album) + { + $photoIds = $request->get('select-photo'); + if (is_null($photoIds) || !is_array($photoIds) || count($photoIds) == 0) + { + $request->session()->flash('warning', trans('admin.no_photo_selected_message')); + return 0; + } + $action = $request->get('bulk-action'); $numberChanged = 0; @@ -449,12 +474,12 @@ class PhotoController extends Controller break; } + $photo->save(); + $numberChanged++; } - $request->session()->flash('success', trans_choice('admin.bulk_photos_changed', $numberChanged, ['number' => $numberChanged])); - - return redirect(route('albums.show', array('id' => $albumId))); + return $numberChanged; } /** @@ -472,4 +497,27 @@ class PhotoController extends Controller return $album; } + + private function updatePhotoDetails(Request $request, Album $album) + { + $numberChanged = 0; + $photos = $request->get('photo'); + + foreach ($photos as $photoId => $value) + { + /** @var Photo $photo */ + $photo = $album->photos()->where('id', intval($photoId))->first(); + if (is_null($photo)) + { + continue; + } + + $photo->fill($value); + $photo->save(); + + $numberChanged++; + } + + return $numberChanged; + } } diff --git a/app/Http/Controllers/Gallery/AlbumController.php b/app/Http/Controllers/Gallery/AlbumController.php index 481af28..3050cbe 100644 --- a/app/Http/Controllers/Gallery/AlbumController.php +++ b/app/Http/Controllers/Gallery/AlbumController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Gallery; use App\Album; use App\Facade\Theme; use App\Facade\UserConfig; +use App\Helpers\ConfigHelper; use App\Helpers\DbHelper; use App\Http\Controllers\Controller; use App\Http\Requests; @@ -23,11 +24,16 @@ class AlbumController extends Controller return null; } - $validViews = ['default', 'slideshow']; + $validViews = UserConfig::allowedAlbumViews(); $requestedView = strtolower($request->get('view')); if (!in_array($requestedView, $validViews)) { - $requestedView = $validViews[0]; + $requestedView = $album->default_view; + + if (!in_array($requestedView, $validViews)) + { + $requestedView = $validViews[0]; + } } $this->authorizeForUser($this->getUser(), 'album.view', $album); @@ -47,6 +53,7 @@ class AlbumController extends Controller return Theme::render(sprintf('gallery.album_%s', $requestedView), [ 'album' => $album, + 'allowed_views' => $validViews, 'current_view' => $requestedView, 'photos' => $photos ]); diff --git a/app/Http/Requests/StoreAlbumRequest.php b/app/Http/Requests/StoreAlbumRequest.php index 617ebdf..9d4e859 100644 --- a/app/Http/Requests/StoreAlbumRequest.php +++ b/app/Http/Requests/StoreAlbumRequest.php @@ -23,10 +23,24 @@ class StoreAlbumRequest extends FormRequest */ public function rules() { - return [ - 'description' => '', - 'name' => 'required|unique:albums|max:255', - 'storage_id' => 'required' - ]; + switch ($this->method()) + { + case 'POST': + return [ + 'description' => '', + 'name' => 'required|unique:albums|max:255', + 'storage_id' => 'required|sometimes' + ]; + + case 'PATCH': + case 'PUT': + $albumId = intval($this->segment(3)); + + return [ + 'description' => 'sometimes', + 'name' => 'required|sometimes|max:255|unique:albums,name,' . $albumId, + 'storage_id' => 'required|sometimes' + ]; + } } } diff --git a/app/Photo.php b/app/Photo.php index aa36ad8..a36e04a 100644 --- a/app/Photo.php +++ b/app/Photo.php @@ -30,7 +30,9 @@ class Photo extends Model 'camera_software', 'width', 'height', - 'is_analysed' + 'is_analysed', + 'created_at', + 'updated_at' ]; /** @@ -46,9 +48,18 @@ class Photo extends Model return $this->belongsTo(Album::class); } - public function thumbnailUrl($thumbnailName = null) + public function thumbnailUrl($thumbnailName = null, $cacheBust = true) { - return $this->album->getAlbumSource()->getUrlToPhoto($this, $thumbnailName); + $url = $this->album->getAlbumSource()->getUrlToPhoto($this, $thumbnailName); + + if ($cacheBust) + { + // Append the timestamp of the last update to avoid browser caching + $theDate = is_null($this->updated_at) ? $this->created_at : $this->updated_at; + $url .= sprintf('%s_=%d', (strpos($url, '?') === false ? '?' : '&'), $theDate->format('U')); + } + + return $url; } public function url() diff --git a/config/session.php b/config/session.php index ace3ef0..d93f72c 100644 --- a/config/session.php +++ b/config/session.php @@ -16,7 +16,7 @@ return [ | */ - 'driver' => env('SESSION_DRIVER', 'file'), + 'driver' => env('SESSION_DRIVER', 'database'), /* |-------------------------------------------------------------------------- @@ -31,7 +31,7 @@ return [ 'lifetime' => 120, - 'expire_on_close' => false, + 'expire_on_close' => true, /* |-------------------------------------------------------------------------- @@ -70,7 +70,7 @@ return [ | */ - 'connection' => null, + 'connection' => 'mysql', /* |-------------------------------------------------------------------------- @@ -122,7 +122,7 @@ return [ | */ - 'cookie' => 'laravel_session', + 'cookie' => 'twilight_session', /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2016_10_05_120046_add_album_view.php b/database/migrations/2016_10_05_120046_add_album_view.php new file mode 100644 index 0000000..b711f7c --- /dev/null +++ b/database/migrations/2016_10_05_120046_add_album_view.php @@ -0,0 +1,32 @@ +string('default_view', 50); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('albums', function (Blueprint $table) { + $table->dropColumn('default_view'); + }); + } +} diff --git a/database/migrations/2016_10_05_141222_create_sessions_table.php b/database/migrations/2016_10_05_141222_create_sessions_table.php new file mode 100644 index 0000000..89fe791 --- /dev/null +++ b/database/migrations/2016_10_05_141222_create_sessions_table.php @@ -0,0 +1,35 @@ +string('id')->unique(); + $table->integer('user_id')->nullable(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->text('payload'); + $table->integer('last_activity'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('sessions'); + } +} diff --git a/public/themes/base/css/app.css b/public/themes/base/css/app.css index e48de88..c71aefd 100644 --- a/public/themes/base/css/app.css +++ b/public/themes/base/css/app.css @@ -1,4 +1,4 @@ -.album-slideshow-container .image-preview { +.album-slideshow-container #image-preview { height: 600px; max-width: 100%; width: 800px; diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index 44e0b82..f5d5d7e 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -4,10 +4,17 @@ return [ 'create_album_link' => 'Create album', 'panel_header' => 'Actions', ], + 'album_appearance_heading' => 'Appearance', + 'album_appearance_intro' => 'The settings shown below control how this album appears to visitors in the gallery.', + 'album_basic_info_heading' => 'Album information', + 'album_basic_info_intro' => 'The album\'s name and description are displayed to visitors in the gallery. If you wish to change them, you can do so below.', 'album_no_photos_p1' => 'No photos in this album', 'album_no_photos_p2' => 'Click the "Upload photos" button below to get started.', 'album_no_photos_button' => 'Upload photos', 'album_photos_tab' => 'Photos', + 'album_saved_successfully' => 'The ":name" album was updated successfully.', + 'album_security_heading' => 'Security', + 'album_security_intro' => 'The settings below affect the visibility of this album to other users.', 'album_settings_tab' => 'Settings', 'album_upload_tab' => 'Upload', 'analyse_photos_failed' => 'The following items could not be analysed and were removed:', @@ -25,6 +32,8 @@ return [ 'create_user' => 'Create user', 'create_user_intro' => 'You can use the form below to create a user account. Users created using this form will be activate immediately.', 'create_user_title' => 'Create a user account', + 'danger_zone_heading' => 'Danger zone', + 'danger_zone_intro' => 'The options below WILL cause data loss - please be careful!', 'delete_album' => 'Delete album :name', 'delete_album_confirm' => 'Are you sure you want to permanently delete this album and all its contents?', 'delete_album_warning' => 'This is a permanent action that cannot be undone!', @@ -41,6 +50,7 @@ return [ 'delete_user_confirm' => 'Are you sure you want to permanently remove :name\'s user account? They will be immediately logged out.', 'delete_user_warning' => 'This is a permanent action that cannot be reversed!', 'edit_album' => 'Edit photo album: :album_name', + 'edit_album_action' => 'Edit album details', '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_storage' => 'Edit storage location: :storage_name', @@ -55,6 +65,7 @@ return [ 'move_successful_message' => 'The photo ":name" was moved successfully to the ":album" album.', 'no_albums_text' => 'You have no photo albums yet. Click the button below to create one.', 'no_albums_title' => 'No Photo Albums', + 'no_photo_selected_message' => 'Please select at least one photo.', 'no_storages_text' => 'You need a storage location to store your uploaded photographs.', 'no_storages_text2' => 'This can be on your server\'s local filesystem or a cloud location such as Amazon S3 or Rackspace.', 'no_storages_title' => 'No storage locations defined', @@ -69,6 +80,8 @@ return [ 'rotate_left' => 'Rotate left', 'rotate_right' => 'Rotate right' ], + 'save_changes_heading' => 'Save changes', + 'save_changes_intro' => 'If you have made any changes to the above fields, you\'ll need to hit the Save Changes button below.', 'settings_image_protection' => 'Image Protection', 'settings_recaptcha' => 'reCAPTCHA settings', 'settings_save_action' => 'Update Settings', diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php index 52ef5f5..fdeaca4 100644 --- a/resources/lang/en/forms.php +++ b/resources/lang/en/forms.php @@ -10,6 +10,7 @@ return [ 'cancel_action' => 'Cancel', 'continue_action' => 'Continue', 'create_action' => 'Create', + 'default_album_view_label' => 'Default view mode in the gallery:', 'default_storage_label' => 'Use as the default storage location for new albums', 'delete_action' => 'Delete', 'description_label' => 'Description:', diff --git a/resources/views/themes/base/admin/create_album.blade.php b/resources/views/themes/base/admin/create_album.blade.php index b397f18..24806e1 100644 --- a/resources/views/themes/base/admin/create_album.blade.php +++ b/resources/views/themes/base/admin/create_album.blade.php @@ -26,8 +26,8 @@ @if (count($errors) > 0)
diff --git a/resources/views/themes/base/admin/show_album.blade.php b/resources/views/themes/base/admin/show_album.blade.php index b85b448..2c7ae17 100644 --- a/resources/views/themes/base/admin/show_album.blade.php +++ b/resources/views/themes/base/admin/show_album.blade.php @@ -155,10 +155,70 @@ {{-- Settings --}}
-
- @lang('forms.edit_action') - @lang('forms.delete_action') + {!! Form::model($album, ['route' => ['albums.update', $album->id], 'method' => 'PUT']) !!} +

@lang('admin.album_basic_info_heading')

+

@lang('admin.album_basic_info_intro')

+ +
+ {!! Form::label('name', trans('forms.name_label'), ['class' => 'control-label']) !!} + {!! Form::text('name', old('name'), ['class' => 'form-control']) !!}
+ +
+ {!! Form::label('description', trans('forms.description_label'), ['class' => 'control-label']) !!} + {!! Form::textarea('description', old('description'), ['class' => 'form-control']) !!} +
+ +
+ +

@lang('admin.album_appearance_heading')

+

@lang('admin.album_appearance_intro')

+ +
+ + {!! Form::select('default_view', $allowed_views, old('default_view'), ['class' => 'form-control']) !!} +
+ +
+ +

@lang('admin.album_security_heading')

+

@lang('admin.album_security_intro')

+ +
+ +
+ +
+ +
+
+
+
@lang('admin.danger_zone_heading')
+
+

@lang('admin.danger_zone_intro')

+

+ @lang('forms.delete_action') +

+
+
+
+ +
+
+
@lang('admin.save_changes_heading')
+
+

@lang('admin.save_changes_intro')

+

+ +

+
+
+
+
+ {!! Form::close() !!}
diff --git a/resources/views/themes/base/partials/album_view_selector.blade.php b/resources/views/themes/base/partials/album_view_selector.blade.php index 0492e06..66f36f1 100644 --- a/resources/views/themes/base/partials/album_view_selector.blade.php +++ b/resources/views/themes/base/partials/album_view_selector.blade.php @@ -2,8 +2,9 @@