From 102fca490c82864e2401c7e2cdf255c39b117859 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 28 Apr 2020 14:08:56 -0400 Subject: [PATCH] Implement featured projects --- hubsrht/blueprints/projects.py | 18 +++++++++++++++++- hubsrht/blueprints/public.py | 11 +++++++++-- hubsrht/decorators.py | 14 ++++++++++++++ hubsrht/templates/project-index.html | 14 +++++++++----- hubsrht/templates/project-summary.html | 25 +++++++++++++++++++++++++ hubsrht/types/__init__.py | 1 + hubsrht/types/feature.py | 16 ++++++++++++++++ 7 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 hubsrht/decorators.py create mode 100644 hubsrht/types/feature.py diff --git a/hubsrht/blueprints/projects.py b/hubsrht/blueprints/projects.py index 0661b40..a93f4c7 100644 --- a/hubsrht/blueprints/projects.py +++ b/hubsrht/blueprints/projects.py @@ -1,7 +1,8 @@ from flask import Blueprint, render_template, request, redirect, url_for +from hubsrht.decorators import adminrequired from hubsrht.projects import ProjectAccess, get_project from hubsrht.services import git, hg -from hubsrht.types import Event, EventType +from hubsrht.types import Feature, Event, EventType from hubsrht.types import Project, RepoType, Visibility from srht.database import db from srht.flask import paginate_query @@ -136,3 +137,18 @@ def delete_POST(owner, project_name): db.session.delete(project) db.session.commit() return redirect(url_for("public.index")) + +@projects.route("///feature", methods=["POST"]) +@adminrequired +def feature_POST(owner, project_name): + owner, project = get_project(owner, project_name, ProjectAccess.read) + valid = Validation(request) + + feature = Feature() + feature.project_id = project.id + feature.summary = valid.require("summary") + if not valid.ok: + abort(400) # admin-only route, who cares + db.session.add(feature) + db.session.commit() + return redirect(url_for("public.project_index")) diff --git a/hubsrht/blueprints/public.py b/hubsrht/blueprints/public.py index 3ed2a4b..795f5ff 100644 --- a/hubsrht/blueprints/public.py +++ b/hubsrht/blueprints/public.py @@ -1,5 +1,5 @@ from flask import Blueprint, render_template, request -from hubsrht.types import Project, Event, EventType, Visibility +from hubsrht.types import Project, Feature, Event, EventType, Visibility, User from srht.flask import paginate_query from srht.oauth import current_user, loginrequired from srht.search import search_by @@ -48,5 +48,12 @@ def project_index(): projects, pagination = paginate_query(projects) + features = (Feature.query + .join(Project, Feature.project_id == Project.id) + .join(User, Project.owner_id == User.id) + .filter(Project.visibility == Visibility.public) + .order_by(Feature.created.desc()) + .limit(5)).all() + return render_template("project-index.html", projects=projects, - search=search, sort=sort, **pagination) + search=search, features=features, sort=sort, **pagination) diff --git a/hubsrht/decorators.py b/hubsrht/decorators.py new file mode 100644 index 0000000..360a582 --- /dev/null +++ b/hubsrht/decorators.py @@ -0,0 +1,14 @@ +from flask import redirect, abort, current_app, request +from functools import wraps +from srht.oauth import current_user, UserType + +def adminrequired(f): + @wraps(f) + def wrapper(*args, **kwargs): + if not current_user: + return redirect(current_app.oauth_service.oauth_url(request.url)) + elif current_user.user_type != UserType.admin: + abort(403) + else: + return f(*args, **kwargs) + return wrapper diff --git a/hubsrht/templates/project-index.html b/hubsrht/templates/project-index.html index 2026482..f6bc5c9 100644 --- a/hubsrht/templates/project-index.html +++ b/hubsrht/templates/project-index.html @@ -76,12 +76,16 @@

Featured projects

- {% for i in range(3) %} - ~sircmpwn/sourcehut + {% for feature in features %} + {{feature.project.owner.canonical_name}}/{{feature.project.name}}
- SourceHut itself is a free and open-source software project. You - can read and contribute to the code, and install it on your own - servers. + {{feature.summary | md}}
{% endfor %}
diff --git a/hubsrht/templates/project-summary.html b/hubsrht/templates/project-summary.html index c17b011..56dc9ed 100644 --- a/hubsrht/templates/project-summary.html +++ b/hubsrht/templates/project-summary.html @@ -203,5 +203,30 @@
{% endif %} + {% if current_user and current_user.user_type.value == "admin" %} +
+
+

Feature this project

+
+ {{csrf_token()}} +
+ +
+ +
+
+
+ {% endif %} {% endblock %} diff --git a/hubsrht/types/__init__.py b/hubsrht/types/__init__.py index 2d0fb15..c7556c5 100644 --- a/hubsrht/types/__init__.py +++ b/hubsrht/types/__init__.py @@ -11,6 +11,7 @@ class Visibility(Enum): private = "private" from hubsrht.types.event import Event, EventType +from hubsrht.types.feature import Feature from hubsrht.types.mailinglist import MailingList from hubsrht.types.project import Project from hubsrht.types.sourcerepo import SourceRepo, RepoType diff --git a/hubsrht/types/feature.py b/hubsrht/types/feature.py new file mode 100644 index 0000000..404ff9d --- /dev/null +++ b/hubsrht/types/feature.py @@ -0,0 +1,16 @@ +import sqlalchemy as sa +import sqlalchemy_utils as sau +from hubsrht.types import Visibility +from srht.database import Base + +class Feature(Base): + __tablename__ = "features" + id = sa.Column(sa.Integer, primary_key=True) + created = sa.Column(sa.DateTime, nullable=False) + + project_id = sa.Column(sa.Integer, + sa.ForeignKey("project.id", ondelete="CASCADE"), + nullable=False) + project = sa.orm.relationship("Project") + + summary = sa.Column(sa.Unicode(), nullable=False)