diff --git a/.ds.baseline b/.ds.baseline index 0aff1ada1..c64d22627 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 e1a372fd3..95a07dbd3 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 483d89ea0..64580fcc1 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 fe98aed09..da5d77bf2 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 249451c66..f40b46dea 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 2804b91f5..c677b3524 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -19,14 +19,18 @@ from app.utils.user import user_is_logged_in 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("/best-practices") - and not current_app.config["FEATURE_BEST_PRACTICES_ENABLED"] + request.path.startswith("/guides/best-practices") + and not current_app.config.get("FEATURE_BEST_PRACTICES_ENABLED", False) + ): + abort(404) + + if ( + request.path.startswith("/about") + and not current_app.config.get("FEATURE_ABOUT_PAGE_ENABLED", False) ): abort(404) @@ -208,7 +212,7 @@ def trial_mode_new(): ) -@main.route("/best-practices") +@main.route("/guides/best-practices") @user_is_logged_in def best_practices(): return render_template( @@ -217,7 +221,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( @@ -226,7 +230,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( @@ -235,7 +239,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( @@ -244,7 +248,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( @@ -253,7 +257,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( @@ -262,7 +266,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( @@ -281,6 +285,7 @@ def why_text_messaging(): @main.route("/using-notify/guidance") +@main.route("/guides/using-notify/guidance") @user_is_logged_in def guidance_index(): return render_template( @@ -292,6 +297,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 e7001c95e..1f7725228 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", @@ -106,7 +110,13 @@ def best_practices_nav(): def about_notify_nav(): return [ { - "name": "Why text messaging", - "link": "main.why_text_messaging", + "name": "About notify", + "link": "main.about_notify", + "sub_navigation_items": [ + { + "name": "Why text messaging", + "link": "main.why_text_messaging", + }, + ], }, ] diff --git a/app/navigation.py b/app/navigation.py index 6ef0907a6..a02df484d 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/header.html b/app/templates/components/header.html index 5ae37fc71..248abf250 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 %} - {% 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"} - ] %} +{% 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')} +] %} - {% endif %} - {% else %} - {% set secondaryNavigation = [{"href": url_for('main.sign_out'), "text": "Sign out"}] %} - {% endif %} +{% 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 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 %}
@@ -36,12 +47,12 @@
Notify.gov logo - - +
{% if navigation %} - + {% endif %} @@ -52,29 +63,29 @@
diff --git a/app/templates/views/about/about.html b/app/templates/views/about/about.html new file mode 100644 index 000000000..39dfce671 --- /dev/null +++ b/app/templates/views/about/about.html @@ -0,0 +1,77 @@ +{% extends "base.html" %} + +{% set page_title = "About notify" %} + +{% block per_page_title %} +{{page_title}} +{% endblock %} + +{% block content_column_content %} + +
+

{{page_title}}

+

Notify.gov is a text messaging service built by and for the government. We help agencies communicate more + effectively with the people they serve. With Notify.gov, federal and federally-funded programs can send customized + text messages that make it possible to:

+ +

Notify.gov is an easy-to-use, web-based platform. It requires no technical expertise or system integration — users + can create an account and get started within minutes. We take the security and privacy of messaging data seriously + by minimizing data retention and using modern encryption methods.

+ +

Product Highlights

+ {% set product_highlights = [ + { + "svg_src": "#send", + "card_heading": "Send customized one-way customized messages", + "p_text": "Upload a file with recipient phone numbers and Notify.gov sends customized messages", + }, + { + "svg_src": "#translate", + "card_heading": "Send in recipients’ preferred language", + "p_text": "Notify.gov supports more than 30 character sets to send messages in almost any language", + }, + { + "svg_src": "#trending_up", + "card_heading": "See how messages perform", + "p_text": "Track how many messages you’ve sent and monitor delivery rates", + }, + { + "svg_src": "#add", + "card_heading": "Create and manage multiple services within a single organization", + "p_text": "Set up individual workspaces for different texting services, allowing multiple teams or programs to manage + day-to-day texting operations across an organization", + }, + { + "svg_src": "#people", + "card_heading": "Manage team member and permissions on each service", + "p_text": "Administrators can add users and control what they can do in Notify.gov", + } + ] %} + +

See if Notify is right for you

+

Notify.gov is a product of the Public Benefits Studio, a product accelerator inside + the federal government.

+ + +
+{% endblock %} diff --git a/app/templates/views/best-practices/best-practices.html b/app/templates/views/best-practices/best-practices.html index a00a48660..7711dea9b 100644 --- a/app/templates/views/best-practices/best-practices.html +++ b/app/templates/views/best-practices/best-practices.html @@ -23,37 +23,37 @@ Best Practices "svg_src": "goal", "card_heading": "Establish clear goals", "p_text": "Start with a singular purpose. Make explicit what you want to achieve.", - "link": "/best-practices/clear-goals" + "link": "/guides/best-practices/clear-goals" }, { "svg_src": "compliant", "card_heading": "Follow rules & regulations", "p_text": "Understand what is required when texting the public.", - "link": "/best-practices/rules-and-regulations" + "link": "/guides/best-practices/rules-and-regulations" }, { "svg_src": "trust", "card_heading": "Establish trust", "p_text": "Help your audience anticipate and welcome your texts.", - "link": "/best-practices/establish-trust" + "link": "/guides/best-practices/establish-trust" }, { "svg_src": "runner", "card_heading": "Write texts that provoke action", "p_text": "Help your audience know what to do with the information you send.", - "link": "/best-practices/write-for-action" + "link": "/guides/best-practices/write-for-action" }, { "svg_src": "language", "card_heading": "Send texts in multiple languages", "p_text": "What to know as you plan translated texts.", - "link": "/best-practices/multiple-languages" + "link": "/guides/best-practices/multiple-languages" }, { "svg_src": "chart", "card_heading": "Measure performance with benchmarking", "p_text": "Learn how effective your texting program can be.", - "link": "/best-practices/benchmark-performance" + "link": "/guides/best-practices/benchmark-performance" } ] %} diff --git a/tests/app/test_navigation.py b/tests/app/test_navigation.py index 03f8e8c86..9d808c4e4 100644 --- a/tests/app/test_navigation.py +++ b/tests/app/test_navigation.py @@ -17,6 +17,7 @@ EXCLUDED_ENDPOINTS = tuple( map( Navigation.get_endpoint_with_blueprint, { + "about_notify", "accept_invite", "accept_org_invite", "accessibility_statement", diff --git a/urls.js b/urls.js index 8ff543470..0440430ec 100644 --- a/urls.js +++ b/urls.js @@ -13,13 +13,25 @@ const sublinks = [ { label: 'Roadmap', path: '/features/roadmap' }, { label: 'Security', path: '/features/security' }, { label: 'Support', path: '/support' }, - { label: 'Best Practices', path: '/best-practices' }, - { label: 'Clear Goals', path: '/best-practices/clear-goals' }, - { label: 'Rules And Regulations', path: '/best-practices/rules-and-regulations' }, - { label: 'Establish Trust', path: '/best-practices/establish-trust' }, - { label: 'Write For Action', path: '/best-practices/write-for-action' }, - { label: 'Multiple Languages', path: '/best-practices/multiple-languages' }, - { label: 'Benchmark Performance', path: '/best-practices/benchmark-performance' }, + { label: 'Best Practices', path: '/guides/best-practices' }, + { label: 'Clear Goals', path: '/guides/best-practices/clear-goals' }, + { + label: 'Rules And Regulations', + path: '/guides/best-practices/rules-and-regulations', + }, + { label: 'Establish Trust', path: '/guides/best-practices/establish-trust' }, + { + label: 'Write For Action', + path: '/guides/best-practices/write-for-action', + }, + { + label: 'Multiple Languages', + path: '/guides/best-practices/multiple-languages', + }, + { + label: 'Benchmark Performance', + path: '/guides/best-practices/benchmark-performance', + }, // Add more links here as needed ];