mirror of https://git.sr.ht/~sircmpwn/hub.sr.ht
Implement featured projects
This commit is contained in:
parent
19c9a87682
commit
102fca490c
|
@ -1,7 +1,8 @@
|
||||||
from flask import Blueprint, render_template, request, redirect, url_for
|
from flask import Blueprint, render_template, request, redirect, url_for
|
||||||
|
from hubsrht.decorators import adminrequired
|
||||||
from hubsrht.projects import ProjectAccess, get_project
|
from hubsrht.projects import ProjectAccess, get_project
|
||||||
from hubsrht.services import git, hg
|
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 hubsrht.types import Project, RepoType, Visibility
|
||||||
from srht.database import db
|
from srht.database import db
|
||||||
from srht.flask import paginate_query
|
from srht.flask import paginate_query
|
||||||
|
@ -136,3 +137,18 @@ def delete_POST(owner, project_name):
|
||||||
db.session.delete(project)
|
db.session.delete(project)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect(url_for("public.index"))
|
return redirect(url_for("public.index"))
|
||||||
|
|
||||||
|
@projects.route("/<owner>/<project_name>/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"))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from flask import Blueprint, render_template, request
|
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.flask import paginate_query
|
||||||
from srht.oauth import current_user, loginrequired
|
from srht.oauth import current_user, loginrequired
|
||||||
from srht.search import search_by
|
from srht.search import search_by
|
||||||
|
@ -48,5 +48,12 @@ def project_index():
|
||||||
|
|
||||||
projects, pagination = paginate_query(projects)
|
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,
|
return render_template("project-index.html", projects=projects,
|
||||||
search=search, sort=sort, **pagination)
|
search=search, features=features, sort=sort, **pagination)
|
||||||
|
|
|
@ -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
|
|
@ -76,12 +76,16 @@
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<h3>Featured projects</h3>
|
<h3>Featured projects</h3>
|
||||||
<div class="event-list">
|
<div class="event-list">
|
||||||
{% for i in range(3) %}
|
{% for feature in features %}
|
||||||
<a href="#">~sircmpwn</a>/<a href="#">sourcehut</a>
|
<a href="{{url_for("users.summary_GET",
|
||||||
|
username=feature.project.owner.username)}}"
|
||||||
|
>{{feature.project.owner.canonical_name}}</a>/<a
|
||||||
|
href="{{url_for("projects.summary_GET",
|
||||||
|
owner=feature.project.owner.canonical_name,
|
||||||
|
project_name=feature.project.name)}}"
|
||||||
|
>{{feature.project.name}}</a>
|
||||||
<blockquote style="margin-top: 0.5rem">
|
<blockquote style="margin-top: 0.5rem">
|
||||||
SourceHut itself is a free and open-source software project. You
|
{{feature.summary | md}}
|
||||||
can read and contribute to the code, and install it on your own
|
|
||||||
servers.
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -203,5 +203,30 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{% if current_user and current_user.user_type.value == "admin" %}
|
||||||
|
<div class="row" style="margin-top: 5rem">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h3>Feature this project</h3>
|
||||||
|
<form action="{{url_for("projects.feature_POST",
|
||||||
|
owner=project.owner.canonical_name,
|
||||||
|
project_name=project.name)}}"
|
||||||
|
method="POST">
|
||||||
|
{{csrf_token()}}
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea
|
||||||
|
class="form-control"
|
||||||
|
name="summary"
|
||||||
|
rows="3"
|
||||||
|
placeholder="Featured project summary"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary"
|
||||||
|
>Feature project {{icon('caret-right')}}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -11,6 +11,7 @@ class Visibility(Enum):
|
||||||
private = "private"
|
private = "private"
|
||||||
|
|
||||||
from hubsrht.types.event import Event, EventType
|
from hubsrht.types.event import Event, EventType
|
||||||
|
from hubsrht.types.feature import Feature
|
||||||
from hubsrht.types.mailinglist import MailingList
|
from hubsrht.types.mailinglist import MailingList
|
||||||
from hubsrht.types.project import Project
|
from hubsrht.types.project import Project
|
||||||
from hubsrht.types.sourcerepo import SourceRepo, RepoType
|
from hubsrht.types.sourcerepo import SourceRepo, RepoType
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue