Example #1
0
async def pkgbase_merge_post(request: Request,
                             name: str,
                             into: str = Form(default=str()),
                             comments: str = Form(default=str()),
                             confirm: bool = Form(default=False),
                             next: str = Form(default=str())):

    pkgbase = get_pkg_or_base(name, PackageBase)
    context = await make_variable_context(request, "Package Merging")
    context["pkgbase"] = pkgbase

    # TODO: Lookup errors from credential instead of hardcoding them.
    if not request.user.has_credential(creds.PKGBASE_MERGE):
        context["errors"] = [
            "Only Trusted Users and Developers can merge packages."
        ]
        return render_template(request,
                               "pkgbase/merge.html",
                               context,
                               status_code=HTTPStatus.UNAUTHORIZED)

    if not confirm:
        context["errors"] = [
            "The selected packages have not been deleted, "
            "check the confirmation checkbox."
        ]
        return render_template(request,
                               "pkgbase/merge.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    try:
        target = get_pkg_or_base(into, PackageBase)
    except HTTPException:
        context["errors"] = [
            "Cannot find package to merge votes and comments into."
        ]
        return render_template(request,
                               "pkgbase/merge.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    if pkgbase == target:
        context["errors"] = ["Cannot merge a package base with itself."]
        return render_template(request,
                               "pkgbase/merge.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    with db.begin():
        update_closure_comment(pkgbase, MERGE_ID, comments, target=target)

    # Merge pkgbase into target.
    actions.pkgbase_merge_instance(request, pkgbase, target, comments=comments)

    if not next:
        next = f"/pkgbase/{target.Name}"

    # Redirect to the newly merged into package.
    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Example #2
0
async def pkgbase_comment_unpin(request: Request,
                                name: str,
                                id: int,
                                next: str = Form(default=None)):
    """
    Unpin a comment.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :param id: PackageComment.ID
    :param next: Optional `next` parameter used in the POST request
    :return: RedirectResponse to `next`
    """
    pkgbase = get_pkg_or_base(name, PackageBase)
    comment = get_pkgbase_comment(pkgbase, id)

    has_cred = request.user.has_credential(creds.COMMENT_PIN,
                                           approved=comment.maintainers())
    if not has_cred:
        _ = l10n.get_translator_for_request(request)
        raise HTTPException(
            status_code=HTTPStatus.UNAUTHORIZED,
            detail=_("You are not allowed to unpin this comment."))

    with db.begin():
        comment.PinnedTS = 0

    if not next:
        next = f"/pkgbase/{name}"

    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Example #3
0
async def pkgbase_delete_post(request: Request,
                              name: str,
                              confirm: bool = Form(default=False),
                              comments: str = Form(default=str()),
                              next: str = Form(default="/packages")):
    pkgbase = get_pkg_or_base(name, PackageBase)

    if not request.user.has_credential(creds.PKGBASE_DELETE):
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    if not confirm:
        context = templates.make_context(request, "Package Deletion")
        context["pkgbase"] = pkgbase
        context["errors"] = [("The selected packages have not been deleted, "
                              "check the confirmation checkbox.")]
        return render_template(request,
                               "pkgbase/delete.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    if comments:
        # Update any existing deletion requests' ClosureComment.
        with db.begin():
            requests = pkgbase.requests.filter(
                and_(PackageRequest.Status == PENDING_ID,
                     PackageRequest.ReqTypeID == DELETION_ID))
            for pkgreq in requests:
                pkgreq.ClosureComment = comments

    notifs = actions.pkgbase_delete_instance(request,
                                             pkgbase,
                                             comments=comments)
    util.apply_all(notifs, lambda n: n.send())
    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Example #4
0
async def pkgbase_keywords(request: Request,
                           name: str,
                           keywords: str = Form(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    # Lowercase all keywords. Our database table is case insensitive,
    # and providing CI duplicates of keywords is erroneous.
    keywords = set(k.lower() for k in keywords.split(" "))

    # Delete all keywords which are not supplied by the user.
    with db.begin():
        other_keywords = pkgbase.keywords.filter(
            ~PackageKeyword.Keyword.in_(keywords))
        other_keyword_strings = set(kwd.Keyword.lower()
                                    for kwd in other_keywords)

        existing_keywords = set(
            kwd.Keyword.lower() for kwd in pkgbase.keywords.filter(
                ~PackageKeyword.Keyword.in_(other_keyword_strings)))

        db.delete_all(other_keywords)
        new_keywords = keywords.difference(existing_keywords)
        for keyword in new_keywords:
            db.create(PackageKeyword, PackageBase=pkgbase, Keyword=keyword)

    return RedirectResponse(f"/pkgbase/{name}",
                            status_code=HTTPStatus.SEE_OTHER)
Example #5
0
async def pkgbase_comments_post(
    request: Request,
    name: str,
    comment: str = Form(default=str()),
    enable_notifications: bool = Form(default=False)):
    """ Add a new comment via POST request. """
    pkgbase = get_pkg_or_base(name, PackageBase)

    if not comment:
        raise HTTPException(status_code=HTTPStatus.BAD_REQUEST)

    # If the provided comment is different than the record's version,
    # update the db record.
    now = time.utcnow()
    with db.begin():
        comment = db.create(PackageComment,
                            User=request.user,
                            PackageBase=pkgbase,
                            Comments=comment,
                            RenderedComment=str(),
                            CommentTS=now)

        if enable_notifications and not request.user.notified(pkgbase):
            db.create(PackageNotification,
                      User=request.user,
                      PackageBase=pkgbase)
    update_comment_render_fastapi(comment)

    notif = notify.CommentNotification(request.user.ID, pkgbase.ID, comment.ID)
    notif.send()

    # Redirect to the pkgbase page.
    return RedirectResponse(f"/pkgbase/{pkgbase.Name}#comment-{comment.ID}",
                            status_code=HTTPStatus.SEE_OTHER)
Example #6
0
async def pkgbase_flag_post(request: Request,
                            name: str,
                            comments: str = Form(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    if not comments:
        context = templates.make_context(request, "Flag Package Out-Of-Date")
        context["pkgbase"] = pkgbase
        context["errors"] = [
            "The selected packages have not been flagged, "
            "please enter a comment."
        ]
        return render_template(request,
                               "pkgbase/flag.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    has_cred = request.user.has_credential(creds.PKGBASE_FLAG)
    if has_cred and not pkgbase.OutOfDateTS:
        now = time.utcnow()
        with db.begin():
            pkgbase.OutOfDateTS = now
            pkgbase.Flagger = request.user
            pkgbase.FlaggerComment = comments

        notify.FlagNotification(request.user.ID, pkgbase.ID).send()

    return RedirectResponse(f"/pkgbase/{name}",
                            status_code=HTTPStatus.SEE_OTHER)
Example #7
0
async def http_exception_handler(request: Request, exc: HTTPException) \
        -> Response:
    """ Handle an HTTPException thrown in a route. """
    phrase = http.HTTPStatus(exc.status_code).phrase
    context = make_context(request, phrase)
    context["exc"] = exc
    context["phrase"] = phrase

    # Additional context for some exceptions.
    if exc.status_code == http.HTTPStatus.NOT_FOUND:
        tokens = request.url.path.split("/")
        matches = re.match("^([a-z0-9][a-z0-9.+_-]*?)(\\.git)?$", tokens[1])
        if matches:
            try:
                pkgbase = get_pkg_or_base(matches.group(1))
                context = pkgbaseutil.make_context(request, pkgbase)
            except HTTPException:
                pass

    try:
        return render_template(request, f"errors/{exc.status_code}.html",
                               context, exc.status_code)
    except TemplateNotFound:
        return render_template(request, "errors/detail.html", context,
                               exc.status_code)
Example #8
0
async def pkgbase_request(request: Request,
                          name: str,
                          next: str = Query(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)
    context = await make_variable_context(request, "Submit Request")
    context["pkgbase"] = pkgbase
    context["next"] = next or f"/pkgbase/{name}"
    return render_template(request, "pkgbase/request.html", context)
Example #9
0
async def pkgbase_flag_comment(request: Request, name: str):
    pkgbase = get_pkg_or_base(name, PackageBase)

    if pkgbase.OutOfDateTS is None:
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Flag Comment")
    context["pkgbase"] = pkgbase
    return render_template(request, "pkgbase/flag-comment.html", context)
Example #10
0
async def pkgbase_delete_get(request: Request,
                             name: str,
                             next: str = Query(default=str())):
    if not request.user.has_credential(creds.PKGBASE_DELETE):
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Package Deletion")
    context["pkgbase"] = get_pkg_or_base(name, PackageBase)
    context["next"] = next or "/packages"
    return render_template(request, "pkgbase/delete.html", context)
Example #11
0
async def pkgbase_flag_get(request: Request, name: str):
    pkgbase = get_pkg_or_base(name, PackageBase)

    has_cred = request.user.has_credential(creds.PKGBASE_FLAG)
    if not has_cred or pkgbase.OutOfDateTS is not None:
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Flag Package Out-Of-Date")
    context["pkgbase"] = pkgbase
    return render_template(request, "pkgbase/flag.html", context)
Example #12
0
async def pkgbase_adopt_post(request: Request, name: str):
    pkgbase = get_pkg_or_base(name, PackageBase)

    has_cred = request.user.has_credential(creds.PKGBASE_ADOPT)
    if has_cred or not pkgbase.Maintainer:
        # If the user has credentials, they'll adopt the package regardless
        # of maintainership. Otherwise, we'll promote the user to maintainer
        # if no maintainer currently exists.
        actions.pkgbase_adopt_instance(request, pkgbase)

    return RedirectResponse(f"/pkgbase/{name}",
                            status_code=HTTPStatus.SEE_OTHER)
Example #13
0
async def pkgbase_disown_get(request: Request,
                             name: str,
                             next: str = Query(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    has_cred = request.user.has_credential(creds.PKGBASE_DISOWN,
                                           approved=[pkgbase.Maintainer])
    if not has_cred:
        return RedirectResponse(f"/pkgbase/{name}", HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Disown Package")
    context["pkgbase"] = pkgbase
    context["next"] = next or "/pkgbase/{name}"
    return render_template(request, "pkgbase/disown.html", context)
Example #14
0
async def pkgbase_unvote(request: Request, name: str):
    pkgbase = get_pkg_or_base(name, PackageBase)

    vote = pkgbase.package_votes.filter(
        PackageVote.UsersID == request.user.ID).first()
    has_cred = request.user.has_credential(creds.PKGBASE_VOTE)
    if has_cred and vote:
        with db.begin():
            db.delete(vote)

        # Update NumVotes/Popularity.
        popupdate.run_single(pkgbase)

    return RedirectResponse(f"/pkgbase/{name}",
                            status_code=HTTPStatus.SEE_OTHER)
Example #15
0
async def pkgbase_voters(request: Request, name: str) -> Response:
    """
    View of package base voters.

    Requires `request.user` has creds.PKGBASE_LIST_VOTERS credential.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :return: HTMLResponse
    """
    # Get the PackageBase.
    pkgbase = get_pkg_or_base(name, PackageBase)

    if not request.user.has_credential(creds.PKGBASE_LIST_VOTERS):
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Voters")
    context["pkgbase"] = pkgbase
    return render_template(request, "pkgbase/voters.html", context)
Example #16
0
async def pkgbase_comaintainers(request: Request, name: str) -> Response:
    # Get the PackageBase.
    pkgbase = get_pkg_or_base(name, PackageBase)

    # Unauthorized users (Non-TU/Dev and not the pkgbase maintainer)
    # get redirected to the package base's page.
    has_creds = request.user.has_credential(creds.PKGBASE_EDIT_COMAINTAINERS,
                                            approved=[pkgbase.Maintainer])
    if not has_creds:
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    # Add our base information.
    context = templates.make_context(request, "Manage Co-maintainers")
    context.update({
        "pkgbase":
        pkgbase,
        "comaintainers": [c.User.Username for c in pkgbase.comaintainers]
    })

    return render_template(request, "pkgbase/comaintainers.html", context)
Example #17
0
async def pkgbase_comment_edit(request: Request,
                               name: str,
                               id: int,
                               next: str = Form(default=None)):
    """
    Render the non-javascript edit form.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :param id: PackageComment.ID
    :param next: Optional `next` parameter used in the POST request
    :return: HTMLResponse
    """
    pkgbase = get_pkg_or_base(name, PackageBase)
    comment = get_pkgbase_comment(pkgbase, id)

    if not next:
        next = f"/pkgbase/{name}"

    context = await make_variable_context(request, "Edit comment", next=next)
    context["comment"] = comment
    return render_template(request, "pkgbase/comments/edit.html", context)
Example #18
0
async def pkgbase_comment_form(request: Request,
                               name: str,
                               id: int,
                               next: str = Query(default=None)):
    """
    Produce a comment form for comment {id}.

    This route is used as a partial HTML endpoint when editing
    package comments via Javascript. This endpoint used to be
    part of the RPC as type=get-comment-form and has been
    relocated here because the form returned cannot be used
    externally and requires a POST request by the user.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :param id: PackageComment.ID
    :param next: Optional `next` value used for the comment form
    :return: JSONResponse
    """
    pkgbase = get_pkg_or_base(name, PackageBase)
    comment = pkgbase.comments.filter(PackageComment.ID == id).first()
    if not comment:
        return JSONResponse({}, status_code=HTTPStatus.NOT_FOUND)

    if not request.user.is_elevated() and request.user != comment.User:
        return JSONResponse({}, status_code=HTTPStatus.UNAUTHORIZED)

    context = pkgbaseutil.make_context(request, pkgbase)
    context["comment"] = comment

    if not next:
        next = f"/pkgbase/{name}"

    context["next"] = next

    form = templates.render_raw_template(
        request, "partials/packages/comment_form.html", context)
    return JSONResponse({"form": form})
Example #19
0
async def pkgbase_comaintainers_post(request: Request, name: str,
                                     users: str = Form(default=str())) \
        -> Response:
    # Get the PackageBase.
    pkgbase = get_pkg_or_base(name, PackageBase)

    # Unauthorized users (Non-TU/Dev and not the pkgbase maintainer)
    # get redirected to the package base's page.
    has_creds = request.user.has_credential(creds.PKGBASE_EDIT_COMAINTAINERS,
                                            approved=[pkgbase.Maintainer])
    if not has_creds:
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    users = {e.strip() for e in users.split("\n") if bool(e.strip())}
    records = {c.User.Username for c in pkgbase.comaintainers}

    users_to_rm = records.difference(users)
    pkgbaseutil.remove_comaintainers(pkgbase, users_to_rm)
    logger.debug(f"{request.user} removed comaintainers from "
                 f"{pkgbase.Name}: {users_to_rm}")

    users_to_add = users.difference(records)
    error = pkgbaseutil.add_comaintainers(request, pkgbase, users_to_add)
    if error:
        context = templates.make_context(request, "Manage Co-maintainers")
        context["pkgbase"] = pkgbase
        context["comaintainers"] = [
            c.User.Username for c in pkgbase.comaintainers
        ]
        context["errors"] = [error]
        return render_template(request, "pkgbase/comaintainers.html", context)

    logger.debug(f"{request.user} added comaintainers to "
                 f"{pkgbase.Name}: {users_to_add}")

    return RedirectResponse(f"/pkgbase/{pkgbase.Name}",
                            status_code=HTTPStatus.SEE_OTHER)
Example #20
0
async def pkgbase_disown_post(request: Request,
                              name: str,
                              comments: str = Form(default=str()),
                              confirm: bool = Form(default=False),
                              next: str = Form(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    has_cred = request.user.has_credential(creds.PKGBASE_DISOWN,
                                           approved=[pkgbase.Maintainer])
    if not has_cred:
        return RedirectResponse(f"/pkgbase/{name}", HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Disown Package")
    context["pkgbase"] = pkgbase
    if not confirm:
        context["errors"] = [("The selected packages have not been disowned, "
                              "check the confirmation checkbox.")]
        return render_template(request,
                               "pkgbase/disown.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    with db.begin():
        update_closure_comment(pkgbase, ORPHAN_ID, comments)

    try:
        actions.pkgbase_disown_instance(request, pkgbase)
    except InvariantError as exc:
        context["errors"] = [str(exc)]
        return render_template(request,
                               "pkgbase/disown.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    if not next:
        next = f"/pkgbase/{name}"

    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Example #21
0
async def pkgbase_comment_post(
    request: Request,
    name: str,
    id: int,
    comment: str = Form(default=str()),
    enable_notifications: bool = Form(default=False),
    next: str = Form(default=None)):
    """ Edit an existing comment. """
    pkgbase = get_pkg_or_base(name, PackageBase)
    db_comment = get_pkgbase_comment(pkgbase, id)

    if not comment:
        raise HTTPException(status_code=HTTPStatus.BAD_REQUEST)

    # If the provided comment is different than the record's version,
    # update the db record.
    now = time.utcnow()
    if db_comment.Comments != comment:
        with db.begin():
            db_comment.Comments = comment
            db_comment.Editor = request.user
            db_comment.EditedTS = now

            db_notif = request.user.notifications.filter(
                PackageNotification.PackageBaseID == pkgbase.ID).first()
            if enable_notifications and not db_notif:
                db.create(PackageNotification,
                          User=request.user,
                          PackageBase=pkgbase)
    update_comment_render_fastapi(db_comment)

    if not next:
        next = f"/pkgbase/{pkgbase.Name}"

    # Redirect to the pkgbase page anchored to the updated comment.
    return RedirectResponse(f"{next}#comment-{db_comment.ID}",
                            status_code=HTTPStatus.SEE_OTHER)
Example #22
0
async def pkgbase_comment_delete(request: Request,
                                 name: str,
                                 id: int,
                                 next: str = Form(default=None)):
    """
    Delete a comment.

    This action does **not** delete the comment from the database, but
    sets PackageBase.DelTS and PackageBase.DeleterUID, which is used to
    decide who gets to view the comment and what utilities it gets.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :param id: PackageComment.ID
    :param next: Optional `next` parameter used in the POST request
    :return: RedirectResposne to `next`
    """
    pkgbase = get_pkg_or_base(name, PackageBase)
    comment = get_pkgbase_comment(pkgbase, id)

    authorized = request.user.has_credential(creds.COMMENT_DELETE,
                                             [comment.User])
    if not authorized:
        _ = l10n.get_translator_for_request(request)
        raise HTTPException(
            status_code=HTTPStatus.UNAUTHORIZED,
            detail=_("You are not allowed to delete this comment."))

    now = time.utcnow()
    with db.begin():
        comment.Deleter = request.user
        comment.DelTS = now

    if not next:
        next = f"/pkgbase/{name}"

    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Example #23
0
async def pkgbase_merge_get(request: Request,
                            name: str,
                            into: str = Query(default=str()),
                            next: str = Query(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    context = templates.make_context(request, "Package Merging")
    context.update({"pkgbase": pkgbase, "into": into, "next": next})

    status_code = HTTPStatus.OK
    # TODO: Lookup errors from credential instead of hardcoding them.
    # Idea: Something like credential_errors(creds.PKGBASE_MERGE).
    # Perhaps additionally: bad_credential_status_code(creds.PKGBASE_MERGE).
    # Don't take these examples verbatim. We should find good naming.
    if not request.user.has_credential(creds.PKGBASE_MERGE):
        context["errors"] = [
            "Only Trusted Users and Developers can merge packages."
        ]
        status_code = HTTPStatus.UNAUTHORIZED

    return render_template(request,
                           "pkgbase/merge.html",
                           context,
                           status_code=status_code)
Example #24
0
async def pkgbase(request: Request, name: str) -> Response:
    """
    Single package base view.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :return: HTMLResponse
    """
    # Get the PackageBase.
    pkgbase = get_pkg_or_base(name, PackageBase)

    # Redirect to /packages if there's only one related Package
    # and its name matches its PackageBase.
    packages = pkgbase.packages.all()
    pkg = packages[0]
    if len(packages) == 1 and pkg.Name == pkgbase.Name:
        return RedirectResponse(f"/packages/{pkg.Name}",
                                status_code=int(HTTPStatus.SEE_OTHER))

    # Add our base information.
    context = pkgbaseutil.make_context(request, pkgbase)
    context["packages"] = packages

    return render_template(request, "pkgbase/index.html", context)
Example #25
0
async def pkgbase_comment_undelete(request: Request,
                                   name: str,
                                   id: int,
                                   next: str = Form(default=None)):
    """
    Undelete a comment.

    This action does **not** undelete any comment from the database, but
    unsets PackageBase.DelTS and PackageBase.DeleterUID which restores
    the comment to a standard state.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :param id: PackageComment.ID
    :param next: Optional `next` parameter used in the POST request
    :return: RedirectResponse to `next`
    """
    pkgbase = get_pkg_or_base(name, PackageBase)
    comment = get_pkgbase_comment(pkgbase, id)

    has_cred = request.user.has_credential(creds.COMMENT_UNDELETE,
                                           approved=[comment.User])
    if not has_cred:
        _ = l10n.get_translator_for_request(request)
        raise HTTPException(
            status_code=HTTPStatus.UNAUTHORIZED,
            detail=_("You are not allowed to undelete this comment."))

    with db.begin():
        comment.Deleter = None
        comment.DelTS = None

    if not next:
        next = f"/pkgbase/{name}"

    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Example #26
0
async def package(
    request: Request,
    name: str,
    all_deps: bool = Query(default=False),
    all_reqs: bool = Query(default=False)
) -> Response:
    """
    Get a package by name.

    By default, we limit the number of depends and requires results
    to 20. To bypass this and load all of them, which should be triggered
    via a "Show more" link near the limited listing.

    :param name: Package.Name
    :param all_deps: Boolean indicating whether we should load all depends
    :param all_reqs: Boolean indicating whether we should load all requires
    :return: FastAPI Response
    """

    # Get the Package.
    pkg = get_pkg_or_base(name, models.Package)
    pkgbase = pkg.PackageBase

    rels = pkg.package_relations.order_by(models.PackageRelation.RelName.asc())
    rels_data = defaultdict(list)
    for rel in rels:
        if rel.RelTypeID == CONFLICTS_ID:
            rels_data["c"].append(rel)
        elif rel.RelTypeID == PROVIDES_ID:
            rels_data["p"].append(rel)
        elif rel.RelTypeID == REPLACES_ID:
            rels_data["r"].append(rel)

    # Add our base information.
    context = await pkgbaseutil.make_variable_context(request, pkgbase)

    context.update({"all_deps": all_deps, "all_reqs": all_reqs})

    context["package"] = pkg

    # Package sources.
    context["sources"] = pkg.package_sources.order_by(
        models.PackageSource.Source.asc()).all()

    # Listing metadata.
    context["max_listing"] = max_listing = 20

    # Package dependencies.
    deps = pkg.package_dependencies.order_by(
        models.PackageDependency.DepTypeID.asc(),
        models.PackageDependency.DepName.asc())
    context["depends_count"] = deps.count()
    if not all_deps:
        deps = deps.limit(max_listing)
    context["dependencies"] = deps.all()

    # Package requirements (other packages depend on this one).
    reqs = pkgutil.pkg_required(pkg.Name,
                                [p.RelName for p in rels_data.get("p", [])])
    context["reqs_count"] = reqs.count()
    if not all_reqs:
        reqs = reqs.limit(max_listing)
    context["required_by"] = reqs.all()

    context["licenses"] = pkg.package_licenses

    conflicts = pkg.package_relations.filter(
        models.PackageRelation.RelTypeID == CONFLICTS_ID).order_by(
            models.PackageRelation.RelName.asc())
    context["conflicts"] = conflicts

    provides = pkg.package_relations.filter(
        models.PackageRelation.RelTypeID == PROVIDES_ID).order_by(
            models.PackageRelation.RelName.asc())
    context["provides"] = provides

    replaces = pkg.package_relations.filter(
        models.PackageRelation.RelTypeID == REPLACES_ID).order_by(
            models.PackageRelation.RelName.asc())
    context["replaces"] = replaces

    return render_template(request, "packages/show.html", context)
Example #27
0
async def pkgbase_request_post(request: Request,
                               name: str,
                               type: str = Form(...),
                               merge_into: str = Form(default=None),
                               comments: str = Form(default=str()),
                               next: str = Form(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    # Create our render context.
    context = await make_variable_context(request, "Submit Request")
    context["pkgbase"] = pkgbase

    types = {"deletion": DELETION_ID, "merge": MERGE_ID, "orphan": ORPHAN_ID}

    if type not in types:
        # In the case that someone crafted a POST request with an invalid
        # type, just return them to the request form with BAD_REQUEST status.
        return render_template(request,
                               "pkgbase/request.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    try:
        validate.request(pkgbase, type, comments, merge_into, context)
    except ValidationError as exc:
        logger.error(f"Request Validation Error: {str(exc.data)}")
        context["errors"] = exc.data
        return render_template(request, "pkgbase/request.html", context)

    # All good. Create a new PackageRequest based on the given type.
    now = time.utcnow()
    with db.begin():
        pkgreq = db.create(PackageRequest,
                           ReqTypeID=types.get(type),
                           User=request.user,
                           RequestTS=now,
                           PackageBase=pkgbase,
                           PackageBaseName=pkgbase.Name,
                           MergeBaseName=merge_into,
                           Comments=comments,
                           ClosureComment=str())

    # Prepare notification object.
    notif = notify.RequestOpenNotification(request.user.ID,
                                           pkgreq.ID,
                                           type,
                                           pkgreq.PackageBase.ID,
                                           merge_into=merge_into or None)

    # Send the notification now that we're out of the DB scope.
    notif.send()

    auto_orphan_age = config.getint("options", "auto_orphan_age")
    auto_delete_age = config.getint("options", "auto_delete_age")

    ood_ts = pkgbase.OutOfDateTS or 0
    flagged = ood_ts and (now - ood_ts) >= auto_orphan_age
    is_maintainer = pkgbase.Maintainer == request.user
    outdated = (now - pkgbase.SubmittedTS) <= auto_delete_age

    if type == "orphan" and flagged:
        # This request should be auto-accepted.
        with db.begin():
            pkgbase.Maintainer = None
            pkgreq.Status = ACCEPTED_ID
        notif = notify.RequestCloseNotification(request.user.ID, pkgreq.ID,
                                                pkgreq.status_display())
        notif.send()
        logger.debug(f"New request #{pkgreq.ID} is marked for auto-orphan.")
    elif type == "deletion" and is_maintainer and outdated:
        # This request should be auto-accepted.
        notifs = actions.pkgbase_delete_instance(request,
                                                 pkgbase,
                                                 comments=comments)
        util.apply_all(notifs, lambda n: n.send())
        logger.debug(f"New request #{pkgreq.ID} is marked for auto-deletion.")

    # Redirect the submitting user to /packages.
    return RedirectResponse("/packages", status_code=HTTPStatus.SEE_OTHER)
Example #28
0
async def pkgbase_unflag(request: Request, name: str):
    pkgbase = get_pkg_or_base(name, PackageBase)
    actions.pkgbase_unflag_instance(request, pkgbase)
    return RedirectResponse(f"/pkgbase/{name}",
                            status_code=HTTPStatus.SEE_OTHER)