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]