diff --git a/.github/workflows/clone-staging.yaml b/.github/workflows/clone-staging.yaml index 790246328..ef259c3b6 100644 --- a/.github/workflows/clone-staging.yaml +++ b/.github/workflows/clone-staging.yaml @@ -19,6 +19,7 @@ jobs: CF_USERNAME: ${{ secrets.CF_MS_USERNAME }} CF_PASSWORD: ${{ secrets.CF_MS_PASSWORD }} steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Clone Database run: | # install cf cli and other tools diff --git a/.github/workflows/createcachetable.yaml b/.github/workflows/createcachetable.yaml index 207ecf70e..768bf050d 100644 --- a/.github/workflows/createcachetable.yaml +++ b/.github/workflows/createcachetable.yaml @@ -37,6 +37,7 @@ jobs: CF_USERNAME: CF_${{ github.event.inputs.environment }}_USERNAME CF_PASSWORD: CF_${{ github.event.inputs.environment }}_PASSWORD steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Create cache table for ${{ github.event.inputs.environment }} uses: cloud-gov/cg-cli-tools@main with: diff --git a/.github/workflows/daily-csv-upload.yaml b/.github/workflows/daily-csv-upload.yaml index 9cacfc3bf..41fb00971 100644 --- a/.github/workflows/daily-csv-upload.yaml +++ b/.github/workflows/daily-csv-upload.yaml @@ -13,6 +13,7 @@ jobs: CF_USERNAME: CF_${{ secrets.CF_REPORT_ENV }}_USERNAME CF_PASSWORD: CF_${{ secrets.CF_REPORT_ENV }}_PASSWORD steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Generate current-federal.csv uses: cloud-gov/cg-cli-tools@main with: diff --git a/.github/workflows/deploy-development.yaml b/.github/workflows/deploy-development.yaml index fa447ed76..9b4ce00e1 100644 --- a/.github/workflows/deploy-development.yaml +++ b/.github/workflows/deploy-development.yaml @@ -17,6 +17,8 @@ jobs: deploy-development: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + - uses: actions/checkout@v3 - name: Compile USWDS assets diff --git a/.github/workflows/deploy-manual.yaml b/.github/workflows/deploy-manual.yaml index a85cc7565..7033bc129 100644 --- a/.github/workflows/deploy-manual.yaml +++ b/.github/workflows/deploy-manual.yaml @@ -44,6 +44,7 @@ jobs: variables: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Setting global variables uses: actions/github-script@v6 id: var @@ -53,6 +54,7 @@ jobs: deploy: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v3 - name: Compile USWDS assets working-directory: ./src diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index e9eb06627..52d0d1830 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -35,6 +35,7 @@ jobs: environment: ${{ steps.var.outputs.environment}} runs-on: "ubuntu-latest" steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Setting global variables uses: actions/github-script@v6 id: var @@ -45,6 +46,7 @@ jobs: runs-on: ubuntu-latest needs: [variables] steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v3 - name: Compile USWDS assets working-directory: ./src diff --git a/.github/workflows/deploy-stable.yaml b/.github/workflows/deploy-stable.yaml index a1b947ca5..7a2e4a940 100644 --- a/.github/workflows/deploy-stable.yaml +++ b/.github/workflows/deploy-stable.yaml @@ -18,6 +18,7 @@ jobs: if: ${{ github.ref_type == 'tag' }} runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v3 - name: Compile USWDS assets diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml index 3cf5ad5a1..81e87b2df 100644 --- a/.github/workflows/deploy-staging.yaml +++ b/.github/workflows/deploy-staging.yaml @@ -18,6 +18,7 @@ jobs: if: ${{ github.ref_type == 'tag' }} runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v3 - name: Compile USWDS assets diff --git a/.github/workflows/issue-label-notifier.yaml b/.github/workflows/issue-label-notifier.yaml index c4f10d48f..ee6aaa844 100644 --- a/.github/workflows/issue-label-notifier.yaml +++ b/.github/workflows/issue-label-notifier.yaml @@ -10,6 +10,7 @@ jobs: notify: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: jenschelkopf/issue-label-notification-action@1.3 with: recipients: | diff --git a/.github/workflows/migrate.yaml b/.github/workflows/migrate.yaml index 1853b3c4f..e18791cad 100644 --- a/.github/workflows/migrate.yaml +++ b/.github/workflows/migrate.yaml @@ -45,6 +45,7 @@ jobs: CF_USERNAME: CF_${{ github.event.inputs.environment }}_USERNAME CF_PASSWORD: CF_${{ github.event.inputs.environment }}_PASSWORD steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Run Django migrations for ${{ github.event.inputs.environment }} uses: cloud-gov/cg-cli-tools@main with: diff --git a/.github/workflows/reset-db.yaml b/.github/workflows/reset-db.yaml index 111555b3c..8e060beaa 100644 --- a/.github/workflows/reset-db.yaml +++ b/.github/workflows/reset-db.yaml @@ -45,6 +45,7 @@ jobs: CF_USERNAME: CF_${{ github.event.inputs.environment }}_USERNAME CF_PASSWORD: CF_${{ github.event.inputs.environment }}_PASSWORD steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Delete existing data for ${{ github.event.inputs.environment }} uses: cloud-gov/cg-cli-tools@main with: diff --git a/.github/workflows/security-check.yaml b/.github/workflows/security-check.yaml index aea700613..ae58b37c6 100644 --- a/.github/workflows/security-check.yaml +++ b/.github/workflows/security-check.yaml @@ -54,6 +54,7 @@ jobs: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Check out uses: actions/checkout@v3 - name: MockUserLogin should not be in settings.MIDDLEWARE diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 642e9dc30..7c7576cae 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -21,6 +21,7 @@ jobs: python-linting: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v3 - name: Linting @@ -32,6 +33,7 @@ jobs: python-test: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v3 - name: Unit tests @@ -41,6 +43,7 @@ jobs: django-migrations-complete: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v3 - name: Check for complete migrations diff --git a/docs/operations/runbooks/rotate_application_secrets.md b/docs/operations/runbooks/rotate_application_secrets.md index 1094b4ff7..1d36f6a74 100644 --- a/docs/operations/runbooks/rotate_application_secrets.md +++ b/docs/operations/runbooks/rotate_application_secrets.md @@ -3,7 +3,7 @@ Secrets are read from the running environment. -Secrets were originally created with: +Secrets are originally created with: ```sh cf cups getgov-credentials -p credentials-.json @@ -38,6 +38,49 @@ cf restage getgov-stable --strategy rolling Non-secret environment variables can be declared in `manifest-.json` directly. +## Rotating login.gov credentials +The DJANGO_SECRET_KEY and DJANGO_SECRET_LOGIN_KEY are reset once a year for each sandbox, see their sections below for more information on them and how to manually generate these keys. To save time, complete the following steps to rotate these credentials using a script in non-production environments: + +### Step 1 login + +To run the script make sure you are logged on the cf cli and make sure you have access to the [Login Partner Dashboard](https://dashboard.int.identitysandbox.gov/service_providers/2640). + +### Step 2 Run the script + +Run the following where "ENV" refers to whichever sandbox you want to reset credentials on. Note, the below assumes you are in the root directory of our app. + +```bash +ops/scripts/rotate_login_certs.sh ENV +``` + +### Step 3 Respond to the terminal prompts + +Respond to the prompts from the script and, when it asks for the cert information, the below is an example of what you should enter. Note for "Common Name" you should put the name of the sandbox and for "Email Address" it should be the address of who owns that sandbox (such as the developer's email, if it's a develop sandbox, or whoever ran this action otherwise) + +```bash +Country Name (2 letter code) [AU]:US +State or Province Name (full name) [Some-State]:DC +Locality Name (eg, city) []:DC +Organization Name (eg, company) [Internet Widgits Pty Ltd]:DHS +Organizational Unit Name (eg, section) []:CISA +Common Name (e.g. server FQDN or YOUR name) []:ENV +Email Address []: example@something.com +``` + +Note when this script is done it will have generated a .pem and a .crt file, as well as updated the cert info on the sandbox + +### Step 4 Delete the old cert + +Navigate to to the Login Partner Dashboard linked above and delete the old cert + +### Step 5 add the new cert + +In whichever directory you ran the script there should now be a .crt file named "public-ENV.crt", where ENV is the space name you used on Step 2. Upload this cert in the Login Partner Dashboard in the same section where you deleted the old one. + +### Production only + +This script should not be run in production. Instead, you will need to manually create the keys and then refrain from updating the sandbox. Once the cert is created you will upload it to the Login Partner Dashboard for our production system, and then open a ticket with them to update our existing Login.gov integration. Once they respond back saying it has been applied, you can then update the sandbox. + ## DJANGO_SECRET_KEY This is a standard Django secret key. See Django documentation for tips on generating a new one. @@ -46,6 +89,7 @@ This is a standard Django secret key. See Django documentation for tips on gener This is the base64 encoded private key used in the OpenID Connect authentication flow with Login.gov. It is used to sign a token during user login; the signature is examined by Login.gov before their API grants access to user data. +### Manually creating creating the Login Key Generate a new key using this command (or whatever is most recently [recommended by Login.gov](https://developers.login.gov/testing/#creating-a-public-certificate)): ```bash @@ -60,6 +104,8 @@ base64 private.pem You also need to upload the `public.crt` key if recently created to the login.gov identity sandbox: https://dashboard.int.identitysandbox.gov/ + + ## AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY To access the AWS Simple Email Service, we need credentials from the CISA AWS @@ -76,6 +122,8 @@ These are the client certificate and its private key used to identify the regist The private key is protected by a passphrase for safer transport and storage. +Note this must be reset once a year. + These were generated with the following steps: ### Step 1: Generate an unencrypted private key with a named curve @@ -90,7 +138,7 @@ openssl ecparam -name prime256v1 -genkey -out client_unencrypted.key openssl pkcs8 -topk8 -v2 aes-256-cbc -in client_unencrypted.key -out client.key ``` -### Generate the certificate +### Step 3: Generate the certificate ```bash openssl req -new -x509 -days 365 -key client.key -out client.crt -subj "/C=US/ST=DC/L=Washington/O=GSA/OU=18F/CN=GOV Prototype Registrar" @@ -112,7 +160,7 @@ base64 -i client.key base64 -i client.crt ``` -You'll need to give the new certificate to the registry vendor _before_ rotating it in production. Once it has been accepted by the vendor, make sure to update the kdbx file on Google Drive. +You'll need to give the new certificate to the registry vendor _before_ rotating it in production. Once it has been accepted by the vendor, make sure to update [the KBDX](https://docs.google.com/document/d/1_BbJmjYZNYLNh4jJPPnUEG9tFCzJrOc0nMrZrnSKKyw) file on Google Drive. ## REGISTRY_HOSTNAME diff --git a/ops/scripts/rotate_login_certs.sh b/ops/scripts/rotate_login_certs.sh new file mode 100755 index 000000000..31363fe36 --- /dev/null +++ b/ops/scripts/rotate_login_certs.sh @@ -0,0 +1,51 @@ +# This script rotates the login.gov credentials, DJANGO_SECRET_KEY and DJANGO_SECRET_LOGIN_KEY that allow for identity sandbox to work on sandboxes and local. +# The echo prints in this script should serve for documentation for running manually. +# Run this script once a year for each environment +# NOTE: This script was written for MacOS and to be run at the root directory. + + +if [ -z "$1" ]; then + echo 'Please specify a space to update (i.e. lmm)' >&2 + exit 1 +fi +echo "You need access to the Login partner dashboard, otherwise you will not be able to complete the steps in this script (https://dashboard.int.identitysandbox.gov/service_providers/2640)" +read -p " Do you have access to the partner dashboard mentioned above? (y/n) " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 +fi + +if [ ! $(command -v jq) ] || [ ! $(command -v cf) ]; then + echo "jq, and cf packages must be installed. Please install via your preferred manager." + exit 1 +fi + +cf target -o cisa-dotgov + +read -p "Are you logged in to the cisa-dotgov CF org above? (y/n) " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]] +then + cf login -a https://api.fr.cloud.gov --sso +fi +echo "Targeting space" +cf target -o cisa-dotgov -s $1 + +echo "Creating new login.gov credentials for $1..." +django_key=$(python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())') +openssl req -noenc -x509 -days 365 -newkey rsa:2048 -keyout private-$1.pem -out public-$1.crt +login_key=$(base64 -i private-$1.pem) + +echo "Creating the final json" +cf env getgov-$1 | awk '/VCAP_SERVICES: /,/^$/' | sed s/VCAP_SERVICES:// | jq '."user-provided"[0].credentials' | jq --arg django_key "$django_key" --arg login_key "$login_key" '. + {"DJANGO_SECRET_KEY":$django_key, "DJANGO_SECRET_LOGIN_KEY":$login_key}' > credentials-$1.json + +echo "Updating creds on the sandbox" +cf uups getgov-credentials -p credentials-$1.json +cf restage getgov-$1 --strategy rolling + +echo "\n\n\nNow you will need to update some things for Login. Please sign-in to https://dashboard.int.identitysandbox.gov/." +echo "Navigate to our application config: https://dashboard.int.identitysandbox.gov/service_providers/2640/edit?" +echo "There are two things to update." +echo "1. Remove the old cert associated with the user's email (under Public Certificates)" +echo "2. You need to upload the public-$1.crt file generated as part of the previous command. See the "choose cert file" button under Public Certificates." +echo "Then, tell the developer to update their local .env file by retrieving their credentials from the sandbox" diff --git a/src/.pa11yci b/src/.pa11yci index 6a5ce4f26..571d0b1c8 100644 --- a/src/.pa11yci +++ b/src/.pa11yci @@ -7,6 +7,7 @@ "http://localhost:8080/", "http://localhost:8080/health/", "http://localhost:8080/request/", + "http://localhost:8080/request/start", "http://localhost:8080/request/organization/", "http://localhost:8080/request/org_federal/", "http://localhost:8080/request/org_election/", diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index 9f8fde0cc..4619befb0 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -39,12 +39,13 @@ from api.views import available, rdap, get_current_federal, get_current_full DOMAIN_REQUEST_NAMESPACE = views.DomainRequestWizard.URL_NAMESPACE + +# dynamically generate the other domain_request_urls domain_request_urls = [ - path("", views.DomainRequestWizard.as_view(), name=""), + path("", RedirectView.as_view(pattern_name="domain-request:start"), name="redirect-to-start"), + path("start/", views.DomainRequestWizard.as_view(), name="start"), path("finished/", views.Finished.as_view(), name="finished"), ] - -# dynamically generate the other domain_request_urls for step, view in [ # add/remove steps here (Step.ORGANIZATION_TYPE, views.OrganizationType), @@ -65,7 +66,7 @@ (PortfolioDomainRequestStep.REQUESTING_ENTITY, views.RequestingEntity), (PortfolioDomainRequestStep.ADDITIONAL_DETAILS, views.PortfolioAdditionalDetails), ]: - domain_request_urls.append(path(f"{step}/", view.as_view(), name=step)) + domain_request_urls.append(path(f"/{step}/", view.as_view(), name=step)) urlpatterns = [ diff --git a/src/registrar/migrations/0137_suborganization_city_suborganization_state_territory.py b/src/registrar/migrations/0137_suborganization_city_suborganization_state_territory.py new file mode 100644 index 000000000..85e8cb60f --- /dev/null +++ b/src/registrar/migrations/0137_suborganization_city_suborganization_state_territory.py @@ -0,0 +1,90 @@ +# Generated by Django 4.2.10 on 2024-11-12 22:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0136_domainrequest_requested_suborganization_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="suborganization", + name="city", + field=models.CharField(blank=True, null=True), + ), + migrations.AddField( + model_name="suborganization", + name="state_territory", + field=models.CharField( + blank=True, + choices=[ + ("AL", "Alabama (AL)"), + ("AK", "Alaska (AK)"), + ("AS", "American Samoa (AS)"), + ("AZ", "Arizona (AZ)"), + ("AR", "Arkansas (AR)"), + ("CA", "California (CA)"), + ("CO", "Colorado (CO)"), + ("CT", "Connecticut (CT)"), + ("DE", "Delaware (DE)"), + ("DC", "District of Columbia (DC)"), + ("FL", "Florida (FL)"), + ("GA", "Georgia (GA)"), + ("GU", "Guam (GU)"), + ("HI", "Hawaii (HI)"), + ("ID", "Idaho (ID)"), + ("IL", "Illinois (IL)"), + ("IN", "Indiana (IN)"), + ("IA", "Iowa (IA)"), + ("KS", "Kansas (KS)"), + ("KY", "Kentucky (KY)"), + ("LA", "Louisiana (LA)"), + ("ME", "Maine (ME)"), + ("MD", "Maryland (MD)"), + ("MA", "Massachusetts (MA)"), + ("MI", "Michigan (MI)"), + ("MN", "Minnesota (MN)"), + ("MS", "Mississippi (MS)"), + ("MO", "Missouri (MO)"), + ("MT", "Montana (MT)"), + ("NE", "Nebraska (NE)"), + ("NV", "Nevada (NV)"), + ("NH", "New Hampshire (NH)"), + ("NJ", "New Jersey (NJ)"), + ("NM", "New Mexico (NM)"), + ("NY", "New York (NY)"), + ("NC", "North Carolina (NC)"), + ("ND", "North Dakota (ND)"), + ("MP", "Northern Mariana Islands (MP)"), + ("OH", "Ohio (OH)"), + ("OK", "Oklahoma (OK)"), + ("OR", "Oregon (OR)"), + ("PA", "Pennsylvania (PA)"), + ("PR", "Puerto Rico (PR)"), + ("RI", "Rhode Island (RI)"), + ("SC", "South Carolina (SC)"), + ("SD", "South Dakota (SD)"), + ("TN", "Tennessee (TN)"), + ("TX", "Texas (TX)"), + ("UM", "United States Minor Outlying Islands (UM)"), + ("UT", "Utah (UT)"), + ("VT", "Vermont (VT)"), + ("VI", "Virgin Islands (VI)"), + ("VA", "Virginia (VA)"), + ("WA", "Washington (WA)"), + ("WV", "West Virginia (WV)"), + ("WI", "Wisconsin (WI)"), + ("WY", "Wyoming (WY)"), + ("AA", "Armed Forces Americas (AA)"), + ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), + ("AP", "Armed Forces Pacific (AP)"), + ], + max_length=2, + null=True, + verbose_name="state, territory, or military post", + ), + ), + ] diff --git a/src/registrar/models/suborganization.py b/src/registrar/models/suborganization.py index 6ad80fdc0..087490244 100644 --- a/src/registrar/models/suborganization.py +++ b/src/registrar/models/suborganization.py @@ -1,4 +1,6 @@ from django.db import models + +from registrar.models.domain_request import DomainRequest from .utility.time_stamped_model import TimeStampedModel @@ -19,5 +21,18 @@ class Suborganization(TimeStampedModel): related_name="portfolio_suborganizations", ) + city = models.CharField( + null=True, + blank=True, + ) + + state_territory = models.CharField( + max_length=2, + choices=DomainRequest.StateTerritoryChoices.choices, + null=True, + blank=True, + verbose_name="state, territory, or military post", + ) + def __str__(self) -> str: return f"{self.name}" diff --git a/src/registrar/registrar_middleware.py b/src/registrar/registrar_middleware.py index 2ccea9321..ed7a4dffc 100644 --- a/src/registrar/registrar_middleware.py +++ b/src/registrar/registrar_middleware.py @@ -92,7 +92,7 @@ def _handle_user_setup_not_finished(self, request, profile_page): We set the "redirect" query param equal to where the user wants to go. - If the user wants to go to '/request/', then we set that + If the user wants to go to '/request/start/' or '/request/', then we set that information in the query param. Otherwise, we assume they want to go to the home page. @@ -100,7 +100,8 @@ def _handle_user_setup_not_finished(self, request, profile_page): # In some cases, we don't want to redirect to home. This handles that. # Can easily be generalized if need be, but for now lets keep this easy to read. - custom_redirect = "domain-request:" if request.path == "/request/" else None + start_paths = ["/request/", "/request/start/"] + custom_redirect = "domain-request:start" if request.path in start_paths else None # Don't redirect on excluded pages (such as the setup page itself) if not any(request.path.startswith(page) for page in self._get_excluded_pages(profile_page)): diff --git a/src/registrar/templates/domain_request_form.html b/src/registrar/templates/domain_request_form.html index 9228e51db..a076220cb 100644 --- a/src/registrar/templates/domain_request_form.html +++ b/src/registrar/templates/domain_request_form.html @@ -10,32 +10,37 @@
- {% if steps.prev %} - + + {% if steps.current == steps.first %} + {% if portfolio %} + {% url 'domain-requests' as url_2 %} + {% else %} + {% url 'home' as url_2 %} + {% endif %} + + {% elif steps.prev %} + Previous step - {% comment %} - TODO: uncomment in #2596 - {% else %} - {% if portfolio %} - {% url 'domain-requests' as url_2 %} - - {% endif %} {% endcomment %} {% endif %} {% block form_messages %} diff --git a/src/registrar/templates/domain_request_intro.html b/src/registrar/templates/domain_request_intro.html index 6b5223991..dd5b7ec6e 100644 --- a/src/registrar/templates/domain_request_intro.html +++ b/src/registrar/templates/domain_request_intro.html @@ -21,7 +21,7 @@

Time to complete the form

If you have all the information you need, completing your domain request might take around 15 minutes.

How we’ll reach you

-

While reviewing your domain request, we may need to reach out with questions. We’ll also email you when we complete our review. If the contact information below is not correct, visit your profile to make updates.

+

While reviewing your domain request, we may need to reach out with questions. We’ll also email you when we complete our review. If the contact information below is not correct, visit your profile to make updates.

{% include "includes/profile_information.html" with user=user%} diff --git a/src/registrar/templates/domain_request_sidebar.html b/src/registrar/templates/domain_request_sidebar.html index f9ca5397d..1af54bb24 100644 --- a/src/registrar/templates/domain_request_sidebar.html +++ b/src/registrar/templates/domain_request_sidebar.html @@ -15,7 +15,7 @@ {% endif %} {% endif %} - Manage your domains - {% comment %} - IMPORTANT: - If this button is added on any other page, make sure to update the - relevant view to reset request.session["new_request"] = True - {% endcomment %}

- Start a new domain request diff --git a/src/registrar/templates/includes/header_extended.html b/src/registrar/templates/includes/header_extended.html index a954eb30f..6384b5249 100644 --- a/src/registrar/templates/includes/header_extended.html +++ b/src/registrar/templates/includes/header_extended.html @@ -72,7 +72,7 @@ >

  • - Start a new domain request
  • diff --git a/src/registrar/templates/includes/portfolio_request_review_steps.html b/src/registrar/templates/includes/portfolio_request_review_steps.html index d0bdd7ba0..5c6e64269 100644 --- a/src/registrar/templates/includes/portfolio_request_review_steps.html +++ b/src/registrar/templates/includes/portfolio_request_review_steps.html @@ -4,7 +4,7 @@ {% for step in steps %}
    {% if is_editable %} - {% namespaced_url 'domain-request' step as domain_request_url %} + {% namespaced_url 'domain-request' step id=domain_request_id as domain_request_url %} {% endif %} {% if step == Step.REQUESTING_ENTITY %} diff --git a/src/registrar/templates/includes/request_review_steps.html b/src/registrar/templates/includes/request_review_steps.html index db1743b34..73b71d536 100644 --- a/src/registrar/templates/includes/request_review_steps.html +++ b/src/registrar/templates/includes/request_review_steps.html @@ -4,7 +4,7 @@ {% for step in steps %}
    {% if is_editable %} - {% namespaced_url 'domain-request' step as domain_request_url %} + {% namespaced_url 'domain-request' step id=domain_request_id as domain_request_url %} {% endif %} {% if step == Step.ORGANIZATION_TYPE %} diff --git a/src/registrar/templates/portfolio_requests.html b/src/registrar/templates/portfolio_requests.html index d21bbcc4e..0eea5f6bd 100644 --- a/src/registrar/templates/portfolio_requests.html +++ b/src/registrar/templates/portfolio_requests.html @@ -22,13 +22,9 @@

    Domain requests

    Domain requests can only be modified by the person who created the request.

    - {% comment %} - IMPORTANT: - If this button is added on any other page, make sure to update the - relevant view to reset request.session["new_request"] = True - {% endcomment %} +

    - Start a new domain request diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 85ff5edd6..f46e417be 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -49,9 +49,9 @@ def test_home_page(self): @less_console_noise_decorator def test_domain_request_form_not_logged_in(self): """Domain request form not accessible without a logged-in user.""" - response = self.client.get("/request/") + response = self.client.get(reverse("domain-request:start")) self.assertEqual(response.status_code, 302) - self.assertIn("/login?next=/request/", response.headers["Location"]) + self.assertIn("/login?next=/request/start/", response.headers["Location"]) class TestWithUser(MockEppLib): @@ -476,7 +476,7 @@ def test_home_deletes_domain_request_and_shared_orphans(self): @less_console_noise_decorator def test_domain_request_form_view(self): - response = self.client.get("/request/", follow=True) + response = self.client.get(reverse("domain-request:start"), follow=True) self.assertContains( response, "You’re about to start your .gov domain request.", @@ -503,7 +503,7 @@ def test_domain_request_form_with_ineligible_user(self): title="title", ) self.client.force_login(restricted_user) - response = self.client.get("/request/", follow=True) + response = self.client.get(reverse("domain-request:start"), follow=True) self.assertEqual(response.status_code, 403) restricted_user.delete() @@ -718,7 +718,7 @@ def test_new_user_goes_to_domain_request(self): self.app.set_user(incomplete_regular_user.username) with override_flag("", active=True): # This will redirect the user to the setup page - finish_setup_page = self.app.get(reverse("domain-request:")).follow() + finish_setup_page = self.app.get(reverse("domain-request:start")).follow() self._set_session_cookie() # Assert that we're on the right page @@ -914,7 +914,7 @@ def test_home_page_main_nav(self): @less_console_noise_decorator def test_new_request_main_nav(self): """test that Your profile is in main nav of new request""" - response = self.client.get("/request/", follow=True) + response = self.client.get(reverse("domain-request:start"), follow=True) self.assertContains(response, "Your profile") @less_console_noise_decorator @@ -927,7 +927,7 @@ def test_user_profile_main_nav(self): def test_user_profile_back_button_when_coming_from_domain_request(self): """tests user profile, and when they are redirected from the domain request page""" - response = self.client.get("/user-profile?redirect=domain-request:") + response = self.client.get("/user-profile?redirect=domain-request:start") self.assertContains(response, "Your profile") self.assertContains(response, "Go back to your domain request") self.assertNotContains(response, "Back to manage your domains") diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py index 402d23b70..a50a78b23 100644 --- a/src/registrar/tests/test_views_portfolio.py +++ b/src/registrar/tests/test_views_portfolio.py @@ -1645,7 +1645,7 @@ def tearDown(self): def test_requesting_entity_page_new_request(self): """Tests that the requesting entity page loads correctly when a new request is started""" - response = self.app.get(reverse("domain-request:")) + response = self.app.get(reverse("domain-request:start")) # Navigate past the intro page session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] @@ -1672,7 +1672,7 @@ def test_requesting_entity_page_new_request(self): @less_console_noise_decorator def test_requesting_entity_page_existing_suborg_submission(self): """Tests that you can submit a form on this page and set a suborg""" - response = self.app.get(reverse("domain-request:")) + response = self.app.get(reverse("domain-request:start")) # Navigate past the intro page session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] @@ -1705,7 +1705,7 @@ def test_requesting_entity_page_existing_suborg_submission(self): @less_console_noise_decorator def test_requesting_entity_page_new_suborg_submission(self): """Tests that you can submit a form on this page and set a new suborg""" - response = self.app.get(reverse("domain-request:")) + response = self.app.get(reverse("domain-request:start")) # Navigate past the intro page session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] @@ -1745,7 +1745,7 @@ def test_requesting_entity_page_new_suborg_submission(self): @less_console_noise_decorator def test_requesting_entity_page_organization_submission(self): """Tests submitting an organization on the requesting org form""" - response = self.app.get(reverse("domain-request:")) + response = self.app.get(reverse("domain-request:start")) # Navigate past the intro page session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index 73e538df3..a73fac5a8 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -49,12 +49,13 @@ def tearDown(self): super().tearDown() DomainRequest.objects.all().delete() DomainInformation.objects.all().delete() + User.objects.all().delete() self.federal_agency.delete() @less_console_noise_decorator def test_domain_request_form_intro_acknowledgement(self): """Tests that user is presented with intro acknowledgement page""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) self.assertContains(intro_page, "You’re about to start your .gov domain request") @less_console_noise_decorator @@ -105,12 +106,12 @@ def test_domain_request_form_intro_is_skipped_when_edit_access(self): self.assertEqual(detail_page.status_code, 302) # You can access the 'Location' header to get the redirect URL redirect_url = detail_page.url - self.assertEqual(redirect_url, "/request/generic_org_type/") + self.assertEqual(redirect_url, f"/request/{domain_request.id}/generic_org_type/") @less_console_noise_decorator def test_domain_request_form_empty_submit(self): """Tests empty submit on the first page after the acknowledgement page""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -141,7 +142,7 @@ def test_domain_request_multiple_domain_requests_exist(self): domain_request.save() # now, attempt to create another one - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] intro_form = intro_page.forms[0] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) @@ -154,59 +155,6 @@ def test_domain_request_multiple_domain_requests_exist(self): self.assertContains(type_page, "You cannot submit this request yet") - @less_console_noise_decorator - def test_domain_request_into_acknowledgement_creates_new_request(self): - """ - We had to solve a bug where the wizard was creating 2 requests on first intro acknowledgement ('continue') - The wizard was also creating multiiple requests on 'continue' -> back button -> 'continue' etc. - - This tests that the domain requests get created only when they should. - """ - # Get the intro page - self.app.get(reverse("home")) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - intro_page = self.app.get(reverse("domain-request:")) - - # Select the form - intro_form = intro_page.forms[0] - - # Submit the form, this creates 1 Request - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - response = intro_form.submit(name="submit_button", value="intro_acknowledge") - - # Landing on the next page used to create another 1 request - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - response.follow() - - # Check if a new DomainRequest object has been created - domain_request_count = DomainRequest.objects.count() - self.assertEqual(domain_request_count, 1) - - # Let's go back to intro and submit again, this should not create a new request - # This is the equivalent of a back button nav from step 1 to intro -> continue - intro_form = intro_page.forms[0] - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - type_form = intro_form.submit(name="submit_button", value="intro_acknowledge") - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - type_form.follow() - domain_request_count = DomainRequest.objects.count() - self.assertEqual(domain_request_count, 1) - - # Go home, which will reset the session flag for new request - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - self.app.get(reverse("home")) - - # This time, clicking continue will create a new request - intro_form = intro_page.forms[0] - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - intro_result = intro_form.submit(name="submit_button", value="intro_acknowledge") - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - intro_result.follow() - domain_request_count = DomainRequest.objects.count() - self.assertEqual(domain_request_count, 2) - @boto3_mocking.patching @less_console_noise_decorator def test_domain_request_form_submission(self): @@ -225,7 +173,7 @@ def test_domain_request_form_submission(self): SKIPPED_PAGES = 3 num_pages = len(self.TITLES) - SKIPPED_PAGES - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -253,7 +201,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(type_result.status_code, 302) - self.assertEqual(type_result["Location"], "/request/organization_federal/") + self.assertEqual(type_result["Location"], f"/request/{domain_request.id}/organization_federal/") num_pages_tested += 1 # ---- FEDERAL BRANCH PAGE ---- @@ -273,7 +221,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(federal_result.status_code, 302) - self.assertEqual(federal_result["Location"], "/request/organization_contact/") + self.assertEqual(federal_result["Location"], f"/request/{domain_request.id}/organization_contact/") num_pages_tested += 1 # ---- ORG CONTACT PAGE ---- @@ -305,7 +253,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(org_contact_result.status_code, 302) - self.assertEqual(org_contact_result["Location"], "/request/senior_official/") + self.assertEqual(org_contact_result["Location"], f"/request/{domain_request.id}/senior_official/") num_pages_tested += 1 # ---- SENIOR OFFICIAL PAGE ---- @@ -330,7 +278,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(so_result.status_code, 302) - self.assertEqual(so_result["Location"], "/request/current_sites/") + self.assertEqual(so_result["Location"], f"/request/{domain_request.id}/current_sites/") num_pages_tested += 1 # ---- CURRENT SITES PAGE ---- @@ -352,7 +300,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(current_sites_result.status_code, 302) - self.assertEqual(current_sites_result["Location"], "/request/dotgov_domain/") + self.assertEqual(current_sites_result["Location"], f"/request/{domain_request.id}/dotgov_domain/") num_pages_tested += 1 # ---- DOTGOV DOMAIN PAGE ---- @@ -372,7 +320,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(dotgov_result.status_code, 302) - self.assertEqual(dotgov_result["Location"], "/request/purpose/") + self.assertEqual(dotgov_result["Location"], f"/request/{domain_request.id}/purpose/") num_pages_tested += 1 # ---- PURPOSE PAGE ---- @@ -391,7 +339,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(purpose_result.status_code, 302) - self.assertEqual(purpose_result["Location"], "/request/other_contacts/") + self.assertEqual(purpose_result["Location"], f"/request/{domain_request.id}/other_contacts/") num_pages_tested += 1 # ---- OTHER CONTACTS PAGE ---- @@ -429,7 +377,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(other_contacts_result.status_code, 302) - self.assertEqual(other_contacts_result["Location"], "/request/additional_details/") + self.assertEqual(other_contacts_result["Location"], f"/request/{domain_request.id}/additional_details/") num_pages_tested += 1 # ---- ADDITIONAL DETAILS PAGE ---- @@ -459,7 +407,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(additional_details_result.status_code, 302) - self.assertEqual(additional_details_result["Location"], "/request/requirements/") + self.assertEqual(additional_details_result["Location"], f"/request/{domain_request.id}/requirements/") num_pages_tested += 1 # ---- REQUIREMENTS PAGE ---- @@ -479,7 +427,7 @@ def test_domain_request_form_submission(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(requirements_result.status_code, 302) - self.assertEqual(requirements_result["Location"], "/request/review/") + self.assertEqual(requirements_result["Location"], f"/request/{domain_request.id}/review/") num_pages_tested += 1 # ---- REVIEW AND FINSIHED PAGES ---- @@ -549,7 +497,7 @@ def test_domain_request_form_submission_incomplete(self): num_pages_tested = 0 # skipping elections, type_of_work, tribal_government - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -577,7 +525,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(type_result.status_code, 302) - self.assertEqual(type_result["Location"], "/request/organization_federal/") + self.assertEqual(type_result["Location"], f"/request/{domain_request.id}/organization_federal/") num_pages_tested += 1 # ---- FEDERAL BRANCH PAGE ---- @@ -597,7 +545,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(federal_result.status_code, 302) - self.assertEqual(federal_result["Location"], "/request/organization_contact/") + self.assertEqual(federal_result["Location"], f"/request/{domain_request.id}/organization_contact/") num_pages_tested += 1 # ---- ORG CONTACT PAGE ---- @@ -629,7 +577,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(org_contact_result.status_code, 302) - self.assertEqual(org_contact_result["Location"], "/request/senior_official/") + self.assertEqual(org_contact_result["Location"], f"/request/{domain_request.id}/senior_official/") num_pages_tested += 1 # ---- SENIOR OFFICIAL PAGE ---- @@ -654,7 +602,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(so_result.status_code, 302) - self.assertEqual(so_result["Location"], "/request/current_sites/") + self.assertEqual(so_result["Location"], f"/request/{domain_request.id}/current_sites/") num_pages_tested += 1 # ---- CURRENT SITES PAGE ---- @@ -676,7 +624,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(current_sites_result.status_code, 302) - self.assertEqual(current_sites_result["Location"], "/request/dotgov_domain/") + self.assertEqual(current_sites_result["Location"], f"/request/{domain_request.id}/dotgov_domain/") num_pages_tested += 1 # ---- DOTGOV DOMAIN PAGE ---- @@ -696,7 +644,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(dotgov_result.status_code, 302) - self.assertEqual(dotgov_result["Location"], "/request/purpose/") + self.assertEqual(dotgov_result["Location"], f"/request/{domain_request.id}/purpose/") num_pages_tested += 1 # ---- PURPOSE PAGE ---- @@ -715,7 +663,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(purpose_result.status_code, 302) - self.assertEqual(purpose_result["Location"], "/request/other_contacts/") + self.assertEqual(purpose_result["Location"], f"/request/{domain_request.id}/other_contacts/") num_pages_tested += 1 # ---- OTHER CONTACTS PAGE ---- @@ -753,7 +701,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(other_contacts_result.status_code, 302) - self.assertEqual(other_contacts_result["Location"], "/request/additional_details/") + self.assertEqual(other_contacts_result["Location"], f"/request/{domain_request.id}/additional_details/") num_pages_tested += 1 # ---- ADDITIONAL DETAILS PAGE ---- @@ -783,7 +731,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(additional_details_result.status_code, 302) - self.assertEqual(additional_details_result["Location"], "/request/requirements/") + self.assertEqual(additional_details_result["Location"], f"/request/{domain_request.id}/requirements/") num_pages_tested += 1 # ---- REQUIREMENTS PAGE ---- @@ -811,7 +759,7 @@ def test_domain_request_form_submission_incomplete(self): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(requirements_result.status_code, 302) - self.assertEqual(requirements_result["Location"], "/request/review/") + self.assertEqual(requirements_result["Location"], f"/request/{domain_request.id}/review/") num_pages_tested += 1 # ---- REVIEW AND FINSIHED PAGES ---- @@ -873,7 +821,7 @@ def test_domain_request_form_started_allsteps(self): @less_console_noise_decorator def test_domain_request_form_conditional_federal(self): """Federal branch question is shown for federal organizations.""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -904,7 +852,7 @@ def test_domain_request_form_conditional_federal(self): # the post request should return a redirect to the federal branch # question self.assertEqual(type_result.status_code, 302) - self.assertEqual(type_result["Location"], "/request/organization_federal/") + self.assertIn("organization_federal", type_result["Location"]) # and the step label should appear in the sidebar of the resulting page # but the step label for the elections page should not appear @@ -921,7 +869,7 @@ def test_domain_request_form_conditional_federal(self): # the post request should return a redirect to the contact # question self.assertEqual(federal_result.status_code, 302) - self.assertEqual(federal_result["Location"], "/request/organization_contact/") + self.assertIn("organization_federal", type_result["Location"]) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) contact_page = federal_result.follow() self.assertContains(contact_page, "Federal agency") @@ -929,7 +877,7 @@ def test_domain_request_form_conditional_federal(self): @less_console_noise_decorator def test_domain_request_form_conditional_elections(self): """Election question is shown for other organizations.""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -959,7 +907,7 @@ def test_domain_request_form_conditional_elections(self): # the post request should return a redirect to the elections question self.assertEqual(type_result.status_code, 302) - self.assertEqual(type_result["Location"], "/request/organization_election/") + self.assertIn("organization_election", type_result["Location"]) # and the step label should appear in the sidebar of the resulting page # but the step label for the elections page should not appear @@ -976,7 +924,7 @@ def test_domain_request_form_conditional_elections(self): # the post request should return a redirect to the contact # question self.assertEqual(election_result.status_code, 302) - self.assertEqual(election_result["Location"], "/request/organization_contact/") + self.assertIn("organization_contact", election_result["Location"]) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) contact_page = election_result.follow() self.assertNotContains(contact_page, "Federal agency") @@ -984,7 +932,8 @@ def test_domain_request_form_conditional_elections(self): @less_console_noise_decorator def test_domain_request_form_section_skipping(self): """Can skip forward and back in sections""" - intro_page = self.app.get(reverse("domain-request:")) + DomainRequest.objects.all().delete() + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -1019,17 +968,20 @@ def test_domain_request_form_section_skipping(self): # Now click back to the organization type self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) new_page = federal_page.click(str(self.TITLES["generic_org_type"]), index=0) - # Should be a link to the organization_federal page since it is now unlocked + all_domain_requests = DomainRequest.objects.all() + self.assertEqual(all_domain_requests.count(), 1) + + new_request_id = all_domain_requests.first().id self.assertGreater( - len(new_page.html.find_all("a", href="/request/organization_federal/")), + len(new_page.html.find_all("a", href=f"/request/{new_request_id}/organization_federal/")), 0, ) @less_console_noise_decorator def test_domain_request_form_nonfederal(self): """Non-federal organizations don't have to provide their federal agency.""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -1069,12 +1021,12 @@ def test_domain_request_form_nonfederal(self): # the post request should return a redirect to the # about your organization page if it was successful. self.assertEqual(contact_result.status_code, 302) - self.assertEqual(contact_result["Location"], "/request/about_your_organization/") + self.assertIn("about_your_organization", contact_result["Location"]) @less_console_noise_decorator def test_domain_request_about_your_organization_special(self): """Special districts have to answer an additional question.""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -1104,7 +1056,7 @@ def test_domain_request_about_your_organization_special(self): def test_federal_agency_dropdown_excludes_expected_values(self): """The Federal Agency dropdown on a domain request form should not include options for gov Administration and Non-Federal Agency""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -1152,7 +1104,7 @@ def test_federal_agency_dropdown_excludes_expected_values(self): def test_yes_no_contact_form_inits_blank_for_new_domain_request(self): """On the Other Contacts page, the yes/no form gets initialized with nothing selected for new domain requests""" - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": 0})) other_contacts_form = other_contacts_page.forms[0] self.assertEquals(other_contacts_form["other_contacts-has_other_contacts"].value, None) @@ -1160,7 +1112,7 @@ def test_yes_no_contact_form_inits_blank_for_new_domain_request(self): def test_yes_no_additional_form_inits_blank_for_new_domain_request(self): """On the Additional Details page, the yes/no form gets initialized with nothing selected for new domain requests""" - additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_details_page = self.app.get(reverse("domain-request:additional_details", kwargs={"id": 0})) additional_form = additional_details_page.forms[0] # Check the cisa representative yes/no field @@ -1184,7 +1136,7 @@ def test_yes_no_form_inits_yes_for_domain_request_with_other_contacts(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -1209,7 +1161,9 @@ def test_yes_no_form_inits_yes_for_cisa_representative_and_anything_else(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_details_page = self.app.get( + reverse("domain-request:additional_details", kwargs={"id": domain_request.pk}) + ) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) additional_details_form = additional_details_page.forms[0] @@ -1239,7 +1193,7 @@ def test_yes_no_form_inits_no_for_domain_request_with_no_other_contacts_rational session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -1268,7 +1222,9 @@ def test_yes_no_form_for_domain_request_with_no_cisa_representative_and_anything session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_details_page = self.app.get( + reverse("domain-request:additional_details", kwargs={"id": domain_request.pk}) + ) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) additional_details_form = additional_details_page.forms[0] @@ -1306,7 +1262,9 @@ def test_submitting_additional_details_deletes_cisa_representative_and_anything_ session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_details_page = self.app.get( + reverse("domain-request:additional_details", kwargs={"id": domain_request.pk}) + ) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) additional_details_form = additional_details_page.forms[0] @@ -1368,7 +1326,9 @@ def test_submitting_additional_details_populates_cisa_representative_and_anythin session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_details_page = self.app.get( + reverse("domain-request:additional_details", kwargs={"id": domain_request.pk}) + ) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) additional_details_form = additional_details_page.forms[0] @@ -1413,7 +1373,9 @@ def test_if_cisa_representative_yes_no_form_is_yes_then_field_is_required(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_details_page = self.app.get( + reverse("domain-request:additional_details", kwargs={"id": domain_request.pk}) + ) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) additional_details_form = additional_details_page.forms[0] @@ -1444,7 +1406,9 @@ def test_if_anything_else_yes_no_form_is_yes_then_field_is_required(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_details_page = self.app.get( + reverse("domain-request:additional_details", kwargs={"id": domain_request.pk}) + ) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) additional_details_form = additional_details_page.forms[0] @@ -1481,7 +1445,9 @@ def test_additional_details_form_fields_required(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_details_page = self.app.get( + reverse("domain-request:additional_details", kwargs={"id": domain_request.id}) + ) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) additional_details_form = additional_details_page.forms[0] @@ -1512,7 +1478,7 @@ def test_submitting_other_contacts_deletes_no_other_contacts_rationale(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -1560,7 +1526,7 @@ def test_submitting_no_other_contacts_rationale_deletes_other_contacts(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -1644,7 +1610,7 @@ def test_submitting_no_other_contacts_rationale_removes_reference_other_contacts session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -1685,7 +1651,7 @@ def test_submitting_no_other_contacts_rationale_removes_reference_other_contacts @less_console_noise_decorator def test_if_yes_no_form_is_no_then_no_other_contacts_required(self): """Applicants with no other contacts have to give a reason.""" - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": 0})) other_contacts_form = other_contacts_page.forms[0] other_contacts_form["other_contacts-has_other_contacts"] = "False" response = other_contacts_page.forms[0].submit() @@ -1701,7 +1667,7 @@ def test_if_yes_no_form_is_no_then_no_other_contacts_required(self): @less_console_noise_decorator def test_if_yes_no_form_is_yes_then_other_contacts_required(self): """Applicants with other contacts do not have to give a reason.""" - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": 0})) other_contacts_form = other_contacts_page.forms[0] other_contacts_form["other_contacts-has_other_contacts"] = "True" response = other_contacts_page.forms[0].submit() @@ -1777,7 +1743,7 @@ def test_delete_other_contact(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.id})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -1850,7 +1816,7 @@ def test_delete_other_contact_does_not_allow_zero_contacts(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.id})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -1927,7 +1893,7 @@ def test_delete_other_contact_sets_visible_empty_form_as_required_after_failed_s session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.id})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -2007,7 +1973,7 @@ def test_edit_other_contact_in_place(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -2083,7 +2049,7 @@ def test_edit_other_contact_creates_new(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) + other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) other_contacts_form = other_contacts_page.forms[0] @@ -2153,7 +2119,7 @@ def test_edit_senior_official_in_place(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - so_page = self.app.get(reverse("domain-request:senior_official")) + so_page = self.app.get(reverse("domain-request:senior_official", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) so_form = so_page.forms[0] @@ -2222,7 +2188,7 @@ def test_edit_senior_official_creates_new(self): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - so_page = self.app.get(reverse("domain-request:senior_official")) + so_page = self.app.get(reverse("domain-request:senior_official", kwargs={"id": domain_request.pk})) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) so_form = so_page.forms[0] @@ -2303,7 +2269,7 @@ def test_edit_creator_in_place(self): @less_console_noise_decorator def test_domain_request_about_your_organiztion_interstate(self): """Special districts have to answer an additional question.""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -2332,7 +2298,7 @@ def test_domain_request_about_your_organiztion_interstate(self): @less_console_noise_decorator def test_domain_request_tribal_government(self): """Tribal organizations have to answer an additional question.""" - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -2363,7 +2329,7 @@ def test_domain_request_tribal_government(self): @less_console_noise_decorator def test_domain_request_so_dynamic_text(self): - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -2447,7 +2413,7 @@ def test_domain_request_so_dynamic_text(self): @less_console_noise_decorator def test_domain_request_dotgov_domain_dynamic_text(self): - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -2555,8 +2521,23 @@ def test_domain_request_dotgov_domain_dynamic_text(self): @less_console_noise_decorator def test_domain_request_formsets(self): """Users are able to add more than one of some fields.""" - current_sites_page = self.app.get(reverse("domain-request:current_sites")) + DomainRequest.objects.all().delete() + + # Create a new domain request + intro_page = self.app.get(reverse("domain-request:start")) session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + + intro_form = intro_page.forms[0] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + intro_form.submit() + + all_domain_requests = DomainRequest.objects.all() + self.assertEqual(all_domain_requests.count(), 1) + + new_domain_request_id = all_domain_requests.first().id + + # Skip to the current sites page + current_sites_page = self.app.get(reverse("domain-request:current_sites", kwargs={"id": new_domain_request_id})) # fill in the form field current_sites_form = current_sites_page.forms[0] self.assertIn("current_sites-0-website", current_sites_form.fields) @@ -2573,8 +2554,11 @@ def test_domain_request_formsets(self): value = current_sites_form["current_sites-0-website"].value self.assertEqual(value, "https://example.com") self.assertIn("current_sites-1-website", current_sites_form.fields) + + all_domain_requests = DomainRequest.objects.all() + self.assertEqual(all_domain_requests.count(), 1, msg="Expected one domain request but got multiple") # and it is correctly referenced in the ManyToOne relationship - domain_request = DomainRequest.objects.get() # there's only one + domain_request = all_domain_requests.first() # there's only one self.assertEqual( domain_request.current_websites.filter(website="https://example.com").count(), 1, @@ -2712,7 +2696,7 @@ def test_long_org_name_in_domain_request(self): Make sure the long name is displaying in the domain request form, org step """ - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) # django-webtest does not handle cookie-based sessions well because it keeps # resetting the session key on each new request, thus destroying the concept # of a "session". We are going to do it manually, saving the session ID here @@ -2738,7 +2722,7 @@ def test_submit_modal_no_domain_text_fallback(self): NOTE: This may be a moot point if we implement a more solid pattern in the future, like not a submit action at all on the review page.""" - review_page = self.app.get(reverse("domain-request:review")) + review_page = self.app.get(reverse("domain-request:review", kwargs={"id": 0})) self.assertContains(review_page, "toggle-submit-domain-request") self.assertContains(review_page, "Your request form is incomplete") @@ -2751,7 +2735,7 @@ def test_portfolio_user_missing_edit_permissions(self): user=self.user, portfolio=portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER] ) # This user should be forbidden from creating new domain requests - intro_page = self.app.get(reverse("domain-request:"), expect_errors=True) + intro_page = self.app.get(reverse("domain-request:start"), expect_errors=True) self.assertEqual(intro_page.status_code, 403) # This user should also be forbidden from editing existing ones @@ -2773,7 +2757,7 @@ def test_portfolio_user_with_edit_permissions(self): ) # This user should be allowed to create new domain requests - intro_page = self.app.get(reverse("domain-request:")) + intro_page = self.app.get(reverse("domain-request:start")) self.assertEqual(intro_page.status_code, 200) # This user should also be allowed to edit existing ones @@ -2975,6 +2959,69 @@ def tearDown(self): DomainRequest.objects.all().delete() DomainInformation.objects.all().delete() + @less_console_noise_decorator + def test_breadcrumb_navigation(self): + """ + Tests the breadcrumb navigation behavior in domain request wizard. + Ensures that: + - Breadcrumb shows correct text based on portfolio flag + - Links point to correct destinations + - Back button appears on appropriate steps + - Back button is not present on first step + """ + # Create initial domain request + domain_request = completed_domain_request( + status=DomainRequest.DomainRequestStatus.STARTED, + user=self.user, + ) + + # Test without portfolio flag + start_page = self.app.get(f"/domain-request/{domain_request.id}/edit/").follow() + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + # Check initial breadcrumb state. + # Ensure that the request name is shown if it exists, otherwise just show new domain request. + self.assertContains(start_page, '

      ') + self.assertContains(start_page, "city.gov") + self.assertContains(start_page, 'href="/"') + self.assertContains(start_page, "Manage your domains") + self.assertNotContains(start_page, "Previous step") + + # Move to next step + form = start_page.forms[0] + next_page = form.submit().follow() + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + # Verify that the back button appears + self.assertContains(next_page, "Previous step") + self.assertContains(next_page, "#arrow_back") + + # Test with portfolio flag + with override_flag("organization_feature", active=True), override_flag("organization_requests", active=True): + portfolio = Portfolio.objects.create( + creator=self.user, + organization_name="test portfolio", + ) + permission = UserPortfolioPermission.objects.create( + user=self.user, + portfolio=portfolio, + roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN], + ) + + # Check portfolio-specific breadcrumb + portfolio_page = self.app.get(f"/domain-request/{domain_request.id}/edit/").follow() + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + self.assertContains(portfolio_page, "Domain requests") + + # Clean up portfolio + permission.delete() + portfolio.delete() + + # Clean up + domain_request.delete() + @less_console_noise_decorator def test_unlocked_steps_empty_domain_request(self): """Test when all fields in the domain request are empty.""" @@ -3016,7 +3063,7 @@ def test_unlocked_steps_full_domain_request(self): # 10 unlocked steps, one active step, the review step will have link_usa but not check_circle self.assertContains(detail_page, "#check_circle", count=9) # Type of organization - self.assertContains(detail_page, "usa-current", count=1) + self.assertContains(detail_page, "usa-current", count=2) self.assertContains(detail_page, "link_usa-checked", count=10) else: @@ -3078,7 +3125,7 @@ def test_unlocked_steps_partial_domain_request(self): # which unlocks if domain exists), one active step, the review step is locked self.assertContains(detail_page, "#check_circle", count=4) # Type of organization - self.assertContains(detail_page, "usa-current", count=1) + self.assertContains(detail_page, "usa-current", count=2) self.assertContains(detail_page, "link_usa-checked", count=4) else: @@ -3152,17 +3199,17 @@ def test_wizard_steps_portfolio(self): self.assertContains(detail_page, "#lock", 1) # The current option should be selected - self.assertContains(detail_page, "usa-current", count=1) + self.assertContains(detail_page, "usa-current", count=2) # We default to the requesting entity page - expected_url = reverse("domain-request:portfolio_requesting_entity") + expected_url = reverse("domain-request:portfolio_requesting_entity", kwargs={"id": domain_request.id}) # This returns the entire url, thus "in" self.assertIn(expected_url, detail_page.request.url) # We shouldn't show the "domains" and "domain requests" buttons # on this page. self.assertNotContains(detail_page, "Domains") - self.assertNotContains(detail_page, "Domain requests") + self.assertNotContains(detail_page, "Domain requests") else: self.fail(f"Expected a redirect, but got a different response: {response}") diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index b1c5c9c89..6519a5bbc 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -53,7 +53,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView): URL_NAMESPACE = "domain-request" # name for accessing /domain-request//edit EDIT_URL_NAME = "edit-domain-request" - NEW_URL_NAME = "/request/" + NEW_URL_NAME = "/request/start/" # region: Titles # We need to pass our human-readable step titles as context to the templates. @@ -158,6 +158,7 @@ def __init__(self): # Configure titles, wizard_conditions, unlocking_steps, and steps self.configure_step_options() self._domain_request = None # for caching + self.kwargs = {} def configure_step_options(self): """Changes which steps are available to the user based on self.is_portfolio. @@ -182,7 +183,7 @@ def configure_step_options(self): def has_pk(self): """Does this wizard know about a DomainRequest database record?""" - return "domain_request_id" in self.storage + return bool(self.kwargs.get("id") is not None) def get_step_enum(self): """Determines which step enum we should use for the wizard""" @@ -214,11 +215,10 @@ def domain_request(self) -> DomainRequest: raise ValueError("Invalid value for User") if self.has_pk(): - id = self.storage["domain_request_id"] try: self._domain_request = DomainRequest.objects.get( creator=creator, - pk=id, + pk=self.kwargs.get("id"), ) return self._domain_request except DomainRequest.DoesNotExist: @@ -238,8 +238,6 @@ def domain_request(self) -> DomainRequest: self._domain_request.save() else: self._domain_request = DomainRequest.objects.create(creator=self.request.user) - - self.storage["domain_request_id"] = self._domain_request.id return self._domain_request @property @@ -295,6 +293,7 @@ def from_model(self, attribute: str, default, *args, **kwargs): def get(self, request, *args, **kwargs): """This method handles GET requests.""" + self.kwargs = kwargs if not self.is_portfolio and self.request.user.is_org_user(request): self.is_portfolio = True # Configure titles, wizard_conditions, unlocking_steps, and steps @@ -307,7 +306,6 @@ def get(self, request, *args, **kwargs): # and remove any prior wizard data from their session if current_url == self.EDIT_URL_NAME and "id" in kwargs: del self.storage - self.storage["domain_request_id"] = kwargs["id"] # if accessing this class directly, redirect to either to an acknowledgement # page or to the first step in the processes (if an edit rather than a new request); @@ -450,7 +448,7 @@ def get_context_data(self): if self.domain_request.requested_domain is not None: requested_domain_name = self.domain_request.requested_domain.name - context_stuff = {} + context = {} # Note: we will want to consolidate the non_org_steps_complete check into the same check that # org_steps_complete is using at some point. @@ -458,7 +456,7 @@ def get_context_data(self): org_steps_complete = len(self.db_check_for_unlocking_steps()) == len(self.steps) if (not self.is_portfolio and non_org_steps_complete) or (self.is_portfolio and org_steps_complete): modal_button = '" - context_stuff = { + context = { "not_form": False, "form_titles": self.titles, "steps": self.steps, @@ -475,7 +473,7 @@ def get_context_data(self): } else: # form is not complete modal_button = '' - context_stuff = { + context = { "not_form": True, "form_titles": self.titles, "steps": self.steps, @@ -491,22 +489,19 @@ def get_context_data(self): } # Hides the requests and domains buttons in the navbar - context_stuff["hide_requests"] = self.is_portfolio - context_stuff["hide_domains"] = self.is_portfolio + context["hide_requests"] = self.is_portfolio + context["hide_domains"] = self.is_portfolio + context["domain_request_id"] = self.domain_request.id - return context_stuff + return context def get_step_list(self) -> list: """Dynamically generated list of steps in the form wizard.""" return request_step_list(self, self.get_step_enum()) def goto(self, step): - if step == "generic_org_type" or step == "portfolio_requesting_entity": - # We need to avoid creating a new domain request if the user - # clicks the back button - self.request.session["new_request"] = False self.steps.current = step - return redirect(reverse(f"{self.URL_NAMESPACE}:{step}")) + return redirect(reverse(f"{self.URL_NAMESPACE}:{step}", kwargs={"id": self.domain_request.id})) def goto_next_step(self): """Redirects to the next step.""" @@ -532,9 +527,6 @@ def post(self, request, *args, **kwargs) -> HttpResponse: # which button did the user press? button: str = request.POST.get("submit_button", "") - if "new_request" not in request.session: - request.session["new_request"] = True - # if user has acknowledged the intro message if button == "intro_acknowledge": # Split into a function: C901 'DomainRequestWizard.post' is too complex (11) @@ -572,9 +564,6 @@ def post(self, request, *args, **kwargs) -> HttpResponse: def handle_intro_acknowledge(self, request): """If we are starting a new request, clear storage and redirect to the first step""" - if request.path_info == self.NEW_URL_NAME: - if self.request.session["new_request"] is True: - del self.storage return self.goto(self.steps.first) def save(self, forms: list): diff --git a/src/registrar/views/index.py b/src/registrar/views/index.py index 53900a4a7..7019c8db3 100644 --- a/src/registrar/views/index.py +++ b/src/registrar/views/index.py @@ -7,7 +7,6 @@ def index(request): if request and request.user and request.user.is_authenticated: # This controls the creation of a new domain request in the wizard - request.session["new_request"] = True context["user_domain_count"] = request.user.get_user_domain_ids(request).count() return render(request, "home.html", context) diff --git a/src/registrar/views/portfolios.py b/src/registrar/views/portfolios.py index 1dbab2913..7839d209e 100644 --- a/src/registrar/views/portfolios.py +++ b/src/registrar/views/portfolios.py @@ -40,8 +40,6 @@ class PortfolioDomainRequestsView(PortfolioDomainRequestsPermissionView, View): template_name = "portfolio_requests.html" def get(self, request): - if self.request.user.is_authenticated: - request.session["new_request"] = True return render(request, "portfolio_requests.html") diff --git a/src/registrar/views/user_profile.py b/src/registrar/views/user_profile.py index 4d3b44366..2012d12ab 100644 --- a/src/registrar/views/user_profile.py +++ b/src/registrar/views/user_profile.py @@ -53,7 +53,7 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Set the profile_back_button_text based on the redirect parameter - if kwargs.get("redirect") == "domain-request:": + if kwargs.get("redirect") == "domain-request:start": context["profile_back_button_text"] = "Go back to your domain request" else: context["profile_back_button_text"] = "Go to manage your domains"