mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-22 14:02:24 +01:00
Use collections for spotlight and featured, remove protected tags
This commit is contained in:
parent
a2ea6573bd
commit
4bd53e4b1a
@ -47,7 +47,6 @@ class TagForm(FlaskForm):
|
|||||||
description = TextAreaField("Description", [Optional(), Length(0, 500)])
|
description = TextAreaField("Description", [Optional(), Length(0, 500)])
|
||||||
name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0,
|
name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0,
|
||||||
"Lower case letters (a-z), digits (0-9), and underscores (_) only")])
|
"Lower case letters (a-z), digits (0-9), and underscores (_) only")])
|
||||||
is_protected = BooleanField("Is Protected")
|
|
||||||
submit = SubmitField("Save")
|
submit = SubmitField("Save")
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +68,6 @@ def create_edit_tag(name=None):
|
|||||||
if tag is None:
|
if tag is None:
|
||||||
tag = Tag(form.title.data)
|
tag = Tag(form.title.data)
|
||||||
tag.description = form.description.data
|
tag.description = form.description.data
|
||||||
tag.is_protected = form.is_protected.data
|
|
||||||
db.session.add(tag)
|
db.session.add(tag)
|
||||||
|
|
||||||
add_audit_log(AuditSeverity.EDITOR, current_user, f"Created tag {tag.name}",
|
add_audit_log(AuditSeverity.EDITOR, current_user, f"Created tag {tag.name}",
|
||||||
|
@ -21,6 +21,7 @@ from typing import List
|
|||||||
import flask_sqlalchemy
|
import flask_sqlalchemy
|
||||||
from flask import request, jsonify, current_app, Response
|
from flask import request, jsonify, current_app, Response
|
||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
|
from sqlalchemy import and_
|
||||||
from sqlalchemy.orm import joinedload
|
from sqlalchemy.orm import joinedload
|
||||||
from sqlalchemy.sql.expression import func
|
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.logic.graphs import get_package_stats, get_package_stats_for_user, get_all_package_stats
|
||||||
from app.markdown import render_markdown
|
from app.markdown import render_markdown
|
||||||
from app.models import Tag, PackageState, PackageType, Package, db, PackageRelease, Permission, ForumTopic, \
|
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.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 app.utils import is_package_page, get_int_or_abort, url_set_query, abs_url, is_yes, get_request_date
|
||||||
from . import bp
|
from . import bp
|
||||||
@ -78,7 +79,8 @@ def packages():
|
|||||||
# Promote featured packages
|
# Promote featured packages
|
||||||
if "sort" not in request.args and "order" not in request.args and "q" not in request.args:
|
if "sort" not in request.args and "order" not in request.args and "q" not in request.args:
|
||||||
featured_lut = set()
|
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:
|
for pkg in featured:
|
||||||
featured_lut.add(f"{pkg['author']}/{pkg['name']}")
|
featured_lut.add(f"{pkg['author']}/{pkg['name']}")
|
||||||
pkg["short_description"] = "Featured. " + pkg["short_description"]
|
pkg["short_description"] = "Featured. " + pkg["short_description"]
|
||||||
@ -535,8 +537,9 @@ def homepage():
|
|||||||
query = Package.query.filter_by(state=PackageState.APPROVED)
|
query = Package.query.filter_by(state=PackageState.APPROVED)
|
||||||
count = query.count()
|
count = query.count()
|
||||||
|
|
||||||
spotlight = query.filter(Package.tags.any(name="spotlight")).order_by(
|
spotlight = query.filter(
|
||||||
func.random()).limit(6).all()
|
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()
|
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_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()
|
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():
|
def welcome_v1():
|
||||||
featured = Package.query \
|
featured = Package.query \
|
||||||
.filter(Package.type == PackageType.GAME, Package.state == PackageState.APPROVED,
|
.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()) \
|
.order_by(func.random()) \
|
||||||
.limit(5).all()
|
.limit(5).all()
|
||||||
|
|
||||||
|
@ -15,8 +15,10 @@
|
|||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from flask import Blueprint, render_template, redirect
|
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__)
|
bp = Blueprint("homepage", __name__)
|
||||||
|
|
||||||
@ -49,7 +51,9 @@ def home():
|
|||||||
query = Package.query.filter_by(state=PackageState.APPROVED)
|
query = Package.query.filter_by(state=PackageState.APPROVED)
|
||||||
count = query.count()
|
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()
|
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()
|
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 = {}
|
tags = {}
|
||||||
for tag in ["Inventory", "Mapgen", "Building",
|
for tag in ["Inventory", "Mapgen", "Building",
|
||||||
"Mobs and NPCs", "Tools", "Player effects",
|
"Mobs and NPCs", "Tools", "Player effects",
|
||||||
"Environment", "Transport", "Maintenance", "Plants and farming",
|
"Environment", "Transport", "Maintenance", "Plants and farming",
|
||||||
"PvP", "PvE", "Survival", "Creative", "Puzzle", "Multiplayer", "Singleplayer",
|
"PvP", "PvE", "Survival", "Creative", "Puzzle", "Multiplayer", "Singleplayer"]:
|
||||||
"Featured", "Spotlight"]:
|
|
||||||
row = Tag(tag)
|
row = Tag(tag)
|
||||||
tags[row.name] = row
|
tags[row.name] = row
|
||||||
session.add(row)
|
session.add(row)
|
||||||
|
|
||||||
licenses = {}
|
licenses = {}
|
||||||
for license in ["GPLv2.1", "GPLv3", "LGPLv2.1", "LGPLv3", "AGPLv2.1", "AGPLv3",
|
for license in ["GPLv2.1", "GPLv3", "LGPLv2.1", "LGPLv3", "AGPLv2.1", "AGPLv3",
|
||||||
"Apache", "BSD 3-Clause", "BSD 2-Clause", "CC0", "CC-BY-SA",
|
"Apache", "BSD 3-Clause", "BSD 2-Clause", "CC0", "CC-BY-SA",
|
||||||
"CC-BY", "MIT", "ZLib", "Other (Free)"]:
|
"CC-BY", "MIT", "ZLib", "Other (Free)"]:
|
||||||
row = License(license)
|
row = License(license)
|
||||||
licenses[row.name] = row
|
licenses[row.name] = row
|
||||||
session.add(row)
|
session.add(row)
|
||||||
|
@ -451,7 +451,6 @@ Supported query parameters:
|
|||||||
* `name`: technical name.
|
* `name`: technical name.
|
||||||
* `title`: human-readable title.
|
* `title`: human-readable title.
|
||||||
* `description`: tag description or null.
|
* `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.
|
* `views`: number of views of this tag.
|
||||||
|
|
||||||
### Content Warnings
|
### Content Warnings
|
||||||
|
@ -170,19 +170,8 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
|
|||||||
if tag is None:
|
if tag is None:
|
||||||
raise LogicError(400, "Unknown tag: " + tag_id)
|
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)
|
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:
|
if "content_warnings" in data:
|
||||||
package.content_warnings.clear()
|
package.content_warnings.clear()
|
||||||
for warning_id in (data["content_warnings"] or []):
|
for warning_id in (data["content_warnings"] or []):
|
||||||
|
@ -865,7 +865,6 @@ class Tag(db.Model):
|
|||||||
backgroundColor = db.Column(db.String(6), nullable=False)
|
backgroundColor = db.Column(db.String(6), nullable=False)
|
||||||
textColor = db.Column(db.String(6), nullable=False)
|
textColor = db.Column(db.String(6), nullable=False)
|
||||||
views = db.Column(db.Integer, nullable=False, default=0)
|
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)
|
packages = db.relationship("Package", back_populates="tags", secondary=Tags)
|
||||||
|
|
||||||
@ -884,7 +883,6 @@ class Tag(db.Model):
|
|||||||
"name": self.name,
|
"name": self.name,
|
||||||
"title": self.title,
|
"title": self.title,
|
||||||
"description": description,
|
"description": description,
|
||||||
"is_protected": self.is_protected,
|
|
||||||
"views": self.views,
|
"views": self.views,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,10 +21,6 @@
|
|||||||
{% if tag %}
|
{% if tag %}
|
||||||
{{ render_field(form.name) }}
|
{{ render_field(form.name) }}
|
||||||
{% endif %}
|
{% 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) }}
|
{{ render_submit_field(form.submit) }}
|
||||||
{% if tag %}
|
{% if tag %}
|
||||||
<a class="ml-5" target="_blank" href="{{ url_for('packages.list_all', tag=tag.name) }}">
|
<a class="ml-5" target="_blank" href="{{ url_for('packages.list_all', tag=tag.name) }}">
|
||||||
|
@ -64,11 +64,9 @@
|
|||||||
</span>
|
</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for t in tags[:3] %}
|
{% for t in tags[:3] %}
|
||||||
{% if t.name != "featured" and t.name != "Spotlight" %}
|
<span class="badge badge-primary" title="{{ t.description or '' }}">
|
||||||
<span class="badge badge-primary" title="{{ t.description or '' }}">
|
{{ t.title }}
|
||||||
{{ t.title }}
|
</span>
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<span class="btn" title="{{ _('Reviews') }}">
|
<span class="btn" title="{{ _('Reviews') }}">
|
||||||
<i class="fas fa-star-half-alt"></i>
|
<i class="fas fa-star-half-alt"></i>
|
||||||
|
26
migrations/versions/7a749a6c8c3a_.py
Normal file
26
migrations/versions/7a749a6c8c3a_.py
Normal file
@ -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))
|
Loading…
Reference in New Issue
Block a user