Use collections for spotlight and featured, remove protected tags

This commit is contained in:
rubenwardy 2023-08-20 22:25:18 +01:00
parent a2ea6573bd
commit 4bd53e4b1a
10 changed files with 49 additions and 38 deletions

@ -47,7 +47,6 @@ class TagForm(FlaskForm):
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")
@ -69,7 +68,6 @@ def create_edit_tag(name=None):
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)
add_audit_log(AuditSeverity.EDITOR, current_user, f"Created tag {tag.name}",

@ -21,6 +21,7 @@ from typing import List
import flask_sqlalchemy
from flask import request, jsonify, current_app, Response
from flask_login import current_user, login_required
from sqlalchemy import and_
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import func
@ -28,7 +29,7 @@ from app import csrf
from app.logic.graphs import get_package_stats, get_package_stats_for_user, get_all_package_stats
from app.markdown import render_markdown
from app.models import Tag, PackageState, PackageType, Package, db, PackageRelease, Permission, ForumTopic, \
MinetestRelease, APIToken, PackageScreenshot, License, ContentWarning, User, PackageReview, Thread
MinetestRelease, APIToken, PackageScreenshot, License, ContentWarning, User, PackageReview, Thread, Collection
from app.querybuilder import QueryBuilder
from app.utils import is_package_page, get_int_or_abort, url_set_query, abs_url, is_yes, get_request_date
from . import bp
@ -78,7 +79,8 @@ def packages():
# Promote featured packages
if "sort" not in request.args and "order" not in request.args and "q" not in request.args:
featured_lut = set()
featured = qb.convert_to_dictionary(query.filter(Package.tags.any(name="featured")).all())
featured = qb.convert_to_dictionary(query.filter(
Package.collections.any(and_(Collection.name == "featured", Collection.author.has(username="ContentDB")))).all())
for pkg in featured:
featured_lut.add(f"{pkg['author']}/{pkg['name']}")
pkg["short_description"] = "Featured. " + pkg["short_description"]
@ -535,8 +537,9 @@ def homepage():
query = Package.query.filter_by(state=PackageState.APPROVED)
count = query.count()
spotlight = query.filter(Package.tags.any(name="spotlight")).order_by(
func.random()).limit(6).all()
spotlight = query.filter(
Package.collections.any(and_(Collection.name == "featured", Collection.author.has(username="ContentDB")))) \
.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()
@ -574,7 +577,8 @@ def homepage():
def welcome_v1():
featured = Package.query \
.filter(Package.type == PackageType.GAME, Package.state == PackageState.APPROVED,
Package.tags.any(name="featured")) \
Package.collections.any(
and_(Collection.name == "featured", Collection.author.has(username="ContentDB")))) \
.order_by(func.random()) \
.limit(5).all()

@ -15,8 +15,10 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from flask import Blueprint, render_template, redirect
from sqlalchemy import and_
from app.models import Package, PackageReview, Thread, User, PackageState, db, PackageType, PackageRelease, Tags, Tag
from app.models import Package, PackageReview, Thread, User, PackageState, db, PackageType, PackageRelease, Tags, Tag, \
Collection
bp = Blueprint("homepage", __name__)
@ -49,7 +51,9 @@ def home():
query = Package.query.filter_by(state=PackageState.APPROVED)
count = query.count()
spotlight_pkgs = query.filter(Package.tags.any(name="spotlight")).order_by(func.random()).limit(6).all()
spotlight_pkgs = query.filter(
Package.collections.any(and_(Collection.name == "featured", Collection.author.has(username="ContentDB")))) \
.order_by(func.random()).limit(6).all()
new = package_load(query.order_by(db.desc(Package.approved_at))).limit(4).all()
pop_mod = package_load(query.filter_by(type=PackageType.MOD).order_by(db.desc(Package.score))).limit(8).all()

@ -44,18 +44,17 @@ def populate(session):
tags = {}
for tag in ["Inventory", "Mapgen", "Building",
"Mobs and NPCs", "Tools", "Player effects",
"Environment", "Transport", "Maintenance", "Plants and farming",
"PvP", "PvE", "Survival", "Creative", "Puzzle", "Multiplayer", "Singleplayer",
"Featured", "Spotlight"]:
"Mobs and NPCs", "Tools", "Player effects",
"Environment", "Transport", "Maintenance", "Plants and farming",
"PvP", "PvE", "Survival", "Creative", "Puzzle", "Multiplayer", "Singleplayer"]:
row = Tag(tag)
tags[row.name] = row
session.add(row)
licenses = {}
for license in ["GPLv2.1", "GPLv3", "LGPLv2.1", "LGPLv3", "AGPLv2.1", "AGPLv3",
"Apache", "BSD 3-Clause", "BSD 2-Clause", "CC0", "CC-BY-SA",
"CC-BY", "MIT", "ZLib", "Other (Free)"]:
"Apache", "BSD 3-Clause", "BSD 2-Clause", "CC0", "CC-BY-SA",
"CC-BY", "MIT", "ZLib", "Other (Free)"]:
row = License(license)
licenses[row.name] = row
session.add(row)

@ -451,7 +451,6 @@ Supported query parameters:
* `name`: technical name.
* `title`: human-readable title.
* `description`: tag description or null.
* `is_protected`: boolean, whether the tag is protected (can only be set by Editors in the web interface).
* `views`: number of views of this tag.
### Content Warnings

@ -170,19 +170,8 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
if tag is None:
raise LogicError(400, "Unknown tag: " + tag_id)
if not was_web and tag.is_protected:
continue
if tag.is_protected and tag not in old_tags and not user.rank.at_least(UserRank.EDITOR):
raise LogicError(400, lazy_gettext("Unable to add protected tag %(title)s to package", title=tag.title))
package.tags.append(tag)
if not was_web:
for tag in old_tags:
if tag.is_protected:
package.tags.append(tag)
if "content_warnings" in data:
package.content_warnings.clear()
for warning_id in (data["content_warnings"] or []):

@ -865,7 +865,6 @@ 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)
@ -884,7 +883,6 @@ class Tag(db.Model):
"name": self.name,
"title": self.title,
"description": description,
"is_protected": self.is_protected,
"views": self.views,
}

@ -21,10 +21,6 @@
{% 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) }}
{% if tag %}
<a class="ml-5" target="_blank" href="{{ url_for('packages.list_all', tag=tag.name) }}">

@ -64,11 +64,9 @@
</span>
{% endfor %}
{% for t in tags[:3] %}
{% if t.name != "featured" and t.name != "Spotlight" %}
<span class="badge badge-primary" title="{{ t.description or '' }}">
{{ t.title }}
</span>
{% endif %}
<span class="badge badge-primary" title="{{ t.description or '' }}">
{{ t.title }}
</span>
{% endfor %}
<span class="btn" title="{{ _('Reviews') }}">
<i class="fas fa-star-half-alt"></i>

@ -0,0 +1,26 @@
"""empty message
Revision ID: 7a749a6c8c3a
Revises: 20f2aa2f40b9
Create Date: 2023-08-20 21:19:26.930069
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '7a749a6c8c3a'
down_revision = '20f2aa2f40b9'
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table('tag', schema=None) as batch_op:
batch_op.drop_column('is_protected')
def downgrade():
with op.batch_alter_table('tag', schema=None) as batch_op:
batch_op.add_column(sa.Column('is_protected', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))