diff --git a/hiboo/account/home.py b/hiboo/account/home.py index 3df2fee9298ecfd253a12cbef24dad2884a1fb75..1cf5199a2e02b325f33de913b26e62f72d4d6740 100644 --- a/hiboo/account/home.py +++ b/hiboo/account/home.py @@ -1,3 +1,4 @@ +import enum from hiboo.account import blueprint from hiboo.profile import common from hiboo import security, models @@ -11,7 +12,9 @@ import flask_login def home(): user = flask_login.current_user page = flask.request.args.get('page', 1, type=int) - events = user.history.filter(models.History.user == user).paginate(page=page, per_page=25) + events = user.history.filter(models.History.user == user).order_by( + models.History.created_at.desc()).paginate(page=page, per_page=25) + # TODO also query user.groups non individual events (renamed or removed group) return flask.render_template("account_home.html", events=events) diff --git a/hiboo/group/views.py b/hiboo/group/views.py index 3affdec883958d5d0ce832d88f2fdeb3e6a8cd35..968ddee0e5a146ae720ec2e223ad52c29f091ee7 100644 --- a/hiboo/group/views.py +++ b/hiboo/group/views.py @@ -4,6 +4,7 @@ from sqlalchemy import not_ from flask_babel import lazy_gettext as _ import flask +import flask_login @blueprint.route("/create", methods=["GET", "POST"]) @security.admin_required() @@ -30,12 +31,7 @@ def create(): @security.confirmation_required(_("Delete the group")) def delete(group_uuid): group = models.Group.query.get(group_uuid) or flask.abort(404) - for user in group.users: - models.log( - models.History.GROUP, - comment=str(_("Group {} was deleted").format(group.groupname)), - user=user, group=group - ) + #TODO log implementation needs a way to keep group infos after deletion models.db.session.delete(group) models.db.session.commit() return flask.redirect(flask.url_for(".list")) @@ -56,12 +52,11 @@ def edit(group_uuid): form.submit.label.text = (_("Edit")) if form.validate_on_submit(): if form.groupname.data != group.groupname: - for user in group.users: - models.log( - models.History.GROUP, - comment=str(_("Group {} was renamed to {}").format(group.groupname, form.groupname.data)), - user=user, group=group - ) + models.log( + models.History.GROUP, + comment=str(_("Group {} was renamed to {}").format(group.groupname, form.groupname.data)), + actor=flask_login.current_user, group=group + ) group.groupname = form.groupname.data group.description = form.description.data models.db.session.add(group) @@ -91,7 +86,7 @@ def membership(group_uuid): models.log( models.History.GROUP, comment=str(_("User {} was added to the group {}").format(user.username, group.groupname)), - user=user, group=group + user=user, actor=flask_login.current_user, group=group ) exit_list = (user for user in legacy_users if user not in form.pre_populate.data) if exit_list: @@ -100,7 +95,7 @@ def membership(group_uuid): models.log( models.History.GROUP, comment=str(_("User {} was removed from the group {}").format(user.username, group.groupname)), - user=user, group=group + user=user, actor=flask_login.current_user, group=group ) selected_users = [] for username in [*form.users.data, *form.pre_populate.data]: diff --git a/hiboo/models.py b/hiboo/models.py index f48ede01c321b12ba8964b05a4968a062c6174d6..fd3dd58e61e318802f0b84e614a0280ef7590780 100644 --- a/hiboo/models.py +++ b/hiboo/models.py @@ -403,7 +403,8 @@ class History(db.Model): PASSWORD: _("changed this account password"), MFA: _("modified this account multi-factor authentication (MFA) setting"), STATUS: _("set the {this.service.name} profile {this.profile.username} as {this.value}"), - TRANSITION: _("{this.transition.label_alt} the profile {this.profile.username} on {this.service.name}") + TRANSITION: _("{this.transition.label_alt} the profile {this.profile.username} on {this.service.name}"), + GROUP: _("modified the group {this.group.groupname}") } user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid)) diff --git a/hiboo/moderation/templates/moderation_activity.html b/hiboo/moderation/templates/moderation_activity.html index 53715836c80cdceacc0619a1332054422ba2168e..a65d228439e7eb387ab37798880f769734d040c7 100644 --- a/hiboo/moderation/templates/moderation_activity.html +++ b/hiboo/moderation/templates/moderation_activity.html @@ -1,12 +1,60 @@ {% extends "base.html" %} +{% from "bootstrap5/pagination.html" import render_pagination %} {% block title %}{% trans %}Moderation{% endtrans %}{% endblock %} {% block subtitle %}{% trans %}Activity logs{% endtrans %}{% endblock %} {% block content %} <div class="row"> - <div class="col-xl-6 col"> - {{ macros.timeline(events, public_only=False, show_user=True) }} + <div class="col"> + <div class="table-responsive"> + <table class="table table-striped table-head-fixed table-hover"> + <thead> + <tr> + <th>{% trans %}Category{% endtrans %}</th> + <th>{% trans %}Timestamp{% endtrans %}</th> + <th>{% trans %}Scope{% endtrans %}</th> + <th>{% trans %}Message{% endtrans %}</th> + </tr> + </thead> + <tbody> + {% for event in events %} + <tr> + <td>{{ macros.history_category_icons(event) }} {{ event.category|upper }}</td> + <td>{{ event.created_at.isoformat(sep=" ", timespec="seconds") }}</td> + <td> + {% if event.user.username %} + <span class="badge border rounded-pill + bg-secondary-subtle border-secondary-subtle + text-secondary-emphasis"> + {{ render_icon("person-fill") }}{{ event.user.username }} + </span> + {% endif %} + {% if event.group.groupname %} + <span class="badge border rounded-pill + bg-secondary-subtle border-secondary-subtle + text-secondary-emphasis">{{ render_icon("diagram-3-fill") }} + {{ event.group.groupname }} + </span> + {% endif %} + </td> + <td> + <b>{% if event.actor.is_admin %}{{ render_icon("shield-shaded") }}{% endif %} + {{ event.actor.username or event.user.username }} + </b> + {{ event.description }} + {% if event.comment %} + <br><span class="text-secondary-emphasis">{{ event.comment }}</span> + {% endif %} + </td> + </tr> + {% endfor %} + </tbody> + </table> + {% if (events.page) and (events.has_next or events.has_prev) %} + {{ render_pagination(events, align="center") }} + {% endif %} + </div> </div> </div> {% endblock %} diff --git a/hiboo/moderation/views.py b/hiboo/moderation/views.py index cda7c7c9b67b7ec0b7d0cfbe46fa3d344f37b0af..c929efc1e563098d8836bc5c8438748379c003c2 100644 --- a/hiboo/moderation/views.py +++ b/hiboo/moderation/views.py @@ -21,5 +21,6 @@ def pending_profiles(): @security.admin_required() def activity(): page = flask.request.args.get('page', 1, type=int) - events = models.History.query.paginate(page=page, per_page=25) + events = models.History.query.order_by( + models.History.created_at.desc()).paginate(page=page, per_page=25) return flask.render_template("moderation_activity.html", events=events) diff --git a/hiboo/profile/views.py b/hiboo/profile/views.py index 6ae56c6093b6825074fe7ab877ba96000ba875cc..93cebf6e3bd17500107cd331d7406b1aaa35d74e 100644 --- a/hiboo/profile/views.py +++ b/hiboo/profile/views.py @@ -74,7 +74,7 @@ def create(service_uuid, create_for=False, quick=False): ) models.db.session.add(profile) models.log(models.History.CREATE, profile.username, profile.comment, - user=user, service=service, profile=profile) + user=user, actor=flask_login.current_user, service=service, profile=profile) models.db.session.commit() return flask.redirect(utils.url_or_intent("account.home")) else: diff --git a/hiboo/templates/macros.html b/hiboo/templates/macros.html index f0b5a56ff194d00adb0f654a51a0e8e0f1a3ec2f..7ff9a7afc4c53b8604665f7776ea0c6f6f28aa65 100644 --- a/hiboo/templates/macros.html +++ b/hiboo/templates/macros.html @@ -2,53 +2,38 @@ {% from 'bootstrap5/pagination.html' import render_pagination %} {% set colors = ["indigo", "green", "orange", "teal", "pink", "purple", "cyan"] %} -{% macro timeline(events, public_only=True, show_user=False) %} -<div class="card border-0"> - <div class="card-body table-responsive"> - <div class="timeline"> - {% set dates = [] %} - {% for event in events | sort(attribute='created_at', reverse=True) %} - {% if event.public or not public_only %} - {% if not event.created_at.date() == dates[-1] %} - {% set _ = dates.append(event.created_at.date()) %} - <div class="time-label"> - <span class="bg-primary-subtle text-primary-emphasis">{{ event.created_at.date() }}</span> - </div> - {% endif %} - <div> - <div class="timeline-icon bg-secondary-subtle text-secondary-emphasis"> - {{ render_icon({ - 'signup': 'box-arrow-in-right', - 'create': 'plus', - 'delete': "person-dash-fill", - 'note': "pen-fill", - 'status': "check", - 'transition': 'caret-right-fill', - 'password': 'key-fill', - 'mfa': 'qr-code', - 'group': 'diagram-3-fill' - }[event.category]) }} - </div> - <div class="timeline-item border border-secondary-subtle rounded"> - <span class="time">{{ render_icon("clock") }} {{ event.created_at.time().strftime("%H:%M") }}</span> - {% if show_user and event.user.username %} - <span class="time">{{ render_icon("person-fill") }} {{ event.user.username }}</span> - {% endif %} - <h3 class="timeline-header rounded{% if event.comment %}-top{% endif %}"> - <strong>{{ event.actor.username or event.user.username }}</strong> - {{ event.description }} - </h3> - {% if event.comment %}<div class="timeline-body">{{ event.comment }}</div>{% endif %} - </div> - </div> - {% endif %} - {% endfor %} +{% macro timeline(events, public_only=True) %} +<div class="timeline"> +{% set dates = [] %} +{% for event in events if event.public or not public_only %} + {% if not event.created_at.date() == dates[-1] %} + {% set _ = dates.append(event.created_at.date()) %} + <div class="time-label"> + <span class="bg-primary-subtle text-primary-emphasis">{{ event.created_at.date() }}</span> + </div> + {% endif %} + <div> + <div class="timeline-icon bg-secondary-subtle text-secondary-emphasis"> + {{ history_category_icons(event) }} + </div> + <div class="timeline-item border border-secondary-subtle rounded"> + <span class="time">{{ render_icon("clock") }} {{ event.created_at.time().strftime("%H:%M") }}</span> + <h3 class="timeline-header rounded{% if event.comment %}-top{% endif %}"> + <strong>{{ event.actor.username or event.user.username }}</strong> + {{ event.description }} + </h3> + {% if event.comment %} + <div class="timeline-body text-secondary-emphasis"> + {{ event.comment }} + </div> + {% endif %} </div> - {% if (events.page) and (events.has_next or events.has_prev) %} - {{ render_pagination(events, align="center") }} - {% endif %} </div> +{% endfor %} </div> +{% if (events.page) and (events.has_next or events.has_prev) %} +{{ render_pagination(events, align="center") }} +{% endif %} {% endmacro %} {% macro profile_status(profile) %} @@ -98,6 +83,20 @@ {% endfor %} {% endmacro %} +{% macro history_category_icons(event) %} + {{ render_icon({ + 'signup': 'box-arrow-in-right', + 'create': 'plus', + 'delete': "person-dash-fill", + 'note': "pen-fill", + 'status': "check", + 'transition': 'caret-right-fill', + 'password': 'key-fill', + 'mfa': 'qr-code', + 'group': 'diagram-3-fill' + }[event.category]) }} +{% endmacro %} + {% macro infobox(title, text, color, icon) %} {% set c = color %} <div class="card mb-3 border-left">