diff --git a/aurweb/routers/packages.py b/aurweb/routers/packages.py index d65cc344..c74a5ba3 100644 --- a/aurweb/routers/packages.py +++ b/aurweb/routers/packages.py @@ -846,11 +846,7 @@ async def pkgbase_unflag(request: Request, name: str): status_code=HTTPStatus.SEE_OTHER) -@router.post("/pkgbase/{name}/notify") -@auth_required(True, redirect="/pkgbase/{name}") -async def pkgbase_notify(request: Request, name: str): - pkgbase = get_pkg_or_base(name, models.PackageBase) - +def pkgbase_notify_instance(request: Request, pkgbase: models.PackageBase): notif = db.query(pkgbase.notifications.filter( models.PackageNotification.UserID == request.user.ID ).exists()).scalar() @@ -861,6 +857,12 @@ async def pkgbase_notify(request: Request, name: str): PackageBase=pkgbase, User=request.user) + +@router.post("/pkgbase/{name}/notify") +@auth_required(True, redirect="/pkgbase/{name}") +async def pkgbase_notify(request: Request, name: str): + pkgbase = get_pkg_or_base(name, models.PackageBase) + pkgbase_notify_instance(request, pkgbase) return RedirectResponse(f"/pkgbase/{name}", status_code=HTTPStatus.SEE_OTHER) @@ -1070,11 +1072,51 @@ async def packages_unflag(request: Request, package_ids: List[int] = [], pkgbase_unflag_instance(request, pkgbase) return (True, ["The selected packages have been unflagged."]) + +async def packages_notify(request: Request, package_ids: List[int] = [], + **kwargs): + # In cases where we encounter errors with the request, we'll + # use this error tuple as a return value. + # TODO: This error does not yet have a translation. + error_tuple = (False, + ["You did not select any packages to be notified about."]) + if not package_ids: + return error_tuple + + bases = set() + package_ids = set(package_ids) + packages = db.query(models.Package).filter( + models.Package.ID.in_(package_ids)).all() + + for pkg in packages: + if pkg.PackageBase not in bases: + bases.update({pkg.PackageBase}) + + # Perform some checks on what the user selected for notify. + for pkgbase in bases: + notif = db.query(pkgbase.notifications.filter( + models.PackageNotification.UserID == request.user.ID + ).exists()).scalar() + has_cred = request.user.has_credential("CRED_PKGBASE_NOTIFY") + + # If the request user either does not have credentials + # or the notification already exists: + if not (has_cred and not notif): + return error_tuple + + # If we get here, user input is good. + for pkgbase in bases: + pkgbase_notify_instance(request, pkgbase) + + # TODO: This message does not yet have a translation. + return (True, ["The selected packages' notifications have been enabled."]) + # A mapping of action string -> callback functions used within the # `packages_post` route below. We expect any action callback to # return a tuple in the format: (succeeded: bool, message: List[str]). PACKAGE_ACTIONS = { "unflag": packages_unflag, + "notify": packages_notify, } diff --git a/po/aurweb.pot b/po/aurweb.pot index 1f7e8784..699a6aef 100644 --- a/po/aurweb.pot +++ b/po/aurweb.pot @@ -958,6 +958,14 @@ msgstr "" msgid "Package details could not be found." msgstr "" +#: aurweb/routers/packages.py +msgid "You did not select any packages to be notified about." +msgstr "" + +#: aurweb/routers/packages.py +msgid "The selected packages' notifications have been enabled." +msgstr "" + #: lib/pkgbasefuncs.inc.php msgid "You must be logged in before you can flag packages." msgstr "" diff --git a/test/test_packages_routes.py b/test/test_packages_routes.py index b35cb377..f86bb805 100644 --- a/test/test_packages_routes.py +++ b/test/test_packages_routes.py @@ -2113,3 +2113,44 @@ def test_packages_post_unflag(client: TestClient, user: User, errors = get_errors(resp.text) expected = "You did not select any packages to unflag." assert errors[0].text.strip() == expected + + +def test_packages_post_notify(client: TestClient, user: User, package: Package): + notif = package.PackageBase.notifications.filter( + PackageNotification.UserID == user.ID + ).first() + assert notif is None + + # Try to enable notifications but supply no packages, causing + # an error to be rendered. + cookies = {"AURSID": user.login(Request(), "testPassword")} + with client as request: + resp = request.post("/packages", data={"action": "notify"}, + cookies=cookies) + assert resp.status_code == int(HTTPStatus.BAD_REQUEST) + errors = get_errors(resp.text) + expected = "You did not select any packages to be notified about." + assert errors[0].text.strip() == expected + + # Now let's actually enable notifications on `package`. + with client as request: + resp = request.post("/packages", data={ + "action": "notify", + "IDs": [package.ID] + }, cookies=cookies) + assert resp.status_code == int(HTTPStatus.OK) + expected = "The selected packages' notifications have been enabled." + successes = get_successes(resp.text) + assert successes[0].text.strip() == expected + + # Try to enable notifications when they're already enabled, + # causing an error to be rendered. + with client as request: + resp = request.post("/packages", data={ + "action": "notify", + "IDs": [package.ID] + }, cookies=cookies) + assert resp.status_code == int(HTTPStatus.BAD_REQUEST) + errors = get_errors(resp.text) + expected = "You did not select any packages to be notified about." + assert errors[0].text.strip() == expected