Compare commits
12 Commits
master
...
package_ty
Author | SHA1 | Date |
---|---|---|
Armen | 21fe900cc8 | |
Armen | 8df6ebd2e7 | |
Armen | 28ba3c6c38 | |
Armen | 6b1b3ac96b | |
Armen | 52858d4d8e | |
Armen | d4be91caee | |
Armen | 1d48f5dabe | |
Armen | 16108d1f39 | |
Armen | e2d26eb6ee | |
Armen | 311bfd2199 | |
Rampoina | ebdaa9d84d | |
Armen | e4dcb89d2e |
12
Dockerfile
12
Dockerfile
|
@ -1,24 +1,22 @@
|
|||
FROM python:3.10
|
||||
|
||||
RUN groupadd -g 5123 cdb && \
|
||||
useradd -r -u 5123 -g cdb cdb
|
||||
|
||||
WORKDIR /home/cdb
|
||||
|
||||
RUN mkdir /var/cdb
|
||||
RUN chown -R cdb:cdb /var/cdb
|
||||
|
||||
COPY requirements.lock.txt requirements.lock.txt
|
||||
RUN apt update
|
||||
RUN apt install -y vim
|
||||
RUN apt install -y libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev gir1.2-gtk-3.0
|
||||
RUN apt install -y libappstream-glib-dev
|
||||
RUN pip install -r requirements.lock.txt
|
||||
RUN pip install gunicorn
|
||||
|
||||
RUN pip3 install pycairo PyGObject
|
||||
COPY utils utils
|
||||
COPY config.cfg config.cfg
|
||||
COPY migrations migrations
|
||||
COPY app app
|
||||
COPY translations translations
|
||||
|
||||
RUN pybabel compile -d translations
|
||||
RUN chown -R cdb:cdb /home/cdb
|
||||
|
||||
USER cdb
|
|
@ -26,9 +26,10 @@ from sqlalchemy import or_, and_
|
|||
|
||||
from app.logic.game_support import GameSupportResolver
|
||||
from app.models import PackageRelease, db, Package, PackageState, PackageScreenshot, MetaPackage, User, \
|
||||
NotificationType, PackageUpdateConfig, License, UserRank, PackageType, PackageGameSupport
|
||||
from app.tasks.forumtasks import importTopicList, checkAllForumAccounts
|
||||
NotificationType, PackageUpdateConfig, License, UserRank, PackageGameSupport
|
||||
# from app.tasks.forumtasks import importTopicList, checkAllForumAccounts
|
||||
from app.tasks.importtasks import importRepoScreenshot, checkZipRelease, check_for_updates
|
||||
from app.tasks.appstreamtasks import importFromFlathub
|
||||
from app.utils import addNotification, get_system_user
|
||||
from app.utils.image import get_image_size
|
||||
|
||||
|
@ -89,16 +90,20 @@ def reimport_packages():
|
|||
return redirect(url_for("todo.view_editor"))
|
||||
|
||||
|
||||
@action("Import forum topic list")
|
||||
def import_topic_list():
|
||||
task = importTopicList.delay()
|
||||
# @action("Import forum topic list")
|
||||
# def import_topic_list():
|
||||
# task = importTopicList.delay()
|
||||
# return redirect(url_for("tasks.check", id=task.id, r=url_for("admin.admin_page")))
|
||||
|
||||
@action("Import appstream from flathub")
|
||||
def import_from_flathub():
|
||||
task = importFromFlathub.delay()
|
||||
return redirect(url_for("tasks.check", id=task.id, r=url_for("todo.topics")))
|
||||
|
||||
|
||||
@action("Check all forum accounts")
|
||||
def check_all_forum_accounts():
|
||||
task = checkAllForumAccounts.delay()
|
||||
return redirect(url_for("tasks.check", id=task.id, r=url_for("admin.admin_page")))
|
||||
# @action("Check all forum accounts")
|
||||
# def check_all_forum_accounts():
|
||||
# task = checkAllForumAccounts.delay()
|
||||
# return redirect(url_for("tasks.check", id=task.id, r=url_for("admin.admin_page")))
|
||||
|
||||
|
||||
@action("Import screenshots")
|
||||
|
@ -292,13 +297,12 @@ def delete_inactive_users():
|
|||
@action("Send Video URL notification")
|
||||
def remind_video_url():
|
||||
users = User.query.filter(User.maintained_packages.any(
|
||||
and_(Package.video_url.is_(None), Package.type==PackageType.GAME, Package.state==PackageState.APPROVED)))
|
||||
and_(Package.video_url.is_(None), Package.state==PackageState.APPROVED)))
|
||||
system_user = get_system_user()
|
||||
for user in users:
|
||||
packages = db.session.query(Package.title).filter(
|
||||
or_(Package.author==user, Package.maintainers.any(User.id==user.id)),
|
||||
Package.video_url.is_(None),
|
||||
Package.type == PackageType.GAME,
|
||||
Package.state == PackageState.APPROVED) \
|
||||
.all()
|
||||
|
||||
|
|
|
@ -22,42 +22,42 @@ from wtforms.validators import InputRequired, Length
|
|||
|
||||
from app.utils import rank_required
|
||||
from . import bp
|
||||
from ...models import UserRank, MinetestRelease, db
|
||||
from ...models import UserRank, db
|
||||
|
||||
|
||||
@bp.route("/versions/")
|
||||
@rank_required(UserRank.MODERATOR)
|
||||
def version_list():
|
||||
return render_template("admin/versions/list.html", versions=MinetestRelease.query.order_by(db.asc(MinetestRelease.id)).all())
|
||||
# @bp.route("/versions/")
|
||||
# @rank_required(UserRank.MODERATOR)
|
||||
# def version_list():
|
||||
# return render_template("admin/versions/list.html", versions=MinetestRelease.query.order_by(db.asc(MinetestRelease.id)).all())
|
||||
|
||||
|
||||
class VersionForm(FlaskForm):
|
||||
name = StringField("Name", [InputRequired(), Length(3, 100)])
|
||||
protocol = IntegerField("Protocol")
|
||||
submit = SubmitField("Save")
|
||||
# class VersionForm(FlaskForm):
|
||||
# name = StringField("Name", [InputRequired(), Length(3, 100)])
|
||||
# protocol = IntegerField("Protocol")
|
||||
# submit = SubmitField("Save")
|
||||
|
||||
|
||||
@bp.route("/versions/new/", methods=["GET", "POST"])
|
||||
@bp.route("/versions/<name>/edit/", methods=["GET", "POST"])
|
||||
@rank_required(UserRank.MODERATOR)
|
||||
def create_edit_version(name=None):
|
||||
version = None
|
||||
if name is not None:
|
||||
version = MinetestRelease.query.filter_by(name=name).first()
|
||||
if version is None:
|
||||
abort(404)
|
||||
# @bp.route("/versions/new/", methods=["GET", "POST"])
|
||||
# @bp.route("/versions/<name>/edit/", methods=["GET", "POST"])
|
||||
# @rank_required(UserRank.MODERATOR)
|
||||
# def create_edit_version(name=None):
|
||||
# version = None
|
||||
# if name is not None:
|
||||
# version = MinetestRelease.query.filter_by(name=name).first()
|
||||
# if version is None:
|
||||
# abort(404)
|
||||
|
||||
form = VersionForm(formdata=request.form, obj=version)
|
||||
if form.validate_on_submit():
|
||||
if version is None:
|
||||
version = MinetestRelease(form.name.data)
|
||||
db.session.add(version)
|
||||
flash("Created version " + form.name.data, "success")
|
||||
else:
|
||||
flash("Updated version " + form.name.data, "success")
|
||||
# form = VersionForm(formdata=request.form, obj=version)
|
||||
# if form.validate_on_submit():
|
||||
# if version is None:
|
||||
# version = MinetestRelease(form.name.data)
|
||||
# db.session.add(version)
|
||||
# flash("Created version " + form.name.data, "success")
|
||||
# else:
|
||||
# flash("Updated version " + form.name.data, "success")
|
||||
|
||||
form.populate_obj(version)
|
||||
db.session.commit()
|
||||
return redirect(url_for("admin.version_list"))
|
||||
# form.populate_obj(version)
|
||||
# db.session.commit()
|
||||
# return redirect(url_for("admin.version_list"))
|
||||
|
||||
return render_template("admin/versions/edit.html", version=version, form=form)
|
||||
# return render_template("admin/versions/edit.html", version=version, form=form)
|
||||
|
|
|
@ -25,10 +25,10 @@ from sqlalchemy.sql.expression import func
|
|||
|
||||
from app import csrf
|
||||
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
|
||||
from app.models import Tag, PackageState, Package, db, PackageRelease, Permission, ForumTopic, \
|
||||
APIToken, PackageScreenshot, License, ContentWarning, User, PackageReview, Thread
|
||||
from app.querybuilder import QueryBuilder
|
||||
from app.utils import is_package_page, get_int_or_abort, url_set_query, abs_url, isYes
|
||||
from app.utils import is_package_page, get_int_or_abort, url_set_query, abs_url, isYes, get_toplevel_tags
|
||||
from . import bp
|
||||
from .auth import is_api_authd
|
||||
from .support import error, api_create_vcs_release, api_create_zip_release, api_create_screenshot, \
|
||||
|
@ -89,9 +89,6 @@ def resolve_package_deps(out, package, only_hard, depth=1):
|
|||
ret = []
|
||||
out[id] = ret
|
||||
|
||||
if package.type != PackageType.MOD:
|
||||
return
|
||||
|
||||
for dep in package.dependencies:
|
||||
if only_hard and dep.optional:
|
||||
continue
|
||||
|
@ -106,7 +103,7 @@ def resolve_package_deps(out, package, only_hard, depth=1):
|
|||
fulfilled_by = [ pkg.getId() for pkg in dep.meta_package.packages]
|
||||
|
||||
if depth == 1 and not dep.optional:
|
||||
most_likely = next((pkg for pkg in dep.meta_package.packages if pkg.type == PackageType.MOD), None)
|
||||
most_likely = next((pkg for pkg in dep.meta_package.packages), None)
|
||||
if most_likely:
|
||||
resolve_package_deps(out, most_likely, only_hard, depth + 1)
|
||||
|
||||
|
@ -470,9 +467,11 @@ def homepage():
|
|||
featured = 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()
|
||||
pop_txp = query.filter_by(type=PackageType.TXP).order_by(db.desc(Package.score)).limit(8).all()
|
||||
toplevel_tags = get_toplevel_tags()
|
||||
popular = {}
|
||||
# new = join(query.order_by(db.desc(Package.approved_at))).limit(4).all()
|
||||
for toplevel_tag in toplevel_tags:
|
||||
popular[toplevel_tag.name] = query.filter(Package.tags.any(name=toplevel_tag.name)).order_by(db.desc(Package.score)).limit(8).all()
|
||||
high_reviewed = query.order_by(db.desc(Package.score - Package.score_downloads)) \
|
||||
.filter(Package.reviews.any()).limit(4).all()
|
||||
|
||||
|
@ -487,16 +486,15 @@ def homepage():
|
|||
|
||||
def mapPackages(packages: List[Package]):
|
||||
return [pkg.getAsDictionaryShort(current_app.config["BASE_URL"]) for pkg in packages]
|
||||
|
||||
popular = { k:mapPackages(v) for (k, v) in popular.items() }
|
||||
return jsonify({
|
||||
"count": count,
|
||||
"downloads": downloads,
|
||||
"featured": mapPackages(featured),
|
||||
"new": mapPackages(new),
|
||||
"updated": mapPackages(updated),
|
||||
"pop_mod": mapPackages(pop_mod),
|
||||
"pop_txp": mapPackages(pop_txp),
|
||||
"pop_game": mapPackages(pop_gam),
|
||||
"popular": popular,
|
||||
"toplevel": [ x.getAsDictionary() for x in toplevel_tags ],
|
||||
"high_reviewed": mapPackages(high_reviewed)
|
||||
})
|
||||
|
||||
|
@ -505,7 +503,7 @@ def homepage():
|
|||
@cors_allowed
|
||||
def welcome_v1():
|
||||
featured = Package.query \
|
||||
.filter(Package.type == PackageType.GAME, Package.state == PackageState.APPROVED,
|
||||
.filter(Package.state == PackageState.APPROVED,
|
||||
Package.tags.any(name="featured")) \
|
||||
.order_by(func.random()) \
|
||||
.limit(5).all()
|
||||
|
@ -520,23 +518,6 @@ def welcome_v1():
|
|||
"featured": map_packages(featured),
|
||||
})
|
||||
|
||||
|
||||
@bp.route("/api/minetest_versions/")
|
||||
@cors_allowed
|
||||
def versions():
|
||||
protocol_version = request.args.get("protocol_version")
|
||||
engine_version = request.args.get("engine_version")
|
||||
if protocol_version or engine_version:
|
||||
rel = MinetestRelease.get(engine_version, get_int_or_abort(protocol_version))
|
||||
if rel is None:
|
||||
error(404, "No releases found")
|
||||
|
||||
return jsonify(rel.getAsDictionary())
|
||||
|
||||
return jsonify([rel.getAsDictionary() \
|
||||
for rel in MinetestRelease.query.all() if rel.getActual() is not None])
|
||||
|
||||
|
||||
@bp.route("/api/dependencies/")
|
||||
@cors_allowed
|
||||
def all_deps():
|
||||
|
@ -545,7 +526,7 @@ def all_deps():
|
|||
|
||||
def format_pkg(pkg: Package):
|
||||
return {
|
||||
"type": pkg.type.toName(),
|
||||
# "type": pkg.type.toName(),
|
||||
"author": pkg.author.username,
|
||||
"name": pkg.name,
|
||||
"provides": [x.name for x in pkg.provides],
|
||||
|
|
|
@ -20,7 +20,7 @@ from flask import jsonify, abort, make_response, url_for, current_app
|
|||
from app.logic.packages import do_edit_package
|
||||
from app.logic.releases import LogicError, do_create_vcs_release, do_create_zip_release
|
||||
from app.logic.screenshots import do_create_screenshot, do_order_screenshots, do_set_cover_image
|
||||
from app.models import APIToken, Package, MinetestRelease, PackageScreenshot
|
||||
from app.models import APIToken, Package, PackageScreenshot
|
||||
|
||||
|
||||
def error(code: int, msg: str):
|
||||
|
@ -38,7 +38,7 @@ def guard(f):
|
|||
|
||||
|
||||
def api_create_vcs_release(token: APIToken, package: Package, title: str, ref: str,
|
||||
min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason="API"):
|
||||
min_v = None, max_v = None, reason="API"):
|
||||
if not token.canOperateOnPackage(package):
|
||||
error(403, "API token does not have access to the package")
|
||||
|
||||
|
@ -54,7 +54,7 @@ def api_create_vcs_release(token: APIToken, package: Package, title: str, ref: s
|
|||
|
||||
|
||||
def api_create_zip_release(token: APIToken, package: Package, title: str, file,
|
||||
min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason="API", commit_hash:str=None):
|
||||
min_v = None, max_v = None, reason="API", commit_hash:str=None):
|
||||
if not token.canOperateOnPackage(package):
|
||||
error(403, "API token does not have access to the package")
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from flask import Blueprint, render_template, redirect
|
|||
bp = Blueprint("homepage", __name__)
|
||||
|
||||
from app.models import *
|
||||
from app.utils import get_toplevel_tags
|
||||
from sqlalchemy.orm import joinedload
|
||||
from sqlalchemy.sql.expression import func
|
||||
|
||||
|
@ -18,11 +19,12 @@ def home():
|
|||
count = query.count()
|
||||
|
||||
featured = query.filter(Package.tags.any(name="featured")).order_by(func.random()).limit(6).all()
|
||||
|
||||
toplevel_tags = get_toplevel_tags() #Tag.query.filter_by(is_toplevel=True).all()
|
||||
popular = {}
|
||||
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()
|
||||
pop_txp = join(query.filter_by(type=PackageType.TXP).order_by(db.desc(Package.score))).limit(8).all()
|
||||
for toplevel_tag in toplevel_tags:
|
||||
popular[toplevel_tag.name] = join(query.filter(Package.tags.any(name=toplevel_tag.name)).order_by(db.desc(Package.score))).limit(8).all()
|
||||
|
||||
high_reviewed = join(query.order_by(db.desc(Package.score - Package.score_downloads))) \
|
||||
.filter(Package.reviews.any()).limit(4).all()
|
||||
|
||||
|
@ -41,4 +43,4 @@ def home():
|
|||
.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, featured=featured,
|
||||
new=new, updated=updated, pop_mod=pop_mod, pop_txp=pop_txp, pop_gam=pop_gam, high_reviewed=high_reviewed, reviews=reviews)
|
||||
new=new, updated=updated, popular=popular, toplevel=toplevel_tags, high_reviewed=high_reviewed, reviews=reviews)
|
||||
|
|
|
@ -18,16 +18,13 @@ from flask import render_template, abort
|
|||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from . import bp
|
||||
from app.utils import is_package_page
|
||||
from ...models import Package, PackageType, PackageState, db, PackageRelease
|
||||
from app.utils import is_package_page, get_toplevel_tags
|
||||
from ...models import Package, PackageState, db, PackageRelease
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/hub/")
|
||||
@is_package_page
|
||||
def game_hub(package: Package):
|
||||
if package.type != PackageType.GAME:
|
||||
abort(404)
|
||||
|
||||
def join(query):
|
||||
return query.options(
|
||||
joinedload(Package.license),
|
||||
|
@ -37,9 +34,11 @@ def game_hub(package: Package):
|
|||
count = query.count()
|
||||
|
||||
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()
|
||||
pop_txp = join(query.filter_by(type=PackageType.TXP).order_by(db.desc(Package.score))).limit(8).all()
|
||||
toplevel_tags = get_toplevel_tags() #Tag.query.filter_by(is_toplevel=True).order_by(db.desc(Tag.id)).all()
|
||||
popular = {}
|
||||
new = join(query.order_by(db.desc(Package.approved_at))).limit(4).all()
|
||||
for toplevel_tag in toplevel_tags:
|
||||
popular[toplevel_tag.name] = join(query.filter(Package.tags.any(name=toplevel_tag.name)).order_by(db.desc(Package.score))).limit(8).all()
|
||||
high_reviewed = join(query.order_by(db.desc(Package.score - Package.score_downloads))) \
|
||||
.filter(Package.reviews.any()).limit(4).all()
|
||||
|
||||
|
@ -50,5 +49,5 @@ def game_hub(package: Package):
|
|||
updated = updated[:4]
|
||||
|
||||
return render_template("packages/game_hub.html", package=package, count=count,
|
||||
new=new, updated=updated, pop_mod=pop_mod, pop_txp=pop_txp, pop_gam=pop_gam,
|
||||
new=new, updated=updated, popular=popular, toplevel=toplevel_tags,
|
||||
high_reviewed=high_reviewed)
|
||||
|
|
|
@ -99,10 +99,12 @@ def list_all():
|
|||
|
||||
selected_tags = set(qb.tags)
|
||||
|
||||
toplevel_tags = get_toplevel_tags() #Tag.query.filter_by(is_toplevel=True).all()
|
||||
|
||||
return render_template("packages/list.html",
|
||||
query_hint=title, packages=query.items, pagination=query,
|
||||
query=search, tags=tags, selected_tags=selected_tags, type=type_name,
|
||||
authors=authors, packages_count=query.total, topics=topics)
|
||||
authors=authors, packages_count=query.total, topics=topics, toplevel=toplevel_tags)
|
||||
|
||||
|
||||
def getReleases(package):
|
||||
|
@ -120,30 +122,28 @@ def view(package):
|
|||
package.checkPerm(current_user, Permission.APPROVE_NEW))
|
||||
|
||||
conflicting_modnames = None
|
||||
if show_similar and package.type != PackageType.TXP:
|
||||
if show_similar:
|
||||
conflicting_modnames = db.session.query(MetaPackage.name) \
|
||||
.filter(MetaPackage.id.in_([ mp.id for mp in package.provides ])) \
|
||||
.filter(MetaPackage.packages.any(Package.id != package.id)) \
|
||||
.all()
|
||||
|
||||
conflicting_modnames += db.session.query(ForumTopic.name) \
|
||||
.filter(ForumTopic.name.in_([ mp.name for mp in package.provides ])) \
|
||||
.filter(ForumTopic.topic_id != package.forums) \
|
||||
.filter(~ db.exists().where(Package.forums==ForumTopic.topic_id)) \
|
||||
.order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \
|
||||
.all()
|
||||
# conflicting_modnames += db.session.query(ForumTopic.name) \
|
||||
# .filter(ForumTopic.name.in_([ mp.name for mp in package.provides ])) \
|
||||
# .filter(ForumTopic.topic_id != package.forums) \
|
||||
# .filter(~ db.exists().where(Package.forums==ForumTopic.topic_id)) \
|
||||
# .order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \
|
||||
# .all()
|
||||
|
||||
conflicting_modnames = set([x[0] for x in conflicting_modnames])
|
||||
|
||||
packages_uses = None
|
||||
if package.type == PackageType.MOD:
|
||||
packages_uses = Package.query.filter(
|
||||
Package.type == PackageType.MOD,
|
||||
Package.id != package.id,
|
||||
Package.state == PackageState.APPROVED,
|
||||
Package.dependencies.any(
|
||||
Dependency.meta_package_id.in_([p.id for p in package.provides]))) \
|
||||
.order_by(db.desc(Package.score)).limit(6).all()
|
||||
packages_uses = Package.query.filter(
|
||||
Package.id != package.id,
|
||||
Package.state == PackageState.APPROVED,
|
||||
Package.dependencies.any(
|
||||
Dependency.meta_package_id.in_([p.id for p in package.provides]))) \
|
||||
.order_by(db.desc(Package.score)).limit(6).all()
|
||||
|
||||
releases = getReleases(package)
|
||||
|
||||
|
@ -153,21 +153,21 @@ def view(package):
|
|||
|
||||
topic_error = None
|
||||
topic_error_lvl = "warning"
|
||||
if package.state != PackageState.APPROVED and package.forums is not None:
|
||||
errors = []
|
||||
if Package.query.filter(Package.forums==package.forums, Package.state!=PackageState.DELETED).count() > 1:
|
||||
errors.append("<b>" + gettext("Error: Another package already uses this forum topic!") + "</b>")
|
||||
topic_error_lvl = "danger"
|
||||
# if package.state != PackageState.APPROVED and package.forums is not None:
|
||||
# errors = []
|
||||
# if Package.query.filter(Package.forums==package.forums, Package.state!=PackageState.DELETED).count() > 1:
|
||||
# errors.append("<b>" + gettext("Error: Another package already uses this forum topic!") + "</b>")
|
||||
# topic_error_lvl = "danger"
|
||||
|
||||
topic = ForumTopic.query.get(package.forums)
|
||||
if topic is not None:
|
||||
if topic.author != package.author:
|
||||
errors.append("<b>" + gettext("Error: Forum topic author doesn't match package author.") + "</b>")
|
||||
topic_error_lvl = "danger"
|
||||
elif package.type != PackageType.TXP:
|
||||
errors.append(gettext("Warning: Forum topic not found. This may happen if the topic has only just been created."))
|
||||
# topic = ForumTopic.query.get(package.forums)
|
||||
# if topic is not None:
|
||||
# if topic.author != package.author:
|
||||
# errors.append("<b>" + gettext("Error: Forum topic author doesn't match package author.") + "</b>")
|
||||
# topic_error_lvl = "danger"
|
||||
# elif package.type != PackageType.ASSETPACK:
|
||||
# errors.append(gettext("Warning: Forum topic not found. This may happen if the topic has only just been created."))
|
||||
|
||||
topic_error = "<br />".join(errors)
|
||||
# topic_error = "<br />".join(errors)
|
||||
|
||||
|
||||
threads = Thread.query.filter_by(package_id=package.id, review_id=None)
|
||||
|
@ -178,11 +178,13 @@ def view(package):
|
|||
|
||||
has_review = current_user.is_authenticated and PackageReview.query.filter_by(package=package, author=current_user).count() > 0
|
||||
|
||||
toplevel_tags = get_toplevel_tags() #Tag.query.filter_by(is_toplevel=True).all()
|
||||
|
||||
return render_template("packages/view.html",
|
||||
package=package, releases=releases, packages_uses=packages_uses,
|
||||
conflicting_modnames=conflicting_modnames,
|
||||
review_thread=review_thread, topic_error=topic_error, topic_error_lvl=topic_error_lvl,
|
||||
threads=threads.all(), has_review=has_review)
|
||||
threads=threads.all(), has_review=has_review, toplevel=toplevel_tags)
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/shields/<type>/")
|
||||
|
@ -226,9 +228,16 @@ def makeLabel(obj):
|
|||
|
||||
|
||||
class PackageForm(FlaskForm):
|
||||
type = SelectField(lazy_gettext("Type"), [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
|
||||
try:
|
||||
toplevel_tags = [ x.title for x in Tag.query.filter_by(is_toplevel=True).all() ]
|
||||
type = SelectField(lazy_gettext("Type"), [InputRequired()], choices=toplevel_tags, default=toplevel_tags[0])
|
||||
except:
|
||||
toplevel_tags = []
|
||||
# type = SelectField(lazy_gettext("Type"), [InputRequired()], choices=toplevel_tags, default=toplevel_tags[0])
|
||||
|
||||
# type = SelectField(lazy_gettext("Type"), [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.GAME)
|
||||
title = StringField(lazy_gettext("Title (Human-readable)"), [InputRequired(), Length(1, 100)])
|
||||
name = StringField(lazy_gettext("Name (Technical)"), [InputRequired(), Length(1, 100), Regexp("^[a-z0-9_]+$", 0, lazy_gettext("Lower case letters (a-z), digits (0-9), and underscores (_) only"))])
|
||||
name = StringField(lazy_gettext("Name (Technical)"), [InputRequired(), Length(1, 100), Regexp("^[a-zA-Z0-9_\-\.]+$", 0, lazy_gettext("Lower case letters (a-z), digits (0-9), and underscores (_), dashes and periods only"))])
|
||||
short_desc = StringField(lazy_gettext("Short Description (Plaintext)"), [InputRequired(), Length(1,200)])
|
||||
|
||||
dev_state = SelectField(lazy_gettext("Maintenance State"), [InputRequired()], choices=PackageDevState.choices(with_none=True), coerce=PackageDevState.coerce)
|
||||
|
@ -243,7 +252,7 @@ class PackageForm(FlaskForm):
|
|||
repo = StringField(lazy_gettext("VCS Repository URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||
website = StringField(lazy_gettext("Website URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||
issueTracker = StringField(lazy_gettext("Issue Tracker URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||
forums = IntegerField(lazy_gettext("Forum Topic ID"), [Optional(), NumberRange(0,999999)])
|
||||
# forums = IntegerField(lazy_gettext("Forum Topic ID"), [Optional(), NumberRange(0,999999)])
|
||||
video_url = StringField(lazy_gettext("Video URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||
|
||||
submit = SubmitField(lazy_gettext("Save"))
|
||||
|
@ -286,16 +295,13 @@ def create_edit(author=None, name=None):
|
|||
form.name.data = request.args.get("bname")
|
||||
form.title.data = request.args.get("title")
|
||||
form.repo.data = request.args.get("repo")
|
||||
form.forums.data = request.args.get("forums")
|
||||
# form.forums.data = request.args.get("forums")
|
||||
form.license.data = None
|
||||
form.media_license.data = None
|
||||
else:
|
||||
form.tags.data = package.tags
|
||||
form.content_warnings.data = package.content_warnings
|
||||
|
||||
if request.method == "POST" and form.type.data == PackageType.TXP:
|
||||
form.license.data = form.media_license.data
|
||||
|
||||
if form.validate_on_submit():
|
||||
wasNew = False
|
||||
if not package:
|
||||
|
@ -327,7 +333,7 @@ def create_edit(author=None, name=None):
|
|||
"repo": form.repo.data,
|
||||
"website": form.website.data,
|
||||
"issueTracker": form.issueTracker.data,
|
||||
"forums": form.forums.data,
|
||||
# "forums": form.forums.data,
|
||||
"video_url": form.video_url.data,
|
||||
})
|
||||
|
||||
|
@ -353,7 +359,7 @@ def create_edit(author=None, name=None):
|
|||
form=form, author=author, enable_wizard=enableWizard,
|
||||
packages=package_query.all(),
|
||||
mpackages=MetaPackage.query.order_by(db.asc(MetaPackage.name)).all(),
|
||||
tabs=get_package_tabs(current_user, package), current_tab="edit")
|
||||
tabs=get_package_tabs(current_user, package), current_tab="edit", toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/state/", methods=["POST"])
|
||||
|
@ -408,7 +414,7 @@ def move_to_state(package):
|
|||
def remove(package):
|
||||
if request.method == "GET":
|
||||
return render_template("packages/remove.html", package=package,
|
||||
tabs=get_package_tabs(current_user, package), current_tab="remove")
|
||||
tabs=get_package_tabs(current_user, package), current_tab="remove", toplevel=get_toplevel_tags())
|
||||
|
||||
reason = request.form.get("reason") or "?"
|
||||
|
||||
|
@ -501,7 +507,7 @@ def edit_maintainers(package):
|
|||
users = User.query.filter(User.rank >= UserRank.NEW_MEMBER).order_by(db.asc(User.username)).all()
|
||||
|
||||
return render_template("packages/edit_maintainers.html", package=package, form=form,
|
||||
users=users, tabs=get_package_tabs(current_user, package), current_tab="maintainers")
|
||||
users=users, tabs=get_package_tabs(current_user, package), current_tab="maintainers", toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/remove-self-maintainer/", methods=["POST"])
|
||||
|
@ -540,13 +546,13 @@ def audit(package):
|
|||
|
||||
pagination = query.paginate(page, num, True)
|
||||
return render_template("packages/audit.html", log=pagination.items, pagination=pagination,
|
||||
package=package, tabs=get_package_tabs(current_user, package), current_tab="audit")
|
||||
package=package, tabs=get_package_tabs(current_user, package), current_tab="audit", toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
class PackageAliasForm(FlaskForm):
|
||||
author = StringField(lazy_gettext("Author Name"), [InputRequired(), Length(1, 50)])
|
||||
name = StringField(lazy_gettext("Name (Technical)"), [InputRequired(), Length(1, 100),
|
||||
Regexp("^[a-z0-9_]+$", 0, lazy_gettext("Lower case letters (a-z), digits (0-9), and underscores (_) only"))])
|
||||
Regexp("^[a-zA-Z0-9_\-\.]+$", 0, lazy_gettext("Lower case letters (a-z), digits (0-9), and underscores (_), dashes and periods only"))])
|
||||
submit = SubmitField(lazy_gettext("Save"))
|
||||
|
||||
|
||||
|
@ -554,7 +560,7 @@ class PackageAliasForm(FlaskForm):
|
|||
@rank_required(UserRank.EDITOR)
|
||||
@is_package_page
|
||||
def alias_list(package: Package):
|
||||
return render_template("packages/alias_list.html", package=package)
|
||||
return render_template("packages/alias_list.html", package=package, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/aliases/new/", methods=["GET", "POST"])
|
||||
|
@ -580,7 +586,7 @@ def alias_create_edit(package: Package, alias_id: int = None):
|
|||
|
||||
return redirect(package.getURL("packages.alias_list"))
|
||||
|
||||
return render_template("packages/alias_create_edit.html", package=package, form=form)
|
||||
return render_template("packages/alias_create_edit.html", package=package, form=form, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/share/")
|
||||
|
@ -588,7 +594,7 @@ def alias_create_edit(package: Package, alias_id: int = None):
|
|||
@is_package_page
|
||||
def share(package):
|
||||
return render_template("packages/share.html", package=package,
|
||||
tabs=get_package_tabs(current_user, package), current_tab="share")
|
||||
tabs=get_package_tabs(current_user, package), current_tab="share", toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/similar/")
|
||||
|
@ -602,12 +608,12 @@ def similar(package):
|
|||
.order_by(db.desc(Package.score)) \
|
||||
.all()
|
||||
|
||||
similar_topics = ForumTopic.query \
|
||||
.filter_by(name=package.name) \
|
||||
.filter(ForumTopic.topic_id != package.forums) \
|
||||
.filter(~ db.exists().where(Package.forums == ForumTopic.topic_id)) \
|
||||
.order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \
|
||||
.all()
|
||||
# similar_topics = ForumTopic.query \
|
||||
# .filter_by(name=package.name) \
|
||||
# .filter(ForumTopic.topic_id != package.forums) \
|
||||
# .filter(~ db.exists().where(Package.forums == ForumTopic.topic_id)) \
|
||||
# .order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \
|
||||
# .all()
|
||||
|
||||
return render_template("packages/similar.html", package=package,
|
||||
packages_modnames=packages_modnames, similar_topics=similar_topics)
|
||||
packages_modnames=packages_modnames, similar_topics=[], toplevel=get_toplevel_tags())
|
||||
|
|
|
@ -35,17 +35,17 @@ from . import bp, get_package_tabs
|
|||
def list_releases(package):
|
||||
return render_template("packages/releases_list.html",
|
||||
package=package,
|
||||
tabs=get_package_tabs(current_user, package), current_tab="releases")
|
||||
tabs=get_package_tabs(current_user, package), current_tab="releases", toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
def get_mt_releases(is_max):
|
||||
query = MinetestRelease.query.order_by(db.asc(MinetestRelease.id))
|
||||
if is_max:
|
||||
query = query.limit(query.count() - 1)
|
||||
else:
|
||||
query = query.filter(MinetestRelease.name != "0.4.17")
|
||||
# def get_mt_releases(is_max):
|
||||
# query = MinetestRelease.query.order_by(db.asc(MinetestRelease.id))
|
||||
# if is_max:
|
||||
# query = query.limit(query.count() - 1)
|
||||
# else:
|
||||
# query = query.filter(MinetestRelease.name != "0.4.17")
|
||||
|
||||
return query
|
||||
# return query
|
||||
|
||||
|
||||
class CreatePackageReleaseForm(FlaskForm):
|
||||
|
@ -53,10 +53,6 @@ class CreatePackageReleaseForm(FlaskForm):
|
|||
uploadOpt = RadioField(lazy_gettext("Method"), choices=[("upload", lazy_gettext("File Upload"))], default="upload")
|
||||
vcsLabel = StringField(lazy_gettext("Git reference (ie: commit hash, branch, or tag)"), default=None)
|
||||
fileUpload = FileField(lazy_gettext("File Upload"))
|
||||
min_rel = QuerySelectField(lazy_gettext("Minimum Minetest Version"), [InputRequired()],
|
||||
query_factory=lambda: get_mt_releases(False), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
max_rel = QuerySelectField(lazy_gettext("Maximum Minetest Version"), [InputRequired()],
|
||||
query_factory=lambda: get_mt_releases(True), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
submit = SubmitField(lazy_gettext("Save"))
|
||||
|
||||
|
||||
|
@ -65,10 +61,6 @@ class EditPackageReleaseForm(FlaskForm):
|
|||
url = StringField(lazy_gettext("URL"), [Optional()])
|
||||
task_id = StringField(lazy_gettext("Task ID"), filters = [lambda x: x or None])
|
||||
approved = BooleanField(lazy_gettext("Is Approved"))
|
||||
min_rel = QuerySelectField(lazy_gettext("Minimum Minetest Version"), [InputRequired()],
|
||||
query_factory=lambda: get_mt_releases(False), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
max_rel = QuerySelectField(lazy_gettext("Maximum Minetest Version"), [InputRequired()],
|
||||
query_factory=lambda: get_mt_releases(True), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
submit = SubmitField(lazy_gettext("Save"))
|
||||
|
||||
|
||||
|
@ -93,16 +85,14 @@ def create_release(package):
|
|||
if form.validate_on_submit():
|
||||
try:
|
||||
if form["uploadOpt"].data == "vcs":
|
||||
rel = do_create_vcs_release(current_user, package, form.title.data,
|
||||
form.vcsLabel.data, form.min_rel.data.getActual(), form.max_rel.data.getActual())
|
||||
rel = do_create_vcs_release(current_user, package, form.title.data, form.vcsLabel.data)
|
||||
else:
|
||||
rel = do_create_zip_release(current_user, package, form.title.data,
|
||||
form.fileUpload.data, form.min_rel.data.getActual(), form.max_rel.data.getActual())
|
||||
rel = do_create_zip_release(current_user, package, form.title.data, form.fileUpload.data)
|
||||
return redirect(url_for("tasks.check", id=rel.task_id, r=rel.getEditURL()))
|
||||
except LogicError as e:
|
||||
flash(e.message, "danger")
|
||||
|
||||
return render_template("packages/release_new.html", package=package, form=form)
|
||||
return render_template("packages/release_new.html", package=package, form=form, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/releases/<id>/download/")
|
||||
|
@ -158,8 +148,6 @@ def edit_release(package, id):
|
|||
if form.validate_on_submit():
|
||||
if canEdit:
|
||||
release.title = form["title"].data
|
||||
release.min_rel = form["min_rel"].data.getActual()
|
||||
release.max_rel = form["max_rel"].data.getActual()
|
||||
|
||||
if package.checkPerm(current_user, Permission.CHANGE_RELEASE_URL):
|
||||
release.url = form["url"].data
|
||||
|
@ -175,19 +163,19 @@ def edit_release(package, id):
|
|||
db.session.commit()
|
||||
return redirect(package.getURL("packages.list_releases"))
|
||||
|
||||
return render_template("packages/release_edit.html", package=package, release=release, form=form)
|
||||
return render_template("packages/release_edit.html", package=package, release=release, form=form, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
|
||||
class BulkReleaseForm(FlaskForm):
|
||||
set_min = BooleanField(lazy_gettext("Set Min"))
|
||||
min_rel = QuerySelectField(lazy_gettext("Minimum Minetest Version"), [InputRequired()],
|
||||
query_factory=lambda: get_mt_releases(False), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
set_max = BooleanField(lazy_gettext("Set Max"))
|
||||
max_rel = QuerySelectField(lazy_gettext("Maximum Minetest Version"), [InputRequired()],
|
||||
query_factory=lambda: get_mt_releases(True), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
only_change_none = BooleanField(lazy_gettext("Only change values previously set as none"))
|
||||
submit = SubmitField(lazy_gettext("Update"))
|
||||
# class BulkReleaseForm(FlaskForm):
|
||||
# set_min = BooleanField(lazy_gettext("Set Min"))
|
||||
# min_rel = QuerySelectField(lazy_gettext("Minimum Minetest Version"), [InputRequired()],
|
||||
# query_factory=lambda: get_mt_releases(False), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
# set_max = BooleanField(lazy_gettext("Set Max"))
|
||||
# max_rel = QuerySelectField(lazy_gettext("Maximum Minetest Version"), [InputRequired()],
|
||||
# query_factory=lambda: get_mt_releases(True), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
# only_change_none = BooleanField(lazy_gettext("Only change values previously set as none"))
|
||||
# submit = SubmitField(lazy_gettext("Update"))
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/releases/bulk_change/", methods=["GET", "POST"])
|
||||
|
@ -205,17 +193,17 @@ def bulk_change_release(package):
|
|||
elif form.validate_on_submit():
|
||||
only_change_none = form.only_change_none.data
|
||||
|
||||
for release in package.releases.all():
|
||||
if form["set_min"].data and (not only_change_none or release.min_rel is None):
|
||||
release.min_rel = form["min_rel"].data.getActual()
|
||||
if form["set_max"].data and (not only_change_none or release.max_rel is None):
|
||||
release.max_rel = form["max_rel"].data.getActual()
|
||||
# for release in package.releases.all():
|
||||
# if form["set_min"].data and (not only_change_none or release.min_rel is None):
|
||||
# release.min_rel = form["min_rel"].data.getActual()
|
||||
# if form["set_max"].data and (not only_change_none or release.max_rel is None):
|
||||
# release.max_rel = form["max_rel"].data.getActual()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return redirect(package.getURL("packages.list_releases"))
|
||||
|
||||
return render_template("packages/release_bulk_change.html", package=package, form=form)
|
||||
return render_template("packages/release_bulk_change.html", package=package, form=form, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/releases/<id>/delete/", methods=["POST"])
|
||||
|
@ -313,7 +301,7 @@ def update_config(package):
|
|||
|
||||
return redirect(package.getURL("packages.list_releases"))
|
||||
|
||||
return render_template("packages/update_config.html", package=package, form=form)
|
||||
return render_template("packages/update_config.html", package=package, form=form, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/setup-releases/")
|
||||
|
@ -326,7 +314,7 @@ def setup_releases(package):
|
|||
if package.update_config:
|
||||
return redirect(package.getURL("packages.update_config"))
|
||||
|
||||
return render_template("packages/release_wizard.html", package=package)
|
||||
return render_template("packages/release_wizard.html", package=package, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/user/update-configs/")
|
||||
|
@ -355,4 +343,4 @@ def bulk_update_config(username=None):
|
|||
Package.update_config.has()) \
|
||||
.order_by(db.asc(Package.title)).all()
|
||||
|
||||
return render_template("packages/bulk_update_conf.html", user=user, confs=confs, form=form)
|
||||
return render_template("packages/bulk_update_conf.html", user=user, confs=confs, form=form, toplevel=get_toplevel_tags())
|
||||
|
|
|
@ -26,7 +26,7 @@ from wtforms import *
|
|||
from wtforms.validators import *
|
||||
from app.models import db, PackageReview, Thread, ThreadReply, NotificationType, PackageReviewVote, Package, UserRank, \
|
||||
Permission, AuditSeverity
|
||||
from app.utils import is_package_page, addNotification, get_int_or_abort, isYes, is_safe_url, rank_required, addAuditLog
|
||||
from app.utils import is_package_page, addNotification, get_int_or_abort, isYes, is_safe_url, rank_required, addAuditLog, get_toplevel_tags
|
||||
from app.tasks.webhooktasks import post_discord_webhook
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ def list_reviews():
|
|||
num = min(40, get_int_or_abort(request.args.get("n"), 100))
|
||||
|
||||
pagination = PackageReview.query.order_by(db.desc(PackageReview.created_at)).paginate(page, num, True)
|
||||
return render_template("packages/reviews_list.html", pagination=pagination, reviews=pagination.items)
|
||||
return render_template("packages/reviews_list.html", pagination=pagination, reviews=pagination.items, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
class ReviewForm(FlaskForm):
|
||||
|
@ -123,7 +123,7 @@ def review(package):
|
|||
return redirect(package.getURL("packages.view"))
|
||||
|
||||
return render_template("packages/review_create_edit.html",
|
||||
form=form, package=package, review=review)
|
||||
form=form, package=package, review=review, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/reviews/<reviewer>/delete/", methods=["POST"])
|
||||
|
@ -237,4 +237,4 @@ def review_votes(package):
|
|||
user_biases_info.sort(key=lambda x: -abs(x.balance))
|
||||
|
||||
return render_template("packages/review_votes.html", form=form, package=package, reviews=package.reviews,
|
||||
user_biases=user_biases_info)
|
||||
user_biases=user_biases_info, toplevel=get_toplevel_tags())
|
||||
|
|
|
@ -73,7 +73,7 @@ def screenshots(package):
|
|||
db.session.commit()
|
||||
|
||||
return render_template("packages/screenshots.html", package=package, form=form,
|
||||
tabs=get_package_tabs(current_user, package), current_tab="screenshots")
|
||||
tabs=get_package_tabs(current_user, package), current_tab="screenshots", toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/screenshots/new/", methods=["GET", "POST"])
|
||||
|
@ -92,7 +92,7 @@ def create_screenshot(package):
|
|||
except LogicError as e:
|
||||
flash(e.message, "danger")
|
||||
|
||||
return render_template("packages/screenshot_new.html", package=package, form=form)
|
||||
return render_template("packages/screenshot_new.html", package=package, form=form, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/screenshots/<id>/edit/", methods=["GET", "POST"])
|
||||
|
@ -124,7 +124,7 @@ def edit_screenshot(package, id):
|
|||
db.session.commit()
|
||||
return redirect(package.getURL("packages.screenshots"))
|
||||
|
||||
return render_template("packages/screenshot_edit.html", package=package, screenshot=screenshot, form=form)
|
||||
return render_template("packages/screenshot_edit.html", package=package, screenshot=screenshot, form=form, toplevel=get_toplevel_tags())
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/screenshots/<id>/delete/", methods=["POST"])
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
import base64
|
||||
import string
|
||||
import random
|
||||
from flask import *
|
||||
from flask_babel import gettext, lazy_gettext, get_locale
|
||||
from flask_login import current_user, login_required, logout_user, login_user
|
||||
|
@ -23,6 +25,7 @@ from flask_wtf import FlaskForm
|
|||
from sqlalchemy import or_
|
||||
from wtforms import *
|
||||
from wtforms.validators import *
|
||||
from captcha.image import ImageCaptcha
|
||||
|
||||
from app.models import *
|
||||
from app.tasks.emails import send_verify_email, send_anon_email, send_unsubscribe_verify, send_user_email
|
||||
|
@ -105,13 +108,13 @@ class RegisterForm(FlaskForm):
|
|||
Regexp("^[a-zA-Z0-9._-]+$", message=lazy_gettext("Only a-zA-Z0-9._ allowed"))])
|
||||
email = StringField(lazy_gettext("Email"), [InputRequired(), Email()])
|
||||
password = PasswordField(lazy_gettext("Password"), [InputRequired(), Length(6, 100)])
|
||||
question = StringField(lazy_gettext("What is the result of the above calculation?"), [InputRequired()])
|
||||
question = StringField(lazy_gettext("Type the characters from the image above."), [InputRequired()])
|
||||
agree = BooleanField(lazy_gettext("I agree"), [DataRequired()])
|
||||
submit = SubmitField(lazy_gettext("Register"))
|
||||
|
||||
|
||||
def handle_register(form):
|
||||
if form.question.data.strip().lower() != "19":
|
||||
if form.question.data.strip().lower() != session["captcha-solution"]:
|
||||
flash(gettext("Incorrect captcha answer"), "danger")
|
||||
return
|
||||
|
||||
|
@ -181,7 +184,13 @@ def register():
|
|||
if ret:
|
||||
return ret
|
||||
|
||||
return render_template("users/register.html", form=form,
|
||||
image = ImageCaptcha()
|
||||
characters = string.ascii_letters + string.digits
|
||||
solution = ''.join(random.choice(characters) for i in range(4))
|
||||
data = image.generate(solution)
|
||||
session["captcha-solution"] = solution
|
||||
b64data = "data:image/png;base64," + base64.b64encode(data.read()).decode("utf-8")
|
||||
return render_template("users/register.html", form=form, captcha=b64data,
|
||||
suggested_password=genphrase(entropy=52, wordset="bip39"))
|
||||
|
||||
|
||||
|
|
|
@ -151,24 +151,17 @@ def get_user_medals(user: User) -> Tuple[List[Medal], List[Medal]]:
|
|||
.filter(text("rank <= 30")) \
|
||||
.all()
|
||||
|
||||
user_package_ranks = next(
|
||||
(x for x in user_package_ranks if x[0] == PackageType.MOD or x[2] <= 10),
|
||||
None)
|
||||
user_package_ranks = next((x for x in user_package_ranks if x[2] <= 10), None)
|
||||
if user_package_ranks:
|
||||
top_rank = user_package_ranks[2]
|
||||
top_type = PackageType.coerce(user_package_ranks[0])
|
||||
# top_type = PackageType.coerce(user_package_ranks[0])
|
||||
if top_rank == 1:
|
||||
title = gettext(u"Top %(type)s", type=top_type.text.lower())
|
||||
title = gettext(u"Top projects", type=top_type.text.lower())
|
||||
else:
|
||||
title = gettext(u"Top %(group)d %(type)s", group=top_rank, type=top_type.text.lower())
|
||||
if top_type == PackageType.MOD:
|
||||
icon = "fa-box"
|
||||
elif top_type == PackageType.GAME:
|
||||
icon = "fa-gamepad"
|
||||
else:
|
||||
icon = "fa-paint-brush"
|
||||
title = gettext(u"Top %(group)d projects", group=top_rank, type=top_type.text.lower())
|
||||
icon = "fa-paint-brush"
|
||||
|
||||
description = gettext(u"%(display_name)s has a %(type)s placed at #%(place)d.",
|
||||
description = gettext(u"%(display_name)s has a projects placed at #%(place)d.",
|
||||
display_name=user.display_name, type=top_type.text.lower(), place=top_rank)
|
||||
unlocked.append(
|
||||
Medal.make_unlocked(place_to_color(top_rank), icon, title, description))
|
||||
|
|
|
@ -1,37 +1,72 @@
|
|||
import string
|
||||
import random
|
||||
from .models import *
|
||||
from .utils import make_flask_login_password
|
||||
|
||||
def generate_password():
|
||||
characters = string.ascii_letters + string.digits + string.punctuation
|
||||
password = ''.join(random.choice(characters) for i in range(16))
|
||||
return password
|
||||
|
||||
def populate(session):
|
||||
admin_user = User("rubenwardy")
|
||||
admin_user = User("libregaming")
|
||||
admin_user.is_active = True
|
||||
admin_user.password = make_flask_login_password("tuckfrump")
|
||||
admin_user.github_username = "rubenwardy"
|
||||
admin_user.forums_username = "rubenwardy"
|
||||
password = generate_password()
|
||||
admin_user.password = make_flask_login_password(password)
|
||||
admin_user.github_username = "libregaming"
|
||||
admin_user.forums_username = "libregaming"
|
||||
admin_user.rank = UserRank.ADMIN
|
||||
session.add(admin_user)
|
||||
print("#####################################")
|
||||
print("Admin user : libregaming")
|
||||
print("Admin password: " + password)
|
||||
print("#####################################")
|
||||
|
||||
system_user = User("ContentDB", active=False)
|
||||
system_user.email_confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000)
|
||||
system_user.rank = UserRank.BOT
|
||||
session.add(system_user)
|
||||
|
||||
session.add(MinetestRelease("None", 0))
|
||||
session.add(MinetestRelease("0.4.16/17", 32))
|
||||
session.add(MinetestRelease("5.0", 37))
|
||||
session.add(MinetestRelease("5.1", 38))
|
||||
session.add(MinetestRelease("5.2", 39))
|
||||
session.add(MinetestRelease("5.3", 39))
|
||||
appstream_user = User("AppStreamBot", active=False)
|
||||
appstream_user.email_confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000)
|
||||
appstream_user.rank = UserRank.BOT
|
||||
session.add(appstream_user)
|
||||
|
||||
featured = Tag("featured")
|
||||
featured.is_protected = True
|
||||
|
||||
# These tags replace "package types"
|
||||
game_tag = Tag("Games")
|
||||
game_tag.is_toplevel = True
|
||||
tool_tag = Tag("Tools")
|
||||
tool_tag.is_toplevel = True
|
||||
mod_tag = Tag("Mods")
|
||||
mod_tag.is_toplevel = True
|
||||
session.add(featured)
|
||||
session.add(game_tag)
|
||||
session.add(tool_tag)
|
||||
session.add(mod_tag)
|
||||
|
||||
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"]:
|
||||
for tag in [
|
||||
"Action",
|
||||
"Adventure",
|
||||
"Arcade",
|
||||
"Board",
|
||||
"Blocks",
|
||||
"Card",
|
||||
"Kids",
|
||||
"Logic",
|
||||
"RolePlaying",
|
||||
"Shooter",
|
||||
"Simulation",
|
||||
"Sports",
|
||||
"Strategy"
|
||||
]:
|
||||
row = Tag(tag)
|
||||
tags[row.name] = row
|
||||
session.add(row)
|
||||
|
||||
tags["featured"] = featured
|
||||
licenses = {}
|
||||
for license in ["GPLv2.1", "GPLv3", "LGPLv2.1", "LGPLv3", "AGPLv2.1", "AGPLv3",
|
||||
"Apache", "BSD 3-Clause", "BSD 2-Clause", "CC0", "CC-BY-SA",
|
||||
|
@ -50,9 +85,9 @@ def populate_test_data(session):
|
|||
licenses = { x.name : x for x in License.query.all() }
|
||||
tags = { x.name : x for x in Tag.query.all() }
|
||||
admin_user = User.query.filter_by(rank=UserRank.ADMIN).first()
|
||||
v4 = MinetestRelease.query.filter_by(protocol=32).first()
|
||||
v50 = MinetestRelease.query.filter_by(protocol=37).first()
|
||||
v51 = MinetestRelease.query.filter_by(protocol=38).first()
|
||||
# v4 = MinetestRelease.query.filter_by(protocol=32).first()
|
||||
# v50 = MinetestRelease.query.filter_by(protocol=37).first()
|
||||
# v51 = MinetestRelease.query.filter_by(protocol=38).first()
|
||||
|
||||
ez = User("Shara")
|
||||
ez.github_username = "Ezhh"
|
||||
|
@ -60,7 +95,7 @@ def populate_test_data(session):
|
|||
ez.rank = UserRank.EDITOR
|
||||
session.add(ez)
|
||||
|
||||
not1 = Notification(admin_user, ez, NotificationType.PACKAGE_APPROVAL, "Awards approved", "/packages/rubenwardy/awards/")
|
||||
not1 = Notification(admin_user, ez, NotificationType.PACKAGE_APPROVAL, "Awards approved", "/packages/libregaming/awards/")
|
||||
session.add(not1)
|
||||
|
||||
jeija = User("Jeija")
|
||||
|
@ -69,25 +104,25 @@ def populate_test_data(session):
|
|||
session.add(jeija)
|
||||
|
||||
|
||||
mod = Package()
|
||||
mod.state = PackageState.APPROVED
|
||||
mod.name = "alpha"
|
||||
mod.title = "Alpha Test"
|
||||
mod.license = licenses["MIT"]
|
||||
mod.media_license = licenses["MIT"]
|
||||
mod.type = PackageType.MOD
|
||||
mod.author = admin_user
|
||||
mod.tags.append(tags["mapgen"])
|
||||
mod.tags.append(tags["environment"])
|
||||
mod.repo = "https://github.com/ezhh/other_worlds"
|
||||
mod.issueTracker = "https://github.com/ezhh/other_worlds/issues"
|
||||
mod.forums = 16015
|
||||
mod.short_desc = "The content library should not be used yet as it is still in alpha"
|
||||
mod.desc = "This is the long desc"
|
||||
session.add(mod)
|
||||
tool = Package()
|
||||
tool.state = PackageState.APPROVED
|
||||
tool.name = "alpha"
|
||||
tool.title = "Alpha Test"
|
||||
tool.license = licenses["MIT"]
|
||||
tool.media_license = licenses["MIT"]
|
||||
# tool.type = PackageType.TOOL
|
||||
tool.author = admin_user
|
||||
tool.tags.append(tags["mapgen"])
|
||||
tool.tags.append(tags["environment"])
|
||||
tool.repo = "https://github.com/ezhh/other_worlds"
|
||||
tool.issueTracker = "https://github.com/ezhh/other_worlds/issues"
|
||||
tool.forums = 16015
|
||||
tool.short_desc = "The content library should not be used yet as it is still in alpha"
|
||||
tool.desc = "This is the long desc"
|
||||
session.add(tool)
|
||||
|
||||
rel = PackageRelease()
|
||||
rel.package = mod
|
||||
rel.package = tool
|
||||
rel.title = "v1.0.0"
|
||||
rel.url = "https://github.com/ezhh/handholds/archive/master.zip"
|
||||
rel.approved = True
|
||||
|
@ -99,11 +134,11 @@ def populate_test_data(session):
|
|||
mod1.title = "Awards"
|
||||
mod1.license = licenses["LGPLv2.1"]
|
||||
mod1.media_license = licenses["MIT"]
|
||||
mod1.type = PackageType.MOD
|
||||
# mod1.type = PackageType.TOOL
|
||||
mod1.author = admin_user
|
||||
mod1.tags.append(tags["player_effects"])
|
||||
mod1.repo = "https://github.com/rubenwardy/awards"
|
||||
mod1.issueTracker = "https://github.com/rubenwardy/awards/issues"
|
||||
mod1.repo = "https://github.com/libregaming/awards"
|
||||
mod1.issueTracker = "https://github.com/libregaming/awards/issues"
|
||||
mod1.forums = 4870
|
||||
mod1.short_desc = "Adds achievements and an API to register new ones."
|
||||
mod1.desc = """
|
||||
|
@ -126,7 +161,7 @@ awards.register_achievement("award_mesefind",{
|
|||
rel.package = mod1
|
||||
rel.min_rel = v51
|
||||
rel.title = "v1.0.0"
|
||||
rel.url = "https://github.com/rubenwardy/awards/archive/master.zip"
|
||||
rel.url = "https://github.com/libregaming/awards/archive/master.zip"
|
||||
rel.approved = True
|
||||
session.add(rel)
|
||||
|
||||
|
@ -135,7 +170,7 @@ awards.register_achievement("award_mesefind",{
|
|||
mod2.name = "mesecons"
|
||||
mod2.title = "Mesecons"
|
||||
mod2.tags.append(tags["tools"])
|
||||
mod2.type = PackageType.MOD
|
||||
# mod2.type = PackageType.TOOL
|
||||
mod2.license = licenses["LGPLv3"]
|
||||
mod2.media_license = licenses["MIT"]
|
||||
mod2.author = jeija
|
||||
|
@ -150,7 +185,7 @@ Mezzee-what?
|
|||
------------
|
||||
[Mesecons](http://mesecons.net/)! They're yellow, they're conductive, and they'll add a whole new dimension to Minetest's gameplay.
|
||||
|
||||
Mesecons is a mod for [Minetest](http://minetest.net/) that implements a ton of items related to digital circuitry, such as wires, buttons, lights, and even programmable controllers. Among other things, there are also pistons, solar panels, pressure plates, and note blocks.
|
||||
Mesecons is a tool for [Minetest](http://minetest.net/) that implements a ton of items related to digital circuitry, such as wires, buttons, lights, and even programmable controllers. Among other things, there are also pistons, solar panels, pressure plates, and note blocks.
|
||||
|
||||
Mesecons has a similar goal to Redstone in Minecraft, but works in its own way, with different rules and mechanics.
|
||||
|
||||
|
@ -160,14 +195,14 @@ Go get it!
|
|||
|
||||
[DOWNLOAD IT NOW](https://github.com/minetest-mods/mesecons/archive/master.zip)
|
||||
|
||||
Now go ahead and install it like any other Minetest mod. Don't know how? Check out [the wonderful page about it](http://wiki.minetest.com/wiki/Mods) over at the Minetest Wiki. For your convenience, here's a quick summary:
|
||||
Now go ahead and install it like any other Minetest tool. Don't know how? Check out [the wonderful page about it](http://wiki.minetest.com/wiki/Tools) over at the Minetest Wiki. For your convenience, here's a quick summary:
|
||||
|
||||
1. If Mesecons is still in a ZIP file, extract the folder inside to somewhere on the computer.
|
||||
2. Make sure that when you open the folder, you can directly find `README.md` in the listing. If you just see another folder, move that folder up one level and delete the old one.
|
||||
3. Open up the Minetest mods folder - usually `/mods/`. If you see the `minetest` or folder inside of that, that is your mod folder instead.
|
||||
3. Open up the Minetest mods folder - usually `/mods/`. If you see the `minetest` or folder inside of that, that is your tool folder instead.
|
||||
4. Copy the Mesecons folder into the mods folder.
|
||||
|
||||
Don't like some parts of Mesecons? Open up the Mesecons folder and delete the subfolder containing the mod you don't want. If you didn't want movestones, for example, all you have to do is delete the `mesecons_movestones` folder and they will no longer be available.
|
||||
Don't like some parts of Mesecons? Open up the Mesecons folder and delete the subfolder containing the tool you don't want. If you didn't want movestones, for example, all you have to do is delete the `mesecons_movestones` folder and they will no longer be available.
|
||||
|
||||
There are no dependencies - it will work right after installing!
|
||||
|
||||
|
@ -219,97 +254,97 @@ No warranty is provided, express or implied, for any part of the project.
|
|||
session.add(mod1)
|
||||
session.add(mod2)
|
||||
|
||||
mod = Package()
|
||||
mod.state = PackageState.APPROVED
|
||||
mod.name = "handholds"
|
||||
mod.title = "Handholds"
|
||||
mod.license = licenses["MIT"]
|
||||
mod.media_license = licenses["MIT"]
|
||||
mod.type = PackageType.MOD
|
||||
mod.author = ez
|
||||
mod.tags.append(tags["player_effects"])
|
||||
mod.repo = "https://github.com/ezhh/handholds"
|
||||
mod.issueTracker = "https://github.com/ezhh/handholds/issues"
|
||||
mod.forums = 17069
|
||||
mod.short_desc = "Adds hand holds and climbing thingies"
|
||||
mod.desc = "This is the long desc"
|
||||
session.add(mod)
|
||||
tool = Package()
|
||||
tool.state = PackageState.APPROVED
|
||||
tool.name = "handholds"
|
||||
tool.title = "Handholds"
|
||||
tool.license = licenses["MIT"]
|
||||
tool.media_license = licenses["MIT"]
|
||||
# tool.type = PackageType.TOOL
|
||||
tool.author = ez
|
||||
tool.tags.append(tags["player_effects"])
|
||||
tool.repo = "https://github.com/ezhh/handholds"
|
||||
tool.issueTracker = "https://github.com/ezhh/handholds/issues"
|
||||
tool.forums = 17069
|
||||
tool.short_desc = "Adds hand holds and climbing thingies"
|
||||
tool.desc = "This is the long desc"
|
||||
session.add(tool)
|
||||
|
||||
rel = PackageRelease()
|
||||
rel.package = mod
|
||||
rel.package = tool
|
||||
rel.title = "v1.0.0"
|
||||
rel.max_rel = v4
|
||||
rel.url = "https://github.com/ezhh/handholds/archive/master.zip"
|
||||
rel.approved = True
|
||||
session.add(rel)
|
||||
|
||||
mod = Package()
|
||||
mod.state = PackageState.APPROVED
|
||||
mod.name = "other_worlds"
|
||||
mod.title = "Other Worlds"
|
||||
mod.license = licenses["MIT"]
|
||||
mod.media_license = licenses["MIT"]
|
||||
mod.type = PackageType.MOD
|
||||
mod.author = ez
|
||||
mod.tags.append(tags["mapgen"])
|
||||
mod.tags.append(tags["environment"])
|
||||
mod.repo = "https://github.com/ezhh/other_worlds"
|
||||
mod.issueTracker = "https://github.com/ezhh/other_worlds/issues"
|
||||
mod.forums = 16015
|
||||
mod.short_desc = "Adds space with asteroids and comets"
|
||||
mod.desc = "This is the long desc"
|
||||
session.add(mod)
|
||||
tool = Package()
|
||||
tool.state = PackageState.APPROVED
|
||||
tool.name = "other_worlds"
|
||||
tool.title = "Other Worlds"
|
||||
tool.license = licenses["MIT"]
|
||||
tool.media_license = licenses["MIT"]
|
||||
# tool.type = PackageType.TOOL
|
||||
tool.author = ez
|
||||
tool.tags.append(tags["mapgen"])
|
||||
tool.tags.append(tags["environment"])
|
||||
tool.repo = "https://github.com/ezhh/other_worlds"
|
||||
tool.issueTracker = "https://github.com/ezhh/other_worlds/issues"
|
||||
tool.forums = 16015
|
||||
tool.short_desc = "Adds space with asteroids and comets"
|
||||
tool.desc = "This is the long desc"
|
||||
session.add(tool)
|
||||
|
||||
mod = Package()
|
||||
mod.state = PackageState.APPROVED
|
||||
mod.name = "food"
|
||||
mod.title = "Food"
|
||||
mod.license = licenses["LGPLv2.1"]
|
||||
mod.media_license = licenses["MIT"]
|
||||
mod.type = PackageType.MOD
|
||||
mod.author = admin_user
|
||||
mod.tags.append(tags["player_effects"])
|
||||
mod.repo = "https://github.com/rubenwardy/food/"
|
||||
mod.issueTracker = "https://github.com/rubenwardy/food/issues/"
|
||||
mod.forums = 2960
|
||||
mod.short_desc = "Adds lots of food and an API to manage ingredients"
|
||||
mod.desc = "This is the long desc"
|
||||
session.add(mod)
|
||||
tool = Package()
|
||||
tool.state = PackageState.APPROVED
|
||||
tool.name = "food"
|
||||
tool.title = "Food"
|
||||
tool.license = licenses["LGPLv2.1"]
|
||||
tool.media_license = licenses["MIT"]
|
||||
# tool.type = PackageType.TOOL
|
||||
tool.author = admin_user
|
||||
tool.tags.append(tags["player_effects"])
|
||||
tool.repo = "https://github.com/libregaming/food/"
|
||||
tool.issueTracker = "https://github.com/libregaming/food/issues/"
|
||||
tool.forums = 2960
|
||||
tool.short_desc = "Adds lots of food and an API to manage ingredients"
|
||||
tool.desc = "This is the long desc"
|
||||
session.add(tool)
|
||||
|
||||
mod = Package()
|
||||
mod.state = PackageState.APPROVED
|
||||
mod.name = "food_sweet"
|
||||
mod.title = "Sweet Foods"
|
||||
mod.license = licenses["CC0"]
|
||||
mod.media_license = licenses["MIT"]
|
||||
mod.type = PackageType.MOD
|
||||
mod.author = admin_user
|
||||
mod.tags.append(tags["player_effects"])
|
||||
mod.repo = "https://github.com/rubenwardy/food_sweet/"
|
||||
mod.issueTracker = "https://github.com/rubenwardy/food_sweet/issues/"
|
||||
mod.forums = 9039
|
||||
mod.short_desc = "Adds sweet food"
|
||||
mod.desc = "This is the long desc"
|
||||
food_sweet = mod
|
||||
session.add(mod)
|
||||
tool = Package()
|
||||
tool.state = PackageState.APPROVED
|
||||
tool.name = "food_sweet"
|
||||
tool.title = "Sweet Foods"
|
||||
tool.license = licenses["CC0"]
|
||||
tool.media_license = licenses["MIT"]
|
||||
# tool.type = PackageType.TOOL
|
||||
tool.author = admin_user
|
||||
tool.tags.append(tags["player_effects"])
|
||||
tool.repo = "https://github.com/libregaming/food_sweet/"
|
||||
tool.issueTracker = "https://github.com/libregaming/food_sweet/issues/"
|
||||
tool.forums = 9039
|
||||
tool.short_desc = "Adds sweet food"
|
||||
tool.desc = "This is the long desc"
|
||||
food_sweet = tool
|
||||
session.add(tool)
|
||||
|
||||
game1 = Package()
|
||||
game1.state = PackageState.APPROVED
|
||||
game1.name = "capturetheflag"
|
||||
game1.title = "Capture The Flag"
|
||||
game1.type = PackageType.GAME
|
||||
# game1.type = PackageType.GAME
|
||||
game1.license = licenses["LGPLv2.1"]
|
||||
game1.media_license = licenses["MIT"]
|
||||
game1.author = admin_user
|
||||
game1.tags.append(tags["pvp"])
|
||||
game1.tags.append(tags["survival"])
|
||||
game1.tags.append(tags["multiplayer"])
|
||||
game1.repo = "https://github.com/rubenwardy/capturetheflag"
|
||||
game1.issueTracker = "https://github.com/rubenwardy/capturetheflag/issues"
|
||||
game1.repo = "https://github.com/libregaming/capturetheflag"
|
||||
game1.issueTracker = "https://github.com/libregaming/capturetheflag/issues"
|
||||
game1.forums = 12835
|
||||
game1.short_desc = "Two teams battle to snatch and return the enemy's flag, before the enemy takes their own!"
|
||||
game1.desc = """
|
||||
As seen on the Capture the Flag server (minetest.rubenwardy.com:30000)
|
||||
As seen on the Capture the Flag server (minetest.libregaming.com:30000)
|
||||
|
||||
` `[`javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"/+/onmouseover=1/+/`](javascript:/*--%3E%3C/title%3E%3C/style%3E%3C/textarea%3E%3C/script%3E%3C/xmp%3E%3Csvg/onload='+/%22/+/onmouseover=1/+/)`[*/[]/+alert(1)//'>`
|
||||
|
||||
|
@ -351,26 +386,26 @@ Uses the CTF PvP Engine.
|
|||
rel = PackageRelease()
|
||||
rel.package = game1
|
||||
rel.title = "v1.0.0"
|
||||
rel.url = "https://github.com/rubenwardy/capturetheflag/archive/master.zip"
|
||||
rel.url = "https://github.com/libregaming/capturetheflag/archive/master.zip"
|
||||
rel.approved = True
|
||||
session.add(rel)
|
||||
|
||||
|
||||
mod = Package()
|
||||
mod.state = PackageState.APPROVED
|
||||
mod.name = "pixelbox"
|
||||
mod.title = "PixelBOX Reloaded"
|
||||
mod.license = licenses["CC0"]
|
||||
mod.media_license = licenses["MIT"]
|
||||
mod.type = PackageType.TXP
|
||||
mod.author = admin_user
|
||||
mod.forums = 14132
|
||||
mod.short_desc = "This is an update of the original PixelBOX texture pack by the brillant artist Gambit"
|
||||
mod.desc = "This is the long desc"
|
||||
session.add(mod)
|
||||
tool = Package()
|
||||
tool.state = PackageState.APPROVED
|
||||
tool.name = "pixelbox"
|
||||
tool.title = "PixelBOX Reloaded"
|
||||
tool.license = licenses["CC0"]
|
||||
tool.media_license = licenses["MIT"]
|
||||
# tool.type = PackageType.ASSETPACK
|
||||
tool.author = admin_user
|
||||
tool.forums = 14132
|
||||
tool.short_desc = "This is an update of the original PixelBOX texture pack by the brillant artist Gambit"
|
||||
tool.desc = "This is the long desc"
|
||||
session.add(tool)
|
||||
|
||||
rel = PackageRelease()
|
||||
rel.package = mod
|
||||
rel.package = tool
|
||||
rel.title = "v1.0.0"
|
||||
rel.url = "http://mamadou3.free.fr/Minetest/PixelBOX.zip"
|
||||
rel.approved = True
|
||||
|
@ -378,16 +413,16 @@ Uses the CTF PvP Engine.
|
|||
|
||||
session.commit()
|
||||
|
||||
metas = {}
|
||||
for package in Package.query.filter_by(type=PackageType.MOD).all():
|
||||
meta = None
|
||||
try:
|
||||
meta = metas[package.name]
|
||||
except KeyError:
|
||||
meta = MetaPackage(package.name)
|
||||
session.add(meta)
|
||||
metas[package.name] = meta
|
||||
package.provides.append(meta)
|
||||
# metas = {}
|
||||
# for package in Package.query.filter_by(type=PackageType.TOOL).all():
|
||||
# meta = None
|
||||
# try:
|
||||
# meta = metas[package.name]
|
||||
# except KeyError:
|
||||
# meta = MetaPackage(package.name)
|
||||
# session.add(meta)
|
||||
# metas[package.name] = meta
|
||||
# package.provides.append(meta)
|
||||
|
||||
dep = Dependency(food_sweet, meta=metas["food"])
|
||||
session.add(dep)
|
||||
# dep = Dependency(food_sweet, meta=metas["food"])
|
||||
# session.add(dep)
|
||||
|
|
|
@ -74,7 +74,7 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
|||
* PUT `/api/packages/<author>/<name>/` (Update)
|
||||
* Requires authentication.
|
||||
* JSON dictionary with any of these keys (all are optional, null to delete Nullables):
|
||||
* `type`: One of `GAME`, `MOD`, `TXP`.
|
||||
* `type`: One of `GAME`, `TOOL`, `ASSETPACK`.
|
||||
* `title`: Human-readable title.
|
||||
* `name`: Technical name (needs permission if already approved).
|
||||
* `short_description`
|
||||
|
@ -99,10 +99,10 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
|||
* Supports [Package Queries](#package-queries)
|
||||
* [Paginated result](#paginated-results), max 300 results per page
|
||||
* Each item in `items` will be a dictionary with the following keys:
|
||||
* `type`: One of `GAME`, `MOD`, `TXP`.
|
||||
* `type`: One of `GAME`, `TOOL`, `ASSETPACK`.
|
||||
* `author`: Username of the package author.
|
||||
* `name`: Package name.
|
||||
* `provides`: List of technical mod names inside the package.
|
||||
* `provides`: List of technical tool names inside the package.
|
||||
* `depends`: List of hard dependencies.
|
||||
* Each dep will either be a metapackage dependency (`name`), or a
|
||||
package dependency (`author/name`).
|
||||
|
@ -134,11 +134,11 @@ curl -X PUT https://content.minetest.net/api/packages/username/name/ \
|
|||
|
||||
Example:
|
||||
|
||||
/api/packages/?type=mod&type=game&q=mobs+fun&hide=nonfree&hide=gore
|
||||
/api/packages/?type=tool&type=game&q=mobs+fun&hide=nonfree&hide=gore
|
||||
|
||||
Supported query parameters:
|
||||
|
||||
* `type`: Package types (`mod`, `game`, `txp`).
|
||||
* `type`: Package types (`tool`, `game`, `asset_pack`).
|
||||
* `q`: Query string.
|
||||
* `author`: Filter by author.
|
||||
* `tag`: Filter by tags.
|
||||
|
@ -173,7 +173,7 @@ Supported query parameters:
|
|||
* `package`
|
||||
* `author`: author username
|
||||
* `name`: technical name
|
||||
* `type`: `mod`, `game`, or `txp`
|
||||
* `type`: `tool`, `game`, or `asset_pack`
|
||||
* GET `/api/packages/<username>/<name>/releases/` (List)
|
||||
* Returns array of release dictionaries, see above, but without package info.
|
||||
* GET `/api/packages/<username>/<name>/releases/<id>/` (Read)
|
||||
|
@ -304,7 +304,7 @@ Example:
|
|||
```json
|
||||
[
|
||||
{
|
||||
"comment": "This is a really good mod!",
|
||||
"comment": "This is a really good tool!",
|
||||
"created_at": "2021-11-24T16:18:33.764084",
|
||||
"is_positive": true,
|
||||
"title": "Really good",
|
||||
|
@ -330,12 +330,12 @@ Example:
|
|||
|
||||
Example:
|
||||
|
||||
/api/topics/?q=mobs&type=mod&type=game
|
||||
/api/topics/?q=mobs&type=tool&type=game
|
||||
|
||||
Supported query parameters:
|
||||
|
||||
* `q`: Query string.
|
||||
* `type`: Package types (`mod`, `game`, `txp`).
|
||||
* `type`: Package types (`tool`, `game`, `asset_pack`).
|
||||
* `sort`: Sort by (`name`, `views`, `created_at`).
|
||||
* `show_added`: Show topics that have an existing package.
|
||||
* `show_discarded`: Show topics marked as discarded.
|
||||
|
@ -390,9 +390,8 @@ Supported query parameters:
|
|||
* `downloads`: get number of downloads
|
||||
* `new`: new packages
|
||||
* `updated`: recently updated packages
|
||||
* `pop_mod`: popular mods
|
||||
* `pop_txp`: popular textures
|
||||
* `pop_game`: popular games
|
||||
* `popular`: popular packages
|
||||
* `toplevel`: toplevel nav tags
|
||||
* `high_reviewed`: highest reviewed
|
||||
* GET `/api/welcome/v1/` ([View](/api/welcome/v1/)) - in-menu welcome dialog. Experimental (may change without warning)
|
||||
* `featured`: featured games
|
||||
|
|
|
@ -13,7 +13,7 @@ If you don't, then you can just sign up using an email address and password.
|
|||
|
||||
GitHub can only be used to login, not to register.
|
||||
|
||||
<a class="btn btn-primary" href="/user/claim/">Register</a>
|
||||
<a class="btn btn-primary" href="/user/register/">Register</a>
|
||||
|
||||
|
||||
### My verification email never arrived
|
||||
|
|
|
@ -69,8 +69,8 @@ is available.
|
|||
* MUST: `screenshot.png` is present and up-to-date, with a correct aspect ratio (3:2, at least 300x200).
|
||||
* MUST: Have a high resolution cover image on ContentDB (at least 1280x720 pixels).
|
||||
It may be shown cropped to 16:9 aspect ratio, or shorter.
|
||||
* MUST: mod.conf/game.conf/texture_pack.conf present with:
|
||||
* name (if mod or game)
|
||||
* MUST: tool.conf/game.conf/texture_pack.conf present with:
|
||||
* name (if tool or game)
|
||||
* description
|
||||
* dependencies (if relevant)
|
||||
* `min_minetest_version` and `max_minetest_version` (if relevant)
|
||||
|
|
|
@ -14,7 +14,7 @@ and they will be subject to limited promotion.
|
|||
of packages with non-free licenses.**
|
||||
|
||||
Minetest is free and open source software, and is only as big as it is now
|
||||
because of this. It's pretty amazing you can take nearly any published mod and modify it
|
||||
because of this. It's pretty amazing you can take nearly any published tool and modify it
|
||||
to how you like - add some features, maybe fix some bugs - and then share those
|
||||
modifications without the worry of legal issues. The project, itself, relies on open
|
||||
source contributions to survive - if it were non-free, then it would have died
|
||||
|
|
|
@ -14,8 +14,8 @@ Every type of content can have a `.conf` file that contains the metadata.
|
|||
|
||||
The filename of the `.conf` file depends on the content type:
|
||||
|
||||
* `mod.conf` for mods.
|
||||
* `modpack.conf` for mod packs.
|
||||
* `tool.conf` for mods.
|
||||
* `modpack.conf` for tool packs.
|
||||
* `game.conf` for games.
|
||||
* `texture_pack.conf` for texture packs.
|
||||
|
||||
|
@ -36,7 +36,7 @@ ContentDB understands the following information:
|
|||
|
||||
and for mods only:
|
||||
|
||||
* `name` - the mod technical name.
|
||||
* `name` - the tool technical name.
|
||||
|
||||
|
||||
## .cdb.json
|
||||
|
@ -46,7 +46,7 @@ to update the package meta.
|
|||
|
||||
It should be a JSON dictionary with one or more of the following optional keys:
|
||||
|
||||
* `type`: One of `GAME`, `MOD`, `TXP`.
|
||||
* `type`: One of `GAME`, `TOOL`, `ASSETPACK`.
|
||||
* `title`: Human-readable title.
|
||||
* `name`: Technical name (needs permission if already approved).
|
||||
* `short_description`
|
||||
|
|
|
@ -3,7 +3,7 @@ title: Top Packages Algorithm
|
|||
## Package Score
|
||||
|
||||
Each package is given a `score`, which is used when ordering them in the
|
||||
"Top Games/Mods/Texture Packs" lists. The intention of this feature is
|
||||
"Top Games/Tools/Asset Packs" lists. The intention of this feature is
|
||||
to make it easier for new users to find good packages.
|
||||
|
||||
A package's score is equal to a rolling average of recent downloads,
|
||||
|
|
|
@ -8,7 +8,7 @@ the listings and to combat abuse.
|
|||
|
||||
* **No inappropriate content.** <sup>2.1</sup>
|
||||
* **Content must be playable/useful, but not necessarily finished.** <sup>2.2</sup>
|
||||
* **Don't use the name of another mod unless your mod is a fork or reimplementation.** <sup>3</sup>
|
||||
* **Don't use the name of another tool unless your tool is a fork or reimplementation.** <sup>3</sup>
|
||||
* **Licenses must allow derivatives, redistribution, and must not discriminate.** <sup>4</sup>
|
||||
* **Don't put promotions or advertisements in any package metadata.** <sup>5</sup>
|
||||
* **The ContentDB admin reserves the right to remove packages for any reason**,
|
||||
|
@ -51,14 +51,14 @@ as this will help advise players.
|
|||
|
||||
Adding non-player facing mods, such as libraries and server tools, is perfectly fine
|
||||
and encouraged. ContentDB isn't just for player-facing things, and adding
|
||||
libraries allows them to be installed when a mod depends on it.
|
||||
libraries allows them to be installed when a tool depends on it.
|
||||
|
||||
|
||||
## 3. Technical Names
|
||||
|
||||
### 3.1 Right to a name
|
||||
|
||||
A package uses a name when it has that name or contains a mod that uses that name.
|
||||
A package uses a name when it has that name or contains a tool that uses that name.
|
||||
|
||||
The first package to use a name based on the creation of its forum topic or
|
||||
ContentDB submission has the right to the technical name. The use of a package
|
||||
|
@ -74,14 +74,14 @@ to change the name of the package, or your package won't be accepted.
|
|||
|
||||
We reserve the right to issue exceptions for this where we feel necessary.
|
||||
|
||||
### 3.2. Mod Forks and Reimplementations
|
||||
### 3.2. Tool Forks and Reimplementations
|
||||
|
||||
An exception to the above is that mods are allowed to have the same name as a
|
||||
mod if it's a fork of that mod (or a close reimplementation). In real terms, it
|
||||
should be possible to use the new mod as a drop-in replacement.
|
||||
tool if it's a fork of that tool (or a close reimplementation). In real terms, it
|
||||
should be possible to use the new tool as a drop-in replacement.
|
||||
|
||||
We reserve the right to decide whether a mod counts as a fork or
|
||||
reimplementation of the mod that owns the name.
|
||||
We reserve the right to decide whether a tool counts as a fork or
|
||||
reimplementation of the tool that owns the name.
|
||||
|
||||
|
||||
## 4. Licenses
|
||||
|
|
|
@ -83,9 +83,9 @@ Please [raise a report](https://content.minetest.net/report/?anon=0) if you
|
|||
wish to remove your personal information.
|
||||
|
||||
ContentDB keeps a record of each username and forum topic on the forums,
|
||||
for use in indexing mod/game topics. ContentDB also requires the use of a username
|
||||
for use in indexing tool/game topics. ContentDB also requires the use of a username
|
||||
to uniquely identify a package. Therefore, an author cannot be removed completely
|
||||
from ContentDB if they have any packages or mod/game topics on the forum.
|
||||
from ContentDB if they have any packages or tool/game topics on the forum.
|
||||
|
||||
If we are unable to remove your account for one of the above reasons, your user
|
||||
account will instead be wiped and deactivated, ending up exactly like an author
|
||||
|
|
|
@ -20,7 +20,7 @@ import sys
|
|||
from typing import List, Dict, Optional, Iterator, Iterable
|
||||
|
||||
from app.logic.LogicError import LogicError
|
||||
from app.models import Package, MetaPackage, PackageType, PackageState, PackageGameSupport, db
|
||||
from app.models import Package, MetaPackage, PackageState, PackageGameSupport, db
|
||||
|
||||
"""
|
||||
get_game_support(package):
|
||||
|
@ -128,39 +128,36 @@ class GameSupportResolver:
|
|||
history = history.copy()
|
||||
history.append(key)
|
||||
|
||||
if package.type == PackageType.GAME:
|
||||
return PackageSet([package])
|
||||
# if package.type == PackageType.GAME:
|
||||
return PackageSet([package])
|
||||
|
||||
if key in self.resolved_packages:
|
||||
return self.resolved_packages.get(key)
|
||||
# if key in self.resolved_packages:
|
||||
# return self.resolved_packages.get(key)
|
||||
|
||||
if key in self.checked_packages:
|
||||
print(f"Error, cycle found: {','.join(history)}", file=sys.stderr)
|
||||
return PackageSet()
|
||||
# if key in self.checked_packages:
|
||||
# print(f"Error, cycle found: {','.join(history)}", file=sys.stderr)
|
||||
# return PackageSet()
|
||||
|
||||
self.checked_packages.add(key)
|
||||
# self.checked_packages.add(key)
|
||||
|
||||
if package.type != PackageType.MOD:
|
||||
raise LogicError(500, "Got non-mod")
|
||||
# retval = PackageSet()
|
||||
|
||||
retval = PackageSet()
|
||||
# for dep in package.dependencies.filter_by(optional=False).all():
|
||||
# ret = self.resolve_for_meta_package(dep.meta_package, history)
|
||||
# if len(ret) == 0:
|
||||
# continue
|
||||
# elif len(retval) == 0:
|
||||
# retval.update(ret)
|
||||
# else:
|
||||
# retval.intersection_update(ret)
|
||||
# if len(retval) == 0:
|
||||
# raise LogicError(500, f"Detected game support contradiction, {key} may not be compatible with any games")
|
||||
|
||||
for dep in package.dependencies.filter_by(optional=False).all():
|
||||
ret = self.resolve_for_meta_package(dep.meta_package, history)
|
||||
if len(ret) == 0:
|
||||
continue
|
||||
elif len(retval) == 0:
|
||||
retval.update(ret)
|
||||
else:
|
||||
retval.intersection_update(ret)
|
||||
if len(retval) == 0:
|
||||
raise LogicError(500, f"Detected game support contradiction, {key} may not be compatible with any games")
|
||||
|
||||
self.resolved_packages[key] = retval
|
||||
return retval
|
||||
# self.resolved_packages[key] = retval
|
||||
# return retval
|
||||
|
||||
def update_all(self) -> None:
|
||||
for package in Package.query.filter(Package.type == PackageType.MOD, Package.state != PackageState.DELETED).all():
|
||||
for package in Package.query.filter(Package.state != PackageState.DELETED).all():
|
||||
retval = self.resolve(package, [])
|
||||
for game in retval:
|
||||
support = PackageGameSupport(package, game)
|
||||
|
|
|
@ -20,7 +20,7 @@ import validators
|
|||
from flask_babel import lazy_gettext
|
||||
|
||||
from app.logic.LogicError import LogicError
|
||||
from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, \
|
||||
from app.models import User, Package, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, \
|
||||
License, UserRank, PackageDevState
|
||||
from app.utils import addAuditLog
|
||||
from app.utils.url import clean_youtube_url
|
||||
|
@ -41,7 +41,7 @@ def get_license(name):
|
|||
return license
|
||||
|
||||
|
||||
name_re = re.compile("^[a-z0-9_]+$")
|
||||
name_re = re.compile("^[a-zA-Z0-9_\-\.]+$")
|
||||
|
||||
AnyType = "?"
|
||||
ALLOWED_FIELDS = {
|
||||
|
@ -118,8 +118,8 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
|
|||
|
||||
validate(data)
|
||||
|
||||
if "type" in data:
|
||||
data["type"] = PackageType.coerce(data["type"])
|
||||
# if "type" in data:
|
||||
# data["type"] = PackageType.coerce(data["type"])
|
||||
|
||||
if "dev_state" in data:
|
||||
data["dev_state"] = PackageDevState.coerce(data["dev_state"])
|
||||
|
@ -140,12 +140,12 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
|
|||
if key in data:
|
||||
setattr(package, key, data[key])
|
||||
|
||||
if package.type == PackageType.TXP:
|
||||
package.license = package.media_license
|
||||
# if package.type == PackageType.ASSETPACK:
|
||||
# package.license = package.media_license
|
||||
|
||||
if was_new and package.type == PackageType.MOD:
|
||||
m = MetaPackage.GetOrCreate(package.name, {})
|
||||
package.provides.append(m)
|
||||
# if was_new and package.type == PackageType.TOOL:
|
||||
# m = MetaPackage.GetOrCreate(package.name, {})
|
||||
# package.provides.append(m)
|
||||
|
||||
if "tags" in data:
|
||||
old_tags = list(package.tags)
|
||||
|
|
|
@ -22,7 +22,7 @@ from flask_babel import lazy_gettext
|
|||
|
||||
from app.logic.LogicError import LogicError
|
||||
from app.logic.uploads import upload_file
|
||||
from app.models import PackageRelease, db, Permission, User, Package, MinetestRelease
|
||||
from app.models import PackageRelease, db, Permission, User, Package
|
||||
from app.tasks.importtasks import makeVCSRelease, checkZipRelease
|
||||
from app.utils import AuditSeverity, addAuditLog, nonEmptyOrNone
|
||||
|
||||
|
@ -38,7 +38,7 @@ def check_can_create_release(user: User, package: Package):
|
|||
|
||||
|
||||
def do_create_vcs_release(user: User, package: Package, title: str, ref: str,
|
||||
min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason: str = None):
|
||||
min_v = None, max_v = None, reason: str = None):
|
||||
check_can_create_release(user, package)
|
||||
|
||||
rel = PackageRelease()
|
||||
|
@ -46,8 +46,6 @@ def do_create_vcs_release(user: User, package: Package, title: str, ref: str,
|
|||
rel.title = title
|
||||
rel.url = ""
|
||||
rel.task_id = uuid()
|
||||
rel.min_rel = min_v
|
||||
rel.max_rel = max_v
|
||||
db.session.add(rel)
|
||||
|
||||
if reason is None:
|
||||
|
@ -64,7 +62,7 @@ def do_create_vcs_release(user: User, package: Package, title: str, ref: str,
|
|||
|
||||
|
||||
def do_create_zip_release(user: User, package: Package, title: str, file,
|
||||
min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason: str = None,
|
||||
min_v = None, max_v = None, reason: str = None,
|
||||
commit_hash: str = None):
|
||||
check_can_create_release(user, package)
|
||||
|
||||
|
@ -81,8 +79,7 @@ def do_create_zip_release(user: User, package: Package, title: str, file,
|
|||
rel.url = uploaded_url
|
||||
rel.task_id = uuid()
|
||||
rel.commit_hash = commit_hash
|
||||
rel.min_rel = min_v
|
||||
rel.max_rel = max_v
|
||||
|
||||
db.session.add(rel)
|
||||
|
||||
if reason is None:
|
||||
|
|
|
@ -120,7 +120,7 @@ class ForumTopic(db.Model):
|
|||
wip = db.Column(db.Boolean, default=False, nullable=False)
|
||||
discarded = db.Column(db.Boolean, default=False, nullable=False)
|
||||
|
||||
type = db.Column(db.Enum(PackageType), nullable=False)
|
||||
# type = db.Column(db.Enum(PackageType), nullable=False)
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
name = db.Column(db.String(30), nullable=True)
|
||||
link = db.Column(db.String(200), nullable=True)
|
||||
|
|
|
@ -23,7 +23,7 @@ from flask_babel import lazy_gettext
|
|||
from flask_sqlalchemy import BaseQuery
|
||||
from sqlalchemy_searchable import SearchQueryMixin
|
||||
from sqlalchemy_utils.types import TSVectorType
|
||||
|
||||
from app.markdown import render_markdown
|
||||
from . import db
|
||||
from .users import Permission, UserRank, User
|
||||
from .. import app
|
||||
|
@ -48,49 +48,49 @@ class License(db.Model):
|
|||
return self.name
|
||||
|
||||
|
||||
class PackageType(enum.Enum):
|
||||
MOD = "Mod"
|
||||
GAME = "Game"
|
||||
TXP = "Texture Pack"
|
||||
# class PackageType(enum.Enum):
|
||||
# GAME = "Game"
|
||||
# TOOL = "Tool"
|
||||
# ASSETPACK = "Asset Pack"
|
||||
|
||||
def toName(self):
|
||||
return self.name.lower()
|
||||
# def toName(self):
|
||||
# return self.name.lower()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
# def __str__(self):
|
||||
# return self.name
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
if self == PackageType.MOD:
|
||||
return lazy_gettext("Mod")
|
||||
elif self == PackageType.GAME:
|
||||
return lazy_gettext("Game")
|
||||
elif self == PackageType.TXP:
|
||||
return lazy_gettext("Texture Pack")
|
||||
# @property
|
||||
# def text(self):
|
||||
# if self == PackageType.TOOL:
|
||||
# return lazy_gettext("Tool")
|
||||
# elif self == PackageType.GAME:
|
||||
# return lazy_gettext("Game")
|
||||
# elif self == PackageType.ASSETPACK:
|
||||
# return lazy_gettext("Asset Pack")
|
||||
|
||||
@property
|
||||
def plural(self):
|
||||
if self == PackageType.MOD:
|
||||
return lazy_gettext("Mods")
|
||||
elif self == PackageType.GAME:
|
||||
return lazy_gettext("Games")
|
||||
elif self == PackageType.TXP:
|
||||
return lazy_gettext("Texture Packs")
|
||||
# @property
|
||||
# def plural(self):
|
||||
# if self == PackageType.TOOL:
|
||||
# return lazy_gettext("Tools")
|
||||
# elif self == PackageType.GAME:
|
||||
# return lazy_gettext("Games")
|
||||
# elif self == PackageType.ASSETPACK:
|
||||
# return lazy_gettext("Asset Packs")
|
||||
|
||||
@classmethod
|
||||
def get(cls, name):
|
||||
try:
|
||||
return PackageType[name.upper()]
|
||||
except KeyError:
|
||||
return None
|
||||
# @classmethod
|
||||
# def get(cls, name):
|
||||
# try:
|
||||
# return PackageType[name.upper()]
|
||||
# except KeyError:
|
||||
# return None
|
||||
|
||||
@classmethod
|
||||
def choices(cls):
|
||||
return [(choice, choice.text) for choice in cls]
|
||||
# @classmethod
|
||||
# def choices(cls):
|
||||
# return [(choice, choice.text) for choice in cls]
|
||||
|
||||
@classmethod
|
||||
def coerce(cls, item):
|
||||
return item if type(item) == PackageType else PackageType[item.upper()]
|
||||
# @classmethod
|
||||
# def coerce(cls, item):
|
||||
# return item if type(item) == PackageType else PackageType[item.upper()]
|
||||
|
||||
|
||||
class PackageDevState(enum.Enum):
|
||||
|
@ -218,7 +218,7 @@ class PackagePropertyKey(enum.Enum):
|
|||
title = "Title"
|
||||
short_desc = "Short Description"
|
||||
desc = "Description"
|
||||
type = "Type"
|
||||
# type = "Type"
|
||||
license = "License"
|
||||
media_license = "Media License"
|
||||
tags = "Tags"
|
||||
|
@ -226,7 +226,7 @@ class PackagePropertyKey(enum.Enum):
|
|||
repo = "Repository"
|
||||
website = "Website"
|
||||
issueTracker = "Issue Tracker"
|
||||
forums = "Forum Topic ID"
|
||||
# forums = "Forum Topic ID"
|
||||
|
||||
def convert(self, value):
|
||||
if self == PackagePropertyKey.tags:
|
||||
|
@ -376,11 +376,13 @@ class Package(db.Model):
|
|||
title = db.Column(db.Unicode(100), nullable=False)
|
||||
short_desc = db.Column(db.Unicode(200), nullable=False)
|
||||
desc = db.Column(db.UnicodeText, nullable=True)
|
||||
type = db.Column(db.Enum(PackageType), nullable=False)
|
||||
build_desc = db.Column(db.UnicodeText, nullable=True)
|
||||
install_desc = db.Column(db.UnicodeText, nullable=True)
|
||||
# type = db.Column(db.Enum(PackageType), nullable=False)
|
||||
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||
approved_at = db.Column(db.DateTime, nullable=True, default=None)
|
||||
|
||||
name_valid = db.CheckConstraint("name ~* '^[a-z0-9_]+$'")
|
||||
name_valid = db.CheckConstraint("name ~* '^[a-zA-Z0-9_\-\.]+$'")
|
||||
|
||||
search_vector = db.Column(TSVectorType("name", "title", "short_desc", "desc",
|
||||
weights={ "name": "A", "title": "B", "short_desc": "C", "desc": "D" }))
|
||||
|
@ -532,7 +534,7 @@ class Package(db.Model):
|
|||
"title": self.title,
|
||||
"author": self.author.username,
|
||||
"short_description": short_desc,
|
||||
"type": self.type.toName(),
|
||||
# "type": self.type.toName(),
|
||||
"release": release_id,
|
||||
"thumbnail": (base_url + tnurl) if tnurl is not None else None,
|
||||
"aliases": [ alias.getAsDictionary() for alias in self.aliases ],
|
||||
|
@ -557,7 +559,7 @@ class Package(db.Model):
|
|||
"title": self.title,
|
||||
"short_description": self.short_desc,
|
||||
"long_description": self.desc,
|
||||
"type": self.type.toName(),
|
||||
# "type": self.type.toName(),
|
||||
"created_at": self.created_at.isoformat(),
|
||||
|
||||
"license": self.license.name,
|
||||
|
@ -636,9 +638,7 @@ class Package(db.Model):
|
|||
|
||||
def getDownloadRelease(self, version=None):
|
||||
for rel in self.releases:
|
||||
if rel.approved and (version is None or
|
||||
((rel.min_rel is None or rel.min_rel_id <= version.id) and
|
||||
(rel.max_rel is None or rel.max_rel_id >= version.id))):
|
||||
if rel.approved:
|
||||
return rel
|
||||
|
||||
return None
|
||||
|
@ -728,7 +728,7 @@ class Package(db.Model):
|
|||
return False
|
||||
|
||||
needsScreenshot = \
|
||||
(self.type == self.type.GAME or self.type == self.type.TXP) and \
|
||||
(self.type == self.type.GAME or self.type == self.type.ASSETPACK) and \
|
||||
self.screenshots.count() == 0
|
||||
|
||||
return self.releases.filter(PackageRelease.task_id.is_(None)).count() > 0 and not needsScreenshot
|
||||
|
@ -765,14 +765,13 @@ class Package(db.Model):
|
|||
review_scores = [ 100 * r.asSign() for r in self.reviews ]
|
||||
self.score = self.score_downloads + sum(review_scores)
|
||||
|
||||
|
||||
class MetaPackage(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(100), unique=True, nullable=False)
|
||||
dependencies = db.relationship("Dependency", back_populates="meta_package", lazy="dynamic")
|
||||
packages = db.relationship("Package", lazy="dynamic", back_populates="provides", secondary=PackageProvides)
|
||||
|
||||
mp_name_valid = db.CheckConstraint("name ~* '^[a-z0-9_]+$'")
|
||||
mp_name_valid = db.CheckConstraint("name ~* '^[a-zA-Z0-9_\-\.]+$'")
|
||||
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
|
@ -848,6 +847,7 @@ class Tag(db.Model):
|
|||
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)
|
||||
is_toplevel = db.Column(db.Boolean, nullable=False, default=False)
|
||||
|
||||
packages = db.relationship("Package", back_populates="tags", secondary=Tags)
|
||||
|
||||
|
@ -857,7 +857,7 @@ class Tag(db.Model):
|
|||
self.textColor = textColor
|
||||
|
||||
import re
|
||||
regex = re.compile("[^a-z_]")
|
||||
regex = re.compile("[^0-9a-z_]")
|
||||
self.name = regex.sub("", self.title.lower().replace(" ", "_"))
|
||||
|
||||
def getAsDictionary(self):
|
||||
|
@ -867,52 +867,11 @@ class Tag(db.Model):
|
|||
"title": self.title,
|
||||
"description": description,
|
||||
"is_protected": self.is_protected,
|
||||
"is_toplevel": self.is_toplevel,
|
||||
"views": self.views,
|
||||
}
|
||||
|
||||
|
||||
class MinetestRelease(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(100), unique=True, nullable=False)
|
||||
protocol = db.Column(db.Integer, nullable=False, default=0)
|
||||
|
||||
def __init__(self, name=None, protocol=0):
|
||||
self.name = name
|
||||
self.protocol = protocol
|
||||
|
||||
def getActual(self):
|
||||
return None if self.name == "None" else self
|
||||
|
||||
def getAsDictionary(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
"protocol_version": self.protocol,
|
||||
"is_dev": "-dev" in self.name,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get(cls, version, protocol_num):
|
||||
if version:
|
||||
parts = version.strip().split(".")
|
||||
if len(parts) >= 2:
|
||||
major_minor = parts[0] + "." + parts[1]
|
||||
query = MinetestRelease.query.filter(MinetestRelease.name.like("{}%".format(major_minor)))
|
||||
if protocol_num:
|
||||
query = query.filter_by(protocol=protocol_num)
|
||||
|
||||
release = query.one_or_none()
|
||||
if release:
|
||||
return release
|
||||
|
||||
if protocol_num:
|
||||
# Find the closest matching release
|
||||
return MinetestRelease.query.order_by(db.desc(MinetestRelease.protocol),
|
||||
db.desc(MinetestRelease.id)) \
|
||||
.filter(MinetestRelease.protocol <= protocol_num).first()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class PackageRelease(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
|
@ -927,11 +886,7 @@ class PackageRelease(db.Model):
|
|||
commit_hash = db.Column(db.String(41), nullable=True, default=None)
|
||||
downloads = db.Column(db.Integer, nullable=False, default=0)
|
||||
|
||||
min_rel_id = db.Column(db.Integer, db.ForeignKey("minetest_release.id"), nullable=True, server_default=None)
|
||||
min_rel = db.relationship("MinetestRelease", foreign_keys=[min_rel_id])
|
||||
|
||||
max_rel_id = db.Column(db.Integer, db.ForeignKey("minetest_release.id"), nullable=True, server_default=None)
|
||||
max_rel = db.relationship("MinetestRelease", foreign_keys=[max_rel_id])
|
||||
channel = db.Column(db.String(200), nullable=False, default="")
|
||||
|
||||
# If the release is approved, then the task_id must be null and the url must be present
|
||||
CK_approval_valid = db.CheckConstraint("not approved OR (task_id IS NULL AND (url = '') IS NOT FALSE)")
|
||||
|
@ -948,8 +903,7 @@ class PackageRelease(db.Model):
|
|||
"release_date": self.releaseDate.isoformat(),
|
||||
"commit": self.commit_hash,
|
||||
"downloads": self.downloads,
|
||||
"min_minetest_version": self.min_rel and self.min_rel.getAsDictionary(),
|
||||
"max_minetest_version": self.max_rel and self.max_rel.getAsDictionary(),
|
||||
"channel": self.channel,
|
||||
}
|
||||
|
||||
def getLongAsDictionary(self):
|
||||
|
@ -960,8 +914,7 @@ class PackageRelease(db.Model):
|
|||
"release_date": self.releaseDate.isoformat(),
|
||||
"commit": self.commit_hash,
|
||||
"downloads": self.downloads,
|
||||
"min_minetest_version": self.min_rel and self.min_rel.getAsDictionary(),
|
||||
"max_minetest_version": self.max_rel and self.max_rel.getAsDictionary(),
|
||||
"channel": self.channel,
|
||||
"package": self.package.getAsDictionaryKey()
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
File diff suppressed because one or more lines are too long
|
@ -53,7 +53,7 @@ ni:" ac co com edu gob mil net nom org ",np:" com edu gov mil net org ",nr:" biz
|
|||
ps:" com edu gov net org plo sec ",pw:" belau co ed go ne or ",ro:" arts com firm info nom nt org rec store tm www ",rs:" ac co edu gov in org ",sb:" com edu gov net org ",sc:" com edu gov net org ",sh:" co com edu gov net nom org ",sl:" com edu gov net org ",st:" co com consulado edu embaixada gov mil net org principe saotome store ",sv:" com edu gob org red ",sz:" ac co org ",tr:" av bbs bel biz com dr edu gen gov info k12 name net org pol tel tsk tv web ",tt:" aero biz cat co com coop edu gov info int jobs mil mobi museum name net org pro tel travel ",
|
||||
tw:" club com ebiz edu game gov idv mil net org ",mu:" ac co com gov net or org ",mz:" ac co edu gov org ",na:" co com ",nz:" ac co cri geek gen govt health iwi maori mil net org parliament school ",pa:" abo ac com edu gob ing med net nom org sld ",pt:" com edu gov int net nome org publ ",py:" com edu gov mil net org ",qa:" com edu gov mil net org ",re:" asso com nom ",ru:" ac adygeya altai amur arkhangelsk astrakhan bashkiria belgorod bir bryansk buryatia cbg chel chelyabinsk chita chukotka chuvashia com dagestan e-burg edu gov grozny int irkutsk ivanovo izhevsk jar joshkar-ola kalmykia kaluga kamchatka karelia kazan kchr kemerovo khabarovsk khakassia khv kirov koenig komi kostroma kranoyarsk kuban kurgan kursk lipetsk magadan mari mari-el marine mil mordovia mosreg msk murmansk nalchik net nnov nov novosibirsk nsk omsk orenburg org oryol penza perm pp pskov ptz rnd ryazan sakhalin samara saratov simbirsk smolensk spb stavropol stv surgut tambov tatarstan tom tomsk tsaritsyn tsk tula tuva tver tyumen udm udmurtia ulan-ude vladikavkaz vladimir vladivostok volgograd vologda voronezh vrn vyatka yakutia yamal yekaterinburg yuzhno-sakhalinsk ",
|
||||
rw:" ac co com edu gouv gov int mil net ",sa:" com edu gov med net org pub sch ",sd:" com edu gov info med net org tv ",se:" a ac b bd c d e f g h i k l m n o org p parti pp press r s t tm u w x y z ",sg:" com edu gov idn net org per ",sn:" art com edu gouv org perso univ ",sy:" com edu gov mil net news org ",th:" ac co go in mi net or ",tj:" ac biz co com edu go gov info int mil name net nic org test web ",tn:" agrinet com defense edunet ens fin gov ind info intl mincom nat net org perso rnrt rns rnu tourism ",
|
||||
tz:" ac co go ne or ",ua:" biz cherkassy chernigov chernovtsy ck cn co com crimea cv dn dnepropetrovsk donetsk dp edu gov if in ivano-frankivsk kh kharkov kherson khmelnitskiy kiev kirovograd km kr ks kv lg lugansk lutsk lviv me mk net nikolaev od odessa org pl poltava pp rovno rv sebastopol sumy te ternopil uzhgorod vinnica vn zaporizhzhe zhitomir zp zt ",ug:" ac co go ne or org sc ",uk:" ac bl british-library co cym gov govt icnet jet lea ltd me mil mod national-library-scotland nel net nhs nic nls org orgn parliament plc police sch scot soc ",
|
||||
tz:" ac co go ne or ",ua:" biz cherkassy chernigov chernovtsy ck cn co com crimea cv dn dnepropetrovsk donetsk dp edu gov if in ivano-frankivsk kh kharkov kherson khmelnitskiy kiev kirovograd km kr ks kv lg lugansk lutsk lviv me mk net nikolaev od odessa org pl poltava pp rovno rv sebastopol sumy te ternopil uzhgorod vinnica vn zaporizhzhe zhitomir zp zt ",ug:" ac co go ne or org sc ",uk:" ac bl british-library co cym gov govt icnet jet lea ltd me mil tool national-library-scotland nel net nhs nic nls org orgn parliament plc police sch scot soc ",
|
||||
us:" dni fed isa kids nsn ",uy:" com edu gub mil net org ",ve:" co com edu gob info mil net org web ",vi:" co com k12 net org ",vn:" ac biz com edu gov health info int name net org pro ",ye:" co com gov ltd me net org plc ",yu:" ac co edu gov org ",za:" ac agric alt bourse city co cybernet db edu gov grondar iaccess imt inca landesign law mil net ngo nis nom olivetti org pix school tm web ",zm:" ac co com edu gov net org sch ",com:"ar br cn de eu gb gr hu jpn kr no qc ru sa se uk us uy za ",net:"gb jp se uk ",
|
||||
org:"ae",de:"com "},has:function(k){var d=k.lastIndexOf(".");if(0>=d||d>=k.length-1)return!1;var m=k.lastIndexOf(".",d-1);if(0>=m||m>=d-1)return!1;var x=n.list[k.slice(d+1)];return x?0<=x.indexOf(" "+k.slice(m+1,d)+" "):!1},is:function(k){var d=k.lastIndexOf(".");if(0>=d||d>=k.length-1||0<=k.lastIndexOf(".",d-1))return!1;var m=n.list[k.slice(d+1)];return m?0<=m.indexOf(" "+k.slice(0,d)+" "):!1},get:function(k){var d=k.lastIndexOf(".");if(0>=d||d>=k.length-1)return null;var m=k.lastIndexOf(".",d-1);
|
||||
if(0>=m||m>=d-1)return null;var x=n.list[k.slice(d+1)];return!x||0>x.indexOf(" "+k.slice(m+1,d)+" ")?null:k.slice(m+1)},noConflict:function(){t.SecondLevelDomains===this&&(t.SecondLevelDomains=w);return this}};return n});
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>ContentDB</ShortName>
|
||||
<LongName>ContentDB</LongName>
|
||||
<ShortName>Okapi</ShortName>
|
||||
<LongName>Okapi ContentDB</LongName>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Description>Search mods, games, and textures for Minetest.</Description>
|
||||
<Tags>Minetest Mod Game Subgame Search</Tags>
|
||||
<Url type="text/html" method="get" template="https://content.minetest.net/packages?q={searchTerms}"/>
|
||||
<Description>Search libre games, tools and mods</Description>
|
||||
<Tags>Libre Tool Game Subgame Search</Tags>
|
||||
<Url type="text/html" method="get" template="https://games.armen138.com/packages?q={searchTerms}"/>
|
||||
</OpenSearchDescription>
|
||||
|
|
|
@ -38,7 +38,7 @@ $(function() {
|
|||
}
|
||||
|
||||
let hint_mtmods = `Tip:
|
||||
Don't include <i>Minetest</i>, <i>mod</i>, or <i>modpack</i> anywhere in the short description.
|
||||
Don't include <i>Minetest</i>, <i>tool</i>, or <i>modpack</i> anywhere in the short description.
|
||||
It is unnecessary and wastes characters.`;
|
||||
|
||||
let hint_thegame = `Tip:
|
||||
|
@ -47,8 +47,8 @@ $(function() {
|
|||
|
||||
$("#short_desc").on("change paste keyup", function() {
|
||||
const val = $(this).val().toLowerCase();
|
||||
if (val.indexOf("minetest") >= 0 || val.indexOf("mod") >= 0 ||
|
||||
val.indexOf("modpack") >= 0 || val.indexOf("mod pack") >= 0) {
|
||||
if (val.indexOf("minetest") >= 0 || val.indexOf("tool") >= 0 ||
|
||||
val.indexOf("modpack") >= 0 || val.indexOf("tool pack") >= 0) {
|
||||
showHint($(this), hint_mtmods);
|
||||
} else if (val.indexOf("the game") >= 0) {
|
||||
showHint($(this), hint_thegame);
|
||||
|
|
|
@ -3,7 +3,7 @@ from sqlalchemy import or_
|
|||
from sqlalchemy.orm import subqueryload
|
||||
from sqlalchemy.sql.expression import func
|
||||
|
||||
from .models import db, PackageType, Package, ForumTopic, License, MinetestRelease, PackageRelease, User, Tag, \
|
||||
from .models import db, Package, ForumTopic, License, PackageRelease, User, Tag, \
|
||||
ContentWarning, PackageState, PackageDevState
|
||||
from .utils import isYes, get_int_or_abort
|
||||
|
||||
|
@ -17,11 +17,11 @@ class QueryBuilder:
|
|||
title = "Packages"
|
||||
|
||||
# Get request types
|
||||
types = args.getlist("type")
|
||||
types = [PackageType.get(tname) for tname in types]
|
||||
types = [type for type in types if type is not None]
|
||||
if len(types) > 0:
|
||||
title = ", ".join([str(type.plural) for type in types])
|
||||
# types = args.getlist("type")
|
||||
# types = [PackageType.get(tname) for tname in types]
|
||||
# types = [type for type in types if type is not None]
|
||||
# if len(types) > 0:
|
||||
# title = ", ".join([str(type.plural) for type in types])
|
||||
|
||||
# Get tags types
|
||||
tags = args.getlist("tag")
|
||||
|
@ -32,7 +32,7 @@ class QueryBuilder:
|
|||
self.hide_flags = set(args.getlist("hide"))
|
||||
|
||||
self.title = title
|
||||
self.types = types
|
||||
# self.types = types
|
||||
self.tags = tags
|
||||
|
||||
self.random = "random" in args
|
||||
|
@ -61,11 +61,11 @@ class QueryBuilder:
|
|||
self.author = args.get("author")
|
||||
|
||||
protocol_version = get_int_or_abort(args.get("protocol_version"))
|
||||
minetest_version = args.get("engine_version")
|
||||
if protocol_version or minetest_version:
|
||||
self.version = MinetestRelease.get(minetest_version, protocol_version)
|
||||
else:
|
||||
self.version = None
|
||||
# minetest_version = args.get("engine_version")
|
||||
# if protocol_version or minetest_version:
|
||||
# self.version = MinetestRelease.get(minetest_version, protocol_version)
|
||||
# else:
|
||||
self.version = None
|
||||
|
||||
self.show_discarded = isYes(args.get("show_discarded"))
|
||||
self.show_added = args.get("show_added")
|
||||
|
@ -126,8 +126,8 @@ class QueryBuilder:
|
|||
return query
|
||||
|
||||
def filterPackageQuery(self, query):
|
||||
if len(self.types) > 0:
|
||||
query = query.filter(Package.type.in_(self.types))
|
||||
# if len(self.types) > 0:
|
||||
# query = query.filter(Package.type.in_(self.types))
|
||||
|
||||
if self.author:
|
||||
author = User.query.filter_by(username=self.author).first()
|
||||
|
@ -233,8 +233,8 @@ class QueryBuilder:
|
|||
query = query.filter(or_(ForumTopic.title.ilike('%' + self.search + '%'),
|
||||
ForumTopic.name == self.search.lower()))
|
||||
|
||||
if len(self.types) > 0:
|
||||
query = query.filter(ForumTopic.type.in_(self.types))
|
||||
# if len(self.types) > 0:
|
||||
# query = query.filter(ForumTopic.type.in_(self.types))
|
||||
|
||||
if self.limit:
|
||||
query = query.limit(self.limit)
|
||||
|
|
|
@ -66,10 +66,10 @@ def make_celery(app):
|
|||
celery = make_celery(app)
|
||||
|
||||
CELERYBEAT_SCHEDULE = {
|
||||
'topic_list_import': {
|
||||
'task': 'app.tasks.forumtasks.importTopicList',
|
||||
'schedule': crontab(minute=1, hour=1), # 0101
|
||||
},
|
||||
# 'topic_list_import': {
|
||||
# 'task': 'app.tasks.forumtasks.importTopicList',
|
||||
# 'schedule': crontab(minute=1, hour=1), # 0101
|
||||
# },
|
||||
'package_score_update': {
|
||||
'task': 'app.tasks.pkgtasks.updatePackageScores',
|
||||
'schedule': crontab(minute=10, hour=1), # 0110
|
||||
|
@ -78,10 +78,10 @@ CELERYBEAT_SCHEDULE = {
|
|||
'task': 'app.tasks.importtasks.check_for_updates',
|
||||
'schedule': crontab(minute=10, hour=1), # 0110
|
||||
},
|
||||
'send_pending_notifications': {
|
||||
'task': 'app.tasks.emails.send_pending_notifications',
|
||||
'schedule': crontab(minute='*/5'), # every 5 minutes
|
||||
},
|
||||
# 'send_pending_notifications': {
|
||||
# 'task': 'app.tasks.emails.send_pending_notifications',
|
||||
# 'schedule': crontab(minute='*/5'), # every 5 minutes
|
||||
# },
|
||||
'send_notification_digests': {
|
||||
'task': 'app.tasks.emails.send_pending_digests',
|
||||
'schedule': crontab(minute=0, hour=14), # 1400
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
import json, re, sys
|
||||
from app.models import *
|
||||
from app.tasks import celery
|
||||
from app.utils import is_username_valid
|
||||
import urllib.request
|
||||
import gi
|
||||
import PIL
|
||||
import requests
|
||||
import os
|
||||
import sys
|
||||
import inspect
|
||||
import shutil
|
||||
import urllib.request
|
||||
from sqlalchemy.sql import func
|
||||
from gi.repository import Gio
|
||||
gi.require_version('AppStreamGlib', '1.0')
|
||||
from gi.repository import AppStreamGlib
|
||||
from app.utils.lists import alwaysAccept, alwaysDeny, badLicenses, badCategories, nonFreeAssets, nonFreeNetworkServices
|
||||
import itertools
|
||||
from app.utils import make_flask_login_password
|
||||
from app.utils.image import get_image_size
|
||||
from app.utils import randomString
|
||||
|
||||
map_categories = {
|
||||
"tools": [ "development", "gtk", "qt" ],
|
||||
"mods": [ "mod", "mods", "extension", "addon" ],
|
||||
"games": [ "games", "game" ]
|
||||
}
|
||||
|
||||
exclude_hashtags = [
|
||||
"game"
|
||||
]
|
||||
|
||||
#Workaround to get the urls because app.get_urls() doesn't work :|
|
||||
def get_urls(app):
|
||||
kinds = [AppStreamGlib.UrlKind(kind) for kind in range(11)]
|
||||
urls = [(app.get_url_item(kind),kind.value_nick) for kind in kinds]
|
||||
return list(filter(lambda a: a[0] is not None, urls))
|
||||
|
||||
def acceptedGame(app):
|
||||
#return 'Game' in app.get_categories()
|
||||
if app.get_id() in alwaysAccept:
|
||||
return True
|
||||
if app.get_id() in alwaysDeny:
|
||||
return False
|
||||
|
||||
return app.get_project_license() and \
|
||||
not [x for x in badLicenses if x in app.get_project_license()] and \
|
||||
'Game' in app.get_categories() and \
|
||||
not [x for x in badCategories if x in app.get_categories()]
|
||||
|
||||
def getScreenshots(app):
|
||||
return [images.get_source() for images in app.get_screenshots()]
|
||||
|
||||
@celery.task()
|
||||
def importFromFlathub():
|
||||
url = "https://flathub.org/repo/appstream/x86_64/appstream.xml.gz"
|
||||
with urllib.request.urlopen(url) as response, open("/var/cdb/uploads/appstream.xml.gz", 'wb') as out_file:
|
||||
shutil.copyfileobj(response, out_file)
|
||||
store = AppStreamGlib.Store()
|
||||
file = Gio.File.new_for_path("/var/cdb/uploads/appstream.xml.gz")
|
||||
file.load_contents()
|
||||
AppStreamGlib.Store.from_file(store, file, ".", None)
|
||||
apps = list(filter(acceptedGame, store.get_apps()))
|
||||
session=db.session
|
||||
licenses = { x.name : x for x in License.query.all() }
|
||||
tags = { x.name : x for x in Tag.query.all() }
|
||||
admin_user = User.query.filter_by(username="AppStreamBot").first()
|
||||
|
||||
if not admin_user:
|
||||
admin_user = User("AppStreamBot")
|
||||
admin_user.is_active = True
|
||||
admin_user.password = make_flask_login_password("AppStreamBot")
|
||||
admin_user.github_username = "AppStreamBot"
|
||||
admin_user.forums_username = "AppStreamBot"
|
||||
admin_user.rank = UserRank.ADMIN
|
||||
session.add(admin_user)
|
||||
|
||||
featured = Tag.query.filter_by(name="featured").first()
|
||||
for app in apps:
|
||||
screenshots = getScreenshots(app)
|
||||
urls = get_urls(app)
|
||||
filename = app.get_name().replace(':', '').replace('/','') + ".html"
|
||||
print("APPLICATION: ",app.get_name())
|
||||
package_exists = Package.query.filter_by(name=app.get_id()).first()
|
||||
if package_exists:
|
||||
print(f"Package {app.get_id()} exists, skipping.")
|
||||
else:
|
||||
game1 = Package()
|
||||
game1.state = PackageState.APPROVED
|
||||
game1.name = app.get_id()
|
||||
game1.title = app.get_name()
|
||||
hashtags = []
|
||||
license = "Uknown" if app.get_project_license() is None else app.get_project_license().split("AND")[0].split("and")[0]
|
||||
if license not in licenses:
|
||||
row = License(license)
|
||||
licenses[row.name] = row
|
||||
session.add(row)
|
||||
session.commit()
|
||||
has_toplevel = False
|
||||
categories = list(set([ x.lower() for x in app.get_categories() ]))
|
||||
added = []
|
||||
# how do we get the type attribute from <component> here?
|
||||
# if app.get_type() == "addon":
|
||||
# game1.tags.append(tags["mods"])
|
||||
# added.append("mods")
|
||||
# has_toplevel = True
|
||||
for category in categories:
|
||||
if category in tags and category not in added:
|
||||
if category in map_categories:
|
||||
has_toplevel = True
|
||||
game1.tags.append(tags[category])
|
||||
added.append(category)
|
||||
elif category not in exclude_hashtags:
|
||||
hashtags.append(category)
|
||||
if not has_toplevel:
|
||||
for map_category in map_categories:
|
||||
if category in map_categories[map_category] and map_category not in added:
|
||||
game1.tags.append(tags[map_category])
|
||||
added.append(map_category)
|
||||
has_toplevel = True
|
||||
break
|
||||
if not has_toplevel and "games" not in added:
|
||||
game1.tags.append(tags["games"])
|
||||
added.append("games")
|
||||
|
||||
# this short list seems like a reasonable set of initial "featured" games
|
||||
if app.get_id() in alwaysAccept:
|
||||
game1.tags.append(featured)
|
||||
|
||||
game1.license = licenses[license]
|
||||
game1.media_license = licenses["MIT"]
|
||||
game1.author = admin_user
|
||||
|
||||
|
||||
for url,t in urls:
|
||||
if t == "bugtracker":
|
||||
game1.issueTracker = url
|
||||
if "git" in url and "issues" in url:
|
||||
game1.repo = url.replace("/issues", "")
|
||||
elif t == "homepage" and not game1.repo:
|
||||
game1.repo = url
|
||||
if t == "homepage" and "git" not in url:
|
||||
game1.website = url
|
||||
|
||||
game1.forums = 12835
|
||||
game1.short_desc = "" or app.get_comment()
|
||||
game1.desc = app.get_description() + "\n " + ",".join([ "#" + x for x in hashtags ])
|
||||
game1.install_desc = "Make sure to follow the [setup guide](https://flatpak.org/setup/) before installing. \n"
|
||||
game1.install_desc += f"\n```\nflatpak install flathub {app.get_id()}\n```\n"
|
||||
game1.install_desc += "Run: \n"
|
||||
game1.install_desc += f"\n```\nflatpak run {app.get_id()}\n```\n"
|
||||
session.add(game1)
|
||||
|
||||
install_url = f"https://dl.flathub.org/repo/appstream/{app.get_id()}.flatpakref"
|
||||
release = PackageRelease()
|
||||
release.package = game1
|
||||
release.title = "Flathub Install"
|
||||
release.url = install_url
|
||||
release.approved = True
|
||||
release.downloads = 0
|
||||
release.releaseDate = func.now()
|
||||
|
||||
session.add(release)
|
||||
|
||||
for screenshot in screenshots:
|
||||
counter = 1
|
||||
url = screenshot.get_url()
|
||||
try:
|
||||
r = requests.get(url,timeout=10)
|
||||
r.raise_for_status()
|
||||
filename = randomString(10) + "." + "png"
|
||||
filepath = os.path.join("/var/cdb/uploads", filename)
|
||||
print("Screenshot url: ", url)
|
||||
with open(filepath,"wb") as f:
|
||||
f.write(r.content)
|
||||
|
||||
width, height = get_image_size(filepath)
|
||||
|
||||
if (width is not None) and (height is not None):
|
||||
ss = PackageScreenshot()
|
||||
ss.package = game1
|
||||
ss.title = "Untitled"
|
||||
ss.url = "/uploads/" + filename
|
||||
ss.width = width
|
||||
ss.height = height
|
||||
ss.approved = True
|
||||
ss.order = counter
|
||||
session.add(ss)
|
||||
session.commit()
|
||||
game1.cover_image = ss
|
||||
session.commit()
|
||||
counter += 1
|
||||
except requests.exceptions.HTTPError as err:
|
||||
print("HTTP error downloading the screenshot ", err)
|
||||
except requests.exceptions.ConnectionError as err:
|
||||
print("HTTP error downloading the screenshot ", err)
|
||||
except requests.exceptions.ReadTimeout as err:
|
||||
print("Screenshot timeout ", err)
|
||||
except PIL.UnidentifiedImageError as err:
|
||||
print("Corrupt image ", err)
|
||||
|
||||
session.commit()
|
||||
def importAppstream():
|
||||
pass
|
|
@ -80,7 +80,7 @@ def checkAllForumAccounts(forceNoSave=False):
|
|||
|
||||
|
||||
regex_tag = re.compile(r"\[([a-z0-9_]+)\]")
|
||||
BANNED_NAMES = ["mod", "game", "old", "outdated", "wip", "api", "beta", "alpha", "git"]
|
||||
BANNED_NAMES = ["tool", "game", "old", "outdated", "wip", "api", "beta", "alpha", "git"]
|
||||
def getNameFromTaglist(taglist):
|
||||
for tag in reversed(regex_tag.findall(taglist)):
|
||||
if len(tag) < 30 and not tag in BANNED_NAMES and \
|
||||
|
@ -112,81 +112,81 @@ def getLinksFromModSearch():
|
|||
pass
|
||||
|
||||
except urllib.error.URLError:
|
||||
print("Unable to open krocks mod search!")
|
||||
print("Unable to open krocks tool search!")
|
||||
return links
|
||||
|
||||
return links
|
||||
|
||||
@celery.task()
|
||||
def importTopicList():
|
||||
links_by_id = getLinksFromModSearch()
|
||||
# @celery.task()
|
||||
# def importTopicList():
|
||||
# links_by_id = getLinksFromModSearch()
|
||||
|
||||
info_by_id = {}
|
||||
getTopicsFromForum(11, out=info_by_id, extra={ 'type': PackageType.MOD, 'wip': False })
|
||||
getTopicsFromForum(9, out=info_by_id, extra={ 'type': PackageType.MOD, 'wip': True })
|
||||
getTopicsFromForum(15, out=info_by_id, extra={ 'type': PackageType.GAME, 'wip': False })
|
||||
getTopicsFromForum(50, out=info_by_id, extra={ 'type': PackageType.GAME, 'wip': True })
|
||||
# info_by_id = {}
|
||||
# getTopicsFromForum(11, out=info_by_id, extra={ 'type': PackageType.TOOL, 'wip': False })
|
||||
# getTopicsFromForum(9, out=info_by_id, extra={ 'type': PackageType.TOOL, 'wip': True })
|
||||
# getTopicsFromForum(15, out=info_by_id, extra={ 'type': PackageType.GAME, 'wip': False })
|
||||
# getTopicsFromForum(50, out=info_by_id, extra={ 'type': PackageType.GAME, 'wip': True })
|
||||
|
||||
# Caches
|
||||
username_to_user = {}
|
||||
topics_by_id = {}
|
||||
for topic in ForumTopic.query.all():
|
||||
topics_by_id[topic.topic_id] = topic
|
||||
# # Caches
|
||||
# username_to_user = {}
|
||||
# topics_by_id = {}
|
||||
# for topic in ForumTopic.query.all():
|
||||
# topics_by_id[topic.topic_id] = topic
|
||||
|
||||
def get_or_create_user(username):
|
||||
user = username_to_user.get(username)
|
||||
if user:
|
||||
return user
|
||||
# def get_or_create_user(username):
|
||||
# user = username_to_user.get(username)
|
||||
# if user:
|
||||
# return user
|
||||
|
||||
if not is_username_valid(username):
|
||||
return None
|
||||
# if not is_username_valid(username):
|
||||
# return None
|
||||
|
||||
user = User.query.filter_by(forums_username=username).first()
|
||||
if user is None:
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user:
|
||||
return None
|
||||
# user = User.query.filter_by(forums_username=username).first()
|
||||
# if user is None:
|
||||
# user = User.query.filter_by(username=username).first()
|
||||
# if user:
|
||||
# return None
|
||||
|
||||
user = User(username)
|
||||
user.forums_username = username
|
||||
db.session.add(user)
|
||||
# user = User(username)
|
||||
# user.forums_username = username
|
||||
# db.session.add(user)
|
||||
|
||||
username_to_user[username] = user
|
||||
return user
|
||||
# username_to_user[username] = user
|
||||
# return user
|
||||
|
||||
# Create or update
|
||||
for info in info_by_id.values():
|
||||
id = int(info["id"])
|
||||
# # Create or update
|
||||
# for info in info_by_id.values():
|
||||
# id = int(info["id"])
|
||||
|
||||
# Get author
|
||||
username = info["author"]
|
||||
user = get_or_create_user(username)
|
||||
if user is None:
|
||||
print("Error! Unable to create user {}".format(username), file=sys.stderr)
|
||||
continue
|
||||
# # Get author
|
||||
# username = info["author"]
|
||||
# user = get_or_create_user(username)
|
||||
# if user is None:
|
||||
# print("Error! Unable to create user {}".format(username), file=sys.stderr)
|
||||
# continue
|
||||
|
||||
# Get / add row
|
||||
topic = topics_by_id.get(id)
|
||||
if topic is None:
|
||||
topic = ForumTopic()
|
||||
db.session.add(topic)
|
||||
# # Get / add row
|
||||
# topic = topics_by_id.get(id)
|
||||
# if topic is None:
|
||||
# topic = ForumTopic()
|
||||
# db.session.add(topic)
|
||||
|
||||
# Parse title
|
||||
title, name = parseTitle(info["title"])
|
||||
# # Parse title
|
||||
# title, name = parseTitle(info["title"])
|
||||
|
||||
# Get link
|
||||
link = links_by_id.get(id)
|
||||
# # Get link
|
||||
# link = links_by_id.get(id)
|
||||
|
||||
# Fill row
|
||||
topic.topic_id = int(id)
|
||||
topic.author = user
|
||||
topic.type = info["type"]
|
||||
topic.title = title
|
||||
topic.name = name
|
||||
topic.link = link
|
||||
topic.wip = info["wip"]
|
||||
topic.posts = int(info["posts"])
|
||||
topic.views = int(info["views"])
|
||||
topic.created_at = info["date"]
|
||||
# # Fill row
|
||||
# topic.topic_id = int(id)
|
||||
# topic.author = user
|
||||
# topic.type = info["type"]
|
||||
# topic.title = title
|
||||
# topic.name = name
|
||||
# topic.link = link
|
||||
# topic.wip = info["wip"]
|
||||
# topic.posts = int(info["posts"])
|
||||
# topic.views = int(info["views"])
|
||||
# topic.created_at = info["date"]
|
||||
|
||||
db.session.commit()
|
||||
# db.session.commit()
|
||||
|
|
|
@ -75,11 +75,8 @@ def getMeta(urlstr, author):
|
|||
|
||||
def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
||||
try:
|
||||
tree = build_tree(path, expected_type=ContentType[release.package.type.name],
|
||||
author=release.package.author.username, name=release.package.name)
|
||||
tree = build_tree(path, author=release.package.author.username, name=release.package.name)
|
||||
|
||||
if tree.name is not None and release.package.name != tree.name and tree.type == ContentType.MOD:
|
||||
raise MinetestCheckError(f"Expected {tree.relative} to have technical name {release.package.name}, instead has name {tree.name}")
|
||||
|
||||
cache = {}
|
||||
def getMetaPackages(names):
|
||||
|
@ -99,14 +96,9 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
|||
optional_depends = tree.fold("meta", "optional_depends")
|
||||
|
||||
# Filter out provides
|
||||
for mod in provides:
|
||||
depends.discard(mod)
|
||||
optional_depends.discard(mod)
|
||||
|
||||
# Raise error on unresolved game dependencies
|
||||
if package.type == PackageType.GAME and len(depends) > 0:
|
||||
deps = ", ".join(depends)
|
||||
raise MinetestCheckError("Game has unresolved hard dependencies: " + deps)
|
||||
for tool in provides:
|
||||
depends.discard(tool)
|
||||
optional_depends.discard(tool)
|
||||
|
||||
# Add dependencies
|
||||
for meta in getMetaPackages(depends):
|
||||
|
@ -116,16 +108,16 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
|||
db.session.add(Dependency(package, meta=meta, optional=True))
|
||||
|
||||
# Update game supports
|
||||
if package.type == PackageType.MOD:
|
||||
resolver = GameSupportResolver()
|
||||
resolver.update(package)
|
||||
# if package.type == PackageType.TOOL:
|
||||
# resolver = GameSupportResolver()
|
||||
# resolver.update(package)
|
||||
|
||||
# Update min/max
|
||||
if tree.meta.get("min_minetest_version"):
|
||||
release.min_rel = MinetestRelease.get(tree.meta["min_minetest_version"], None)
|
||||
# # Update min/max
|
||||
# if tree.meta.get("min_minetest_version"):
|
||||
# release.min_rel = MinetestRelease.get(tree.meta["min_minetest_version"], None)
|
||||
|
||||
if tree.meta.get("max_minetest_version"):
|
||||
release.max_rel = MinetestRelease.get(tree.meta["max_minetest_version"], None)
|
||||
# if tree.meta.get("max_minetest_version"):
|
||||
# release.max_rel = MinetestRelease.get(tree.meta["max_minetest_version"], None)
|
||||
|
||||
try:
|
||||
with open(os.path.join(tree.baseDir, ".cdb.json"), "r") as f:
|
||||
|
|
|
@ -8,13 +8,13 @@ class MinetestCheckError(Exception):
|
|||
|
||||
class ContentType(Enum):
|
||||
UNKNOWN = "unknown"
|
||||
MOD = "mod"
|
||||
TOOL = "tool"
|
||||
MODPACK = "modpack"
|
||||
GAME = "game"
|
||||
TXP = "texture pack"
|
||||
ASSETPACK = "texture pack"
|
||||
|
||||
def isModLike(self):
|
||||
return self == ContentType.MOD or self == ContentType.MODPACK
|
||||
return self == ContentType.TOOL or self == ContentType.MODPACK
|
||||
|
||||
def validate_same(self, other):
|
||||
"""
|
||||
|
@ -22,18 +22,6 @@ class ContentType(Enum):
|
|||
"""
|
||||
assert other
|
||||
|
||||
if self == ContentType.MOD:
|
||||
if not other.isModLike():
|
||||
raise MinetestCheckError("Expected a mod or modpack, found " + other.value)
|
||||
|
||||
elif self == ContentType.TXP:
|
||||
if other != ContentType.UNKNOWN and other != ContentType.TXP:
|
||||
raise MinetestCheckError("expected a " + self.value + ", found a " + other.value)
|
||||
|
||||
elif other != self:
|
||||
raise MinetestCheckError("Expected a " + self.value + ", found a " + other.value)
|
||||
|
||||
|
||||
from .tree import PackageTreeNode, get_base_dir
|
||||
|
||||
def build_tree(path, expected_type=None, author=None, repo=None, name=None):
|
||||
|
|
|
@ -19,14 +19,14 @@ def detect_type(path):
|
|||
if os.path.isfile(path + "/game.conf"):
|
||||
return ContentType.GAME
|
||||
elif os.path.isfile(path + "/init.lua"):
|
||||
return ContentType.MOD
|
||||
return ContentType.TOOL
|
||||
elif os.path.isfile(path + "/modpack.txt") or \
|
||||
os.path.isfile(path + "/modpack.conf"):
|
||||
return ContentType.MODPACK
|
||||
# elif os.path.isdir(path + "/mods"):
|
||||
# return ContentType.GAME
|
||||
elif os.path.isfile(path + "/texture_pack.conf"):
|
||||
return ContentType.TXP
|
||||
return ContentType.ASSETPACK
|
||||
else:
|
||||
return ContentType.UNKNOWN
|
||||
|
||||
|
@ -53,13 +53,7 @@ class PackageTreeNode:
|
|||
self.read_meta()
|
||||
|
||||
if self.type == ContentType.GAME:
|
||||
if not os.path.isdir(baseDir + "/mods"):
|
||||
raise MinetestCheckError("Game at {} does not have a mods/ folder".format(self.relative))
|
||||
self.add_children_from_mod_dir("mods")
|
||||
elif self.type == ContentType.MOD:
|
||||
if self.name and not basenamePattern.match(self.name):
|
||||
raise MinetestCheckError("Invalid base name for mod {} at {}, names must only contain a-z0-9_." \
|
||||
.format(self.name, self.relative))
|
||||
elif self.type == ContentType.MODPACK:
|
||||
self.add_children_from_mod_dir(None)
|
||||
|
||||
|
@ -72,11 +66,11 @@ class PackageTreeNode:
|
|||
def getMetaFileName(self):
|
||||
if self.type == ContentType.GAME:
|
||||
return "game.conf"
|
||||
elif self.type == ContentType.MOD:
|
||||
return "mod.conf"
|
||||
elif self.type == ContentType.TOOL:
|
||||
return "tool.conf"
|
||||
elif self.type == ContentType.MODPACK:
|
||||
return "modpack.conf"
|
||||
elif self.type == ContentType.TXP:
|
||||
elif self.type == ContentType.ASSETPACK:
|
||||
return "texture_pack.conf"
|
||||
else:
|
||||
return None
|
||||
|
@ -94,14 +88,7 @@ class PackageTreeNode:
|
|||
conf = parse_conf(myfile.read())
|
||||
for key, value in conf.items():
|
||||
result[key] = value
|
||||
except SyntaxError as e:
|
||||
raise MinetestCheckError("Error while reading {}: {}".format(meta_file_rel , e.msg))
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
if "release" in result:
|
||||
raise MinetestCheckError("{} should not contain 'release' key, as this is for use by ContentDB only.".format(meta_file_rel))
|
||||
|
||||
except IOError: pass
|
||||
|
||||
# description.txt
|
||||
if not "description" in result:
|
||||
|
@ -139,16 +126,7 @@ class PackageTreeNode:
|
|||
result["optional_depends"] = []
|
||||
|
||||
|
||||
def checkDependencies(deps):
|
||||
for dep in deps:
|
||||
if not basenamePattern.match(dep):
|
||||
if " " in dep:
|
||||
raise MinetestCheckError("Invalid dependency name '{}' for mod at {}, did you forget a comma?" \
|
||||
.format(dep, self.relative))
|
||||
else:
|
||||
raise MinetestCheckError(
|
||||
"Invalid dependency name '{}' for mod at {}, names must only contain a-z0-9_." \
|
||||
.format(dep, self.relative))
|
||||
def checkDependencies(deps): pass
|
||||
|
||||
# Check dependencies
|
||||
checkDependencies(result["depends"])
|
||||
|
@ -187,17 +165,11 @@ class PackageTreeNode:
|
|||
path = os.path.join(dir, entry)
|
||||
if not entry.startswith('.') and os.path.isdir(path):
|
||||
child = PackageTreeNode(path, relative + entry + "/", name=entry)
|
||||
if not child.type.isModLike():
|
||||
raise MinetestCheckError("Expecting mod or modpack, found {} at {} inside {}" \
|
||||
.format(child.type.value, child.relative, self.type.value))
|
||||
|
||||
if child.name is None:
|
||||
raise MinetestCheckError("Missing base name for mod at {}".format(self.relative))
|
||||
|
||||
self.children.append(child)
|
||||
|
||||
def getModNames(self):
|
||||
return self.fold("name", type=ContentType.MOD)
|
||||
return self.fold("name", type=ContentType.TOOL)
|
||||
|
||||
# attr: Attribute name
|
||||
# key: Key in attribute
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
<a class="list-group-item list-group-item-action" href="{{ url_for('users.list_all') }}">User list</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.tag_list') }}">Tag Editor</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.license_list') }}">License Editor</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.version_list') }}">Version Editor</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.warning_list') }}">Warning Editor</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.send_bulk_email') }}">Send bulk email</a>
|
||||
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.send_bulk_notification') }}">Send bulk notification</a>
|
||||
|
|
|
@ -1,23 +1,2 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{% if version %}
|
||||
Edit {{ version.name }}
|
||||
{% else %}
|
||||
New Minetest Version
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<a class="btn btn-primary float-right" href="{{ url_for('admin.create_edit_version') }}">New Version</a>
|
||||
<a class="btn btn-secondary mb-4" href="{{ url_for('admin.version_list') }}">Back to list</a>
|
||||
|
||||
{% from "macros/forms.html" import render_field, render_submit_field %}
|
||||
<form method="POST" action="" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{{ render_field(form.name) }}
|
||||
{{ render_field(form.protocol) }}
|
||||
{{ render_submit_field(form.submit) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -6,32 +6,29 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/libs/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/static/custom.css?v=34">
|
||||
<link rel="stylesheet" type="text/css" href="/static/custom.css?v=35">
|
||||
<link rel="search" type="application/opensearchdescription+xml" href="/static/opensearch.xml" title="ContentDB" />
|
||||
<link rel="shortcut icon" href="/favicon-16.png" sizes="16x16">
|
||||
<link rel="icon" href="/favicon-128.png" sizes="128x128">
|
||||
<link rel="icon" href="/favicon-32.png" sizes="32x32">
|
||||
<link rel="icon" href="/static/lg-logo.png" sizes="290x290">
|
||||
{% block headextra %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">{{ config.USER_APP_NAME }}</a>
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="/static/lg-logo.png" width="30" height="30" class="d-inline-block align-top" alt="">
|
||||
{{ config.USER_APP_NAME }}
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarColor01">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
{% for tag in toplevel %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('packages.list_all', type='mod') }}">{{ _("Mods") }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('packages.list_all', type='game') }}">{{ _("Games") }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('packages.list_all', type='txp') }}">{{ _("Texture Packs") }}</a>
|
||||
<a class="nav-link" href="{{ url_for('packages.list_all', tag=tag.name) }}">{{ _(tag.title) }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('packages.list_all', random=1, lucky=1) }}">{{ _("Random") }}</a>
|
||||
</li>
|
||||
|
@ -39,13 +36,13 @@
|
|||
<a class="nav-link" href="{{ url_for('flatpage', path='help') }}">{{ _("Help") }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('threads.list_all') }}">{{ _("Threads") }}</a>
|
||||
<a class="nav-link" href="{{ url_for('threads.list_all') }}">{{ _("Discussion") }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="form-inline my-2 my-lg-0" method="GET" action="/packages/">
|
||||
{% if type %}<input type="hidden" name="type" value="{{ type }}" />{% endif %}
|
||||
<input class="form-control" name="q" type="text"
|
||||
placeholder="{% if query_hint %}{{ _('Search %(type)s', type=query_hint | lower) }}{% else %}{{ _('Search all packages') }}{% endif %}"
|
||||
placeholder="{% if query_hint %}{{ _('Search projects', type=query_hint | lower) }}{% else %}{{ _('Search all packages') }}{% endif %}"
|
||||
value="{{ query or ''}}">
|
||||
<input class="btn btn-secondary my-2 my-sm-0 mr-sm-2" type="submit" value="{{ _('Search') }}" />
|
||||
<!-- <input class="btn btn-secondary my-2 my-sm-0"
|
||||
|
|
|
@ -34,5 +34,5 @@
|
|||
{{ _("Unsubscribe") }}
|
||||
</a> <br>
|
||||
|
||||
{{ _("This is a '%(type)s' notification.", type=notification.type.getTitle()) }}
|
||||
{{ _("This is a 'projects' notification.", type=notification.type.getTitle()) }}
|
||||
{% endblock %}
|
||||
|
|
|
@ -38,21 +38,11 @@
|
|||
alt="{{ _('%(title)s by %(author)s', title=package.title, author=package.author.display_name) }}">
|
||||
</div>
|
||||
<div class="carousel-caption text-shadow">
|
||||
<h3 class="mt-0 mb-3">
|
||||
{% if package.author %}
|
||||
{{ _('<strong>%(title)s</strong> by %(author)s', title=package.title, author=package.author.display_name) }}
|
||||
{% else %}
|
||||
<strong>{{ package.title }}</strong>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<p>
|
||||
{{ package.short_desc }}
|
||||
</p>
|
||||
{% if package.author %}
|
||||
<div class="d-none d-md-block">
|
||||
<span class="mr-2">
|
||||
{{ package.type.text }}
|
||||
</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>
|
||||
|
@ -110,27 +100,13 @@
|
|||
<h2 class="my-3">{{ _("Recently Updated") }}</h2>
|
||||
{{ render_pkggrid(updated) }}
|
||||
|
||||
|
||||
<a href="{{ url_for('packages.list_all', type='game', sort='score', order='desc') }}" class="btn btn-secondary float-right">
|
||||
{% for tag in toplevel %}
|
||||
<a href="{{ url_for('packages.list_all', tag=tag.name, sort='score', order='desc') }}" class="btn btn-secondary float-right">
|
||||
{{ _("See more") }}
|
||||
</a>
|
||||
<h2 class="my-3">{{ _("Top Games") }}</h2>
|
||||
{{ render_pkggrid(pop_gam) }}
|
||||
|
||||
|
||||
<a href="{{ url_for('packages.list_all', type='mod', sort='score', order='desc') }}" class="btn btn-secondary float-right">
|
||||
{{ _("See more") }}
|
||||
</a>
|
||||
<h2 class="my-3">{{ _("Top Mods") }}</h2>
|
||||
{{ render_pkggrid(pop_mod) }}
|
||||
|
||||
|
||||
<a href="{{ url_for('packages.list_all', type='txp', sort='score', order='desc') }}" class="btn btn-secondary float-right">
|
||||
{{ _("See more") }}
|
||||
</a>
|
||||
<h2 class="my-3">{{ _("Top Texture Packs") }}</h2>
|
||||
{{ render_pkggrid(pop_txp) }}
|
||||
|
||||
<h2 class="my-3">{{ _("Top " + tag.title) }}</h2>
|
||||
{{ render_pkggrid(popular[tag.name]) }}
|
||||
{% endfor %}
|
||||
|
||||
<h2 class="my-3">{{ _("Search by Tags") }}</h2>
|
||||
{% for pair in tags %}
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
{% elif (package.type == package.type.GAME or package.type == package.type.TXP) and package.screenshots.count() == 0 %}
|
||||
{% elif package.screenshots.count() == 0 %}
|
||||
{% set message = _("You need to add at least one screenshot.") %}
|
||||
|
||||
{% elif package.getMissingHardDependenciesQuery().count() > 0 %}
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
{{ package.short_desc }}
|
||||
</p>
|
||||
|
||||
{% if not package.license.is_foss and not package.media_license.is_foss and package.type != package.type.TXP %}
|
||||
{% if not package.license.is_foss and not package.media_license.is_foss %}
|
||||
<p style="color:#f33;">
|
||||
{{ _("<b>Warning:</b> Non-free code and media.") }}
|
||||
</p>
|
||||
{% elif not package.license.is_foss and package.type != package.type.TXP %}
|
||||
{% elif not package.license.is_foss %}
|
||||
<p style="color:#f33;">
|
||||
{{ _("<b>Warning:</b> Non-free code.") }}
|
||||
</p>
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
<form method="post" action="{{ package.getURL("packages.review") }}" class="card-body">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<p>
|
||||
{{ _("Do you recommend this %(type)s?", type=package.type.text | lower) }}
|
||||
{{ _("Do you recommend this?") }}
|
||||
</p>
|
||||
|
||||
<div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
|
@ -145,7 +145,7 @@
|
|||
<form method="post" action="{{ package.getURL("packages.review") }}" class="card-body">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<p>
|
||||
{{ _("Do you recommend this %(type)s?", type=package.type.text | lower) }}
|
||||
{{ _("Do you recommend this?") }}
|
||||
</p>
|
||||
|
||||
<div class="btn-group">
|
||||
|
|
|
@ -14,23 +14,8 @@
|
|||
<h3>{{ _("Games") }}</h3>
|
||||
{{ render_pkggrid(mpackage.packages.filter_by(type="GAME", state="APPROVED").all()) }}
|
||||
|
||||
<h3>{{ _("Mods") }}</h3>
|
||||
{{ render_pkggrid(mpackage.packages.filter_by(type="MOD", state="APPROVED").all()) }}
|
||||
|
||||
{% if similar_topics %}
|
||||
<h3>{{ _("Forum Topics") }}</h3>
|
||||
<ul>
|
||||
{% for t in similar_topics %}
|
||||
<li>
|
||||
[{{ t.type.text }}]
|
||||
<a href="https://forum.minetest.net/viewtopic.php?t={{ t.topic_id }}">
|
||||
{{ _("%(title)s by %(display_name)s", title=t.title, display_name=t.author.display_name) }}
|
||||
</a>
|
||||
{% if t.wip %}[{{ _("WIP") }}]{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<h3>{{ _("Tools") }}</h3>
|
||||
{{ render_pkggrid(mpackage.packages.filter_by(type="TOOL", state="APPROVED").all()) }}
|
||||
|
||||
<h2>{{ _("Required By") }}</h2>
|
||||
{{ render_pkggrid(dependers) }}
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<div class="alert alert-secondary">
|
||||
<a class="float-right btn btn-sm btn-default" href="/help/package_config/#cdbjson">{{ _("Read more") }}</a>
|
||||
|
||||
{{ _("You can include a .cdb.json file in your %(type)s to update these details automatically.", type=package.type.text.lower()) }}
|
||||
{{ _("You can include a .cdb.json file in your projects to update these details automatically.") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
@ -72,7 +72,7 @@
|
|||
{{ render_field(form.name, class_="pkg_meta col-sm-4",
|
||||
readonly=True, hint=_("Please open a thread to request a name change")) }}
|
||||
{% else %}
|
||||
{{ render_field(form.name, class_="pkg_meta col-sm-4", pattern="[a-z0-9_]+", title=_("Lower case letters (a-z), digits (0-9), and underscores (_) only")) }}
|
||||
{{ render_field(form.name, class_="pkg_meta col-sm-4", pattern="[a-zA-Z0-9_\-\.]+", title=_("Lower case letters (a-z), digits (0-9), and underscores (_), dashes and periods only")) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ render_field(form.short_desc, class_="pkg_meta") }}
|
||||
|
@ -113,10 +113,6 @@
|
|||
|
||||
{{ render_field(form.website, class_="pkg_meta") }}
|
||||
{{ render_field(form.issueTracker, class_="pkg_meta") }}
|
||||
{{ render_field_prefix_button(form.forums, class_="pkg_meta",
|
||||
pattern="[0-9]+",
|
||||
prefix="forum.minetest.net/viewtopic.php?t=",
|
||||
placeholder=_("Tip: paste in a forum topic URL")) }}
|
||||
{{ render_field(form.video_url, class_="pkg_meta", hint=_("YouTube videos will be shown in an embed.")) }}
|
||||
</fieldset>
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
{% block headextra %}
|
||||
<meta name="og:title" content="{{ self.title() }}"/>
|
||||
<meta name="og:description" content="{{ _('Mods for %(title)s', title=package.title) }}"/>
|
||||
<meta name="description" content="{{ _('Mods for %(title)s', title=package.title) }}"/>
|
||||
<meta name="og:description" content="{{ _('Tools for %(title)s', title=package.title) }}"/>
|
||||
<meta name="description" content="{{ _('Tools for %(title)s', title=package.title) }}"/>
|
||||
<meta name="og:url" content="{{ package.getURL('packages.game_hub', absolute=True) }}"/>
|
||||
{% if package.getMainScreenshotURL() %}
|
||||
<meta name="og:image" content="{{ package.getMainScreenshotURL(absolute=True) }}"/>
|
||||
|
@ -40,13 +40,13 @@
|
|||
<h2 class="my-3">{{ _("Recently Updated") }}</h2>
|
||||
{{ render_pkggrid(updated) }}
|
||||
|
||||
|
||||
<a href="{{ url_for('packages.list_all', type='mod', sort='score', order='desc', game=package.getId()) }}" class="btn btn-secondary float-right">
|
||||
{% for tag in toplevel %}
|
||||
<a href="{{ url_for('packages.list_all', tag=tag.name, sort='score', order='desc', game=package.getId()) }}" class="btn btn-secondary float-right">
|
||||
{{ _("See more") }}
|
||||
</a>
|
||||
<h2 class="my-3">{{ _("Top Mods") }}</h2>
|
||||
{{ render_pkggrid(pop_mod) }}
|
||||
|
||||
<h2 class="my-3">{{ _("Top " + tag.title) }}</h2>
|
||||
{{ render_pkggrid(popular[tag.name]) }}
|
||||
{% endfor %}
|
||||
|
||||
<a href="{{ url_for('packages.list_all', sort='reviews', order='desc', game=package.getId()) }}" class="btn btn-secondary float-right">
|
||||
{{ _("See more") }}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{% block author_links %}
|
||||
{% if authors %}
|
||||
{% for author in authors %}
|
||||
<a href="{{ url_for('packages.list_all', type=type, author=author[0], q=author[1]) }}">{{ author[0] }}</a>
|
||||
<a href="{{ url_for('packages.list_all', author=author[0], q=author[1]) }}">{{ author[0] }}</a>
|
||||
{% if not loop.last %}
|
||||
,
|
||||
{% endif %}
|
||||
|
|
|
@ -43,25 +43,6 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<h3 class="mt-5">{{ _("Supported Minetest versions") }}</h3>
|
||||
|
||||
<div class="row">
|
||||
{{ render_field(form.min_rel, class_="col-sm-6") }}
|
||||
{{ render_field(form.max_rel, class_="col-sm-6") }}
|
||||
</div>
|
||||
|
||||
<p id="minmax_warning" style="color:#f00; display: none;">
|
||||
{{ _("Maximum must be greater than or equal to the minimum!") }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ _("Set the minimum and maximum Minetest versions supported.
|
||||
This release will be hidden to clients outside of that range. ") }}
|
||||
<br />
|
||||
{{ _("Leave both as None if in doubt.") }}
|
||||
|
||||
{{ _("You can <a href='/help/package_config/'>set this automatically</a> in the .conf of your package.") }}
|
||||
</p>
|
||||
<p class="mt-5">
|
||||
{{ render_submit_field(form.submit) }}
|
||||
</p>
|
||||
|
|
|
@ -58,30 +58,6 @@
|
|||
tips on customising releases.") }}
|
||||
</p>
|
||||
|
||||
<h3 class="mt-5">{{ _("3. Supported Minetest versions") }}</h3>
|
||||
|
||||
<div class="row">
|
||||
{{ render_field(form.min_rel, class_="col-sm-6") }}
|
||||
{{ render_field(form.max_rel, class_="col-sm-6") }}
|
||||
</div>
|
||||
|
||||
|
||||
<p id="minmax_warning" style="color:#f00; display: none;">
|
||||
{{ _("Maximum must be greater than or equal to the minimum!") }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<i class="fas fa-exclamation-circle mr-2"></i>
|
||||
{{ _("The .conf of your package can <a href='/help/package_config/'>set this automatically</a>,
|
||||
which will override your selection.") }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ _("Set the minimum and maximum Minetest versions supported.
|
||||
This release will be hidden to clients outside of that range. ") }}
|
||||
<br />
|
||||
{{ _("Leave both as None if in doubt.") }}
|
||||
</p>
|
||||
<p class="mt-5">
|
||||
{{ render_submit_field(form.submit) }}
|
||||
</p>
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
<h1>{{ self.title() }}</h1>
|
||||
|
||||
<p>
|
||||
{{ _("A release is a single downloadable version of your %(title)s.", title=package.type.text.lower()) }}
|
||||
{{ _("You need to create releases even if you use a rolling release development cycle, as Minetest needs them to check for updates.") }}
|
||||
{{ _("A release is a single downloadable version of your project") }}
|
||||
</p>
|
||||
|
||||
{% if package.repo %}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</div>
|
||||
<div class="card-body">
|
||||
<p>
|
||||
{{ _("Do you recommend this %(type)s?", type=package.type.text | lower) }}
|
||||
{{ _("Do you recommend this?") }}
|
||||
</p>
|
||||
{{ render_toggle_field(form.recommends, icons={"yes":"fa-thumbs-up", "no":"fa-thumbs-down"}) }}
|
||||
|
||||
|
|
|
@ -28,18 +28,4 @@
|
|||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if similar_topics %}
|
||||
<h3>{{ _("Similar Forum Topics") }}</h3>
|
||||
<ul>
|
||||
{% for t in similar_topics %}
|
||||
<li>
|
||||
[{{ t.type.value }}]
|
||||
<a href="https://forum.minetest.net/viewtopic.php?t={{ t.topic_id }}">
|
||||
{{ _("%(title)s by %(display_name)s", title=t.title, display_name=t.author.display_name) }}
|
||||
</a>
|
||||
{% if t.wip %}[{{ _("WIP") }}]{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -37,37 +37,8 @@
|
|||
{{ _("Download") }}
|
||||
</div>
|
||||
|
||||
{% if release and (release.min_rel or release.max_rel) %}
|
||||
<small class="count display-block">
|
||||
{% if release.min_rel and release.max_rel %}
|
||||
{{ _("Minetest %(min)s - %(max)s", min=release.min_rel.name, max=release.max_rel.name) }}
|
||||
{% elif release.min_rel %}
|
||||
{{ _("For Minetest %(min)s and above", min=release.min_rel.name) }}
|
||||
{% elif release.max_rel %}
|
||||
{{ _("Minetest %(max)s and below", max=release.max_rel.name) }}
|
||||
{% endif %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
{% if package.type == package.type.MOD %}
|
||||
{% set installing_url = "https://wiki.minetest.net/Installing_Mods" %}
|
||||
{% elif package.type == package.type.GAME %}
|
||||
{% set installing_url = "https://wiki.minetest.net/Games#Installing_games" %}
|
||||
{% elif package.type == package.type.TXP %}
|
||||
{% set installing_url = "https://wiki.minetest.net/Installing_Texture_Packs" %}
|
||||
{% else %}
|
||||
{{ 0 / 0 }}
|
||||
{% endif %}
|
||||
|
||||
<p class="text-center mt-1 mb-4">
|
||||
<a href="{{ installing_url }}">
|
||||
<small>
|
||||
<i class="fas fa-question-circle mr-1"></i>
|
||||
{{ _("How do I install this?") }}
|
||||
</small>
|
||||
</a>
|
||||
</p>
|
||||
<p></p>
|
||||
{% else %}
|
||||
<i>
|
||||
{{ _("No downloads available") }}
|
||||
|
@ -76,9 +47,9 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block container %}
|
||||
{% if not package.license.is_foss and not package.media_license.is_foss and package.type != package.type.TXP %}
|
||||
{% if not package.license.is_foss and not package.media_license.is_foss %}
|
||||
{% set package_warning=_("Non-free code and media") %}
|
||||
{% elif not package.license.is_foss and package.type != package.type.TXP %}
|
||||
{% elif not package.license.is_foss %}
|
||||
{% set package_warning=_("Non-free code") %}
|
||||
{% elif not package.media_license.is_foss %}
|
||||
{% set package_warning=_("Non-free media") %}
|
||||
|
@ -170,7 +141,7 @@
|
|||
<span class="count">{{ package.downloads }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a class="btn" href="{{ url_for('threads.list_all', pid=package.id) }}" title="{{ _("Threads") }}">
|
||||
<a class="btn" href="{{ url_for('threads.list_all', pid=package.id) }}" title="{{ _("Discussion") }}">
|
||||
<i class="fas fa-comment-alt"></i>
|
||||
<span class="count">{{ threads | length }}</span>
|
||||
</a>
|
||||
|
@ -194,12 +165,6 @@
|
|||
<span class="count">{{ _("Source") }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if package.forums %}
|
||||
<a class="btn" href="https://forum.minetest.net/viewtopic.php?t={{ package.forums }}">
|
||||
<i class="fas fa-comments"></i>
|
||||
<span class="count">{{ _("Forums") }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if package.issueTracker %}
|
||||
<a class="btn" href="{{ package.issueTracker }}">
|
||||
<i class="fas fa-bug"></i>
|
||||
|
@ -265,7 +230,7 @@
|
|||
{% for ss in screenshots %}
|
||||
{% if ss.approved or package.checkPerm(current_user, "ADD_SCREENSHOTS") %}
|
||||
<li>
|
||||
<a href="{{ ss.url }}" class="gallery-image">
|
||||
<a href="{{ss.url}}" class="gallery-image" data-toggle="modal" data-target="#screenshot_{{ss.id}}">
|
||||
<img src="{{ ss.getThumbnailURL() }}" alt="{{ ss.title }}" />
|
||||
{% if not ss.approved %}
|
||||
<span class="badge bg-dark badge-tr">{{ _("Awaiting review") }}</span>
|
||||
|
@ -290,6 +255,25 @@
|
|||
</article>
|
||||
{% endif %}
|
||||
|
||||
{% if package.build_desc %}
|
||||
<article class="markdown panel mb-5">
|
||||
{{ package.build_desc | markdown }}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
{% if package.install_desc %}
|
||||
<div class="card mt-0 mb-4">
|
||||
<div class="card-header">
|
||||
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#install_desc" aria-expanded="true" aria-controls="install_desc">
|
||||
{{ _("Installation Instructions") }}
|
||||
</button>
|
||||
</div>
|
||||
<article class="markdown panel mb-5 collapse" id="install_desc">
|
||||
{{ package.install_desc | markdown }}
|
||||
</article>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h2 id="reviews" class="mt-0">{{ _("Reviews") }}</h2>
|
||||
|
||||
{% from "macros/reviews.html" import render_reviews, render_review_form, render_review_preview with context %}
|
||||
|
@ -323,12 +307,10 @@
|
|||
{{ render_pkggrid(packages_uses) }}
|
||||
{% endif %}
|
||||
|
||||
{% if package.type == package.type.GAME %}
|
||||
<h2>{{ _("Content") }}</h2>
|
||||
<a href="{{ package.getURL('packages.game_hub') }}" class="btn btn-lg btn-primary">
|
||||
{{ _("View content for game") }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<h2>{{ _("Content") }}</h2>
|
||||
<a href="{{ package.getURL('packages.game_hub') }}" class="btn btn-lg btn-primary">
|
||||
{{ _("View content for game") }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<aside class="col-md-3 info-sidebar">
|
||||
|
@ -378,92 +360,81 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if package.type == package.type.GAME %}
|
||||
<a href="{{ package.getURL('packages.game_hub') }}" class="btn btn-lg btn-block mb-4 btn-primary">
|
||||
{{ _("View content for game") }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{{ package.getURL('packages.game_hub') }}" class="btn btn-lg btn-block mb-4 btn-primary">
|
||||
{{ _("View content for game") }}
|
||||
</a>
|
||||
<h3>{{ _("Dependencies") }}</h3>
|
||||
<dl>
|
||||
<dt>{{ _("Required") }}</dt>
|
||||
<dd>
|
||||
{% for dep in package.getSortedHardDependencies() %}
|
||||
{%- if dep.package %}
|
||||
<a class="badge badge-primary"
|
||||
href="{{ dep.package.getURL("packages.view") }}">
|
||||
{{ _("%(title)s by %(display_name)s",
|
||||
title=dep.package.title, display_name=dep.package.author.display_name) }}
|
||||
</a>
|
||||
{% elif dep.meta_package %}
|
||||
<a class="badge badge-primary"
|
||||
href="{{ url_for('metapackages.view', name=dep.meta_package.name) }}">
|
||||
{{ dep.meta_package.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ "Expected package or meta_package in dep!" | throw }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ _("No required dependencies") }}
|
||||
{% endfor %}
|
||||
</dd>
|
||||
|
||||
{% if package.type != package.type.TXP %}
|
||||
<h3>{{ _("Dependencies") }}</h3>
|
||||
<dl>
|
||||
<dt>{{ _("Required") }}</dt>
|
||||
{% set optional_deps=package.getSortedOptionalDependencies() %}
|
||||
{% if optional_deps %}
|
||||
<dt>{{ _("Optional") }}</dt>
|
||||
<dd>
|
||||
{% for dep in package.getSortedHardDependencies() %}
|
||||
{% for dep in optional_deps %}
|
||||
{%- if dep.package %}
|
||||
<a class="badge badge-primary"
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ dep.package.getURL("packages.view") }}">
|
||||
{{ _("%(title)s by %(display_name)s",
|
||||
title=dep.package.title, display_name=dep.package.author.display_name) }}
|
||||
</a>
|
||||
{% elif dep.meta_package %}
|
||||
<a class="badge badge-primary"
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ url_for('metapackages.view', name=dep.meta_package.name) }}">
|
||||
{{ dep.meta_package.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ "Expected package or meta_package in dep!" | throw }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ _("No required dependencies") }}
|
||||
{% endif %}</a>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
|
||||
{% set optional_deps=package.getSortedOptionalDependencies() %}
|
||||
{% if optional_deps %}
|
||||
<dt>{{ _("Optional") }}</dt>
|
||||
<dd>
|
||||
{% for dep in optional_deps %}
|
||||
{%- if dep.package %}
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ dep.package.getURL("packages.view") }}">
|
||||
{{ _("%(title)s by %(display_name)s",
|
||||
title=dep.package.title, display_name=dep.package.author.display_name) }}
|
||||
{% elif dep.meta_package %}
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ url_for('metapackages.view', name=dep.meta_package.name) }}">
|
||||
{{ dep.meta_package.name }}
|
||||
{% else %}
|
||||
{{ "Expected package or meta_package in dep!" | throw }}
|
||||
{% endif %}</a>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
{% endif %}
|
||||
|
||||
{% if package.type == package.type.MOD %}
|
||||
<h3>{{ _("Compatible Games") }}</h3>
|
||||
{% for support in package.getSortedSupportedGames() %}
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ support.game.getURL('packages.view') }}">
|
||||
{{ _("%(title)s by %(display_name)s",
|
||||
title=support.game.title, display_name=support.game.author.display_name) }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ _("No specific game is required") }}
|
||||
{% endfor %}
|
||||
<p class="text-muted small mt-2 mb-0">
|
||||
{{ _("This is an experimental feature.") }}
|
||||
{{ _("Supported games are determined by an algorithm, and may not be correct.") }}
|
||||
</p>
|
||||
{% endif %}
|
||||
<h3>{{ _("Compatible Games") }}</h3>
|
||||
{% for support in package.getSortedSupportedGames() %}
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ support.game.getURL('packages.view') }}">
|
||||
{{ _("%(title)s by %(display_name)s",
|
||||
title=support.game.title, display_name=support.game.author.display_name) }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ _("No specific game is required") }}
|
||||
{% endfor %}
|
||||
<p class="text-muted small mt-2 mb-0">
|
||||
{{ _("This is an experimental feature.") }}
|
||||
{{ _("Supported games are determined by an algorithm, and may not be correct.") }}
|
||||
</p>
|
||||
|
||||
<h3>
|
||||
{{ _("Information") }}
|
||||
</h3>
|
||||
|
||||
<dl>
|
||||
<dt>{{ _("Type") }}</dt>
|
||||
<dd>{{ package.type.text }}</dd>
|
||||
<dt>{{ _("Technical Name") }}</dt>
|
||||
<dd>{{ package.name }}</dd>
|
||||
<dt>{{ _("License") }}</dt>
|
||||
<dd>
|
||||
{% if package.license == package.media_license %}
|
||||
{{ render_license(package.license) }}
|
||||
{% elif package.type == package.type.TXP %}
|
||||
{{ render_license(package.media_license) }}
|
||||
{% else %}
|
||||
{{ _("%(code_license)s for code,<br>%(media_license)s for media.",
|
||||
code_license=render_license(package.license), media_license=render_license(package.media_license)) }}
|
||||
|
@ -526,7 +497,7 @@
|
|||
<a class="btn btn-primary btn-sm mx-1" href="{{ url_for('threads.new', pid=package.id) }}"><i class="fas fa-plus"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ _("Threads") }}
|
||||
{{ _("Discussion") }}
|
||||
</h3>
|
||||
<div class="list-group">
|
||||
{% from "macros/threads.html" import render_compact_threadlist %}
|
||||
|
@ -552,5 +523,25 @@
|
|||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
{% for ss in screenshots %}
|
||||
<div class="modal fade" id="screenshot_{{ss.id}}" tabindex="-1" aria-labelledby="screenshot" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="screenshot title">"{{ ss.title }}"</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<img src="{{ ss.url }}" alt="{{ ss.title }}" style="width: 100%"/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{{ _("Threads") }}
|
||||
{{ _("Discussion") }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
<div class="btn-group btn-group-sm mr-2">
|
||||
{% if is_mtm_only %}
|
||||
<a class="btn btn-sm btn-primary active" href="{{ url_set_query(mtm=0) }}">
|
||||
{{ _("Minetest-Mods org only") }}
|
||||
{{ _("Minetest-Tools org only") }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="btn btn-sm btn-secondary" href="{{ url_set_query(mtm=1) }}">
|
||||
{{ _("Minetest-Mods org only") }}
|
||||
{{ _("Minetest-Tools org only") }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -29,12 +29,6 @@
|
|||
{{ _("Package Tags") }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if current_tab == "topics" %}active{% endif %}"
|
||||
href="{{ url_for('todo.topics') }}">
|
||||
{{ _("Forum Topics") }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<i class="fab fa-github mr-1"></i>
|
||||
{{ _("GitHub") }}
|
||||
</a>
|
||||
<a class="btn btn-secondary" href="{{ url_for('users.claim') }}">
|
||||
<a class="btn btn-secondary" href="{{ url_for('users.register') }}">
|
||||
<i class="fas fa-user-plus mr-1"></i>
|
||||
{{ _("Register") }}
|
||||
</a>
|
||||
|
|
|
@ -161,13 +161,6 @@
|
|||
<p>
|
||||
{{ medal.description }}
|
||||
</p>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
style="width: {{ [100 * medal.progress[0] / medal.progress[1], 100] | min }}%;"
|
||||
aria-valuenow="{{ medal.progress[0] }}" aria-valuemin="0" aria-valuemax="{{ medal.progress[1] }}">
|
||||
{{ _("%(value)d / %(target)d", value=medal.progress[0], target=medal.progress[1]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</p>
|
||||
|
||||
<p>
|
||||
<img src="/static/puzzle.png" />
|
||||
<img src="{{captcha}}" />
|
||||
</p>
|
||||
{{ render_field(form.question, hint=_("Please prove that you are human")) }}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from typing import List, Tuple, Optional
|
||||
|
||||
from app.default_data import populate_test_data
|
||||
from app.models import db, License, PackageType, User, Package, PackageState, PackageRelease, MinetestRelease
|
||||
from app.models import db, License, User, Package, PackageState, PackageRelease, MinetestRelease
|
||||
from .utils import parse_json, validate_package_list
|
||||
from .utils import client # noqa
|
||||
|
||||
|
@ -10,36 +10,36 @@ def make_package(name: str, versions: List[Tuple[Optional[str], Optional[str]]])
|
|||
license = License.query.filter_by(name="MIT").first()
|
||||
author = User.query.first()
|
||||
|
||||
mod = Package()
|
||||
mod.state = PackageState.APPROVED
|
||||
mod.name = name.lower()
|
||||
mod.title = name
|
||||
mod.license = license
|
||||
mod.media_license = license
|
||||
mod.type = PackageType.MOD
|
||||
mod.author = author
|
||||
mod.short_desc = "The content library should not be used yet as it is still in alpha"
|
||||
mod.desc = "This is the long desc"
|
||||
db.session.add(mod)
|
||||
tool = Package()
|
||||
tool.state = PackageState.APPROVED
|
||||
tool.name = name.lower()
|
||||
tool.title = name
|
||||
tool.license = license
|
||||
tool.media_license = license
|
||||
# tool.type = PackageType.TOOL
|
||||
tool.author = author
|
||||
tool.short_desc = "The content library should not be used yet as it is still in alpha"
|
||||
tool.desc = "This is the long desc"
|
||||
db.session.add(tool)
|
||||
|
||||
rels = []
|
||||
# rels = []
|
||||
|
||||
for (minv, maxv) in versions:
|
||||
rel = PackageRelease()
|
||||
rel.package = mod
|
||||
rel.title = "test"
|
||||
rel.url = "https://github.com/ezhh/handholds/archive/master.zip"
|
||||
# for (minv, maxv) in versions:
|
||||
# rel = PackageRelease()
|
||||
# rel.package = tool
|
||||
# rel.title = "test"
|
||||
# rel.url = "https://github.com/ezhh/handholds/archive/master.zip"
|
||||
|
||||
if minv:
|
||||
rel.min_rel = MinetestRelease.query.filter_by(name=minv).first()
|
||||
assert rel.min_rel
|
||||
if maxv:
|
||||
rel.max_rel = MinetestRelease.query.filter_by(name=maxv).first()
|
||||
assert rel.max_rel
|
||||
# # if minv:
|
||||
# # rel.min_rel = MinetestRelease.query.filter_by(name=minv).first()
|
||||
# # assert rel.min_rel
|
||||
# # if maxv:
|
||||
# # rel.max_rel = MinetestRelease.query.filter_by(name=maxv).first()
|
||||
# # assert rel.max_rel
|
||||
|
||||
rel.approved = True
|
||||
db.session.add(rel)
|
||||
rels.append(rel)
|
||||
# rel.approved = True
|
||||
# db.session.add(rel)
|
||||
# rels.append(rel)
|
||||
|
||||
db.session.flush()
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ from .user import *
|
|||
|
||||
YESES = ["yes", "true", "1", "on"]
|
||||
|
||||
def get_toplevel_tags():
|
||||
return Tag.query.filter_by(is_toplevel=True).order_by(db.asc(Tag.id)).all()
|
||||
|
||||
|
||||
def is_username_valid(username):
|
||||
return username is not None and len(username) >= 2 and re.match(r"^[A-Za-z0-9._-]*$", username)
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
badLicenses = [
|
||||
'LicenseRef-proprietary',
|
||||
'LicenseRef-Proprietary',
|
||||
'proprietary',
|
||||
'Proprietary',
|
||||
'CC-BY-NC-SA-3.0',
|
||||
'CC-BY-NC-ND-3.0'
|
||||
]
|
||||
|
||||
badCategories = [
|
||||
'Emulator',
|
||||
'PackageManager',
|
||||
'System',
|
||||
'Utility'
|
||||
]
|
||||
|
||||
nonFreeAssets = [
|
||||
'jp.yvt.OpenSpades',
|
||||
'net.openra.OpenRA',
|
||||
'org.openmw.OpenMW',
|
||||
'org.zdoom.GZDoom',
|
||||
'io.github.ezQuake',
|
||||
'com.etlegacy.ETLegacy',
|
||||
'com.github.iortcw.iortcw',
|
||||
'org.yamagi.YamagiQ2',
|
||||
'org.dhewm3.Dhewm3',
|
||||
'com.github.bvschaik.julius',
|
||||
'io.openrct2.OpenRCT2',
|
||||
'com.github.skullernet.q2pro',
|
||||
'org.raceintospace.Raceintospace',
|
||||
'org.srb2.SRB2',
|
||||
'org.srb2.SRB2Kart',
|
||||
'io.sourceforge.clonekeenplus',
|
||||
'io.github.fabiangreffrath.Doom',
|
||||
'net.dengine.Doomsday',
|
||||
'com.github.keriew.augustus',
|
||||
'io.github.yairm210.unciv',
|
||||
'com.corsixth.corsixth'
|
||||
]
|
||||
|
||||
nonFreeNetworkServices = [
|
||||
'io.github.yairm210.unciv'
|
||||
]
|
||||
|
||||
alwaysAccept = [
|
||||
'org.freecol.FreeCol',
|
||||
'org.freeciv.Freeciv',
|
||||
'io.github.EndlessSky.endless-sky',
|
||||
'org.kde.ksudoku',
|
||||
'net.veloren.veloren'
|
||||
]
|
||||
|
||||
alwaysDeny = [
|
||||
'com.moonlight_stream.Moonlight',
|
||||
'org.gnome.Games',
|
||||
'org.ppsspp.PPSSPP',
|
||||
'org.scummvm.ScummVM',
|
||||
'org.pegasus_frontend.Pegasus',
|
||||
'com.gitlab.coringao.cavestory-nx',
|
||||
'org.sauerbraten.Sauerbraten',
|
||||
'net.runelite.RuneLite',
|
||||
'com.zandronum.Zandronum',
|
||||
'io.mrarm.mcpelauncher',
|
||||
'org.unitystation.StationHub',
|
||||
'org.firestormviewer.FirestormViewer',
|
||||
'com.eduke32.EDuke32',
|
||||
'io.github.hmlendea.geforcenow-electron',
|
||||
'io.gdevs.GDLauncher',
|
||||
'io.github.sharkwouter.Minigalaxy',
|
||||
'com.katawa_shoujo.KatawaShoujo',
|
||||
're.chiaki.Chiaki'
|
||||
]
|
|
@ -18,7 +18,7 @@
|
|||
from functools import wraps
|
||||
from flask import abort, redirect, url_for, request
|
||||
from flask_login import current_user
|
||||
from app.models import User, NotificationType, Package, UserRank, Notification, db, AuditSeverity, AuditLogEntry, ThreadReply, Thread, PackageState, PackageType, PackageAlias
|
||||
from app.models import User, NotificationType, Package, UserRank, Notification, db, AuditSeverity, AuditLogEntry, ThreadReply, Thread, PackageState, PackageAlias
|
||||
|
||||
|
||||
def getPackageByInfo(author, name):
|
||||
|
@ -45,7 +45,7 @@ def is_package_page(f):
|
|||
package = getPackageByInfo(author, name)
|
||||
if package is None:
|
||||
package = getPackageByInfo(author, name + "_game")
|
||||
if package and package.type == PackageType.GAME:
|
||||
if package:
|
||||
args = dict(kwargs)
|
||||
args["name"] = name + "_game"
|
||||
return redirect(url_for(request.endpoint, **args))
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
version: '3'
|
||||
|
||||
|
||||
services:
|
||||
db:
|
||||
image: "postgres:14.1"
|
||||
volumes:
|
||||
- "./data/db:/var/lib/postgresql/data"
|
||||
environment: &env
|
||||
- POSTGRES_USER=contentdb
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=contentdb
|
||||
- FLASK_DEBUG=1
|
||||
- FLASK_CONFIG=../config.cfg
|
||||
adminer:
|
||||
image: adminer
|
||||
restart: always
|
||||
environment:
|
||||
- ADMINER_DEFAULT_SERVER=db
|
||||
- ADMINER_DESIGN=pepa-linha
|
||||
ports:
|
||||
- 8081:8080
|
||||
|
||||
redis:
|
||||
image: 'redis:6.2-alpine'
|
||||
command: redis-server
|
||||
volumes:
|
||||
- './data/redis:/data'
|
||||
|
||||
redis-commander:
|
||||
container_name: redis-commander
|
||||
hostname: redis-commander
|
||||
image: rediscommander/redis-commander:latest
|
||||
restart: always
|
||||
environment:
|
||||
- REDIS_HOSTS=local:redis:6379
|
||||
ports:
|
||||
- "8082:8081"
|
||||
|
||||
app:
|
||||
build: .
|
||||
container_name: contentdb_app_1
|
||||
command: ./utils/entrypoint.sh
|
||||
environment: *env
|
||||
ports:
|
||||
- 5123:5123
|
||||
volumes:
|
||||
- "./data/uploads:/var/cdb/uploads"
|
||||
- "./app:/source/app"
|
||||
- "./migrations:/source/migrations"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
|
||||
worker:
|
||||
build: .
|
||||
command: celery -A app.tasks.celery worker --concurrency 1
|
||||
environment: *env
|
||||
volumes:
|
||||
- "./data/uploads:/var/cdb/uploads"
|
||||
- "./app:/home/cdb/app"
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
beat:
|
||||
build: .
|
||||
command: celery -A app.tasks.celery beat
|
||||
environment: *env
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
exporter:
|
||||
image: ovalmoney/celery-exporter
|
||||
environment: *env
|
||||
ports:
|
||||
- 5125:9540
|
||||
depends_on:
|
||||
- redis
|
|
@ -1,11 +1,17 @@
|
|||
version: '3'
|
||||
|
||||
|
||||
services:
|
||||
db:
|
||||
image: "postgres:14.1"
|
||||
volumes:
|
||||
- "./data/db:/var/lib/postgresql/data"
|
||||
env_file:
|
||||
- config.env
|
||||
environment: &env
|
||||
- POSTGRES_USER=contentdb
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=contentdb
|
||||
- FLASK_DEBUG=0
|
||||
- FLASK_CONFIG=../config.cfg
|
||||
|
||||
redis:
|
||||
image: 'redis:6.2-alpine'
|
||||
|
@ -15,9 +21,9 @@ services:
|
|||
|
||||
app:
|
||||
build: .
|
||||
container_name: contentdb_app_1
|
||||
command: ./utils/entrypoint.sh
|
||||
env_file:
|
||||
- config.env
|
||||
environment: *env
|
||||
ports:
|
||||
- 5123:5123
|
||||
volumes:
|
||||
|
@ -31,10 +37,7 @@ services:
|
|||
worker:
|
||||
build: .
|
||||
command: celery -A app.tasks.celery worker --concurrency 1
|
||||
env_file:
|
||||
- config.env
|
||||
environment:
|
||||
- FLASK_CONFIG=../config.cfg
|
||||
environment: *env
|
||||
volumes:
|
||||
- "./data/uploads:/var/cdb/uploads"
|
||||
- "./app:/home/cdb/app"
|
||||
|
@ -44,17 +47,13 @@ services:
|
|||
beat:
|
||||
build: .
|
||||
command: celery -A app.tasks.celery beat
|
||||
env_file:
|
||||
- config.env
|
||||
environment:
|
||||
- FLASK_CONFIG=../config.cfg
|
||||
environment: *env
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
exporter:
|
||||
image: ovalmoney/celery-exporter
|
||||
env_file:
|
||||
- config.env
|
||||
environment: *env
|
||||
ports:
|
||||
- 5125:9540
|
||||
depends_on:
|
||||
|
|
|
@ -13,7 +13,7 @@ The query arguments will include a list of supported types, the current
|
|||
and any hidden [Content Flags](https://content.minetest.net/help/content_flags/).
|
||||
|
||||
Example URL:
|
||||
<https://content.minetest.net/api/packages/?type=mod&type=game&type=txp&protocol_version=39&engine_version=5.3.0&hide=nonfree&hide=desktop_default>
|
||||
<https://content.minetest.net/api/packages/?type=tool&type=game&type=asset_pack&protocol_version=39&engine_version=5.3.0&hide=nonfree&hide=desktop_default>
|
||||
|
||||
Example response:
|
||||
|
||||
|
@ -33,7 +33,7 @@ Example response:
|
|||
|
||||
`thumbnail` is optional, but all other fields are required.
|
||||
|
||||
`type` is one of `mod`, `game`, or `txp`.
|
||||
`type` is one of `tool`, `game`, or `asset_pack`.
|
||||
|
||||
`release` is the release ID. Newer releases have higher IDs.
|
||||
Minetest compares this ID to a locally stored version to detect whether a package has updates.
|
||||
|
@ -67,7 +67,7 @@ dependencies for a package.
|
|||
|
||||
Then, it resolves each dependency recursively.
|
||||
|
||||
Say you're resolving for `basic_materials`, then it will attempt to find the mod in this order:
|
||||
Say you're resolving for `basic_materials`, then it will attempt to find the tool in this order:
|
||||
|
||||
1. It first checks installed mods in the game and mods folder (ie: `mods/basic_materials/`)
|
||||
2. Then it looks on ContentDB for exact name matches (ie: `VanessaE/basic_materials`)
|
||||
|
@ -77,7 +77,7 @@ Say you're resolving for `basic_materials`, then it will attempt to find the mod
|
|||
### Long version
|
||||
|
||||
When installing a package, an API request is made to ContentDB to find out the dependencies.
|
||||
If there are no dependencies, then the mod is installed straight away.
|
||||
If there are no dependencies, then the tool is installed straight away.
|
||||
|
||||
If there are dependencies, it will resolve them and show a dialog with a list of mods to install.
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 011e42c52d21
|
||||
Revises: 6e57b2b4dcdf
|
||||
Create Date: 2022-01-25 18:48:46.367409
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '011e42c52d21'
|
||||
down_revision = '6e57b2b4dcdf'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('package', sa.Column('video_url', sa.String(length=200), nullable=True))
|
||||
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('package', 'video_url')
|
|
@ -1,29 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 019da77ba02d
|
||||
Revises: 4f2e19bc2a27
|
||||
Create Date: 2020-07-09 04:07:23.926213
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import datetime
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '019da77ba02d'
|
||||
down_revision = '4f2e19bc2a27'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('package_review', sa.Column('created_at', sa.DateTime(), nullable=False, server_default=datetime.datetime.utcnow().isoformat()))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('package_review', 'created_at')
|
||||
# ### end Alembic commands ###
|
|
@ -1,35 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 06d23947e7ef
|
||||
Revises: 5d7233cf8a00
|
||||
Create Date: 2020-12-05 20:30:12.166357
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '06d23947e7ef'
|
||||
down_revision = '5d7233cf8a00'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('email_subscription',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('email', sa.String(length=100), nullable=False),
|
||||
sa.Column('blacklisted', sa.Boolean(), nullable=False),
|
||||
sa.Column('token', sa.String(length=32), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('email')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('email_subscription')
|
||||
# ### end Alembic commands ###
|
|
@ -1,30 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 105d4c740ad6
|
||||
Revises: 886c92dc6eaa
|
||||
Create Date: 2020-12-15 17:28:56.559801
|
||||
|
||||
"""
|
||||
import datetime
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
from sqlalchemy import orm
|
||||
from app.models import User, UserRank
|
||||
|
||||
revision = '105d4c740ad6'
|
||||
down_revision = '886c92dc6eaa'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.execute("COMMIT")
|
||||
op.execute("ALTER TYPE userrank ADD VALUE 'BOT' AFTER 'EDITOR'")
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
|
@ -1,28 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 11b6ef362f98
|
||||
Revises: 9fc23495713b
|
||||
Create Date: 2018-07-04 01:01:45.440662
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '11b6ef362f98'
|
||||
down_revision = '9fc23495713b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('package', sa.Column('score', sa.Float(), nullable=False, server_default="0.0"))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('package', 'score')
|
||||
# ### end Alembic commands ###
|
|
@ -1,28 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 13113e5710da
|
||||
Revises: ead35f7d446c
|
||||
Create Date: 2018-05-23 20:18:07.606646
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '13113e5710da'
|
||||
down_revision = 'ead35f7d446c'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('package', sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('package', 'created_at')
|
||||
# ### end Alembic commands ###
|
|
@ -1,27 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 17b303f33f68
|
||||
Revises: 96a01fe23389
|
||||
Create Date: 2021-12-20 19:48:58.571336
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '17b303f33f68'
|
||||
down_revision = '96a01fe23389'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
status = postgresql.ENUM('WIP', 'BETA', 'ACTIVELY_DEVELOPED', 'MAINTENANCE_ONLY', 'AS_IS', 'DEPRECATED', 'LOOKING_FOR_MAINTAINER', name='packagedevstate')
|
||||
status.create(op.get_bind())
|
||||
|
||||
op.add_column('package', sa.Column('dev_state', sa.Enum('WIP', 'BETA', 'ACTIVELY_DEVELOPED', 'MAINTENANCE_ONLY', 'AS_IS', 'DEPRECATED', 'LOOKING_FOR_MAINTAINER', name='packagedevstate'), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('package', 'dev_state')
|
|
@ -1,25 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 1af840af0209
|
||||
Revises: 725ff70ea316
|
||||
Create Date: 2021-08-16 17:17:12.060257
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1af840af0209'
|
||||
down_revision = '725ff70ea316'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.execute("COMMIT")
|
||||
op.execute("ALTER TYPE userrank ADD VALUE 'APPROVER' BEFORE 'EDITOR'")
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
|
@ -1,25 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 28a427cbd4cf
|
||||
Revises: e9f534df23a8
|
||||
Create Date: 2018-06-03 01:47:33.006039
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '28a427cbd4cf'
|
||||
down_revision = 'e9f534df23a8'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
|
@ -1,35 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 2f3c3597c78d
|
||||
Revises: 9ec17b558413
|
||||
Create Date: 2019-01-29 02:43:08.865695
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy_searchable import sync_trigger
|
||||
from sqlalchemy_utils.types import TSVectorType
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2f3c3597c78d'
|
||||
down_revision = '9ec17b558413'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('package', 'shortDesc', nullable=False, new_column_name='short_desc')
|
||||
op.add_column('package', sa.Column('search_vector', TSVectorType("title", "short_desc", "desc"), nullable=True))
|
||||
op.create_index('ix_package_search_vector', 'package', ['search_vector'], unique=False, postgresql_using='gin')
|
||||
|
||||
conn = op.get_bind()
|
||||
sync_trigger(conn, 'package', 'search_vector', ["title", "short_desc", "desc"])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index('ix_package_search_vector', table_name='package')
|
||||
op.drop_column('package', 'search_vector')
|
||||
# ### end Alembic commands ###
|
|
@ -1,24 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 306ce331a2a7
|
||||
Revises: 6dca6eceb04d
|
||||
Create Date: 2020-01-18 23:00:40.487425
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '306ce331a2a7'
|
||||
down_revision = '6dca6eceb04d'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
conn = op.get_bind()
|
||||
op.create_check_constraint("CK_approval_valid", "package_release", "not approved OR (task_id IS NULL AND NOT url = '')")
|
||||
|
||||
|
||||
def downgrade():
|
||||
conn = op.get_bind()
|
||||
op.drop_constraint("CK_approval_valid", "package_release", type_="check")
|
|
@ -1,54 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 3710e5fbbe87
|
||||
Revises: f6ef5f35abca
|
||||
Create Date: 2022-01-27 18:50:11.705061
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3710e5fbbe87'
|
||||
down_revision = 'f6ef5f35abca'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
command = """
|
||||
CREATE OR REPLACE FUNCTION parse_websearch(config regconfig, search_query text)
|
||||
RETURNS tsquery AS $$
|
||||
SELECT
|
||||
string_agg(
|
||||
(
|
||||
CASE
|
||||
WHEN position('''' IN words.word) > 0 THEN CONCAT(words.word, ':*')
|
||||
ELSE words.word
|
||||
END
|
||||
),
|
||||
' '
|
||||
)::tsquery
|
||||
FROM (
|
||||
SELECT trim(
|
||||
regexp_split_to_table(
|
||||
websearch_to_tsquery(config, lower(search_query))::text,
|
||||
' '
|
||||
)
|
||||
) AS word
|
||||
) AS words
|
||||
$$ LANGUAGE SQL IMMUTABLE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION parse_websearch(search_query text)
|
||||
RETURNS tsquery AS $$
|
||||
SELECT parse_websearch('pg_catalog.simple', search_query);
|
||||
$$ LANGUAGE SQL IMMUTABLE;"""
|
||||
|
||||
op.execute(command)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.execute('DROP FUNCTION public.parse_websearch(regconfig, text);')
|
||||
op.execute('DROP FUNCTION public.parse_websearch(text);')
|
|
@ -1,28 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 3a24fc02365e
|
||||
Revises: b370c3eb4227
|
||||
Create Date: 2020-07-17 20:58:31.130449
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3a24fc02365e'
|
||||
down_revision = 'b370c3eb4227'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('tag', sa.Column('description', sa.String(length=500), nullable=True, server_default=None))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('tag', 'description')
|
||||
# ### end Alembic commands ###
|
|
@ -1,28 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 3f4d7cd8401f
|
||||
Revises: 13113e5710da
|
||||
Create Date: 2018-05-25 17:53:13.215127
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3f4d7cd8401f'
|
||||
down_revision = '13113e5710da'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
conn = op.get_bind()
|
||||
conn.execute("ALTER TYPE packagepropertykey ADD VALUE 'harddeps'")
|
||||
conn.execute("ALTER TYPE packagepropertykey ADD VALUE 'softdeps'")
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
|
@ -1,37 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 3f5836a3df5c
|
||||
Revises: b3c7ff6655af
|
||||
Create Date: 2020-12-04 22:30:33.420071
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3f5836a3df5c'
|
||||
down_revision = 'b3c7ff6655af'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.alter_column('user', 'password',
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
nullable=True,
|
||||
existing_server_default=sa.text("''::character varying"))
|
||||
|
||||
op.execute("""
|
||||
UPDATE "user" SET password=NULL WHERE password=''
|
||||
""")
|
||||
op.create_check_constraint("CK_password", "user",
|
||||
"password IS NULL OR password != ''")
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_constraint("CK_password", "user", type_="check")
|
||||
op.alter_column('user', 'password',
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
nullable=False,
|
||||
existing_server_default=sa.text("''::character varying"))
|
|
@ -1,28 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 43dc7dbf64c8
|
||||
Revises: c1ea65e2b492
|
||||
Create Date: 2020-12-09 19:06:11.891807
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '43dc7dbf64c8'
|
||||
down_revision = 'c1ea65e2b492'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.alter_column('audit_log_entry', 'causer_id',
|
||||
existing_type=sa.INTEGER(),
|
||||
nullable=True)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.alter_column('audit_log_entry', 'causer_id',
|
||||
existing_type=sa.INTEGER(),
|
||||
nullable=False)
|
|
@ -1,28 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 44e138485931
|
||||
Revises: 9e2ac631efb0
|
||||
Create Date: 2018-07-28 14:45:28.879331
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '44e138485931'
|
||||
down_revision = '9e2ac631efb0'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('package_release', sa.Column('commit_hash', sa.String(length=41), nullable=True, server_default=None))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('package_release', 'commit_hash')
|
||||
# ### end Alembic commands ###
|
|
@ -1,24 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 4585ce5147b8
|
||||
Revises: 105d4c740ad6
|
||||
Create Date: 2020-12-15 21:35:18.982716
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4585ce5147b8'
|
||||
down_revision = '105d4c740ad6'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('package_update_config', sa.Column('outdated', sa.Boolean(), nullable=False, server_default="false"))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('package_update_config', 'outdated')
|
|
@ -1,39 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 4e482c47e519
|
||||
Revises: 900758871713
|
||||
Create Date: 2018-05-27 22:38:16.507155
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4e482c47e519'
|
||||
down_revision = '900758871713'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('dependency',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('depender_id', sa.Integer(), nullable=True),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('meta_package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('optional', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['depender_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['meta_package_id'], ['meta_package.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('depender_id', 'package_id', 'meta_package_id', name='_dependency_uc')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('dependency')
|
||||
# ### end Alembic commands ###
|
|
@ -1,40 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 4f2e19bc2a27
|
||||
Revises: dd27f1311a90
|
||||
Create Date: 2020-07-09 00:35:35.066719
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4f2e19bc2a27'
|
||||
down_revision = 'dd27f1311a90'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('package_review',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('recommends', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.add_column('thread', sa.Column('review_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'thread', 'package_review', ['review_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'thread', type_='foreignkey')
|
||||
op.drop_column('thread', 'review_id')
|
||||
op.drop_table('package_review')
|
||||
# ### end Alembic commands ###
|
|
@ -1,35 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 51be0401bb85
|
||||
Revises: d4262fb15b37
|
||||
Create Date: 2021-07-24 00:25:04.706191
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '51be0401bb85'
|
||||
down_revision = 'd4262fb15b37'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('package_alias',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.Column('author', sa.String(length=50), nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('package_alias')
|
||||
# ### end Alembic commands ###
|
|
@ -1,42 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 5d7233cf8a00
|
||||
Revises: 81de25b72f66
|
||||
Create Date: 2020-12-05 03:50:18.843494
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5d7233cf8a00'
|
||||
down_revision = '81de25b72f66'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('user_notification_preferences',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_other', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_package_edit', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_package_approval', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_new_thread', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_new_review', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_thread_reply', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_maintainer', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_editor_alert', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_editor_misc', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('user_notification_preferences')
|
||||
# ### end Alembic commands ###
|
|
@ -1,55 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 605b3d74ada1
|
||||
Revises: 28a427cbd4cf
|
||||
Create Date: 2018-06-11 22:50:36.828818
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '605b3d74ada1'
|
||||
down_revision = '28a427cbd4cf'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('thread',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(length=100), nullable=False),
|
||||
sa.Column('private', sa.Boolean(), server_default='0', nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('thread_reply',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('thread_id', sa.Integer(), nullable=False),
|
||||
sa.Column('comment', sa.String(length=500), nullable=False),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['thread_id'], ['thread.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
|
||||
op.add_column('package', sa.Column('review_thread_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'package', 'thread', ['review_thread_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'package', type_='foreignkey')
|
||||
op.drop_constraint(None, 'package', type_='foreignkey')
|
||||
op.drop_column('package', 'review_thread_id')
|
||||
op.drop_table('thread_reply')
|
||||
op.drop_table('thread')
|
||||
# ### end Alembic commands ###
|
|
@ -1,22 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 64fee8e5ab34
|
||||
Revises: 306ce331a2a7
|
||||
Create Date: 2020-01-19 02:28:05.432244
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '64fee8e5ab34'
|
||||
down_revision = '306ce331a2a7'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.alter_column('user', 'confirmed_at', nullable=False, new_column_name='email_confirmed_at')
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.alter_column('user', 'email_confirmed_at', nullable=False, new_column_name='confirmed_at')
|
|
@ -0,0 +1,431 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 668167a0e2d8
|
||||
Revises:
|
||||
Create Date: 2022-02-25 02:30:06.547144
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '668167a0e2d8'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
command = """
|
||||
CREATE OR REPLACE FUNCTION parse_websearch(config regconfig, search_query text)
|
||||
RETURNS tsquery AS $$
|
||||
SELECT
|
||||
string_agg(
|
||||
(
|
||||
CASE
|
||||
WHEN position('''' IN words.word) > 0 THEN CONCAT(words.word, ':*')
|
||||
ELSE words.word
|
||||
END
|
||||
),
|
||||
' '
|
||||
)::tsquery
|
||||
FROM (
|
||||
SELECT trim(
|
||||
regexp_split_to_table(
|
||||
websearch_to_tsquery(config, lower(search_query))::text,
|
||||
' '
|
||||
)
|
||||
) AS word
|
||||
) AS words
|
||||
$$ LANGUAGE SQL IMMUTABLE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION parse_websearch(search_query text)
|
||||
RETURNS tsquery AS $$
|
||||
SELECT parse_websearch('pg_catalog.simple', search_query);
|
||||
$$ LANGUAGE SQL IMMUTABLE;"""
|
||||
|
||||
op.execute(command)
|
||||
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('content_warning',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.Column('title', sa.String(length=100), nullable=False),
|
||||
sa.Column('description', sa.String(length=500), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('email_subscription',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('email', sa.String(length=100), nullable=False),
|
||||
sa.Column('blacklisted', sa.Boolean(), nullable=False),
|
||||
sa.Column('token', sa.String(length=32), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('email')
|
||||
)
|
||||
op.create_table('license',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=50), nullable=False),
|
||||
sa.Column('is_foss', sa.Boolean(), nullable=False),
|
||||
sa.Column('url', sa.String(length=128), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('meta_package',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('username', sa.String(length=50), nullable=False),
|
||||
sa.Column('password', sa.String(length=255), nullable=True),
|
||||
sa.Column('reset_password_token', sa.String(length=100), server_default='', nullable=False),
|
||||
sa.Column('rank', sa.Enum('BANNED', 'NOT_JOINED', 'NEW_MEMBER', 'MEMBER', 'TRUSTED_MEMBER', 'APPROVER', 'EDITOR', 'BOT', 'MODERATOR', 'ADMIN', name='userrank'), nullable=False),
|
||||
sa.Column('github_username', sa.String(length=50), nullable=True),
|
||||
sa.Column('forums_username', sa.String(length=50), nullable=True),
|
||||
sa.Column('github_access_token', sa.String(length=50), nullable=True),
|
||||
sa.Column('email', sa.String(length=255), nullable=True),
|
||||
sa.Column('email_confirmed_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('locale', sa.String(length=10), nullable=True),
|
||||
sa.Column('profile_pic', sa.String(length=255), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), server_default='0', nullable=False),
|
||||
sa.Column('display_name', sa.String(length=100), nullable=False),
|
||||
sa.Column('website_url', sa.String(length=255), nullable=True),
|
||||
sa.Column('donate_url', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('email'),
|
||||
sa.UniqueConstraint('forums_username'),
|
||||
sa.UniqueConstraint('github_username')
|
||||
)
|
||||
op.create_table('thread',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
# sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
# sa.Column('review_id', sa.Integer(), nullable=True),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(length=100), nullable=False),
|
||||
sa.Column('private', sa.Boolean(), server_default='0', nullable=False),
|
||||
sa.Column('locked', sa.Boolean(), server_default='0', nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
# sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
# sa.ForeignKeyConstraint(['review_id'], ['package_review.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
|
||||
op.create_table('package',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.Unicode(length=100), nullable=False),
|
||||
sa.Column('title', sa.Unicode(length=100), nullable=False),
|
||||
sa.Column('short_desc', sa.Unicode(length=200), nullable=False),
|
||||
sa.Column('desc', sa.UnicodeText(), nullable=True),
|
||||
sa.Column('build_desc', sa.UnicodeText(), nullable=True),
|
||||
sa.Column('install_desc', sa.UnicodeText(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('approved_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('search_vector', sqlalchemy_utils.types.ts_vector.TSVectorType(), nullable=True),
|
||||
sa.Column('license_id', sa.Integer(), nullable=False),
|
||||
sa.Column('media_license_id', sa.Integer(), nullable=False),
|
||||
sa.Column('state', sa.Enum('WIP', 'CHANGES_NEEDED', 'READY_FOR_REVIEW', 'APPROVED', 'DELETED', name='packagestate'), nullable=False),
|
||||
sa.Column('dev_state', sa.Enum('WIP', 'BETA', 'ACTIVELY_DEVELOPED', 'MAINTENANCE_ONLY', 'AS_IS', 'DEPRECATED', 'LOOKING_FOR_MAINTAINER', name='packagedevstate'), nullable=True),
|
||||
sa.Column('score', sa.Float(), nullable=False),
|
||||
sa.Column('score_downloads', sa.Float(), nullable=False),
|
||||
sa.Column('downloads', sa.Integer(), nullable=False),
|
||||
sa.Column('review_thread_id', sa.Integer(), nullable=True),
|
||||
sa.Column('repo', sa.String(length=200), nullable=True),
|
||||
sa.Column('website', sa.String(length=200), nullable=True),
|
||||
sa.Column('issueTracker', sa.String(length=200), nullable=True),
|
||||
sa.Column('forums', sa.Integer(), nullable=True),
|
||||
sa.Column('video_url', sa.String(length=200), nullable=True),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['license_id'], ['license.id'], ),
|
||||
sa.ForeignKeyConstraint(['media_license_id'], ['license.id'], ),
|
||||
sa.ForeignKeyConstraint(['review_thread_id'], ['thread.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('package_review',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('recommends', sa.Boolean(), nullable=False),
|
||||
sa.Column('score', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.add_column('thread', sa.Column('package_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'thread', 'package', ['package_id'], ['id'])
|
||||
op.add_column('thread', sa.Column('review_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'thread', 'package_review', ['review_id'], ['id'] )
|
||||
|
||||
op.create_index('ix_package_search_vector', 'package', ['search_vector'], unique=False, postgresql_using='gin')
|
||||
|
||||
op.create_table('package_screenshot',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.Column('order', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(length=100), nullable=False),
|
||||
sa.Column('url', sa.String(length=100), nullable=False),
|
||||
sa.Column('approved', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('width', sa.Integer(), nullable=False),
|
||||
sa.Column('height', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# sa.Column('cover_image_id', sa.Integer(), nullable=True),
|
||||
# sa.ForeignKeyConstraint(['cover_image_id'], ['package_screenshot.id'], ),
|
||||
|
||||
op.add_column('package', sa.Column('cover_image_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'package', 'package_screenshot', ['cover_image_id'], ['id'])
|
||||
|
||||
op.create_table('tag',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.Column('title', sa.String(length=100), nullable=False),
|
||||
sa.Column('description', sa.String(length=500), nullable=True),
|
||||
sa.Column('backgroundColor', sa.String(length=6), nullable=False),
|
||||
sa.Column('textColor', sa.String(length=6), nullable=False),
|
||||
sa.Column('views', sa.Integer(), nullable=False),
|
||||
sa.Column('is_protected', sa.Boolean(), nullable=False),
|
||||
sa.Column('is_toplevel', sa.Boolean(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
|
||||
op.create_index(op.f('ix_user_username'), 'user', ['username'], unique=True)
|
||||
op.create_table('api_token',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('access_token', sa.String(length=34), nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.Column('owner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['owner_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('access_token')
|
||||
)
|
||||
op.create_table('audit_log_entry',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('causer_id', sa.Integer(), nullable=True),
|
||||
sa.Column('severity', sa.Enum('NORMAL', 'USER', 'EDITOR', 'MODERATION', name='auditseverity'), nullable=False),
|
||||
sa.Column('title', sa.String(length=100), nullable=False),
|
||||
sa.Column('url', sa.String(length=200), nullable=True),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['causer_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('content_warnings',
|
||||
sa.Column('content_warning_id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['content_warning_id'], ['content_warning.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('content_warning_id', 'package_id')
|
||||
)
|
||||
op.create_table('dependency',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('depender_id', sa.Integer(), nullable=True),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('meta_package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('optional', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['depender_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['meta_package_id'], ['meta_package.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('depender_id', 'package_id', 'meta_package_id', name='_dependency_uc')
|
||||
)
|
||||
op.create_table('forum_topic',
|
||||
sa.Column('topic_id', sa.Integer(), autoincrement=False, nullable=False),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('wip', sa.Boolean(), nullable=False),
|
||||
sa.Column('discarded', sa.Boolean(), nullable=False),
|
||||
sa.Column('type', sa.Enum('GAME', 'TOOL', 'ASSETPACK', name='packagetype'), nullable=False),
|
||||
sa.Column('title', sa.String(length=200), nullable=False),
|
||||
sa.Column('name', sa.String(length=30), nullable=True),
|
||||
sa.Column('link', sa.String(length=200), nullable=True),
|
||||
sa.Column('posts', sa.Integer(), nullable=False),
|
||||
sa.Column('views', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('topic_id')
|
||||
)
|
||||
op.create_table('maintainers',
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('user_id', 'package_id')
|
||||
)
|
||||
op.create_table('notification',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('causer_id', sa.Integer(), nullable=False),
|
||||
sa.Column('type', sa.Enum('PACKAGE_EDIT', 'PACKAGE_APPROVAL', 'NEW_THREAD', 'NEW_REVIEW', 'THREAD_REPLY', 'BOT', 'MAINTAINER', 'EDITOR_ALERT', 'EDITOR_MISC', 'OTHER', name='notificationtype'), nullable=False),
|
||||
sa.Column('emailed', sa.Boolean(), nullable=False),
|
||||
sa.Column('title', sa.String(length=100), nullable=False),
|
||||
sa.Column('url', sa.String(length=200), nullable=True),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['causer_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('package_alias',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.Column('author', sa.String(length=50), nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('package_game_support',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.Column('game_id', sa.Integer(), nullable=False),
|
||||
sa.Column('supports', sa.Boolean(), nullable=False),
|
||||
sa.Column('confidence', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['game_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('game_id', 'package_id', name='_package_game_support_uc')
|
||||
)
|
||||
op.create_table('package_release',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('title', sa.String(length=100), nullable=False),
|
||||
sa.Column('releaseDate', sa.DateTime(), nullable=False),
|
||||
sa.Column('url', sa.String(length=200), nullable=False),
|
||||
sa.Column('approved', sa.Boolean(), nullable=False),
|
||||
sa.Column('task_id', sa.String(length=37), nullable=True),
|
||||
sa.Column('commit_hash', sa.String(length=41), nullable=True),
|
||||
sa.Column('downloads', sa.Integer(), nullable=False),
|
||||
sa.Column('channel', sa.String(length=200), nullable=False),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('package_review_vote',
|
||||
sa.Column('review_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('is_positive', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['review_id'], ['package_review.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('review_id', 'user_id')
|
||||
)
|
||||
op.create_table('package_update_config',
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.Column('last_commit', sa.String(length=41), nullable=True),
|
||||
sa.Column('last_tag', sa.String(length=41), nullable=True),
|
||||
sa.Column('outdated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('trigger', sa.Enum('COMMIT', 'TAG', name='packageupdatetrigger'), nullable=False),
|
||||
sa.Column('ref', sa.String(length=41), nullable=True),
|
||||
sa.Column('make_release', sa.Boolean(), nullable=False),
|
||||
sa.Column('auto_created', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('package_id')
|
||||
)
|
||||
op.create_table('provides',
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.Column('metapackage_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['metapackage_id'], ['meta_package.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('package_id', 'metapackage_id')
|
||||
)
|
||||
op.create_table('tags',
|
||||
sa.Column('tag_id', sa.Integer(), nullable=False),
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['tag_id'], ['tag.id'], ),
|
||||
sa.PrimaryKeyConstraint('tag_id', 'package_id')
|
||||
)
|
||||
op.create_table('thread_reply',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('thread_id', sa.Integer(), nullable=False),
|
||||
sa.Column('comment', sa.String(length=2000), nullable=False),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['thread_id'], ['thread.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('user_email_verification',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('email', sa.String(length=100), nullable=False),
|
||||
sa.Column('token', sa.String(length=32), nullable=True),
|
||||
sa.Column('is_password_reset', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('user_notification_preferences',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_package_edit', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_package_approval', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_new_thread', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_new_review', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_thread_reply', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_bot', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_maintainer', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_editor_alert', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_editor_misc', sa.Integer(), nullable=False),
|
||||
sa.Column('pref_other', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('watchers',
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('thread_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['thread_id'], ['thread.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('user_id', 'thread_id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('watchers')
|
||||
op.drop_table('user_notification_preferences')
|
||||
op.drop_table('user_email_verification')
|
||||
op.drop_table('thread_reply')
|
||||
op.drop_table('tags')
|
||||
op.drop_table('provides')
|
||||
op.drop_table('package_update_config')
|
||||
op.drop_table('package_review_vote')
|
||||
op.drop_table('package_release')
|
||||
op.drop_table('package_game_support')
|
||||
op.drop_table('package_alias')
|
||||
op.drop_table('notification')
|
||||
op.drop_table('maintainers')
|
||||
op.drop_table('forum_topic')
|
||||
op.drop_table('dependency')
|
||||
op.drop_table('content_warnings')
|
||||
op.drop_table('audit_log_entry')
|
||||
op.drop_table('api_token')
|
||||
op.drop_index(op.f('ix_user_username'), table_name='user')
|
||||
op.drop_table('user')
|
||||
op.drop_table('thread')
|
||||
op.drop_table('tag')
|
||||
op.drop_table('package_screenshot')
|
||||
op.drop_table('package_review')
|
||||
op.drop_index('ix_package_search_vector', table_name='package', postgresql_using='gin')
|
||||
op.drop_table('package')
|
||||
op.drop_table('meta_package')
|
||||
op.drop_table('license')
|
||||
op.drop_table('email_subscription')
|
||||
op.drop_table('content_warning')
|
||||
# ### end Alembic commands ###
|
|
@ -1,28 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 6dca6eceb04d
|
||||
Revises: fd25bf3e57c3
|
||||
Create Date: 2020-01-18 17:32:21.885068
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
from sqlalchemy_searchable import sync_trigger
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6dca6eceb04d'
|
||||
down_revision = 'fd25bf3e57c3'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
conn = op.get_bind()
|
||||
sync_trigger(conn, 'package', 'search_vector', ["name", "title", "short_desc", "desc"])
|
||||
op.create_check_constraint("name_valid", "package", "name ~* '^[a-z0-9_]+$'")
|
||||
|
||||
|
||||
def downgrade():
|
||||
conn = op.get_bind()
|
||||
sync_trigger(conn, 'package', 'search_vector', ["title", "short_desc", "desc"])
|
||||
op.drop_constraint("name_valid", "package", type_="check")
|
|
@ -1,24 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 6e57b2b4dcdf
|
||||
Revises: 17b303f33f68
|
||||
Create Date: 2022-01-22 20:35:25.494712
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6e57b2b4dcdf'
|
||||
down_revision = '17b303f33f68'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('user', sa.Column('locale', sa.String(length=10), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('user', 'locale')
|
|
@ -1,28 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 725ff70ea316
|
||||
Revises: 51be0401bb85
|
||||
Create Date: 2021-07-31 19:10:36.683434
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '725ff70ea316'
|
||||
down_revision = '51be0401bb85'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('license', sa.Column('url', sa.String(length=128), nullable=True, default=None))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('license', 'url')
|
||||
# ### end Alembic commands ###
|
|
@ -1,23 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 7a48dbd05780
|
||||
Revises: df66c78e6791
|
||||
Create Date: 2020-01-24 21:52:49.744404
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7a48dbd05780'
|
||||
down_revision = 'df66c78e6791'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('user', sa.Column('github_access_token', sa.String(length=50), nullable=True, server_default=None))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('user', 'github_access_token')
|
|
@ -1,40 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 7def3e843d04
|
||||
Revises: dce69ad1e4eb
|
||||
Create Date: 2019-01-28 20:27:33.760232
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7def3e843d04'
|
||||
down_revision = 'dce69ad1e4eb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('minetest_release',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.add_column('package_release', sa.Column('max_rel_id', sa.Integer(), nullable=True, server_default=None))
|
||||
op.add_column('package_release', sa.Column('min_rel_id', sa.Integer(), nullable=True, server_default=None))
|
||||
op.create_foreign_key(None, 'package_release', 'minetest_release', ['max_rel_id'], ['id'])
|
||||
op.create_foreign_key(None, 'package_release', 'minetest_release', ['min_rel_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'package_release', type_='foreignkey')
|
||||
op.drop_constraint(None, 'package_release', type_='foreignkey')
|
||||
op.drop_column('package_release', 'min_rel_id')
|
||||
op.drop_column('package_release', 'max_rel_id')
|
||||
op.drop_table('minetest_release')
|
||||
# ### end Alembic commands ###
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue