Skip to content

Commit a0a5cbe

Browse files
committed
feat(users): add user edit view & roles management
* Implemented user edit view for role assignment. * Added templates for user details and edit views. * Updated user management routes and tests.
1 parent c4ecb17 commit a0a5cbe

File tree

6 files changed

+518
-7
lines changed

6 files changed

+518
-7
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{#
2+
Copyright (C) 2025 CERN.
3+
4+
Invenio App RDM is free software; you can redistribute it and/or modify it
5+
under the terms of the MIT License; see LICENSE file for more details.
6+
#}
7+
8+
{% extends "invenio_administration/details.html" %}
9+
10+
{% block page_title %}
11+
<div class="rel-mb-2">
12+
<a href="{{ list_ui_endpoint or url_for('administration.users') }}"
13+
class="ui mini button labeled icon rel-mr-1">
14+
<i class="ui icon left arrow" aria-hidden="true"></i>
15+
{{ _('Back') }}
16+
</a>
17+
</div>
18+
{% endblock page_title %}
19+
20+
{% block admin_page_content %}
21+
{{ super() }}
22+
{% endblock admin_page_content %}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
{#
2+
Copyright (C) 2025 CERN.
3+
Copyright (C) 2025 KTH Royal Institute of Technology.
4+
5+
Invenio App RDM is free software; you can redistribute it and/or modify it
6+
under the terms of the MIT License; see LICENSE file for more details.
7+
#}
8+
9+
{% from "invenio_administration/macros.html" import go_back %}
10+
11+
{% extends admin_base_template %}
12+
13+
{% block page_title %}
14+
{{ go_back() }}
15+
<h1 class="ui header">{{ title }}</h1>
16+
<div class="ui divider" aria-hidden="true"></div>
17+
{% endblock page_title %}
18+
19+
{%- block flashmessages %}
20+
{%- from "invenio_theme/macros/messages.html" import flashed_messages with context -%}
21+
{{ flashed_messages() }}
22+
{%- endblock flashmessages %}
23+
24+
{% block admin_page_content %}
25+
26+
<section class="ui segment" aria-labelledby="user-summary">
27+
<h3 id="user-summary" class="ui header">{{ user_label }}</h3>
28+
<div class="ui relaxed list">
29+
{% if user.email %}
30+
<div class="item">
31+
<strong>{{ _("Email") }}:</strong>
32+
<span>{{ user.email }}</span>
33+
</div>
34+
{% endif %}
35+
{% if user.username %}
36+
<div class="item">
37+
<strong>{{ _("Username") }}:</strong>
38+
<span>{{ user.username }}</span>
39+
</div>
40+
{% endif %}
41+
<div class="item">
42+
<strong>{{ _("User ID") }}:</strong>
43+
<span>{{ user.id }}</span>
44+
</div>
45+
</div>
46+
</section>
47+
48+
<div class="ui stackable grid">
49+
<div class="sixteen wide mobile twelve wide tablet eight wide computer column">
50+
<form class="ui form" method="post" novalidate>
51+
{{ form.hidden_tag() }}
52+
53+
<div class="field {% if form.roles.errors %}error{% endif %}">
54+
<label for="{{ form.roles.id }}">{{ form.roles.label.text }}</label>
55+
<div class="ui info message">
56+
{% if not can_manage_roles %}
57+
{{ _("You cannot edit your own roles or manage roles for this user due to insufficient permissions.") }}
58+
{% else %}
59+
{{ _("Choose roles carefully. e.g: 'administration' or 'administration-moderation' grants admin-panel access and can perform destructive actions.") }}
60+
{% endif %}
61+
</div>
62+
63+
{% if has_roles %}
64+
<div class="ui form" role="group" aria-label="{{ _('Available roles') }}">
65+
<div class="ui segments">
66+
{% for role in role_entries %}
67+
{# Assigns each checkbox a unique id #}
68+
{% set role_input_id = 'role-input-' ~ loop.index0 %}
69+
<div class="ui segment">
70+
<div class="field">
71+
<div class="ui toggle checkbox">
72+
<input
73+
type="checkbox"
74+
id="{{ role_input_id }}"
75+
name="roles"
76+
value="{{ role.name }}"
77+
{% if role.selected %}checked{% endif %}
78+
{% if not can_manage_roles %}disabled{% endif %}
79+
tabindex="0"
80+
>
81+
<label for="{{ role_input_id }}" class="left aligned">
82+
<strong class="left aligned">{{ role.name }}</strong>
83+
{% if role.description %}
84+
<div class="ui small text left aligned">{{ role.description }}</div>
85+
{% endif %}
86+
</label>
87+
</div>
88+
</div>
89+
</div>
90+
{% endfor %}
91+
</div>
92+
</div>
93+
{% else %}
94+
<div class="ui message">
95+
{{ _("No roles are configured yet. Create roles first to assign them to users.") }}
96+
</div>
97+
{% endif %}
98+
99+
{% if form.roles.errors %}
100+
<div class="ui pointing red basic label">
101+
{{ form.roles.errors|join(", ") }}
102+
</div>
103+
{% endif %}
104+
</div>
105+
106+
<div class="ui divider" aria-hidden="true"></div>
107+
108+
<button type="submit" class="ui primary button" {% if not can_manage_roles %}disabled{% endif %}>
109+
<i class="save icon" aria-hidden="true"></i>
110+
{{ form.submit.label.text }}
111+
</button>
112+
<a class="ui button" href="{{ detail_url }}">{{ _("Cancel") }}</a>
113+
</form>
114+
</div>
115+
</div>
116+
{% endblock admin_page_content %}
117+
118+
{% block javascript %}
119+
{{ super() }}
120+
{# Custom style for Focus checkbox #}
121+
<style>
122+
.ui.segments .ui.segment:has(input[type="checkbox"]:focus-visible) {
123+
outline: 2px solid #2185d0;
124+
outline-offset: -2px;
125+
box-shadow: 0 0 0 2px rgba(33, 133, 208, 0.2);
126+
}
127+
128+
/* Force left alignment for role names and descriptions */
129+
.ui.toggle.checkbox label {
130+
text-align: left !important;
131+
display: block;
132+
}
133+
134+
.ui.toggle.checkbox label strong,
135+
.ui.toggle.checkbox label .ui.small.text {
136+
text-align: left !important;
137+
display: block;
138+
word-wrap: break-word;
139+
}
140+
141+
/* Disabled state styling */
142+
.ui.toggle.checkbox input[type="checkbox"]:disabled ~ label {
143+
opacity: 0.6;
144+
cursor: not-allowed !important;
145+
}
146+
147+
.ui.segment:has(input[type="checkbox"]:disabled) {
148+
opacity: 0.7;
149+
background-color: #fafafa;
150+
}
151+
</style>
152+
{% endblock %}

invenio_app_rdm/administration/users/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
"""Invenio administration module for user resources."""
99

10-
from .users import UsersDetailView, UsersListView
10+
from .users import UsersDetailView, UsersEditView, UsersListView
1111

12-
__all__ = ("UsersDetailView", "UsersListView")
12+
__all__ = (
13+
"UsersDetailView",
14+
"UsersEditView",
15+
"UsersListView",
16+
)

0 commit comments

Comments
 (0)