refactor: the core module has its own main blueprint

This commit is contained in:
Éloi Rivard 2023-08-14 13:52:24 +02:00
parent c6a543535c
commit d27aab8651
No known key found for this signature in database
GPG key ID: 7EDA204EA57DD184
40 changed files with 166 additions and 151 deletions

View file

@ -84,16 +84,12 @@ def setup_themer(app):
def setup_blueprints(app):
import canaille.core.account
import canaille.core.admin
import canaille.core.groups
import canaille.core.blueprints
import canaille.oidc.blueprints
app.url_map.strict_slashes = False
app.register_blueprint(canaille.core.account.bp)
app.register_blueprint(canaille.core.admin.bp)
app.register_blueprint(canaille.core.groups.bp)
app.register_blueprint(canaille.core.blueprints.bp)
app.register_blueprint(canaille.oidc.blueprints.bp)

View file

@ -228,7 +228,7 @@ WRITE = [
# PREFERRED_USERNAME = "{{ user.display_name }}"
# LOCALE = "{{ user.preferred_language }}"
# ADDRESS = "{{ user.formatted_address[0] }}"
# PICTURE = "{% if user.photo %}{{ url_for('account.photo', user=user, field='photo', _external=True) }}{% endif %}"
# PICTURE = "{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}"
# WEBSITE = "{{ user.profile_url[0] }}"
# The SMTP server options. If not set, mail related features such as

View file

@ -62,15 +62,15 @@ def index():
user = current_user()
if not user:
return redirect(url_for("account.login"))
return redirect(url_for("core.account.login"))
if user.can_edit_self or user.can_manage_users:
return redirect(url_for("account.profile_edition", edited_user=user))
return redirect(url_for("core.account.profile_edition", edited_user=user))
if user.can_use_oidc:
return redirect(url_for("oidc.consents.consents"))
return redirect(url_for("account.about"))
return redirect(url_for("core.account.about"))
@bp.route("/about")
@ -85,7 +85,9 @@ def about():
@bp.route("/login", methods=("GET", "POST"))
def login():
if current_user():
return redirect(url_for("account.profile_edition", edited_user=current_user()))
return redirect(
url_for("core.account.profile_edition", edited_user=current_user())
)
form = LoginForm(request.form or None)
form.render_field_macro_file = "partial/login_field.html"
@ -96,7 +98,7 @@ def login():
user = models.User.get_from_login(form.login.data)
if user and not user.has_password():
return redirect(url_for("account.firstlogin", user=user))
return redirect(url_for("core.account.firstlogin", user=user))
if not form.validate():
models.User.logout()
@ -104,13 +106,13 @@ def login():
return render_template("login.html", form=form)
session["attempt_login"] = form.login.data
return redirect(url_for("account.password"))
return redirect(url_for("core.account.password"))
@bp.route("/password", methods=("GET", "POST"))
def password():
if "attempt_login" not in session:
return redirect(url_for("account.login"))
return redirect(url_for("core.account.login"))
form = PasswordForm(request.form or None)
form.render_field_macro_file = "partial/login_field.html"
@ -122,7 +124,7 @@ def password():
user = models.User.get_from_login(session["attempt_login"])
if user and not user.has_password():
return redirect(url_for("account.firstlogin", user=user))
return redirect(url_for("core.account.firstlogin", user=user))
if not form.validate() or not user:
models.User.logout()
@ -145,7 +147,7 @@ def password():
_("Connection successful. Welcome %(user)s", user=user.formatted_name[0]),
"success",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
@bp.route("/logout")
@ -266,7 +268,7 @@ def user_invitation(user):
form.groups.data,
)
registration_url = url_for(
"account.registration",
"core.account.registration",
data=invitation.b64(),
hash=invitation.build_hash(),
_external=True,
@ -294,35 +296,35 @@ def registration(data, hash):
_("The invitation link that brought you here was invalid."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if invitation.has_expired():
flash(
_("The invitation link that brought you here has expired."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if models.User.get_from_login(invitation.user_name):
flash(
_("Your account has already been created."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if current_user():
flash(
_("You are already logged in, you cannot create an account."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if hash != invitation.build_hash():
flash(
_("The invitation link that brought you here was invalid."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
data = {
"user_name": invitation.user_name,
@ -383,7 +385,7 @@ def registration(data, hash):
user = profile_create(current_app, form)
user.login()
flash(_("Your account has been created successfully."), "success")
return redirect(url_for("account.profile_edition", edited_user=user))
return redirect(url_for("core.account.profile_edition", edited_user=user))
@bp.route("/email-confirmation/<data>/<hash>")
@ -395,21 +397,21 @@ def email_confirmation(data, hash):
_("The email confirmation link that brought you here is invalid."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if confirmation_obj.has_expired():
flash(
_("The email confirmation link that brought you here has expired."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if hash != confirmation_obj.build_hash():
flash(
_("The invitation link that brought you here was invalid."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
user = models.User.get(confirmation_obj.identifier)
if not user:
@ -417,26 +419,26 @@ def email_confirmation(data, hash):
_("The email confirmation link that brought you here is invalid."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if confirmation_obj.email in user.emails:
flash(
_("This address email have already been confirmed."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if models.User.query(emails=confirmation_obj.email):
flash(
_("This address email is already associated with another account."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
user.emails = user.emails + [confirmation_obj.email]
user.save()
flash(_("Your email address have been confirmed."), "success")
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
@bp.route("/profile", methods=("GET", "POST"))
@ -469,7 +471,7 @@ def profile_creation(user):
)
user = profile_create(current_app, form)
return redirect(url_for("account.profile_edition", edited_user=user))
return redirect(url_for("core.account.profile_edition", edited_user=user))
def profile_create(current_app, form):
@ -589,7 +591,7 @@ def profile_edition_add_email(user, edited_user, emails_form):
emails_form.new_email.data,
)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=email_confirmation.b64(),
hash=email_confirmation.build_hash(),
_external=True,
@ -651,7 +653,9 @@ def profile_edition(user, edited_user):
profile_edition_main_form_validation(user, edited_user, profile_form)
flash(_("Profile updated successfully."), "success")
return redirect(url_for("account.profile_edition", edited_user=edited_user))
return redirect(
url_for("core.account.profile_edition", edited_user=edited_user)
)
if request.form.get("action") == "add_email":
if not emails_form.validate():
@ -669,7 +673,9 @@ def profile_edition(user, edited_user):
else:
flash(_("Could not send the verification email"), "error")
return redirect(url_for("account.profile_edition", edited_user=edited_user))
return redirect(
url_for("core.account.profile_edition", edited_user=edited_user)
)
if request.form.get("email_remove"):
if not profile_edition_remove_email(
@ -679,7 +685,9 @@ def profile_edition(user, edited_user):
return render_template("profile_edit.html", **render_context)
flash(_("The email have been successfully deleted."), "success")
return redirect(url_for("account.profile_edition", edited_user=edited_user))
return redirect(
url_for("core.account.profile_edition", edited_user=edited_user)
)
abort(400, f"bad form action: {request.form.get('action')}")
@ -819,7 +827,7 @@ def profile_settings_edit(editor, edited_user):
edited_user.save()
flash(_("Profile updated successfully."), "success")
return redirect(
url_for("account.profile_settings", edited_user=edited_user)
url_for("core.account.profile_settings", edited_user=edited_user)
)
return render_template(
@ -846,8 +854,8 @@ def profile_delete(user, edited_user):
edited_user.delete()
if self_deletion:
return redirect(url_for("account.index"))
return redirect(url_for("account.users"))
return redirect(url_for("core.account.index"))
return redirect(url_for("core.account.users"))
@bp.route("/impersonate/<user:puppet>")
@ -858,7 +866,7 @@ def impersonate(user, puppet):
_("Connection successful. Welcome %(user)s", user=puppet.formatted_name),
"success",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
@bp.route("/reset", methods=["GET", "POST"])
@ -929,14 +937,14 @@ def reset(user, hash):
_("The password reset link that brought you here was invalid."),
"error",
)
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
if request.form and form.validate():
user.set_password(form.password.data)
user.login()
flash(_("Your password has been updated successfully"), "success")
return redirect(url_for("account.profile_edition", edited_user=user))
return redirect(url_for("core.account.profile_edition", edited_user=user))
return render_template("reset-password.html", form=form, user=user, hash=hash)

View file

@ -49,7 +49,7 @@ def mail_index(user):
@bp.route("/mail/test.html")
@permissions_needed("manage_oidc")
def test_html(user):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
return render_template(
"mail/test.html",
site_name=current_app.config.get("NAME", "Canaille"),
@ -64,7 +64,7 @@ def test_html(user):
@bp.route("/mail/test.txt")
@permissions_needed("manage_oidc")
def test_txt(user):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
return render_template(
"mail/test.txt",
site_name=current_app.config.get("NAME", "Canaille"),
@ -75,9 +75,9 @@ def test_txt(user):
@bp.route("/mail/password-init.html")
@permissions_needed("manage_oidc")
def password_init_html(user):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
reset_url = url_for(
"account.reset",
"core.account.reset",
user=user,
hash=build_hash(user.identifier, user.preferred_email, user.password[0]),
title=_("Password initialization on {website_name}").format(
@ -101,9 +101,9 @@ def password_init_html(user):
@bp.route("/mail/password-init.txt")
@permissions_needed("manage_oidc")
def password_init_txt(user):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
reset_url = url_for(
"account.reset",
"core.account.reset",
user=user,
hash=build_hash(user.identifier, user.preferred_email, user.password[0]),
_external=True,
@ -120,9 +120,9 @@ def password_init_txt(user):
@bp.route("/mail/reset.html")
@permissions_needed("manage_oidc")
def password_reset_html(user):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
reset_url = url_for(
"account.reset",
"core.account.reset",
user=user,
hash=build_hash(user.identifier, user.preferred_email, user.password[0]),
title=_("Password reset on {website_name}").format(
@ -146,9 +146,9 @@ def password_reset_html(user):
@bp.route("/mail/reset.txt")
@permissions_needed("manage_oidc")
def password_reset_txt(user):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
reset_url = url_for(
"account.reset",
"core.account.reset",
user=user,
hash=build_hash(user.identifier, user.preferred_email, user.password[0]),
_external=True,
@ -165,9 +165,9 @@ def password_reset_txt(user):
@bp.route("/mail/<identifier>/<email>/invitation.html")
@permissions_needed("manage_oidc")
def invitation_html(user, identifier, email):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
registration_url = url_for(
"account.registration",
"core.account.registration",
data=obj_to_b64([identifier, email]),
hash=build_hash(identifier, email),
_external=True,
@ -188,9 +188,9 @@ def invitation_html(user, identifier, email):
@bp.route("/mail/<identifier>/<email>/invitation.txt")
@permissions_needed("manage_oidc")
def invitation_txt(user, identifier, email):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
registration_url = url_for(
"account.registration",
"core.account.registration",
data=obj_to_b64([identifier, email]),
hash=build_hash(identifier, email),
_external=True,
@ -207,9 +207,9 @@ def invitation_txt(user, identifier, email):
@bp.route("/mail/<identifier>/<email>/email-confirmation.html")
@permissions_needed("manage_oidc")
def email_confirmation_html(user, identifier, email):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=obj_to_b64([identifier, email]),
hash=build_hash(identifier, email),
_external=True,
@ -230,9 +230,9 @@ def email_confirmation_html(user, identifier, email):
@bp.route("/mail/<identifier>/<email>/email-confirmation.txt")
@permissions_needed("manage_oidc")
def email_confirmation_txt(user, identifier, email):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=obj_to_b64([identifier, email]),
hash=build_hash(identifier, email),
_external=True,

View file

@ -0,0 +1,11 @@
from flask import Blueprint
from . import account
from . import admin
from . import groups
bp = Blueprint("core", __name__)
bp.register_blueprint(account.bp)
bp.register_blueprint(admin.bp)
bp.register_blueprint(groups.bp)

View file

@ -48,7 +48,7 @@ def create_group(user):
),
"success",
)
return redirect(url_for("groups.group", group=group))
return redirect(url_for("core.groups.group", group=group))
return render_template(
"group.html", menuitem="groups", form=form, edited_group=None, members=None
@ -98,7 +98,7 @@ def edit_group(group):
),
"success",
)
return redirect(url_for("groups.group", group=group))
return redirect(url_for("core.groups.group", group=group))
else:
flash(_("Group edition failed."), "error")
@ -118,4 +118,4 @@ def delete_group(group):
"success",
)
group.delete()
return redirect(url_for("groups.groups"))
return redirect(url_for("core.groups.groups"))

View file

@ -8,7 +8,7 @@ from flask_themer import render_template
def send_test_mail(email):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
logo_cid, logo_filename, logo_raw = logo()
subject = _("Test email from {website_name}").format(
@ -37,9 +37,9 @@ def send_test_mail(email):
def send_password_reset_mail(user, mail):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
reset_url = url_for(
"account.reset",
"core.account.reset",
user=user,
hash=build_hash(
user.identifier,
@ -78,9 +78,9 @@ def send_password_reset_mail(user, mail):
def send_password_initialization_mail(user, email):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
reset_url = url_for(
"account.reset",
"core.account.reset",
user=user,
hash=build_hash(
user.identifier,
@ -119,7 +119,7 @@ def send_password_initialization_mail(user, email):
def send_invitation_mail(email, registration_url):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
logo_cid, logo_filename, logo_raw = logo()
subject = _("You have been invited to create an account on {website_name}").format(
@ -150,7 +150,7 @@ def send_invitation_mail(email, registration_url):
def send_confirmation_email(email, confirmation_url):
base_url = url_for("account.index", _external=True)
base_url = url_for("core.account.index", _external=True)
logo_cid, logo_filename, logo_raw = logo()
subject = _("Confirm your address email on {website_name}").format(

View file

@ -265,7 +265,7 @@ def end_session():
user = current_user()
if not user:
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
form = LogoutForm(request.form)
form.action = url_for("oidc.endpoints.end_session_submit")
@ -345,7 +345,7 @@ def end_session():
return redirect(url)
flash(_("You have been disconnected"), "success")
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))
@bp.route("/end_session_confirm", methods=["POST"])
@ -363,4 +363,4 @@ def end_session_submit():
flash(_("You have not been disconnected"), "info")
return redirect(url_for("account.index"))
return redirect(url_for("core.account.index"))

View file

@ -45,7 +45,7 @@ DEFAULT_JWT_MAPPING = {
"PREFERRED_USERNAME": "{% if user.display_name %}{{ user.display_name }}{% endif %}",
"LOCALE": "{% if user.preferred_language %}{{ user.preferred_language }}{% endif %}",
"ADDRESS": "{% if user.formatted_address %}{{ user.formatted_address[0] }}{% endif %}",
"PICTURE": "{% if user.photo %}{{ url_for('account.photo', user=user, field='photo', _external=True) }}{% endif %}",
"PICTURE": "{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}",
"WEBSITE": "{% if user.profile_url %}{{ user.profile_url[0] }}{% endif %}",
}

View file

@ -7,7 +7,7 @@
{% block content %}
<div class="ui clearing segment">
<a href="{{ url_for('account.index') }}">
<a href="{{ url_for('core.account.index') }}">
<img class="ui tiny centered image" src="/static/img/canaille-head.png" alt="{{ website_name }}">
</a>

View file

@ -25,7 +25,7 @@
<div class="ui right aligned container">
<div class="ui stackable buttons">
<a type="button" class="ui right floated button" href="{{ url_for('account.login') }}">{{ _("Login page") }}</a>
<a type="button" class="ui right floated button" href="{{ url_for('core.account.login') }}">{{ _("Login page") }}</a>
<button type="submit" name="action" value="sendmail" class="ui right floated primary button">{{ _("Send the initialization email") }}</button>
</div>
</div>

View file

@ -29,7 +29,7 @@
<div class="ui right aligned container">
<div class="ui stackable buttons">
<a type="button" class="ui right floated button" href="{{ url_for('account.login') }}">{{ _("Login page") }}</a>
<a type="button" class="ui right floated button" href="{{ url_for('core.account.login') }}">{{ _("Login page") }}</a>
<button type="submit" class="ui right floated {% if request.method != "POST" or form.errors %}primary {% endif %}button">
{% if request.method == "POST" %}
{{ _("Send again") }}

View file

@ -12,11 +12,11 @@
{% block submenu %}
<nav class="ui bottom attached two item borderless menu">
<a class="{% if edited_group %}active {% endif %}item" href="{{ url_for('groups.groups') }}">
<a class="{% if edited_group %}active {% endif %}item" href="{{ url_for('core.groups.groups') }}">
<i class="th list icon"></i>
{% trans %}View{% endtrans %}
</a>
<a class="{% if not edited_group %}active {% endif %}item" href="{{ url_for('groups.create_group') }}">
<a class="{% if not edited_group %}active {% endif %}item" href="{{ url_for('core.groups.create_group') }}">
<i class="plus icon"></i>
{% trans %}Add{% endtrans %}
</a>

View file

@ -7,11 +7,11 @@
{% block submenu %}
<nav class="ui bottom attached two item borderless menu">
<a class="active item" href="{{ url_for('groups.groups') }}">
<a class="active item" href="{{ url_for('core.groups.groups') }}">
<i class="th list icon"></i>
{% trans %}View{% endtrans %}
</a>
<a class="item" href="{{ url_for('groups.create_group') }}">
<a class="item" href="{{ url_for('core.groups.create_group') }}">
<i class="plus icon"></i>
{% trans %}Add{% endtrans %}
</a>

View file

@ -11,15 +11,15 @@
{% block submenu %}
<nav class="ui bottom attached three item borderless menu">
<a class="item" href="{{ url_for('account.users') }}">
<a class="item" href="{{ url_for('core.account.users') }}">
<i class="th list icon"></i>
{% trans %}View{% endtrans %}
</a>
<a class="item" href="{{ url_for('account.profile_creation') }}">
<a class="item" href="{{ url_for('core.account.profile_creation') }}">
<i class="plus icon"></i>
{% trans %}Add{% endtrans %}
</a>
<a class="active item" href="{{ url_for('account.user_invitation') }}">
<a class="active item" href="{{ url_for('core.account.user_invitation') }}">
<i class="paper plane icon"></i>
{% trans %}Invite{% endtrans %}
</a>
@ -79,11 +79,11 @@
</div>
<br>
<div class="ui stackable buttons">
<a class="ui right floated button" href="{{ url_for("account.profile_creation") }}">
<a class="ui right floated button" href="{{ url_for("core.account.profile_creation") }}">
{{ _("Create a user") }}
</a>
{% if request.form["action"] == "generate" or email_sent %}
<a href="{{ url_for('account.user_invitation') }}" class="ui right floated button" name="action" value="invite" id="invite">
<a href="{{ url_for('core.account.user_invitation') }}" class="ui right floated button" name="action" value="invite" id="invite">
{{ _("Invite another user") }}
</a>
{% endif %}
@ -126,7 +126,7 @@
<div class="ui right aligned container">
<div class="ui stackable buttons">
<a class="ui right floated button" href="{{ url_for("account.profile_creation") }}">
<a class="ui right floated button" href="{{ url_for("core.account.profile_creation") }}">
{{ _("Create a user") }}
</a>
<button type="submit" name="action" value="generate" class="ui right floated primary button">

View file

@ -8,7 +8,7 @@
<div class="content">
<div class="ui clearing segment">
{% if logo_url %}
<a href="{{ url_for('account.index') }}">
<a href="{{ url_for('core.account.index') }}">
<img class="ui tiny centered image" src="{{ logo_url }}" alt="{{ website_name }}">
</a>
{% else %}
@ -34,7 +34,7 @@
<div class="ui right aligned container">
<div class="ui stackable buttons">
{% if has_smtp and has_password_recovery %}
<a type="button" class="ui right floated button" href="{{ url_for('account.forgotten') }}">{{ _("Forgotten password") }}</a>
<a type="button" class="ui right floated button" href="{{ url_for('core.account.forgotten') }}">{{ _("Forgotten password") }}</a>
{% endif %}
<button type="submit" name="answer" class="ui right floated primary button" hx-boost="false">{{ _("Continue") }}</button>
</div>

View file

@ -7,7 +7,7 @@
{% block submenu %}
<nav class="ui bottom attached five item borderless menu">
<a class="active item" href="{{ url_for('admin.mail_index') }}">
<a class="active item" href="{{ url_for('core.admin.mail_index') }}">
<i class="user mail icon"></i>
{% trans %}Emails{% endtrans %}
</a>
@ -65,8 +65,8 @@
<div class="item">
<div class="right floated content">
<div class="ui buttons">
<a class="ui button primary" href="{{ url_for("admin.test_txt") }}">TXT</a>
<a class="ui button primary" href="{{ url_for("admin.test_html") }}">HTML</a>
<a class="ui button primary" href="{{ url_for("core.admin.test_txt") }}">TXT</a>
<a class="ui button primary" href="{{ url_for("core.admin.test_html") }}">HTML</a>
</div>
</div>
<div class="middle aligned content">
@ -77,8 +77,8 @@
<div class="item">
<div class="right floated content">
<div class="ui buttons">
<a class="ui button primary" href="{{ url_for("admin.password_init_txt") }}">TXT</a>
<a class="ui button primary" href="{{ url_for("admin.password_init_html") }}">HTML</a>
<a class="ui button primary" href="{{ url_for("core.admin.password_init_txt") }}">TXT</a>
<a class="ui button primary" href="{{ url_for("core.admin.password_init_html") }}">HTML</a>
</div>
</div>
<div class="middle aligned content">
@ -89,8 +89,8 @@
<div class="item">
<div class="right floated content">
<div class="ui buttons">
<a class="ui button primary" href="{{ url_for("admin.password_reset_txt") }}">TXT</a>
<a class="ui button primary" href="{{ url_for("admin.password_reset_html") }}">HTML</a>
<a class="ui button primary" href="{{ url_for("core.admin.password_reset_txt") }}">TXT</a>
<a class="ui button primary" href="{{ url_for("core.admin.password_reset_html") }}">HTML</a>
</div>
</div>
<div class="middle aligned content">
@ -101,8 +101,8 @@
<div class="item">
<div class="right floated content">
<div class="ui buttons">
<a class="ui button primary" href="{{ url_for("admin.email_confirmation_txt", identifier=user.identifier, email=user.preferred_email) }}">TXT</a>
<a class="ui button primary" href="{{ url_for("admin.email_confirmation_html", identifier=user.identifier, email=user.preferred_email) }}">HTML</a>
<a class="ui button primary" href="{{ url_for("core.admin.email_confirmation_txt", identifier=user.identifier, email=user.preferred_email) }}">TXT</a>
<a class="ui button primary" href="{{ url_for("core.admin.email_confirmation_html", identifier=user.identifier, email=user.preferred_email) }}">HTML</a>
</div>
</div>
<div class="middle aligned content">
@ -114,8 +114,8 @@
<div class="item">
<div class="right floated content">
<div class="ui buttons">
<a class="ui button primary" href="{{ url_for("admin.invitation_txt", identifier=user.identifier, email=user.preferred_email) }}">TXT</a>
<a class="ui button primary" href="{{ url_for("admin.invitation_html", identifier=user.identifier, email=user.preferred_email) }}">HTML</a>
<a class="ui button primary" href="{{ url_for("core.admin.invitation_txt", identifier=user.identifier, email=user.preferred_email) }}">TXT</a>
<a class="ui button primary" href="{{ url_for("core.admin.invitation_html", identifier=user.identifier, email=user.preferred_email) }}">HTML</a>
</div>
</div>
<div class="middle aligned content">

View file

@ -7,7 +7,7 @@
{% block submenu %}
<nav class="ui bottom attached five item borderless menu">
<a class="item" href="{{ url_for('admin.mail_index') }}">
<a class="item" href="{{ url_for('core.admin.mail_index') }}">
<i class="user mail icon"></i>
{% trans %}Emails{% endtrans %}
</a>

View file

@ -6,7 +6,7 @@
{% block submenu %}
<nav class="ui bottom attached five item borderless menu">
<a class="item" href="{{ url_for('admin.mail_index') }}">
<a class="item" href="{{ url_for('core.admin.mail_index') }}">
<i class="user mail icon"></i>
{% trans %}Emails{% endtrans %}
</a>

View file

@ -7,7 +7,7 @@
{% block submenu %}
<nav class="ui bottom attached five item borderless menu">
<a class="item" href="{{ url_for('admin.mail_index') }}">
<a class="item" href="{{ url_for('core.admin.mail_index') }}">
<i class="user mail icon"></i>
{% trans %}Emails{% endtrans %}
</a>

View file

@ -7,7 +7,7 @@
{% block submenu %}
<nav class="ui bottom attached five item borderless menu">
<a class="item" href="{{ url_for('admin.mail_index') }}">
<a class="item" href="{{ url_for('core.admin.mail_index') }}">
<i class="user mail icon"></i>
{% trans %}Emails{% endtrans %}
</a>

View file

@ -7,7 +7,7 @@
{% block submenu %}
<nav class="ui bottom attached five item borderless menu">
<a class="item" href="{{ url_for('admin.mail_index') }}">
<a class="item" href="{{ url_for('core.admin.mail_index') }}">
<i class="user mail icon"></i>
{% trans %}Emails{% endtrans %}
</a>

View file

@ -7,7 +7,7 @@
{% block submenu %}
<nav class="ui bottom attached five item borderless menu">
<a class="item" href="{{ url_for('admin.mail_index') }}">
<a class="item" href="{{ url_for('core.admin.mail_index') }}">
<i class="user mail icon"></i>
{% trans %}Emails{% endtrans %}
</a>

View file

@ -11,7 +11,7 @@
{% block submenu %}
<nav class="ui bottom attached five item borderless menu">
<a class="item" href="{{ url_for('admin.mail_index') }}">
<a class="item" href="{{ url_for('core.admin.mail_index') }}">
<i class="user mail icon"></i>
{% trans %}Emails{% endtrans %}
</a>
@ -54,7 +54,7 @@
<tr>
<td>{{ _("Subject") }}</td>
<td>
<a href="{{ url_for("account.profile_edition", edited_user=token.subject) }}">
<a href="{{ url_for("core.account.profile_edition", edited_user=token.subject) }}">
{{ token.subject.identifier }}
</a>
</td>

View file

@ -12,11 +12,11 @@
{% for group in table_form.items_slice %}
<tr>
<td>
<a href="{{ url_for('groups.group', group=group) }}">
<a href="{{ url_for('core.groups.group', group=group) }}">
<i class="users circular black inverted icon"></i>
</a>
</td>
<td><a href="{{ url_for('groups.group', group=group) }}">{{ group.display_name }}</a></td>
<td><a href="{{ url_for('core.groups.group', group=group) }}">{{ group.display_name }}</a></td>
<td>{% if group.description %}{{ group.description[0] }}{% endif %}</td>
<td>{{ group.members|len }}</td>
</tr>

View file

@ -14,7 +14,7 @@
<td><a href="{{ url_for('oidc.authorizations.view', authorization=authorization) }}">{{ authorization.authorization_code_id }}</a></td>
<td><a href="{{ url_for('oidc.clients.edit', client=authorization.client) }}">{{ authorization.client.client_id }}</a></td>
<td>
<a href="{{ url_for("account.profile_edition", edited_user=authorization.subject) }}">
<a href="{{ url_for("core.account.profile_edition", edited_user=authorization.subject) }}">
{{ authorization.subject.user_name[0] }}
</a>
</td>

View file

@ -22,7 +22,7 @@
</a>
</td>
<td>
<a href="{{ url_for("account.profile_edition", edited_user=token.subject) }}">
<a href="{{ url_for("core.account.profile_edition", edited_user=token.subject) }}">
{{ token.subject.user_name[0] }}
</a>
</td>

View file

@ -24,11 +24,11 @@
<tr>
{% if user.can_read("photo") %}
<td>
<a href="{{ url_for('account.profile_edition', edited_user=watched_user) }}">
<a href="{{ url_for('core.account.profile_edition', edited_user=watched_user) }}">
{% if user.can_manage_users and watched_user.locked %}
<i class="lock circle big black icon" title="{% trans %}This account is locked{% endtrans %}"></i>
{% elif watched_user.photo and watched_user.photo[0] %}
<img class="ui avatar image" src="{{ url_for("account.photo", user=watched_user, field="photo") }}" alt="User photo">
<img class="ui avatar image" src="{{ url_for("core.account.photo", user=watched_user, field="photo") }}" alt="User photo">
{% else %}
<i class="user circle big black icon"></i>
{% endif %}
@ -37,7 +37,7 @@
{% endif %}
{% if user.can_read("user_name") %}
<td>
<a href="{{ url_for('account.profile_edition', edited_user=watched_user) }}">
<a href="{{ url_for('core.account.profile_edition', edited_user=watched_user) }}">
{% if watched_user.user_name %}
{{ watched_user.user_name[0] }}
{% else %}
@ -55,7 +55,7 @@
{% if user.can_manage_groups %}
<td>
{% for group in watched_user.groups %}
<a class="ui label" href="{{ url_for('groups.group', group=group) }}"{% if group.description %} title="{{ group.description[0] }}"{% endif %}>
<a class="ui label" href="{{ url_for('core.groups.group', group=group) }}"{% if group.description %} title="{{ group.description[0] }}"{% endif %}>
{{ group.display_name }}
</a>
{% endfor %}

View file

@ -8,7 +8,7 @@
<div class="content">
<div class="ui clearing segment">
{% if logo_url %}
<a href="{{ url_for('account.index') }}">
<a href="{{ url_for('core.account.index') }}">
<img class="ui tiny centered image" src="{{ logo_url }}" alt="{{ website_name }}">
</a>
{% else %}
@ -29,9 +29,9 @@
<div class="ui right aligned container">
<div class="ui stackable buttons">
<a type="button" class="ui right floated button" href="{{ url_for('account.login') }}">{{ _("I am not %(username)s", username=username) }}</a>
<a type="button" class="ui right floated button" href="{{ url_for('core.account.login') }}">{{ _("I am not %(username)s", username=username) }}</a>
{% if has_smtp and has_password_recovery %}
<a type="button" class="ui right floated button" href="{{ url_for('account.forgotten') }}">{{ _("Forgotten password") }}</a>
<a type="button" class="ui right floated button" href="{{ url_for('core.account.forgotten') }}">{{ _("Forgotten password") }}</a>
{% endif %}
<button type="submit" class="ui right floated primary button">{{ _("Sign in") }}</button>
</div>

View file

@ -12,16 +12,16 @@
{% block submenu %}
<nav class="ui bottom attached {% if has_smtp %}three{% else %}two{% endif %} item borderless menu">
<a class="item" href="{{ url_for('account.users') }}">
<a class="item" href="{{ url_for('core.account.users') }}">
<i class="th list icon"></i>
{% trans %}View{% endtrans %}
</a>
<a class="active item" href="{{ url_for('account.profile_creation') }}">
<a class="active item" href="{{ url_for('core.account.profile_creation') }}">
<i class="plus icon"></i>
{% trans %}Add{% endtrans %}
</a>
{% if has_smtp %}
<a class="item" href="{{ url_for('account.user_invitation') }}">
<a class="item" href="{{ url_for('core.account.user_invitation') }}">
<i class="paper plane icon"></i>
{% trans %}Invite{% endtrans %}
</a>

View file

@ -18,11 +18,11 @@
{% block submenu %}
<nav class="ui bottom attached two item borderless menu">
<a class="active item" href="{{ url_for('account.profile_edition', edited_user=edited_user) }}">
<a class="active item" href="{{ url_for('core.account.profile_edition', edited_user=edited_user) }}">
<i class="id card icon"></i>
{% trans %}Personal information{% endtrans %}
</a>
<a class="item" href="{{ url_for('account.profile_settings', edited_user=edited_user) }}">
<a class="item" href="{{ url_for('core.account.profile_settings', edited_user=edited_user) }}">
<i class="tools icon"></i>
{% trans %}Account settings{% endtrans %}
</a>
@ -66,7 +66,7 @@
<a class="ui right corner label photo-delete-icon" title="{{ _("Delete the photo") }}">
<i class="times icon"></i>
</a>
<img src="{% if photo %}{{ url_for("account.photo", user=edited_user, field="photo") }}{% endif %}" alt="User photo">
<img src="{% if photo %}{{ url_for("core.account.photo", user=edited_user, field="photo") }}{% endif %}" alt="User photo">
</label>
<label
class="ui centered photo-placeholder"

View file

@ -11,11 +11,11 @@
{% block submenu %}
<nav class="ui bottom attached two item borderless menu">
<a class="item" href="{{ url_for('account.profile_edition', edited_user=edited_user) }}">
<a class="item" href="{{ url_for('core.account.profile_edition', edited_user=edited_user) }}">
<i class="id card icon"></i>
{% trans %}Personal information{% endtrans %}
</a>
<a class="active item" href="{{ url_for('account.profile_settings', edited_user=edited_user) }}">
<a class="active item" href="{{ url_for('core.account.profile_settings', edited_user=edited_user) }}">
<i class="tools icon"></i>
{% trans %}Account settings{% endtrans %}
</a>
@ -156,7 +156,7 @@
{% endif %}
{% if user.can_impersonate_users and user.identifier != edited_user.identifier %}
<a href="{{ url_for('account.impersonate', puppet=edited_user) }}" class="ui right floated basic button" name="action" value="impersonate" id="impersonate" hx-boost="false">
<a href="{{ url_for('core.account.impersonate', puppet=edited_user) }}" class="ui right floated basic button" name="action" value="impersonate" id="impersonate" hx-boost="false">
{{ _("Impersonate") }}
</a>
{% endif %}

View file

@ -13,7 +13,7 @@
</h3>
<div class="ui attached clearing segment">
{{ fui.render_form(form, _("Password reset"), action=url_for("account.reset", user=user, hash=hash)) }}
{{ fui.render_form(form, _("Password reset"), action=url_for("core.account.reset", user=user, hash=hash)) }}
</div>
</div>
{% endblock %}

View file

@ -7,16 +7,16 @@
{% block submenu %}
<nav class="ui bottom attached {% if has_smtp %}three{% else %}two{% endif %} item borderless menu">
<a class="active item" href="{{ url_for('account.users') }}">
<a class="active item" href="{{ url_for('core.account.users') }}">
<i class="th list icon"></i>
{% trans %}View{% endtrans %}
</a>
<a class="item" href="{{ url_for('account.profile_creation') }}">
<a class="item" href="{{ url_for('core.account.profile_creation') }}">
<i class="plus icon"></i>
{% trans %}Add{% endtrans %}
</a>
{% if has_smtp %}
<a class="item" href="{{ url_for('account.user_invitation') }}">
<a class="item" href="{{ url_for('core.account.user_invitation') }}">
<i class="paper plane icon"></i>
{% trans %}Invite{% endtrans %}
</a>

View file

@ -31,7 +31,7 @@
{% if user.can_edit_self %}
<a class="item {% if menuitem is defined and menuitem == "profile" %}active{% endif %}"
href="{{ url_for('account.profile_edition', edited_user=user) }}">
href="{{ url_for('core.account.profile_edition', edited_user=user) }}">
<i class="id card icon"></i>
{% trans %}Profile{% endtrans %}
</a>
@ -45,25 +45,25 @@
{% endif %}
{% if user.can_manage_users %}
<a class="item {% if menuitem is defined and menuitem == "users" %}active{% endif %}"
href="{{ url_for('account.users') }}">
href="{{ url_for('core.account.users') }}">
<i class="address book icon"></i>
{% trans %}Users{% endtrans %}
</a>
{% endif %}
{% if user.can_manage_groups %}
<a class="item {% if menuitem is defined and menuitem == "groups" %}active{% endif %}"
href="{{ url_for('groups.groups') }}">
href="{{ url_for('core.groups.groups') }}">
<i class="users icon"></i>
{% trans %}Groups{% endtrans %}
</a>
{% endif %}
{% if user.can_manage_oidc %}
<a class="item {% if menuitem is defined and menuitem == "admin" %}active{% endif %}" href="{{ url_for('admin.mail_index') }}">
<a class="item {% if menuitem is defined and menuitem == "admin" %}active{% endif %}" href="{{ url_for('core.admin.mail_index') }}">
<i class="settings icon"></i>
{% trans %}Admin{% endtrans %}
</a>
{% endif %}
<a class="item" href="{{ url_for('account.logout') }}">
<a class="item" href="{{ url_for('core.account.logout') }}">
<i class="sign out alternate icon"></i>
{% trans %}Log out{% endtrans %}
</a>
@ -84,7 +84,7 @@
{% endblock %}
<footer>
<a href="{{ url_for('account.about') }}">{{ _("About Canaille") }}</a>
<a href="{{ url_for('core.account.about') }}">{{ _("About Canaille") }}</a>
</footer>
{% if config.get("JAVASCRIPT", true) %}
{% if not is_boosted %}

View file

@ -234,7 +234,7 @@ DYNAMIC_CLIENT_REGISTRATION_TOKENS = [
# PREFERRED_USERNAME = "{{ user.display_name }}"
# LOCALE = "{{ user.preferred_language }}"
# ADDRESS = "{{ user.formatted_address[0] }}"
# PICTURE = "{% if user.photo %}{{ url_for('account.photo', user=user, field='photo', _external=True) }}{% endif %}"
# PICTURE = "{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}"
# WEBSITE = "{{ user.profile_url[0] }}"
# The SMTP server options. If not set, mail related features such as

View file

@ -238,7 +238,7 @@ DYNAMIC_CLIENT_REGISTRATION_TOKENS = [
# PREFERRED_USERNAME = "{{ user.display_name }}"
# LOCALE = "{{ user.preferred_language }}"
# ADDRESS = "{{ user.formatted_address[0] }}"
# PICTURE = "{% if user.photo %}{{ url_for('account.photo', user=user, field='photo', _external=True) }}{% endif %}"
# PICTURE = "{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}"
# WEBSITE = "{{ user.profile_url[0] }}"
# The SMTP server options. If not set, mail related features such as

View file

@ -274,7 +274,7 @@ Attributes are rendered using jinja2, and can use a ``user`` variable.
*Optional.* Defaults to ``{{ user.address[0] }}``
:PICTURE:
*Optional.* Defaults to ``{% if user.photo %}{{ url_for('account.photo', user_name=user.user_name[0], field='photo', _external=True) }}{% endif %}``
*Optional.* Defaults to ``{% if user.photo %}{{ url_for('core.account.photo', user_name=user.user_name[0], field='photo', _external=True) }}{% endif %}``
:WEBSITE:
*Optional.* Defaults to ``{{ user.profile_url[0] }}``

View file

@ -28,7 +28,7 @@
</div>
{% endif %}
<a class="item {% if menuitem == "profile" %}active{% endif %}"
href="{{ url_for('account.profile_edition', edited_user=user) }}">
href="{{ url_for('core.account.profile_edition', edited_user=user) }}">
<i class="id card icon"></i>
{% trans %}My profile{% endtrans %}
</a>
@ -39,14 +39,14 @@
</a>
{% if user.can_manage_users %}
<a class="item {% if menuitem == "users" %}active{% endif %}"
href="{{ url_for('account.users') }}">
href="{{ url_for('core.account.users') }}">
<i class="users icon"></i>
{% trans %}Users{% endtrans %}
</a>
{% endif %}
{% if user.can_manage_groups %}
<a class="item {% if menuitem == "groups" %}active{% endif %}"
href="{{ url_for('groups.groups') }}">
href="{{ url_for('core.groups.groups') }}">
<i class="users cog icon"></i>
{% trans %}Groups{% endtrans %}
</a>
@ -75,7 +75,7 @@
</div>
</div>
{% endif %}
<a class="item" href="{{ url_for('account.logout') }}">
<a class="item" href="{{ url_for('core.account.logout') }}">
<i class="sign out alternate icon"></i>
{% trans %}Log out{% endtrans %}
</a>
@ -91,7 +91,7 @@
</div>
<footer>
<a href="{{ url_for('account.about') }}">{{ _("About canaille") }}</a>
<a href="{{ url_for('core.account.about') }}">{{ _("About canaille") }}</a>
</footer>
<script src="/static/jquery/jquery.min.js" defer></script>
<script src="/static/fomanticui/semantic.min.js" defer></script>

View file

@ -168,7 +168,7 @@ def test_confirmation_unset_smtp_enabled_email_user_validation(
"new_email@mydomain.tld",
)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=email_confirmation.b64(),
hash=email_confirmation.build_hash(),
_external=True,
@ -264,7 +264,7 @@ def test_confirmation_expired_link(testclient, backend, user):
"new_email@mydomain.tld",
)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=email_confirmation.b64(),
hash=email_confirmation.build_hash(),
_external=True,
@ -291,7 +291,7 @@ def test_confirmation_invalid_hash_link(testclient, backend, user):
"new_email@mydomain.tld",
)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=email_confirmation.b64(),
hash="invalid",
_external=True,
@ -320,7 +320,7 @@ def test_confirmation_invalid_user_link(testclient, backend, user):
"new_email@mydomain.tld",
)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=email_confirmation.b64(),
hash=email_confirmation.build_hash(),
_external=True,
@ -347,7 +347,7 @@ def test_confirmation_email_already_confirmed_link(testclient, backend, user, ad
"john@doe.com",
)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=email_confirmation.b64(),
hash=email_confirmation.build_hash(),
_external=True,
@ -376,7 +376,7 @@ def test_confirmation_email_already_used_link(testclient, backend, user, admin):
"jane@doe.com",
)
email_confirmation_url = url_for(
"account.email_confirmation",
"core.account.email_confirmation",
data=email_confirmation.b64(),
hash=email_confirmation.build_hash(),
_external=True,