This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git


The following commit(s) were added to refs/heads/main by this push:
     new 941d6a2  Add the ability for users to delete unused projects that they 
created
941d6a2 is described below

commit 941d6a2aae1abe17b0594f1562e619c63e060235
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Apr 14 16:21:50 2025 +0100

    Add the ability for users to delete unused projects that they created
---
 atr/routes/projects.py          | 40 ++++++++++++++++++++++++++++++++++++++++
 atr/templates/project-view.html | 17 +++++++++++++++++
 atr/templates/projects.html     | 25 ++++++++++++++++++++++++-
 3 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/atr/routes/projects.py b/atr/routes/projects.py
index ec7cef0..e5e9cc5 100644
--- a/atr/routes/projects.py
+++ b/atr/routes/projects.py
@@ -133,6 +133,46 @@ async def _add_project(form: AddFormProtocol, asf_id: str) 
-> response.Response:
     return quart.redirect(util.as_url(view, name=new_project_label))
 
 
[email protected]("/project/delete", methods=["POST"])
+async def delete(session: routes.CommitterSession) -> response.Response:
+    """Delete a project created by the user."""
+    form_data = await quart.request.form
+    project_name = form_data.get("project_name")
+    if not project_name:
+        return await session.redirect(projects, error="Missing project name 
for deletion.")
+
+    async with db.session() as data:
+        project = await data.project(name=project_name, _releases=True, 
_distribution_channels=True).get()
+
+        if not project:
+            return await session.redirect(projects, error=f"Project 
'{project_name}' not found.")
+
+        # Check for ownership or admin status
+        is_owner = project.created_by == session.uid
+        is_privileged = util.is_user_viewing_as_admin(session.uid)
+
+        if not (is_owner or is_privileged):
+            return await session.redirect(
+                projects, error=f"You do not have permission to delete project 
'{project_name}'."
+            )
+
+        # Prevent deletion if there are associated releases or channels
+        if project.releases:
+            return await session.redirect(
+                projects, error=f"Cannot delete project '{project_name}' 
because it has associated releases."
+            )
+        if project.distribution_channels:
+            return await session.redirect(
+                projects,
+                error=f"Cannot delete project '{project_name}' because it has 
associated distribution channels.",
+            )
+
+        await data.delete(project)
+        await data.commit()
+
+    return await session.redirect(projects, success=f"Project '{project_name}' 
deleted successfully.")
+
+
 @routes.public("/projects")
 async def projects() -> str:
     """Main project directory page."""
diff --git a/atr/templates/project-view.html b/atr/templates/project-view.html
index a630088..117f24b 100644
--- a/atr/templates/project-view.html
+++ b/atr/templates/project-view.html
@@ -174,6 +174,23 @@
     </div>
   </div>
 
+  {% if project.created_by == current_user.uid %}
+    <h2>Actions</h2>
+    <div class="mt-3">
+      <form method="post"
+            action="{{ as_url(routes.projects.delete) }}"
+            class="d-inline-block m-0"
+            onsubmit="return confirm('Are you sure you want to delete the 
project \'{{ project.display_name }}\'? This cannot be undone.');">
+        <input type="hidden" name="project_name" value="{{ project.name }}" />
+        <button type="submit"
+                class="btn btn-sm btn-outline-danger"
+                title="Delete {{ project.display_name }}">
+          <i class="fa-solid fa-trash"></i> Delete project
+        </button>
+      </form>
+    </div>
+  {% endif %}
+
 {% endblock content %}
 
 {% block javascripts %}
diff --git a/atr/templates/projects.html b/atr/templates/projects.html
index 6068147..e9804c1 100644
--- a/atr/templates/projects.html
+++ b/atr/templates/projects.html
@@ -59,6 +59,25 @@
                 {% endfor %}
               </div>
             {% endif %}
+
+            {# TODO: Could add "or is_viewing_as_admin_fn(current_user.uid)" #}
+            {# But then the page is noisy for admins #}
+            {% if project.created_by == current_user.uid %}
+              <div class="mt-3">
+                <form method="post"
+                      action="{{ as_url(routes.projects.delete) }}"
+                      class="d-inline-block m-0"
+                      onsubmit="return confirm('Are you sure you want to 
delete the project \'{{ project.display_name }}\'? This cannot be undone.');">
+                  <input type="hidden" name="project_name" value="{{ 
project.name }}" />
+                  <button type="submit"
+                          class="btn btn-sm btn-outline-danger"
+                          title="Delete {{ project.display_name }}">
+                    <i class="fa-solid fa-trash"></i> Delete project
+                  </button>
+                </form>
+              </div>
+            {% endif %}
+
           </div>
         </div>
       </div>
@@ -94,7 +113,11 @@
 
       // Add click handlers for project cards
       document.querySelectorAll(".project-card").forEach(function(card) {
-          card.addEventListener("click", function() {
+          card.addEventListener("click", function(event) {
+              // Prevent card navigation if click is inside a form
+              if (event.target.closest("form")) {
+                  return;
+              }
               window.location.href = this.getAttribute("data-project-url");
           });
       });


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to