diff --git a/.ds.baseline b/.ds.baseline
index 0aff1ada14..c64d226275 100644
--- a/.ds.baseline
+++ b/.ds.baseline
@@ -161,7 +161,7 @@
"filename": "app/config.py",
"hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc",
"is_verified": false,
- "line_number": 121,
+ "line_number": 125,
"is_secret": false
}
],
@@ -684,5 +684,5 @@
}
]
},
- "generated_at": "2024-10-29T19:28:03Z"
+ "generated_at": "2024-11-14T15:53:44Z"
}
diff --git a/README.md b/README.md
index e1a372fd32..95a07dbd31 100644
--- a/README.md
+++ b/README.md
@@ -372,15 +372,9 @@ After you have completed all setup steps, you will be unable to log in, because
will not be a user in the database to link to the login.gov account you are using. So
you will need to create that user in your database using the 'create-test-user' command.
-Open two terminals pointing to the api project and then run these commands in the
-respective terminals.
+Open a terminal pointing to the api project and then run this command.
-(Server 1)
-env ALLOW_EXPIRED_API_TOKEN=1 make run-flask
-
-(Server 2)
-poetry run flask command create-admin-jwt | tail -n 1 | pbcopy
-poetry run flask command create-test-user --admin=True;
+```poetry run flask command create-test-user --admin=True```
Supply your name, email address, mobile number, and password when prompted. Make sure the email address
is the same one you are using in login.gov and make sure your phone number is in the format 5555555555.
diff --git a/app/__init__.py b/app/__init__.py
index 483d89ea06..64580fcc13 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -162,6 +162,17 @@ def _csp(config):
def create_app(application):
+ application.config["FEATURE_BEST_PRACTICES_ENABLED"] = (
+ os.getenv("FEATURE_BEST_PRACTICES_ENABLED", "false").lower() == "true"
+ )
+
+ @application.context_processor
+ def inject_feature_flags():
+ feature_best_practices_enabled = application.config[
+ "FEATURE_BEST_PRACTICES_ENABLED"
+ ]
+ return dict(FEATURE_BEST_PRACTICES_ENABLED=feature_best_practices_enabled)
+
notify_environment = os.environ["NOTIFY_ENVIRONMENT"]
application.config.from_object(configs[notify_environment])
diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss
index fe98aed09b..da5d77bf24 100644
--- a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss
+++ b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss
@@ -897,3 +897,22 @@ li.linked-card:hover svg,
.usa-sidenav__item a {
display: block;
}
+
+.about-icon-list {
+ display: flex;
+ width: 24px;
+ height: 24px;
+ padding: 2px 1px;
+ justify-content: center;
+ align-items: center;
+ margin-right: 4px;
+}
+
+.usa-icon-list__content{
+ padding-left: 0;
+}
+
+.indented-paragraph {
+ margin-left: calc(24px + 4px);
+ margin-top: 4px;
+}
diff --git a/app/config.py b/app/config.py
index 249451c66f..f40b46dea3 100644
--- a/app/config.py
+++ b/app/config.py
@@ -91,6 +91,10 @@ class Config(object):
getenv("FEATURE_BEST_PRACTICES_ENABLED", "false") == "true"
)
+ FEATURE_ABOUT_PAGE_ENABLED = (
+ getenv("FEATURE_ABOUT_PAGE_ENABLED", "false") == "true"
+ )
+
def _s3_credentials_from_env(bucket_prefix):
return {
diff --git a/app/main/views/index.py b/app/main/views/index.py
index 28ede59d17..370a884015 100644
--- a/app/main/views/index.py
+++ b/app/main/views/index.py
@@ -2,7 +2,15 @@
import secrets
from urllib.parse import unquote
-from flask import abort, current_app, redirect, render_template, request, url_for
+from flask import (
+ abort,
+ current_app,
+ jsonify,
+ redirect,
+ render_template,
+ request,
+ url_for,
+)
from flask_login import current_user
from app import redis_client, status_api_client
@@ -10,6 +18,7 @@
from app.main import main
from app.main.views.pricing import CURRENT_SMS_RATE
from app.main.views.sub_navigation_dictionaries import (
+ about_notify_nav,
best_practices_nav,
features_nav,
using_notify_nav,
@@ -18,18 +27,33 @@
from notifications_utils.url_safe_token import generate_token
-# Hook to check for guidance routes
+# Hook to check for feature flags
@main.before_request
-def check_guidance_feature():
- current_app.logger.warning("best practices 1234")
- current_app.logger.warning(current_app.config["FEATURE_BEST_PRACTICES_ENABLED"])
+def check_feature_flags():
+ if (
+ request.path.startswith("/guides/best-practices")
+ and not current_app.config.get("FEATURE_BEST_PRACTICES_ENABLED", False)
+ ):
+ abort(404)
+
if (
- request.path.startswith("/best-practices")
- and not current_app.config["FEATURE_BEST_PRACTICES_ENABLED"]
+ request.path.startswith("/about")
+ and not current_app.config.get("FEATURE_ABOUT_PAGE_ENABLED", False)
):
abort(404)
+@main.route("/test/feature-flags")
+def test_feature_flags():
+ return jsonify(
+ {
+ "FEATURE_BEST_PRACTICES_ENABLED": current_app.config[
+ "FEATURE_BEST_PRACTICES_ENABLED"
+ ]
+ }
+ )
+
+
@main.route("/")
def index():
if current_user and current_user.is_authenticated:
@@ -207,7 +231,7 @@ def trial_mode_new():
)
-@main.route("/best-practices")
+@main.route("/guides/best-practices")
@user_is_logged_in
def best_practices():
return render_template(
@@ -216,7 +240,7 @@ def best_practices():
)
-@main.route("/best-practices/clear-goals")
+@main.route("/guides/best-practices/clear-goals")
@user_is_logged_in
def clear_goals():
return render_template(
@@ -225,7 +249,7 @@ def clear_goals():
)
-@main.route("/best-practices/rules-and-regulations")
+@main.route("/guides/best-practices/rules-and-regulations")
@user_is_logged_in
def rules_and_regulations():
return render_template(
@@ -234,7 +258,7 @@ def rules_and_regulations():
)
-@main.route("/best-practices/establish-trust")
+@main.route("/guides/best-practices/establish-trust")
@user_is_logged_in
def establish_trust():
return render_template(
@@ -243,7 +267,7 @@ def establish_trust():
)
-@main.route("/best-practices/write-for-action")
+@main.route("/guides/best-practices/write-for-action")
@user_is_logged_in
def write_for_action():
return render_template(
@@ -252,7 +276,7 @@ def write_for_action():
)
-@main.route("/best-practices/multiple-languages")
+@main.route("/guides/best-practices/multiple-languages")
@user_is_logged_in
def multiple_languages():
return render_template(
@@ -261,7 +285,7 @@ def multiple_languages():
)
-@main.route("/best-practices/benchmark-performance")
+@main.route("/guides/best-practices/benchmark-performance")
@user_is_logged_in
def benchmark_performance():
return render_template(
@@ -270,7 +294,7 @@ def benchmark_performance():
)
-@main.route("/using-notify/guidance")
+@main.route("/guides/using-notify/guidance")
@user_is_logged_in
def guidance_index():
return render_template(
@@ -282,6 +306,14 @@ def guidance_index():
)
+@main.route("/about")
+def about_notify():
+ return render_template(
+ "views/about/about.html",
+ navigation_links=about_notify_nav(),
+ )
+
+
@main.route("/using-notify/guidance/create-and-send-messages")
@user_is_logged_in
def create_and_send_messages():
diff --git a/app/main/views/sub_navigation_dictionaries.py b/app/main/views/sub_navigation_dictionaries.py
index 0689c198d4..b9fb7f8aef 100644
--- a/app/main/views/sub_navigation_dictionaries.py
+++ b/app/main/views/sub_navigation_dictionaries.py
@@ -27,6 +27,10 @@ def using_notify_nav():
"name": "Get started",
"link": "main.get_started",
},
+ {
+ "name": "Guides",
+ "link": "main.best_practices",
+ },
{
"name": "Trial mode",
"link": "main.trial_mode_new",
@@ -101,3 +105,12 @@ def best_practices_nav():
"link": "main.benchmark_performance",
},
]
+
+
+def about_notify_nav():
+ return [
+ {
+ "name": "About notify",
+ "link": "main.about_notify",
+ },
+ ]
diff --git a/app/navigation.py b/app/navigation.py
index 6ef0907a68..a02df484df 100644
--- a/app/navigation.py
+++ b/app/navigation.py
@@ -46,6 +46,15 @@ class HeaderNavigation(Navigation):
"roadmap",
"security",
},
+ "best_practices": {
+ "best_practices",
+ "clear_goals",
+ "rules_and_regulations",
+ "establish_trust",
+ "write_for_action",
+ "multiple_languages",
+ "benchmark_performance"
+ },
"using_notify": {
"get_started",
"using_notify",
diff --git a/app/templates/components/folder-path.html b/app/templates/components/folder-path.html
index c686702ba4..b6615a2da7 100644
--- a/app/templates/components/folder-path.html
+++ b/app/templates/components/folder-path.html
@@ -22,7 +22,7 @@
{{ folder.name }}
{% endif %}
{% else %}
- Templates
+ Templates
{% endif %}
{% if not loop.last %}{{ folder_path_separator() }}{% endif %}
{% endif %}
diff --git a/app/templates/components/header.html b/app/templates/components/header.html
index 5ae37fc714..248abf2508 100644
--- a/app/templates/components/header.html
+++ b/app/templates/components/header.html
@@ -1,32 +1,43 @@
{% if current_user.is_authenticated %}
- {% set navigation = [
- {"href": url_for("main.show_accounts_or_dashboard"), "text": "Current service", "active": header_navigation.is_selected('accounts-or-dashboard')},
- {"href": url_for('main.get_started'), "text": "Using Notify", "active": header_navigation.is_selected('using_notify')},
- {"href": url_for('main.features'), "text": "Features", "active": header_navigation.is_selected('features')},
- {"href": url_for('main.support'), "text": "Contact us", "active": header_navigation.is_selected('support')}
- ] %}
+{% set navigation = [
+{"href": url_for("main.show_accounts_or_dashboard"), "text": "Current service", "active":
+header_navigation.is_selected('accounts-or-dashboard')},
+{"href": url_for('main.get_started'), "text": "Using Notify", "active": header_navigation.is_selected('using_notify')}
+] %}
- {% if current_user.platform_admin %}
- {% set navigation = navigation + [{"href": url_for('main.platform_admin_splash_page'), "text": "Platform admin", "active": header_navigation.is_selected('platform-admin')}] %}
- {% else %}
- {% set navigation = navigation + [{"href": url_for('main.user_profile'), "text": "User profile", "active": header_navigation.is_selected('user-profile')}] %}
- {% endif %}
+{% if FEATURE_BEST_PRACTICES_ENABLED %}
+{% set navigation = navigation + [{"href": url_for('main.best_practices'), "text": "Guides", "active":
+header_navigation.is_selected('best_practices')}] %}
+{% endif %}
+
+{% set navigation = navigation + [
+{"href": url_for('main.features'), "text": "Features", "active": header_navigation.is_selected('features')},
+{"href": url_for('main.support'), "text": "Contact us", "active": header_navigation.is_selected('support')}
+] %}
- {% if current_service %}
- {% if current_user.has_permissions('manage_service') %}
- {% set secondaryNavigation = [
- {"href": url_for('main.service_settings', service_id=current_service.id), "text": "Settings", "active": secondary_navigation.is_selected('settings')},
- {"href": url_for('main.sign_out'), "text": "Sign out"}
- ] %}
- {% else %}
- {% set secondaryNavigation = [
- {"href": url_for('main.sign_out'), "text": "Sign out"}
- ] %}
+{% if current_user.platform_admin %}
+{% set navigation = navigation + [{"href": url_for('main.platform_admin_splash_page'), "text": "Platform admin",
+"active": header_navigation.is_selected('platform-admin')}] %}
+{% else %}
+{% set navigation = navigation + [{"href": url_for('main.user_profile'), "text": "User profile", "active":
+header_navigation.is_selected('user-profile')}] %}
+{% endif %}
- {% endif %}
- {% else %}
- {% set secondaryNavigation = [{"href": url_for('main.sign_out'), "text": "Sign out"}] %}
- {% endif %}
+{% if current_service %}
+{% if current_user.has_permissions('manage_service') %}
+{% set secondaryNavigation = [
+{"href": url_for('main.service_settings', service_id=current_service.id), "text": "Settings", "active":
+secondary_navigation.is_selected('settings')},
+{"href": url_for('main.sign_out'), "text": "Sign out"}
+] %}
+{% else %}
+{% set secondaryNavigation = [
+{"href": url_for('main.sign_out'), "text": "Sign out"}
+] %}
+{% endif %}
+{% else %}
+{% set secondaryNavigation = [{"href": url_for('main.sign_out'), "text": "Sign out"}] %}
+{% endif %}
{% endif %}