]> BookStack Code Mirror - bookstack/commitdiff
Notifications: Added new preferences view and access control
authorDan Brown <redacted>
Mon, 14 Aug 2023 16:29:12 +0000 (17:29 +0100)
committerDan Brown <redacted>
Mon, 14 Aug 2023 16:29:12 +0000 (17:29 +0100)
- Added general user preferences view and updated link in profile menu
  to suit.
- Made notification permission required for notification preferences
  view, added test to cover.

app/Users/Controllers/UserPreferencesController.php
lang/en/common.php
lang/en/preferences.php
resources/icons/user-preferences.svg [new file with mode: 0644]
resources/views/common/header.blade.php
resources/views/users/preferences/index.blade.php [new file with mode: 0644]
resources/views/users/preferences/notifications.blade.php
routes/web.php
tests/User/UserPreferencesTest.php

index 999115e7b126592b81cf915514693cf91a73983e..d9ee50ca71396d11b18328e80c17b2ea23bffc18 100644 (file)
@@ -17,6 +17,14 @@ class UserPreferencesController extends Controller
     ) {
     }
 
+    /**
+     * Show the overview for user preferences.
+     */
+    public function index()
+    {
+        return view('users.preferences.index');
+    }
+
     /**
      * Show the user-specific interface shortcuts.
      */
@@ -53,6 +61,8 @@ class UserPreferencesController extends Controller
      */
     public function showNotifications(PermissionApplicator $permissions)
     {
+        $this->checkPermission('receive-notifications');
+
         $preferences = (new UserNotificationPreferences(user()));
 
         $query = Watch::query()->where('user_id', '=', user()->id);
@@ -70,6 +80,7 @@ class UserPreferencesController extends Controller
      */
     public function updateNotifications(Request $request)
     {
+        $this->checkPermission('receive-notifications');
         $data = $this->validate($request, [
            'preferences' => ['required', 'array'],
            'preferences.*' => ['required', 'string'],
index de7937b2be406a00fb344bb079b0773a77b5533c..47b74d5b6eb87a9aa912849b5affe0970f5f70a8 100644 (file)
@@ -42,6 +42,7 @@ return [
     'remove' => 'Remove',
     'add' => 'Add',
     'configure' => 'Configure',
+    'manage' => 'Manage',
     'fullscreen' => 'Fullscreen',
     'favourite' => 'Favourite',
     'unfavourite' => 'Unfavourite',
index 97968f8b125e6976f186aeaa6f2b4e01a26f57a0..118e8ba820f5dd6d4249832fcca696a44504e585 100644 (file)
@@ -5,6 +5,8 @@
  */
 
 return [
+    'preferences' => 'Preferences',
+
     'shortcuts' => 'Shortcuts',
     'shortcuts_interface' => 'Interface Keyboard Shortcuts',
     'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
@@ -15,6 +17,7 @@ return [
     'shortcuts_save' => 'Save Shortcuts',
     'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
     'shortcuts_update_success' => 'Shortcut preferences have been updated!',
+    'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
 
     'notifications' => 'Notification Preferences',
     'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
@@ -25,4 +28,6 @@ return [
     'notifications_update_success' => 'Notification preferences have been updated!',
     'notifications_watched' => 'Watched & Ignored Items',
     'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+    'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
 ];
diff --git a/resources/icons/user-preferences.svg b/resources/icons/user-preferences.svg
new file mode 100644 (file)
index 0000000..5ae1773
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 24 24"><g><g><circle cx="10" cy="8" r="4"/><path d="M10.67,13.02C10.45,13.01,10.23,13,10,13c-2.42,0-4.68,0.67-6.61,1.82C2.51,15.34,2,16.32,2,17.35V20h9.26 C10.47,18.87,10,17.49,10,16C10,14.93,10.25,13.93,10.67,13.02z"/><path d="M20.75,16c0-0.22-0.03-0.42-0.06-0.63l1.14-1.01l-1-1.73l-1.45,0.49c-0.32-0.27-0.68-0.48-1.08-0.63L18,11h-2l-0.3,1.49 c-0.4,0.15-0.76,0.36-1.08,0.63l-1.45-0.49l-1,1.73l1.14,1.01c-0.03,0.21-0.06,0.41-0.06,0.63s0.03,0.42,0.06,0.63l-1.14,1.01 l1,1.73l1.45-0.49c0.32,0.27,0.68,0.48,1.08,0.63L16,21h2l0.3-1.49c0.4-0.15,0.76-0.36,1.08-0.63l1.45,0.49l1-1.73l-1.14-1.01 C20.72,16.42,20.75,16.22,20.75,16z M17,18c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S18.1,18,17,18z"/></g></g></svg>
\ No newline at end of file
index a8b711595ad85dcce6d80ca59246515fd238f91a..97a411d84541684150f5b81bd4e6b6c8e9a8825f 100644 (file)
                         </li>
                         <li><hr></li>
                         <li>
-                            <a href="{{ url('/preferences/shortcuts') }}" class="icon-item">
-                                @icon('shortcuts')
-                                <div>{{ trans('preferences.shortcuts') }}</div>
+                            <a href="{{ url('/preferences') }}" class="icon-item">
+                                @icon('user-preferences')
+                                <div>{{ trans('preferences.preferences') }}</div>
                             </a>
                         </li>
                         <li>
diff --git a/resources/views/users/preferences/index.blade.php b/resources/views/users/preferences/index.blade.php
new file mode 100644 (file)
index 0000000..a79245a
--- /dev/null
@@ -0,0 +1,41 @@
+@extends('layouts.simple')
+
+@section('body')
+    <div class="container small my-xl">
+
+        <section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row">
+            <div>
+                <h2 class="list-heading">{{ trans('preferences.shortcuts_interface') }}</h2>
+                <p class="text-muted">{{ trans('preferences.shortcuts_overview_desc') }}</p>
+            </div>
+            <div class="text-right">
+                <a href="{{ url('/preferences/shortcuts') }}" class="button outline">{{ trans('common.manage') }}</a>
+            </div>
+        </section>
+
+        @if(userCan('receive-notifications'))
+            <section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row">
+                <div>
+                    <h2 class="list-heading">{{ trans('preferences.notifications') }}</h2>
+                    <p class="text-muted">{{ trans('preferences.notifications_desc') }}</p>
+                </div>
+                <div class="text-right">
+                    <a href="{{ url('/preferences/notifications') }}" class="button outline">{{ trans('common.manage') }}</a>
+                </div>
+            </section>
+        @endif
+
+        @if(signedInUser())
+            <section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row">
+                <div>
+                    <h2 class="list-heading">{{ trans('settings.users_edit_profile') }}</h2>
+                    <p class="text-muted">{{ trans('preferences.profile_overview_desc') }}</p>
+                </div>
+                <div class="text-right">
+                    <a href="{{ user()->getEditUrl() }}" class="button outline">{{ trans('common.manage') }}</a>
+                </div>
+            </section>
+        @endif
+
+    </div>
+@stop
index 3bbf78280e6d3c457342339b954b57e941c41e51..ae89c087e765dcce9a202cb9013d6cfc891f1a80 100644 (file)
                 <h1 class="list-heading">{{ trans('preferences.notifications') }}</h1>
                 <p class="text-small text-muted">{{ trans('preferences.notifications_desc') }}</p>
 
-                <div class="toggle-switch-list">
-                    <div>
-                        @include('form.toggle-switch', [
-                            'name' => 'preferences[own-page-changes]',
-                            'value' => $preferences->notifyOnOwnPageChanges(),
-                            'label' => trans('preferences.notifications_opt_own_page_changes'),
-                        ])
-                    </div>
-                    <div>
-                        @include('form.toggle-switch', [
-                            'name' => 'preferences[own-page-comments]',
-                            'value' => $preferences->notifyOnOwnPageComments(),
-                            'label' => trans('preferences.notifications_opt_own_page_comments'),
-                        ])
+                <div class="flex-container-row wrap justify-space-between pb-m">
+                    <div class="toggle-switch-list min-width-l">
+                        <div>
+                            @include('form.toggle-switch', [
+                                'name' => 'preferences[own-page-changes]',
+                                'value' => $preferences->notifyOnOwnPageChanges(),
+                                'label' => trans('preferences.notifications_opt_own_page_changes'),
+                            ])
+                        </div>
+                        <div>
+                            @include('form.toggle-switch', [
+                                'name' => 'preferences[own-page-comments]',
+                                'value' => $preferences->notifyOnOwnPageComments(),
+                                'label' => trans('preferences.notifications_opt_own_page_comments'),
+                            ])
+                        </div>
+                        <div>
+                            @include('form.toggle-switch', [
+                                'name' => 'preferences[comment-replies]',
+                                'value' => $preferences->notifyOnCommentReplies(),
+                                'label' => trans('preferences.notifications_opt_comment_replies'),
+                            ])
+                        </div>
                     </div>
-                    <div>
-                        @include('form.toggle-switch', [
-                            'name' => 'preferences[comment-replies]',
-                            'value' => $preferences->notifyOnCommentReplies(),
-                            'label' => trans('preferences.notifications_opt_comment_replies'),
-                        ])
+
+                    <div class="mt-auto">
+                        <button class="button">{{ trans('preferences.notifications_save') }}</button>
                     </div>
                 </div>
 
-                <div class="form-group text-right">
-                    <button class="button">{{ trans('preferences.notifications_save') }}</button>
-                </div>
             </form>
         </section>
 
index 27a54f8b486933ce70759083dfdda468fb63199a..c7fc92fc77da3bf5819dcc75cd558ce02756700d 100644 (file)
@@ -231,7 +231,7 @@ Route::middleware('auth')->group(function () {
     Route::delete('/settings/users/{id}', [UserControllers\UserController::class, 'destroy']);
 
     // User Preferences
-    Route::redirect('/preferences', '/');
+    Route::get('/preferences', [UserControllers\UserPreferencesController::class, 'index']);
     Route::get('/preferences/shortcuts', [UserControllers\UserPreferencesController::class, 'showShortcuts']);
     Route::put('/preferences/shortcuts', [UserControllers\UserPreferencesController::class, 'updateShortcuts']);
     Route::get('/preferences/notifications', [UserControllers\UserPreferencesController::class, 'showNotifications']);
index e83df57317b973a3da5cce1053f45b94f7cde2f0..a30484bd220d9fe4d68b5ff06aba2f13ac94f144 100644 (file)
@@ -6,6 +6,22 @@ use Tests\TestCase;
 
 class UserPreferencesTest extends TestCase
 {
+    public function test_index_view()
+    {
+        $resp = $this->asEditor()->get('/preferences');
+        $resp->assertOk();
+        $resp->assertSee('Interface Keyboard Shortcuts');
+        $resp->assertSee('Edit Profile');
+    }
+
+    public function test_index_view_accessible_but_without_profile_for_guest_user()
+    {
+        $this->setSettings(['app-public' => 'true']);
+        $resp = $this->get('/preferences');
+        $resp->assertOk();
+        $resp->assertSee('Interface Keyboard Shortcuts');
+        $resp->assertDontSee('Edit Profile');
+    }
     public function test_interface_shortcuts_updating()
     {
         $this->asEditor();
@@ -45,12 +61,28 @@ class UserPreferencesTest extends TestCase
         $this->withHtml($this->get('/'))->assertElementExists('body[component="shortcuts"]');
     }
 
+    public function test_notification_routes_requires_notification_permission()
+    {
+        $viewer = $this->users->viewer();
+        $resp = $this->actingAs($viewer)->get('/preferences/notifications');
+        $this->assertPermissionError($resp);
+
+        $resp = $this->put('/preferences/notifications');
+        $this->assertPermissionError($resp);
+
+        $this->permissions->grantUserRolePermissions($viewer, ['receive-notifications']);
+        $resp = $this->get('/preferences/notifications');
+        $resp->assertOk();
+        $resp->assertSee('Notification Preferences');
+    }
+
     public function test_notification_preferences_updating()
     {
-        $this->asEditor();
+        $editor = $this->users->editor();
+        $this->permissions->grantUserRolePermissions($editor, ['receive-notifications']);
 
         // View preferences with defaults
-        $resp = $this->get('/preferences/notifications');
+        $resp = $this->actingAs($editor)->get('/preferences/notifications');
         $resp->assertSee('Notification Preferences');
 
         $html = $this->withHtml($resp);
Morty Proxy This is a proxified and sanitized view of the page, visit original site.