use flask flash for notifications
This commit is contained in:
@@ -5,13 +5,14 @@ from flask import current_app as app
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
import pendulum
|
||||
import os
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from atst.domain.requests import Requests
|
||||
from atst.domain.users import Users
|
||||
from atst.domain.authnid import AuthenticationContext
|
||||
from atst.domain.audit_log import AuditLog
|
||||
from atst.domain.auth import logout as _logout
|
||||
from werkzeug.exceptions import NotFound
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
bp = Blueprint("atst", __name__)
|
||||
@@ -28,10 +29,9 @@ def root():
|
||||
redirect_url,
|
||||
"?{}".format(url.urlencode({"next": request.args.get("next")})),
|
||||
)
|
||||
flash("login_next")
|
||||
|
||||
return render_template(
|
||||
"login.html", redirect=bool(request.args.get("next")), redirect_url=redirect_url
|
||||
)
|
||||
return render_template("login.html", redirect_url=redirect_url)
|
||||
|
||||
|
||||
@bp.route("/help")
|
||||
|
@@ -9,6 +9,7 @@ from atst.domain.invitations import (
|
||||
WrongUserError as InvitationWrongUserError,
|
||||
)
|
||||
from atst.domain.workspaces import WorkspaceError
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
def log_error(e):
|
||||
@@ -39,7 +40,8 @@ def make_error_pages(app):
|
||||
# pylint: disable=unused-variable
|
||||
def session_expired(e):
|
||||
log_error(e)
|
||||
url_args = {"sessionExpired": True, "next": request.path}
|
||||
url_args = {"next": request.path}
|
||||
flash("session_expired")
|
||||
if request.method == "POST":
|
||||
url_args[app.form_cache.PARAM_NAME] = app.form_cache.write(request.form)
|
||||
return redirect(url_for("atst.root", **url_args))
|
||||
|
@@ -13,6 +13,7 @@ from atst.domain.requests import Requests
|
||||
from atst.domain.exceptions import NotFoundError
|
||||
from atst.forms.ccpo_review import CCPOReviewForm
|
||||
from atst.forms.internal_comment import InternalCommentForm
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
def map_ccpo_authorizing(user):
|
||||
@@ -63,6 +64,7 @@ def submit_approval(request_id):
|
||||
|
||||
return redirect(url_for("requests.requests_index"))
|
||||
else:
|
||||
flash("form_errors")
|
||||
return render_approval(request, form)
|
||||
|
||||
|
||||
@@ -94,4 +96,5 @@ def create_internal_comment(request_id):
|
||||
url_for("requests.approval", request_id=request_id, _anchor="ccpo-notes")
|
||||
)
|
||||
else:
|
||||
flash("form_errors")
|
||||
return render_approval(request, internal_comment_form=form)
|
||||
|
@@ -13,6 +13,7 @@ from atst.domain.requests.financial_verification import (
|
||||
)
|
||||
from atst.models.attachment import Attachment
|
||||
from atst.domain.task_orders import TaskOrders
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
def fv_extended(_http_request):
|
||||
@@ -201,11 +202,13 @@ def financial_verification(request_id):
|
||||
g.current_user, request, is_extended=is_extended
|
||||
).execute()
|
||||
|
||||
if request.review_comment:
|
||||
flash("request_review_comment", {"comment": request.review_comment})
|
||||
|
||||
return render_template(
|
||||
"requests/financial_verification.html",
|
||||
f=form,
|
||||
jedi_request=request,
|
||||
review_comment=request.review_comment,
|
||||
extended=is_extended,
|
||||
saved_draft=saved_draft,
|
||||
)
|
||||
@@ -236,11 +239,8 @@ def update_financial_verification(request_id):
|
||||
|
||||
if updated_request.task_order.verified:
|
||||
workspace = Requests.auto_approve_and_create_workspace(updated_request)
|
||||
return redirect(
|
||||
url_for(
|
||||
"workspaces.new_project", workspace_id=workspace.id, newWorkspace=True
|
||||
)
|
||||
)
|
||||
flash("new_workspace")
|
||||
return redirect(url_for("workspaces.new_project", workspace_id=workspace.id))
|
||||
else:
|
||||
return redirect(url_for("requests.requests_index", modal="pendingCCPOApproval"))
|
||||
|
||||
|
@@ -5,6 +5,7 @@ from . import requests_bp
|
||||
from atst.domain.requests import Requests
|
||||
from atst.models.permissions import Permissions
|
||||
from atst.forms.data import SERVICE_BRANCHES
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
class RequestsIndex(object):
|
||||
@@ -99,4 +100,8 @@ class RequestsIndex(object):
|
||||
@requests_bp.route("/requests", methods=["GET"])
|
||||
def requests_index():
|
||||
context = RequestsIndex(g.current_user).execute()
|
||||
|
||||
if context.get("num_action_required"):
|
||||
flash("requests_action_required", {"count": context.get("num_action_required")})
|
||||
|
||||
return render_template("requests/index.html", **context)
|
||||
|
@@ -124,6 +124,10 @@ class JEDIRequestFlow(object):
|
||||
},
|
||||
]
|
||||
|
||||
@property
|
||||
def is_review_screen(self):
|
||||
return self.screens[-1] == self.current_screen
|
||||
|
||||
def create_or_update_request(self):
|
||||
request_data = self.map_request_data(self.form_section, self.form.data)
|
||||
if self.request_id:
|
||||
|
@@ -13,6 +13,7 @@ from atst.forms.data import (
|
||||
FUNDING_TYPES,
|
||||
TASK_ORDER_SOURCES,
|
||||
)
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
@requests_bp.context_processor
|
||||
@@ -31,6 +32,9 @@ def option_data():
|
||||
def requests_form_new(screen):
|
||||
jedi_flow = JEDIRequestFlow(screen, request=None, current_user=g.current_user)
|
||||
|
||||
if jedi_flow.is_review_screen and not jedi_flow.can_submit:
|
||||
flash("request_incomplete")
|
||||
|
||||
return render_template(
|
||||
"requests/screen-%d.html" % int(screen),
|
||||
f=jedi_flow.form,
|
||||
@@ -54,6 +58,12 @@ def requests_form_update(screen=1, request_id=None):
|
||||
screen, request=request, request_id=request_id, current_user=g.current_user
|
||||
)
|
||||
|
||||
if jedi_flow.is_review_screen and not jedi_flow.can_submit:
|
||||
flash("request_incomplete")
|
||||
|
||||
if request.review_comment:
|
||||
flash("request_review_comment", {"comment": request.review_comment})
|
||||
|
||||
return render_template(
|
||||
"requests/screen-%d.html" % int(screen),
|
||||
f=jedi_flow.form,
|
||||
@@ -63,7 +73,6 @@ def requests_form_update(screen=1, request_id=None):
|
||||
next_screen=screen + 1,
|
||||
request_id=request_id,
|
||||
jedi_request=jedi_flow.request,
|
||||
review_comment=request.review_comment,
|
||||
can_submit=jedi_flow.can_submit,
|
||||
)
|
||||
|
||||
@@ -103,6 +112,7 @@ def requests_update(screen=1, request_id=None):
|
||||
where = "/requests"
|
||||
return redirect(where)
|
||||
else:
|
||||
flash("form_errors")
|
||||
rerender_args = dict(
|
||||
f=jedi_flow.form,
|
||||
data=post_data,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from flask import Blueprint, render_template, g, request as http_request, redirect
|
||||
from atst.forms.edit_user import EditUserForm
|
||||
from atst.domain.users import Users
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
bp = Blueprint("users", __name__)
|
||||
@@ -10,9 +11,12 @@ bp = Blueprint("users", __name__)
|
||||
def user():
|
||||
user = g.current_user
|
||||
form = EditUserForm(data=user.to_dictionary())
|
||||
return render_template(
|
||||
"user/edit.html", next=http_request.args.get("next"), form=form, user=user
|
||||
)
|
||||
next_ = http_request.args.get("next")
|
||||
|
||||
if next_:
|
||||
flash("user_must_complete_profile")
|
||||
|
||||
return render_template("user/edit.html", next=next_, form=form, user=user)
|
||||
|
||||
|
||||
@bp.route("/user", methods=["POST"])
|
||||
@@ -20,11 +24,12 @@ def update_user():
|
||||
user = g.current_user
|
||||
form = EditUserForm(http_request.form)
|
||||
next_url = http_request.args.get("next")
|
||||
rerender_args = {"form": form, "user": user, "next": next_url}
|
||||
if form.validate():
|
||||
Users.update(user, form.data)
|
||||
rerender_args["updated"] = True
|
||||
flash("user_updated")
|
||||
if next_url:
|
||||
return redirect(next_url)
|
||||
else:
|
||||
flash("form_errors")
|
||||
|
||||
return render_template("user/edit.html", **rerender_args)
|
||||
return render_template("user/edit.html", form=form, user=user, next=next_url)
|
||||
|
@@ -8,6 +8,7 @@ from atst.domain.workspaces import Workspaces
|
||||
from atst.forms.workspace import WorkspaceForm
|
||||
from atst.domain.authz import Authorization
|
||||
from atst.models.permissions import Permissions
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces")
|
||||
@@ -33,6 +34,7 @@ def edit_workspace(workspace_id):
|
||||
url_for("workspaces.workspace_projects", workspace_id=workspace.id)
|
||||
)
|
||||
else:
|
||||
flash("form_errors")
|
||||
return render_template("workspaces/edit.html", form=form, workspace=workspace)
|
||||
|
||||
|
||||
|
@@ -4,6 +4,7 @@ from . import workspaces_bp
|
||||
from atst.domain.workspaces import Workspaces
|
||||
from atst.domain.invitations import Invitations
|
||||
from atst.queue import queue
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
def send_invite_email(owner_name, token, new_member_email):
|
||||
@@ -40,10 +41,5 @@ def revoke_invitation(workspace_id, token):
|
||||
def resend_invitation(workspace_id, token):
|
||||
invite = Invitations.resend(g.current_user, workspace_id, token)
|
||||
send_invite_email(g.current_user.full_name, invite.token, invite.email)
|
||||
return redirect(
|
||||
url_for(
|
||||
"workspaces.workspace_members",
|
||||
workspace_id=workspace_id,
|
||||
resentInvitationTo=invite.user_name,
|
||||
)
|
||||
)
|
||||
flash("resent_workspace_invitation", {"user_name": invite.user_name})
|
||||
return redirect(url_for("workspaces.workspace_members", workspace_id=workspace_id))
|
||||
|
@@ -21,12 +21,13 @@ from atst.domain.authz import Authorization
|
||||
from atst.models.permissions import Permissions
|
||||
from atst.domain.invitations import Invitations
|
||||
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/members")
|
||||
def workspace_members(workspace_id):
|
||||
workspace = Workspaces.get_with_members(g.current_user, workspace_id)
|
||||
new_member_name = http_request.args.get("newMemberName")
|
||||
resent_invitation_to = http_request.args.get("resentInvitationTo")
|
||||
new_member = next(
|
||||
filter(lambda m: m.user_name == new_member_name, workspace.members), None
|
||||
)
|
||||
@@ -51,7 +52,6 @@ def workspace_members(workspace_id):
|
||||
status_choices=MEMBER_STATUS_CHOICES,
|
||||
members=members_list,
|
||||
new_member=new_member,
|
||||
resent_invitation_to=resent_invitation_to,
|
||||
)
|
||||
|
||||
|
||||
@@ -76,12 +76,13 @@ def create_member(workspace_id):
|
||||
invite = Invitations.create(user, new_member, form.data["email"])
|
||||
send_invite_email(g.current_user.full_name, invite.token, invite.email)
|
||||
|
||||
flash(
|
||||
"new_workspace_member",
|
||||
{"new_member": new_member, "workspace": workspace},
|
||||
)
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
"workspaces.workspace_members",
|
||||
workspace_id=workspace.id,
|
||||
newMemberName=new_member.user_name,
|
||||
)
|
||||
url_for("workspaces.workspace_members", workspace_id=workspace.id)
|
||||
)
|
||||
except AlreadyExistsError:
|
||||
return render_template(
|
||||
@@ -107,6 +108,10 @@ def view_member(workspace_id, member_id):
|
||||
form = EditMemberForm(workspace_role=member.role_name)
|
||||
editable = g.current_user == member.user
|
||||
can_revoke_access = Workspaces.can_revoke_access_for(workspace, member)
|
||||
|
||||
if member.has_dod_id_error:
|
||||
flash("workspace_member_dod_id_error")
|
||||
|
||||
return render_template(
|
||||
"workspaces/members/edit.html",
|
||||
workspace=workspace,
|
||||
@@ -155,13 +160,13 @@ def update_member(workspace_id, member_id):
|
||||
g.current_user, workspace, member, ids_and_roles
|
||||
)
|
||||
|
||||
flash(
|
||||
"workspace_role_updated",
|
||||
{"member_name": member.user_name, "updated_role": new_role_name},
|
||||
)
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
"workspaces.workspace_members",
|
||||
workspace_id=workspace.id,
|
||||
memberName=member.user_name,
|
||||
updatedRole=new_role_name,
|
||||
)
|
||||
url_for("workspaces.workspace_members", workspace_id=workspace.id)
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
@@ -177,10 +182,5 @@ def update_member(workspace_id, member_id):
|
||||
)
|
||||
def revoke_access(workspace_id, member_id):
|
||||
revoked_role = Workspaces.revoke_access(g.current_user, workspace_id, member_id)
|
||||
return redirect(
|
||||
url_for(
|
||||
"workspaces.workspace_members",
|
||||
workspace_id=workspace_id,
|
||||
revokedMemberName=revoked_role.user_name,
|
||||
)
|
||||
)
|
||||
flash("revoked_workspace_access", {"member_name": revoked_role.user.full_name})
|
||||
return redirect(url_for("workspaces.workspace_members", workspace_id=workspace_id))
|
||||
|
107
atst/utils/flash.py
Normal file
107
atst/utils/flash.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from flask import flash, render_template_string
|
||||
|
||||
MESSAGES = {
|
||||
"new_workspace_member": {
|
||||
"title_template": "Member added successfully",
|
||||
"message_template": """
|
||||
<p>{{ new_member.user_name }} was successfully invited via email to this workspace. They do not yet have access to any environments.</p>
|
||||
<p><a href="{{ url_for('workspaces.update_member', workspace_id=workspace.id, member_id=new_member.user_id) }}">Add environment access.</a></p>
|
||||
""",
|
||||
"category": "success",
|
||||
},
|
||||
"revoked_workspace_access": {
|
||||
"title_template": "Removed workspace access",
|
||||
"message_template": """
|
||||
<p>Removed {{ member_name }} from this workspace.</p>
|
||||
""",
|
||||
"category": "success",
|
||||
},
|
||||
"resent_workspace_invitation": {
|
||||
"title_template": "Invitation resent",
|
||||
"message_template": """
|
||||
<p>Successfully sent a new invitation to {{ user_name }}.</p>
|
||||
""",
|
||||
"category": "success",
|
||||
},
|
||||
"workspace_role_updated": {
|
||||
"title_template": "Workspace role updated successfully",
|
||||
"message_template": """
|
||||
<p>{{ member_name }}'s role was successfully updated to {{ updated_role }}</p>
|
||||
""",
|
||||
"category": "success",
|
||||
},
|
||||
"session_expired": {
|
||||
"title_template": "Session Expired",
|
||||
"message_template": """
|
||||
Your session expired due to inactivity. Please log in again to continue.
|
||||
""",
|
||||
"category": "error",
|
||||
},
|
||||
"login_next": {
|
||||
"title_template": "Log in Required.",
|
||||
"message_template": """
|
||||
After you log in, you will be redirected to your destination page.
|
||||
""",
|
||||
"category": "warning",
|
||||
},
|
||||
"new_workspace": {
|
||||
"title_template": "Workspace created!",
|
||||
"message_template": """
|
||||
<p>You are now ready to create projects and environments within the JEDI Cloud.</p>
|
||||
""",
|
||||
"category": "success",
|
||||
},
|
||||
"workspace_member_dod_id_error": {
|
||||
"title_template": "CAC ID Error",
|
||||
"message_template": """
|
||||
The member attempted to accept this invite, but their CAC ID did not match the CAC ID you specified on the invite. Please confirm that the DOD ID is accurate.
|
||||
""",
|
||||
"category": "error",
|
||||
},
|
||||
"form_errors": {
|
||||
"title_template": "There were some errors",
|
||||
"message_template": "<p>Please see below.</p>",
|
||||
"category": "error",
|
||||
},
|
||||
"user_must_complete_profile": {
|
||||
"title_template": "You must complete your profile",
|
||||
"message_template": "<p>Before continuing, you must complete your profile</p>",
|
||||
"category": "info",
|
||||
},
|
||||
"user_updated": {
|
||||
"title_template": "User information updated.",
|
||||
"message_template": "",
|
||||
"category": "success",
|
||||
},
|
||||
"request_incomplete": {
|
||||
"title_template": "Please complete all sections",
|
||||
"message_template": """
|
||||
<p>In order to submit your JEDI Cloud request, you'll need to complete all required sections of this form without error. Missing or invalid fields are noted below.</p>
|
||||
""",
|
||||
"category": "error",
|
||||
},
|
||||
"requests_action_required": {
|
||||
"title_template": "Action required on {{ count }} requests.",
|
||||
"message_template": "",
|
||||
"category": "info",
|
||||
},
|
||||
"request_review_comment": {
|
||||
"title_template": "Changes Requested",
|
||||
"message_template": """
|
||||
<p>CCPO has requested changes to your submission with the following notes:
|
||||
<br>
|
||||
{{ comment }}
|
||||
<br>
|
||||
Please contact info@jedi.cloud or 123-123-4567 for further discussion.</p>
|
||||
""",
|
||||
"category": "warning",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def formatted_flash(message_name, message_args=None):
|
||||
config = MESSAGES[message_name]
|
||||
args = message_args or {}
|
||||
title = render_template_string(config["title_template"], **args)
|
||||
message = render_template_string(config["message_template"], **args)
|
||||
flash({"title": title, "message": message}, config["category"])
|
Reference in New Issue
Block a user