mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
Each row of the Users page asked has_perm whether its user may clone: two queries a user (the user's permissions and the groups'). The list's query now annotates it with one Exists over the clone permission, given to the user or to a group of the user; a blocked user is shown without it, as has_perm did. The page's query count no longer grows with the users.
93 lines
6.2 KiB
HTML
93 lines
6.2 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
{% load bootstrap_icons %}
|
|
|
|
{% block title %}{{ title }}{% endblock %}
|
|
|
|
{% block page_heading %}{{ title }}{% endblock page_heading %}
|
|
|
|
{% block page_heading_extra %}
|
|
<a href="{% url 'admin:user_create' %}" class="btn btn-primary">
|
|
<span aria-hidden="true">{% bs_icon 'plus-lg' %}</span> {% trans "New user" %}
|
|
</a>
|
|
{% endblock page_heading_extra %}
|
|
|
|
{% block content %}
|
|
<div class="card mb-3">
|
|
<div class="card-header d-flex flex-wrap align-items-center gap-2">
|
|
<span class="me-auto">{% trans "Users" %} <span class="badge text-bg-light border">{{ users|length }}</span></span>
|
|
<input id="filter" class="form-control form-control-sm w-auto" type="search" placeholder="{% trans "Search" %}" aria-label="{% trans "Search users" %}">
|
|
</div>
|
|
<div class="list-group list-group-flush searchable">
|
|
{% for user in users %}
|
|
<div class="list-group-item py-3" data-search="{{ user.username }} {{ user.email }}{% for group in user.groups.all %} {{ group.name }}{% endfor %} {% if user.is_superuser %}{% trans "Superuser" %}{% elif user.is_staff %}{% trans "Template editor" %}{% else %}{% trans "User" %}{% endif %} {% if user.is_active %}{% trans "Active" %}{% else %}{% trans "Blocked" %}{% endif %}">
|
|
<div class="row g-2 align-items-center">
|
|
<div class="col-12 col-md-4">
|
|
<a href="{% url 'accounts:account' user.id %}" class="fw-semibold text-break">{{ user.username }}</a>
|
|
<div class="d-flex flex-wrap align-items-center gap-2 mt-1 small text-body-secondary">
|
|
{% if user.is_superuser %}
|
|
<span class="badge text-bg-secondary">{% trans "Superuser" %}</span>
|
|
{% elif user.is_staff %}
|
|
<span class="badge text-bg-light border">{% trans "Template editor" %}</span>
|
|
{% else %}
|
|
<span>{% trans "User" %}</span>
|
|
{% endif %}
|
|
{% if user.is_active %}
|
|
<span>{% trans "Active" %}</span>
|
|
{% else %}
|
|
<span class="badge text-bg-danger"><span aria-hidden="true">{% bs_icon 'slash-circle' %}</span> {% trans "Blocked" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-5 small">
|
|
<div class="d-flex flex-wrap align-items-center gap-2">
|
|
{% for group in user.groups.all %}<span class="badge text-bg-light border mw-100 text-wrap text-break">{{ group.name }}</span>{% endfor %}
|
|
{# a superuser has every permission: only a granted clone permission is news #}
|
|
{% if user.is_active and user.can_clone and not user.is_superuser %}<span>{% trans "Can clone" %}</span>{% endif %}
|
|
{% if user.api_tokens %}
|
|
<a href="{% url 'accounts:account' user.id %}#apitokens">{% blocktrans count count=user.api_tokens %}{{ count }} API token{% plural %}{{ count }} API tokens{% endblocktrans %}</a>
|
|
{% endif %}
|
|
{% if not user.groups.all and not user.api_tokens %}{% if user.is_superuser or not user.is_active or not user.can_clone %}
|
|
<span class="text-body-secondary">{% trans "No groups or API tokens" %}</span>
|
|
{% endif %}{% endif %}
|
|
</div>
|
|
<div class="text-body-secondary mt-1">
|
|
{% trans "Last login" %}: <span class="text-nowrap">{{ user.last_login|date:"Y-m-d H:i"|default:_("never") }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-3 text-md-end">
|
|
<div class="btn-group">
|
|
<a href="{% url 'admin:user_update' user.id %}" class="btn btn-sm btn-outline-secondary">{% trans "Edit" %}</a>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false"
|
|
data-bs-popper-config='{"strategy": "fixed"}' aria-label="{% blocktrans with name=user.username %}More actions for {{ name }}{% endblocktrans %}">{% bs_icon 'three-dots' %}</button>
|
|
<ul class="dropdown-menu dropdown-menu-end shadow">
|
|
<li><a class="dropdown-item" href="{% url 'accounts:account' user.id %}">{% bs_icon 'person' %} {% trans "View profile" %}</a></li>
|
|
{# an administrator does not block or delete their own account #}
|
|
<li><form method="post" action="{% if user.is_active %}{% url 'admin:user_block' user.id %}{% else %}{% url 'admin:user_unblock' user.id %}{% endif %}">{% csrf_token %}
|
|
<button type="submit" class="dropdown-item"{% if user == request.user %} disabled{% endif %}>
|
|
{% if user.is_active %}{% bs_icon 'slash-circle' %} {% trans "Block" %}{% else %}{% bs_icon 'play' %} {% trans "Unblock" %}{% endif %}
|
|
</button>
|
|
</form></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li>{% if user == request.user %}
|
|
<button type="button" class="dropdown-item text-danger" disabled>{% bs_icon 'trash' %} {% trans "Delete" %}</button>
|
|
{% else %}
|
|
<a class="dropdown-item text-danger" href="{% url 'admin:user_delete' user.id %}">{% bs_icon 'trash' %} {% trans "Delete" %}</a>
|
|
{% endif %}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<div role="status">
|
|
<div id="no-match" class="text-center text-body-secondary py-3" hidden>{% trans "No user matches the search." %}</div>
|
|
</div>
|
|
</div>
|
|
{% endblock content %}
|
|
|
|
{% block script %}
|
|
<script src="{% static 'js/table_filter.js' %}"></script>
|
|
{% endblock script %}
|