diff --git a/app/blueprints/admin/tagseditor.py b/app/blueprints/admin/tagseditor.py
index 10156a79..77454597 100644
--- a/app/blueprints/admin/tagseditor.py
+++ b/app/blueprints/admin/tagseditor.py
@@ -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}",
diff --git a/app/blueprints/api/endpoints.py b/app/blueprints/api/endpoints.py
index c9e80f46..22369a0c 100644
--- a/app/blueprints/api/endpoints.py
+++ b/app/blueprints/api/endpoints.py
@@ -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()
diff --git a/app/blueprints/homepage/__init__.py b/app/blueprints/homepage/__init__.py
index 381f25f7..8f58cad7 100644
--- a/app/blueprints/homepage/__init__.py
+++ b/app/blueprints/homepage/__init__.py
@@ -15,8 +15,10 @@
# along with this program. If not, see .
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()
diff --git a/app/default_data.py b/app/default_data.py
index 63dd8770..917395bb 100644
--- a/app/default_data.py
+++ b/app/default_data.py
@@ -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)
diff --git a/app/flatpages/help/api.md b/app/flatpages/help/api.md
index 1862ed32..7220249f 100644
--- a/app/flatpages/help/api.md
+++ b/app/flatpages/help/api.md
@@ -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
diff --git a/app/logic/packages.py b/app/logic/packages.py
index 09734d14..43b95448 100644
--- a/app/logic/packages.py
+++ b/app/logic/packages.py
@@ -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 []):
diff --git a/app/models/packages.py b/app/models/packages.py
index 42303654..4cf6683e 100644
--- a/app/models/packages.py
+++ b/app/models/packages.py
@@ -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,
}
diff --git a/app/templates/admin/tags/edit.html b/app/templates/admin/tags/edit.html
index 039c29a3..a8881833 100644
--- a/app/templates/admin/tags/edit.html
+++ b/app/templates/admin/tags/edit.html
@@ -21,10 +21,6 @@
{% 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) }}
{% if tag %}
diff --git a/app/templates/index.html b/app/templates/index.html
index 04e33537..c42fda9f 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -64,11 +64,9 @@
{% endfor %}
{% for t in tags[:3] %}
- {% if t.name != "featured" and t.name != "Spotlight" %}
-
- {{ t.title }}
-
- {% endif %}
+
+ {{ t.title }}
+
{% endfor %}
diff --git a/migrations/versions/7a749a6c8c3a_.py b/migrations/versions/7a749a6c8c3a_.py
new file mode 100644
index 00000000..ad452e2f
--- /dev/null
+++ b/migrations/versions/7a749a6c8c3a_.py
@@ -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))