diff --git a/app/Helpers/ConfigHelper.php b/app/Helpers/ConfigHelper.php index 4272bcb..ce0af5b 100644 --- a/app/Helpers/ConfigHelper.php +++ b/app/Helpers/ConfigHelper.php @@ -111,6 +111,7 @@ class ConfigHelper 'smtp_password' => '', 'smtp_port' => 25, 'smtp_username' => '', + 'social_user_profiles' => false, 'theme' => 'default' ); } diff --git a/app/Http/Controllers/Admin/DefaultController.php b/app/Http/Controllers/Admin/DefaultController.php index 9f43ac7..c031915 100644 --- a/app/Http/Controllers/Admin/DefaultController.php +++ b/app/Http/Controllers/Admin/DefaultController.php @@ -223,6 +223,7 @@ class DefaultController extends Controller 'require_email_verification', 'restrict_original_download', 'smtp_encryption', + 'social_user_profiles' ]; $updateKeys = [ 'albums_menu_number_items', diff --git a/app/Http/Controllers/Gallery/UserController.php b/app/Http/Controllers/Gallery/UserController.php new file mode 100644 index 0000000..31dc1dd --- /dev/null +++ b/app/Http/Controllers/Gallery/UserController.php @@ -0,0 +1,173 @@ +first(); + + if (is_null($user)) + { + App::abort(404); + return null; + } + + $this->authorizeForUser($this->getUser(), 'view', $user); + + $albums = $this->getAlbumsForUser($user); + $albumIDs = $this->getAlbumIDsForUser($user); + $cameras = $this->getCamerasUsedInAlbums($albumIDs); + $activity = $this->getActivityDatesInAlbums($albumIDs); + + return Theme::render('gallery.user_profile', [ + 'activity_taken' => $this->constructActivityGrid($activity['taken']), + 'activity_uploaded' => $this->constructActivityGrid($activity['uploaded']), + 'albums' => $albums, + 'cameras' => $cameras, + 'user' => $user + ]); + } + + private function constructActivityGrid(Collection $collection) + { + $results = []; + + $lastYearFrom = new \DateTime(); + $lastYearFrom->sub(new \DateInterval('P1Y')); + $lastYearFrom->add(new \DateInterval('P1D')); + + $today = new \DateTime(); + $current = clone $lastYearFrom; + + while ($current < $today) + { + $year = intval($current->format('Y')); + $month = intval($current->format('m')); + $date = intval($current->format('d')); + + if (!isset($results[$year])) + { + $results[$year] = []; + } + + if (!isset($results[$year][$month])) + { + $results[$year][$month] = []; + } + + if (!isset($results[$year][$month][$date])) + { + $results[$year][$month][$date] = 0; + } + + $current->add(new \DateInterval('P1D')); + } + + // Now update the totals from the collection + foreach ($collection as $photoInfo) + { + $date = \DateTime::createFromFormat('Y-m-d', $photoInfo->the_date); + + $year = intval($date->format('Y')); + $month = intval($date->format('m')); + $date = intval($date->format('d')); + + $results[$year][$month][$date] = $photoInfo->photos_count; + } + + // Replace the month names + foreach ($results as $year => &$months) + { + foreach ($months as $month => $dates) + { + $monthDate = \DateTime::createFromFormat('m', $month); + $months[$monthDate->format('M')] = $dates; + unset($months[$month]); + } + } + + return $results; + } + + private function getActivityDatesInAlbums(array $albumIDs) + { + $createdAt = DB::table('photos') + ->whereIn('album_id', $albumIDs) + ->whereRaw(DB::raw('DATE(created_at) > DATE(DATE_SUB(NOW(), INTERVAL 1 year))')) + ->select([ + DB::raw('DATE(created_at) AS the_date'), + DB::raw('COUNT(photos.id) AS photos_count') + ]) + ->groupBy(DB::raw('DATE(created_at)')) + ->orderBy(DB::raw('DATE(created_at)')) + ->get(); + + $takenAt = DB::table('photos') + ->whereIn('album_id', $albumIDs) + ->whereRaw(DB::raw('DATE(taken_at) > DATE(DATE_SUB(NOW(), INTERVAL 1 year))')) + ->select([ + DB::raw('DATE(taken_at) AS the_date'), + DB::raw('COUNT(photos.id) AS photos_count') + ]) + ->groupBy(DB::raw('DATE(taken_at)')) + ->orderBy(DB::raw('DATE(taken_at)')) + ->get(); + + return ['uploaded' => $createdAt, 'taken' => $takenAt]; + } + + private function getAlbumsForUser(User $user) + { + return DbHelper::getAlbumsForCurrentUser_NonPaged() + ->where('user_id', $user->id) + ->paginate(UserConfig::get('items_per_page')); + } + + private function getAlbumIDsForUser(User $user) + { + $results = []; + + $albums = DbHelper::getAlbumsForCurrentUser_NonPaged() + ->where('user_id', $user->id) + ->select('albums.id') + ->get(); + + foreach ($albums as $album) + { + $results[] = intval($album->id); + } + + return $results; + } + + private function getCamerasUsedInAlbums(array $albumIDs) + { + return DB::table('photos') + ->whereIn('album_id', $albumIDs) + ->where([ + ['camera_make', '!=', ''], + ['camera_model', '!=', ''] + ]) + ->groupBy('camera_make', 'camera_model', 'camera_software') + ->select('camera_make', 'camera_model', 'camera_software', DB::raw('count(*) as photo_count')) + ->orderBy('photo_count', 'desc') + ->orderBy('camera_make') + ->orderBy('camera_model') + ->orderBy('camera_software') + ->get(); + } +} \ No newline at end of file diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php new file mode 100644 index 0000000..5c79201 --- /dev/null +++ b/app/Policies/UserPolicy.php @@ -0,0 +1,37 @@ + AlbumPolicy::class, - Photo::class => PhotoPolicy::class + Photo::class => PhotoPolicy::class, + User::class => UserPolicy::class ]; /** diff --git a/app/User.php b/app/User.php index fb96d80..ad6af6b 100644 --- a/app/User.php +++ b/app/User.php @@ -50,6 +50,11 @@ class User extends Authenticatable : $user); } + public function albums() + { + return $this->hasMany(Album::class); + } + public function groups() { return $this->belongsToMany(Group::class, 'user_groups'); diff --git a/database/migrations/2018_07_16_041933_add_user_profile_columns.php b/database/migrations/2018_07_16_041933_add_user_profile_columns.php new file mode 100644 index 0000000..dc81513 --- /dev/null +++ b/database/migrations/2018_07_16_041933_add_user_profile_columns.php @@ -0,0 +1,36 @@ +boolean('enable_profile_page')->default(false); + $table->string('profile_alias')->nullable(true); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) + { + $table->dropColumn('enable_profile_page'); + $table->dropColumn('profile_alias'); + }); + } +} diff --git a/resources/assets/css/gallery.css b/resources/assets/css/gallery.css index 8d499c1..e17da91 100644 --- a/resources/assets/css/gallery.css +++ b/resources/assets/css/gallery.css @@ -1,3 +1,16 @@ +.activity-grid { + font-size: smaller; +} + +.activity-grid th,td { + padding: 5px !important; + text-align: center; +} + +.activity-grid td { + color: #fff; +} + .album-slideshow-container #image-preview { height: 600px; max-width: 100%; diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index 19bcbd6..665f129 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -210,7 +210,8 @@ return [ 'analytics_enable_visitor_hits_description' => 'Visitor hits to the public gallery will be recorded in the Blue Twilight database, allowing for analysis such as the most popular album/photo.', 'analytics_tab' => 'Analytics', 'security_allow_self_registration' => 'Allow self-registration', - 'security_allow_self_registration_description' => 'With this option enabled, users can sign up for their own accounts. You can grant permissions to accounts to allow users to upload their own photos or manage yours.' + 'security_allow_self_registration_description' => 'With this option enabled, users can sign up for their own accounts. You can grant permissions to accounts to allow users to upload their own photos or manage yours.', + 'social_tab' => 'Social' ], 'select_all_action' => 'Select all', 'select_all_album_active' => 'Any action you select in the list below will apply to all photos in this album.', diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php index 07f21b6..eaabb28 100644 --- a/resources/lang/en/forms.php +++ b/resources/lang/en/forms.php @@ -45,6 +45,8 @@ return [ '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_help' => 'With this option enabled, only the photo\'s owner can download the original high-resolution images.', + 'settings_social_user_profiles' => 'Enable public user profiles', + 'settings_social_user_profiles_help' => 'Display public pages for users showing their albums, cameras used and activity.', 'storage_access_key_label' => 'Access key:', 'storage_active_label' => 'Location is active. Uncheck to prevent creating new albums in this location.', 'storage_api_key_label' => 'API key:', diff --git a/resources/lang/en/gallery.php b/resources/lang/en/gallery.php index 4681e4b..eb5cf35 100644 --- a/resources/lang/en/gallery.php +++ b/resources/lang/en/gallery.php @@ -64,5 +64,15 @@ return [ ], 'title' => 'Statistics', 'uploaded_12_months' => 'Photos uploaded in the last 12 months', + ], + 'user_profile' => [ + 'activity' => 'Activity', + 'activity_summary' => ':count photo on :date|:count photos on :date', + 'activity_taken_p1' => 'Photos taken by :user_name:', + 'activity_uploaded_p1' => 'Photos uploaded by :user_name:', + 'albums' => 'Albums by :user_name', + 'cameras' => 'Cameras', + 'no_albums_p1' => 'No Photo Albums', + 'no_albums_p2' => ':user_name has not created any albums yet.' ] ]; \ No newline at end of file diff --git a/resources/views/themes/base/admin/settings.blade.php b/resources/views/themes/base/admin/settings.blade.php index df1ced9..faab460 100644 --- a/resources/views/themes/base/admin/settings.blade.php +++ b/resources/views/themes/base/admin/settings.blade.php @@ -23,6 +23,7 @@ @include(Theme::viewName('partials.tab'), ['active_tab' => 'general', 'tab_name' => 'email', 'tab_icon' => 'envelope', 'tab_text' => trans('admin.settings_email_tab')]) @include(Theme::viewName('partials.tab'), ['active_tab' => 'general', 'tab_name' => 'security', 'tab_icon' => 'lock', 'tab_text' => trans('admin.settings_security_tab')]) @include(Theme::viewName('partials.tab'), ['active_tab' => 'general', 'tab_name' => 'analytics', 'tab_icon' => 'line-chart', 'tab_text' => trans('admin.settings.analytics_tab')]) + @include(Theme::viewName('partials.tab'), ['active_tab' => 'general', 'tab_name' => 'social', 'tab_icon' => 'users', 'tab_text' => trans('admin.settings.social_tab')]) {{-- Tab panes --}} @@ -313,6 +314,17 @@ + + {{-- Social --}} +
+
+ + +
+
diff --git a/resources/views/themes/base/gallery/user_profile.blade.php b/resources/views/themes/base/gallery/user_profile.blade.php new file mode 100644 index 0000000..77aa1c5 --- /dev/null +++ b/resources/views/themes/base/gallery/user_profile.blade.php @@ -0,0 +1,95 @@ +@extends(Theme::viewName('layout')) +@section('title', $user->name) + +@section('breadcrumb') + + +@endsection + +@section('content') +
+
+
+ +
+ +
+

{{ $user->name }}

+ @if (!empty($user->profile_alias)) +

{{ $user->profile_alias }}

+ @endif +
+
+ +
+
+ @if (count($albums) == 0) +

@lang('gallery.user_profile.no_albums_p1')

+

@lang('gallery.user_profile.no_albums_p2', ['user_name' => $user->name])

+ @else +

@lang('gallery.user_profile.albums', ['user_name' => $user->name])

+ +
+ @foreach ($albums as $album) +
+
+ + + +
+
+ @endforeach +
+ @endif + +

@lang('gallery.user_profile.activity')

+

@lang('gallery.user_profile.activity_taken_p1', ['user_name' => $user->name])

+ @include (Theme::viewName('partials.user_profile_activity_grid'), ['activity' => $activity_taken]) + + @if (count($cameras) > 0) +

@lang('gallery.user_profile.cameras')

+
+ + + + + + + + + + + @foreach ($cameras as $camera) + + + + + + + @endforeach + +
@lang('admin.album_camera_make')@lang('admin.album_camera_model')@lang('admin.album_camera_software')@lang('admin.album_camera_photo_count')
{{ $camera->camera_make }}{{ $camera->camera_model }}{{ $camera->camera_software }}{{ $camera->photo_count }}
+
+ @endif +
+
+
+@endsection + +@push ('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/themes/base/partials/user_profile_activity_grid.blade.php b/resources/views/themes/base/partials/user_profile_activity_grid.blade.php new file mode 100644 index 0000000..41e479c --- /dev/null +++ b/resources/views/themes/base/partials/user_profile_activity_grid.blade.php @@ -0,0 +1,30 @@ + + + + + @foreach ($activity as $year => $months) + @foreach ($months as $month => $dates) + + @endforeach + @endforeach + + + + @for ($i = 1; $i <= 31; $i++) + + + @foreach ($activity as $year => $months) + @foreach ($months as $month => $dates) + @if (isset($dates[$i]) && $dates[$i] > 0) + + @elseif (isset($dates[$i]) && $dates[$i] == 0) + + @else + + @endif + @endforeach + @endforeach + + @endfor + +
{{ $month }}@if ($month == 'Jan')
{{ $year }}@endif
{{ $i }}{{ $dates[$i] }}  
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 72b79fd..f0357fe 100644 --- a/routes/web.php +++ b/routes/web.php @@ -103,4 +103,7 @@ Route::get('i/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@downloa ->where('albumUrlAlias', '.*'); Route::get('label/{labelAlias}', 'Gallery\LabelController@show') ->name('viewLabel') - ->where('labelAlias', '.*'); \ No newline at end of file + ->where('labelAlias', '.*'); +Route::get('user/{idOrAlias}', 'Gallery\UserController@show') + ->name('viewUser') + ->where('idOrAlias', '.*'); \ No newline at end of file