diff --git a/app/Actions/Settings/SetDefaultSettingsForUser.php b/app/Actions/Settings/SetDefaultSettingsForUser.php index 01fdae86..557bddcf 100644 --- a/app/Actions/Settings/SetDefaultSettingsForUser.php +++ b/app/Actions/Settings/SetDefaultSettingsForUser.php @@ -23,6 +23,8 @@ class SetDefaultSettingsForUser extends SettingsMigration $this->migrator->add($group . '.time_format', $defaults['time_format']); $this->migrator->add($group . '.locale', $defaults['locale']); + $this->migrator->add($group . '.profile_is_public', $defaults['profile_is_public']); + $this->migrator->add($group . '.links_default_visibility', $defaults['links_default_visibility']); $this->migrator->add($group . '.notes_default_visibility', $defaults['notes_default_visibility']); $this->migrator->add($group . '.lists_default_visibility', $defaults['lists_default_visibility']); diff --git a/app/Helper/functions.php b/app/Helper/functions.php index 4a1294bd..a853c4a1 100644 --- a/app/Helper/functions.php +++ b/app/Helper/functions.php @@ -30,15 +30,20 @@ function setupCompleted() /** * Shorthand for the current user settings * - * @param string $key + * @param string $key + * @param int|null $userId * @return mixed */ -function usersettings(string $key = ''): mixed +function usersettings(string $key = '', ?int $userId = null): mixed { - if (!auth()->user()) { + if (is_null($userId) && !auth()->user()) { return null; } + if (!is_null($userId)) { + app(UserSettings::class)::setUserId($userId); + } + if ($key === '') { return app(UserSettings::class)->toArray(); } diff --git a/app/Http/Controllers/App/UserSettingsController.php b/app/Http/Controllers/App/UserSettingsController.php index 37fda5be..a0bfdcf7 100644 --- a/app/Http/Controllers/App/UserSettingsController.php +++ b/app/Http/Controllers/App/UserSettingsController.php @@ -53,8 +53,6 @@ class UserSettingsController extends Controller */ public function saveAppSettings(UserSettings $settings, UserSettingsUpdateRequest $request): RedirectResponse { - $userId = $request->user()->id; - // Save all user settings or update them $newSettings = $request->except(['_token', 'share']); foreach ($newSettings as $key => $value) { diff --git a/app/Http/Controllers/Guest/UserController.php b/app/Http/Controllers/Guest/UserController.php new file mode 100644 index 00000000..2fb4c9a4 --- /dev/null +++ b/app/Http/Controllers/Guest/UserController.php @@ -0,0 +1,54 @@ +id) === false) { + abort(404); + } + + $links = Link::byUser($user->id) + ->whereVisibility(ModelAttribute::VISIBILITY_PUBLIC) + ->latest() + ->take(10) + ->paginate(pageName: 'link_page'); + + $lists = LinkList::byUser($user->id) + ->whereVisibility(ModelAttribute::VISIBILITY_PUBLIC) + ->latest() + ->take(10) + ->paginate(pageName: 'link_page'); + + $tags = Tag::byUser($user->id) + ->whereVisibility(ModelAttribute::VISIBILITY_PUBLIC) + ->latest() + ->take(10) + ->paginate(pageName: 'link_page'); + + $stats = [ + 'total_links' => Link::byUser($user->id)->whereVisibility(ModelAttribute::VISIBILITY_PUBLIC)->count(), + 'total_lists' => LinkList::byUser($user->id)->whereVisibility(ModelAttribute::VISIBILITY_PUBLIC)->count(), + 'total_tags' => Tag::byUser($user->id)->whereVisibility(ModelAttribute::VISIBILITY_PUBLIC)->count(), + 'total_notes' => Note::byUser($user->id)->whereVisibility(ModelAttribute::VISIBILITY_PUBLIC)->count(), + ]; + + return view('guest.users.show', [ + 'user' => $user, + 'links' => $links, + 'lists' => $lists, + 'tags' => $tags, + 'stats' => $stats, + ]); + } +} diff --git a/app/Settings/UserSettings.php b/app/Settings/UserSettings.php index fc3a5dbb..c90c8576 100644 --- a/app/Settings/UserSettings.php +++ b/app/Settings/UserSettings.php @@ -12,6 +12,8 @@ class UserSettings extends Settings public string $time_format; public string $locale; + public bool $profile_is_public; + public int $links_default_visibility; public int $notes_default_visibility; public int $lists_default_visibility; @@ -47,16 +49,23 @@ class UserSettings extends Settings public bool $share_whatsapp; public bool $share_xing; + private static int $user_id = 0; + public static function group(): string { - return 'user-' . auth()->id(); + return 'user-' . self::getUserId(); + } + + public static function setUserId(int $user_id): void + { + self::$user_id = $user_id; + } + + protected static function getUserId(): int + { + return self::$user_id ?: auth()->id(); } - /** - * Returns the default settings for users - * - * @return array{string: string|int|bool|null} - */ public static function defaults(): array { return [ @@ -64,6 +73,7 @@ class UserSettings extends Settings 'date_format' => config('linkace.default.date_format'), 'time_format' => config('linkace.default.time_format'), 'locale' => config('app.fallback_locale'), + 'profile_is_public' => false, 'links_default_visibility' => ModelAttribute::VISIBILITY_PUBLIC, 'notes_default_visibility' => ModelAttribute::VISIBILITY_PUBLIC, 'lists_default_visibility' => ModelAttribute::VISIBILITY_PUBLIC, diff --git a/database/settings/2022_06_22_124112_migrate_existing_settings.php b/database/settings/2022_06_22_124112_migrate_existing_settings.php index c57fd0ae..62f06741 100644 --- a/database/settings/2022_06_22_124112_migrate_existing_settings.php +++ b/database/settings/2022_06_22_124112_migrate_existing_settings.php @@ -79,6 +79,11 @@ class MigrateExistingSettings extends SettingsMigration $this->userSettings->get('locale', config('app.fallback_locale')) ); + $this->migrator->add( + 'user-1.profile_is_public', + (bool)$this->sysSettings->get('system_guest_access', false) + ); + $this->migrator->add( 'user-1.links_default_visibility', $this->userSettings->get('links_private_default', false) diff --git a/lang/en_US/settings.php b/lang/en_US/settings.php index c9b836cf..3c4b2dfa 100644 --- a/lang/en_US/settings.php +++ b/lang/en_US/settings.php @@ -18,7 +18,9 @@ return [ 'markdown_for_text' => 'Enable Markdown for descriptions and notes', 'privacy' => 'Privacy', - 'default_visibility_help' => 'Choose the default visibility for Links, Lists, Notes and Tags when adding new entries.', + 'profile_privacy' => 'The following settings apply to your user profile which is visible to guests.', + 'profile_is_public' => 'Profile is public', + 'default_visibility_help' => 'The following settings define the default visibility for Links, Lists, Notes and Tags when adding new ones.', 'links_default_visibility' => 'Default Links visibility', 'notes_default_visibility' => 'Default Notes visibility', 'lists_default_visibility' => 'Default Lists visibility', diff --git a/resources/views/app/settings/partials/app-settings/privacy.blade.php b/resources/views/app/settings/partials/app-settings/privacy.blade.php index 0f67a9d8..0fd4efd8 100644 --- a/resources/views/app/settings/partials/app-settings/privacy.blade.php +++ b/resources/views/app/settings/partials/app-settings/privacy.blade.php @@ -14,6 +14,33 @@ $settings = [ @lang('settings.privacy') +

@lang('settings.profile_privacy')

+ +
+
+ + + @if ($errors->has('profile_is_public')) + + @endif +
+
+ +

@lang('settings.default_visibility_help')

diff --git a/resources/views/guest/users/show.blade.php b/resources/views/guest/users/show.blade.php new file mode 100644 index 00000000..f9266ba1 --- /dev/null +++ b/resources/views/guest/users/show.blade.php @@ -0,0 +1,103 @@ +@extends('layouts.app') + +@section('content') + +
+
+ @lang('user.user') +
+
+

{{ $user->name }}

+
+
+ +
+
+ +
+
+ @lang('link.recent_links') +
+ + +
+ +
+
+ +
+
+ @lang('stats.stats') +
+ +
    +
  • + @lang('stats.total_links') + {{ $stats['total_links'] }} +
  • +
  • + @lang('stats.total_lists') + {{ $stats['total_lists'] }} +
  • +
  • + @lang('stats.total_tags') + {{ $stats['total_tags'] }} +
  • +
  • + @lang('stats.total_notes') + {{ $stats['total_notes'] }} +
  • +
+
+ +
+
+ @lang('list.recent_lists') +
+ +
+ @forelse($lists as $list) + + {{ $list->name }} + + @empty +
+ @lang('linkace.no_results_found', ['model' => trans('list.lists')]) +
+ @endforelse +
+
+ +
+
+ @lang('tag.recent_tags') +
+ +
+ @forelse($tags as $tag) + + {{ $tag->name }} + + @empty +
+ @lang('linkace.no_results_found', ['model' => trans('tag.tags')]) +
+ @endforelse +
+
+ +
+
+ +@endsection diff --git a/resources/views/models/users/show.blade.php b/resources/views/models/users/show.blade.php index adbc32b2..20736dc5 100644 --- a/resources/views/models/users/show.blade.php +++ b/resources/views/models/users/show.blade.php @@ -62,12 +62,6 @@
- - - -
-
-
@lang('list.recent_lists') @@ -75,7 +69,7 @@
@forelse($lists as $list) - + {{ $list->name }} @empty @@ -86,9 +80,6 @@
-
-
-
@lang('tag.recent_tags') @@ -96,7 +87,7 @@
@forelse($tags as $tag) - + {{ $tag->name }} @empty diff --git a/routes/web.php b/routes/web.php index 5cbf47f3..1407682e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -18,6 +18,7 @@ use App\Http\Controllers\Guest\FeedController as GuestFeedController; use App\Http\Controllers\Guest\LinkController as GuestLinkController; use App\Http\Controllers\Guest\ListController as GuestListController; use App\Http\Controllers\Guest\TagController as GuestTagController; +use App\Http\Controllers\Guest\UserController as GuestUserController; use App\Http\Controllers\Models\LinkController; use App\Http\Controllers\Models\ListController; use App\Http\Controllers\Models\NoteController; @@ -180,6 +181,8 @@ Route::prefix('guest')->middleware(['guestaccess'])->group(function () { Route::get('tags/feed', [GuestFeedController::class, 'tags'])->name('guest.tags.feed'); Route::get('tags/{tag}/feed', [GuestFeedController::class, 'tagLinks'])->name('guest.tags.links.feed'); + Route::get('users/{user:name}', [GuestUserController::class, 'show'])->name('guest.users.show'); + Route::resource('links', GuestLinkController::class) ->only(['index']) ->names([ diff --git a/tests/Controller/App/FeedControllerTest.php b/tests/Controller/App/FeedControllerTest.php index 07344255..47ddd97d 100644 --- a/tests/Controller/App/FeedControllerTest.php +++ b/tests/Controller/App/FeedControllerTest.php @@ -14,7 +14,7 @@ class FeedControllerTest extends TestCase { use RefreshDatabase; - private $user; + private User $user; protected function setUp(): void { @@ -82,7 +82,7 @@ class FeedControllerTest extends TestCase $tagLink = Link::factory()->create(); $unrelatedLink = Link::factory()->create(); - $tagLink->tags()->sync(['tag' => $tag]); + $tagLink->tags()->sync([$tag->id]); $response = $this->getAuthorized('tags/1/feed'); diff --git a/tests/Controller/Guest/UserControllerTest.php b/tests/Controller/Guest/UserControllerTest.php new file mode 100644 index 00000000..0cc11bd3 --- /dev/null +++ b/tests/Controller/Guest/UserControllerTest.php @@ -0,0 +1,68 @@ + true, 'guest_access_enabled' => true]); + + User::factory()->create(['name' => 'MrTestUser']); + + Link::factory()->create(['url' => 'https://public.com', 'visibility' => ModelAttribute::VISIBILITY_PUBLIC]); + Link::factory()->create(['url' => 'https://internal.com', 'visibility' => ModelAttribute::VISIBILITY_INTERNAL]); + Link::factory()->create(['url' => 'https://private.com', 'visibility' => ModelAttribute::VISIBILITY_PRIVATE]); + + LinkList::factory()->create(['name' => 'Public List', 'visibility' => ModelAttribute::VISIBILITY_PUBLIC]); + LinkList::factory()->create(['name' => 'Internal List', 'visibility' => ModelAttribute::VISIBILITY_INTERNAL]); + LinkList::factory()->create(['name' => 'Private List', 'visibility' => ModelAttribute::VISIBILITY_PRIVATE]); + + Tag::factory()->create(['name' => 'Public Tag', 'visibility' => ModelAttribute::VISIBILITY_PUBLIC]); + Tag::factory()->create(['name' => 'Internal Tag', 'visibility' => ModelAttribute::VISIBILITY_INTERNAL]); + Tag::factory()->create(['name' => 'Private Tag', 'visibility' => ModelAttribute::VISIBILITY_PRIVATE]); + + UserSettings::setUserId(1); + UserSettings::fake([ + 'profile_is_public' => true, + ]); + + $this->get('guest/users/MrTestUser') + ->assertOk() + ->assertSee('MrTestUser') + ->assertSee('https://public.com') + ->assertDontSee('https://internal.com') + ->assertDontSee('https://private.com') + ->assertSee('Public List') + ->assertDontSee('Internal List') + ->assertDontSee('Private List') + ->assertSee('Public Tag') + ->assertDontSee('Internal Tag') + ->assertDontSee('Private Tag'); + } + + public function testPrivateUserProfile(): void + { + SystemSettings::fake(['setup_completed' => true, 'guest_access_enabled' => true]); + + User::factory()->create(['name' => 'MrPrivateUser']); + + UserSettings::fake([ + 'profile_is_public' => false, + ]); + + $this->get('guest/user/MrPrivateUser')->assertNotFound(); + } +}