]> BookStack Code Mirror - bookstack/commitdiff
Added user-interface for "Everyone Else" entity permission item
authorDan Brown <redacted>
Sun, 2 Oct 2022 17:09:48 +0000 (18:09 +0100)
committerDan Brown <redacted>
Sun, 2 Oct 2022 17:09:48 +0000 (18:09 +0100)
Nothing on back-end logic done to hook this new option up.
Addition of permissions for role_id=0 works out of the box, but active
"everyone else" permissions, with no priviliges, is currently not
working. Needs change of permission gen logic also.

app/Auth/Role.php
app/Entities/Models/Entity.php
resources/icons/groups.svg [new file with mode: 0644]
resources/js/components/entity-permissions.js [new file with mode: 0644]
resources/js/components/index.js
resources/sass/_components.scss
resources/sass/_forms.scss
resources/views/form/custom-checkbox.blade.php
resources/views/form/entity-permissions-row.blade.php
resources/views/form/entity-permissions.blade.php

index 51b2ce301eae721fd1a7beb7e0d871f6c522836e..3ae469b59c10fb106d4f7364567101ce8e698cde 100644 (file)
@@ -120,6 +120,19 @@ class Role extends Model implements Loggable
             ->get();
     }
 
+    /**
+     * Get a role to represent the case of 'Everyone else' in the system.
+     * Used within the interface since the default-fallback for permissions uses role_id=0.
+     */
+    public static function getEveryoneElseRole(): self
+    {
+        return (new static())->forceFill([
+            'id' => 0,
+            'display_name' => 'Everyone Else',
+            'description'  => 'Set permissions for all roles not specifically overridden.'
+        ]);
+    }
+
     /**
      * {@inheritdoc}
      */
index 26a52073e016358f4f604c47eb3b2f0699fc88dd..3528eaf2becee478ab19b5c60f10d125f34a22f6 100644 (file)
@@ -184,8 +184,10 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
      */
     public function hasRestriction(int $role_id, string $action): bool
     {
-        return $this->permissions()->where('role_id', '=', $role_id)
-            ->where('action', '=', $action)->count() > 0;
+        return $this->permissions()
+                ->where('role_id', '=', $role_id)
+                ->where('action', '=', $action)
+                ->count() > 0;
     }
 
     /**
diff --git a/resources/icons/groups.svg b/resources/icons/groups.svg
new file mode 100644 (file)
index 0000000..c99a6b5
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px"><g><path d="M12,12.75c1.63,0,3.07,0.39,4.24,0.9c1.08,0.48,1.76,1.56,1.76,2.73L18,17c0,0.55-0.45,1-1,1H7c-0.55,0-1-0.45-1-1l0-0.61 c0-1.18,0.68-2.26,1.76-2.73C8.93,13.14,10.37,12.75,12,12.75z M4,13c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2 C2,12.1,2.9,13,4,13z M5.13,14.1C4.76,14.04,4.39,14,4,14c-0.99,0-1.93,0.21-2.78,0.58C0.48,14.9,0,15.62,0,16.43L0,17 c0,0.55,0.45,1,1,1l3.5,0v-1.61C4.5,15.56,4.73,14.78,5.13,14.1z M20,13c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2 C18,12.1,18.9,13,20,13z M24,16.43c0-0.81-0.48-1.53-1.22-1.85C21.93,14.21,20.99,14,20,14c-0.39,0-0.76,0.04-1.13,0.1 c0.4,0.68,0.63,1.46,0.63,2.29V18l3.5,0c0.55,0,1-0.45,1-1L24,16.43z M12,6c1.66,0,3,1.34,3,3c0,1.66-1.34,3-3,3s-3-1.34-3-3 C9,7.34,10.34,6,12,6z"/></g></svg>
\ No newline at end of file
diff --git a/resources/js/components/entity-permissions.js b/resources/js/components/entity-permissions.js
new file mode 100644 (file)
index 0000000..8b57d33
--- /dev/null
@@ -0,0 +1,24 @@
+
+
+class EntityPermissions {
+
+    setup() {
+        this.everyoneInheritToggle = this.$refs.everyoneInherit;
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.everyoneInheritToggle.addEventListener('change', event => {
+            const inherit = event.target.checked;
+            const permissions = document.querySelectorAll('input[type="checkbox"][name^="restrictions[0]["]');
+            for (const permission of permissions) {
+                permission.disabled = inherit;
+                permission.checked = false;
+            }
+        })
+    }
+
+}
+
+export default EntityPermissions;
\ No newline at end of file
index 4b17dc4039aca4524f9a7e856565f4ac43cc4864..7d00cb671130841ae9477f62d0af9f4a9df8d047 100644 (file)
@@ -18,6 +18,7 @@ import dropdown from "./dropdown.js"
 import dropdownSearch from "./dropdown-search.js"
 import dropzone from "./dropzone.js"
 import editorToolbox from "./editor-toolbox.js"
+import entityPermissions from "./entity-permissions";
 import entitySearch from "./entity-search.js"
 import entitySelector from "./entity-selector.js"
 import entitySelectorPopup from "./entity-selector-popup.js"
@@ -74,6 +75,7 @@ const componentMapping = {
     "dropdown-search": dropdownSearch,
     "dropzone": dropzone,
     "editor-toolbox": editorToolbox,
+    "entity-permissions": entityPermissions,
     "entity-search": entitySearch,
     "entity-selector": entitySelector,
     "entity-selector-popup": entitySelectorPopup,
index 9f737f3be483142123020998e15f1109987e0076..d0aadce6e47a5ec43eeeb845702b16f623ac08f7 100644 (file)
@@ -824,9 +824,6 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 .content-permissions-row:hover .content-permissions-row-toggle-all {
   visibility: visible;
 }
-.content-permissions-row-label {
-  font-weight: bold;
-}
 
 .template-item {
   cursor: pointer;
index 7025aa8984eda05f24188ce80fdcc7629e638fca..5c1c8b2e81b6f1e9b81ef191e290a65d375b3204 100644 (file)
@@ -266,6 +266,15 @@ input[type=color] {
     background-color: rgba(0, 0, 0, 0.05);
     opacity: 0.8;
   }
+  input[type=checkbox][disabled] ~ * {
+    opacity: 0.8;
+    cursor: not-allowed;
+  }
+  input[type=checkbox][disabled] ~ .custom-checkbox {
+    border-color: #999;
+    color: #999 !important;
+    background: #f2f2f2;
+  }
 }
 .toggle-switch-list {
   .toggle-switch {
index 2bf4e223201a5e04b8accd48b575f7b887f378c5..de3ffe922c13145c04a2e08c87d5ca80d33857b0 100644 (file)
@@ -5,7 +5,7 @@ $checked
 $label
 --}}
 <label custom-checkbox class="toggle-switch @if($errors->has($name)) text-neg @endif">
-    <input type="checkbox" name="{{$name}}" value="{{ $value }}" @if($checked) checked="checked" @endif>
+    <input type="checkbox" name="{{$name}}" value="{{ $value }}" @if($checked) checked="checked" @endif @if($disabled ?? false) disabled="disabled" @endif>
     <span tabindex="0" role="checkbox"
           aria-checked="{{ $checked ? 'true' : 'false' }}"
           class="custom-checkbox text-primary">@icon('check')</span>
index bff7315a0caf24dc3d1db3e7fe4ee949b72e51a6..f8c1dc1c7ac7a0f25c454ab47e12f42b25d398e8 100644 (file)
@@ -1,28 +1,46 @@
 <div component="permissions-table" class="content-permissions-row flex-container-row justify-space-between wrap">
-    <div class="content-permissions-row-label gap-x-m flex-container-row items-center px-l py-m flex">
-        <div class="text-large" title="{{ trans('common.role') }}">
-            @icon('role')
+    <div class="gap-x-m flex-container-row items-center px-l py-m flex">
+        <div class="text-large" title="{{ $role->id === 0 ? 'Everyone Else' : trans('common.role') }}">
+            @icon($role->id === 0 ? 'groups' : 'role')
         </div>
-        <span>{{ $role->display_name }}</span>
-        <button type="button"
+        <span>
+            <strong>{{ $role->display_name }}</strong> <br>
+            <small class="text-muted">{{ $role->description }}</small>
+        </span>
+        @if($role->id !== 0)
+            <button type="button"
                 class="ml-auto flex-none text-small text-primary text-button hover-underline content-permissions-row-toggle-all hide-under-s"
                 refs="permissions-table@toggle-all"
-                >{{ trans('common.toggle_all') }}</button>
+                ><strong>{{ trans('common.toggle_all') }}</strong></button>
+        @endif
     </div>
+    @php
+        $inheriting = ($role->id === 0 && !$model->restricted);
+    @endphp
+    @if($role->id === 0)
+        <div class="px-l flex-container-row items-center" refs="entity-permissions@everyoneInherit">
+            @include('form.custom-checkbox', [
+                'name' => 'entity-permissions-inherit',
+                'label' => 'Inherit defaults',
+                'value' => 'true',
+                'checked' => $inheriting
+            ])
+        </div>
+    @endif
     <div class="flex-container-row justify-space-between gap-x-xl wrap items-center">
         <div class="px-l">
-            @include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.view'), 'action' => 'view'])
+            @include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.view'), 'action' => 'view', 'disabled' => $inheriting])
         </div>
         <div class="px-l">
             @if(!$model instanceof \BookStack\Entities\Models\Page)
-                @include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.create'), 'action' => 'create'])
+                @include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.create'), 'action' => 'create', 'disabled' => $inheriting])
             @endif
         </div>
         <div class="px-l">
-            @include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.update'), 'action' => 'update'])
+            @include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.update'), 'action' => 'update', 'disabled' => $inheriting])
         </div>
         <div class="px-l">
-            @include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.delete'), 'action' => 'delete'])
+            @include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.delete'), 'action' => 'delete', 'disabled' => $inheriting])
         </div>
     </div>
 </div>
\ No newline at end of file
index 321e2f06cb072d79966ba679a51f7a386cddf510..408414b7651dcb650a175eff68aaf667e40bf21e 100644 (file)
@@ -1,4 +1,4 @@
-<form action="{{ $model->getUrl('/permissions') }}" method="POST">
+<form component="entity-permissions" action="{{ $model->getUrl('/permissions') }}" method="POST">
     {!! csrf_field() !!}
     <input type="hidden" name="_method" value="PUT">
 
         @endforeach
     </div>
 
+    <div class="content-permissions mt-m mb-xl">
+        @include('form.entity-permissions-row', ['role' => \BookStack\Auth\Role::getEveryoneElseRole(), 'model' => $model])
+    </div>
+
     <div class="text-right">
         <a href="{{ $model->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
         <button type="submit" class="button">{{ trans('entities.permissions_save') }}</button>