From 7ea1dc5c8302f8b43bbcfabc10c497b2c95db186 Mon Sep 17 00:00:00 2001 From: Andy Heathershaw Date: Mon, 17 Apr 2017 17:11:59 +0100 Subject: [PATCH] #4: Nested albums are now supported in the admin panel --- app/Album.php | 32 +++++++++- app/Helpers/DbHelper.php | 12 +++- .../Controllers/Admin/AlbumController.php | 29 +++++++-- app/Services/AlbumService.php | 42 +++++++++++++ ...04_16_095641_create_visitor_hits_table.php | 6 +- .../2017_04_16_095657_attach_hit_columns.php | 6 +- ...7_04_17_154654_add_parent_album_column.php | 39 ++++++++++++ resources/assets/css/admin.css | 5 ++ resources/assets/css/global.css | 4 ++ resources/lang/en/forms.php | 3 + .../themes/base/admin/create_album.blade.php | 12 +++- .../themes/base/admin/edit_album.blade.php | 10 +++ .../themes/base/admin/list_albums.blade.php | 62 +++++++++++-------- .../partials/single_album_admin.blade.php | 32 ++++++++++ 14 files changed, 253 insertions(+), 41 deletions(-) create mode 100644 app/Services/AlbumService.php create mode 100644 database/migrations/2017_04_17_154654_add_parent_album_column.php create mode 100644 resources/views/themes/base/partials/single_album_admin.blade.php diff --git a/app/Album.php b/app/Album.php index cdcbba1..3e4fe8b 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', 'default_view' + 'name', 'description', 'url_alias', 'is_private', 'user_id', 'storage_id', 'default_view', 'parent_album_id' ]; /** @@ -35,6 +35,11 @@ class Album extends Model return $this->belongsToMany(Permission::class, 'album_anonymous_permissions'); } + public function children() + { + return $this->hasMany(Album::class, 'parent_album_id'); + } + public function doesGroupHavePermission(Group $group, Permission $permission) { return $this->groupPermissions()->where([ @@ -84,6 +89,31 @@ class Album extends Model return $this->belongsToMany(Permission::class, 'album_group_permissions'); } + /** + * Returns true if this album is a descendant of the given album. + * @param Album $album + */ + public function isChildOf(Album $album) + { + $currentAlbum = $this; + while (!is_null($currentAlbum)) + { + if ($currentAlbum->parent_album_id == $album->id) + { + return true; + } + + $currentAlbum = Album::where('id', $currentAlbum->parent_album_id)->first(); + } + + return false; + } + + public function parent() + { + return $this->belongsTo(Album::class, 'parent_album_id'); + } + public function photos() { return $this->hasMany(Photo::class); diff --git a/app/Helpers/DbHelper.php b/app/Helpers/DbHelper.php index 9df062e..516f9ce 100644 --- a/app/Helpers/DbHelper.php +++ b/app/Helpers/DbHelper.php @@ -9,10 +9,16 @@ use Illuminate\Support\Facades\Auth; class DbHelper { - public static function getAlbumsForCurrentUser() + public static function getAlbumsForCurrentUser($parentID = -1) { - return self::getAlbumsForCurrentUser_NonPaged() - ->paginate(UserConfig::get('items_per_page')); + $query = self::getAlbumsForCurrentUser_NonPaged(); + + if ($parentID == 0) + { + $query = $query->where('albums.parent_album_id', null); + } + + return $query->paginate(UserConfig::get('items_per_page')); } public static function getAlbumsForCurrentUser_NonPaged() diff --git a/app/Http/Controllers/Admin/AlbumController.php b/app/Http/Controllers/Admin/AlbumController.php index 6b0b3ac..90308b9 100644 --- a/app/Http/Controllers/Admin/AlbumController.php +++ b/app/Http/Controllers/Admin/AlbumController.php @@ -14,6 +14,7 @@ use App\Http\Controllers\Controller; use App\Http\Requests; use App\Permission; use App\Photo; +use App\Services\AlbumService; use App\Services\PhotoService; use App\Storage; use App\Upload; @@ -71,11 +72,13 @@ class AlbumController extends Controller return redirect(route('storage.create')); } + $albumService = new AlbumService(); $defaultSource = Storage::where('is_default', true)->limit(1)->first(); return Theme::render('admin.create_album', [ 'album_sources' => $albumSources, - 'default_storage_id' => (!is_null($defaultSource) ? $defaultSource->id : 0) + 'default_storage_id' => (!is_null($defaultSource) ? $defaultSource->id : 0), + 'parent_albums' => $albumService->getFlattenedAlbumTree() ]); } @@ -133,7 +136,12 @@ class AlbumController extends Controller $request->session()->flash('_old_input', $album->toArray()); } - return Theme::render('admin.edit_album', ['album' => $album]); + $albumService = new AlbumService(); + + return Theme::render('admin.edit_album', [ + 'album' => $album, + 'parent_albums' => $albumService->getFlattenedAlbumTree() + ]); } /** @@ -145,7 +153,8 @@ class AlbumController extends Controller { $this->authorizeAccessToAdminPanel('admin:manage-albums'); - $albums = DbHelper::getAlbumsForCurrentUser(); + // Only get top-level albums + $albums = DbHelper::getAlbumsForCurrentUser(0); return Theme::render('admin.list_albums', [ 'albums' => $albums, @@ -384,7 +393,12 @@ class AlbumController extends Controller $this->authorizeAccessToAdminPanel('admin:manage-albums'); $album = new Album(); - $album->fill($request->only(['name', 'description', 'storage_id'])); + $album->fill($request->only(['name', 'description', 'storage_id', 'parent_album_id'])); + + if (strlen($album->parent_album_id) == 0) + { + $album->parent_album_id = null; + } $album->default_view = UserConfig::get('default_album_view'); $album->is_private = (strtolower($request->get('is_private')) == 'on'); @@ -408,9 +422,14 @@ class AlbumController extends Controller $this->authorizeAccessToAdminPanel('admin:manage-albums'); $album = $this->loadAlbum($id); - $album->fill($request->only(['name', 'description'])); + $album->fill($request->only(['name', 'description', 'parent_album_id'])); $album->is_private = (strtolower($request->get('is_private')) == 'on'); + if (strlen($album->parent_album_id) == 0) + { + $album->parent_album_id = null; + } + // 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) { diff --git a/app/Services/AlbumService.php b/app/Services/AlbumService.php new file mode 100644 index 0000000..fd6f30b --- /dev/null +++ b/app/Services/AlbumService.php @@ -0,0 +1,42 @@ +get(); + $result = []; + + $this->buildAlbumTree($result, $allAlbums); + + return $result; + } + + private function buildAlbumTree(array &$result, $allAlbums, Album $parent = null, $level = 0) + { + $parentAlbums = []; + foreach ($allAlbums as $album) + { + if ((is_null($parent) && is_null($album->parent_album_id)) || (!is_null($parent) && $album->parent_album_id == $parent->id)) + { + $parentAlbums[] = $album; + } + } + + foreach ($parentAlbums as $album) + { + $album->display_name = trim(sprintf('%s %s', str_repeat('-', $level), $album->name)); + $result[$album->id] = $album; + $this->buildAlbumTree($result, $allAlbums, $album, $level + 1); + } + } +} \ No newline at end of file diff --git a/database/migrations/2017_04_16_095641_create_visitor_hits_table.php b/database/migrations/2017_04_16_095641_create_visitor_hits_table.php index 41c87f9..551f570 100644 --- a/database/migrations/2017_04_16_095641_create_visitor_hits_table.php +++ b/database/migrations/2017_04_16_095641_create_visitor_hits_table.php @@ -27,13 +27,13 @@ class CreateVisitorHitsTable extends Migration $table->foreign('album_id') ->references('id')->on('albums') - ->onDelete('no action'); + ->onDelete('cascade'); $table->foreign('photo_id') ->references('id')->on('photos') - ->onDelete('no action'); + ->onDelete('cascade'); $table->foreign('user_id') ->references('id')->on('users') - ->onDelete('no action'); + ->onDelete('cascade'); $table->timestamps(); }); diff --git a/database/migrations/2017_04_16_095657_attach_hit_columns.php b/database/migrations/2017_04_16_095657_attach_hit_columns.php index 50e9c35..599ec70 100644 --- a/database/migrations/2017_04_16_095657_attach_hit_columns.php +++ b/database/migrations/2017_04_16_095657_attach_hit_columns.php @@ -14,12 +14,12 @@ class AttachHitColumns extends Migration public function up() { Schema::table('albums', function (Blueprint $table) { - $table->bigInteger('hits'); + $table->bigInteger('hits')->default(0); }); Schema::table('photos', function (Blueprint $table) { - $table->bigInteger('hits'); - $table->bigInteger('hits_download'); + $table->bigInteger('hits')->default(0); + $table->bigInteger('hits_download')->default(0); }); } diff --git a/database/migrations/2017_04_17_154654_add_parent_album_column.php b/database/migrations/2017_04_17_154654_add_parent_album_column.php new file mode 100644 index 0000000..cb4125d --- /dev/null +++ b/database/migrations/2017_04_17_154654_add_parent_album_column.php @@ -0,0 +1,39 @@ +unsignedInteger('parent_album_id')->nullable(); + + $table->foreign('parent_album_id') + ->references('id')->on('albums') + ->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('albums', function (Blueprint $table) + { + $table->dropForeign('albums_parent_album_id_foreign'); + $table->dropColumn('parent_album_id'); + }); + } +} diff --git a/resources/assets/css/admin.css b/resources/assets/css/admin.css index 21f7b52..1a980df 100644 --- a/resources/assets/css/admin.css +++ b/resources/assets/css/admin.css @@ -2,6 +2,11 @@ margin-bottom: 15px; } +.album-expand-handle { + cursor: pointer; + margin-top: 5px; +} + .card-header.card-danger { color: #fff; font-weight: bold; diff --git a/resources/assets/css/global.css b/resources/assets/css/global.css index 0237fc0..82b31d0 100644 --- a/resources/assets/css/global.css +++ b/resources/assets/css/global.css @@ -19,6 +19,10 @@ textarea { margin-top: 20px; } +.hidden { + display: none; +} + .tab-content { border: solid 1px rgb(221, 221, 221); border-top: 0; diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php index fb3e4ac..77a9476 100644 --- a/resources/lang/en/forms.php +++ b/resources/lang/en/forms.php @@ -19,6 +19,8 @@ return [ 'email_label' => 'E-mail address:', 'login_action' => 'Login', 'name_label' => 'Name:', + 'parent_album_label' => 'Parent album:', + 'parent_album_placeholder' => 'None (top-level album)', 'password_label' => 'Password:', 'password_confirm_label' => 'Confirm password:', 'private_album_label' => 'Private album (only visible to me)', @@ -27,6 +29,7 @@ return [ 'remember_me_label' => 'Remember me', 'remove_action' => 'Remove', 'select' => 'Select', + 'select_current_text' => '(current)', '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_restrict_originals_download' => 'Restrict access to original images', diff --git a/resources/views/themes/base/admin/create_album.blade.php b/resources/views/themes/base/admin/create_album.blade.php index e58be58..287fc49 100644 --- a/resources/views/themes/base/admin/create_album.blade.php +++ b/resources/views/themes/base/admin/create_album.blade.php @@ -35,9 +35,19 @@ +
+ + +
+
- @foreach ($album_sources as $key => $value) @endforeach diff --git a/resources/views/themes/base/admin/edit_album.blade.php b/resources/views/themes/base/admin/edit_album.blade.php index 4f98a70..4ba06e4 100644 --- a/resources/views/themes/base/admin/edit_album.blade.php +++ b/resources/views/themes/base/admin/edit_album.blade.php @@ -44,6 +44,16 @@ @endif
+
+ + +
+
@lang('forms.cancel_action') diff --git a/resources/views/themes/base/admin/list_albums.blade.php b/resources/views/themes/base/admin/list_albums.blade.php index d278a4e..2ab4fcb 100644 --- a/resources/views/themes/base/admin/list_albums.blade.php +++ b/resources/views/themes/base/admin/list_albums.blade.php @@ -28,30 +28,7 @@ @foreach ($albums as $album) - - - - + @include (Theme::viewName('partials.single_album_admin'), ['is_child' => false]) @endforeach
- - @can('edit', $album) - {{ $album->name }} - @endcan - @cannot('edit', $album) - {{ $album->name }} - @endcannot -
-

{{ $album->description }}

-

{{ $album->photos_count }} {{ trans_choice('admin.stats_widget.photos', $album->photos_count) }}

-
-
- @can('edit', $album) - @lang('forms.edit_action') - @endcan - @can('delete', $album) - @lang('forms.delete_action') - @endcan -
-
@@ -67,4 +44,39 @@
-@endsection \ No newline at end of file +@endsection + +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/themes/base/partials/single_album_admin.blade.php b/resources/views/themes/base/partials/single_album_admin.blade.php new file mode 100644 index 0000000..99ec1c2 --- /dev/null +++ b/resources/views/themes/base/partials/single_album_admin.blade.php @@ -0,0 +1,32 @@ +parent_album_id)) data-parent-album-id="{{ $album->parent_album_id }}" @endif> + + @if ($album->children()->count() > 0) + + @endif + + + + @can('edit', $album) + {{ $album->name }} + @endcan + @cannot('edit', $album) + {{ $album->name }} + @endcannot +
+

{{ $album->description }}

+

{{ $album->photos_count }} {{ trans_choice('admin.stats_widget.photos', $album->photos_count) }}

+ + +
+ @can('edit', $album) + @lang('forms.edit_action') + @endcan + @can('delete', $album) + @lang('forms.delete_action') + @endcan +
+ + +@foreach ($album->children as $album) + @include (Theme::viewName('partials.single_album_admin'), ['is_child' => true]) +@endforeach \ No newline at end of file