diff --git a/aurweb/routers/requests.py b/aurweb/routers/requests.py
index a259dd63..585dc157 100644
--- a/aurweb/routers/requests.py
+++ b/aurweb/routers/requests.py
@@ -40,13 +40,21 @@ async def requests(
filter_accepted: bool = False,
filter_rejected: bool = False,
filter_maintainer_requests: bool = False,
+ filter_pkg_name: str = None,
):
context = make_context(request, "Requests")
context["q"] = dict(request.query_params)
+ # Set pending filter by default if no status filter was provided.
+ # In case we got a package name filter, but no status filter,
+ # we enable the other ones too.
if not dict(request.query_params).keys() & FILTER_PARAMS:
filter_pending = True
+ if filter_pkg_name:
+ filter_closed = True
+ filter_accepted = True
+ filter_rejected = True
O, PP = util.sanitize_params(str(O), str(PP))
context["O"] = O
@@ -56,6 +64,7 @@ async def requests(
context["filter_accepted"] = filter_accepted
context["filter_rejected"] = filter_rejected
context["filter_maintainer_requests"] = filter_maintainer_requests
+ context["filter_pkg_name"] = filter_pkg_name
Maintainer = orm.aliased(User)
# A PackageRequest query
@@ -78,7 +87,7 @@ async def requests(
rejected_count = 0 + query.filter(PackageRequest.Status == REJECTED_ID).count()
context["rejected_requests"] = rejected_count
- # Apply filters
+ # Apply status filters
in_filters = []
if filter_pending:
in_filters.append(PENDING_ID)
@@ -89,6 +98,11 @@ async def requests(
if filter_rejected:
in_filters.append(REJECTED_ID)
filtered = query.filter(PackageRequest.Status.in_(in_filters))
+
+ # Name filter
+ if filter_pkg_name:
+ filtered = filtered.filter(PackageBase.Name == filter_pkg_name)
+
# Additionally filter for requests made from package maintainer
if filter_maintainer_requests:
filtered = filtered.filter(PackageRequest.UsersID == PackageBase.MaintainerUID)
diff --git a/po/aurweb.pot b/po/aurweb.pot
index f4e3c1ba..b975ab91 100644
--- a/po/aurweb.pot
+++ b/po/aurweb.pot
@@ -2362,3 +2362,7 @@ msgstr ""
#: templates/partials/packages/comment_form.html
msgid "Cancel"
msgstr ""
+
+#: templates/requests.html
+msgid "Package name"
+msgstr ""
diff --git a/static/js/typeahead-requests.js b/static/js/typeahead-requests.js
new file mode 100644
index 00000000..9f636eab
--- /dev/null
+++ b/static/js/typeahead-requests.js
@@ -0,0 +1,6 @@
+document.addEventListener('DOMContentLoaded', function() {
+ const input = document.getElementById('id_filter_pkg_name');
+ const form = document.getElementById('todolist_filter');
+ const type = 'suggest-pkgbase';
+ typeahead.init(type, input, form);
+});
diff --git a/templates/partials/packages/actions.html b/templates/partials/packages/actions.html
index fa8c994f..ae8cf141 100644
--- a/templates/partials/packages/actions.html
+++ b/templates/partials/packages/actions.html
@@ -97,11 +97,19 @@
{% endif %}
{% if requests %}
-
-
- {{ requests | tn("%d pending request", "%d pending requests") | format(requests) }}
-
-
+ {% if request.user.has_credential(creds.PKGREQ_LIST) %}
+
+
+ {{ requests | tn("%d pending request", "%d pending requests") | format(requests) }}
+
+
+ {% else %}
+
+
+ {{ requests | tn("%d pending request", "%d pending requests") | format(requests) }}
+
+
+ {% endif %}
{% endif %}
diff --git a/templates/requests.html b/templates/requests.html
index 697fbedb..9eb911f5 100644
--- a/templates/requests.html
+++ b/templates/requests.html
@@ -62,7 +62,13 @@
value="True" {{ "checked" if filter_maintainer_requests == true }}/>
-
+
+
+
+
+
+
@@ -194,4 +200,7 @@
{% include "partials/pager.html" %}
{% endif %}
+
+
+
{% endblock %}
diff --git a/test/test_packages_routes.py b/test/test_packages_routes.py
index 21ccdd7b..93dc404a 100644
--- a/test/test_packages_routes.py
+++ b/test/test_packages_routes.py
@@ -555,6 +555,16 @@ def test_package_authenticated(client: TestClient, user: User, package: Package)
for expected_text in expected:
assert expected_text in resp.text
+ # make sure we don't have these. Only for Maintainer/TUs/Devs
+ not_expected = [
+ "Disown Package",
+ "View Requests",
+ "Delete Package",
+ "Merge Package",
+ ]
+ for unexpected_text in not_expected:
+ assert unexpected_text not in resp.text
+
# When no requests are up, make sure we don't see the display for them.
root = parse_root(resp.text)
selector = '//div[@id="actionlist"]/ul/li/span[@class="flagged"]'
@@ -586,8 +596,19 @@ def test_package_authenticated_maintainer(
for expected_text in expected:
assert expected_text in resp.text
+ # make sure we don't have these. Only for TUs/Devs
+ not_expected = [
+ "1 pending request",
+ "Delete Package",
+ "Merge Package",
+ ]
+ for unexpected_text in not_expected:
+ assert unexpected_text not in resp.text
-def test_package_authenticated_tu(client: TestClient, tu_user: User, package: Package):
+
+def test_package_authenticated_tu(
+ client: TestClient, tu_user: User, package: Package, pkgreq: PackageRequest
+):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
request.cookies = cookies
@@ -603,6 +624,7 @@ def test_package_authenticated_tu(client: TestClient, tu_user: User, package: Pa
"Vote for this package",
"Enable notifications",
"Manage Co-Maintainers",
+ "1 pending request",
"Submit Request",
"Delete Package",
"Merge Package",
diff --git a/test/test_requests.py b/test/test_requests.py
index eb05596c..7ddb76a0 100644
--- a/test/test_requests.py
+++ b/test/test_requests.py
@@ -912,6 +912,44 @@ def test_requests_for_maintainer_requests(
assert len(rows) == 2
+def test_requests_with_package_name_filter(
+ client: TestClient,
+ tu_user: User,
+ user2: User,
+ packages: list[Package],
+ requests: list[PackageRequest],
+):
+ # test as TU
+ cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
+ with client as request:
+ request.cookies = cookies
+ resp = request.get(
+ "/requests",
+ params={"filter_pkg_name": packages[0].PackageBase.Name},
+ )
+ assert resp.status_code == int(HTTPStatus.OK)
+
+ root = parse_root(resp.text)
+ rows = root.xpath('//table[@class="results"]/tbody/tr')
+ # We only expect 1 request for our first package
+ assert len(rows) == 1
+
+ # test as regular user, not related to our package
+ cookies = {"AURSID": user2.login(Request(), "testPassword")}
+ with client as request:
+ request.cookies = cookies
+ resp = request.get(
+ "/requests",
+ params={"filter_pkg_name": packages[0].PackageBase.Name},
+ )
+ assert resp.status_code == int(HTTPStatus.OK)
+
+ root = parse_root(resp.text)
+ rows = root.xpath('//table[@class="results"]/tbody/tr')
+ # We don't expect to get any requests
+ assert len(rows) == 0
+
+
def test_requests_by_deleted_users(
client: TestClient, user: User, tu_user: User, pkgreq: PackageRequest
):