diff options
Diffstat (limited to 'Pages')
| -rw-r--r-- | Pages/Component/AclActionSwitch.razor | 20 | ||||
| -rw-r--r-- | Pages/Component/AclActionSwitch.razor.css | 48 | ||||
| -rw-r--r-- | Pages/Component/AclDialog.razor | 95 | ||||
| -rw-r--r-- | Pages/Component/AclDialog.razor.css | 32 | ||||
| -rw-r--r-- | Pages/Component/Titlebar.razor | 2 | ||||
| -rw-r--r-- | Pages/ViewMedia.razor | 4 |
6 files changed, 195 insertions, 6 deletions
diff --git a/Pages/Component/AclActionSwitch.razor b/Pages/Component/AclActionSwitch.razor new file mode 100644 index 0000000..8bc61d2 --- /dev/null +++ b/Pages/Component/AclActionSwitch.razor @@ -0,0 +1,20 @@ +<label> + <input + type="checkbox" + checked=@InitialValue + @onchange=@(e => OnToggle.InvokeAsync((e.Value as bool?) ?? false)) + hidden> + <div class="outer"> + <p>Deny</p> + <p>Allow</p> + <div class="inner"/> + </div> +</label> + +@code { + [Parameter] + public bool InitialValue { get; set; } = false; + + [Parameter] + public EventCallback<bool> OnToggle { get; set; } +}
\ No newline at end of file diff --git a/Pages/Component/AclActionSwitch.razor.css b/Pages/Component/AclActionSwitch.razor.css new file mode 100644 index 0000000..e9de5a2 --- /dev/null +++ b/Pages/Component/AclActionSwitch.razor.css @@ -0,0 +1,48 @@ +div.outer { + align-items: center; + border-radius: 10px; + border: 1px solid var(--color-aclaction-deny); + cursor: pointer; + display: flex; + flex-direction: row; + height: 20px; + position: relative; + transition: border-color 0.1s linear; + user-select: none; + width: min-content; +} + +div.inner { + background: var(--color-aclaction-deny); + border-radius: 8px; + height: calc(100% - 2px); + left: 1px; + position: absolute; + top: 1px; + transition: left 0.1s linear, background 0.1s linear; + width: calc(50% - 2px); + z-index: 1; +} + +div.outer p { + color: white; + font-family: 'Trebuchet MS'; + font-size: 8pt; + text-align: center; + transition: color 0.1s linear; + width: 50px; + z-index: 2; +} + +input:checked + div.outer { + border-color: var(--color-aclaction-allow); +} + +input:checked + div.outer > div.inner { + background: var(--color-aclaction-allow); + left: calc(50% + 1px); +} + +input:checked + div.outer p:nth-child(2) { + color: black; +}
\ No newline at end of file diff --git a/Pages/Component/AclDialog.razor b/Pages/Component/AclDialog.razor index 33d1f03..8116f04 100644 --- a/Pages/Component/AclDialog.razor +++ b/Pages/Component/AclDialog.razor @@ -1,19 +1,104 @@ -@implements IDialog +@using System.Numerics; +@inject IDbContextFactory<HBContext> dbFactory; +@implements IDialog <Dialog Title="Edit permissions" @ref=dialog> + @if(obj?.Acl is not null) { + <table class="data-table"> + <tr> + <th>Action</th> + <th>Subject</th> + <th>Permissions</th> + <th></th> + </tr> + @foreach(var rule in obj.Acl.Rules.OrderByDescending(r => r.Action)) { + <tr> + <td><div><AclActionSwitch InitialValue=@(rule.Action == AclRuleAction.Allow)/></div></td> + <td>@rule.Principal.ToString()</td> + <td>@GetActivePermissions(rule)</td> + <td> + <a title="Edit" href="javascript:;">🖉</a> + <a title="Delete" href="javascript:;">✖</a> + </td> + </tr> + } + </table> + <br/> + <center><a href="javascript:;">Add new</a></center> + <ButtonContainer> + <button class="secondary" @onclick=Hide>Cancel</button> + <button data-keyboard-shortcut="a" @onclick=Hide><u>A</u>pply</button> + </ButtonContainer> + } else { + <center><i>This item does not have any permissions set!</i></center> + <ButtonContainer> + <button class="secondary" @onclick=Hide>Cancel</button> + </ButtonContainer> + } </Dialog> @code { - [Parameter] - public HBObject Object { get; set; } - public bool Visible { get => dialog.Visible; - set => dialog.Visible = value; + set { + dialog.Visible = value; + if(value) + StateHasChanged(); + } } + private HBObject? obj; + private Dialog dialog; public void Show() => Visible = true; public void Hide() => Visible = false; + + public void Show(HBObject obj) { + Object = obj; + Show(); + } + + public HBObject? Object { + get => obj; + set { + if(value is null) { + obj = null; + return; + } + + using var db = dbFactory.CreateDbContext(); + + obj = db.Objects + .Include(o => o.Acl) + .First(o => o.ObjectId == value.ObjectId); + + if(obj.Acl is not null) + db.Entry(obj.Acl).Collection(a => a.Rules).Load(); + } + } + + public string GetActivePermissions(AclRule rule) { + var perms = Acl.GetPermissionDescriptions(obj!) + .Where(kv => (rule.Permissions & kv.Value) == kv.Value) + .ToList(); + + // Filter the list of matching permissions to include the + // most relevant encapsulation permissions only. E.g. if + // 'Full access' includes 'Read' and 'Write', then only + // show 'Full access'. + List<int> toRemove = new(); + for(int i = 0; i < perms.Count(); i++) + for(int j = 0; j < perms.Count(); j++) + if(i != j) + if((perms[i].Value & perms[j].Value) == perms[i].Value) + toRemove.Add(i); + for(int i = toRemove.Count() - 1; i >= 0; i--) + perms.RemoveAt(toRemove[i]); + + return string.Join(", ", perms + .OrderByDescending(kv => BitOperations.PopCount(kv.Value)) + .ThenByDescending(kv => kv.Value) + .Select(kv => kv.Key)); + } }
\ No newline at end of file diff --git a/Pages/Component/AclDialog.razor.css b/Pages/Component/AclDialog.razor.css new file mode 100644 index 0000000..f1b7931 --- /dev/null +++ b/Pages/Component/AclDialog.razor.css @@ -0,0 +1,32 @@ +table p { + margin: 8px 0 8px 0; +} + +table tr { + background: none !important; +} + +table td { + font-family: 'Lucida Console'; + font-size: 8pt; + text-overflow: ellipsis; + white-space: nowrap; +} + +table td:last-child { + font-size: 12pt; +} + +table tr:nth-child(2n+1) td:not(:first-child) { + background: rgba(255, 255, 255, 0.1); +} + +table td:nth-child(2n) { + white-space: nowrap; + width: 1px; +} + +table td > div { + margin: auto; + width: min-content; +}
\ No newline at end of file diff --git a/Pages/Component/Titlebar.razor b/Pages/Component/Titlebar.razor index ad41532..766787a 100644 --- a/Pages/Component/Titlebar.razor +++ b/Pages/Component/Titlebar.razor @@ -74,5 +74,5 @@ private AboutDialog aboutDialog; - private string username => AuthState.GetAwaiter().GetResult().User.Identity?.Name ?? "fugg"; + private string username => AuthState.GetAwaiter().GetResult().User.Identity?.Name!; } diff --git a/Pages/ViewMedia.razor b/Pages/ViewMedia.razor index 444fbc5..05cf700 100644 --- a/Pages/ViewMedia.razor +++ b/Pages/ViewMedia.razor @@ -84,6 +84,7 @@ <div id="button-container"> <ButtonContainer> <button @onclick=@(() => deleteDialog.Show()) class="warning" data-keyboard-shortcut="d"><u>D</u>elete</button> + <button @onclick=@(() => aclDialog.Show(media)) class="secondary" data-keyboard-shortcut="p">Edit <u>P</u>ermissions</button> <button @onclick=@(() => tagDialog.Show()) class="secondary" data-keyboard-shortcut="t">Add <u>T</u>ag</button> <button @onclick=@(() => ocrDialog.Show()) class="secondary" data-keyboard-shortcut="o">View <u>O</u>CR</button> @if(infoEditMode) { @@ -125,6 +126,8 @@ OnSubmit=AddTags @ref=tagDialog/> +<AclDialog @ref=aclDialog/> + @code { [Parameter] [SupplyParameterFromQuery(Name = "m")] @@ -142,6 +145,7 @@ private Dialog deleteDialog; private Dialog ocrDialog; private TagSelectDialog tagDialog; + private AclDialog aclDialog; private ElementReference shortDescriptionInput; |
