-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor settings module to split the settings routes into different …
…files
- Loading branch information
Showing
15 changed files
with
1,320 additions
and
1,015 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.