mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-22 14:02:24 +01:00
Add Featured packages and protected tags
This commit is contained in:
parent
ce2bb3abad
commit
148ece162c
@ -44,6 +44,7 @@ class TagForm(FlaskForm):
|
||||
title = StringField("Title", [InputRequired(), Length(3,100)])
|
||||
description = TextAreaField("Description", [Optional(), Length(0, 500)])
|
||||
name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
|
||||
is_protected = BooleanField("Is Protected")
|
||||
submit = SubmitField("Save")
|
||||
|
||||
@bp.route("/tags/new/", methods=["GET", "POST"])
|
||||
@ -59,14 +60,16 @@ def create_edit_tag(name=None):
|
||||
if not Permission.checkPerm(current_user, Permission.EDIT_TAGS if tag else Permission.CREATE_TAG):
|
||||
abort(403)
|
||||
|
||||
form = TagForm(formdata=request.form, obj=tag)
|
||||
form = TagForm( obj=tag)
|
||||
if form.validate_on_submit():
|
||||
if tag is None:
|
||||
tag = Tag(form.title.data)
|
||||
tag.description = form.description.data
|
||||
tag.is_protected = form.is_protected.data
|
||||
db.session.add(tag)
|
||||
else:
|
||||
form.populate_obj(tag)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
if Permission.EDIT_TAGS.check(current_user):
|
||||
|
@ -364,6 +364,8 @@ def homepage():
|
||||
query = Package.query.filter_by(state=PackageState.APPROVED)
|
||||
count = query.count()
|
||||
|
||||
featured = Package.query.filter(Package.tags.any(name="featured")).order_by(
|
||||
func.random()).limit(6).all()
|
||||
new = query.order_by(db.desc(Package.approved_at)).limit(4).all()
|
||||
pop_mod = query.filter_by(type=PackageType.MOD).order_by(db.desc(Package.score)).limit(8).all()
|
||||
pop_gam = query.filter_by(type=PackageType.GAME).order_by(db.desc(Package.score)).limit(8).all()
|
||||
@ -386,6 +388,7 @@ def homepage():
|
||||
return {
|
||||
"count": count,
|
||||
"downloads": downloads,
|
||||
"featured": featured,
|
||||
"new": mapPackages(new),
|
||||
"updated": mapPackages(updated),
|
||||
"pop_mod": mapPackages(pop_mod),
|
||||
|
@ -18,6 +18,8 @@ def home():
|
||||
query = Package.query.filter_by(state=PackageState.APPROVED)
|
||||
count = query.count()
|
||||
|
||||
featured = Package.query.filter(Package.tags.any(name="featured")).order_by(func.random()).limit(6).all()
|
||||
|
||||
new = join(query.order_by(db.desc(Package.approved_at))).limit(4).all()
|
||||
pop_mod = join(query.filter_by(type=PackageType.MOD).order_by(db.desc(Package.score))).limit(8).all()
|
||||
pop_gam = join(query.filter_by(type=PackageType.GAME).order_by(db.desc(Package.score))).limit(8).all()
|
||||
@ -39,5 +41,5 @@ def home():
|
||||
tags = db.session.query(func.count(Tags.c.tag_id), Tag) \
|
||||
.select_from(Tag).outerjoin(Tags).group_by(Tag.id).order_by(db.asc(Tag.title)).all()
|
||||
|
||||
return render_template("index.html", count=count, downloads=downloads, tags=tags,
|
||||
return render_template("index.html", count=count, downloads=downloads, tags=tags, featured=featured,
|
||||
new=new, updated=updated, pop_mod=pop_mod, pop_txp=pop_txp, pop_gam=pop_gam, high_reviewed=high_reviewed, reviews=reviews)
|
||||
|
@ -19,7 +19,7 @@ import re
|
||||
import validators
|
||||
|
||||
from app.logic.LogicError import LogicError
|
||||
from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, License
|
||||
from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, License, UserRank
|
||||
from app.utils import addAuditLog
|
||||
|
||||
|
||||
@ -134,14 +134,19 @@ def do_edit_package(user: User, package: Package, was_new: bool, data: dict, rea
|
||||
package.provides.append(m)
|
||||
|
||||
if "tags" in data:
|
||||
old_tags = package.tags
|
||||
package.tags.clear()
|
||||
for tag_id in data["tags"]:
|
||||
if is_int(tag_id):
|
||||
package.tags.append(Tag.query.get(tag_id))
|
||||
tag = Tag.query.get(tag_id)
|
||||
else:
|
||||
tag = Tag.query.filter_by(name=tag_id).first()
|
||||
if tag is None:
|
||||
raise LogicError(400, "Unknown tag: " + tag_id)
|
||||
|
||||
if tag.is_protected and tag not in old_tags and not user.rank.atLeast(UserRank.EDITOR):
|
||||
raise LogicError(400, f"Unable to add protected tag {tag.title} to package")
|
||||
|
||||
package.tags.append(tag)
|
||||
|
||||
if "content_warnings" in data:
|
||||
|
@ -744,6 +744,7 @@ class Tag(db.Model):
|
||||
backgroundColor = db.Column(db.String(6), nullable=False)
|
||||
textColor = db.Column(db.String(6), nullable=False)
|
||||
views = db.Column(db.Integer, nullable=False, default=0)
|
||||
is_protected = db.Column(db.Boolean, nullable=False, default=False)
|
||||
|
||||
packages = db.relationship("Package", back_populates="tags", secondary=Tags)
|
||||
|
||||
|
@ -173,4 +173,12 @@ pre code {
|
||||
}
|
||||
}
|
||||
|
||||
.fs-2 {
|
||||
font-size: calc(1.325rem + .9vw) !important;
|
||||
}
|
||||
|
||||
.text-shadow {
|
||||
text-shadow: 3px 3px 3px rgba(10,10,10,0.2);
|
||||
}
|
||||
|
||||
@import "dracula.scss";
|
||||
|
@ -70,6 +70,23 @@ def getMeta(urlstr, author):
|
||||
return result
|
||||
|
||||
|
||||
def get_edit_data_from_dir(dir: str):
|
||||
data = {}
|
||||
for path in [os.path.join(dir, ".cdb.json"), os.path.join(dir, ".cdb", "meta.json")]:
|
||||
if os.path.isfile(path):
|
||||
with open(path, "r") as f:
|
||||
data = json.loads(f.read())
|
||||
break
|
||||
|
||||
for path in [os.path.join(dir, ".cdb.md"), os.path.join(dir, ".cdb", "long_description.md")]:
|
||||
if os.path.isfile(path):
|
||||
with open(path, "r") as f:
|
||||
data["long_description"] = f.read().replace("\r\n", "\n")
|
||||
break
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
||||
try:
|
||||
tree = build_tree(path, expected_type=ContentType[release.package.type.name],
|
||||
@ -117,13 +134,11 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
||||
release.max_rel = MinetestRelease.get(tree.meta["max_minetest_version"], None)
|
||||
|
||||
try:
|
||||
with open(os.path.join(tree.baseDir, ".cdb.json"), "r") as f:
|
||||
data = json.loads(f.read())
|
||||
data = get_edit_data_from_dir(tree.baseDir)
|
||||
if data != {}: # Not sure if this will actually work to check not empty, probably not
|
||||
do_edit_package(package.author, package, False, data, "Post release hook")
|
||||
except LogicError as e:
|
||||
raise TaskError(e.message)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return tree
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
<a class="btn btn-primary float-right" href="{{ url_for('admin.create_edit_tag') }}">New Tag</a>
|
||||
<a class="btn btn-secondary mb-4" href="{{ url_for('admin.tag_list') }}">Back to list</a>
|
||||
|
||||
{% from "macros/forms.html" import render_field, render_submit_field %}
|
||||
{% from "macros/forms.html" import render_field, render_submit_field, render_checkbox_field %}
|
||||
<form method="POST" action="" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
@ -21,6 +21,10 @@
|
||||
{% if tag %}
|
||||
{{ render_field(form.name) }}
|
||||
{% endif %}
|
||||
<div class="form-group my-5">
|
||||
{{ render_checkbox_field(form.is_protected) }}
|
||||
<small class="form-text text-muted">Whether non-Editors can add this tag to packages</small>
|
||||
</div>
|
||||
{{ render_submit_field(form.submit) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
@ -21,7 +21,69 @@
|
||||
|
||||
{% block content %}
|
||||
{% from "macros/packagegridtile.html" import render_pkggrid %}
|
||||
|
||||
<div id="featuredCarousel" class="carousel slide mb-5" data-ride="carousel">
|
||||
<ol class="carousel-indicators">
|
||||
{% for package in featured %}
|
||||
<li data-target="#featuredCarousel" data-slide-to="{{ loop.index - 1 }}" {% if loop.index == 1 %}class="active"{% endif %}></li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<div class="carousel-inner">
|
||||
{% for package in featured %}
|
||||
{% set cover_image = package.cover_image.url or package.getMainScreenshotURL() %}
|
||||
{% set tags = package.tags | sort(attribute="views", reverse=True) %}
|
||||
<div class="carousel-item {% if loop.index == 1 %}active{% endif %}">
|
||||
<a href="{{ package.getDetailsURL() }}">
|
||||
<div class="embed-responsive embed-responsive-16by9">
|
||||
<img class="embed-responsive-item" src="{{ cover_image }}"
|
||||
alt="{{ _('%(title)s by %(author)s', title=package.title, author=package.author.display_name) }}">
|
||||
</div>
|
||||
<div class="carousel-caption d-none d-md-block text-shadow">
|
||||
<h3 class="mt-0 mb-3 fs-2">
|
||||
{{ _('<strong>%(title)s</strong> by %(author)s', title=package.title, author=package.author.display_name) }}
|
||||
</h3>
|
||||
<p>
|
||||
{{ package.short_desc }}
|
||||
</p>
|
||||
<div>
|
||||
<span class="mr-2">
|
||||
{{ package.type.value }}
|
||||
</span>
|
||||
{% for warning in package.content_warnings %}
|
||||
<span class="badge badge-warning" title="{{ warning.description }}">
|
||||
<i class="fas fa-exclamation-circle" style="margin-right: 0.3em;"></i>
|
||||
{{ warning.title }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% for t in tags[:3] %}
|
||||
{% if t.name != "featured" %}
|
||||
<span class="badge badge-primary" title="{{ t.description or '' }}">
|
||||
{{ t.title }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<span class="btn" title="{{ _("Reviews") }}">
|
||||
<i class="fas fa-star-half-alt"></i>
|
||||
<span class="count">
|
||||
+{{ package.reviews | selectattr("recommends") | list | length }}
|
||||
/
|
||||
-{{ package.reviews | rejectattr("recommends") | list | length }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<a class="carousel-control-prev" href="#featuredCarousel" role="button" data-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||
<span class="sr-only">{{ _("Previous") }}</span>
|
||||
</a>
|
||||
<a class="carousel-control-next" href="#featuredCarousel" role="button" data-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||
<span class="sr-only">{{ _("Next") }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a href="{{ url_for('packages.list_all', sort='approved_at', order='desc') }}" class="btn btn-secondary float-right">
|
||||
{{ _("See more") }}
|
||||
@ -76,7 +138,7 @@
|
||||
<a href="{{ url_for('packages.list_all', sort='reviews', order='desc') }}" class="btn btn-secondary float-right">
|
||||
{{ _("See more") }}
|
||||
</a>
|
||||
<h2 class="my-3">{{ _("Top Reviewed") }}</h2>
|
||||
<h2 class="my-3">{{ _("Highest Reviewed") }}</h2>
|
||||
{{ render_pkggrid(high_reviewed) }}
|
||||
|
||||
|
||||
|
28
migrations/versions/d4262fb15b37_.py
Normal file
28
migrations/versions/d4262fb15b37_.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: d4262fb15b37
|
||||
Revises: 8ee3cf3fb312
|
||||
Create Date: 2021-07-22 10:59:03.217264
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd4262fb15b37'
|
||||
down_revision = '8ee3cf3fb312'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('tag', sa.Column('is_protected', sa.Boolean(), nullable=False, server_default="false"))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('tag', 'is_protected')
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue
Block a user