Skip to content

Commit

Permalink
Refactor settings module to split the settings routes into different …
Browse files Browse the repository at this point in the history
…files
  • Loading branch information
micahflee committed Jan 14, 2025
1 parent 97e99b7 commit 2f99538
Show file tree
Hide file tree
Showing 15 changed files with 1,320 additions and 1,015 deletions.
1,042 changes: 30 additions & 1,012 deletions hushline/settings/__init__.py

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions hushline/settings/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from flask import (
Blueprint,
render_template,
session,
)

from hushline.auth import admin_authentication_required
from hushline.db import db
from hushline.model import User, Username


def register_admin_routes(bp: Blueprint) -> None:
@bp.route("/admin")
@admin_authentication_required
def admin() -> str:
user = db.session.scalars(db.select(User).filter_by(id=session["user_id"])).one()

all_users = list(
db.session.scalars(db.select(User).join(Username).order_by(Username._username)).all()
)
user_count = len(all_users)
two_fa_count = sum(1 for _ in filter(lambda x: x._totp_secret, all_users))
pgp_key_count = sum(1 for _ in filter(lambda x: x._pgp_key, all_users))

return render_template(
"settings/admin.html",
user=user,
all_users=all_users,
user_count=user_count,
two_fa_count=two_fa_count,
pgp_key_count=pgp_key_count,
two_fa_percentage=(two_fa_count / user_count * 100) if user_count else 0,
pgp_key_percentage=(pgp_key_count / user_count * 100) if user_count else 0,
)
19 changes: 19 additions & 0 deletions hushline/settings/advanced.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from flask import (
Blueprint,
render_template,
session,
)

from hushline.auth import authentication_required
from hushline.db import db
from hushline.model import (
User,
)


def register_advanced_routes(bp: Blueprint) -> None:
@bp.route("/advanced")
@authentication_required
def advanced() -> str:
user = db.session.scalars(db.select(User).filter_by(id=session["user_id"])).one()
return render_template("settings/advanced.html", user=user)
106 changes: 106 additions & 0 deletions hushline/settings/aliases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from typing import Tuple

from flask import (
Blueprint,
current_app,
flash,
redirect,
render_template,
request,
session,
url_for,
)
from werkzeug.wrappers.response import Response

from hushline.auth import authentication_required
from hushline.db import db
from hushline.model import (
User,
Username,
)
from hushline.settings.common import (
form_error,
handle_display_name_form,
handle_new_alias_form,
handle_update_bio,
handle_update_directory_visibility,
)
from hushline.settings.forms import (
DirectoryVisibilityForm,
DisplayNameForm,
NewAliasForm,
ProfileForm,
)


def register_aliases_routes(bp: Blueprint) -> None:
@bp.route("/aliases", methods=["GET", "POST"])
@authentication_required
def aliases() -> Response | Tuple[str, int]:
user = db.session.scalars(db.select(User).filter_by(id=session["user_id"])).one()
new_alias_form = NewAliasForm()

status_code = 200
if request.method == "POST":
if new_alias_form.validate() and (resp := handle_new_alias_form(user, new_alias_form)):
return resp
else:
form_error()
status_code = 400

aliases = db.session.scalars(
db.select(Username)
.filter_by(is_primary=False, user_id=user.id)
.order_by(db.func.coalesce(Username._display_name, Username._username))
).all()

return render_template(
"settings/aliases.html",
user=user,
aliases=aliases,
new_alias_form=new_alias_form,
), status_code

@bp.route("/alias/<int:username_id>", methods=["GET", "POST"])
@authentication_required
async def alias(username_id: int) -> Response | str:
alias = db.session.scalars(
db.select(Username).filter_by(
id=username_id, user_id=session["user_id"], is_primary=False
)
).one_or_none()
if not alias:
flash("Alias not found.")
return redirect(url_for(".index"))

display_name_form = DisplayNameForm()
profile_form = ProfileForm()
directory_visibility_form = DirectoryVisibilityForm(
show_in_directory=alias.show_in_directory
)

if request.method == "POST":
if "update_bio" in request.form and profile_form.validate_on_submit():
return await handle_update_bio(alias, profile_form)
elif (
"update_directory_visibility" in request.form
and directory_visibility_form.validate_on_submit()
):
return handle_update_directory_visibility(alias, directory_visibility_form)
elif "update_display_name" in request.form and display_name_form.validate_on_submit():
return handle_display_name_form(alias, display_name_form)
else:
current_app.logger.error(
f"Unable to handle form submission on endpoint {request.endpoint!r}, "
f"form fields: {request.form.keys()}"
)
flash("Uh oh. There was an error handling your data. Please notify the admin.")

return render_template(
"settings/alias.html",
user=alias.user,
alias=alias,
display_name_form=display_name_form,
directory_visibility_form=directory_visibility_form,
profile_form=profile_form,
)
54 changes: 54 additions & 0 deletions hushline/settings/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from typing import Tuple

from flask import (
Blueprint,
render_template,
request,
session,
)
from werkzeug.wrappers.response import Response

from hushline.auth import authentication_required
from hushline.db import db
from hushline.model import (
User,
)
from hushline.settings.common import (
form_error,
handle_change_password_form,
handle_change_username_form,
)
from hushline.settings.forms import (
ChangePasswordForm,
ChangeUsernameForm,
)


def register_auth_routes(bp: Blueprint) -> None:
@bp.route("/auth", methods=["GET", "POST"])
@authentication_required
def auth() -> Response | Tuple[str, int]:
user = db.session.scalars(db.select(User).filter_by(id=session["user_id"])).one()
change_username_form = ChangeUsernameForm()
change_password_form = ChangePasswordForm()

status_code = 200
if request.method == "POST":
if change_username_form.submit.name in request.form and change_username_form.validate():
return handle_change_username_form(user.primary_username, change_username_form)
elif (
change_password_form.submit.name in request.form
and change_password_form.validate()
and (resp := handle_change_password_form(user, change_password_form))
):
return resp
else:
form_error()
status_code = 400

return render_template(
"settings/auth.html",
user=user,
change_username_form=change_username_form,
change_password_form=change_password_form,
), status_code
173 changes: 173 additions & 0 deletions hushline/settings/branding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
from typing import Tuple

from flask import (
Blueprint,
abort,
current_app,
flash,
render_template,
request,
session,
)

from hushline.auth import admin_authentication_required
from hushline.db import db
from hushline.model import (
OrganizationSetting,
User,
)
from hushline.settings.common import (
form_error,
)
from hushline.settings.forms import (
DeleteBrandLogoForm,
SetHomepageUsernameForm,
UpdateBrandAppNameForm,
UpdateBrandLogoForm,
UpdateBrandPrimaryColorForm,
UpdateDirectoryTextForm,
)
from hushline.storage import public_store


def register_branding_routes(bp: Blueprint) -> None:
@bp.route("/branding", methods=["GET", "POST"])
@admin_authentication_required
def branding() -> Tuple[str, int]:
user = db.session.scalars(db.select(User).filter_by(id=session["user_id"])).one()

update_directory_text_form = UpdateDirectoryTextForm(
markdown=OrganizationSetting.fetch_one(OrganizationSetting.DIRECTORY_INTRO_TEXT)
)
update_brand_logo_form = UpdateBrandLogoForm()
delete_brand_logo_form = DeleteBrandLogoForm()
update_brand_primary_color_form = UpdateBrandPrimaryColorForm()
update_brand_app_name_form = UpdateBrandAppNameForm()
set_homepage_username_form = SetHomepageUsernameForm(
username=OrganizationSetting.fetch_one(OrganizationSetting.HOMEPAGE_USER_NAME)
)

status_code = 200
if request.method == "POST":
if (
update_directory_text_form.submit.name in request.form
and update_directory_text_form.validate()
):
if md := update_directory_text_form.markdown.data.strip():
OrganizationSetting.upsert(
key=OrganizationSetting.DIRECTORY_INTRO_TEXT, value=md
)
db.session.commit()
flash("👍 Directory intro text updated")
else:
row_count = db.session.execute(
db.delete(OrganizationSetting).where(
OrganizationSetting.key == OrganizationSetting.DIRECTORY_INTRO_TEXT
)
).rowcount
if row_count > 1:
current_app.logger.error(
"Would have deleted multiple rows for OrganizationSetting key="
+ OrganizationSetting.DIRECTORY_INTRO_TEXT
)
db.session.rollback()
abort(503)
db.session.commit()
flash("👍 Directory intro text was reset to defaults")
elif (
update_brand_logo_form.submit.name in request.form
and update_brand_logo_form.validate()
):
public_store.put(
OrganizationSetting.BRAND_LOGO_VALUE, update_brand_logo_form.logo.data
)
OrganizationSetting.upsert(
key=OrganizationSetting.BRAND_LOGO,
value=OrganizationSetting.BRAND_LOGO_VALUE,
)
db.session.commit()
flash("👍 Brand logo updated successfully.")
elif (
delete_brand_logo_form.submit.name in request.form
and delete_brand_logo_form.validate()
):
row_count = db.session.execute(
db.delete(OrganizationSetting).where(
OrganizationSetting.key == OrganizationSetting.BRAND_LOGO
)
).rowcount
if row_count > 1:
current_app.logger.error(
"Would have deleted multiple rows for OrganizationSetting key="
+ OrganizationSetting.BRAND_LOGO
)
db.session.rollback()
abort(503)
db.session.commit()
public_store.delete(OrganizationSetting.BRAND_LOGO_VALUE)
flash("👍 Brand logo deleted.")
elif (
update_brand_primary_color_form.submit.name in request.form
and update_brand_primary_color_form.validate()
):
OrganizationSetting.upsert(
key=OrganizationSetting.BRAND_PRIMARY_COLOR,
value=update_brand_primary_color_form.brand_primary_hex_color.data,
)
db.session.commit()
flash("👍 Brand primary color updated successfully.")
elif (
update_brand_app_name_form.submit.name in request.form
and update_brand_app_name_form.validate()
):
OrganizationSetting.upsert(
key=OrganizationSetting.BRAND_NAME,
value=update_brand_app_name_form.brand_app_name.data,
)
db.session.commit()
flash("👍 Brand app name updated successfully.")
elif set_homepage_username_form.delete_submit.name in request.form:
row_count = db.session.execute(
db.delete(OrganizationSetting).filter_by(
key=OrganizationSetting.HOMEPAGE_USER_NAME
)
).rowcount
match row_count:
case 0:
flash("👍 Homepage reset to default")
case 1:
db.session.commit()
set_homepage_username_form.username.data = None
flash("👍 Homepage reset to default")
case _:
current_app.logger.error(
f"Deleting OrganizationSetting {OrganizationSetting.HOMEPAGE_USER_NAME}"
" would have deleted multiple rows"
)
status_code = 500
db.session.rollback()
flash("There was an error and the setting could not reset")
elif (
set_homepage_username_form.submit.name in request.form
and set_homepage_username_form.validate()
):
OrganizationSetting.upsert(
key=OrganizationSetting.HOMEPAGE_USER_NAME,
value=set_homepage_username_form.username.data,
)
db.session.commit()
flash(f"👍 Homepage set to user {set_homepage_username_form.username.data!r}")
else:
form_error()
status_code = 400

return render_template(
"settings/branding.html",
user=user,
update_directory_text_form=update_directory_text_form,
update_brand_logo_form=update_brand_logo_form,
delete_brand_logo_form=delete_brand_logo_form,
update_brand_primary_color_form=update_brand_primary_color_form,
update_brand_app_name_form=update_brand_app_name_form,
set_homepage_username_form=set_homepage_username_form,
), status_code
Loading

0 comments on commit 2f99538

Please sign in to comment.