Repository: pypi/warehouse

Issue:
Disable 'delete confirm' button until confirmation word is correct
We currently have a modal on `warehouse/templates/manage/settings.html`, that allows the user to confirm that they want to delete their project:

![screenshot from 2018-02-03 14-43-29](https://user-images.githubusercontent.com/3323703/35768242-9dcfc21a-08f0-11e8-834d-fdcc3e6cd998.png)

The user is required to enter the project name as an extra security measure.  If they get it wrong, we show them this error:

![screenshot from 2018-02-03 14-44-19](https://user-images.githubusercontent.com/3323703/35768249-bba976d2-08f0-11e8-97ba-99c37bfc7479.png)

## Proposal

It would be really nice if we could `disable` the delete button until the correct project name is given, e.g.

![screenshot from 2018-02-03 14-46-02](https://user-images.githubusercontent.com/3323703/35768271-fa2cdc64-08f0-11e8-848f-58433e60ae6b.png)

![screenshot from 2018-02-03 14-46-25](https://user-images.githubusercontent.com/3323703/35768274-0692bca8-08f1-11e8-9149-3aa7a5faad65.png)

## Notes

We will have several other delete confirmation modals on other pages, sometimes with multiple modals on a single page (e.g. delete release, delete file) - so the code will need to be written to take this into account.



Discussion:
**Good First Issue**: This issue is good for first time contributors. If there is not a corresponding pull request for this issue, it is up for grabs. For directions for getting set up, see our [Getting Started Guide](https://warehouse.pypa.io/development/getting-started/). If you are working on this issue and have questions, please feel free to ask them here, [`#pypa-dev` on Freenode](https://webchat.freenode.net/?channels=%23pypa-dev), or the [pypa-dev mailing list](https://groups.google.com/forum/#!forum/pypa-dev).

If you're interested in working on this, speak up here, and mention `@di` -- he has some thoughts on adding some light JavaScript touches to broadly handle this and similar instances of this elsewhere in our codebase (as Nicole mentioned in her "Notes" section above).

Resolving patch:
diff --git a/warehouse/utils/project.py b/warehouse/utils/project.py
--- a/warehouse/utils/project.py
+++ b/warehouse/utils/project.py
@@ -19,7 +19,7 @@
 
 
 def confirm_project(project, request, fail_route):
-    confirm = request.POST.get("confirm")
+    confirm = request.POST.get("confirm_project_name")
     project_name = project.normalized_name
     if not confirm:
         request.session.flash(


Test patch:
diff --git a/tests/unit/admin/views/test_projects.py b/tests/unit/admin/views/test_projects.py
--- a/tests/unit/admin/views/test_projects.py
+++ b/tests/unit/admin/views/test_projects.py
@@ -434,7 +434,7 @@ def test_no_confirm(self):
     def test_wrong_confirm(self):
         project = pretend.stub(normalized_name='foo')
         request = pretend.stub(
-            POST={"confirm": "bar"},
+            POST={"confirm_project_name": "bar"},
             session=pretend.stub(
                 flash=pretend.call_recorder(lambda *a, **kw: None),
             ),
@@ -461,7 +461,7 @@ def test_deletes_project(self, db_request):
         db_request.session = pretend.stub(
             flash=pretend.call_recorder(lambda *a, **kw: None),
         )
-        db_request.POST["confirm"] = project.normalized_name
+        db_request.POST["confirm_project_name"] = project.normalized_name
         db_request.user = UserFactory.create()
         db_request.remote_addr = "192.168.1.1"
 
diff --git a/tests/unit/manage/test_views.py b/tests/unit/manage/test_views.py
--- a/tests/unit/manage/test_views.py
+++ b/tests/unit/manage/test_views.py
@@ -773,7 +773,7 @@ def test_delete_project_no_confirm(self):
     def test_delete_project_wrong_confirm(self):
         project = pretend.stub(normalized_name='foo')
         request = pretend.stub(
-            POST={"confirm": "bar"},
+            POST={"confirm_project_name": "bar"},
             session=pretend.stub(
                 flash=pretend.call_recorder(lambda *a, **kw: None),
             ),
@@ -801,7 +801,7 @@ def test_delete_project(self, db_request):
         db_request.session = pretend.stub(
             flash=pretend.call_recorder(lambda *a, **kw: None),
         )
-        db_request.POST["confirm"] = project.normalized_name
+        db_request.POST["confirm_project_name"] = project.normalized_name
         db_request.user = UserFactory.create()
         db_request.remote_addr = "192.168.1.1"
 
diff --git a/tests/unit/utils/test_project.py b/tests/unit/utils/test_project.py
--- a/tests/unit/utils/test_project.py
+++ b/tests/unit/utils/test_project.py
@@ -29,7 +29,7 @@
 def test_confirm():
     project = stub(normalized_name='foobar')
     request = stub(
-        POST={'confirm': 'foobar'},
+        POST={'confirm_project_name': 'foobar'},
         route_path=call_recorder(lambda *a, **kw: stub()),
         session=stub(flash=call_recorder(lambda *a, **kw: stub())),
     )
@@ -43,7 +43,7 @@ def test_confirm():
 def test_confirm_no_input():
     project = stub(normalized_name='foobar')
     request = stub(
-        POST={'confirm': ''},
+        POST={'confirm_project_name': ''},
         route_path=call_recorder(lambda *a, **kw: '/the-redirect'),
         session=stub(flash=call_recorder(lambda *a, **kw: stub())),
     )
@@ -63,7 +63,7 @@ def test_confirm_no_input():
 def test_confirm_incorrect_input():
     project = stub(normalized_name='foobar')
     request = stub(
-        POST={'confirm': 'bizbaz'},
+        POST={'confirm_project_name': 'bizbaz'},
         route_path=call_recorder(lambda *a, **kw: '/the-redirect'),
         session=stub(flash=call_recorder(lambda *a, **kw: stub())),
     )