Release v2.4.2

This commit is contained in:
2026-08-03 12:45:27 +01:00
parent 2b9cabdab2
commit e95928f31c
141 changed files with 7694 additions and 5078 deletions
-82
View File
@@ -1,82 +0,0 @@
<div class="page-header">
<div>
<h2>Create role</h2>
<p>Define a new role and choose its permissions before saving.</p>
</div>
</div>
<form method="post" action="/rbac" id="role-create-form" data-async-save data-async-save-close-url="/rbac">
<div class="row g-4">
<div class="col-12 col-xl-4 d-flex flex-column gap-4">
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
<h3 class="card-title">Role details</h3>
</div>
<div class="card-body">
<div class="mb-3">
<label for="role-name" class="form-label">Role name</label>
<input id="role-name" name="name" class="form-control" value="{{formValues.name}}" required />
</div>
<div class="mb-0">
<label for="role-description" class="form-label">Description</label>
<textarea id="role-description" name="description" class="form-control" rows="5">{{formValues.description}}</textarea>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-8 d-flex flex-column gap-4">
<div class="card card-outline card-secondary admin-form-card">
<div class="card-header">
<h3 class="card-title">Permissions</h3>
</div>
<div class="card-body">
<p class="text-body-secondary mb-3">Pick the permissions this role should have now.</p>
{{#if permissionGroups.length}}
<div class="accordion" id="role-create-permissions-accordion">
{{#each permissionGroups}}
<div class="accordion-item">
<h2 class="accordion-header" id="create-heading-{{id}}">
<button class="accordion-button {{#unless @first}}collapsed{{/unless}}" type="button" data-bs-toggle="collapse" data-bs-target="#create-collapse-{{id}}" aria-expanded="{{#if @first}}true{{else}}false{{/if}}" aria-controls="create-collapse-{{id}}">
<span>{{title}}</span>
{{#if categoryName}}
<span class="badge text-bg-secondary ms-2">{{categoryName}}</span>
{{/if}}
</button>
</h2>
<div id="create-collapse-{{id}}" class="accordion-collapse collapse {{#if @first}}show{{/if}}" aria-labelledby="create-heading-{{id}}" data-bs-parent="#role-create-permissions-accordion">
<div class="accordion-body">
<div class="row g-3">
{{#each permissions}}
<div class="col-12 col-md-6">
<label class="form-check card card-outline card-secondary position-relative p-3 h-100 mb-0">
<input class="form-check-input position-absolute top-0 end-0 m-3" type="checkbox" name="permission_keys[]" value="{{permission_key}}" data-permission-key="{{permission_key}}" {{#if isSelected}}checked{{/if}} />
<span class="form-check-label pe-4">
<strong>{{actionLabel}}</strong>
<span class="d-block text-body-secondary small">{{#if description}}{{description}}{{else}}No description provided.{{/if}}</span>
</span>
</label>
</div>
{{/each}}
</div>
</div>
</div>
</div>
{{/each}}
</div>
{{else}}
<div class="alert alert-warning mb-0">No permissions are available.</div>
{{/if}}
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end mt-4">
<div class="btn-group" role="group" aria-label="Role actions">
{{{saveActionButtons formId="role-create-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
<a class="btn btn-warning" href="/rbac" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
<button type="submit" class="btn btn-outline-danger" disabled>Delete</button>
</div>
</div>
</form>
@@ -1,15 +1,14 @@
<div class="page-header">
<div>
<h2>Edit role - {{role.name}}</h2>
<p>Choose which permissions and users this role receives.</p>
<h2>{{#if isEdit}}Edit role - {{role.name}}{{else}}Create role{{/if}}</h2>
<p>{{#if isEdit}}Choose which permissions and users this role receives.{{else}}Define a new role and choose its permissions and users before saving.{{/if}}</p>
</div>
</div>
<form method="post" action="/rbac/{{role.id}}" id="role-edit-form" data-async-save data-async-save-close-url="/rbac">
<input type="hidden" name="permissions_present" value="1" />
<form id="{{formId}}" method="post" action="{{formAction}}" {{{formAttrs}}}>
<input type="hidden" name="users_present" value="1" />
<div class="row g-4">
<div class="row g-3">
<div class="col-12 col-xl-4 d-flex flex-column gap-4">
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
@@ -17,23 +16,17 @@
</div>
<div class="card-body">
<div class="mb-3">
<label for="role-name-edit" class="form-label">Role Name</label>
<input id="role-name-edit" name="name" class="form-control" value="{{role.name}}" required />
<label for="{{#if isEdit}}role-name-edit{{else}}role-name{{/if}}" class="form-label">Role name</label>
<input id="{{#if isEdit}}role-name-edit{{else}}role-name{{/if}}" name="name" class="form-control" value="{{#if isEdit}}{{role.name}}{{else}}{{formValues.name}}{{/if}}" required />
</div>
<div class="mb-0">
<label for="role-description-edit" class="form-label">Description</label>
<textarea id="role-description-edit" name="description" class="form-control" rows="5">{{role.description}}</textarea>
<label for="{{#if isEdit}}role-description-edit{{else}}role-description{{/if}}" class="form-label">Description</label>
<textarea id="{{#if isEdit}}role-description-edit{{else}}role-description{{/if}}" name="description" class="form-control" rows="5">{{#if isEdit}}{{role.description}}{{else}}{{formValues.description}}{{/if}}</textarea>
</div>
</div>
<div class="card-footer d-flex justify-content-end flex-wrap">
<div class="btn-group" role="group" aria-label="Role actions">
{{{saveActionButtons formId="role-edit-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
<a class="btn btn-warning" href="/rbac" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
{{#if inUse}}
<button type="submit" class="btn btn-outline-danger" form="role-delete-form" disabled>Delete</button>
{{else}}
<button type="submit" class="btn btn-danger" form="role-delete-form">Delete</button>
{{/if}}
{{{saveActionButtons formId=formId saveUrl=footerSaveUrl cancelUrl=footerCancelUrl deleteUrl=footerDeleteUrl deleteDisabled=footerDeleteDisabled deleteTitle=footerDeleteTitle showSaveAndClose=false showSaveAndNew=false showDelete=footerShowDelete}}}
</div>
</div>
</div>
@@ -56,7 +49,7 @@
<th style="width: 1%" scope="col" data-sortable="false">Select</th>
<th scope="col" data-table-sort-key="user">Username</th>
<th scope="col" data-table-sort-key="name">Name</th>
<th scope="col" data-table-sort-key="roles">Roles</th>
{{!-- <th scope="col" data-table-sort-key="roles">Roles</th> --}}
</tr>
</thead>
<tbody>
@@ -72,7 +65,7 @@
</div>
</td>
<td data-label="Name">{{#if name}}{{name}}{{else}}-{{/if}}</td>
<td data-label="Roles" class="text-body-secondary">{{#if roleNames}}{{roleNames}}{{else}}No roles assigned{{/if}}</td>
{{!-- <td data-label="Roles" class="text-body-secondary">{{#if roleNames}}{{roleNames}}{{else}}No roles assigned{{/if}}</td> --}}
</tr>
{{/each}}
{{else}}
@@ -91,20 +84,20 @@
<h3 class="card-title">Permissions</h3>
</div>
<div class="card-body">
<p class="text-body-secondary mb-3">Each section below is a resource. Pick the actions this role should have for that resource.</p>
<p class="text-body-secondary mb-3">{{#if isEdit}}Each section below is a resource. Pick the actions this role should have for that resource.{{else}}Each section below is a resource. Pick the actions this role should have for that resource.{{/if}}</p>
{{#if permissionGroups.length}}
<div class="accordion" id="role-permissions-accordion">
<div class="accordion" id="{{#if isEdit}}role-permissions-accordion{{else}}role-create-permissions-accordion{{/if}}">
{{#each permissionGroups}}
<div class="accordion-item">
<h2 class="accordion-header" id="heading-{{id}}">
<button class="accordion-button {{#unless @first}}collapsed{{/unless}}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-{{id}}" aria-expanded="{{#if @first}}true{{else}}false{{/if}}" aria-controls="collapse-{{id}}">
<h2 class="accordion-header" id="{{#if ../isEdit}}heading-{{id}}{{else}}create-heading-{{id}}{{/if}}">
<button class="accordion-button {{#unless @first}}collapsed{{/unless}}" type="button" data-bs-toggle="collapse" data-bs-target="#{{#if ../isEdit}}collapse-{{id}}{{else}}create-collapse-{{id}}{{/if}}" aria-expanded="{{#if @first}}true{{else}}false{{/if}}" aria-controls="{{#if ../isEdit}}collapse-{{id}}{{else}}create-collapse-{{id}}{{/if}}">
<span>{{title}}</span>
{{#if categoryName}}
<span class="badge text-bg-secondary ms-2">{{categoryName}}</span>
{{/if}}
</button>
</h2>
<div id="collapse-{{id}}" class="accordion-collapse collapse {{#if @first}}show{{/if}}" aria-labelledby="heading-{{id}}" data-bs-parent="#role-permissions-accordion">
<div id="{{#if ../isEdit}}collapse-{{id}}{{else}}create-collapse-{{id}}{{/if}}" class="accordion-collapse collapse {{#if @first}}show{{/if}}" aria-labelledby="{{#if ../isEdit}}heading-{{id}}{{else}}create-heading-{{id}}{{/if}}" data-bs-parent="#{{#if ../isEdit}}role-permissions-accordion{{else}}role-create-permissions-accordion{{/if}}">
<div class="accordion-body">
<div class="row g-3">
{{#each permissions}}
@@ -128,14 +121,7 @@
<div class="alert alert-warning mb-0">No permissions are available.</div>
{{/if}}
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Permission actions">
{{{saveActionButtons formId="role-edit-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
</div>
</div>
</div>
</div>
</div>
</form>
<form method="post" action="/rbac/{{role.id}}/delete" id="role-delete-form" class="d-none" data-confirm-message="Delete {{role.name}}?"></form>
</form>
-75
View File
@@ -1,75 +0,0 @@
<div class="page-header">
<div>
<h2>Add user</h2>
<p>Create a new login for the web UI.</p>
</div>
</div>
<form id="user-form" method="post" action="/users">
<div class="row g-4">
<div class="col-12 col-xl-4">
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
<h3 class="card-title">User details</h3>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12 col-md-6">
<label for="user-username" class="form-label">Username</label>
<input id="user-username" type="text" name="username" class="form-control" autocomplete="username" value="{{formValues.username}}" required />
</div>
<div class="col-12 col-md-6">
<label for="user-name" class="form-label">Name</label>
<input id="user-name" type="text" name="name" class="form-control" autocomplete="name" value="{{formValues.name}}" required />
</div>
<div class="col-12 col-md-6">
<label for="user-password" class="form-label">Password</label>
<input id="user-password" type="password" name="password" class="form-control" autocomplete="new-password" minlength="8" required />
</div>
<div class="col-12 col-md-6">
<label for="user-confirm-password" class="form-label">Confirm password</label>
<input id="user-confirm-password" type="password" name="confirm_password" class="form-control" autocomplete="new-password" minlength="8" required />
</div>
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="User actions">
{{{saveActionButtons formId="user-form" saveLabel="Save" showSaveAndClose=false showSaveAndNew=false}}}
<a class="btn btn-warning" href="/users" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
<button type="submit" class="btn btn-outline-danger" disabled>Delete</button>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-8">
<div class="card card-outline card-secondary admin-form-card">
<div class="card-header">
<h3 class="card-title">Roles</h3>
</div>
<div class="card-body">
<div class="row g-3">
{{#if roles.length}}
{{#each roles}}
<div class="col-4">
<label class="form-check card card-outline card-secondary position-relative p-3 h-100 mb-0">
<input class="form-check-input position-absolute top-0 end-0 m-3" type="checkbox" name="role_ids[]" value="{{id}}" {{#if isSelected}}checked{{/if}} />
<span class="form-check-label pe-4">
<strong>{{name}}</strong>
<span class="d-block text-body-secondary small">{{#if description}}{{description}}{{else}}No description provided.{{/if}}</span>
</span>
</label>
</div>
{{/each}}
{{else}}
<div class="col-12">
<div class="alert alert-warning mb-0">Create at least one role before adding users.</div>
</div>
{{/if}}
</div>
<div class="form-text mt-2">Select at least one role so the new user can sign in with access.</div>
</div>
</div>
</div>
</div>
</form>
-102
View File
@@ -1,102 +0,0 @@
<div class="page-header">
<div>
<h2>Edit user - {{user.name}}</h2>
<p>Update the name or password for {{user.username}}.</p>
</div>
</div>
<div class="row g-4">
<div class="col-12 col-xl-6">
<div class="card card-outline card-secondary admin-form-card mb-0">
<div class="card-header">
<h3 class="card-title">Profile</h3>
</div>
<form id="user-profile-form" method="post" action="/users/{{user.id}}/username" data-async-save data-async-save-close-url="/users">
<div class="card-body">
<div class="mb-3">
<label for="edit-user-username" class="form-label">Username</label>
<input id="edit-user-username" type="text" name="username" value="{{user.username}}" class="form-control" autocomplete="username" required disabled />
</div>
<div class="mb-0">
<label for="edit-user-name" class="form-label">Name</label>
<input id="edit-user-name" type="text" name="name" value="{{user.name}}" class="form-control" autocomplete="name" required />
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Profile actions">
{{{saveActionButtons formId="user-profile-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
<a class="btn btn-warning" href="/users" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
{{#if inUse}}
<button type="submit" class="btn btn-outline-danger" form="delete-user-form" disabled>Delete</button>
{{else}}
<button type="submit" class="btn btn-danger" form="delete-user-form">Delete</button>
{{/if}}
</div>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="card card-outline card-warning admin-form-card h-100 mb-0">
<div class="card-header">
<h3 class="card-title">Reset password</h3>
</div>
<form id="user-password-form" method="post" action="/users/{{user.id}}/password" data-async-save data-async-save-close-url="/users">
<div class="card-body">
<div class="mb-3">
<label for="edit-user-password" class="form-label">New password</label>
<input id="edit-user-password" type="password" name="password" class="form-control" autocomplete="new-password" minlength="8" required />
</div>
<div class="mb-0">
<label for="edit-user-confirm-password" class="form-label">Confirm password</label>
<input id="edit-user-confirm-password" type="password" name="confirm_password" class="form-control" autocomplete="new-password" minlength="8" required />
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Password actions">
{{{saveActionButtons formId="user-password-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
</div>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="card card-outline card-secondary admin-form-card h-100 mb-0">
<div class="card-header">
<h3 class="card-title">Roles</h3>
</div>
<form id="user-roles-form" method="post" action="/users/{{user.id}}/roles" data-async-save data-async-save-close-url="/users">
<div class="card-body">
<div class="row g-3">
{{#if roles.length}}
{{#each roles}}
<div class="col-12 col-md-6">
<label class="form-check card card-outline card-secondary p-3 h-100 mb-0">
<input class="form-check-input" type="checkbox" name="role_ids[]" value="{{id}}" {{#if isSelected}}checked{{/if}} />
<span class="form-check-label ms-2">
<strong>{{name}}</strong>
<span class="d-block text-body-secondary small">{{#if description}}{{description}}{{else}}No description provided.{{/if}}</span>
</span>
</label>
</div>
{{/each}}
{{else}}
<div class="col-12">
<div class="alert alert-warning mb-0">Create at least one user before assigning this role.</div>
</div>
{{/if}}
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Role actions">
{{{saveActionButtons formId="user-roles-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
</div>
</div>
</form>
</div>
</div>
</div>
<form id="delete-user-form" method="post" action="/users/{{user.id}}/delete" class="d-none" data-confirm-message="Delete {{user.username}}?"></form>
+87
View File
@@ -0,0 +1,87 @@
<div class="page-header">
<div>
<h2>{{#if isEdit}}Edit user - {{user.name}}{{else}}Add user{{/if}}</h2>
<p>{{#if isEdit}}Update the user's login details and roles.{{else}}Create a new login for the web UI.{{/if}}</p>
</div>
</div>
<form id="{{formId}}" method="post" action="{{formAction}}" {{{formAttrs}}}>
<div class="row g-3">
<div class="col-12 col-xl-4">
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
<h3 class="card-title">{{#if isEdit}}Profile{{else}}User details{{/if}}</h3>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12 col-md-6">
<label for="{{#if isEdit}}edit-user-username{{else}}user-username{{/if}}" class="form-label">Username</label>
<input id="{{#if isEdit}}edit-user-username{{else}}user-username{{/if}}" type="text" name="username" class="form-control" autocomplete="username" value="{{#if isEdit}}{{user.username}}{{else}}{{formValues.username}}{{/if}}" {{#if isEdit}}disabled{{else}}required{{/if}} />
</div>
<div class="col-12 col-md-6">
<label for="{{#if isEdit}}edit-user-name{{else}}user-name{{/if}}" class="form-label">Name</label>
<input id="{{#if isEdit}}edit-user-name{{else}}user-name{{/if}}" type="text" name="name" class="form-control" autocomplete="name" value="{{#if isEdit}}{{user.name}}{{else}}{{formValues.name}}{{/if}}" required />
</div>
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="{{#if isEdit}}Profile actions{{else}}User actions{{/if}}">
{{{saveActionButtons formId=formId saveUrl=footerSaveUrl cancelUrl=footerCancelUrl deleteUrl=footerDeleteUrl deleteDisabled=footerDeleteDisabled deleteTitle=footerDeleteTitle showSaveAndClose=false showSaveAndNew=false showDelete=footerShowDelete}}}
</div>
</div>
</div>
<div class="card card-outline card-warning admin-form-card mt-3">
<div class="card-header">
<h3 class="card-title">{{#if isEdit}}Reset password{{else}}Password{{/if}}</h3>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12 col-md-6">
<label for="{{#if isEdit}}edit-user-password{{else}}user-password{{/if}}" class="form-label">Password</label>
<input id="{{#if isEdit}}edit-user-password{{else}}user-password{{/if}}" type="password" name="password" class="form-control" autocomplete="new-password" minlength="8" {{#unless isEdit}}required{{/unless}} />
</div>
<div class="col-12 col-md-6">
<label for="{{#if isEdit}}edit-user-confirm-password{{else}}user-confirm-password{{/if}}" class="form-label">Confirm password</label>
<input id="{{#if isEdit}}edit-user-confirm-password{{else}}user-confirm-password{{/if}}" type="password" name="confirm_password" class="form-control" autocomplete="new-password" minlength="8" {{#unless isEdit}}required{{/unless}} />
</div>
</div>
{{#if isEdit}}
<div class="form-text mt-2">Leave both fields blank to keep the current password.</div>
{{/if}}
</div>
</div>
</div>
<div class="col-12 col-xl-8">
<div class="card card-outline card-secondary admin-form-card">
<div class="card-header">
<h3 class="card-title">Roles</h3>
</div>
<div class="card-body">
<div class="row g-3">
{{#if roles.length}}
{{#each roles}}
<div class="col-12 col-md-6 col-xl-4">
<label class="form-check card card-outline card-secondary position-relative p-3 h-100 mb-0">
<input class="form-check-input position-absolute top-0 end-0 m-3" type="checkbox" name="role_ids[]" value="{{id}}" {{#if isSelected}}checked{{/if}} />
<span class="form-check-label pe-4">
<strong>{{name}}</strong>
<span class="d-block text-body-secondary small">{{#if description}}{{description}}{{else}}No description provided.{{/if}}</span>
</span>
</label>
</div>
{{/each}}
{{else}}
<div class="col-12">
<div class="alert alert-warning mb-0">{{#if isEdit}}Create at least one role before assigning this role.{{else}}Create at least one role before adding users.{{/if}}</div>
</div>
{{/if}}
</div>
<div class="form-text mt-2">{{#if isEdit}}Select at least one role so the user can sign in with access.{{else}}Select at least one role so the new user can sign in with access.{{/if}}</div>
</div>
</div>
</div>
</div>
</form>