[FastAPI] Basic pkgbase template

Co-author: Kevin Morris <kevr@0cost.org>

Signed-off-by: Leonidas Spyropoulos <artafinde@gmail.com>
Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Leonidas Spyropoulos 2021-06-28 22:57:21 +01:00 committed by Kevin Morris
parent 9197f86a03
commit 1e1c0c3fe5
6 changed files with 303 additions and 1 deletions

View file

@ -18,7 +18,7 @@ from aurweb.auth import BasicAuthBackend
from aurweb.db import get_engine, query from aurweb.db import get_engine, query
from aurweb.models.accepted_term import AcceptedTerm from aurweb.models.accepted_term import AcceptedTerm
from aurweb.models.term import Term from aurweb.models.term import Term
from aurweb.routers import accounts, auth, errors, html, rss, sso, trusted_user from aurweb.routers import accounts, auth, errors, html, packages, rss, sso, trusted_user
# Setup the FastAPI app. # Setup the FastAPI app.
app = FastAPI(exception_handlers=errors.exceptions) app = FastAPI(exception_handlers=errors.exceptions)
@ -51,6 +51,7 @@ async def app_startup():
app.include_router(accounts.router) app.include_router(accounts.router)
app.include_router(trusted_user.router) app.include_router(trusted_user.router)
app.include_router(rss.router) app.include_router(rss.router)
app.include_router(packages.router)
# Initialize the database engine and ORM. # Initialize the database engine and ORM.
get_engine() get_engine()

View file

@ -0,0 +1,37 @@
from http import HTTPStatus
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import RedirectResponse
import aurweb.models.package
import aurweb.models.package_comment
import aurweb.models.package_keyword
from aurweb import db
from aurweb.models.package_base import PackageBase
from aurweb.templates import make_variable_context, render_template
router = APIRouter()
@router.get("/packages/{package}")
async def package_base(request: Request, package: str):
package = db.query(PackageBase).filter(PackageBase.Name == package).first()
if not package:
raise HTTPException(status_code=int(HTTPStatus.NOT_FOUND))
context = await make_variable_context(request, package.Name)
context["git_clone_uri_anon"] = aurweb.config.get("options", "git_clone_uri_anon")
context["git_clone_uri_priv"] = aurweb.config.get("options", "git_clone_uri_priv")
context["pkgbase"] = package
context["packages"] = package.packages.all()
context["packages_count"] = package.packages.count()
context["keywords"] = package.keywords.all()
context["comments"] = package.comments.all()
return render_template(request, "pkgbase.html", context)
@router.get("/pkgbase/{package}")
async def package_base_redirect(request: Request, package: str):
return RedirectResponse(f"/packages/{package}")

View file

@ -0,0 +1,73 @@
<!--
This partial requires the following to render properly
- pkgname
- pkgbase-id
- comments (list)
-->
<div id="generic-form" class="box">
<h2>Add Comment</h2>
<form action="/pkgbase/{{ pkgname }}/" method="post">
<fieldset>
<div>
<input type="hidden" name="action" value="do_AddComment"/>
<input type="hidden" name="ID" value="{{ pkgbase_id }}"/>
</div>
<p>
Git commit identifiers referencing commits in the AUR package
repository and URLs are converted to links automatically.
<a href="https://daringfireball.net/projects/markdown/syntax">
Markdown syntax
</a>
is partially supported.
</p>
<p>
<textarea id="id_comment" name="comment" cols="80" rows="10"></textarea>
</p>
<p>
<input type="submit" value="Add Comment"/>
</p>
</fieldset>
</form>
</div>
<div class="comments package-comments">
<div class="comments-header">
<h3>
<span class="text">Latest Comments</span>
<span class="arrow"></span>
</h3>
</div>
{% for comment in comments %}
<h4 id="comment-{{ comment.ID }}" class="comment-header">
{% set commentTimestamp = comment.CommentTS | dt | as_timezone(timezone) %}
<a href="/account/{{ comment.User.Username }}" title="View account information for {{ comment.User.Username }}">{{ comment.User.Username }}</a>
commented on <a href="#comment-{{ comment.ID }}" class="date">{{ "%s" | tr | format(commentTimestamp.strftime("%Y-%m-%d %H:%M")) }}</a>
{% if request.user.is_authenticated() and pkgbase.Maintainer.Username == request.user.Username %}
<form class="delete-comment-form" method="post" action="/pkgbase/{{ pkgname }}/">
<fieldset style="display:inline;">
<input type="hidden" name="action" value="do_DeleteComment" />
<input type="hidden" name="comment_id" value="{{ comment.ID }}"/>
<input type="hidden" name="return_to" value="/pkgbase/{{ pkgname }}/"/>
<input type="image" class="delete-comment" src="/images/x.min.svg" width="11" height="11" alt="Delete comment" title="Delete comment" name="submit" value="1" />
</fieldset>
</form>
<a href="/pkgbase/{{ pkgname }}/edit-comment/?comment_id={{ comment.ID }}" class="edit-comment" title="Edit comment"><img src="/images/pencil.min.svg" alt="Edit comment" width="11" height="11"></a>
{% endif %}
<form class="pin-comment-form" method="post" action="/pkgbase/{{ pkgname }}/">
<fieldset style="display:inline;">
<input type="hidden" name="action" value="do_PinComment"/>
<input type="hidden" name="comment_id" value="{{ comment.ID }}"/>
<input type="hidden" name="package_base" value="{{ pkgbase_id }}"/>
<input type="hidden" name="return_to" value="/pkgbase/{{ pkgname }}/"/>
<input type="image" class="pin-comment" src="/images/pin.min.svg" width="11" height="11" alt="Pin comment" title="Pin comment" name="submit" value="1"/>
</fieldset>
</form>
</h4>
<div id="comment-{{ comment.ID }}-content" class="article-content">
<div>
<p>{{ comment.Comments }}</p>
</div>
</div>
{% endfor %}
</div>

View file

@ -0,0 +1,33 @@
<!--
This partial requires pkgname to render
-->
<div id="detailslinks" class="listing">
<div id="actionlist">
<h4>Package Actions</h4>
<ul class="small">
<li>
<a href="/cgit/aur.git/tree/PKGBUILD?h={{ pkgname }}">View PKGBUILD</a> /
<a href="/cgit/aur.git/log/?h={{ pkgname }}">View Changes</a>
</li>
<li><a href="/cgit/aur.git/snapshot/{{ pkgname }}.tar.gz">Download snapshot</a>
<li><a href="https://wiki.archlinux.org/title/Special:Search?search={{ pkgname }}">Search wiki</a></li>
<li><span class="flagged"></span></li>
<li><a href="/pkgbase/{{ pkgname }}/flag/">Flag package out-of-date</a></li>
<li>
<form action="/pkgbase/{{ pkgname }}/vote/" method="post"><input type="submit" class="button text-button" name="do_Vote" value="Vote for this package"/></form>
</li>
<li>
<form action="/pkgbase/{{ pkgname }}/unnotify/" method="post"><input type="submit" class="button text-button" name="do_UnNotify" value="Disable notifications"/>
</form>
</li>
<li><a href="/pkgbase/{{ pkgname }}/comaintainers/">Manage Co-Maintainers</a></li>
<li><span class="flagged"></span></li>
<li><a href="/pkgbase/{{ pkgname }}/request/">Submit Request</a></li>
<li><a href="/pkgbase/{{ pkgname }}/delete/">Delete Package</a></li>
<li><a href="/pkgbase/{{ pkgname }}/merge/">Merge Package</a></li>
<li>
<form action="/pkgbase/{{ pkgname }}/disown/" method="post"><input type="submit" class="button text-button" name="do_Disown" value="Disown Package"/></form>
</li>
</ul>
</div>
</div>

View file

@ -0,0 +1,69 @@
<div id="pkglist-search" class="box filter-criteria">
<h2>{% trans %}Search Criteria{% endtrans %}</h2>
<form action='/packages/' method='get'>
<p><input type='hidden' name='O' value='0'/></p>
<fieldset>
<legend>Enter search criteria</legend>
<div>
<label for="id_method">Search by</label>
<select name='SeB'>
<option value="nd">Name, Description</option>
<option value="n">Name Only</option>
<option value="b">Package Base</option>
<option value="N">Exact Name</option>
<option value="B">Exact Package Base</option>
<option value="k">Keywords</option>
<option value="m">Maintainer</option>
<option value="c">Co-maintainer</option>
<option value="M">Maintainer, Co-maintainer</option>
<option value="s">Submitter</option>
</select>
</div>
<div>
<label for="id_q">Keywords</label>
<input type='text' name='K' size='30' value="" maxlength='35'/>
</div>
<div>
<label for="id_out_of_date">Out of Date</label>
<select name='outdated'>
<option value=''>All</option>
<option value='on'>Flagged</option>
<option value='off'>Not Flagged</option>
</select>
</div>
<div>
<label for="id_sort_by">Sort by</label>
<select name='SB'>
<option value='n'>Name</option>
<option value='v'>Votes</option>
<option value='p'>Popularity</option>
<option value='w'>Voted</option>
<option value='o'>Notify</option>
<option value='m'>Maintainer</option>
<option value='l'>Last modified</option>
</select>
</div>
<div>
<label for="id_order_by">Sort order</label>
<select name='SO'>
<option value='a'>Ascending</option>
<option value='d'>Descending</option>
</select>
</div>
<div>
<label for="id_per_page">Per page</label>
<select name='PP'>
<option value="50">50</option>
<option value="100">100</option>
<option value="250">250</option>
</select>
</div>
<div>
<label>&nbsp;</label>
<input type='submit' class='button' name='do_Search' value='Go'/>
<input type='submit' class='button' name='do_Orphans' value='Orphans'/>
</div>
</fieldset>
</form>
</div>

89
templates/pkgbase.html Normal file
View file

@ -0,0 +1,89 @@
{% extends "partials/layout.html" %}
{% block pageContent %}
{% include "partials/packages/search.html" %}
<div id="pkgdetails" class="box">
<h2>Package Base Details: {{ pkgbase.Name }}</h2>
{% set result = pkgbase %}
{% set pkgname = "result.Name" %}
{% include "partials/packages/package_actions.html" %}
<table id="pkginfo">
<tr>
<th>Git Clone URL: </th>
<td>
<a class="copy" href="{{ git_clone_uri_anon | format(pkgbase.Name) }}">{{ git_clone_uri_anon | format(pkgbase.Name) }}</a> (read-only, click to copy)
{% if request.user.is_authenticated() and pkgbase.Maintainer.Username == request.user.Username %}
<br /> <a class="copy" href="{{ git_clone_uri_priv | format(pkgbase.Name) }}">{{ git_clone_uri_priv | format(pkgbase.Name) }}</a> (click to copy)
{% endif %}
</td>
</tr>
<tr>
<th>Keywords: </th>
{% if request.user.is_authenticated() and pkgbase.Maintainer.Username == request.user.Username %}
<td>
<form method="post" action="/pkgbase/{{ pkgbase.Name }}/">
<div>
<input type="hidden" name="action" value="do_SetKeywords" />
<input type="text" name="keywords" value="{{ pkgbase.keywords | join(' ', attribute='Keyword') }}"/>
<input type="submit" value="Update"/>
</div>
</form>
</td>
{% else %}
<td>
{% for item in pkgbase.keywords %}
<a class="keyword" href="/packages/?K={{ item.Keyword }}&amp;SB=p">{{ item.Keyword }}</a>
{% endfor %}
</td>
{% endif %}
</tr>
<tr>
<th>Submitter: </th>
<td>{{ pkgbase.Submitter.Username | default("None") }}</td>
</tr>
<tr>
<th>Maintainer: </th>
<td>{{ pkgbase.Maintainer.Username | default("None") }}</td>
</tr>
<tr>
<th>Last Packager: </th>
<td>{{ pkgbase.Packager.Username | default("None") }}</td>
</tr>
<tr>
<th>Votes: </th>
<td>{{ pkgbase.NumVotes }}</td>
</tr>
<tr>
<th>Popularity: </th>
<td>{{ '%0.2f' % pkgbase.Popularity | float }}</td>
</tr>
<tr>
{% set submitted = pkgbase.SubmittedTS | dt | as_timezone(timezone) %}
<th>First Submitted: </th>
<td>{{ "%s" | tr | format(submitted.strftime("%Y-%m-%d %H:%M")) }}</td>
</tr>
<tr>
<th>Last Updated: </th>
{% set updated = pkgbase.ModifiedTS | dt | as_timezone(timezone) %}
<td>{{ "%s" | tr | format(updated.strftime("%Y-%m-%d %H:%M")) }}</td>
</tr>
</table>
<div id="metadata">
<div id="pkgs" class="listing">
<h3>Packages ({{ packages_count }})</h3>
<ul>
{% for result in packages %}
<li><a href="/packages/{{ result.Name }}/" title="View packages details for {{ result.Name }}">{{ result.Name }}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% set pkgname = result.Name %}
{% set pkgbase_id = result.ID %}
{% set comments = comments %}
{% include "partials/packages/comments.html" %}
{% endblock %}