From 148ece162c739d23b7203807028b7ca02541b68f Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Thu, 22 Jul 2021 12:13:16 +0100 Subject: [PATCH] Add Featured packages and protected tags --- app/blueprints/admin/tagseditor.py | 5 ++- app/blueprints/api/endpoints.py | 3 ++ app/blueprints/homepage/__init__.py | 4 +- app/logic/packages.py | 11 +++-- app/models/packages.py | 1 + app/scss/custom.scss | 8 ++++ app/tasks/importtasks.py | 23 ++++++++-- app/templates/admin/tags/edit.html | 6 ++- app/templates/index.html | 66 +++++++++++++++++++++++++++- migrations/versions/d4262fb15b37_.py | 28 ++++++++++++ 10 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 migrations/versions/d4262fb15b37_.py diff --git a/app/blueprints/admin/tagseditor.py b/app/blueprints/admin/tagseditor.py index 14a6d6c..ad0d731 100644 --- a/app/blueprints/admin/tagseditor.py +++ b/app/blueprints/admin/tagseditor.py @@ -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): diff --git a/app/blueprints/api/endpoints.py b/app/blueprints/api/endpoints.py index 6fcfe3e..4610515 100644 --- a/app/blueprints/api/endpoints.py +++ b/app/blueprints/api/endpoints.py @@ -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), diff --git a/app/blueprints/homepage/__init__.py b/app/blueprints/homepage/__init__.py index 3e2e79a..a768590 100644 --- a/app/blueprints/homepage/__init__.py +++ b/app/blueprints/homepage/__init__.py @@ -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) diff --git a/app/logic/packages.py b/app/logic/packages.py index 6284b05..d478c78 100644 --- a/app/logic/packages.py +++ b/app/logic/packages.py @@ -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,15 +134,20 @@ 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) - package.tags.append(tag) + + 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: package.content_warnings.clear() diff --git a/app/models/packages.py b/app/models/packages.py index 0fb845b..d2e6444 100644 --- a/app/models/packages.py +++ b/app/models/packages.py @@ -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) diff --git a/app/scss/custom.scss b/app/scss/custom.scss index cafab72..989339b 100644 --- a/app/scss/custom.scss +++ b/app/scss/custom.scss @@ -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"; diff --git a/app/tasks/importtasks.py b/app/tasks/importtasks.py index f20c08c..2532ce8 100644 --- a/app/tasks/importtasks.py +++ b/app/tasks/importtasks.py @@ -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 diff --git a/app/templates/admin/tags/edit.html b/app/templates/admin/tags/edit.html index bb8d462..75a0c76 100644 --- a/app/templates/admin/tags/edit.html +++ b/app/templates/admin/tags/edit.html @@ -12,7 +12,7 @@ New Tag Back to list - {% from "macros/forms.html" import render_field, render_submit_field %} + {% from "macros/forms.html" import render_field, render_submit_field, render_checkbox_field %}
{{ form.hidden_tag() }} @@ -21,6 +21,10 @@ {% if tag %} {{ render_field(form.name) }} {% endif %} +
+ {{ render_checkbox_field(form.is_protected) }} + Whether non-Editors can add this tag to packages +
{{ render_submit_field(form.submit) }}
{% endblock %} diff --git a/app/templates/index.html b/app/templates/index.html index 05ea824..c4a840e 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -21,7 +21,69 @@ {% block content %} {% from "macros/packagegridtile.html" import render_pkggrid %} - + {{ _("See more") }} @@ -76,7 +138,7 @@ {{ _("See more") }} -

{{ _("Top Reviewed") }}

+

{{ _("Highest Reviewed") }}

{{ render_pkggrid(high_reviewed) }} diff --git a/migrations/versions/d4262fb15b37_.py b/migrations/versions/d4262fb15b37_.py new file mode 100644 index 0000000..0b68e41 --- /dev/null +++ b/migrations/versions/d4262fb15b37_.py @@ -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 ###