Use consistent naming scheme for methods/functions

This commit is contained in:
rubenwardy 2023-06-18 21:56:19 +01:00
parent e0b25054dc
commit 8585357942
84 changed files with 629 additions and 639 deletions

@ -106,10 +106,10 @@ def restore():
package.state = target package.state = target
addAuditLog(AuditSeverity.EDITOR, current_user, f"Restored package to state {target.value}", addAuditLog(AuditSeverity.EDITOR, current_user, f"Restored package to state {target.value}",
package.getURL("packages.view"), package) package.get_url("packages.view"), package)
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
deleted_packages = Package.query \ deleted_packages = Package.query \
.filter(Package.state == PackageState.DELETED) \ .filter(Package.state == PackageState.DELETED) \

@ -45,7 +45,7 @@ def audit():
@login_required @login_required
def audit_view(id_): def audit_view(id_):
entry: AuditLogEntry = AuditLogEntry.query.get_or_404(id_) entry: AuditLogEntry = AuditLogEntry.query.get_or_404(id_)
if not entry.checkPerm(current_user, Permission.VIEW_AUDIT_DESCRIPTION): if not entry.check_perm(current_user, Permission.VIEW_AUDIT_DESCRIPTION):
abort(403) abort(403)
return render_template("admin/audit_view.html", entry=entry) return render_template("admin/audit_view.html", entry=entry)

@ -60,7 +60,7 @@ def create_edit_tag(name=None):
if tag is None: if tag is None:
abort(404) abort(404)
if not Permission.checkPerm(current_user, Permission.EDIT_TAGS if tag else Permission.CREATE_TAG): if not Permission.check_perm(current_user, Permission.EDIT_TAGS if tag else Permission.CREATE_TAG):
abort(403) abort(403)
form = TagForm( obj=tag) form = TagForm( obj=tag)

@ -69,7 +69,7 @@ def packages():
query = qb.buildPackageQuery() query = qb.buildPackageQuery()
if request.args.get("fmt") == "keys": if request.args.get("fmt") == "keys":
return jsonify([package.getAsDictionaryKey() for package in query.all()]) return jsonify([package.as_key_dict() for package in query.all()])
pkgs = qb.convertToDictionary(query.all()) pkgs = qb.convertToDictionary(query.all())
if "engine_version" in request.args or "protocol_version" in request.args: if "engine_version" in request.args or "protocol_version" in request.args:
@ -93,7 +93,7 @@ def packages():
@is_package_page @is_package_page
@cors_allowed @cors_allowed
def package(package): def package(package):
return jsonify(package.getAsDictionary(current_app.config["BASE_URL"])) return jsonify(package.as_dict(current_app.config["BASE_URL"]))
@bp.route("/api/packages/<author>/<name>/hypertext/") @bp.route("/api/packages/<author>/<name>/hypertext/")
@ -119,7 +119,7 @@ def edit_package(token, package):
def resolve_package_deps(out, package, only_hard, depth=1): def resolve_package_deps(out, package, only_hard, depth=1):
id = package.getId() id = package.get_id()
if id in out: if id in out:
return return
@ -135,12 +135,12 @@ def resolve_package_deps(out, package, only_hard, depth=1):
if dep.package: if dep.package:
name = dep.package.name name = dep.package.name
fulfilled_by = [ dep.package.getId() ] fulfilled_by = [ dep.package.get_id() ]
resolve_package_deps(out, dep.package, only_hard, depth) resolve_package_deps(out, dep.package, only_hard, depth)
elif dep.meta_package: elif dep.meta_package:
name = dep.meta_package.name name = dep.meta_package.name
fulfilled_by = [ pkg.getId() for pkg in dep.meta_package.packages if pkg.state == PackageState.APPROVED] fulfilled_by = [ pkg.get_id() for pkg in dep.meta_package.packages if pkg.state == PackageState.APPROVED]
if depth == 1 and not dep.optional: if depth == 1 and not dep.optional:
most_likely = next((pkg for pkg in dep.meta_package.packages \ most_likely = next((pkg for pkg in dep.meta_package.packages \
@ -175,7 +175,7 @@ def package_dependencies(package):
def topics(): def topics():
qb = QueryBuilder(request.args) qb = QueryBuilder(request.args)
query = qb.buildTopicQuery(show_added=True) query = qb.buildTopicQuery(show_added=True)
return jsonify([t.getAsDictionary() for t in query.all()]) return jsonify([t.as_dict() for t in query.all()])
@bp.route("/api/topic_discard/", methods=["POST"]) @bp.route("/api/topic_discard/", methods=["POST"])
@ -187,13 +187,13 @@ def topic_set_discard():
error(400, "Missing topic ID or discard bool") error(400, "Missing topic ID or discard bool")
topic = ForumTopic.query.get(tid) topic = ForumTopic.query.get(tid)
if not topic.checkPerm(current_user, Permission.TOPIC_DISCARD): if not topic.check_perm(current_user, Permission.TOPIC_DISCARD):
error(403, "Permission denied, need: TOPIC_DISCARD") error(403, "Permission denied, need: TOPIC_DISCARD")
topic.discarded = discard == "true" topic.discarded = discard == "true"
db.session.commit() db.session.commit()
return jsonify(topic.getAsDictionary()) return jsonify(topic.as_dict())
@bp.route("/api/whoami/") @bp.route("/api/whoami/")
@ -232,14 +232,14 @@ def list_all_releases():
query = query.join(Package) query = query.join(Package)
query = query.filter(Package.maintainers.contains(maintainer)) query = query.filter(Package.maintainers.contains(maintainer))
return jsonify([ rel.getLongAsDictionary() for rel in query.limit(30).all() ]) return jsonify([ rel.as_long_dict() for rel in query.limit(30).all() ])
@bp.route("/api/packages/<author>/<name>/releases/") @bp.route("/api/packages/<author>/<name>/releases/")
@is_package_page @is_package_page
@cors_allowed @cors_allowed
def list_releases(package): def list_releases(package):
return jsonify([ rel.getAsDictionary() for rel in package.releases.all() ]) return jsonify([ rel.as_dict() for rel in package.releases.all() ])
@bp.route("/api/packages/<author>/<name>/releases/new/", methods=["POST"]) @bp.route("/api/packages/<author>/<name>/releases/new/", methods=["POST"])
@ -251,7 +251,7 @@ def create_release(token, package):
if not token: if not token:
error(401, "Authentication needed") error(401, "Authentication needed")
if not package.checkPerm(token.owner, Permission.APPROVE_RELEASE): if not package.check_perm(token.owner, Permission.APPROVE_RELEASE):
error(403, "You do not have the permission to approve releases") error(403, "You do not have the permission to approve releases")
if request.headers.get("Content-Type") == "application/json": if request.headers.get("Content-Type") == "application/json":
@ -290,7 +290,7 @@ def release(package: Package, id: int):
if release is None or release.package != package: if release is None or release.package != package:
error(404, "Release not found") error(404, "Release not found")
return jsonify(release.getAsDictionary()) return jsonify(release.as_dict())
@bp.route("/api/packages/<author>/<name>/releases/<int:id>/", methods=["DELETE"]) @bp.route("/api/packages/<author>/<name>/releases/<int:id>/", methods=["DELETE"])
@ -309,7 +309,7 @@ def delete_release(token: APIToken, package: Package, id: int):
if not token.canOperateOnPackage(package): if not token.canOperateOnPackage(package):
error(403, "API token does not have access to the package") error(403, "API token does not have access to the package")
if not release.checkPerm(token.owner, Permission.DELETE_RELEASE): if not release.check_perm(token.owner, Permission.DELETE_RELEASE):
error(403, "Unable to delete the release, make sure there's a newer release available") error(403, "Unable to delete the release, make sure there's a newer release available")
db.session.delete(release) db.session.delete(release)
@ -323,7 +323,7 @@ def delete_release(token: APIToken, package: Package, id: int):
@cors_allowed @cors_allowed
def list_screenshots(package): def list_screenshots(package):
screenshots = package.screenshots.all() screenshots = package.screenshots.all()
return jsonify([ss.getAsDictionary(current_app.config["BASE_URL"]) for ss in screenshots]) return jsonify([ss.as_dict(current_app.config["BASE_URL"]) for ss in screenshots])
@bp.route("/api/packages/<author>/<name>/screenshots/new/", methods=["POST"]) @bp.route("/api/packages/<author>/<name>/screenshots/new/", methods=["POST"])
@ -335,7 +335,7 @@ def create_screenshot(token: APIToken, package: Package):
if not token: if not token:
error(401, "Authentication needed") error(401, "Authentication needed")
if not package.checkPerm(token.owner, Permission.ADD_SCREENSHOTS): if not package.check_perm(token.owner, Permission.ADD_SCREENSHOTS):
error(403, "You do not have the permission to create screenshots") error(403, "You do not have the permission to create screenshots")
data = request.form data = request.form
@ -357,7 +357,7 @@ def screenshot(package, id):
if ss is None or ss.package != package: if ss is None or ss.package != package:
error(404, "Screenshot not found") error(404, "Screenshot not found")
return jsonify(ss.getAsDictionary(current_app.config["BASE_URL"])) return jsonify(ss.as_dict(current_app.config["BASE_URL"]))
@bp.route("/api/packages/<author>/<name>/screenshots/<int:id>/", methods=["DELETE"]) @bp.route("/api/packages/<author>/<name>/screenshots/<int:id>/", methods=["DELETE"])
@ -373,7 +373,7 @@ def delete_screenshot(token: APIToken, package: Package, id: int):
if not token: if not token:
error(401, "Authentication needed") error(401, "Authentication needed")
if not package.checkPerm(token.owner, Permission.ADD_SCREENSHOTS): if not package.check_perm(token.owner, Permission.ADD_SCREENSHOTS):
error(403, "You do not have the permission to delete screenshots") error(403, "You do not have the permission to delete screenshots")
if not token.canOperateOnPackage(package): if not token.canOperateOnPackage(package):
@ -398,7 +398,7 @@ def order_screenshots(token: APIToken, package: Package):
if not token: if not token:
error(401, "Authentication needed") error(401, "Authentication needed")
if not package.checkPerm(token.owner, Permission.ADD_SCREENSHOTS): if not package.check_perm(token.owner, Permission.ADD_SCREENSHOTS):
error(403, "You do not have the permission to change screenshots") error(403, "You do not have the permission to change screenshots")
if not token.canOperateOnPackage(package): if not token.canOperateOnPackage(package):
@ -420,7 +420,7 @@ def set_cover_image(token: APIToken, package: Package):
if not token: if not token:
error(401, "Authentication needed") error(401, "Authentication needed")
if not package.checkPerm(token.owner, Permission.ADD_SCREENSHOTS): if not package.check_perm(token.owner, Permission.ADD_SCREENSHOTS):
error(403, "You do not have the permission to change screenshots") error(403, "You do not have the permission to change screenshots")
if not token.canOperateOnPackage(package): if not token.canOperateOnPackage(package):
@ -438,7 +438,7 @@ def set_cover_image(token: APIToken, package: Package):
@cors_allowed @cors_allowed
def list_reviews(package): def list_reviews(package):
reviews = package.reviews reviews = package.reviews
return jsonify([review.getAsDictionary() for review in reviews]) return jsonify([review.as_dict() for review in reviews])
@bp.route("/api/reviews/") @bp.route("/api/reviews/")
@ -473,7 +473,7 @@ def list_all_reviews():
"previous": abs_url(url_set_query(page=page - 1)) if pagination.has_prev else None, "previous": abs_url(url_set_query(page=page - 1)) if pagination.has_prev else None,
"next": abs_url(url_set_query(page=page + 1)) if pagination.has_next else None, "next": abs_url(url_set_query(page=page + 1)) if pagination.has_next else None,
}, },
"items": [review.getAsDictionary(True) for review in pagination.items], "items": [review.as_dict(True) for review in pagination.items],
}) })
@ -501,20 +501,20 @@ def package_scores():
qb = QueryBuilder(request.args) qb = QueryBuilder(request.args)
query = qb.buildPackageQuery() query = qb.buildPackageQuery()
pkgs = [package.getScoreDict() for package in query.all()] pkgs = [package.as_score_dict() for package in query.all()]
return jsonify(pkgs) return jsonify(pkgs)
@bp.route("/api/tags/") @bp.route("/api/tags/")
@cors_allowed @cors_allowed
def tags(): def tags():
return jsonify([tag.getAsDictionary() for tag in Tag.query.all() ]) return jsonify([tag.as_dict() for tag in Tag.query.all() ])
@bp.route("/api/content_warnings/") @bp.route("/api/content_warnings/")
@cors_allowed @cors_allowed
def content_warnings(): def content_warnings():
return jsonify([warning.getAsDictionary() for warning in ContentWarning.query.all() ]) return jsonify([warning.as_dict() for warning in ContentWarning.query.all() ])
@bp.route("/api/licenses/") @bp.route("/api/licenses/")
@ -549,7 +549,7 @@ def homepage():
downloads = 0 if not downloads_result or not downloads_result[0] else downloads_result[0] downloads = 0 if not downloads_result or not downloads_result[0] else downloads_result[0]
def mapPackages(packages: List[Package]): def mapPackages(packages: List[Package]):
return [pkg.getAsDictionaryShort(current_app.config["BASE_URL"]) for pkg in packages] return [pkg.as_short_dict(current_app.config["BASE_URL"]) for pkg in packages]
return jsonify({ return jsonify({
"count": count, "count": count,
@ -574,7 +574,7 @@ def welcome_v1():
.limit(5).all() .limit(5).all()
def map_packages(packages: List[Package]): def map_packages(packages: List[Package]):
return [pkg.getAsDictionaryShort(current_app.config["BASE_URL"]) for pkg in packages] return [pkg.as_short_dict(current_app.config["BASE_URL"]) for pkg in packages]
return jsonify({ return jsonify({
"featured": map_packages(featured), "featured": map_packages(featured),
@ -591,10 +591,10 @@ def versions():
if rel is None: if rel is None:
error(404, "No releases found") error(404, "No releases found")
return jsonify(rel.getAsDictionary()) return jsonify(rel.as_dict())
return jsonify([rel.getAsDictionary() \ return jsonify([rel.as_dict() \
for rel in MinetestRelease.query.all() if rel.getActual() is not None]) for rel in MinetestRelease.query.all() if rel.get_actual() is not None])
@bp.route("/api/dependencies/") @bp.route("/api/dependencies/")
@ -605,7 +605,7 @@ def all_deps():
def format_pkg(pkg: Package): def format_pkg(pkg: Package):
return { return {
"type": pkg.type.toName(), "type": pkg.type.to_name(),
"author": pkg.author.username, "author": pkg.author.username,
"name": pkg.name, "name": pkg.name,
"provides": [x.name for x in pkg.provides], "provides": [x.name for x in pkg.provides],

@ -49,7 +49,7 @@ def api_create_vcs_release(token: APIToken, package: Package, title: str, ref: s
return jsonify({ return jsonify({
"success": True, "success": True,
"task": url_for("tasks.check", id=rel.task_id), "task": url_for("tasks.check", id=rel.task_id),
"release": rel.getAsDictionary() "release": rel.as_dict()
}) })
@ -65,7 +65,7 @@ def api_create_zip_release(token: APIToken, package: Package, title: str, file,
return jsonify({ return jsonify({
"success": True, "success": True,
"task": url_for("tasks.check", id=rel.task_id), "task": url_for("tasks.check", id=rel.task_id),
"release": rel.getAsDictionary() "release": rel.as_dict()
}) })
@ -79,7 +79,7 @@ def api_create_screenshot(token: APIToken, package: Package, title: str, file, i
return jsonify({ return jsonify({
"success": True, "success": True,
"screenshot": ss.getAsDictionary() "screenshot": ss.as_dict()
}) })
@ -115,5 +115,5 @@ def api_edit_package(token: APIToken, package: Package, data: dict, reason: str
return jsonify({ return jsonify({
"success": True, "success": True,
"package": package.getAsDictionary(current_app.config["BASE_URL"]) "package": package.as_dict(current_app.config["BASE_URL"])
}) })

@ -49,7 +49,7 @@ def list_tokens(username):
if user is None: if user is None:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CREATE_TOKEN): if not user.check_perm(current_user, Permission.CREATE_TOKEN):
abort(403) abort(403)
return render_template("api/list_tokens.html", user=user, tabs=get_setting_tabs(user), current_tab="api_tokens") return render_template("api/list_tokens.html", user=user, tabs=get_setting_tabs(user), current_tab="api_tokens")
@ -63,7 +63,7 @@ def create_edit_token(username, id=None):
if user is None: if user is None:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CREATE_TOKEN): if not user.check_perm(current_user, Permission.CREATE_TOKEN):
abort(403) abort(403)
is_new = id is None is_new = id is None
@ -108,7 +108,7 @@ def reset_token(username, id):
if user is None: if user is None:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CREATE_TOKEN): if not user.check_perm(current_user, Permission.CREATE_TOKEN):
abort(403) abort(403)
token = APIToken.query.get(id) token = APIToken.query.get(id)
@ -134,7 +134,7 @@ def delete_token(username, id):
if user is None: if user is None:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CREATE_TOKEN): if not user.check_perm(current_user, Permission.CREATE_TOKEN):
abort(403) abort(403)
is_new = id is None is_new = id is None

@ -123,7 +123,7 @@ def webhook():
if actual_token is None: if actual_token is None:
return error(403, "Invalid authentication, couldn't validate API token") return error(403, "Invalid authentication, couldn't validate API token")
if not package.checkPerm(actual_token.owner, Permission.APPROVE_RELEASE): if not package.check_perm(actual_token.owner, Permission.APPROVE_RELEASE):
return error(403, "You do not have the permission to approve releases") return error(403, "You do not have the permission to approve releases")
# #

@ -43,7 +43,7 @@ def webhook_impl():
if token is None: if token is None:
return error(403, "Invalid authentication") return error(403, "Invalid authentication")
if not package.checkPerm(token.owner, Permission.APPROVE_RELEASE): if not package.check_perm(token.owner, Permission.APPROVE_RELEASE):
return error(403, "You do not have the permission to approve releases") return error(403, "You do not have the permission to approve releases")
# #

@ -23,49 +23,49 @@ bp = Blueprint("packages", __name__)
def get_package_tabs(user: User, package: Package): def get_package_tabs(user: User, package: Package):
if package is None or not package.checkPerm(user, Permission.EDIT_PACKAGE): if package is None or not package.check_perm(user, Permission.EDIT_PACKAGE):
return [] return []
retval = [ retval = [
{ {
"id": "edit", "id": "edit",
"title": gettext("Edit Details"), "title": gettext("Edit Details"),
"url": package.getURL("packages.create_edit") "url": package.get_url("packages.create_edit")
}, },
{ {
"id": "releases", "id": "releases",
"title": gettext("Releases"), "title": gettext("Releases"),
"url": package.getURL("packages.list_releases") "url": package.get_url("packages.list_releases")
}, },
{ {
"id": "screenshots", "id": "screenshots",
"title": gettext("Screenshots"), "title": gettext("Screenshots"),
"url": package.getURL("packages.screenshots") "url": package.get_url("packages.screenshots")
}, },
{ {
"id": "maintainers", "id": "maintainers",
"title": gettext("Maintainers"), "title": gettext("Maintainers"),
"url": package.getURL("packages.edit_maintainers") "url": package.get_url("packages.edit_maintainers")
}, },
{ {
"id": "audit", "id": "audit",
"title": gettext("Audit Log"), "title": gettext("Audit Log"),
"url": package.getURL("packages.audit") "url": package.get_url("packages.audit")
}, },
{ {
"id": "stats", "id": "stats",
"title": gettext("Statistics"), "title": gettext("Statistics"),
"url": package.getURL("packages.statistics") "url": package.get_url("packages.statistics")
}, },
{ {
"id": "share", "id": "share",
"title": gettext("Share and Badges"), "title": gettext("Share and Badges"),
"url": package.getURL("packages.share") "url": package.get_url("packages.share")
}, },
{ {
"id": "remove", "id": "remove",
"title": gettext("Remove"), "title": gettext("Remove"),
"url": package.getURL("packages.remove") "url": package.get_url("packages.remove")
} }
] ]
@ -73,7 +73,7 @@ def get_package_tabs(user: User, package: Package):
retval.insert(1, { retval.insert(1, {
"id": "game_support", "id": "game_support",
"title": gettext("Supported Games"), "title": gettext("Supported Games"),
"url": package.getURL("packages.game_support") "url": package.get_url("packages.game_support")
}) })
return retval return retval

@ -68,7 +68,7 @@ def list_all():
if qb.lucky: if qb.lucky:
package = query.first() package = query.first()
if package: if package:
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
topic = qb.buildTopicQuery().first() topic = qb.buildTopicQuery().first()
if qb.search and topic: if qb.search and topic:
@ -108,7 +108,7 @@ def list_all():
def getReleases(package): def getReleases(package):
if package.checkPerm(current_user, Permission.MAKE_RELEASE): if package.check_perm(current_user, Permission.MAKE_RELEASE):
return package.releases.limit(5) return package.releases.limit(5)
else: else:
return package.releases.filter_by(approved=True).limit(5) return package.releases.filter_by(approved=True).limit(5)
@ -117,12 +117,12 @@ def getReleases(package):
@bp.route("/packages/<author>/<name>/") @bp.route("/packages/<author>/<name>/")
@is_package_page @is_package_page
def view(package): def view(package):
if package.state != PackageState.APPROVED and not package.checkPerm(current_user, Permission.EDIT_PACKAGE): if package.state != PackageState.APPROVED and not package.check_perm(current_user, Permission.EDIT_PACKAGE):
return render_template("packages/gone.html", package=package), 403 return render_template("packages/gone.html", package=package), 403
show_similar = not package.approved and ( show_similar = not package.approved and (
current_user in package.maintainers or current_user in package.maintainers or
package.checkPerm(current_user, Permission.APPROVE_NEW)) package.check_perm(current_user, Permission.APPROVE_NEW))
conflicting_modnames = None conflicting_modnames = None
if show_similar and package.type != PackageType.TXP: if show_similar and package.type != PackageType.TXP:
@ -153,7 +153,7 @@ def view(package):
releases = getReleases(package) releases = getReleases(package)
review_thread = package.review_thread review_thread = package.review_thread
if review_thread is not None and not review_thread.checkPerm(current_user, Permission.SEE_THREAD): if review_thread is not None and not review_thread.check_perm(current_user, Permission.SEE_THREAD):
review_thread = None review_thread = None
topic_error = None topic_error = None
@ -209,7 +209,7 @@ def shield(package, type):
@bp.route("/packages/<author>/<name>/download/") @bp.route("/packages/<author>/<name>/download/")
@is_package_page @is_package_page
def download(package): def download(package):
release = package.getDownloadRelease() release = package.get_download_release()
if release is None: if release is None:
if "application/zip" in request.accept_mimetypes and \ if "application/zip" in request.accept_mimetypes and \
@ -217,9 +217,9 @@ def download(package):
return "", 204 return "", 204
else: else:
flash(gettext("No download available."), "danger") flash(gettext("No download available."), "danger")
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
else: else:
return redirect(release.getDownloadURL()) return redirect(release.get_download_url())
def makeLabel(obj): def makeLabel(obj):
@ -269,7 +269,7 @@ def handle_create_edit(package: typing.Optional[Package], form: PackageForm, aut
"danger") "danger")
else: else:
flash(markupsafe.Markup( flash(markupsafe.Markup(
f"<a class='btn btn-sm btn-danger float-right' href='{package.getURL('packages.view')}'>View</a>" + f"<a class='btn btn-sm btn-danger float-right' href='{package.get_url('packages.view')}'>View</a>" +
gettext("Package already exists")), "danger") gettext("Package already exists")), "danger")
return None return None
@ -301,16 +301,16 @@ def handle_create_edit(package: typing.Optional[Package], form: PackageForm, aut
if wasNew: if wasNew:
msg = f"Created package {author.username}/{form.name.data}" msg = f"Created package {author.username}/{form.name.data}"
addAuditLog(AuditSeverity.NORMAL, current_user, msg, package.getURL("packages.view"), package) addAuditLog(AuditSeverity.NORMAL, current_user, msg, package.get_url("packages.view"), package)
if wasNew and package.repo is not None: if wasNew and package.repo is not None:
importRepoScreenshot.delay(package.id) importRepoScreenshot.delay(package.id)
next_url = package.getURL("packages.view") next_url = package.get_url("packages.view")
if wasNew and ("WTFPL" in package.license.name or "WTFPL" in package.media_license.name): if wasNew and ("WTFPL" in package.license.name or "WTFPL" in package.media_license.name):
next_url = url_for("flatpage", path="help/wtfpl", r=next_url) next_url = url_for("flatpage", path="help/wtfpl", r=next_url)
elif wasNew: elif wasNew:
next_url = package.getURL("packages.setup_releases") next_url = package.get_url("packages.setup_releases")
return redirect(next_url) return redirect(next_url)
except LogicError as e: except LogicError as e:
@ -333,7 +333,7 @@ def create_edit(author=None, name=None):
flash(gettext("Unable to find that user"), "danger") flash(gettext("Unable to find that user"), "danger")
return redirect(url_for("packages.create_edit")) return redirect(url_for("packages.create_edit"))
if not author.checkPerm(current_user, Permission.CHANGE_AUTHOR): if not author.check_perm(current_user, Permission.CHANGE_AUTHOR):
flash(gettext("Permission denied"), "danger") flash(gettext("Permission denied"), "danger")
return redirect(url_for("packages.create_edit")) return redirect(url_for("packages.create_edit"))
@ -341,8 +341,8 @@ def create_edit(author=None, name=None):
package = getPackageByInfo(author, name) package = getPackageByInfo(author, name)
if package is None: if package is None:
abort(404) abort(404)
if not package.checkPerm(current_user, Permission.EDIT_PACKAGE): if not package.check_perm(current_user, Permission.EDIT_PACKAGE):
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
author = package.author author = package.author
@ -389,9 +389,9 @@ def move_to_state(package):
if state is None: if state is None:
abort(400) abort(400)
if not package.canMoveToState(current_user, state): if not package.can_move_to_state(current_user, state):
flash(gettext("You don't have permission to do that"), "danger") flash(gettext("You don't have permission to do that"), "danger")
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
package.state = state package.state = state
msg = "Marked {} as {}".format(package.title, state.value) msg = "Marked {} as {}".format(package.title, state.value)
@ -399,8 +399,8 @@ def move_to_state(package):
if state == PackageState.APPROVED: if state == PackageState.APPROVED:
if not package.approved_at: if not package.approved_at:
post_discord_webhook.delay(package.author.username, post_discord_webhook.delay(package.author.username,
"New package {}".format(package.getURL("packages.view", absolute=True)), False, "New package {}".format(package.get_url("packages.view", absolute=True)), False,
package.title, package.short_desc, package.getThumbnailURL(2, True)) package.title, package.short_desc, package.get_thumb_url(2, True))
package.approved_at = datetime.datetime.now() package.approved_at = datetime.datetime.now()
screenshots = PackageScreenshot.query.filter_by(package=package, approved=False).all() screenshots = PackageScreenshot.query.filter_by(package=package, approved=False).all()
@ -410,23 +410,23 @@ def move_to_state(package):
msg = "Approved {}".format(package.title) msg = "Approved {}".format(package.title)
elif state == PackageState.READY_FOR_REVIEW: elif state == PackageState.READY_FOR_REVIEW:
post_discord_webhook.delay(package.author.username, post_discord_webhook.delay(package.author.username,
"Ready for Review: {}".format(package.getURL("packages.view", absolute=True)), True, "Ready for Review: {}".format(package.get_url("packages.view", absolute=True)), True,
package.title, package.short_desc, package.getThumbnailURL(2, True)) package.title, package.short_desc, package.get_thumb_url(2, True))
addNotification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.getURL("packages.view"), package) addNotification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.get_url("packages.view"), package)
severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR
addAuditLog(severity, current_user, msg, package.getURL("packages.view"), package) addAuditLog(severity, current_user, msg, package.get_url("packages.view"), package)
db.session.commit() db.session.commit()
if package.state == PackageState.CHANGES_NEEDED: if package.state == PackageState.CHANGES_NEEDED:
flash(gettext("Please comment what changes are needed in the approval thread"), "warning") flash(gettext("Please comment what changes are needed in the approval thread"), "warning")
if package.review_thread: if package.review_thread:
return redirect(package.review_thread.getViewURL()) return redirect(package.review_thread.get_view_url())
else: else:
return redirect(url_for('threads.new', pid=package.id, title='Package approval comments')) return redirect(url_for('threads.new', pid=package.id, title='Package approval comments'))
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
@bp.route("/packages/<author>/<name>/remove/", methods=["GET", "POST"]) @bp.route("/packages/<author>/<name>/remove/", methods=["GET", "POST"])
@ -440,9 +440,9 @@ def remove(package):
reason = request.form.get("reason") or "?" reason = request.form.get("reason") or "?"
if "delete" in request.form: if "delete" in request.form:
if not package.checkPerm(current_user, Permission.DELETE_PACKAGE): if not package.check_perm(current_user, Permission.DELETE_PACKAGE):
flash(gettext("You don't have permission to do that"), "danger") flash(gettext("You don't have permission to do that"), "danger")
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
package.state = PackageState.DELETED package.state = PackageState.DELETED
@ -456,21 +456,21 @@ def remove(package):
return redirect(url) return redirect(url)
elif "unapprove" in request.form: elif "unapprove" in request.form:
if not package.checkPerm(current_user, Permission.UNAPPROVE_PACKAGE): if not package.check_perm(current_user, Permission.UNAPPROVE_PACKAGE):
flash(gettext("You don't have permission to do that"), "danger") flash(gettext("You don't have permission to do that"), "danger")
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
package.state = PackageState.WIP package.state = PackageState.WIP
msg = "Unapproved {}, reason={}".format(package.title, reason) msg = "Unapproved {}, reason={}".format(package.title, reason)
addNotification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.getURL("packages.view"), package) addNotification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.get_url("packages.view"), package)
addAuditLog(AuditSeverity.EDITOR, current_user, msg, package.getURL("packages.view"), package) addAuditLog(AuditSeverity.EDITOR, current_user, msg, package.get_url("packages.view"), package)
db.session.commit() db.session.commit()
flash(gettext("Unapproved package"), "success") flash(gettext("Unapproved package"), "success")
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
else: else:
abort(400) abort(400)
@ -485,9 +485,9 @@ class PackageMaintainersForm(FlaskForm):
@login_required @login_required
@is_package_page @is_package_page
def edit_maintainers(package): def edit_maintainers(package):
if not package.checkPerm(current_user, Permission.EDIT_MAINTAINERS): if not package.check_perm(current_user, Permission.EDIT_MAINTAINERS):
flash(gettext("You don't have permission to edit maintainers"), "danger") flash(gettext("You don't have permission to edit maintainers"), "danger")
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
form = PackageMaintainersForm(formdata=request.form) form = PackageMaintainersForm(formdata=request.form)
if request.method == "GET": if request.method == "GET":
@ -504,12 +504,12 @@ def edit_maintainers(package):
if thread: if thread:
thread.watchers.append(user) thread.watchers.append(user)
addNotification(user, current_user, NotificationType.MAINTAINER, addNotification(user, current_user, NotificationType.MAINTAINER,
"Added you as a maintainer of {}".format(package.title), package.getURL("packages.view"), package) "Added you as a maintainer of {}".format(package.title), package.get_url("packages.view"), package)
for user in package.maintainers: for user in package.maintainers:
if user != package.author and not user in users: if user != package.author and not user in users:
addNotification(user, current_user, NotificationType.MAINTAINER, addNotification(user, current_user, NotificationType.MAINTAINER,
"Removed you as a maintainer of {}".format(package.title), package.getURL("packages.view"), package) "Removed you as a maintainer of {}".format(package.title), package.get_url("packages.view"), package)
package.maintainers.clear() package.maintainers.clear()
package.maintainers.extend(users) package.maintainers.extend(users)
@ -517,13 +517,13 @@ def edit_maintainers(package):
package.maintainers.append(package.author) package.maintainers.append(package.author)
msg = "Edited {} maintainers".format(package.title) msg = "Edited {} maintainers".format(package.title)
addNotification(package.author, current_user, NotificationType.MAINTAINER, msg, package.getURL("packages.view"), package) addNotification(package.author, current_user, NotificationType.MAINTAINER, msg, package.get_url("packages.view"), package)
severity = AuditSeverity.NORMAL if current_user == package.author else AuditSeverity.MODERATION severity = AuditSeverity.NORMAL if current_user == package.author else AuditSeverity.MODERATION
addAuditLog(severity, current_user, msg, package.getURL("packages.view"), package) addAuditLog(severity, current_user, msg, package.get_url("packages.view"), package)
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
users = User.query.filter(User.rank >= UserRank.NEW_MEMBER).order_by(db.asc(User.username)).all() users = User.query.filter(User.rank >= UserRank.NEW_MEMBER).order_by(db.asc(User.username)).all()
@ -545,19 +545,19 @@ def remove_self_maintainers(package):
package.maintainers.remove(current_user) package.maintainers.remove(current_user)
addNotification(package.author, current_user, NotificationType.MAINTAINER, addNotification(package.author, current_user, NotificationType.MAINTAINER,
"Removed themself as a maintainer of {}".format(package.title), package.getURL("packages.view"), package) "Removed themself as a maintainer of {}".format(package.title), package.get_url("packages.view"), package)
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
@bp.route("/packages/<author>/<name>/audit/") @bp.route("/packages/<author>/<name>/audit/")
@login_required @login_required
@is_package_page @is_package_page
def audit(package): def audit(package):
if not (package.checkPerm(current_user, Permission.EDIT_PACKAGE) or if not (package.check_perm(current_user, Permission.EDIT_PACKAGE) or
package.checkPerm(current_user, Permission.APPROVE_NEW)): package.check_perm(current_user, Permission.APPROVE_NEW)):
abort(403) abort(403)
page = get_int_or_abort(request.args.get("page"), 1) page = get_int_or_abort(request.args.get("page"), 1)
@ -605,7 +605,7 @@ def alias_create_edit(package: Package, alias_id: int = None):
form.populate_obj(alias) form.populate_obj(alias)
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.alias_list")) return redirect(package.get_url("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)
@ -655,8 +655,8 @@ def game_support(package):
if package.type != PackageType.MOD and package.type != PackageType.TXP: if package.type != PackageType.MOD and package.type != PackageType.TXP:
abort(404) abort(404)
can_edit = package.checkPerm(current_user, Permission.EDIT_PACKAGE) can_edit = package.check_perm(current_user, Permission.EDIT_PACKAGE)
if not (can_edit or package.checkPerm(current_user, Permission.APPROVE_NEW)): if not (can_edit or package.check_perm(current_user, Permission.APPROVE_NEW)):
abort(403) abort(403)
force_game_detection = package.supported_games.filter(and_( force_game_detection = package.supported_games.filter(and_(
@ -694,7 +694,7 @@ def game_support(package):
except LogicError as e: except LogicError as e:
flash(e.message, "danger") flash(e.message, "danger")
next_url = package.getURL("packages.game_support") next_url = package.get_url("packages.game_support")
enable_support_detection = form.enable_support_detection.data or force_game_detection enable_support_detection = form.enable_support_detection.data or force_game_detection
if enable_support_detection != package.enable_game_support_detection: if enable_support_detection != package.enable_game_support_detection:
@ -706,7 +706,7 @@ def game_support(package):
package.supports_all_games = form.supports_all_games.data package.supports_all_games = form.supports_all_games.data
addAuditLog(AuditSeverity.NORMAL, current_user, "Edited game support", package.getURL("packages.game_support"), package) addAuditLog(AuditSeverity.NORMAL, current_user, "Edited game support", package.get_url("packages.game_support"), package)
db.session.commit() db.session.commit()

@ -76,8 +76,8 @@ class EditPackageReleaseForm(FlaskForm):
@login_required @login_required
@is_package_page @is_package_page
def create_release(package): def create_release(package):
if not package.checkPerm(current_user, Permission.MAKE_RELEASE): if not package.check_perm(current_user, Permission.MAKE_RELEASE):
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
# Initial form class from post data and default data # Initial form class from post data and default data
form = CreatePackageReleaseForm() form = CreatePackageReleaseForm()
@ -94,11 +94,11 @@ def create_release(package):
try: try:
if form["uploadOpt"].data == "vcs": if form["uploadOpt"].data == "vcs":
rel = do_create_vcs_release(current_user, package, form.title.data, rel = do_create_vcs_release(current_user, package, form.title.data,
form.vcsLabel.data, form.min_rel.data.getActual(), form.max_rel.data.getActual()) form.vcsLabel.data, form.min_rel.data.get_actual(), form.max_rel.data.get_actual())
else: else:
rel = do_create_zip_release(current_user, package, form.title.data, rel = do_create_zip_release(current_user, package, form.title.data,
form.file_upload.data, form.min_rel.data.getActual(), form.max_rel.data.getActual()) form.file_upload.data, form.min_rel.data.get_actual(), form.max_rel.data.get_actual())
return redirect(url_for("tasks.check", id=rel.task_id, r=rel.getEditURL())) return redirect(url_for("tasks.check", id=rel.task_id, r=rel.get_edit_url()))
except LogicError as e: except LogicError as e:
flash(e.message, "danger") flash(e.message, "danger")
@ -151,10 +151,10 @@ def edit_release(package, id):
if release is None or release.package != package: if release is None or release.package != package:
abort(404) abort(404)
canEdit = package.checkPerm(current_user, Permission.MAKE_RELEASE) canEdit = package.check_perm(current_user, Permission.MAKE_RELEASE)
canApprove = release.checkPerm(current_user, Permission.APPROVE_RELEASE) canApprove = release.check_perm(current_user, Permission.APPROVE_RELEASE)
if not (canEdit or canApprove): if not (canEdit or canApprove):
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
# Initial form class from post data and default data # Initial form class from post data and default data
form = EditPackageReleaseForm(formdata=request.form, obj=release) form = EditPackageReleaseForm(formdata=request.form, obj=release)
@ -166,10 +166,10 @@ def edit_release(package, id):
if form.validate_on_submit(): if form.validate_on_submit():
if canEdit: if canEdit:
release.title = form["title"].data release.title = form["title"].data
release.min_rel = form["min_rel"].data.getActual() release.min_rel = form["min_rel"].data.get_actual()
release.max_rel = form["max_rel"].data.getActual() release.max_rel = form["max_rel"].data.get_actual()
if package.checkPerm(current_user, Permission.CHANGE_RELEASE_URL): if package.check_perm(current_user, Permission.CHANGE_RELEASE_URL):
release.url = form["url"].data release.url = form["url"].data
release.task_id = form["task_id"].data release.task_id = form["task_id"].data
if release.task_id is not None: if release.task_id is not None:
@ -181,7 +181,7 @@ def edit_release(package, id):
release.approved = False release.approved = False
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.list_releases")) return redirect(package.get_url("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)
@ -202,8 +202,8 @@ class BulkReleaseForm(FlaskForm):
@login_required @login_required
@is_package_page @is_package_page
def bulk_change_release(package): def bulk_change_release(package):
if not package.checkPerm(current_user, Permission.MAKE_RELEASE): if not package.check_perm(current_user, Permission.MAKE_RELEASE):
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
# Initial form class from post data and default data # Initial form class from post data and default data
form = BulkReleaseForm() form = BulkReleaseForm()
@ -215,13 +215,13 @@ def bulk_change_release(package):
for release in package.releases.all(): for release in package.releases.all():
if form["set_min"].data and (not only_change_none or release.min_rel is None): if form["set_min"].data and (not only_change_none or release.min_rel is None):
release.min_rel = form["min_rel"].data.getActual() release.min_rel = form["min_rel"].data.get_actual()
if form["set_max"].data and (not only_change_none or release.max_rel is None): if form["set_max"].data and (not only_change_none or release.max_rel is None):
release.max_rel = form["max_rel"].data.getActual() release.max_rel = form["max_rel"].data.get_actual()
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.list_releases")) return redirect(package.get_url("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)
@ -234,13 +234,13 @@ def delete_release(package, id):
if release is None or release.package != package: if release is None or release.package != package:
abort(404) abort(404)
if not release.checkPerm(current_user, Permission.DELETE_RELEASE): if not release.check_perm(current_user, Permission.DELETE_RELEASE):
return redirect(package.getURL("packages.list_releases")) return redirect(package.get_url("packages.list_releases"))
db.session.delete(release) db.session.delete(release)
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
class PackageUpdateConfigFrom(FlaskForm): class PackageUpdateConfigFrom(FlaskForm):
@ -288,12 +288,12 @@ def set_update_config(package, form):
@login_required @login_required
@is_package_page @is_package_page
def update_config(package): def update_config(package):
if not package.checkPerm(current_user, Permission.MAKE_RELEASE): if not package.check_perm(current_user, Permission.MAKE_RELEASE):
abort(403) abort(403)
if not package.repo: if not package.repo:
flash(gettext("Please add a Git repository URL in order to set up automatic releases"), "danger") flash(gettext("Please add a Git repository URL in order to set up automatic releases"), "danger")
return redirect(package.getURL("packages.create_edit")) return redirect(package.get_url("packages.create_edit"))
form = PackageUpdateConfigFrom(obj=package.update_config) form = PackageUpdateConfigFrom(obj=package.update_config)
if request.method == "GET": if request.method == "GET":
@ -317,9 +317,9 @@ def update_config(package):
if not form.disable.data and package.releases.count() == 0: if not form.disable.data and package.releases.count() == 0:
flash(gettext("Now, please create an initial release"), "success") flash(gettext("Now, please create an initial release"), "success")
return redirect(package.getURL("packages.create_release")) return redirect(package.get_url("packages.create_release"))
return redirect(package.getURL("packages.list_releases")) return redirect(package.get_url("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)
@ -328,11 +328,11 @@ def update_config(package):
@login_required @login_required
@is_package_page @is_package_page
def setup_releases(package): def setup_releases(package):
if not package.checkPerm(current_user, Permission.MAKE_RELEASE): if not package.check_perm(current_user, Permission.MAKE_RELEASE):
abort(403) abort(403)
if package.update_config: if package.update_config:
return redirect(package.getURL("packages.update_config")) return redirect(package.get_url("packages.update_config"))
return render_template("packages/release_wizard.html", package=package) return render_template("packages/release_wizard.html", package=package)

@ -54,13 +54,13 @@ class ReviewForm(FlaskForm):
def review(package): def review(package):
if current_user in package.maintainers: if current_user in package.maintainers:
flash(gettext("You can't review your own package!"), "danger") flash(gettext("You can't review your own package!"), "danger")
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
if package.state != PackageState.APPROVED: if package.state != PackageState.APPROVED:
abort(404) abort(404)
review = PackageReview.query.filter_by(package=package, author=current_user).first() review = PackageReview.query.filter_by(package=package, author=current_user).first()
can_review = review is not None or current_user.canReviewRL() can_review = review is not None or current_user.can_review_ratelimit()
if not can_review: if not can_review:
flash(gettext("You've reviewed too many packages recently. Please wait before trying again, and consider making your reviews more detailed"), "danger") flash(gettext("You've reviewed too many packages recently. Please wait before trying again, and consider making your reviews more detailed"), "danger")
@ -75,7 +75,7 @@ def review(package):
# Validate and submit # Validate and submit
elif can_review and form.validate_on_submit(): elif can_review and form.validate_on_submit():
if has_blocked_domains(form.comment.data, current_user.username, f"review of {package.getId()}"): if has_blocked_domains(form.comment.data, current_user.username, f"review of {package.get_id()}"):
flash(gettext("Linking to blocked sites is not allowed"), "danger") flash(gettext("Linking to blocked sites is not allowed"), "danger")
else: else:
was_new = False was_new = False
@ -114,7 +114,7 @@ def review(package):
db.session.commit() db.session.commit()
package.recalcScore() package.recalculate_score()
if was_new: if was_new:
notif_msg = "New review '{}'".format(form.title.data) notif_msg = "New review '{}'".format(form.title.data)
@ -128,11 +128,11 @@ def review(package):
if was_new: if was_new:
post_discord_webhook.delay(thread.author.username, post_discord_webhook.delay(thread.author.username,
"Reviewed {}: {}".format(package.title, thread.getViewURL(absolute=True)), False) "Reviewed {}: {}".format(package.title, thread.get_view_url(absolute=True)), False)
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
return render_template("packages/review_create_edit.html", return render_template("packages/review_create_edit.html",
form=form, package=package, review=review) form=form, package=package, review=review)
@ -148,7 +148,7 @@ def delete_review(package, reviewer):
if review is None or review.package != package: if review is None or review.package != package:
abort(404) abort(404)
if not review.checkPerm(current_user, Permission.DELETE_REVIEW): if not review.check_perm(current_user, Permission.DELETE_REVIEW):
abort(403) abort(403)
thread = review.thread thread = review.thread
@ -164,18 +164,18 @@ def delete_review(package, reviewer):
msg = "Converted review by {} to thread".format(review.author.display_name) msg = "Converted review by {} to thread".format(review.author.display_name)
addAuditLog(AuditSeverity.MODERATION if current_user.username != reviewer else AuditSeverity.NORMAL, addAuditLog(AuditSeverity.MODERATION if current_user.username != reviewer else AuditSeverity.NORMAL,
current_user, msg, thread.getViewURL(), thread.package) current_user, msg, thread.get_view_url(), thread.package)
notif_msg = "Deleted review '{}', comments were kept as a thread".format(thread.title) notif_msg = "Deleted review '{}', comments were kept as a thread".format(thread.title)
addNotification(package.maintainers, current_user, NotificationType.OTHER, notif_msg, url_for("threads.view", id=thread.id), package) addNotification(package.maintainers, current_user, NotificationType.OTHER, notif_msg, url_for("threads.view", id=thread.id), package)
db.session.delete(review) db.session.delete(review)
package.recalcScore() package.recalculate_score()
db.session.commit() db.session.commit()
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
def handle_review_vote(package: Package, review_id: int): def handle_review_vote(package: Package, review_id: int):
@ -219,7 +219,7 @@ def review_vote(package, review_id):
if next_url and is_safe_url(next_url): if next_url and is_safe_url(next_url):
return redirect(next_url) return redirect(next_url)
else: else:
return redirect(review.thread.getViewURL()) return redirect(review.thread.get_view_url())
@bp.route("/packages/<author>/<name>/review-votes/") @bp.route("/packages/<author>/<name>/review-votes/")
@ -228,7 +228,7 @@ def review_vote(package, review_id):
def review_votes(package): def review_votes(package):
user_biases = {} user_biases = {}
for review in package.reviews: for review in package.reviews:
review_sign = review.asWeight() review_sign = review.as_weight()
for vote in review.votes: for vote in review.votes:
user_biases[vote.user.username] = user_biases.get(vote.user.username, [0, 0]) user_biases[vote.user.username] = user_biases.get(vote.user.username, [0, 0])
vote_sign = 1 if vote.is_positive else -1 vote_sign = 1 if vote.is_positive else -1

@ -50,8 +50,8 @@ class EditPackageScreenshotsForm(FlaskForm):
@login_required @login_required
@is_package_page @is_package_page
def screenshots(package): def screenshots(package):
if not package.checkPerm(current_user, Permission.ADD_SCREENSHOTS): if not package.check_perm(current_user, Permission.ADD_SCREENSHOTS):
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
form = EditPackageScreenshotsForm(obj=package) form = EditPackageScreenshotsForm(obj=package)
form.cover_image.query = package.screenshots form.cover_image.query = package.screenshots
@ -61,7 +61,7 @@ def screenshots(package):
if order: if order:
try: try:
do_order_screenshots(current_user, package, order.split(",")) do_order_screenshots(current_user, package, order.split(","))
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
except LogicError as e: except LogicError as e:
flash(e.message, "danger") flash(e.message, "danger")
@ -77,15 +77,15 @@ def screenshots(package):
@login_required @login_required
@is_package_page @is_package_page
def create_screenshot(package): def create_screenshot(package):
if not package.checkPerm(current_user, Permission.ADD_SCREENSHOTS): if not package.check_perm(current_user, Permission.ADD_SCREENSHOTS):
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
# Initial form class from post data and default data # Initial form class from post data and default data
form = CreateScreenshotForm() form = CreateScreenshotForm()
if form.validate_on_submit(): if form.validate_on_submit():
try: try:
do_create_screenshot(current_user, package, form.title.data, form.file_upload.data, False) do_create_screenshot(current_user, package, form.title.data, form.file_upload.data, False)
return redirect(package.getURL("packages.screenshots")) return redirect(package.get_url("packages.screenshots"))
except LogicError as e: except LogicError as e:
flash(e.message, "danger") flash(e.message, "danger")
@ -100,10 +100,10 @@ def edit_screenshot(package, id):
if screenshot is None or screenshot.package != package: if screenshot is None or screenshot.package != package:
abort(404) abort(404)
canEdit = package.checkPerm(current_user, Permission.ADD_SCREENSHOTS) canEdit = package.check_perm(current_user, Permission.ADD_SCREENSHOTS)
canApprove = package.checkPerm(current_user, Permission.APPROVE_SCREENSHOT) canApprove = package.check_perm(current_user, Permission.APPROVE_SCREENSHOT)
if not (canEdit or canApprove): if not (canEdit or canApprove):
return redirect(package.getURL("packages.screenshots")) return redirect(package.get_url("packages.screenshots"))
# Initial form class from post data and default data # Initial form class from post data and default data
form = EditScreenshotForm(obj=screenshot) form = EditScreenshotForm(obj=screenshot)
@ -119,7 +119,7 @@ def edit_screenshot(package, id):
screenshot.approved = wasApproved screenshot.approved = wasApproved
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.screenshots")) return redirect(package.get_url("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)
@ -132,7 +132,7 @@ def delete_screenshot(package, id):
if screenshot is None or screenshot.package != package: if screenshot is None or screenshot.package != package:
abort(404) abort(404)
if not package.checkPerm(current_user, Permission.ADD_SCREENSHOTS): if not package.check_perm(current_user, Permission.ADD_SCREENSHOTS):
flash(gettext("Permission denied"), "danger") flash(gettext("Permission denied"), "danger")
return redirect(url_for("homepage.home")) return redirect(url_for("homepage.home"))
@ -143,4 +143,4 @@ def delete_screenshot(package, id):
db.session.delete(screenshot) db.session.delete(screenshot)
db.session.commit() db.session.commit()
return redirect(package.getURL("packages.screenshots")) return redirect(package.get_url("packages.screenshots"))

@ -60,7 +60,7 @@ def list_all():
@login_required @login_required
def subscribe(id): def subscribe(id):
thread = Thread.query.get(id) thread = Thread.query.get(id)
if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD): if thread is None or not thread.check_perm(current_user, Permission.SEE_THREAD):
abort(404) abort(404)
if current_user in thread.watchers: if current_user in thread.watchers:
@ -70,14 +70,14 @@ def subscribe(id):
thread.watchers.append(current_user) thread.watchers.append(current_user)
db.session.commit() db.session.commit()
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
@bp.route("/threads/<int:id>/unsubscribe/", methods=["POST"]) @bp.route("/threads/<int:id>/unsubscribe/", methods=["POST"])
@login_required @login_required
def unsubscribe(id): def unsubscribe(id):
thread = Thread.query.get(id) thread = Thread.query.get(id)
if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD): if thread is None or not thread.check_perm(current_user, Permission.SEE_THREAD):
abort(404) abort(404)
if current_user in thread.watchers: if current_user in thread.watchers:
@ -87,14 +87,14 @@ def unsubscribe(id):
else: else:
flash(gettext("Already not subscribed!"), "success") flash(gettext("Already not subscribed!"), "success")
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
@bp.route("/threads/<int:id>/set-lock/", methods=["POST"]) @bp.route("/threads/<int:id>/set-lock/", methods=["POST"])
@login_required @login_required
def set_lock(id): def set_lock(id):
thread = Thread.query.get(id) thread = Thread.query.get(id)
if thread is None or not thread.checkPerm(current_user, Permission.LOCK_THREAD): if thread is None or not thread.check_perm(current_user, Permission.LOCK_THREAD):
abort(404) abort(404)
thread.locked = isYes(request.args.get("lock")) thread.locked = isYes(request.args.get("lock"))
@ -109,19 +109,19 @@ def set_lock(id):
msg = "Unlocked thread '{}'".format(thread.title) msg = "Unlocked thread '{}'".format(thread.title)
flash(gettext("Unlocked thread"), "success") flash(gettext("Unlocked thread"), "success")
addNotification(thread.watchers, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package) addNotification(thread.watchers, current_user, NotificationType.OTHER, msg, thread.get_view_url(), thread.package)
addAuditLog(AuditSeverity.MODERATION, current_user, msg, thread.getViewURL(), thread.package) addAuditLog(AuditSeverity.MODERATION, current_user, msg, thread.get_view_url(), thread.package)
db.session.commit() db.session.commit()
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
@bp.route("/threads/<int:id>/delete/", methods=["GET", "POST"]) @bp.route("/threads/<int:id>/delete/", methods=["GET", "POST"])
@login_required @login_required
def delete_thread(id): def delete_thread(id):
thread = Thread.query.get(id) thread = Thread.query.get(id)
if thread is None or not thread.checkPerm(current_user, Permission.DELETE_THREAD): if thread is None or not thread.check_perm(current_user, Permission.DELETE_THREAD):
abort(404) abort(404)
if request.method == "GET": if request.method == "GET":
@ -157,21 +157,21 @@ def delete_reply(id):
if thread.first_reply == reply: if thread.first_reply == reply:
flash(gettext("Cannot delete thread opening post!"), "danger") flash(gettext("Cannot delete thread opening post!"), "danger")
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
if not reply.checkPerm(current_user, Permission.DELETE_REPLY): if not reply.check_perm(current_user, Permission.DELETE_REPLY):
abort(403) abort(403)
if request.method == "GET": if request.method == "GET":
return render_template("threads/delete_reply.html", thread=thread, reply=reply) return render_template("threads/delete_reply.html", thread=thread, reply=reply)
msg = "Deleted reply by {}".format(reply.author.display_name) msg = "Deleted reply by {}".format(reply.author.display_name)
addAuditLog(AuditSeverity.MODERATION, current_user, msg, thread.getViewURL(), thread.package, reply.comment) addAuditLog(AuditSeverity.MODERATION, current_user, msg, thread.get_view_url(), thread.package, reply.comment)
db.session.delete(reply) db.session.delete(reply)
db.session.commit() db.session.commit()
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
class CommentForm(FlaskForm): class CommentForm(FlaskForm):
@ -194,7 +194,7 @@ def edit_reply(id):
if reply is None or reply.thread != thread: if reply is None or reply.thread != thread:
abort(404) abort(404)
if not reply.checkPerm(current_user, Permission.EDIT_REPLY): if not reply.check_perm(current_user, Permission.EDIT_REPLY):
abort(403) abort(403)
form = CommentForm(formdata=request.form, obj=reply) form = CommentForm(formdata=request.form, obj=reply)
@ -205,14 +205,14 @@ def edit_reply(id):
else: else:
msg = "Edited reply by {}".format(reply.author.display_name) msg = "Edited reply by {}".format(reply.author.display_name)
severity = AuditSeverity.NORMAL if current_user == reply.author else AuditSeverity.MODERATION severity = AuditSeverity.NORMAL if current_user == reply.author else AuditSeverity.MODERATION
addNotification(reply.author, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package) addNotification(reply.author, current_user, NotificationType.OTHER, msg, thread.get_view_url(), thread.package)
addAuditLog(severity, current_user, msg, thread.getViewURL(), thread.package, reply.comment) addAuditLog(severity, current_user, msg, thread.get_view_url(), thread.package, reply.comment)
reply.comment = comment reply.comment = comment
db.session.commit() db.session.commit()
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
return render_template("threads/edit_reply.html", thread=thread, reply=reply, form=form) return render_template("threads/edit_reply.html", thread=thread, reply=reply, form=form)
@ -220,20 +220,20 @@ def edit_reply(id):
@bp.route("/threads/<int:id>/", methods=["GET", "POST"]) @bp.route("/threads/<int:id>/", methods=["GET", "POST"])
def view(id): def view(id):
thread: Thread = Thread.query.get(id) thread: Thread = Thread.query.get(id)
if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD): if thread is None or not thread.check_perm(current_user, Permission.SEE_THREAD):
abort(404) abort(404)
form = CommentForm(formdata=request.form) if thread.checkPerm(current_user, Permission.COMMENT_THREAD) else None form = CommentForm(formdata=request.form) if thread.check_perm(current_user, Permission.COMMENT_THREAD) else None
# Check that title is none to load comments into textarea if redirected from new thread page # Check that title is none to load comments into textarea if redirected from new thread page
if form and form.validate_on_submit() and request.form.get("title") is None: if form and form.validate_on_submit() and request.form.get("title") is None:
comment = form.comment.data comment = form.comment.data
if not current_user.canCommentRL(): if not current_user.can_comment_ratelimit():
flash(gettext("Please wait before commenting again"), "danger") flash(gettext("Please wait before commenting again"), "danger")
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
if has_blocked_domains(comment, current_user.username, f"reply to {thread.getViewURL(True)}"): if has_blocked_domains(comment, current_user.username, f"reply to {thread.get_view_url(True)}"):
flash(gettext("Linking to blocked sites is not allowed"), "danger") flash(gettext("Linking to blocked sites is not allowed"), "danger")
return render_template("threads/view.html", thread=thread, form=form) return render_template("threads/view.html", thread=thread, form=form)
@ -253,23 +253,23 @@ def view(id):
msg = "Mentioned by {} in '{}'".format(current_user.display_name, thread.title) msg = "Mentioned by {} in '{}'".format(current_user.display_name, thread.title)
addNotification(mentioned, current_user, NotificationType.THREAD_REPLY, addNotification(mentioned, current_user, NotificationType.THREAD_REPLY,
msg, thread.getViewURL(), thread.package) msg, thread.get_view_url(), thread.package)
thread.watchers.append(mentioned) thread.watchers.append(mentioned)
msg = "New comment on '{}'".format(thread.title) msg = "New comment on '{}'".format(thread.title)
addNotification(thread.watchers, current_user, NotificationType.THREAD_REPLY, msg, thread.getViewURL(), thread.package) addNotification(thread.watchers, current_user, NotificationType.THREAD_REPLY, msg, thread.get_view_url(), thread.package)
if thread.author == get_system_user(): if thread.author == get_system_user():
approvers = User.query.filter(User.rank >= UserRank.APPROVER).all() approvers = User.query.filter(User.rank >= UserRank.APPROVER).all()
addNotification(approvers, current_user, NotificationType.EDITOR_MISC, msg, addNotification(approvers, current_user, NotificationType.EDITOR_MISC, msg,
thread.getViewURL(), thread.package) thread.get_view_url(), thread.package)
post_discord_webhook.delay(current_user.username, post_discord_webhook.delay(current_user.username,
"Replied to bot messages: {}".format(thread.getViewURL(absolute=True)), True) "Replied to bot messages: {}".format(thread.get_view_url(absolute=True)), True)
db.session.commit() db.session.commit()
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
return render_template("threads/view.html", thread=thread, form=form) return render_template("threads/view.html", thread=thread, form=form)
@ -300,7 +300,7 @@ def new():
is_review_thread = package and not package.approved is_review_thread = package and not package.approved
# Check that user can make the thread # Check that user can make the thread
if package and not package.checkPerm(current_user, Permission.CREATE_THREAD): if package and not package.check_perm(current_user, Permission.CREATE_THREAD):
flash(gettext("Unable to create thread!"), "danger") flash(gettext("Unable to create thread!"), "danger")
return redirect(url_for("homepage.home")) return redirect(url_for("homepage.home"))
@ -308,13 +308,13 @@ def new():
elif is_review_thread and package.review_thread is not None: elif is_review_thread and package.review_thread is not None:
# Redirect submit to `view` page, which checks for `title` in the form data and so won't commit the reply # Redirect submit to `view` page, which checks for `title` in the form data and so won't commit the reply
flash(gettext("An approval thread already exists! Consider replying there instead"), "danger") flash(gettext("An approval thread already exists! Consider replying there instead"), "danger")
return redirect(package.review_thread.getViewURL(), code=307) return redirect(package.review_thread.get_view_url(), code=307)
elif not current_user.canOpenThreadRL(): elif not current_user.can_open_thread_ratelimit():
flash(gettext("Please wait before opening another thread"), "danger") flash(gettext("Please wait before opening another thread"), "danger")
if package: if package:
return redirect(package.getURL("packages.view")) return redirect(package.get_url("packages.view"))
else: else:
return redirect(url_for("homepage.home")) return redirect(url_for("homepage.home"))
@ -359,24 +359,24 @@ def new():
msg = "Mentioned by {} in new thread '{}'".format(current_user.display_name, thread.title) msg = "Mentioned by {} in new thread '{}'".format(current_user.display_name, thread.title)
addNotification(mentioned, current_user, NotificationType.NEW_THREAD, addNotification(mentioned, current_user, NotificationType.NEW_THREAD,
msg, thread.getViewURL(), thread.package) msg, thread.get_view_url(), thread.package)
thread.watchers.append(mentioned) thread.watchers.append(mentioned)
notif_msg = "New thread '{}'".format(thread.title) notif_msg = "New thread '{}'".format(thread.title)
if package is not None: if package is not None:
addNotification(package.maintainers, current_user, NotificationType.NEW_THREAD, notif_msg, thread.getViewURL(), package) addNotification(package.maintainers, current_user, NotificationType.NEW_THREAD, notif_msg, thread.get_view_url(), package)
approvers = User.query.filter(User.rank >= UserRank.APPROVER).all() approvers = User.query.filter(User.rank >= UserRank.APPROVER).all()
addNotification(approvers, current_user, NotificationType.EDITOR_MISC, notif_msg, thread.getViewURL(), package) addNotification(approvers, current_user, NotificationType.EDITOR_MISC, notif_msg, thread.get_view_url(), package)
if is_review_thread: if is_review_thread:
post_discord_webhook.delay(current_user.username, post_discord_webhook.delay(current_user.username,
"Opened approval thread: {}".format(thread.getViewURL(absolute=True)), True) "Opened approval thread: {}".format(thread.get_view_url(absolute=True)), True)
db.session.commit() db.session.commit()
return redirect(thread.getViewURL()) return redirect(thread.get_view_url())
return render_template("threads/new.html", form=form, allow_private_change=allow_private_change, package=package) return render_template("threads/new.html", form=form, allow_private_change=allow_private_change, package=package)

@ -106,7 +106,7 @@ def apply_all_updates(username):
.order_by(db.asc(Package.title)).all() .order_by(db.asc(Package.title)).all()
for package in outdated_packages: for package in outdated_packages:
if not package.checkPerm(current_user, Permission.MAKE_RELEASE): if not package.check_perm(current_user, Permission.MAKE_RELEASE):
continue continue
if package.releases.filter(or_(PackageRelease.task_id.isnot(None), if package.releases.filter(or_(PackageRelease.task_id.isnot(None),
@ -129,8 +129,8 @@ def apply_all_updates(username):
msg = "Created release {} (Applied all Git Update Detection)".format(rel.title) msg = "Created release {} (Applied all Git Update Detection)".format(rel.title)
addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg,
rel.getURL("packages.create_edit"), package) rel.get_url("packages.create_edit"), package)
addAuditLog(AuditSeverity.NORMAL, current_user, msg, package.getURL("packages.view"), package) addAuditLog(AuditSeverity.NORMAL, current_user, msg, package.get_url("packages.view"), package)
db.session.commit() db.session.commit()
return redirect(url_for("todo.view_user", username=username)) return redirect(url_for("todo.view_user", username=username))
@ -174,7 +174,7 @@ def confirm_supports_all_games(username=None):
db.session.merge(package) db.session.merge(package)
addAuditLog(AuditSeverity.NORMAL, current_user, "Enabled 'Supports all games' (bulk)", addAuditLog(AuditSeverity.NORMAL, current_user, "Enabled 'Supports all games' (bulk)",
package.getURL("packages.game_support"), package) package.get_url("packages.game_support"), package)
db.session.commit() db.session.commit()

@ -216,7 +216,7 @@ def profile(username):
if not user: if not user:
abort(404) abort(404)
if not current_user.is_authenticated or (user != current_user and not current_user.canAccessTodoList()): if not current_user.is_authenticated or (user != current_user and not current_user.can_access_todo_list()):
packages = user.packages.filter_by(state=PackageState.APPROVED) packages = user.packages.filter_by(state=PackageState.APPROVED)
maintained_packages = user.maintained_packages.filter_by(state=PackageState.APPROVED) maintained_packages = user.maintained_packages.filter_by(state=PackageState.APPROVED)
else: else:

@ -59,7 +59,7 @@ def handle_profile_edit(form: UserProfileForm, user: User, username: str):
url_for("users.profile", username=username)) url_for("users.profile", username=username))
display_name = form.display_name.data or user.username display_name = form.display_name.data or user.username
if user.checkPerm(current_user, Permission.CHANGE_DISPLAY_NAME) and \ if user.check_perm(current_user, Permission.CHANGE_DISPLAY_NAME) and \
user.display_name != display_name: user.display_name != display_name:
if User.query.filter(User.id != user.id, if User.query.filter(User.id != user.id,
@ -82,7 +82,7 @@ def handle_profile_edit(form: UserProfileForm, user: User, username: str):
.format(user.username, user.display_name), .format(user.username, user.display_name),
url_for("users.profile", username=username)) url_for("users.profile", username=username))
if user.checkPerm(current_user, Permission.CHANGE_PROFILE_URLS): if user.check_perm(current_user, Permission.CHANGE_PROFILE_URLS):
if has_blocked_domains(form.website_url.data, current_user.username, f"{user.username}'s website_url") or \ if has_blocked_domains(form.website_url.data, current_user.username, f"{user.username}'s website_url") or \
has_blocked_domains(form.donate_url.data, current_user.username, f"{user.username}'s donate_url"): has_blocked_domains(form.donate_url.data, current_user.username, f"{user.username}'s donate_url"):
flash(gettext("Linking to blocked sites is not allowed"), "danger") flash(gettext("Linking to blocked sites is not allowed"), "danger")
@ -124,7 +124,7 @@ def make_settings_form():
} }
for notificationType in NotificationType: for notificationType in NotificationType:
key = "pref_" + notificationType.toName() key = "pref_" + notificationType.to_name()
attrs[key] = BooleanField("") attrs[key] = BooleanField("")
attrs[key + "_digest"] = BooleanField("") attrs[key + "_digest"] = BooleanField("")
@ -135,15 +135,15 @@ SettingsForm = make_settings_form()
def handle_email_notifications(user, prefs: UserNotificationPreferences, is_new, form): def handle_email_notifications(user, prefs: UserNotificationPreferences, is_new, form):
for notificationType in NotificationType: for notificationType in NotificationType:
field_email = getattr(form, "pref_" + notificationType.toName()).data field_email = getattr(form, "pref_" + notificationType.to_name()).data
field_digest = getattr(form, "pref_" + notificationType.toName() + "_digest").data or field_email field_digest = getattr(form, "pref_" + notificationType.to_name() + "_digest").data or field_email
prefs.set_can_email(notificationType, field_email) prefs.set_can_email(notificationType, field_email)
prefs.set_can_digest(notificationType, field_digest) prefs.set_can_digest(notificationType, field_digest)
if is_new: if is_new:
db.session.add(prefs) db.session.add(prefs)
if user.checkPerm(current_user, Permission.CHANGE_EMAIL): if user.check_perm(current_user, Permission.CHANGE_EMAIL):
newEmail = form.email.data newEmail = form.email.data
if newEmail and newEmail != user.email and newEmail.strip() != "": if newEmail and newEmail != user.email and newEmail.strip() != "":
if EmailSubscription.query.filter_by(email=form.email.data, blacklisted=True).count() > 0: if EmailSubscription.query.filter_by(email=form.email.data, blacklisted=True).count() > 0:
@ -182,7 +182,7 @@ def email_notifications(username=None):
if not user: if not user:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CHANGE_EMAIL): if not user.check_perm(current_user, Permission.CHANGE_EMAIL):
abort(403) abort(403)
is_new = False is_new = False
@ -195,8 +195,8 @@ def email_notifications(username=None):
types = [] types = []
for notificationType in NotificationType: for notificationType in NotificationType:
types.append(notificationType) types.append(notificationType)
data["pref_" + notificationType.toName()] = prefs.get_can_email(notificationType) data["pref_" + notificationType.to_name()] = prefs.get_can_email(notificationType)
data["pref_" + notificationType.toName() + "_digest"] = prefs.get_can_digest(notificationType) data["pref_" + notificationType.to_name() + "_digest"] = prefs.get_can_digest(notificationType)
data["email"] = user.email data["email"] = user.email
@ -285,7 +285,7 @@ def modtools(username):
if not user: if not user:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CHANGE_EMAIL): if not user.check_perm(current_user, Permission.CHANGE_EMAIL):
abort(403) abort(403)
form = ModToolsForm(obj=user) form = ModToolsForm(obj=user)
@ -295,7 +295,7 @@ def modtools(username):
url_for("users.profile", username=username)) url_for("users.profile", username=username))
# Copy form fields to user_profile fields # Copy form fields to user_profile fields
if user.checkPerm(current_user, Permission.CHANGE_USERNAMES): if user.check_perm(current_user, Permission.CHANGE_USERNAMES):
if user.username != form.username.data: if user.username != form.username.data:
for package in user.packages: for package in user.packages:
alias = PackageAlias(user.username, package.name) alias = PackageAlias(user.username, package.name)
@ -308,12 +308,12 @@ def modtools(username):
user.forums_username = nonEmptyOrNone(form.forums_username.data) user.forums_username = nonEmptyOrNone(form.forums_username.data)
user.github_username = nonEmptyOrNone(form.github_username.data) user.github_username = nonEmptyOrNone(form.github_username.data)
if user.checkPerm(current_user, Permission.CHANGE_RANK): if user.check_perm(current_user, Permission.CHANGE_RANK):
newRank = form["rank"].data newRank = form["rank"].data
if current_user.rank.atLeast(newRank): if current_user.rank.atLeast(newRank):
if newRank != user.rank: if newRank != user.rank:
user.rank = form["rank"].data user.rank = form["rank"].data
msg = "Set rank of {} to {}".format(user.display_name, user.rank.getTitle()) msg = "Set rank of {} to {}".format(user.display_name, user.rank.get_title())
addAuditLog(AuditSeverity.MODERATION, current_user, msg, addAuditLog(AuditSeverity.MODERATION, current_user, msg,
url_for("users.profile", username=username)) url_for("users.profile", username=username))
else: else:
@ -333,7 +333,7 @@ def modtools_set_email(username):
if not user: if not user:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CHANGE_EMAIL): if not user.check_perm(current_user, Permission.CHANGE_EMAIL):
abort(403) abort(403)
user.email = request.form["email"] user.email = request.form["email"]
@ -364,7 +364,7 @@ def modtools_ban(username):
if not user: if not user:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CHANGE_RANK): if not user.check_perm(current_user, Permission.CHANGE_RANK):
abort(403) abort(403)
message = request.form["message"] message = request.form["message"]
@ -394,7 +394,7 @@ def modtools_unban(username):
if not user: if not user:
abort(404) abort(404)
if not user.checkPerm(current_user, Permission.CHANGE_RANK): if not user.check_perm(current_user, Permission.CHANGE_RANK):
abort(403) abort(403)
if user.ban: if user.ban:

@ -16,7 +16,7 @@
import sys import sys
from typing import List, Dict, Optional, Iterable from typing import List, Dict
import sqlalchemy.orm import sqlalchemy.orm
@ -102,7 +102,7 @@ class GameSupportResolver:
print(f"Resolving for {key}", file=sys.stderr) print(f"Resolving for {key}", file=sys.stderr)
history = history.copy() history = history.copy()
history.append(package.getId()) history.append(package.get_id())
if package.type == PackageType.GAME: if package.type == PackageType.GAME:
return {package.id} return {package.id}
@ -135,7 +135,7 @@ class GameSupportResolver:
self.resolved_packages[key] = retval self.resolved_packages[key] = retval
return retval return retval
def update_all(self) -> None: def init_all(self) -> None:
for package in self.session.query(Package).filter(Package.type == PackageType.MOD, Package.state != PackageState.DELETED).all(): for package in self.session.query(Package).filter(Package.type == PackageType.MOD, Package.state != PackageState.DELETED).all():
retval = self.resolve(package, []) retval = self.resolve(package, [])
for game_id in retval: for game_id in retval:

@ -109,7 +109,7 @@ def get_package_overview_for_user(user: Optional[User], start_date: datetime.dat
if user: if user:
package_title_by_id[package.id] = package.title package_title_by_id[package.id] = package.title
else: else:
package_title_by_id[package.id] = package.getId() package_title_by_id[package.id] = package.get_id()
result = {} result = {}

@ -24,7 +24,7 @@ def validate_package_for_approval(package: Package) -> List[ValidationError]:
# Don't bother validating any more until we have a release # Don't bother validating any more until we have a release
return retval return retval
missing_deps = package.getMissingHardDependenciesQuery().all() missing_deps = package.get_missing_hard_dependencies_query().all()
if len(missing_deps) > 0: if len(missing_deps) > 0:
retval.append(("danger", lazy_gettext( retval.append(("danger", lazy_gettext(
"The following hard dependencies need to be added to ContentDB first: %(deps)s", deps=missing_deps))) "The following hard dependencies need to be added to ContentDB first: %(deps)s", deps=missing_deps)))

@ -106,16 +106,16 @@ def validate(data: dict):
def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool, data: dict, def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool, data: dict,
reason: str = None): reason: str = None):
if not package.checkPerm(user, Permission.EDIT_PACKAGE): if not package.check_perm(user, Permission.EDIT_PACKAGE):
raise LogicError(403, lazy_gettext("You don't have permission to edit this package")) raise LogicError(403, lazy_gettext("You don't have permission to edit this package"))
if "name" in data and package.name != data["name"] and \ if "name" in data and package.name != data["name"] and \
not package.checkPerm(user, Permission.CHANGE_NAME): not package.check_perm(user, Permission.CHANGE_NAME):
raise LogicError(403, lazy_gettext("You don't have permission to change the package name")) raise LogicError(403, lazy_gettext("You don't have permission to change the package name"))
before_dict = None before_dict = None
if not was_new: if not was_new:
before_dict = package.getAsDictionary("/") before_dict = package.as_dict("/")
for alias, to in ALIASES.items(): for alias, to in ALIASES.items():
if alias in data: if alias in data:
@ -125,7 +125,7 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
for field in ["short_desc", "desc", "website", "issueTracker", "repo", "video_url", "donate_url"]: for field in ["short_desc", "desc", "website", "issueTracker", "repo", "video_url", "donate_url"]:
if field in data and has_blocked_domains(data[field], user.username, if field in data and has_blocked_domains(data[field], user.username,
f"{field} of {package.getId()}"): f"{field} of {package.get_id()}"):
raise LogicError(403, lazy_gettext("Linking to blocked sites is not allowed")) raise LogicError(403, lazy_gettext("Linking to blocked sites is not allowed"))
if "type" in data: if "type" in data:
@ -193,7 +193,7 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
package.content_warnings.append(warning) package.content_warnings.append(warning)
if not was_new: if not was_new:
after_dict = package.getAsDictionary("/") after_dict = package.as_dict("/")
diff = diff_dictionaries(before_dict, after_dict) diff = diff_dictionaries(before_dict, after_dict)
if reason is None: if reason is None:
@ -206,7 +206,7 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
msg += " [" + diff_desc + "]" msg += " [" + diff_desc + "]"
severity = AuditSeverity.NORMAL if user in package.maintainers else AuditSeverity.EDITOR severity = AuditSeverity.NORMAL if user in package.maintainers else AuditSeverity.EDITOR
addAuditLog(severity, user, msg, package.getURL("packages.view"), package, json.dumps(diff, indent=4)) addAuditLog(severity, user, msg, package.get_url("packages.view"), package, json.dumps(diff, indent=4))
db.session.commit() db.session.commit()

@ -28,7 +28,7 @@ from app.utils import AuditSeverity, addAuditLog, nonEmptyOrNone
def check_can_create_release(user: User, package: Package): def check_can_create_release(user: User, package: Package):
if not package.checkPerm(user, Permission.MAKE_RELEASE): if not package.check_perm(user, Permission.MAKE_RELEASE):
raise LogicError(403, lazy_gettext("You don't have permission to make releases")) raise LogicError(403, lazy_gettext("You don't have permission to make releases"))
five_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes=5) five_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes=5)
@ -54,7 +54,7 @@ def do_create_vcs_release(user: User, package: Package, title: str, ref: str,
msg = "Created release {}".format(rel.title) msg = "Created release {}".format(rel.title)
else: else:
msg = "Created release {} ({})".format(rel.title, reason) msg = "Created release {} ({})".format(rel.title, reason)
addAuditLog(AuditSeverity.NORMAL, user, msg, package.getURL("packages.view"), package) addAuditLog(AuditSeverity.NORMAL, user, msg, package.get_url("packages.view"), package)
db.session.commit() db.session.commit()
@ -89,7 +89,7 @@ def do_create_zip_release(user: User, package: Package, title: str, file,
msg = "Created release {}".format(rel.title) msg = "Created release {}".format(rel.title)
else: else:
msg = "Created release {} ({})".format(rel.title, reason) msg = "Created release {} ({})".format(rel.title, reason)
addAuditLog(AuditSeverity.NORMAL, user, msg, package.getURL("packages.view"), package) addAuditLog(AuditSeverity.NORMAL, user, msg, package.get_url("packages.view"), package)
db.session.commit() db.session.commit()

@ -26,7 +26,7 @@ def do_create_screenshot(user: User, package: Package, title: str, file, is_cove
ss.package = package ss.package = package
ss.title = title or "Untitled" ss.title = title or "Untitled"
ss.url = uploaded_url ss.url = uploaded_url
ss.approved = package.checkPerm(user, Permission.APPROVE_SCREENSHOT) ss.approved = package.check_perm(user, Permission.APPROVE_SCREENSHOT)
ss.order = counter ss.order = counter
ss.width, ss.height = get_image_size(uploaded_path) ss.width, ss.height = get_image_size(uploaded_path)
@ -42,8 +42,8 @@ def do_create_screenshot(user: User, package: Package, title: str, file, is_cove
else: else:
msg = "Created screenshot {} ({})".format(ss.title, reason) msg = "Created screenshot {} ({})".format(ss.title, reason)
addNotification(package.maintainers, user, NotificationType.PACKAGE_EDIT, msg, package.getURL("packages.view"), package) addNotification(package.maintainers, user, NotificationType.PACKAGE_EDIT, msg, package.get_url("packages.view"), package)
addAuditLog(AuditSeverity.NORMAL, user, msg, package.getURL("packages.view"), package) addAuditLog(AuditSeverity.NORMAL, user, msg, package.get_url("packages.view"), package)
db.session.commit() db.session.commit()

@ -63,12 +63,12 @@ class AuditSeverity(enum.Enum):
def __str__(self): def __str__(self):
return self.name return self.name
def getTitle(self): def get_title(self):
return self.name.replace("_", " ").title() return self.name.replace("_", " ").title()
@classmethod @classmethod
def choices(cls): def choices(cls):
return [(choice, choice.getTitle()) for choice in cls] return [(choice, choice.get_title()) for choice in cls]
@classmethod @classmethod
def coerce(cls, item): def coerce(cls, item):
@ -106,14 +106,14 @@ class AuditLogEntry(db.Model):
self.package = package self.package = package
self.description = description self.description = description
def checkPerm(self, user, perm): def check_perm(self, user, perm):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
raise Exception("Unknown permission given to AuditLogEntry.checkPerm()") raise Exception("Unknown permission given to AuditLogEntry.check_perm()")
if perm == Permission.VIEW_AUDIT_DESCRIPTION: if perm == Permission.VIEW_AUDIT_DESCRIPTION:
return user.rank.atLeast(UserRank.APPROVER if self.package is not None else UserRank.MODERATOR) return user.rank.atLeast(UserRank.APPROVER if self.package is not None else UserRank.MODERATOR)
@ -156,11 +156,11 @@ class ForumTopic(db.Model):
return self.link.replace("repo.or.cz/w/", "repo.or.cz/") return self.link.replace("repo.or.cz/w/", "repo.or.cz/")
def getAsDictionary(self): def as_dict(self):
return { return {
"author": self.author.username, "author": self.author.username,
"name": self.name, "name": self.name,
"type": self.type.toName(), "type": self.type.to_name(),
"title": self.title, "title": self.title,
"id": self.topic_id, "id": self.topic_id,
"link": self.link, "link": self.link,
@ -171,14 +171,14 @@ class ForumTopic(db.Model):
"created_at": self.created_at.isoformat(), "created_at": self.created_at.isoformat(),
} }
def checkPerm(self, user, perm): def check_perm(self, user, perm):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
raise Exception("Unknown permission given to ForumTopic.checkPerm()") raise Exception("Unknown permission given to ForumTopic.check_perm()")
if perm == Permission.TOPIC_DISCARD: if perm == Permission.TOPIC_DISCARD:
return self.author == user or user.rank.atLeast(UserRank.EDITOR) return self.author == user or user.rank.atLeast(UserRank.EDITOR)

@ -17,7 +17,6 @@
import datetime import datetime
import enum import enum
import typing
from flask import url_for from flask import url_for
from flask_babel import lazy_gettext from flask_babel import lazy_gettext
@ -55,7 +54,7 @@ class PackageType(enum.Enum):
GAME = "Game" GAME = "Game"
TXP = "Texture Pack" TXP = "Texture Pack"
def toName(self): def to_name(self):
return self.name.lower() return self.name.lower()
def __str__(self): def __str__(self):
@ -104,7 +103,7 @@ class PackageDevState(enum.Enum):
DEPRECATED = "Deprecated" DEPRECATED = "Deprecated"
LOOKING_FOR_MAINTAINER = "Looking for Maintainer" LOOKING_FOR_MAINTAINER = "Looking for Maintainer"
def toName(self): def to_name(self):
return self.name.lower() return self.name.lower()
def __str__(self): def __str__(self):
@ -161,7 +160,7 @@ class PackageState(enum.Enum):
APPROVED = "Approved" APPROVED = "Approved"
DELETED = "Deleted" DELETED = "Deleted"
def toName(self): def to_name(self):
return self.name.lower() return self.name.lower()
def verb(self): def verb(self):
@ -503,26 +502,23 @@ class Package(db.Model):
return Package.query.filter(Package.name == parts[1], Package.author.has(username=parts[0])).first() return Package.query.filter(Package.name == parts[1], Package.author.has(username=parts[0])).first()
def getId(self): def get_id(self):
return "{}/{}".format(self.author.username, self.name) return "{}/{}".format(self.author.username, self.name)
def getIsFOSS(self): def get_sorted_dependencies(self, is_hard=None):
return self.license.is_foss and self.media_license.is_foss
def getSortedDependencies(self, is_hard=None):
query = self.dependencies query = self.dependencies
if is_hard is not None: if is_hard is not None:
query = query.filter_by(optional=not is_hard) query = query.filter_by(optional=not is_hard)
deps = query.all() deps = query.all()
deps.sort(key = lambda x: x.getName()) deps.sort(key=lambda x: x.getName())
return deps return deps
def getSortedHardDependencies(self): def get_sorted_hard_dependencies(self):
return self.getSortedDependencies(True) return self.get_sorted_dependencies(True)
def getSortedOptionalDependencies(self): def get_sorted_optional_dependencies(self):
return self.getSortedDependencies(False) return self.get_sorted_dependencies(False)
def get_sorted_game_support(self): def get_sorted_game_support(self):
query = self.supported_games.filter(PackageGameSupport.game.has(state=PackageState.APPROVED)) query = self.supported_games.filter(PackageGameSupport.game.has(state=PackageState.APPROVED))
@ -542,18 +538,18 @@ class Package(db.Model):
return self.supports_all_games or \ return self.supports_all_games or \
self.supported_games.filter(PackageGameSupport.confidence > 1).count() > 0 self.supported_games.filter(PackageGameSupport.confidence > 1).count() > 0
def getAsDictionaryKey(self): def as_key_dict(self):
return { return {
"name": self.name, "name": self.name,
"author": self.author.username, "author": self.author.username,
"type": self.type.toName(), "type": self.type.to_name(),
} }
def getAsDictionaryShort(self, base_url, version=None, release_id=None, no_load=False): def as_short_dict(self, base_url, version=None, release_id=None, no_load=False):
tnurl = self.getThumbnailURL(1) tnurl = self.get_thumb_url(1)
if release_id is None and no_load == False: if release_id is None and no_load == False:
release = self.getDownloadRelease(version=version) release = self.get_download_release(version=version)
release_id = release and release.id release_id = release and release.id
short_desc = self.short_desc short_desc = self.short_desc
@ -565,10 +561,10 @@ class Package(db.Model):
"title": self.title, "title": self.title,
"author": self.author.username, "author": self.author.username,
"short_description": short_desc, "short_description": short_desc,
"type": self.type.toName(), "type": self.type.to_name(),
"release": release_id, "release": release_id,
"thumbnail": (base_url + tnurl) if tnurl is not None else None, "thumbnail": (base_url + tnurl) if tnurl is not None else None,
"aliases": [ alias.getAsDictionary() for alias in self.aliases ], "aliases": [alias.as_dict() for alias in self.aliases],
} }
if not ret["aliases"]: if not ret["aliases"]:
@ -576,9 +572,9 @@ class Package(db.Model):
return ret return ret
def getAsDictionary(self, base_url, version=None): def as_dict(self, base_url, version=None):
tnurl = self.getThumbnailURL(1) tnurl = self.get_thumb_url(1)
release = self.getDownloadRelease(version=version) release = self.get_download_release(version=version)
return { return {
"author": self.author.username, "author": self.author.username,
"maintainers": [x.username for x in self.maintainers], "maintainers": [x.username for x in self.maintainers],
@ -590,7 +586,7 @@ class Package(db.Model):
"title": self.title, "title": self.title,
"short_description": self.short_desc, "short_description": self.short_desc,
"long_description": self.desc, "long_description": self.desc,
"type": self.type.toName(), "type": self.type.to_name(),
"created_at": self.created_at.isoformat(), "created_at": self.created_at.isoformat(),
"license": self.license.name, "license": self.license.name,
@ -610,7 +606,7 @@ class Package(db.Model):
"thumbnail": (base_url + tnurl) if tnurl is not None else None, "thumbnail": (base_url + tnurl) if tnurl is not None else None,
"screenshots": [base_url + ss.url for ss in self.screenshots], "screenshots": [base_url + ss.url for ss in self.screenshots],
"url": base_url + self.getURL("packages.download"), "url": base_url + self.get_url("packages.download"),
"release": release and release.id, "release": release and release.id,
"score": round(self.score * 10) / 10, "score": round(self.score * 10) / 10,
@ -620,86 +616,86 @@ class Package(db.Model):
{ {
"supports": support.supports, "supports": support.supports,
"confidence": support.confidence, "confidence": support.confidence,
"game": support.game.getAsDictionaryShort(base_url, version) "game": support.game.as_short_dict(base_url, version)
} for support in self.supported_games.all() } for support in self.supported_games.all()
] ]
} }
def getThumbnailOrPlaceholder(self, level=2): def get_thumb_or_placeholder(self, level=2):
return self.getThumbnailURL(level) or "/static/placeholder.png" return self.get_thumb_url(level) or "/static/placeholder.png"
def getThumbnailURL(self, level=2, abs=False): def get_thumb_url(self, level=2, abs=False):
screenshot = self.main_screenshot screenshot = self.main_screenshot
url = screenshot.getThumbnailURL(level) if screenshot is not None else None url = screenshot.get_thumb_url(level) if screenshot is not None else None
if abs: if abs:
from app.utils import abs_url from app.utils import abs_url
return abs_url(url) return abs_url(url)
else: else:
return url return url
def getCoverImageURL(self): def get_cover_image_url(self):
screenshot = self.cover_image or self.main_screenshot screenshot = self.cover_image or self.main_screenshot
return screenshot and screenshot.getThumbnailURL(4) return screenshot and screenshot.get_thumb_url(4)
def getURL(self, endpoint, absolute=False, **kwargs): def get_url(self, endpoint, absolute=False, **kwargs):
if absolute: if absolute:
from app.utils import abs_url_for from app.utils import abs_url_for
return abs_url_for(endpoint, author=self.author.username, name=self.name, **kwargs) return abs_url_for(endpoint, author=self.author.username, name=self.name, **kwargs)
else: else:
return url_for(endpoint, author=self.author.username, name=self.name, **kwargs) return url_for(endpoint, author=self.author.username, name=self.name, **kwargs)
def getShieldURL(self, type): def get_shield_url(self, type):
from app.utils import abs_url_for from app.utils import abs_url_for
return abs_url_for("packages.shield", return abs_url_for("packages.shield",
author=self.author.username, name=self.name, type=type) author=self.author.username, name=self.name, type=type)
def makeShield(self, type): def make_shield(self, type):
return "[![ContentDB]({})]({})" \ return "[![ContentDB]({})]({})" \
.format(self.getShieldURL(type), self.getURL("packages.view", True)) .format(self.get_shield_url(type), self.get_url("packages.view", True))
def getSetStateURL(self, state): def get_set_state_url(self, state):
if type(state) == str: if type(state) == str:
state = PackageState[state] state = PackageState[state]
elif type(state) != PackageState: elif type(state) != PackageState:
raise Exception("Unknown state given to Package.canMoveToState()") raise Exception("Unknown state given to Package.can_move_to_state()")
return url_for("packages.move_to_state", return url_for("packages.move_to_state",
author=self.author.username, name=self.name, state=state.name.lower()) author=self.author.username, name=self.name, state=state.name.lower())
def getDownloadRelease(self, version=None): def get_download_release(self, version=None):
for rel in self.releases: for rel in self.releases:
if rel.approved and (version is None or if rel.approved and (version is None or
((rel.min_rel is None or rel.min_rel_id <= version.id) and ((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))): (rel.max_rel is None or rel.max_rel_id >= version.id))):
return rel return rel
return None return None
def checkPerm(self, user, perm): def check_perm(self, user, perm):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
raise Exception("Unknown permission given to Package.checkPerm()") raise Exception("Unknown permission given to Package.check_perm()")
isOwner = user == self.author is_owner = user == self.author
isMaintainer = isOwner or user.rank.atLeast(UserRank.EDITOR) or user in self.maintainers is_maintainer = is_owner or user.rank.atLeast(UserRank.EDITOR) or user in self.maintainers
isApprover = user.rank.atLeast(UserRank.APPROVER) is_approver = user.rank.atLeast(UserRank.APPROVER)
if perm == Permission.CREATE_THREAD: if perm == Permission.CREATE_THREAD:
return user.rank.atLeast(UserRank.NEW_MEMBER) return user.rank.atLeast(UserRank.NEW_MEMBER)
# Members can edit their own packages, and editors can edit any packages # Members can edit their own packages, and editors can edit any packages
elif perm == Permission.MAKE_RELEASE or perm == Permission.ADD_SCREENSHOTS: elif perm == Permission.MAKE_RELEASE or perm == Permission.ADD_SCREENSHOTS:
return isMaintainer return is_maintainer
elif perm == Permission.EDIT_PACKAGE: elif perm == Permission.EDIT_PACKAGE:
return isMaintainer and user.rank.atLeast(UserRank.NEW_MEMBER) return is_maintainer and user.rank.atLeast(UserRank.NEW_MEMBER)
elif perm == Permission.APPROVE_RELEASE: elif perm == Permission.APPROVE_RELEASE:
return (isMaintainer or isApprover) and user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER) return (is_maintainer or is_approver) and user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)
# Anyone can change the package name when not approved, but only editors when approved # Anyone can change the package name when not approved, but only editors when approved
elif perm == Permission.CHANGE_NAME: elif perm == Permission.CHANGE_NAME:
@ -707,17 +703,17 @@ class Package(db.Model):
# Editors can change authors and approve new packages # Editors can change authors and approve new packages
elif perm == Permission.APPROVE_NEW or perm == Permission.CHANGE_AUTHOR: elif perm == Permission.APPROVE_NEW or perm == Permission.CHANGE_AUTHOR:
return isApprover return is_approver
elif perm == Permission.APPROVE_SCREENSHOT: elif perm == Permission.APPROVE_SCREENSHOT:
return (isMaintainer or isApprover) and \ return (is_maintainer or is_approver) and \
user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER) user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)
elif perm == Permission.EDIT_MAINTAINERS or perm == Permission.DELETE_PACKAGE: elif perm == Permission.EDIT_MAINTAINERS or perm == Permission.DELETE_PACKAGE:
return isOwner or user.rank.atLeast(UserRank.EDITOR) return is_owner or user.rank.atLeast(UserRank.EDITOR)
elif perm == Permission.UNAPPROVE_PACKAGE: elif perm == Permission.UNAPPROVE_PACKAGE:
return isOwner or user.rank.atLeast(UserRank.APPROVER) return is_owner or user.rank.atLeast(UserRank.APPROVER)
elif perm == Permission.CHANGE_RELEASE_URL: elif perm == Permission.CHANGE_RELEASE_URL:
return user.rank.atLeast(UserRank.MODERATOR) return user.rank.atLeast(UserRank.MODERATOR)
@ -725,65 +721,64 @@ class Package(db.Model):
else: else:
raise Exception("Permission {} is not related to packages".format(perm.name)) raise Exception("Permission {} is not related to packages".format(perm.name))
def getMissingHardDependenciesQuery(self): def get_missing_hard_dependencies_query(self):
return MetaPackage.query \ return MetaPackage.query \
.filter(~ MetaPackage.packages.any(state=PackageState.APPROVED)) \ .filter(~ MetaPackage.packages.any(state=PackageState.APPROVED)) \
.filter(MetaPackage.dependencies.any(optional=False, depender=self)) \ .filter(MetaPackage.dependencies.any(optional=False, depender=self)) \
.order_by(db.asc(MetaPackage.name)) .order_by(db.asc(MetaPackage.name))
def getMissingHardDependencies(self): def get_missing_hard_dependencies(self):
return [mp.name for mp in self.getMissingHardDependenciesQuery().all()] return [mp.name for mp in self.get_missing_hard_dependencies_query().all()]
def canMoveToState(self, user, state): def can_move_to_state(self, user, state):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
if type(state) == str: if type(state) == str:
state = PackageState[state] state = PackageState[state]
elif type(state) != PackageState: elif type(state) != PackageState:
raise Exception("Unknown state given to Package.canMoveToState()") raise Exception("Unknown state given to Package.can_move_to_state()")
if state not in PACKAGE_STATE_FLOW[self.state]: if state not in PACKAGE_STATE_FLOW[self.state]:
return False return False
if state == PackageState.READY_FOR_REVIEW or state == PackageState.APPROVED: if state == PackageState.READY_FOR_REVIEW or state == PackageState.APPROVED:
if state == PackageState.APPROVED and not self.checkPerm(user, Permission.APPROVE_NEW): if state == PackageState.APPROVED and not self.check_perm(user, Permission.APPROVE_NEW):
return False return False
if not (self.checkPerm(user, Permission.APPROVE_NEW) or self.checkPerm(user, Permission.EDIT_PACKAGE)): if not (self.check_perm(user, Permission.APPROVE_NEW) or self.check_perm(user, Permission.EDIT_PACKAGE)):
return False return False
if state == PackageState.APPROVED and ("Other" in self.license.name or "Other" in self.media_license.name): if state == PackageState.APPROVED and ("Other" in self.license.name or "Other" in self.media_license.name):
return False return False
if self.getMissingHardDependenciesQuery().count() > 0: if self.get_missing_hard_dependencies_query().count() > 0:
return False return False
needsScreenshot = \ needs_screenshot = \
(self.type == self.type.GAME or self.type == self.type.TXP) and \ (self.type == self.type.GAME or self.type == self.type.TXP) and self.screenshots.count() == 0
self.screenshots.count() == 0
return self.releases.filter(PackageRelease.task_id==None).count() > 0 and not needsScreenshot return self.releases.filter(PackageRelease.task_id==None).count() > 0 and not needs_screenshot
elif state == PackageState.CHANGES_NEEDED: elif state == PackageState.CHANGES_NEEDED:
return self.checkPerm(user, Permission.APPROVE_NEW) return self.check_perm(user, Permission.APPROVE_NEW)
elif state == PackageState.WIP: elif state == PackageState.WIP:
return self.checkPerm(user, Permission.EDIT_PACKAGE) and \ return self.check_perm(user, Permission.EDIT_PACKAGE) and \
(user in self.maintainers or user.rank.atLeast(UserRank.ADMIN)) (user in self.maintainers or user.rank.atLeast(UserRank.ADMIN))
return True return True
def getNextStates(self, user): def get_next_states(self, user):
states = [] states = []
for state in PackageState: for state in PackageState:
if self.canMoveToState(user, state): if self.can_move_to_state(user, state):
states.append(state) states.append(state)
return states return states
def getScoreDict(self): def as_score_dict(self):
return { return {
"author": self.author.username, "author": self.author.username,
"name": self.name, "name": self.name,
@ -793,16 +788,16 @@ class Package(db.Model):
"downloads": self.downloads "downloads": self.downloads
} }
def recalcScore(self): def recalculate_score(self):
review_scores = [ 100 * r.asWeight() for r in self.reviews ] review_scores = [ 100 * r.as_weight() for r in self.reviews ]
self.score = self.score_downloads + sum(review_scores) self.score = self.score_downloads + sum(review_scores)
class MetaPackage(db.Model): class MetaPackage(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True, nullable=False) name = db.Column(db.String(100), unique=True, nullable=False)
dependencies = db.relationship("Dependency", back_populates="meta_package", lazy="dynamic") dependencies = db.relationship("Dependency", back_populates="meta_package", lazy="dynamic")
packages = db.relationship("Package", lazy="dynamic", back_populates="provides", secondary=PackageProvides) 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-z0-9_]+$'")
@ -866,7 +861,7 @@ class ContentWarning(db.Model):
regex = re.compile("[^a-z_]") regex = re.compile("[^a-z_]")
self.name = regex.sub("", self.title.lower().replace(" ", "_")) self.name = regex.sub("", self.title.lower().replace(" ", "_"))
def getAsDictionary(self): def as_dict(self):
description = self.description if self.description != "" else None description = self.description if self.description != "" else None
return { "name": self.name, "title": self.title, "description": description } return { "name": self.name, "title": self.title, "description": description }
@ -892,7 +887,7 @@ class Tag(db.Model):
regex = re.compile("[^a-z_]") regex = re.compile("[^a-z_]")
self.name = regex.sub("", self.title.lower().replace(" ", "_")) self.name = regex.sub("", self.title.lower().replace(" ", "_"))
def getAsDictionary(self): def as_dict(self):
description = self.description if self.description != "" else None description = self.description if self.description != "" else None
return { return {
"name": self.name, "name": self.name,
@ -912,10 +907,10 @@ class MinetestRelease(db.Model):
self.name = name self.name = name
self.protocol = protocol self.protocol = protocol
def getActual(self): def get_actual(self):
return None if self.name == "None" else self return None if self.name == "None" else self
def getAsDictionary(self): def as_dict(self):
return { return {
"name": self.name, "name": self.name,
"protocol_version": self.protocol, "protocol_version": self.protocol,
@ -972,7 +967,7 @@ class PackageRelease(db.Model):
def file_path(self): def file_path(self):
return self.url.replace("/uploads/", app.config["UPLOAD_DIR"]) return self.url.replace("/uploads/", app.config["UPLOAD_DIR"])
def getAsDictionary(self): def as_dict(self):
return { return {
"id": self.id, "id": self.id,
"title": self.title, "title": self.title,
@ -980,11 +975,11 @@ class PackageRelease(db.Model):
"release_date": self.releaseDate.isoformat(), "release_date": self.releaseDate.isoformat(),
"commit": self.commit_hash, "commit": self.commit_hash,
"downloads": self.downloads, "downloads": self.downloads,
"min_minetest_version": self.min_rel and self.min_rel.getAsDictionary(), "min_minetest_version": self.min_rel and self.min_rel.as_dict(),
"max_minetest_version": self.max_rel and self.max_rel.getAsDictionary(), "max_minetest_version": self.max_rel and self.max_rel.as_dict(),
} }
def getLongAsDictionary(self): def as_long_dict(self):
return { return {
"id": self.id, "id": self.id,
"title": self.title, "title": self.title,
@ -992,24 +987,24 @@ class PackageRelease(db.Model):
"release_date": self.releaseDate.isoformat(), "release_date": self.releaseDate.isoformat(),
"commit": self.commit_hash, "commit": self.commit_hash,
"downloads": self.downloads, "downloads": self.downloads,
"min_minetest_version": self.min_rel and self.min_rel.getAsDictionary(), "min_minetest_version": self.min_rel and self.min_rel.as_dict(),
"max_minetest_version": self.max_rel and self.max_rel.getAsDictionary(), "max_minetest_version": self.max_rel and self.max_rel.as_dict(),
"package": self.package.getAsDictionaryKey() "package": self.package.as_key_dict()
} }
def getEditURL(self): def get_edit_url(self):
return url_for("packages.edit_release", return url_for("packages.edit_release",
author=self.package.author.username, author=self.package.author.username,
name=self.package.name, name=self.package.name,
id=self.id) id=self.id)
def getDeleteURL(self): def get_delete_url(self):
return url_for("packages.delete_release", return url_for("packages.delete_release",
author=self.package.author.username, author=self.package.author.username,
name=self.package.name, name=self.package.name,
id=self.id) id=self.id)
def getDownloadURL(self): def get_download_url(self):
return url_for("packages.download_release", return url_for("packages.download_release",
author=self.package.author.username, author=self.package.author.username,
name=self.package.name, name=self.package.name,
@ -1018,11 +1013,11 @@ class PackageRelease(db.Model):
def __init__(self): def __init__(self):
self.releaseDate = datetime.datetime.now() self.releaseDate = datetime.datetime.now()
def getDownloadFileName(self): def get_download_filename(self):
return f"{self.package.name}_{self.id}.zip" return f"{self.package.name}_{self.id}.zip"
def approve(self, user): def approve(self, user):
if not self.checkPerm(user, Permission.APPROVE_RELEASE): if not self.check_perm(user, Permission.APPROVE_RELEASE):
return False return False
if self.approved: if self.approved:
@ -1038,22 +1033,22 @@ class PackageRelease(db.Model):
return True return True
def checkPerm(self, user, perm): def check_perm(self, user, perm):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
raise Exception("Unknown permission given to PackageRelease.checkPerm()") raise Exception("Unknown permission given to PackageRelease.check_perm()")
isMaintainer = user == self.package.author or user in self.package.maintainers is_maintainer = user == self.package.author or user in self.package.maintainers
if perm == Permission.DELETE_RELEASE: if perm == Permission.DELETE_RELEASE:
if user.rank.atLeast(UserRank.ADMIN): if user.rank.atLeast(UserRank.ADMIN):
return True return True
if not (isMaintainer or user.rank.atLeast(UserRank.EDITOR)): if not (is_maintainer or user.rank.atLeast(UserRank.EDITOR)):
return False return False
if not self.package.approved or self.task_id is not None: if not self.package.approved or self.task_id is not None:
@ -1066,7 +1061,7 @@ class PackageRelease(db.Model):
return count > 0 return count > 0
elif perm == Permission.APPROVE_RELEASE: elif perm == Permission.APPROVE_RELEASE:
return user.rank.atLeast(UserRank.APPROVER) or \ return user.rank.atLeast(UserRank.APPROVER) or \
(isMaintainer and user.rank.atLeast( (is_maintainer and user.rank.atLeast(
UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)) UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER))
else: else:
raise Exception("Permission {} is not related to releases".format(perm.name)) raise Exception("Permission {} is not related to releases".format(perm.name))
@ -1103,22 +1098,22 @@ class PackageScreenshot(db.Model):
def file_path(self): def file_path(self):
return self.url.replace("/uploads/", app.config["UPLOAD_DIR"]) return self.url.replace("/uploads/", app.config["UPLOAD_DIR"])
def getEditURL(self): def get_edit_url(self):
return url_for("packages.edit_screenshot", return url_for("packages.edit_screenshot",
author=self.package.author.username, author=self.package.author.username,
name=self.package.name, name=self.package.name,
id=self.id) id=self.id)
def getDeleteURL(self): def get_delete_url(self):
return url_for("packages.delete_screenshot", return url_for("packages.delete_screenshot",
author=self.package.author.username, author=self.package.author.username,
name=self.package.name, name=self.package.name,
id=self.id) id=self.id)
def getThumbnailURL(self, level=2): def get_thumb_url(self, level=2):
return self.url.replace("/uploads/", "/thumbnails/{:d}/".format(level)) return self.url.replace("/uploads/", "/thumbnails/{:d}/".format(level))
def getAsDictionary(self, base_url=""): def as_dict(self, base_url=""):
return { return {
"id": self.id, "id": self.id,
"order": self.order, "order": self.order,
@ -1136,7 +1131,7 @@ class PackageUpdateTrigger(enum.Enum):
COMMIT = "New Commit" COMMIT = "New Commit"
TAG = "New Tag" TAG = "New Tag"
def toName(self): def to_name(self):
return self.name.lower() return self.name.lower()
def __str__(self): def __str__(self):
@ -1199,7 +1194,7 @@ class PackageUpdateConfig(db.Model):
return self.last_tag or self.last_commit return self.last_tag or self.last_commit
def get_create_release_url(self): def get_create_release_url(self):
return self.package.getURL("packages.create_release", title=self.get_title(), ref=self.get_ref()) return self.package.get_url("packages.create_release", title=self.get_title(), ref=self.get_ref())
class PackageAlias(db.Model): class PackageAlias(db.Model):
@ -1215,11 +1210,11 @@ class PackageAlias(db.Model):
self.author = author self.author = author
self.name = name self.name = name
def getEditURL(self): def get_edit_url(self):
return url_for("packages.alias_create_edit", author=self.package.author.username, return url_for("packages.alias_create_edit", author=self.package.author.username,
name=self.package.name, alias_id=self.id) name=self.package.name, alias_id=self.id)
def getAsDictionary(self): def as_dict(self):
return f"{self.author}/{self.name}" return f"{self.author}/{self.name}"

@ -66,27 +66,27 @@ class Thread(db.Model):
else: else:
return comment return comment
def getViewURL(self, absolute=False): def get_view_url(self, absolute=False):
if absolute: if absolute:
from ..utils import abs_url_for from ..utils import abs_url_for
return abs_url_for("threads.view", id=self.id) return abs_url_for("threads.view", id=self.id)
else: else:
return url_for("threads.view", id=self.id, _external=False) return url_for("threads.view", id=self.id, _external=False)
def getSubscribeURL(self): def get_subscribe_url(self):
return url_for("threads.subscribe", id=self.id) return url_for("threads.subscribe", id=self.id)
def getUnsubscribeURL(self): def get_unsubscribe_url(self):
return url_for("threads.unsubscribe", id=self.id) return url_for("threads.unsubscribe", id=self.id)
def checkPerm(self, user, perm): def check_perm(self, user, perm):
if not user.is_authenticated: if not user.is_authenticated:
return perm == Permission.SEE_THREAD and not self.private return perm == Permission.SEE_THREAD and not self.private
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
raise Exception("Unknown permission given to Thread.checkPerm()") raise Exception("Unknown permission given to Thread.check_perm()")
isMaintainer = user == self.author or (self.package is not None and self.package.author == user) isMaintainer = user == self.author or (self.package is not None and self.package.author == user)
if self.package: if self.package:
@ -145,16 +145,16 @@ class ThreadReply(db.Model):
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
def get_url(self, absolute=False): def get_url(self, absolute=False):
return self.thread.getViewURL(absolute) + "#reply-" + str(self.id) return self.thread.get_view_url(absolute) + "#reply-" + str(self.id)
def checkPerm(self, user, perm): def check_perm(self, user, perm):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
raise Exception("Unknown permission given to ThreadReply.checkPerm()") raise Exception("Unknown permission given to ThreadReply.check_perm()")
if perm == Permission.EDIT_REPLY: if perm == Permission.EDIT_REPLY:
return user.rank.atLeast(UserRank.NEW_MEMBER if user == self.author else UserRank.MODERATOR) and not self.thread.locked return user.rank.atLeast(UserRank.NEW_MEMBER if user == self.author else UserRank.MODERATOR) and not self.thread.locked
@ -191,7 +191,7 @@ class PackageReview(db.Model):
user_vote = next(filter(lambda vote: vote.user == current_user, votes), None) user_vote = next(filter(lambda vote: vote.user == current_user, votes), None)
return pos, neg, user_vote.is_positive if user_vote else None return pos, neg, user_vote.is_positive if user_vote else None
def getAsDictionary(self, include_package=False): def as_dict(self, include_package=False):
pos, neg, _user = self.get_totals() pos, neg, _user = self.get_totals()
ret = { ret = {
"is_positive": self.rating > 3, "is_positive": self.rating > 3,
@ -209,19 +209,19 @@ class PackageReview(db.Model):
"comment": self.thread.first_reply.comment, "comment": self.thread.first_reply.comment,
} }
if include_package: if include_package:
ret["package"] = self.package.getAsDictionaryKey() ret["package"] = self.package.as_key_dict()
return ret return ret
def asWeight(self): def as_weight(self):
""" """
From (1, 5) to (-1 to 1) From (1, 5) to (-1 to 1)
""" """
return (self.rating - 3.0) / 2.0 return (self.rating - 3.0) / 2.0
def getEditURL(self): def get_edit_url(self):
return self.package.getURL("packages.review") return self.package.get_url("packages.review")
def getDeleteURL(self): def get_delete_url(self):
return url_for("packages.delete_review", return url_for("packages.delete_review",
author=self.package.author.username, author=self.package.author.username,
name=self.package.name, name=self.package.name,
@ -238,14 +238,14 @@ class PackageReview(db.Model):
(pos, neg, _) = self.get_totals() (pos, neg, _) = self.get_totals()
self.score = 3 * (pos - neg) + 1 self.score = 3 * (pos - neg) + 1
def checkPerm(self, user, perm): def check_perm(self, user, perm):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
raise Exception("Unknown permission given to PackageReview.checkPerm()") raise Exception("Unknown permission given to PackageReview.check_perm()")
if perm == Permission.DELETE_REVIEW: if perm == Permission.DELETE_REVIEW:
return user == self.author or user.rank.atLeast(UserRank.MODERATOR) return user == self.author or user.rank.atLeast(UserRank.MODERATOR)

@ -41,10 +41,10 @@ class UserRank(enum.Enum):
def atLeast(self, min): def atLeast(self, min):
return self.value >= min.value return self.value >= min.value
def getTitle(self): def get_title(self):
return self.name.replace("_", " ").title() return self.name.replace("_", " ").title()
def toName(self): def to_name(self):
return self.name.lower() return self.name.lower()
def __str__(self): def __str__(self):
@ -52,7 +52,7 @@ class UserRank(enum.Enum):
@classmethod @classmethod
def choices(cls): def choices(cls):
return [(choice, choice.getTitle()) for choice in cls] return [(choice, choice.get_title()) for choice in cls]
@classmethod @classmethod
def coerce(cls, item): def coerce(cls, item):
@ -93,7 +93,7 @@ class Permission(enum.Enum):
VIEW_AUDIT_DESCRIPTION = "VIEW_AUDIT_DESCRIPTION" VIEW_AUDIT_DESCRIPTION = "VIEW_AUDIT_DESCRIPTION"
# Only return true if the permission is valid for *all* contexts # Only return true if the permission is valid for *all* contexts
# See Package.checkPerm for package-specific contexts # See Package.check_perm for package-specific contexts
def check(self, user): def check(self, user):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
@ -108,10 +108,10 @@ class Permission(enum.Enum):
return user.rank.atLeast(UserRank.EDITOR) return user.rank.atLeast(UserRank.EDITOR)
else: else:
raise Exception("Non-global permission checked globally. Use Package.checkPerm or User.checkPerm instead.") raise Exception("Non-global permission checked globally. Use Package.check_perm or User.check_perm instead.")
@staticmethod @staticmethod
def checkPerm(user, perm): def check_perm(user, perm):
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
@ -213,14 +213,10 @@ class User(db.Model, UserMixin):
self.password = password self.password = password
self.rank = UserRank.NOT_JOINED self.rank = UserRank.NOT_JOINED
def canAccessTodoList(self): def can_access_todo_list(self):
return Permission.APPROVE_NEW.check(self) or \ return Permission.APPROVE_NEW.check(self) or Permission.APPROVE_RELEASE.check(self)
Permission.APPROVE_RELEASE.check(self)
def isClaimed(self): def get_profile_pic_url(self):
return self.rank.atLeast(UserRank.NEW_MEMBER)
def getProfilePicURL(self):
if self.profile_pic: if self.profile_pic:
return self.profile_pic return self.profile_pic
elif self.rank == UserRank.BOT: elif self.rank == UserRank.BOT:
@ -228,14 +224,14 @@ class User(db.Model, UserMixin):
else: else:
return gravatar(self.email or f"{self.username}@content.minetest.net") return gravatar(self.email or f"{self.username}@content.minetest.net")
def checkPerm(self, user, perm): def check_perm(self, user, perm):
if not user.is_authenticated: if not user.is_authenticated:
return False return False
if type(perm) == str: if type(perm) == str:
perm = Permission[perm] perm = Permission[perm]
elif type(perm) != Permission: elif type(perm) != Permission:
raise Exception("Unknown permission given to User.checkPerm()") raise Exception("Unknown permission given to User.check_perm()")
# Members can edit their own packages, and editors can edit any packages # Members can edit their own packages, and editors can edit any packages
if perm == Permission.CHANGE_AUTHOR: if perm == Permission.CHANGE_AUTHOR:
@ -256,7 +252,7 @@ class User(db.Model, UserMixin):
else: else:
raise Exception("Permission {} is not related to users".format(perm.name)) raise Exception("Permission {} is not related to users".format(perm.name))
def canCommentRL(self): def can_comment_ratelimit(self):
from app.models import ThreadReply from app.models import ThreadReply
factor = 1 factor = 1
@ -279,7 +275,7 @@ class User(db.Model, UserMixin):
return True return True
def canOpenThreadRL(self): def can_open_thread_ratelimit(self):
from app.models import Thread from app.models import Thread
factor = 1 factor = 1
@ -291,10 +287,10 @@ class User(db.Model, UserMixin):
factor = 2 factor = 2
hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1) hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
return Thread.query.filter_by(author=self) \ return Thread.query.filter_by(author=self)\
.filter(Thread.created_at > hour_ago).count() < 2 * factor .filter(Thread.created_at > hour_ago).count() < 2 * factor
def canReviewRL(self): def can_review_ratelimit(self):
from app.models import PackageReview from app.models import PackageReview
factor = 1 factor = 1
@ -310,8 +306,7 @@ class User(db.Model, UserMixin):
hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1) hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
return PackageReview.query.filter_by(author=self) \ return PackageReview.query.filter_by(author=self) \
.filter(PackageReview.created_at > hour_ago).count() < 10 * factor .filter(PackageReview.created_at > hour_ago).count() < 10 * factor
def __eq__(self, other): def __eq__(self, other):
if other is None: if other is None:
@ -324,9 +319,9 @@ class User(db.Model, UserMixin):
return self.id == other.id return self.id == other.id
def can_see_edit_profile(self, current_user): def can_see_edit_profile(self, current_user):
return self.checkPerm(current_user, Permission.CHANGE_USERNAMES) or \ return self.check_perm(current_user, Permission.CHANGE_USERNAMES) or \
self.checkPerm(current_user, Permission.CHANGE_EMAIL) or \ self.check_perm(current_user, Permission.CHANGE_EMAIL) or \
self.checkPerm(current_user, Permission.CHANGE_RANK) self.check_perm(current_user, Permission.CHANGE_RANK)
def can_delete(self): def can_delete(self):
from app.models import ForumTopic from app.models import ForumTopic
@ -392,10 +387,10 @@ class NotificationType(enum.Enum):
OTHER = 0 OTHER = 0
def getTitle(self): def get_title(self):
return self.name.replace("_", " ").title() return self.name.replace("_", " ").title()
def toName(self): def to_name(self):
return self.name.lower() return self.name.lower()
def get_description(self): def get_description(self):
@ -430,7 +425,7 @@ class NotificationType(enum.Enum):
@classmethod @classmethod
def choices(cls): def choices(cls):
return [(choice, choice.getTitle()) for choice in cls] return [(choice, choice.get_title()) for choice in cls]
@classmethod @classmethod
def coerce(cls, item): def coerce(cls, item):
@ -512,21 +507,21 @@ class UserNotificationPreferences(db.Model):
self.pref_other = 0 self.pref_other = 0
def get_can_email(self, notification_type): def get_can_email(self, notification_type):
return getattr(self, "pref_" + notification_type.toName()) == 2 return getattr(self, "pref_" + notification_type.to_name()) == 2
def set_can_email(self, notification_type, value): def set_can_email(self, notification_type, value):
value = 2 if value else 0 value = 2 if value else 0
setattr(self, "pref_" + notification_type.toName(), value) setattr(self, "pref_" + notification_type.to_name(), value)
def get_can_digest(self, notification_type): def get_can_digest(self, notification_type):
return getattr(self, "pref_" + notification_type.toName()) >= 1 return getattr(self, "pref_" + notification_type.to_name()) >= 1
def set_can_digest(self, notification_type, value): def set_can_digest(self, notification_type, value):
if self.get_can_email(notification_type): if self.get_can_email(notification_type):
return return
value = 1 if value else 0 value = 1 if value else 0
setattr(self, "pref_" + notification_type.toName(), value) setattr(self, "pref_" + notification_type.to_name(), value)
class UserBan(db.Model): class UserBan(db.Model):

@ -128,7 +128,7 @@ class QueryBuilder:
def toJson(package: Package): def toJson(package: Package):
release_id = releases.get(package.id) release_id = releases.get(package.id)
return package.getAsDictionaryShort(current_app.config["BASE_URL"], release_id=release_id, no_load=True) return package.as_short_dict(current_app.config["BASE_URL"], release_id=release_id, no_load=True)
return [toJson(pkg) for pkg in packages] return [toJson(pkg) for pkg in packages]

@ -173,7 +173,7 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
db.session.rollback() db.session.rollback()
task_url = url_for('tasks.check', id=self.request.id) task_url = url_for('tasks.check', id=self.request.id)
msg = f"{err}\n\n[View Release]({release.getEditURL()}) | [View Task]({task_url})" msg = f"{err}\n\n[View Release]({release.get_edit_url()}) | [View Task]({task_url})"
post_bot_message(release.package, f"Release {release.title} validation failed", msg) post_bot_message(release.package, f"Release {release.title} validation failed", msg)
if "Fails validation" not in release.title: if "Fails validation" not in release.title:
@ -309,7 +309,7 @@ def check_update_config_impl(package):
db.session.add(rel) db.session.add(rel)
msg = "Created release {} (Git Update Detection)".format(rel.title) msg = "Created release {} (Git Update Detection)".format(rel.title)
addSystemAuditLog(AuditSeverity.NORMAL, msg, package.getURL("packages.view"), package) addSystemAuditLog(AuditSeverity.NORMAL, msg, package.get_url("packages.view"), package)
db.session.commit() db.session.commit()
@ -367,7 +367,7 @@ def check_update_config(self, package_id):
.strip() .strip()
msg = "Error: {}.\n\n[Change update configuration]({}) | [View task]({})" \ msg = "Error: {}.\n\n[Change update configuration]({}) | [View task]({})" \
.format(err, package.getURL("packages.update_config"), url_for("tasks.check", id=self.request.id)) .format(err, package.get_url("packages.update_config"), url_for("tasks.check", id=self.request.id))
post_bot_message(package, "Failed to check git repository", msg) post_bot_message(package, "Failed to check git repository", msg)

@ -24,6 +24,6 @@ def updatePackageScores():
db.session.commit() db.session.commit()
for package in Package.query.all(): for package in Package.query.all():
package.recalcScore() package.recalculate_score()
db.session.commit() db.session.commit()

@ -36,7 +36,7 @@ def post_discord_webhook(username: Optional[str], content: str, is_queue: bool,
json["username"] = username json["username"] = username
user = User.query.filter_by(username=username).first() user = User.query.filter_by(username=username).first()
if user: if user:
json["avatar_url"] = user.getProfilePicURL().replace("/./", "/") json["avatar_url"] = user.get_profile_pic_url().replace("/./", "/")
if json["avatar_url"].startswith("/"): if json["avatar_url"].startswith("/"):
json["avatar_url"] = app.config["BASE_URL"] + json["avatar_url"] json["avatar_url"] = app.config["BASE_URL"] + json["avatar_url"]

@ -39,7 +39,7 @@ def search_in_releases(query: str, file_filter: str):
continue continue
elif exit_code == 0: elif exit_code == 0:
results.append({ results.append({
"package": package.getAsDictionaryKey(), "package": package.as_key_dict(),
"lines": handle.stdout.read(), "lines": handle.stdout.read(),
}) })
@ -48,7 +48,7 @@ def search_in_releases(query: str, file_filter: str):
# Create new # Create new
while len(running) < 1 and len(packages) > 0: while len(running) < 1 and len(packages) > 0:
package = packages.pop() package = packages.pop()
release: Optional[PackageRelease] = package.getDownloadRelease() release: Optional[PackageRelease] = package.get_download_release()
if release: if release:
handle = Popen(["zipgrep", query, release.file_path, file_filter], stdout=PIPE, encoding="UTF-8") handle = Popen(["zipgrep", query, release.file_path, file_filter], stdout=PIPE, encoding="UTF-8")
running.append([package, handle]) running.append([package, handle])

@ -20,7 +20,7 @@ def inject_debug():
@app.context_processor @app.context_processor
def inject_functions(): def inject_functions():
check_global_perm = Permission.checkPerm check_global_perm = Permission.check_perm
return dict(abs_url_for=abs_url_for, url_set_query=url_set_query, url_set_anchor=url_set_anchor, return dict(abs_url_for=abs_url_for, url_set_query=url_set_query, url_set_anchor=url_set_anchor,
check_global_perm=check_global_perm, get_headings=get_headings, url_current=url_current) check_global_perm=check_global_perm, get_headings=get_headings, url_current=url_current)
@ -28,7 +28,7 @@ def inject_functions():
@app.context_processor @app.context_processor
def inject_todo(): def inject_todo():
todo_list_count = None todo_list_count = None
if current_user and current_user.is_authenticated and current_user.canAccessTodoList(): if current_user and current_user.is_authenticated and current_user.can_access_todo_list():
todo_list_count = Package.query.filter_by(state=PackageState.READY_FOR_REVIEW).count() todo_list_count = Package.query.filter_by(state=PackageState.READY_FOR_REVIEW).count()
todo_list_count += PackageRelease.query.filter_by(approved=False, task_id=None).count() todo_list_count += PackageRelease.query.filter_by(approved=False, task_id=None).count()

@ -21,11 +21,11 @@
<li class="list-group-item"> <li class="list-group-item">
<div class="row"> <div class="row">
<div class="col-sm-auto text-muted" style="min-width: 250px;"> <div class="col-sm-auto text-muted" style="min-width: 250px;">
<a href="{{ package.getURL('packages.view') }}"> <a href="{{ package.get_url('packages.view') }}">
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}
@ -38,7 +38,7 @@
</div> </div>
<div class="col-sm-auto"> <div class="col-sm-auto">
<a href="{{ package.getURL('packages.view') }}" class="btn btn-sm btn-secondary mr-1"> <a href="{{ package.get_url('packages.view') }}" class="btn btn-sm btn-secondary mr-1">
{{ _("View package") }} {{ _("View package") }}
</a> </a>
<a href="{{ package.donate_url_actual }}" class="btn btn-sm btn-primary" rel="nofollow"> <a href="{{ package.donate_url_actual }}" class="btn btn-sm btn-primary" rel="nofollow">

@ -34,5 +34,5 @@
{{ _("Unsubscribe") }} {{ _("Unsubscribe") }}
</a> <br> </a> <br>
{{ _("This is a '%(type)s' notification.", type=notification.type.getTitle()) }} {{ _("This is a '%(type)s' notification.", type=notification.type.get_title()) }}
{% endblock %} {% endblock %}

@ -33,10 +33,10 @@
</ol> </ol>
<div class="carousel-inner"> <div class="carousel-inner">
{% for package in spotlight_pkgs %} {% for package in spotlight_pkgs %}
{% set cover_image = package.getCoverImageURL() %} {% set cover_image = package.get_cover_image_url() %}
{% set tags = package.tags | sort(attribute="views", reverse=True) %} {% set tags = package.tags | sort(attribute="views", reverse=True) %}
<div class="carousel-item {% if loop.index == 1 %}active{% endif %}"> <div class="carousel-item {% if loop.index == 1 %}active{% endif %}">
<a href="{{ package.getURL("packages.view") }}"> <a href="{{ package.get_url("packages.view") }}">
<div class="embed-responsive embed-responsive-16by9"> <div class="embed-responsive embed-responsive-16by9">
<img class="embed-responsive-item" src="{{ cover_image }}" <img class="embed-responsive-item" src="{{ cover_image }}"
alt="{{ _('%(title)s by %(author)s', title=package.title, author=package.author.display_name) }}"> alt="{{ _('%(title)s by %(author)s', title=package.title, author=package.author.display_name) }}">

@ -2,7 +2,7 @@
<div class="list-group mt-3"> <div class="list-group mt-3">
{% for entry in log %} {% for entry in log %}
<a class="list-group-item list-group-item-action" <a class="list-group-item list-group-item-action"
{% if entry.description and entry.checkPerm(current_user, 'VIEW_AUDIT_DESCRIPTION') %} {% if entry.description and entry.check_perm(current_user, 'VIEW_AUDIT_DESCRIPTION') %}
href="{{ url_for('admin.audit_view', id_=entry.id) }}"> href="{{ url_for('admin.audit_view', id_=entry.id) }}">
{% else %} {% else %}
href="{{ entry.url }}"> href="{{ entry.url }}">
@ -10,7 +10,7 @@
<div class="row {% if entry.severity == entry.severity.NORMAL %}text-muted{% endif %}"> <div class="row {% if entry.severity == entry.severity.NORMAL %}text-muted{% endif %}">
<div class="col-sm-auto text-center" style="width: 50px;" <div class="col-sm-auto text-center" style="width: 50px;"
title="{{ _('Severity: %(sev)s.', sev=entry.severity.getTitle()) }}"> title="{{ _('Severity: %(sev)s.', sev=entry.severity.get_title()) }}">
{% if entry.severity == entry.severity.MODERATION %} {% if entry.severity == entry.severity.MODERATION %}
<i class="fas fa-exclamation-triangle" style="color: yellow;"></i> <i class="fas fa-exclamation-triangle" style="color: yellow;"></i>
{% elif entry.severity == entry.severity.EDITOR %} {% elif entry.severity == entry.severity.EDITOR %}
@ -25,7 +25,7 @@
<img <img
class="img-fluid user-photo img-thumbnail img-thumbnail-1" class="img-fluid user-photo img-thumbnail img-thumbnail-1"
style="max-height: 22px;" style="max-height: 22px;"
src="{{ entry.causer.getProfilePicURL() }}" /> src="{{ entry.causer.get_profile_pic_url() }}" />
<span class="pl-2">{{ entry.causer.username }}</span> <span class="pl-2">{{ entry.causer.username }}</span>
{% else %} {% else %}
@ -50,7 +50,7 @@
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ entry.package.getThumbnailOrPlaceholder() }}" /> src="{{ entry.package.get_thumb_or_placeholder() }}" />
</div> </div>
{% endif %} {% endif %}

@ -5,8 +5,8 @@
{{ _("State") }}: <strong>{{ package.state.value }}</strong> {{ _("State") }}: <strong>{{ package.state.value }}</strong>
</span> </span>
{% for state in package.getNextStates(current_user) %} {% for state in package.get_next_states(current_user) %}
<form class="col-auto" method="post" action="{{ package.getSetStateURL(state) }}"> <form class="col-auto" method="post" action="{{ package.get_set_state_url(state) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input class="btn btn-sm btn-primary" type="submit" value="{{ state.verb() }}" /> <input class="btn btn-sm btn-primary" type="submit" value="{{ state.verb() }}" />
</form> </form>
@ -16,13 +16,13 @@
{% set level = "warning" %} {% set level = "warning" %}
{% if package.releases.filter_by(task_id=None).count() == 0 %} {% if package.releases.filter_by(task_id=None).count() == 0 %}
{% set message %} {% set message %}
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
{% if package.update_config %} {% if package.update_config %}
<a class="btn btn-sm btn-warning float-right" href="{{ package.getURL('packages.create_release') }}"> <a class="btn btn-sm btn-warning float-right" href="{{ package.get_url('packages.create_release') }}">
{{ _("Create release") }} {{ _("Create release") }}
</a> </a>
{% else %} {% else %}
<a class="btn btn-sm btn-warning float-right" href="{{ package.getURL('packages.setup_releases') }}"> <a class="btn btn-sm btn-warning float-right" href="{{ package.get_url('packages.setup_releases') }}">
{{ _("Set up releases") }} {{ _("Set up releases") }}
</a> </a>
{% endif %} {% endif %}
@ -40,8 +40,8 @@
{% elif (package.type == package.type.GAME or package.type == package.type.TXP) and package.screenshots.count() == 0 %} {% elif (package.type == package.type.GAME or package.type == package.type.TXP) and package.screenshots.count() == 0 %}
{% set message = _("You need to add at least one screenshot.") %} {% set message = _("You need to add at least one screenshot.") %}
{% elif package.getMissingHardDependenciesQuery().count() > 0 %} {% elif package.get_missing_hard_dependencies_query().count() > 0 %}
{% set deps = package.getMissingHardDependencies() | join(", ") %} {% set deps = package.get_missing_hard_dependencies() | join(", ") %}
{% set message = _("The following hard dependencies need to be added to ContentDB first: %(deps)s", deps=deps) %} {% set message = _("The following hard dependencies need to be added to ContentDB first: %(deps)s", deps=deps) %}
{% elif topic_error_lvl == "danger" %} {% elif topic_error_lvl == "danger" %}
@ -58,15 +58,15 @@
{% endif %} {% endif %}
{% if package.state == package.state.READY_FOR_REVIEW %} {% if package.state == package.state.READY_FOR_REVIEW %}
{% if not package.getDownloadRelease() %} {% if not package.get_download_release() %}
{{ _("Please wait for the release to be approved.") }} {{ _("Please wait for the release to be approved.") }}
{% elif package.checkPerm(current_user, "APPROVE_NEW") %} {% elif package.check_perm(current_user, "APPROVE_NEW") %}
{{ _("You can now approve this package if you're ready.") }} {{ _("You can now approve this package if you're ready.") }}
{% else %} {% else %}
{{ _("Please wait for the package to be approved.") }} {{ _("Please wait for the package to be approved.") }}
{% endif %} {% endif %}
{% else %} {% else %}
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %} {% if package.check_perm(current_user, "EDIT_PACKAGE") %}
{{ _("You can now submit this package for approval if you're ready.") }} {{ _("You can now submit this package for approval if you're ready.") }}
{% else %} {% else %}
{{ _("This package can be submitted for approval when ready.") }} {{ _("This package can be submitted for approval when ready.") }}
@ -95,7 +95,7 @@
{% if conflicting_modnames %} {% if conflicting_modnames %}
<div class="alert alert-warning"> <div class="alert alert-warning">
<a class="float-right btn btn-sm btn-warning" href="{{ package.getURL('packages.similar') }}"> <a class="float-right btn btn-sm btn-warning" href="{{ package.get_url('packages.similar') }}">
More info More info
</a> </a>
{% if conflicting_modnames | length > 4 %} {% if conflicting_modnames | length > 4 %}
@ -106,7 +106,7 @@
</div> </div>
{% endif %} {% endif %}
{% if not package.review_thread and (package.author == current_user or package.checkPerm(current_user, "APPROVE_NEW")) %} {% if not package.review_thread and (package.author == current_user or package.check_perm(current_user, "APPROVE_NEW")) %}
<div class="alert alert-secondary"> <div class="alert alert-secondary">
<a class="float-right btn btn-sm btn-secondary" href="{{ url_for('threads.new', pid=package.id, title='Package approval comments') }}"> <a class="float-right btn btn-sm btn-secondary" href="{{ url_for('threads.new', pid=package.id, title='Package approval comments') }}">
{{ _("Open Thread") }} {{ _("Open Thread") }}

@ -1,6 +1,6 @@
{% macro render_pkgtile(package, show_author) -%} {% macro render_pkgtile(package, show_author) -%}
<li class="packagetile flex-fill"><a href="{{ package.getURL("packages.view") }}" <li class="packagetile flex-fill"><a href="{{ package.get_url("packages.view") }}"
style="background-image: url({{ package.getThumbnailOrPlaceholder(2) }});"> style="background-image: url({{ package.get_thumb_or_placeholder(2) }});">
<div class="packagegridscrub"></div> <div class="packagegridscrub"></div>
<div class="packagegridinfo"> <div class="packagegridinfo">
<h3> <h3>

@ -1,6 +1,6 @@
{% macro render_releases_edit(releases, package) %} {% macro render_releases_edit(releases, package) %}
{% for rel in releases %} {% for rel in releases %}
<a class="list-group-item list-group-item-action" href="{{ rel.getEditURL() }}"> <a class="list-group-item list-group-item-action" href="{{ rel.get_edit_url() }}">
{{ rel.title }} {{ rel.title }}
<span class="text-muted ml-1"> <span class="text-muted ml-1">
{% if rel.min_rel and rel.max_rel %} {% if rel.min_rel and rel.max_rel %}
@ -26,8 +26,8 @@
{% macro render_releases_download(releases, package, current_user) %} {% macro render_releases_download(releases, package, current_user) %}
{% for rel in releases %} {% for rel in releases %}
{% if rel.approved or package.checkPerm(current_user, "MAKE_RELEASE") or rel.checkPerm(current_user, "APPROVE_RELEASE") %} {% if rel.approved or package.check_perm(current_user, "MAKE_RELEASE") or rel.check_perm(current_user, "APPROVE_RELEASE") %}
<a class="list-group-item list-group-item-action" href="{{ rel.getDownloadURL() }}"> <a class="list-group-item list-group-item-action" href="{{ rel.get_download_url() }}">
{{ rel.title }} {{ rel.title }}
<span class="text-muted ml-1"> <span class="text-muted ml-1">
{% if rel.min_rel and rel.max_rel %} {% if rel.min_rel and rel.max_rel %}
@ -55,8 +55,8 @@
{% macro render_releases(releases, package, current_user) -%} {% macro render_releases(releases, package, current_user) -%}
{% for rel in releases %} {% for rel in releases %}
<div class="list-group-item"> <div class="list-group-item">
<a class="btn btn-sm btn-primary float-right" href="{{ rel.getEditURL() }}"> <a class="btn btn-sm btn-primary float-right" href="{{ rel.get_edit_url() }}">
{% if not rel.task_id and not rel.approved and rel.checkPerm(current_user, "APPROVE_RELEASE") %} {% if not rel.task_id and not rel.approved and rel.check_perm(current_user, "APPROVE_RELEASE") %}
{{ _("Edit / Approve") }} {{ _("Edit / Approve") }}
{% else %} {% else %}
{{ _("Edit") }} {{ _("Edit") }}
@ -65,7 +65,7 @@
{% if not rel.approved %}<i>{% endif %} {% if not rel.approved %}<i>{% endif %}
<a href="{{ rel.getDownloadURL() }}" rel="nofollow" download="{{ rel.getDownloadFileName() }}"> <a href="{{ rel.get_download_url() }}" rel="nofollow" download="{{ rel.get_download_filename() }}">
{{ rel.title }} {{ rel.title }}
</a> </a>
@ -88,8 +88,8 @@
{{ _("created %(date)s", date=rel.releaseDate | date) }}. {{ _("created %(date)s", date=rel.releaseDate | date) }}.
</small> </small>
{% if (package.checkPerm(current_user, "MAKE_RELEASE") or rel.checkPerm(current_user, "APPROVE_RELEASE")) and rel.task_id %} {% if (package.check_perm(current_user, "MAKE_RELEASE") or rel.check_perm(current_user, "APPROVE_RELEASE")) and rel.task_id %}
<a href="{{ url_for('tasks.check', id=rel.task_id, r=package.getURL("packages.view")) }}"> <a href="{{ url_for('tasks.check', id=rel.task_id, r=package.get_url("packages.view")) }}">
{{ _("Importing...") }} {{ _("Importing...") }}
</a> </a>
{% elif not rel.approved %} {% elif not rel.approved %}

@ -27,7 +27,7 @@
<a id="{{ review_anchor }}"></a> <a id="{{ review_anchor }}"></a>
<div class="col-md-1 p-1"> <div class="col-md-1 p-1">
<a href="{{ url_for('users.profile', username=review.author.username) }}"> <a href="{{ url_for('users.profile', username=review.author.username) }}">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ review.author.getProfilePicURL() }}"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ review.author.get_profile_pic_url() }}">
</a> </a>
</div> </div>
<div class="col-md-auto pl-1 pr-3 pt-2 text-center" style=" font-size: 200%;"> <div class="col-md-auto pl-1 pr-3 pt-2 text-center" style=" font-size: 200%;">
@ -58,7 +58,7 @@
<div class="card-body markdown"> <div class="card-body markdown">
{% if current_user == review.author %} {% if current_user == review.author %}
<a class="btn btn-primary btn-sm ml-1 float-right" <a class="btn btn-primary btn-sm ml-1 float-right"
href="{{ review.package.getURL("packages.review") }}"> href="{{ review.package.get_url("packages.review") }}">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</a> </a>
{% endif %} {% endif %}
@ -71,7 +71,7 @@
<div class="btn-toolbar mt-2 mb-0"> <div class="btn-toolbar mt-2 mb-0">
{% if show_package_link %} {% if show_package_link %}
<a class="btn btn-primary mr-1" href="{{ review.package.getURL("packages.view") }}"> <a class="btn btn-primary mr-1" href="{{ review.package.get_url("packages.view") }}">
{{ _("%(title)s by %(author)s", {{ _("%(title)s by %(author)s",
title="<b>" | safe + review.package.title + "</b>" | safe, title="<b>" | safe + review.package.title + "</b>" | safe,
author=review.package.author.display_name) }} author=review.package.author.display_name) }}
@ -105,7 +105,7 @@
<div class="card-header"> <div class="card-header">
{{ _("Review") }} {{ _("Review") }}
</div> </div>
<form method="post" action="{{ package.getURL("packages.review") }}" class="card-body"> <form method="post" action="{{ package.get_url("packages.review") }}" class="card-body">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<p> <p>
{{ _("Do you recommend this %(type)s?", type=package.type.text | lower) }} {{ _("Do you recommend this %(type)s?", type=package.type.text | lower) }}
@ -148,7 +148,7 @@
<div class="card-header"> <div class="card-header">
{{ _("Review") }} {{ _("Review") }}
</div> </div>
<form method="post" action="{{ package.getURL("packages.review") }}" class="card-body"> <form method="post" action="{{ package.get_url("packages.review") }}" class="card-body">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<p> <p>
{{ _("Do you recommend this %(type)s?", type=package.type.text | lower) }} {{ _("Do you recommend this %(type)s?", type=package.type.text | lower) }}

@ -21,7 +21,7 @@
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
{% for package in user.packages.filter_by(state='APPROVED').all() %} {% for package in user.packages.filter_by(state='APPROVED').all() %}
<a class="dropdown-item" href="{{ package.getURL('packages.statistics') }}"> <a class="dropdown-item" href="{{ package.get_url('packages.statistics') }}">
{{ package.title }} {{ package.title }}
</a> </a>
{% endfor %} {% endfor %}

@ -4,7 +4,7 @@
<li class="row my-2 mx-0"> <li class="row my-2 mx-0">
<div class="col-md-1 p-1"> <div class="col-md-1 p-1">
<a href="{{ url_for('users.profile', username=r.author.username) }}"> <a href="{{ url_for('users.profile', username=r.author.username) }}">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ r.author.getProfilePicURL() }}"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ r.author.get_profile_pic_url() }}">
</a> </a>
</div> </div>
<div class="col pr-0"> <div class="col pr-0">
@ -28,7 +28,7 @@
{% endif %} {% endif %}
{% if r.author.rank == r.author.rank.BOT %} {% if r.author.rank == r.author.rank.BOT %}
<span class="badge badge-dark"> <span class="badge badge-dark">
{{ r.author.rank.getTitle() }} {{ r.author.rank.get_title() }}
</span> </span>
{% endif %} {% endif %}
@ -39,7 +39,7 @@
</div> </div>
<div class="card-body markdown"> <div class="card-body markdown">
{% if r.checkPerm(current_user, "DELETE_REPLY") %} {% if r.check_perm(current_user, "DELETE_REPLY") %}
<a class="float-right btn btn-secondary btn-sm ml-2" <a class="float-right btn btn-secondary btn-sm ml-2"
href="{{ url_for('threads.delete_reply', id=thread.id, reply=r.id) }}"> href="{{ url_for('threads.delete_reply', id=thread.id, reply=r.id) }}">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
@ -55,10 +55,10 @@
{% if current_user == thread.author and thread.review and thread.first_reply == r %} {% if current_user == thread.author and thread.review and thread.first_reply == r %}
<a class="float-right btn btn-primary btn-sm ml-2" <a class="float-right btn btn-primary btn-sm ml-2"
href="{{ thread.review.package.getURL('packages.review') }}"> href="{{ thread.review.package.get_url('packages.review') }}">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</a> </a>
{% elif r.checkPerm(current_user, "EDIT_REPLY") %} {% elif r.check_perm(current_user, "EDIT_REPLY") %}
<a class="float-right btn btn-primary btn-sm ml-2" <a class="float-right btn btn-primary btn-sm ml-2"
href="{{ url_for('threads.edit_reply', id=thread.id, reply=r.id) }}"> href="{{ url_for('threads.edit_reply', id=thread.id, reply=r.id) }}">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
@ -68,7 +68,7 @@
{{ r.comment | markdown }} {{ r.comment | markdown }}
{% if thread.first_reply == r and thread.review %} {% if thread.first_reply == r and thread.review %}
{{ render_review_vote(thread.review, current_user, thread.getViewURL()) }} {{ render_review_vote(thread.review, current_user, thread.get_view_url()) }}
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -81,7 +81,7 @@
<li class="row my-2 mx-0 align-items-center"> <li class="row my-2 mx-0 align-items-center">
<div class="col-md-1 p-1"> <div class="col-md-1 p-1">
<a href="{{ url_for('users.profile', username=r.author.username) }}"> <a href="{{ url_for('users.profile', username=r.author.username) }}">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ r.author.getProfilePicURL() }}"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ r.author.get_profile_pic_url() }}">
</a> </a>
</div> </div>
<div class="col-auto"> <div class="col-auto">
@ -137,7 +137,7 @@
<div class="row mt-0 mb-4 comments mx-0"> <div class="row mt-0 mb-4 comments mx-0">
<div class="col-md-1 p-1"> <div class="col-md-1 p-1">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" <img class="img-fluid user-photo img-thumbnail img-thumbnail-1"
src="{{ current_user.getProfilePicURL() }}"> src="{{ current_user.get_profile_pic_url() }}">
</div> </div>
<div class="col pr-0"> <div class="col pr-0">
<div class="card"> <div class="card">
@ -146,12 +146,12 @@
<a name="reply"></a> <a name="reply"></a>
</div> </div>
{% if not current_user.canCommentRL() %} {% if not current_user.can_comment_ratelimit() %}
<div class="card-body"> <div class="card-body">
<textarea class="form-control" readonly disabled>{{ _("Please wait before commenting again.") }}</textarea><br /> <textarea class="form-control" readonly disabled>{{ _("Please wait before commenting again.") }}</textarea><br />
<input class="btn btn-primary" type="submit" disabled value="Comment" /> <input class="btn btn-primary" type="submit" disabled value="Comment" />
</div> </div>
{% elif not thread.checkPerm(current_user, "COMMENT_THREAD") %} {% elif not thread.check_perm(current_user, "COMMENT_THREAD") %}
<div class="card-body"> <div class="card-body">
{% if thread.locked %} {% if thread.locked %}
<textarea class="form-control" readonly disabled>{{ _("This thread has been locked.") }}</textarea><br /> <textarea class="form-control" readonly disabled>{{ _("This thread has been locked.") }}</textarea><br />
@ -266,7 +266,7 @@
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ t.package.getThumbnailOrPlaceholder() }}" /><br /> src="{{ t.package.get_thumb_or_placeholder() }}" /><br />
<span class="pl-2"> <span class="pl-2">
{{ t.package.title }} {{ t.package.title }}

@ -4,11 +4,11 @@
{% set config = package.update_config %} {% set config = package.update_config %}
<li class="list-group-item"> <li class="list-group-item">
<div class="row"> <div class="row">
<a class="col-sm-auto text-muted" style="min-width: 200px;" href="{{ package.getURL("packages.view") }}"> <a class="col-sm-auto text-muted" style="min-width: 200px;" href="{{ package.get_url("packages.view") }}">
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}
@ -29,7 +29,7 @@
<div class="col-sm-auto"> <div class="col-sm-auto">
{% if not show_config %} {% if not show_config %}
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
<a class="btn btn-sm btn-primary mr-2" href="{{ config.get_create_release_url() }}"> <a class="btn btn-sm btn-primary mr-2" href="{{ config.get_create_release_url() }}">
<i class="fas fa-plus mr-1"></i> <i class="fas fa-plus mr-1"></i>
{{ _("Release") }} {{ _("Release") }}
@ -42,8 +42,8 @@
{{ _("Repo") }} {{ _("Repo") }}
</a> </a>
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
<a class="btn btn-sm btn-secondary" href="{{ package.getURL("packages.update_config") }}"> <a class="btn btn-sm btn-secondary" href="{{ package.get_url("packages.update_config") }}">
<i class="fas fa-cog mr-1"></i> <i class="fas fa-cog mr-1"></i>
{{ _("Update settings") }} {{ _("Update settings") }}
</a> </a>
@ -60,13 +60,13 @@
{% macro render_mtsupport_packages(packages, current_user, show_config=False) -%} {% macro render_mtsupport_packages(packages, current_user, show_config=False) -%}
<div class="list-group mt-3"> <div class="list-group mt-3">
{% for package in packages %} {% for package in packages %}
<a class="list-group-item list-group-item-action" href="{{ package.getURL('packages.list_releases') }}"> <a class="list-group-item list-group-item-action" href="{{ package.get_url('packages.list_releases') }}">
<div class="row"> <div class="row">
<div class="col-sm-auto text-muted" style="min-width: 200px;"> <div class="col-sm-auto text-muted" style="min-width: 200px;">
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}
@ -74,7 +74,7 @@
</div> </div>
<div class="col-sm"> <div class="col-sm">
{% set release = package.getDownloadRelease() %} {% set release = package.get_download_release() %}
{% if release %} {% if release %}
{{ release.min_rel.name }} - {{ release.max_rel.name }} {{ release.min_rel.name }} - {{ release.max_rel.name }}
{% endif %} {% endif %}

@ -23,13 +23,13 @@
<td>{{ topic.name or ""}}</td> <td>{{ topic.name or ""}}</td>
<td>{{ topic.created_at | date }}</td> <td>{{ topic.created_at | date }}</td>
<td class="btn-group"> <td class="btn-group">
{% if current_user == topic.author or topic.author.checkPerm(current_user, "CHANGE_AUTHOR") %} {% if current_user == topic.author or topic.author.check_perm(current_user, "CHANGE_AUTHOR") %}
<a class="btn btn-primary" <a class="btn btn-primary"
href="{{ url_for('packages.create_edit', author=topic.author.username, repo=topic.getRepoURL(), forums=topic.topic_id, title=topic.title, bname=topic.name) }}"> href="{{ url_for('packages.create_edit', author=topic.author.username, repo=topic.getRepoURL(), forums=topic.topic_id, title=topic.title, bname=topic.name) }}">
{{ _("Create") }} {{ _("Create") }}
</a> </a>
{% endif %} {% endif %}
{% if show_discard and current_user.is_authenticated and topic.checkPerm(current_user, "TOPIC_DISCARD") %} {% if show_discard and current_user.is_authenticated and topic.check_perm(current_user, "TOPIC_DISCARD") %}
<a class="btn btn-{% if topic.discarded %}success{% else %}danger{% endif %} topic-discard" data-tid={{ topic.topic_id }}> <a class="btn btn-{% if topic.discarded %}success{% else %}danger{% endif %} topic-discard" data-tid={{ topic.topic_id }}>
{% if topic.discarded %} {% if topic.discarded %}
{{ _("Show") }} {{ _("Show") }}
@ -59,7 +59,7 @@
{% if show_author %} {% if show_author %}
by <a href="{{ url_for('users.profile', username=topic.author.username) }}">{{ topic.author.display_name }}</a> by <a href="{{ url_for('users.profile', username=topic.author.username) }}">{{ topic.author.display_name }}</a>
{% endif %} {% endif %}
{% if topic.author == current_user or topic.author.checkPerm(current_user, "CHANGE_AUTHOR") %} {% if topic.author == current_user or topic.author.check_perm(current_user, "CHANGE_AUTHOR") %}
| |
<a href="{{ url_for('packages.create_edit', author=topic.author.username, repo=topic.getRepoURL(), forums=topic.topic_id, title=topic.title, bname=topic.name) }}"> <a href="{{ url_for('packages.create_edit', author=topic.author.username, repo=topic.getRepoURL(), forums=topic.topic_id, title=topic.title, bname=topic.name) }}">
{{ _("Create") }} {{ _("Create") }}

@ -36,7 +36,7 @@
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ n.package.getThumbnailOrPlaceholder() }}" /> src="{{ n.package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ n.package.title }} {{ n.package.title }}
@ -53,7 +53,7 @@
<img <img
class="img-fluid user-photo img-thumbnail img-thumbnail-1" class="img-fluid user-photo img-thumbnail img-thumbnail-1"
style="max-height: 22px;" style="max-height: 22px;"
src="{{ n.causer.getProfilePicURL() }}" /> src="{{ n.causer.get_profile_pic_url() }}" />
</div> </div>
</div> </div>
</a> </a>
@ -74,7 +74,7 @@
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ n.package.getThumbnailOrPlaceholder() }}" /> src="{{ n.package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ n.package.title }} {{ n.package.title }}
@ -91,7 +91,7 @@
<img <img
class="img-fluid user-photo img-thumbnail img-thumbnail-1" class="img-fluid user-photo img-thumbnail img-thumbnail-1"
style="max-height: 22px;" style="max-height: 22px;"
src="{{ n.causer.getProfilePicURL() }}" /> src="{{ n.causer.get_profile_pic_url() }}" />
</div> </div>
</div> </div>
</a> </a>

@ -5,11 +5,11 @@
{% endblock %} {% endblock %}
{% block link %} {% block link %}
<a href="{{ package.getURL("packages.view") }}">{{ package.title }}</a> <a href="{{ package.get_url("packages.view") }}">{{ package.title }}</a>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<a class="btn btn-secondary" href="{{ package.getURL("packages.alias_list") }}"> <a class="btn btn-secondary" href="{{ package.get_url("packages.alias_list") }}">
{{ _("Back to Aliases") }} {{ _("Back to Aliases") }}
</a> </a>

@ -5,18 +5,18 @@
{% endblock %} {% endblock %}
{% block link %} {% block link %}
<a href="{{ package.getURL("packages.view") }}">{{ package.title }}</a> <a href="{{ package.get_url("packages.view") }}">{{ package.title }}</a>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<a class="btn btn-primary float-right" href="{{ package.getURL("packages.alias_create_edit") }}"> <a class="btn btn-primary float-right" href="{{ package.get_url("packages.alias_create_edit") }}">
{{ _("Create") }} {{ _("Create") }}
</a> </a>
<h1>{{ _("Aliases for %(title)s by %(author)s", title=self.link(), author=package.author.display_name) }}</h1> <h1>{{ _("Aliases for %(title)s by %(author)s", title=self.link(), author=package.author.display_name) }}</h1>
<div class="list-group"> <div class="list-group">
{% for alias in package.aliases %} {% for alias in package.aliases %}
<a class="list-group-item list-group-item-action" href="{{ alias.getEditURL() }}"> <a class="list-group-item list-group-item-action" href="{{ alias.get_edit_url() }}">
{{ alias.author }} / {{ alias.name }} {{ alias.author }} / {{ alias.name }}
</a> </a>
{% else %} {% else %}

@ -68,7 +68,7 @@
<div class="row"> <div class="row">
{{ render_field(form.type, class_="pkg_meta col-sm-2") }} {{ render_field(form.type, class_="pkg_meta col-sm-2") }}
{{ render_field(form.title, class_="pkg_meta col-sm-6") }} {{ render_field(form.title, class_="pkg_meta col-sm-6") }}
{% if package and package.approved and not package.checkPerm(current_user, "CHANGE_NAME") %} {% if package and package.approved and not package.check_perm(current_user, "CHANGE_NAME") %}
{{ render_field(form.name, class_="pkg_meta col-sm-4", {{ render_field(form.name, class_="pkg_meta col-sm-4",
readonly=True, hint=_("Please open a thread to request a name change")) }} readonly=True, hint=_("Please open a thread to request a name change")) }}
{% else %} {% else %}

@ -10,8 +10,8 @@
{% endblock %} {% endblock %}
{% block headextra %} {% block headextra %}
{% if package.getThumbnailURL(3, True) %} {% if package.get_thumb_url(3, True) %}
<meta name="og:image" content="{{ package.getThumbnailURL(3, True) }}"/> <meta name="og:image" content="{{ package.get_thumb_url(3, True) }}"/>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
@ -21,40 +21,40 @@
<h1 class="mb-5"> <h1 class="mb-5">
{{ _("Community Hub") }} - {{ _("Community Hub") }} -
<a href="{{ package.getURL('packages.view') }}"> <a href="{{ package.get_url('packages.view') }}">
{{ _('%(title)s by %(author)s', title=package.title, author=package.author.display_name) }} {{ _('%(title)s by %(author)s', title=package.title, author=package.author.display_name) }}
</a> </a>
</h1> </h1>
<a href="{{ url_for('packages.list_all', sort='approved_at', order='desc', game=package.getId()) }}" class="btn btn-secondary float-right"> <a href="{{ url_for('packages.list_all', sort='approved_at', order='desc', game=package.get_id()) }}" class="btn btn-secondary float-right">
{{ _("See more") }} {{ _("See more") }}
</a> </a>
<h2 class="my-3">{{ _("Recently Added") }}</h2> <h2 class="my-3">{{ _("Recently Added") }}</h2>
{{ render_pkggrid(new) }} {{ render_pkggrid(new) }}
<a href="{{ url_for('packages.list_all', sort='last_release', order='desc', game=package.getId()) }}" class="btn btn-secondary float-right"> <a href="{{ url_for('packages.list_all', sort='last_release', order='desc', game=package.get_id()) }}" class="btn btn-secondary float-right">
{{ _("See more") }} {{ _("See more") }}
</a> </a>
<h2 class="my-3">{{ _("Recently Updated") }}</h2> <h2 class="my-3">{{ _("Recently Updated") }}</h2>
{{ render_pkggrid(updated) }} {{ 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"> <a href="{{ url_for('packages.list_all', type='mod', sort='score', order='desc', game=package.get_id()) }}" class="btn btn-secondary float-right">
{{ _("See more") }} {{ _("See more") }}
</a> </a>
<h2 class="my-3">{{ _("Top Mods") }}</h2> <h2 class="my-3">{{ _("Top Mods") }}</h2>
{{ render_pkggrid(pop_mod) }} {{ render_pkggrid(pop_mod) }}
<a href="{{ url_for('packages.list_all', type='txp', sort='score', order='desc', game=package.getId()) }}" class="btn btn-secondary float-right"> <a href="{{ url_for('packages.list_all', type='txp', sort='score', order='desc', game=package.get_id()) }}" class="btn btn-secondary float-right">
{{ _("See more") }} {{ _("See more") }}
</a> </a>
<h2 class="my-3">{{ _("Top Texture Packs") }}</h2> <h2 class="my-3">{{ _("Top Texture Packs") }}</h2>
{{ render_pkggrid(pop_txp) }} {{ render_pkggrid(pop_txp) }}
<a href="{{ url_for('packages.list_all', sort='reviews', order='desc', game=package.getId()) }}" class="btn btn-secondary float-right"> <a href="{{ url_for('packages.list_all', sort='reviews', order='desc', game=package.get_id()) }}" class="btn btn-secondary float-right">
{{ _("See more") }} {{ _("See more") }}
</a> </a>
<h2 class="my-3">{{ _("Highest Reviewed") }}</h2> <h2 class="my-3">{{ _("Highest Reviewed") }}</h2>

@ -36,7 +36,7 @@
{% for support in package.get_sorted_game_support() %} {% for support in package.get_sorted_game_support() %}
<a class="list-group-item list-group-item-action" <a class="list-group-item list-group-item-action"
href="{{ support.game.getURL('packages.view') }}"> href="{{ support.game.get_url('packages.view') }}">
<div class="row"> <div class="row">
<span class="col-5"> <span class="col-5">
{{ _("%(title)s by %(display_name)s", {{ _("%(title)s by %(display_name)s",

@ -9,8 +9,8 @@
{% endblock %} {% endblock %}
{% block headextra %} {% block headextra %}
{% if package.getThumbnailURL(3, True) -%} {% if package.get_thumb_url(3, True) -%}
<meta name="og:image" content="{{ package.getThumbnailURL(3, True) }}"/> <meta name="og:image" content="{{ package.get_thumb_url(3, True) }}"/>
{%- endif %} {%- endif %}
{% endblock %} {% endblock %}

@ -6,11 +6,11 @@
<div class="row"> <div class="row">
<div class="col-md-3 mb-4"> <div class="col-md-3 mb-4">
<div class="list-group"> <div class="list-group">
<a class="list-group-item list-group-item-action" href="{{ package.getURL("packages.view") }}"> <a class="list-group-item list-group-item-action" href="{{ package.get_url("packages.view") }}">
<span class="row m-0 p-0"> <span class="row m-0 p-0">
<span class="col-auto m-0 p-0"> <span class="col-auto m-0 p-0">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" <img class="img-fluid user-photo img-thumbnail img-thumbnail-1"
src="{{ package.getThumbnailOrPlaceholder(1) }}" alt="{{ _('Thumbnail') }}" style="max-height: 20px;"> src="{{ package.get_thumb_or_placeholder(1) }}" alt="{{ _('Thumbnail') }}" style="max-height: 20px;">
</span> </span>
<span class="col m-0 p-0 pl-2"> <span class="col m-0 p-0 pl-2">
{{ package.title }} {{ package.title }}

@ -11,13 +11,13 @@
<form method="POST" action=""> <form method="POST" action="">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
{{ render_field(form.title) }} {{ render_field(form.title) }}
{% else %} {% else %}
{{ _("Title") }}: {{ release.title }} {{ _("Title") }}: {{ release.title }}
{% endif %} {% endif %}
{% if package.checkPerm(current_user, "CHANGE_RELEASE_URL") %} {% if package.check_perm(current_user, "CHANGE_RELEASE_URL") %}
{{ render_field(form.url) }} {{ render_field(form.url) }}
{% else %} {% else %}
{{ _("URL") }}: <a href="{{ release.url }}">{{ release.url }}</a><br /> {{ _("URL") }}: <a href="{{ release.url }}">{{ release.url }}</a><br />
@ -29,14 +29,14 @@
{% if release.task_id %} {% if release.task_id %}
{{ _("Importing...") }} {{ _("Importing...") }}
<a href="{{ url_for('tasks.check', id=release.task_id, r=release.getEditURL()) }}">{{ _("view task") }}</a><br /> <a href="{{ url_for('tasks.check', id=release.task_id, r=release.get_edit_url()) }}">{{ _("view task") }}</a><br />
{% if package.checkPerm(current_user, "CHANGE_RELEASE_URL") %} {% if package.check_perm(current_user, "CHANGE_RELEASE_URL") %}
{{ render_field(form.task_id) }} {{ render_field(form.task_id) }}
{% endif %} {% endif %}
<br /> <br />
{% else %} {% else %}
{% if release.checkPerm(current_user, "APPROVE_RELEASE") %} {% if release.check_perm(current_user, "APPROVE_RELEASE") %}
{{ render_checkbox_field(form.approved, class_="my-3") }} {{ render_checkbox_field(form.approved, class_="my-3") }}
{% else %} {% else %}
{{ _("Approved") }}: {{ release.approved }} {{ _("Approved") }}: {{ release.approved }}
@ -69,8 +69,8 @@
<h2 class="mt-5">{{ _("Delete Release") }}</h2> <h2 class="mt-5">{{ _("Delete Release") }}</h2>
{% if release.checkPerm(current_user, "DELETE_RELEASE") %} {% if release.check_perm(current_user, "DELETE_RELEASE") %}
<form method="POST" action="{{ release.getDeleteURL() }}" class="alert alert-secondary mb-5"> <form method="POST" action="{{ release.get_delete_url() }}" class="alert alert-secondary mb-5">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input class="btn btn-sm btn-danger float-right" type="submit" value="{{ _('Delete') }}"> <input class="btn btn-sm btn-danger float-right" type="submit" value="{{ _('Delete') }}">
<b>{{ _("This is permanent.") }}</b> <b>{{ _("This is permanent.") }}</b>

@ -9,7 +9,7 @@
{% if package.update_config %} {% if package.update_config %}
<p class="alert alert-secondary mb-4"> <p class="alert alert-secondary mb-4">
<a class="float-right btn btn-sm btn-secondary" href="{{ package.getURL("packages.update_config") }}">{{ _("Settings") }}</a> <a class="float-right btn btn-sm btn-secondary" href="{{ package.get_url("packages.update_config") }}">{{ _("Settings") }}</a>
{% if package.update_config.make_release %} {% if package.update_config.make_release %}
{{ _("You have automatic releases enabled.") }} {{ _("You have automatic releases enabled.") }}
{% else %} {% else %}
@ -20,12 +20,12 @@
{% else %} {% else %}
<p class="alert alert-info mb-4"> <p class="alert alert-info mb-4">
{% if package.repo %} {% if package.repo %}
<a class="float-right btn btn-sm btn-info" href="{{ package.getURL("packages.setup_releases") }}">{{ _("Set up") }}</a> <a class="float-right btn btn-sm btn-info" href="{{ package.get_url("packages.setup_releases") }}">{{ _("Set up") }}</a>
<i class="fas fa-info mr-2"></i> <i class="fas fa-info mr-2"></i>
{{ _("You can create releases automatically when you push commits or tags to your repository.") }} {{ _("You can create releases automatically when you push commits or tags to your repository.") }}
{% else %} {% else %}
<a class="float-right btn btn-sm btn-info" href="{{ package.getURL("packages.create_edit") }}">{{ _("Add Git repo") }}</a> <a class="float-right btn btn-sm btn-info" href="{{ package.get_url("packages.create_edit") }}">{{ _("Add Git repo") }}</a>
<i class="fas fa-info mr-2"></i> <i class="fas fa-info mr-2"></i>
{{ _("Using Git would allow you to create releases automatically when you push code or tags.") }} {{ _("Using Git would allow you to create releases automatically when you push code or tags.") }}

@ -5,7 +5,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<a class="btn btn-secondary float-right" href="{{ package.getURL("packages.view") }}"> <a class="btn btn-secondary float-right" href="{{ package.get_url("packages.view") }}">
{{ _("Later") }} {{ _("Later") }}
</a> </a>
<h1>{{ self.title() }}</h1> <h1>{{ self.title() }}</h1>
@ -30,13 +30,13 @@
<div class="col-md-6 mt-5"> <div class="col-md-6 mt-5">
<h3 class="mt-0">{{ _("Automatically (Recommended)") }}</h3> <h3 class="mt-0">{{ _("Automatically (Recommended)") }}</h3>
<p> <p>
<a class="btn btn-primary" href="{{ package.getURL("packages.update_config", trigger="commit") }}"> <a class="btn btn-primary" href="{{ package.get_url("packages.update_config", trigger="commit") }}">
{{ _("Rolling Release") }} {{ _("Rolling Release") }}
</a> </a>
<a class="btn btn-primary ml-2" href="{{ package.getURL("packages.update_config", trigger="tag") }}"> <a class="btn btn-primary ml-2" href="{{ package.get_url("packages.update_config", trigger="tag") }}">
{{ _("On Git Tag") }} {{ _("On Git Tag") }}
</a> </a>
{# <a class="btn btn-secondary ml-2" href="{{ package.getURL("packages.update_config") }}">#} {# <a class="btn btn-secondary ml-2" href="{{ package.get_url("packages.update_config") }}">#}
{# {{ _("Advanced") }}#} {# {{ _("Advanced") }}#}
{# </a>#} {# </a>#}
</p> </p>
@ -45,10 +45,10 @@
<div class="col-md-6 mt-5"> <div class="col-md-6 mt-5">
<h3 class="mt-0">{{ _("Manually") }}</h3> <h3 class="mt-0">{{ _("Manually") }}</h3>
<p> <p>
<a class="btn btn-secondary" href="{{ package.getURL("packages.update_config", action="notification") }}"> <a class="btn btn-secondary" href="{{ package.get_url("packages.update_config", action="notification") }}">
{{ _("With reminders") }} {{ _("With reminders") }}
</a> </a>
<a class="btn btn-secondary ml-2" href="{{ package.getURL("packages.create_release") }}"> <a class="btn btn-secondary ml-2" href="{{ package.get_url("packages.create_release") }}">
{{ _("No reminders") }} {{ _("No reminders") }}
</a> </a>
</p> </p>
@ -65,13 +65,13 @@
</p> </p>
<p class="mt-5"> <p class="mt-5">
<a class="btn btn-primary" href="{{ package.getURL("packages.create_edit") }}"> <a class="btn btn-primary" href="{{ package.get_url("packages.create_edit") }}">
{{ _("Add Git repo") }} {{ _("Add Git repo") }}
</a> </a>
<a class="btn btn-secondary ml-2" href="{{ package.getURL("packages.create_release") }}"> <a class="btn btn-secondary ml-2" href="{{ package.get_url("packages.create_release") }}">
{{ _("Create releases manually") }} {{ _("Create releases manually") }}
</a> </a>
<a class="btn btn-secondary ml-2" href="{{ package.getURL("packages.view") }}"> <a class="btn btn-secondary ml-2" href="{{ package.get_url("packages.view") }}">
{{ _("Later") }} {{ _("Later") }}
</a> </a>
</p> </p>

@ -5,26 +5,26 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
<p class="float-right"> <p class="float-right">
{% if package.update_config %} {% if package.update_config %}
<a class="btn btn-secondary" href="{{ package.getURL("packages.update_config") }}"> <a class="btn btn-secondary" href="{{ package.get_url("packages.update_config") }}">
<i class="fas fa-cog mr-1"></i> <i class="fas fa-cog mr-1"></i>
{{ _("Update settings") }} {{ _("Update settings") }}
</a> </a>
{% elif package.repo %} {% elif package.repo %}
<a class="btn btn-secondary" href="{{ package.getURL("packages.setup_releases") }}"> <a class="btn btn-secondary" href="{{ package.get_url("packages.setup_releases") }}">
<i class="fas fa-hat-wizard mr-1"></i> <i class="fas fa-hat-wizard mr-1"></i>
{{ _("Set up automatic releases") }} {{ _("Set up automatic releases") }}
</a> </a>
{% endif %} {% endif %}
<a class="btn btn-secondary ml-1" href="{{ package.getURL("packages.bulk_change_release") }}"> <a class="btn btn-secondary ml-1" href="{{ package.get_url("packages.bulk_change_release") }}">
<i class="fas fa-wrench mr-1"></i> <i class="fas fa-wrench mr-1"></i>
{{ _("Bulk update") }} {{ _("Bulk update") }}
</a> </a>
<a class="btn btn-primary ml-1" href="{{ package.getURL("packages.create_release") }}"> <a class="btn btn-primary ml-1" href="{{ package.get_url("packages.create_release") }}">
<i class="fas fa-plus mr-1"></i> <i class="fas fa-plus mr-1"></i>
{{ _("Create") }} {{ _("Create") }}
</a> </a>
@ -35,7 +35,7 @@
<div class="list-group"> <div class="list-group">
{% from "macros/releases.html" import render_releases_edit, render_releases_download %} {% from "macros/releases.html" import render_releases_edit, render_releases_download %}
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
{{ render_releases_edit(package.releases, package) }} {{ render_releases_edit(package.releases, package) }}
{% else %} {% else %}
{{ render_releases_download(package.releases, package, current_user) }} {{ render_releases_download(package.releases, package, current_user) }}

@ -31,7 +31,7 @@ Remove {{ package.title }}
</small> </small>
</div> </div>
<a class="btn btn-secondary float-right" href="{{ package.getURL("packages.view") }}">{{ _("Cancel") }}</a> <a class="btn btn-secondary float-right" href="{{ package.get_url("packages.view") }}">{{ _("Cancel") }}</a>
<input type="submit" name="delete" value="{{ _('Remove') }}" class="btn btn-danger mr-2" /> <input type="submit" name="delete" value="{{ _('Remove') }}" class="btn btn-danger mr-2" />

@ -5,7 +5,7 @@
{% endblock %} {% endblock %}
{% block link %} {% block link %}
<a href="{{ package.getURL("packages.view") }}">{{ package.title }}</a> <a href="{{ package.get_url("packages.view") }}">{{ package.title }}</a>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -22,7 +22,7 @@
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="row mt-0 mb-4 comments mx-0"> <div class="row mt-0 mb-4 comments mx-0">
<div class="col-md-1 p-1"> <div class="col-md-1 p-1">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ current_user.getProfilePicURL() }}"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ current_user.get_profile_pic_url() }}">
</div> </div>
<div class="col"> <div class="col">
<div class="card"> <div class="card">
@ -50,7 +50,7 @@
</form> </form>
{% if review %} {% if review %}
<form method="POST" action="{{ review.getDeleteURL() }}" class="alert alert-secondary my-5"> <form method="POST" action="{{ review.get_delete_url() }}" class="alert alert-secondary my-5">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input class="btn btn-sm btn-danger float-right" type="submit" value="{{ _('Delete') }}"> <input class="btn btn-sm btn-danger float-right" type="submit" value="{{ _('Delete') }}">
<b>{{ _("Delete review.") }}</b> <b>{{ _("Delete review.") }}</b>

@ -5,7 +5,7 @@
{% endblock %} {% endblock %}
{% block link %} {% block link %}
<a href="{{ package.getURL("packages.view") }}">{{ package.title }}</a> <a href="{{ package.get_url("packages.view") }}">{{ package.title }}</a>
{% endblock %} {% endblock %}
@ -61,7 +61,7 @@
{% else %} {% else %}
<i class="fas fa-minus mr-2"></i> <i class="fas fa-minus mr-2"></i>
{% endif %} {% endif %}
<a href="{{ review.thread.getViewURL() }}"> <a href="{{ review.thread.get_view_url() }}">
{{ review.thread.title }} {{ review.thread.title }}
</a> by {{ review.author.display_name }} </a> by {{ review.author.display_name }}
</th> </th>

@ -14,7 +14,7 @@
{{ render_field(form.title) }} {{ render_field(form.title) }}
{% if package.checkPerm(current_user, "APPROVE_SCREENSHOT") %} {% if package.check_perm(current_user, "APPROVE_SCREENSHOT") %}
{{ render_checkbox_field(form.approved) }} {{ render_checkbox_field(form.approved) }}
{% else %} {% else %}
<p>{{ _("Approved") }}: {{ screenshot.approved }}</p> <p>{{ _("Approved") }}: {{ screenshot.approved }}</p>
@ -24,7 +24,7 @@
</form> </form>
<a href="{{ screenshot.url }}" class="col-md-4 text-right"> <a href="{{ screenshot.url }}" class="col-md-4 text-right">
<img src="{{ screenshot.getThumbnailURL() }}" alt="{{ screenshot.title }}" /> <img src="{{ screenshot.get_thumb_url() }}" alt="{{ screenshot.title }}" />
</a> </a>
</div> </div>
{% endblock %} {% endblock %}

@ -5,8 +5,8 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if package.checkPerm(current_user, "ADD_SCREENSHOTS") %} {% if package.check_perm(current_user, "ADD_SCREENSHOTS") %}
<a href="{{ package.getURL('packages.create_screenshot') }}" class="btn btn-primary float-right"> <a href="{{ package.get_url('packages.create_screenshot') }}" class="btn btn-primary float-right">
<i class="fas fa-plus mr-1"></i> <i class="fas fa-plus mr-1"></i>
{{ _("Add Image") }} {{ _("Add Image") }}
</a> </a>
@ -16,14 +16,14 @@
<ul class="list-group sortable"> <ul class="list-group sortable">
{% set screenshots = package.screenshots.all() %} {% set screenshots = package.screenshots.all() %}
{% for ss in screenshots %} {% for ss in screenshots %}
{% if ss.approved or package.checkPerm(current_user, "ADD_SCREENSHOTS") %} {% if ss.approved or package.check_perm(current_user, "ADD_SCREENSHOTS") %}
<li class="list-group-item" data-id="{{ ss.id }}"> <li class="list-group-item" data-id="{{ ss.id }}">
<div class="row"> <div class="row">
<div class="col-auto text-muted pr-2"> <div class="col-auto text-muted pr-2">
<i class="fas fa-bars"></i> <i class="fas fa-bars"></i>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<img class="img-fluid" style="max-height: 64px;" src="{{ ss.getThumbnailURL() }}" /> <img class="img-fluid" style="max-height: 64px;" src="{{ ss.get_thumb_url() }}" />
</div> </div>
<div class="col"> <div class="col">
{{ ss.title }} {{ ss.title }}
@ -52,9 +52,9 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
<form action="{{ ss.getDeleteURL() }}" method="POST" class="col-auto text-right" role="form"> <form action="{{ ss.get_delete_url() }}" method="POST" class="col-auto text-right" role="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<a class="btn btn-sm btn-primary" href="{{ ss.getEditURL() }}"> <a class="btn btn-sm btn-primary" href="{{ ss.get_edit_url() }}">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</a> </a>
<button type="submit" class="btn btn-sm btn-danger ml-2"> <button type="submit" class="btn btn-sm btn-danger ml-2">
@ -90,7 +90,7 @@
{{ _("The first screenshot in the list above will be the package thumbnail.") }} {{ _("The first screenshot in the list above will be the package thumbnail.") }}
</p> </p>
<div class="client-preview d-flex flex-row align-items-center"> <div class="client-preview d-flex flex-row align-items-center">
<img class="mt-thumb" src="{{ package.getThumbnailOrPlaceholder(1) }}" alt="{{ _('Thumbnail') }}"> <img class="mt-thumb" src="{{ package.get_thumb_or_placeholder(1) }}" alt="{{ _('Thumbnail') }}">
<div class="flex-grow desc align-self-stretch"> <div class="flex-grow desc align-self-stretch">
<p> <p>
<span class="title">{{ package.title }}</span> by {{ package.author.username }} <span class="title">{{ package.title }}</span> by {{ package.author.username }}

@ -13,23 +13,23 @@
{{ _("Review link") }}: {{ _("Review link") }}:
</p> </p>
<pre><code>{{ package.getURL("packages.review", absolute=True) }}</code></pre> <pre><code>{{ package.get_url("packages.review", absolute=True) }}</code></pre>
<h3>{{ _("Badges") }}</h3> <h3>{{ _("Badges") }}</h3>
<p> <p>
{{ package.makeShield("title") | markdown }} {{ package.make_shield("title") | markdown }}
</p> </p>
<p> <p>
<pre><code>{{ package.makeShield("title") }}</code></pre> <pre><code>{{ package.make_shield("title") }}</code></pre>
</p> </p>
<p> <p>
{{ package.makeShield("downloads") | markdown }} {{ package.make_shield("downloads") | markdown }}
</p> </p>
<p> <p>
<pre><code>{{ package.makeShield("downloads") }}</code></pre> <pre><code>{{ package.make_shield("downloads") }}</code></pre>
</p> </p>
{% endblock %} {% endblock %}

@ -5,7 +5,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1><a href="{{ package.getURL("packages.view") }}">{{ package.title }}</a></h1> <h1><a href="{{ package.get_url("packages.view") }}">{{ package.title }}</a></h1>
<h2>{{ self.title() }}</h2> <h2>{{ self.title() }}</h2>
{% if packages_modnames %} {% if packages_modnames %}
@ -18,7 +18,7 @@
<ul> <ul>
{% for pkg in packages %} {% for pkg in packages %}
<li> <li>
<a href="{{ pkg.getURL('packages.view') }}"> <a href="{{ pkg.get_url('packages.view') }}">
{{ _("%(title)s by %(author)s", title=pkg.title, author=pkg.author.display_name) }} {{ _("%(title)s by %(author)s", title=pkg.title, author=pkg.author.display_name) }}
</a> </a>
[{{ pkg.type.text }}] [{{ pkg.type.text }}]

@ -13,7 +13,7 @@
{% block content %} {% block content %}
<div class="float-right"> <div class="float-right">
<a class="btn btn-secondary mr-2" href="{{ package.getURL('packages.stats_csv') }}"> <a class="btn btn-secondary mr-2" href="{{ package.get_url('packages.stats_csv') }}">
<i class="fas fa-download mr-1"></i> <i class="fas fa-download mr-1"></i>
{{ _("Download (.csv)") }} {{ _("Download (.csv)") }}
</a> </a>
@ -21,5 +21,5 @@
{{ render_package_selector(package.author, package=package) }} {{ render_package_selector(package.author, package=package) }}
</div> </div>
<h2 class="mt-0">{{ _("Statistics") }}</h2> <h2 class="mt-0">{{ _("Statistics") }}</h2>
{{ render_package_stats(package.getURL('api.package_stats', start=start, end=end), package.downloads, start or end) }} {{ render_package_stats(package.get_url('api.package_stats', start=start, end=end), package.downloads, start or end) }}
{% endblock %} {% endblock %}

@ -1,5 +1,5 @@
{% set query=package.name %} {% set query=package.name %}
{% set release = package.getDownloadRelease() %} {% set release = package.get_download_release() %}
{% extends "base.html" %} {% extends "base.html" %}
@ -12,8 +12,8 @@
{% endblock %} {% endblock %}
{% block headextra %} {% block headextra %}
{% if package.getThumbnailURL(3, True) -%} {% if package.get_thumb_url(3, True) -%}
<meta name="og:image" content="{{ package.getThumbnailURL(3, True) }}"/> <meta name="og:image" content="{{ package.get_thumb_url(3, True) }}"/>
{%- endif %} {%- endif %}
{% endblock %} {% endblock %}
@ -31,8 +31,8 @@
{% block download_btn %} {% block download_btn %}
{% if release %} {% if release %}
<a class="btn btn-block btn-download" rel="nofollow" download="{{ release.getDownloadFileName() }}" <a class="btn btn-block btn-download" rel="nofollow" download="{{ release.get_download_filename() }}"
href="{{ release.getDownloadURL() }}"> href="{{ release.get_download_url() }}">
<div> <div>
{{ _("Download") }} {{ _("Download") }}
</div> </div>
@ -90,7 +90,7 @@
{% from "macros/package_approval.html" import render_banners %} {% from "macros/package_approval.html" import render_banners %}
{{ render_banners(package, current_user, topic_error, topic_error_lvl, conflicting_modnames) }} {{ render_banners(package, current_user, topic_error, topic_error_lvl, conflicting_modnames) }}
{% if review_thread and review_thread.checkPerm(current_user, "SEE_THREAD") %} {% if review_thread and review_thread.check_perm(current_user, "SEE_THREAD") %}
<h2>{% if review_thread.private %}&#x1f512;{% endif %} {{ review_thread.title }}</h2> <h2>{% if review_thread.private %}&#x1f512;{% endif %} {{ review_thread.title }}</h2>
{% if review_thread.private %} {% if review_thread.private %}
<p><i> <p><i>
@ -105,7 +105,7 @@
</section> </section>
{% endif %} {% endif %}
{% set cover_image = package.getCoverImageURL() %} {% set cover_image = package.get_cover_image_url() %}
<header class="jumbotron pb-3" <header class="jumbotron pb-3"
style="background: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.7)), url('{{ cover_image }}'); style="background: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.7)), url('{{ cover_image }}');
background-size: cover; background-size: cover;
@ -113,20 +113,20 @@
background-position: center;"> background-position: center;">
<div class="container"> <div class="container">
<div class="btn-group float-right mb-4"> <div class="btn-group float-right mb-4">
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %} {% if package.check_perm(current_user, "EDIT_PACKAGE") %}
<a class="btn btn-primary" href="{{ package.getURL('packages.create_edit') }}"> <a class="btn btn-primary" href="{{ package.get_url('packages.create_edit') }}">
<i class="fas fa-pen mr-1"></i> <i class="fas fa-pen mr-1"></i>
{{ _("Edit") }} {{ _("Edit") }}
</a> </a>
{% endif %} {% endif %}
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
<a class="btn btn-primary" href="{{ package.getURL('packages.create_release') }}"> <a class="btn btn-primary" href="{{ package.get_url('packages.create_release') }}">
<i class="fas fa-plus mr-1"></i> <i class="fas fa-plus mr-1"></i>
{{ _("Release") }} {{ _("Release") }}
</a> </a>
{% endif %} {% endif %}
{% if package.checkPerm(current_user, "DELETE_PACKAGE") or package.checkPerm(current_user, "UNAPPROVE_PACKAGE") %} {% if package.check_perm(current_user, "DELETE_PACKAGE") or package.check_perm(current_user, "UNAPPROVE_PACKAGE") %}
<a class="btn btn-danger" href="{{ package.getURL('packages.remove') }}"> <a class="btn btn-danger" href="{{ package.get_url('packages.remove') }}">
<i class="fas fa-trash mr-1"></i> <i class="fas fa-trash mr-1"></i>
{{ _("Remove") }} {{ _("Remove") }}
</a> </a>
@ -179,14 +179,14 @@
<div class="info-row row" style="margin-top: 2rem;"> <div class="info-row row" style="margin-top: 2rem;">
<div class="btn-group-horizontal col"> <div class="btn-group-horizontal col">
<a class="btn" href="{{ url_for('users.profile', username=package.author.username) }}" title="{{ _("Author") }}"> <a class="btn" href="{{ url_for('users.profile', username=package.author.username) }}" title="{{ _("Author") }}">
<img src="{{ package.author.getProfilePicURL() }}" style="max-height: 1em; filter: none"> <img src="{{ package.author.get_profile_pic_url() }}" style="max-height: 1em; filter: none">
<span class="count"> <span class="count">
{{ package.author.display_name }} {{ package.author.display_name }}
</span> </span>
</a> </a>
{% if release %} {% if release %}
<a class="btn" rel="nofollow" href="{{ release.getDownloadURL() }}" title="{{ _('Downloads') }}" <a class="btn" rel="nofollow" href="{{ release.get_download_url() }}" title="{{ _('Downloads') }}"
download="{{ release.getDownloadFileName() }}"> download="{{ release.get_download_filename() }}">
<i class="fas fa-download"></i> <i class="fas fa-download"></i>
<span class="count">{{ package.downloads }}</span> <span class="count">{{ package.downloads }}</span>
</a> </a>
@ -229,7 +229,7 @@
<span class="count">{{ _("Issue Tracker") }}</span> <span class="count">{{ _("Issue Tracker") }}</span>
</a> </a>
{% endif %} {% endif %}
<a class="btn" href="{{ package.getURL('packages.statistics') }}"> <a class="btn" href="{{ package.get_url('packages.statistics') }}">
<i class="fas fa-chart-line"></i> <i class="fas fa-chart-line"></i>
<span class="count">{{ _("Statistics") }}</span> <span class="count">{{ _("Statistics") }}</span>
</a> </a>
@ -247,14 +247,14 @@
<div class="col-md-9" style="padding-right: 45px;"> <div class="col-md-9" style="padding-right: 45px;">
{% set screenshots = package.screenshots.all() %} {% set screenshots = package.screenshots.all() %}
{% if package.checkPerm(current_user, "ADD_SCREENSHOTS") %} {% if package.check_perm(current_user, "ADD_SCREENSHOTS") %}
<a href="{{ package.getURL('packages.screenshots') }}" class="btn btn-primary float-right"> <a href="{{ package.get_url('packages.screenshots') }}" class="btn btn-primary float-right">
<i class="fas fa-images mr-1"></i> <i class="fas fa-images mr-1"></i>
{{ _("Edit") }} {{ _("Edit") }}
</a> </a>
{% endif %} {% endif %}
{% if screenshots or package.checkPerm(current_user, "ADD_SCREENSHOTS") or package.video_url %} {% if screenshots or package.check_perm(current_user, "ADD_SCREENSHOTS") or package.video_url %}
<ul class="gallery"> <ul class="gallery">
{% if package.video_url %} {% if package.video_url %}
<li> <li>
@ -267,12 +267,12 @@
</li> </li>
{% endif %} {% endif %}
{% if screenshots or package.checkPerm(current_user, "ADD_SCREENSHOTS") %} {% if screenshots or package.check_perm(current_user, "ADD_SCREENSHOTS") %}
{% for ss in screenshots %} {% for ss in screenshots %}
{% if ss.approved or package.checkPerm(current_user, "ADD_SCREENSHOTS") %} {% if ss.approved or package.check_perm(current_user, "ADD_SCREENSHOTS") %}
<li> <li>
<a href="{{ ss.url }}" class="gallery-image"> <a href="{{ ss.url }}" class="gallery-image">
<img src="{{ ss.getThumbnailURL() }}" alt="{{ ss.title }}" title="{{ ss.title }}" /> <img src="{{ ss.get_thumb_url() }}" alt="{{ ss.title }}" title="{{ ss.title }}" />
{% if not ss.approved %} {% if not ss.approved %}
<span class="badge bg-dark badge-tr">{{ _("Awaiting review") }}</span> <span class="badge bg-dark badge-tr">{{ _("Awaiting review") }}</span>
{% endif %} {% endif %}
@ -281,7 +281,7 @@
{% endif %} {% endif %}
{% else %} {% else %}
<li> <li>
<a href="{{ package.getURL('packages.create_screenshot') }}"> <a href="{{ package.get_url('packages.create_screenshot') }}">
<i class="fas fa-plus screenshot-add"></i> <i class="fas fa-plus screenshot-add"></i>
</a> </a>
</li> </li>
@ -303,7 +303,7 @@
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{% if has_review %} {% if has_review %}
<p> <p>
<a class="btn btn-primary" href="{{ package.getURL("packages.review") }}"> <a class="btn btn-primary" href="{{ package.get_url("packages.review") }}">
{{ _("Edit Review") }} {{ _("Edit Review") }}
</a> </a>
</p> </p>
@ -324,7 +324,7 @@
{% endif %} {% endif %}
{% if current_user.is_authenticated and current_user.rank.atLeast(current_user.rank.ADMIN) %} {% if current_user.is_authenticated and current_user.rank.atLeast(current_user.rank.ADMIN) %}
<a href="{{ package.getURL('packages.review_votes') }}" class="btn btn-secondary">{{ _("Review Votes") }}</a> <a href="{{ package.get_url('packages.review_votes') }}" class="btn btn-secondary">{{ _("Review Votes") }}</a>
{% endif %} {% endif %}
{{ render_reviews(package.reviews, current_user) }} {{ render_reviews(package.reviews, current_user) }}
@ -337,7 +337,7 @@
{% if package.type == package.type.GAME %} {% if package.type == package.type.GAME %}
<h2>{{ _("Content") }}</h2> <h2>{{ _("Content") }}</h2>
<a href="{{ package.getURL('packages.game_hub') }}" class="btn btn-lg btn-primary"> <a href="{{ package.get_url('packages.game_hub') }}" class="btn btn-lg btn-primary">
{{ _("View content for game") }} {{ _("View content for game") }}
</a> </a>
{% endif %} {% endif %}
@ -348,7 +348,7 @@
{{ self.download_btn() }} {{ self.download_btn() }}
</div> </div>
{% if package.checkPerm(current_user, "MAKE_RELEASE") and package.update_config and package.update_config.outdated_at %} {% if package.check_perm(current_user, "MAKE_RELEASE") and package.update_config and package.update_config.outdated_at %}
{% set config = package.update_config %} {% set config = package.update_config %}
<div class="alert alert-warning"> <div class="alert alert-warning">
<p class="mt-0 my-1"> <p class="mt-0 my-1">
@ -366,7 +366,7 @@
<i class="fas fa-plus mr-1"></i> <i class="fas fa-plus mr-1"></i>
{{ _("Release") }} {{ _("Release") }}
</a> </a>
<a class="btn btn-warning" href="{{ package.getURL("packages.update_config") }}"> <a class="btn btn-warning" href="{{ package.get_url("packages.update_config") }}">
<i class="fas fa-cog mr-1"></i> <i class="fas fa-cog mr-1"></i>
{{ _("Update settings") }} {{ _("Update settings") }}
</a> </a>
@ -391,7 +391,7 @@
{% endif %} {% endif %}
{% if package.type == package.type.GAME %} {% if package.type == package.type.GAME %}
<a href="{{ package.getURL('packages.game_hub') }}" class="btn btn-lg btn-block mb-4 btn-primary"> <a href="{{ package.get_url('packages.game_hub') }}" class="btn btn-lg btn-block mb-4 btn-primary">
{{ _("View content for game") }} {{ _("View content for game") }}
</a> </a>
{% endif %} {% endif %}
@ -401,10 +401,10 @@
<dl> <dl>
<dt>{{ _("Required") }}</dt> <dt>{{ _("Required") }}</dt>
<dd> <dd>
{% for dep in package.getSortedHardDependencies() %} {% for dep in package.get_sorted_hard_dependencies() %}
{%- if dep.package %} {%- if dep.package %}
<a class="badge badge-primary" <a class="badge badge-primary"
href="{{ dep.package.getURL("packages.view") }}"> href="{{ dep.package.get_url("packages.view") }}">
{{ _("%(title)s by %(display_name)s", {{ _("%(title)s by %(display_name)s",
title=dep.package.title, display_name=dep.package.author.display_name) }} title=dep.package.title, display_name=dep.package.author.display_name) }}
</a> </a>
@ -421,14 +421,14 @@
{% endfor %} {% endfor %}
</dd> </dd>
{% set optional_deps=package.getSortedOptionalDependencies() %} {% set optional_deps=package.get_sorted_optional_dependencies() %}
{% if optional_deps %} {% if optional_deps %}
<dt>{{ _("Optional") }}</dt> <dt>{{ _("Optional") }}</dt>
<dd> <dd>
{% for dep in optional_deps %} {% for dep in optional_deps %}
{%- if dep.package %} {%- if dep.package %}
<a class="badge badge-secondary" <a class="badge badge-secondary"
href="{{ dep.package.getURL("packages.view") }}"> href="{{ dep.package.get_url("packages.view") }}">
{{ _("%(title)s by %(display_name)s", {{ _("%(title)s by %(display_name)s",
title=dep.package.title, display_name=dep.package.author.display_name) }} title=dep.package.title, display_name=dep.package.author.display_name) }}
{% elif dep.meta_package %} {% elif dep.meta_package %}
@ -451,8 +451,8 @@
{% set show_unsupported = package.supports_all_games or supported_games == [] %} {% set show_unsupported = package.supports_all_games or supported_games == [] %}
{% if supported_games or unsupported_games or package.type == package.type.MOD %} {% if supported_games or unsupported_games or package.type == package.type.MOD %}
<h3> <h3>
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %} {% if package.check_perm(current_user, "EDIT_PACKAGE") %}
<a href="{{ package.getURL('packages.game_support') }}" class="btn btn-secondary btn-sm float-right"> <a href="{{ package.get_url('packages.game_support') }}" class="btn btn-secondary btn-sm float-right">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</a> </a>
{% endif %} {% endif %}
@ -473,7 +473,7 @@
<div style="max-height: 300px; overflow: hidden auto;" class="mb-3"> <div style="max-height: 300px; overflow: hidden auto;" class="mb-3">
{% for support in supported_games %} {% for support in supported_games %}
<a class="badge badge-secondary" <a class="badge badge-secondary"
href="{{ support.game.getURL('packages.view') }}"> href="{{ support.game.get_url('packages.view') }}">
{{ _("%(title)s by %(display_name)s", {{ _("%(title)s by %(display_name)s",
title=support.game.title, display_name=support.game.author.display_name) }} title=support.game.title, display_name=support.game.author.display_name) }}
</a> </a>
@ -483,14 +483,14 @@
<p> <p>
{{ _("No specific game required") }} {{ _("No specific game required") }}
</p> </p>
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %} {% if package.check_perm(current_user, "EDIT_PACKAGE") %}
<div class="alert alert-warning"> <div class="alert alert-warning">
<p> <p>
{{ _("Is the above correct?") }} {{ _("Is the above correct?") }}
{{ _("You need to either confirm this or tell ContentDB about supported games") }} {{ _("You need to either confirm this or tell ContentDB about supported games") }}
</p> </p>
<a class="btn btn-sm btn-warning" href="{{ package.getURL('packages.game_support') }}"> <a class="btn btn-sm btn-warning" href="{{ package.get_url('packages.game_support') }}">
Update Update
</a> </a>
</div> </div>
@ -504,7 +504,7 @@
<div style="max-height: 300px; overflow: hidden auto;"> <div style="max-height: 300px; overflow: hidden auto;">
{% for support in unsupported_games %} {% for support in unsupported_games %}
<a class="badge badge-danger" <a class="badge badge-danger"
href="{{ support.game.getURL('packages.view') }}"> href="{{ support.game.get_url('packages.view') }}">
<i class="fas fa-times mr-1"></i> <i class="fas fa-times mr-1"></i>
{{ _("%(title)s by %(display_name)s", {{ _("%(title)s by %(display_name)s",
title=support.game.title, display_name=support.game.author.display_name) }} title=support.game.title, display_name=support.game.author.display_name) }}
@ -520,10 +520,10 @@
{{ _("Supported games are determined by an algorithm, and may not be correct.") }} {{ _("Supported games are determined by an algorithm, and may not be correct.") }}
</p> </p>
{% endif %} {% endif %}
{% elif package.checkPerm(current_user, "EDIT_PACKAGE") %} {% elif package.check_perm(current_user, "EDIT_PACKAGE") %}
<p class="alert alert-warning"> <p class="alert alert-warning">
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %} {% if package.check_perm(current_user, "EDIT_PACKAGE") %}
<a href="{{ package.getURL('packages.game_support') }}" class="btn btn-warning btn-sm float-right"> <a href="{{ package.get_url('packages.game_support') }}" class="btn btn-warning btn-sm float-right">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</a> </a>
{% endif %} {% endif %}
@ -570,7 +570,7 @@
{% endfor %} {% endfor %}
{% if current_user in package.maintainers and current_user != package.author %} {% if current_user in package.maintainers and current_user != package.author %}
<form class="mt-2" method="post" action="{{ package.getURL("packages.remove_self_maintainers") }}"> <form class="mt-2" method="post" action="{{ package.get_url("packages.remove_self_maintainers") }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input class="btn btn-sm btn-link p-0" type="submit" value="{{ _('Remove myself') }}" /> <input class="btn btn-sm btn-link p-0" type="submit" value="{{ _('Remove myself') }}" />
</form> </form>
@ -586,25 +586,25 @@
</dl> </dl>
<h3> <h3>
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
<a class="btn btn-primary btn-sm float-right" href="{{ package.getURL("packages.create_release") }}"><i class="fas fa-plus"></i></a> <a class="btn btn-primary btn-sm float-right" href="{{ package.get_url("packages.create_release") }}"><i class="fas fa-plus"></i></a>
{% endif %} {% endif %}
{{ _("Releases") }} {{ _("Releases") }}
</h3> </h3>
<div class="list-group"> <div class="list-group">
{% from "macros/releases.html" import render_releases, render_releases_download %} {% from "macros/releases.html" import render_releases, render_releases_download %}
{% if package.checkPerm(current_user, "MAKE_RELEASE") %} {% if package.check_perm(current_user, "MAKE_RELEASE") %}
{{ render_releases(releases, package, current_user) }} {{ render_releases(releases, package, current_user) }}
{% else %} {% else %}
{{ render_releases_download(releases, package, current_user) }} {{ render_releases_download(releases, package, current_user) }}
{% endif %} {% endif %}
<a class="list-group-item list-group-item-action text-center py-1 text-muted" href="{{ package.getURL("packages.list_releases") }}"> <a class="list-group-item list-group-item-action text-center py-1 text-muted" href="{{ package.get_url("packages.list_releases") }}">
{{ _("More") }} {{ _("More") }}
</a> </a>
</div> </div>
<h3> <h3>
{% if package.approved and package.checkPerm(current_user, "CREATE_THREAD") %} {% if package.approved and package.check_perm(current_user, "CREATE_THREAD") %}
<div class="btn-group float-right"> <div class="btn-group float-right">
<a class="btn btn-primary btn-sm mx-1" href="{{ url_for('threads.new', pid=package.id) }}"><i class="fas fa-plus"></i></a> <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> </div>
@ -623,11 +623,11 @@
{{ _("Report") }} {{ _("Report") }}
</a> </a>
{% endif %} {% endif %}
{% if package.checkPerm(current_user, "EDIT_PACKAGE") or package.checkPerm(current_user, "APPROVE_NEW") %} {% if package.check_perm(current_user, "EDIT_PACKAGE") or package.check_perm(current_user, "APPROVE_NEW") %}
{% if package.approved and current_user != package.author %} {% if package.approved and current_user != package.author %}
| |
{% endif %} {% endif %}
<a href="{{ package.getURL('packages.audit') }}"> <a href="{{ package.get_url('packages.audit') }}">
{{ _("See audit log") }} {{ _("See audit log") }}
</a> </a>
{% endif %} {% endif %}

@ -15,7 +15,7 @@
<div class="card-body"> <div class="card-body">
<p>{{ _("Deleting is permanent") }}</p> <p>{{ _("Deleting is permanent") }}</p>
<a class="btn btn-secondary mr-3" href="{{ thread.getViewURL() }}">{{ _("Cancel") }}</a> <a class="btn btn-secondary mr-3" href="{{ thread.get_view_url() }}">{{ _("Cancel") }}</a>
<input type="submit" value="{{ _('Delete') }}" class="btn btn-danger" /> <input type="submit" value="{{ _('Delete') }}" class="btn btn-danger" />
</div> </div>
</form> </form>

@ -15,7 +15,7 @@
<div class="card-body"> <div class="card-body">
<p>{{ _("Deleting is permanent") }}</p> <p>{{ _("Deleting is permanent") }}</p>
<a class="btn btn-secondary mr-3" href="{{ thread.getViewURL() }}">{{ _("Cancel") }}</a> <a class="btn btn-secondary mr-3" href="{{ thread.get_view_url() }}">{{ _("Cancel") }}</a>
<input type="submit" value="{{ _('Delete') }}" class="btn btn-danger" /> <input type="submit" value="{{ _('Delete') }}" class="btn btn-danger" />
</div> </div>
</form> </form>

@ -20,7 +20,7 @@
<div class="row mt-0 mb-4 comments mx-0"> <div class="row mt-0 mb-4 comments mx-0">
<div class="col-md-1 p-1"> <div class="col-md-1 p-1">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ current_user.getProfilePicURL() }}"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ current_user.get_profile_pic_url() }}">
</div> </div>
<div class="col"> <div class="col">
<div class="card"> <div class="card">

@ -18,7 +18,7 @@
<li class="row my-2 mx-0"> <li class="row my-2 mx-0">
<div class="col-md-1 p-1"> <div class="col-md-1 p-1">
<a href="{{ url_for('users.profile', username=r.author.username) }}"> <a href="{{ url_for('users.profile', username=r.author.username) }}">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ r.author.getProfilePicURL() }}"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ r.author.get_profile_pic_url() }}">
</a> </a>
</div> </div>
<div class="col pr-0"> <div class="col pr-0">
@ -37,12 +37,12 @@
{% endif %} {% endif %}
{% if r == r.thread.first_reply %} {% if r == r.thread.first_reply %}
<a class="badge badge-primary" href="{{ r.thread.getViewURL() }}"> <a class="badge badge-primary" href="{{ r.thread.get_view_url() }}">
{{ r.thread.title }} {{ r.thread.title }}
</a> </a>
{% else %} {% else %}
<i class="fas fa-reply mr-2"></i> <i class="fas fa-reply mr-2"></i>
<a class="badge badge-dark" href="{{ r.thread.getViewURL() }}"> <a class="badge badge-dark" href="{{ r.thread.get_view_url() }}">
{{ _("Reply to <b>%(title)s</b>", title=r.thread.title) }} {{ _("Reply to <b>%(title)s</b>", title=r.thread.title) }}
</a> </a>
{% endif %} {% endif %}

@ -22,32 +22,32 @@
{%- endblock %} {%- endblock %}
{% block headextra %} {% block headextra %}
<meta name="og:image" content="{{ thread.author.getProfilePicURL() }}"/> <meta name="og:image" content="{{ thread.author.get_profile_pic_url() }}"/>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{% if current_user in thread.watchers %} {% if current_user in thread.watchers %}
<form method="post" action="{{ thread.getUnsubscribeURL() }}" class="float-right"> <form method="post" action="{{ thread.get_unsubscribe_url() }}" class="float-right">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="submit" class="btn btn-primary" value="{{ _('Unsubscribe') }}" /> <input type="submit" class="btn btn-primary" value="{{ _('Unsubscribe') }}" />
</form> </form>
{% else %} {% else %}
<form method="post" action="{{ thread.getSubscribeURL() }}" class="float-right"> <form method="post" action="{{ thread.get_subscribe_url() }}" class="float-right">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="submit" class="btn btn-primary" value="{{ _('Subscribe') }}" /> <input type="submit" class="btn btn-primary" value="{{ _('Subscribe') }}" />
</form> </form>
{% endif %} {% endif %}
{% if thread.checkPerm(current_user, "DELETE_THREAD") %} {% if thread.check_perm(current_user, "DELETE_THREAD") %}
<a href="{{ url_for('threads.delete_thread', id=thread.id) }}" class="float-right mr-2 btn btn-danger">{{ _('Delete') }}</a> <a href="{{ url_for('threads.delete_thread', id=thread.id) }}" class="float-right mr-2 btn btn-danger">{{ _('Delete') }}</a>
{% endif %} {% endif %}
{% if thread.review and thread.review.checkPerm(current_user, "DELETE_REVIEW") and current_user.username != thread.review.author.username %} {% if thread.review and thread.review.check_perm(current_user, "DELETE_REVIEW") and current_user.username != thread.review.author.username %}
<form method="post" action="{{ thread.review.getDeleteURL() }}" class="float-right mr-2"> <form method="post" action="{{ thread.review.get_delete_url() }}" class="float-right mr-2">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="submit" class="btn btn-danger" value="{{ _('Convert to Thread') }}" /> <input type="submit" class="btn btn-danger" value="{{ _('Convert to Thread') }}" />
</form> </form>
{% endif %} {% endif %}
{% if thread.checkPerm(current_user, "LOCK_THREAD") %} {% if thread.check_perm(current_user, "LOCK_THREAD") %}
{% if thread.locked %} {% if thread.locked %}
<form method="post" action="{{ url_for('threads.set_lock', id=thread.id, lock=0) }}" class="float-right mr-2"> <form method="post" action="{{ url_for('threads.set_lock', id=thread.id, lock=0) }}" class="float-right mr-2">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
@ -64,7 +64,7 @@
{% if current_user == thread.author and thread.review %} {% if current_user == thread.author and thread.review %}
<a class="btn btn-primary ml-1 float-right mr-2" <a class="btn btn-primary ml-1 float-right mr-2"
href="{{ thread.review.package.getURL("packages.review") }}"> href="{{ thread.review.package.get_url("packages.review") }}">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
{{ _("Edit Review") }} {{ _("Edit Review") }}
</a> </a>
@ -85,7 +85,7 @@
{% if thread.package %} {% if thread.package %}
<p> <p>
{{ _("Package") }}: <a href="{{ thread.package.getURL('packages.view') }}">{{ thread.package.title }}</a> {{ _("Package") }}: <a href="{{ thread.package.get_url('packages.view') }}">{{ thread.package.title }}</a>
</p> </p>
{% endif %} {% endif %}
@ -103,7 +103,7 @@
</span> </span>
{% for viewer in thread.get_visible_to() %} {% for viewer in thread.get_visible_to() %}
<a href="{{ url_for('users.profile', username=viewer.username) }}" title="{{ viewer.display_name }}"> <a href="{{ url_for('users.profile', username=viewer.username) }}" title="{{ viewer.display_name }}">
<img style="max-height: 2em;" src="{{ viewer.getProfilePicURL() }}" alt="{{ viewer.display_name }}" /> <img style="max-height: 2em;" src="{{ viewer.get_profile_pic_url() }}" alt="{{ viewer.display_name }}" />
</a> </a>
{% endfor %} {% endfor %}
<a href="{{ url_for('users.list_all') }}" title="{{ _('Plus approvers and editors') }}"> <a href="{{ url_for('users.list_all') }}" title="{{ _('Plus approvers and editors') }}">

@ -17,8 +17,8 @@
</h3> </h3>
<ul class="card-body d-flex p-0 flex-row flex-wrap justify-content-start align-content-start p-4"> <ul class="card-body d-flex p-0 flex-row flex-wrap justify-content-start align-content-start p-4">
{% for s in screenshots %} {% for s in screenshots %}
<li class="packagetile flex-fill"><a href="{{ s.getEditURL() }}" <li class="packagetile flex-fill"><a href="{{ s.get_edit_url() }}"
style="background-image: url({{ s.getThumbnailURL(3) }});"> style="background-image: url({{ s.get_thumb_url(3) }});">
<div class="packagegridscrub"></div> <div class="packagegridscrub"></div>
<div class="packagegridinfo"> <div class="packagegridinfo">
<h3> <h3>
@ -46,7 +46,7 @@
<h3 class="card-header">{{ _("Packages") }}</h3> <h3 class="card-header">{{ _("Packages") }}</h3>
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
{% for p in packages %} {% for p in packages %}
<a href="{{ p.getURL("packages.view") }}" class="list-group-item list-group-item-action"> <a href="{{ p.get_url("packages.view") }}" class="list-group-item list-group-item-action">
<span class="float-right" title="Created {{ p.created_at | datetime }}"> <span class="float-right" title="Created {{ p.created_at | datetime }}">
<small> <small>
{{ p.created_at | timedelta }} ago {{ p.created_at | timedelta }} ago
@ -79,9 +79,9 @@
{% if r.task_id %} {% if r.task_id %}
<span class="mr-2 badge badge-warning">{{ _("Importing") }}</span> <span class="mr-2 badge badge-warning">{{ _("Importing") }}</span>
{% endif %} {% endif %}
<a href="{{ r.getEditURL() }}">{{ r.title }}</a> <a href="{{ r.get_edit_url() }}">{{ r.title }}</a>
on on
<a href="{{ r.package.getURL("packages.view") }}"> <a href="{{ r.package.get_url("packages.view") }}">
{{ _("%(title)s by %(display_name)s", {{ _("%(title)s by %(display_name)s",
title=r.package.title, display_name=r.package.author.display_name) }} title=r.package.title, display_name=r.package.author.display_name) }}
</a> </a>
@ -107,7 +107,7 @@
<h3 class="card-header">{{ _("License Needed") }}</h3> <h3 class="card-header">{{ _("License Needed") }}</h3>
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
{% for p in license_needed %} {% for p in license_needed %}
<a href="{{ p.getURL("packages.view") }}" class="list-group-item list-group-item-action"> <a href="{{ p.get_url("packages.view") }}" class="list-group-item list-group-item-action">
<span class="float-right" title="Created {{ p.created_at | datetime }}"> <span class="float-right" title="Created {{ p.created_at | datetime }}">
<small> <small>
{{ p.created_at | timedelta }} ago {{ p.created_at | timedelta }} ago
@ -164,7 +164,7 @@
<h3 class="card-header">WIP Packages</h3> <h3 class="card-header">WIP Packages</h3>
<div class="list-group list-group-flush" style="max-height: 300px; overflow: hidden auto;"> <div class="list-group list-group-flush" style="max-height: 300px; overflow: hidden auto;">
{% for p in wip_packages %} {% for p in wip_packages %}
<a href="{{ p.getURL("packages.view") }}" class="list-group-item list-group-item-action"> <a href="{{ p.get_url("packages.view") }}" class="list-group-item list-group-item-action">
<span class="float-right" title="Created {{ p.created_at | datetime }}"> <span class="float-right" title="Created {{ p.created_at | datetime }}">
<small> <small>
{{ p.created_at | timedelta }} ago {{ p.created_at | timedelta }} ago

@ -22,7 +22,7 @@
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}
@ -41,7 +41,7 @@
{% if supported_games %} {% if supported_games %}
{% for support in supported_games %} {% for support in supported_games %}
<a class="badge badge-secondary" <a class="badge badge-secondary"
href="{{ support.game.getURL('packages.view') }}"> href="{{ support.game.get_url('packages.view') }}">
{{ _("%(title)s by %(display_name)s", {{ _("%(title)s by %(display_name)s",
title=support.game.title, display_name=support.game.author.display_name) }} title=support.game.title, display_name=support.game.author.display_name) }}
</a> </a>
@ -53,8 +53,8 @@
{% endif %} {% endif %}
</div> </div>
<div class="col-auto"> <div class="col-auto">
<a class="btn btn-sm btn-primary" href="{{ package.getURL('packages.game_support') }}">Game Support</a> <a class="btn btn-sm btn-primary" href="{{ package.get_url('packages.game_support') }}">Game Support</a>
<a class="btn btn-sm btn-secondary" href="{{ package.getURL('packages.view') }}">Package</a> <a class="btn btn-sm btn-secondary" href="{{ package.get_url('packages.view') }}">Package</a>
</div> </div>
</div> </div>
</div> </div>

@ -42,15 +42,15 @@
{% for package in packages %} {% for package in packages %}
<tr> <tr>
<td> <td>
<a href="{{ package.getURL("packages.view") }}"> <a href="{{ package.get_url("packages.view") }}">
{{ package.title }} {{ package.title }}
</a> </a>
by {{ package.author.display_name }} by {{ package.author.display_name }}
</td> </td>
<td class="text-center"> <td class="text-center">
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %} {% if package.check_perm(current_user, "EDIT_PACKAGE") %}
<a class="btn btn-link btn-sm py-0" href="{{ package.getURL("packages.create_edit") }}"> <a class="btn btn-link btn-sm py-0" href="{{ package.get_url("packages.create_edit") }}">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</a> </a>
{% endif %} {% endif %}

@ -18,13 +18,13 @@
<h2>{{ _("Unapproved Packages Needing Action") }}</h2> <h2>{{ _("Unapproved Packages Needing Action") }}</h2>
<div class="list-group mt-3 mb-5"> <div class="list-group mt-3 mb-5">
{% for package in unapproved_packages %} {% for package in unapproved_packages %}
<a class="list-group-item list-group-item-action" href="{{ package.getURL("packages.view") }}"> <a class="list-group-item list-group-item-action" href="{{ package.get_url("packages.view") }}">
<div class="row"> <div class="row">
<div class="col-sm-2 text-muted"> <div class="col-sm-2 text-muted">
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}
@ -80,11 +80,11 @@
{% endif %} {% endif %}
<div class="list-group mt-3 mb-5"> <div class="list-group mt-3 mb-5">
{% for package in missing_game_support %} {% for package in missing_game_support %}
<a class="list-group-item list-group-item-action" href="{{ package.getURL('packages.game_support') }}"> <a class="list-group-item list-group-item-action" href="{{ package.get_url('packages.game_support') }}">
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}
@ -100,11 +100,11 @@
<h2 id="missing-screenshots">{{ _("Missing Screenshots") }}</h2> <h2 id="missing-screenshots">{{ _("Missing Screenshots") }}</h2>
<div class="list-group mt-3 mb-5"> <div class="list-group mt-3 mb-5">
{% for package in packages_with_no_screenshots %} {% for package in packages_with_no_screenshots %}
<a class="list-group-item list-group-item-action" href="{{ package.getURL('packages.screenshots') }}"> <a class="list-group-item list-group-item-action" href="{{ package.get_url('packages.screenshots') }}">
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}
@ -138,13 +138,13 @@
{% endif %} {% endif %}
<div class="list-group mt-3 mb-5"> <div class="list-group mt-3 mb-5">
{% for package in packages_with_small_screenshots %} {% for package in packages_with_small_screenshots %}
<a class="list-group-item list-group-item-action" href="{{ package.getURL('packages.screenshots') }}"> <a class="list-group-item list-group-item-action" href="{{ package.get_url('packages.screenshots') }}">
<div class="row"> <div class="row">
<div class="col-sm-3 text-muted" style="min-width: 200px;"> <div class="col-sm-3 text-muted" style="min-width: 200px;">
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}
@ -183,11 +183,11 @@
</p> </p>
<div class="list-group mt-3 mb-5"> <div class="list-group mt-3 mb-5">
{% for package in needs_tags %} {% for package in needs_tags %}
<a class="list-group-item list-group-item-action" href="{{ package.getURL("packages.view") }}"> <a class="list-group-item list-group-item-action" href="{{ package.get_url("packages.view") }}">
<img <img
class="img-fluid" class="img-fluid"
style="max-height: 22px; max-width: 22px;" style="max-height: 22px; max-width: 22px;"
src="{{ package.getThumbnailOrPlaceholder() }}" /> src="{{ package.get_thumb_or_placeholder() }}" />
<span class="pl-2"> <span class="pl-2">
{{ package.title }} {{ package.title }}

@ -31,7 +31,7 @@
<a class="list-group-item list-group-item-action" href="{{ url_for('users.profile', username=user.username) }}"> <a class="list-group-item list-group-item-action" href="{{ url_for('users.profile', username=user.username) }}">
<div class="row"> <div class="row">
<div class="col-sm-2 {{ user.rank }}" <div class="col-sm-2 {{ user.rank }}"
title="{{ _('Rank: %(rank)s.', rank=user.rank.getTitle()) }}"> title="{{ _('Rank: %(rank)s.', rank=user.rank.get_title()) }}">
{% if user.rank == user.rank.ADMIN %} {% if user.rank == user.rank.ADMIN %}
<i class="fas fa-user-cog mr-2"></i> <i class="fas fa-user-cog mr-2"></i>
{% elif user.rank == user.rank.MODERATOR %} {% elif user.rank == user.rank.MODERATOR %}
@ -46,7 +46,7 @@
<i class="fas fa-user mr-2"></i> <i class="fas fa-user mr-2"></i>
{% endif %} {% endif %}
{{ user.rank.getTitle() }} {{ user.rank.get_title() }}
</div> </div>
<span class="col-sm {{ user.rank }}"> <span class="col-sm {{ user.rank }}">

@ -15,14 +15,14 @@
<form action="" method="POST" class="form" role="form"> <form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{% if user.checkPerm(current_user, "CHANGE_USERNAMES") %} {% if user.check_perm(current_user, "CHANGE_USERNAMES") %}
{{ render_field(form.username, tabindex=230) }} {{ render_field(form.username, tabindex=230) }}
{{ render_field(form.display_name, tabindex=230) }} {{ render_field(form.display_name, tabindex=230) }}
{{ render_field(form.forums_username, tabindex=230) }} {{ render_field(form.forums_username, tabindex=230) }}
{{ render_field_prefix(form.github_username, tabindex=230) }} {{ render_field_prefix(form.github_username, tabindex=230) }}
{% endif %} {% endif %}
{% if user.checkPerm(current_user, "CHANGE_RANK") %} {% if user.check_perm(current_user, "CHANGE_RANK") %}
{{ render_field(form.rank, tabindex=250) }} {{ render_field(form.rank, tabindex=250) }}
{% endif %} {% endif %}

@ -8,7 +8,7 @@
<article class="row mb-5"> <article class="row mb-5">
<div class="col-auto image mx-0"> <div class="col-auto image mx-0">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ user.getProfilePicURL() }}" alt="{{ _('Profile picture') }}"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ user.get_profile_pic_url() }}" alt="{{ _('Profile picture') }}">
</div> </div>
<div class="col"> <div class="col">
{% if user.can_see_edit_profile(current_user) %} {% if user.can_see_edit_profile(current_user) %}
@ -57,7 +57,7 @@
<span class="btn"> <span class="btn">
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
<span class="count"> <span class="count">
{{ user.rank.getTitle() }} {{ user.rank.get_title() }}
</span> </span>
</span> </span>
@ -195,7 +195,7 @@
</div> </div>
{% endif %} {% endif %}
{% if current_user == user or user.checkPerm(current_user, "CHANGE_AUTHOR") %} {% if current_user == user or user.check_perm(current_user, "CHANGE_AUTHOR") %}
<a class="float-right btn btn-sm btn-primary" <a class="float-right btn btn-sm btn-primary"
href="{{ url_for('packages.create_edit', author=user.username) }}"> href="{{ url_for('packages.create_edit', author=user.username) }}">
<i class="fas fa-plus mr-1"></i> <i class="fas fa-plus mr-1"></i>

@ -15,7 +15,7 @@
{% elif user.email %} {% elif user.email %}
<a href="https://en.gravatar.com/"> <a href="https://en.gravatar.com/">
{% endif %} {% endif %}
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ user.getProfilePicURL() }}"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ user.get_profile_pic_url() }}">
{% if user.forums_username or user.email %} {% if user.forums_username or user.email %}
</a> </a>
{% endif %} {% endif %}
@ -42,11 +42,11 @@
<form action="" method="POST" class="form" role="form"> <form action="" method="POST" class="form" role="form">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{% if user.checkPerm(current_user, "CHANGE_DISPLAY_NAME") %} {% if user.check_perm(current_user, "CHANGE_DISPLAY_NAME") %}
{{ render_field(form.display_name, tabindex=230, hint=_("Pretending to be another user is grounds for a permanent ban")) }} {{ render_field(form.display_name, tabindex=230, hint=_("Pretending to be another user is grounds for a permanent ban")) }}
{% endif %} {% endif %}
{% if user.checkPerm(current_user, "CHANGE_PROFILE_URLS") %} {% if user.check_perm(current_user, "CHANGE_PROFILE_URLS") %}
{{ render_field(form.website_url, tabindex=232) }} {{ render_field(form.website_url, tabindex=232) }}
{{ render_field(form.donate_url, tabindex=233) }} {{ render_field(form.donate_url, tabindex=233) }}
{% endif %} {% endif %}

@ -7,7 +7,7 @@
<a class="list-group-item list-group-item-action" href="{{ url_for('users.profile', username=user.username) }}"> <a class="list-group-item list-group-item-action" href="{{ url_for('users.profile', username=user.username) }}">
<span class="row m-0 p-0"> <span class="row m-0 p-0">
<span class="col-auto m-0 p-0"> <span class="col-auto m-0 p-0">
<img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ user.getProfilePicURL() }}" alt="Profile picture" style="max-height: 20px;"> <img class="img-fluid user-photo img-thumbnail img-thumbnail-1" src="{{ user.get_profile_pic_url() }}" alt="Profile picture" style="max-height: 20px;">
</span> </span>
<span class="col m-0 p-0 pl-2"> <span class="col m-0 p-0 pl-2">
{{ user.display_name }} {{ user.display_name }}

@ -47,13 +47,13 @@
</tr> </tr>
{% for type in types %} {% for type in types %}
<tr> <tr>
<td>{{ type.getTitle() }}</td> <td>{{ type.get_title() }}</td>
<td>{{ type.get_description() }}</td> <td>{{ type.get_description() }}</td>
<td style="text-align: center;"> <td style="text-align: center;">
{{ render_checkbox_field(form["pref_" + type.toName()]) }} {{ render_checkbox_field(form["pref_" + type.to_name()]) }}
</td> </td>
<td style="text-align: center;"> <td style="text-align: center;">
{{ render_checkbox_field(form["pref_" + type.toName() + "_digest"]) }} {{ render_checkbox_field(form["pref_" + type.to_name() + "_digest"]) }}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

@ -20,10 +20,10 @@
<div class="col-sm-2 text-muted"> <div class="col-sm-2 text-muted">
<img <img
class="img-fluid" class="img-fluid"
src="{{ match.package.getThumbnailOrPlaceholder() }}" /> src="{{ match.package.get_thumb_or_placeholder() }}" />
<div class="mt-2"> <div class="mt-2">
<a href="{{ match.package.getURL('packages.view') }}"> <a href="{{ match.package.get_url('packages.view') }}">
{{ match.package.title }} {{ match.package.title }}
</a> </a>
by {{ match.package.author.display_name }} by {{ match.package.author.display_name }}
@ -33,7 +33,7 @@
{{ match.lines.split("\n") | select | list | count }} match(es) {{ match.lines.split("\n") | select | list | count }} match(es)
</p> </p>
<a class="mt-4 btn btn-secondary" href="{{ match.package.getDownloadRelease().getDownloadURL() }}"> <a class="mt-4 btn btn-secondary" href="{{ match.package.get_download_release().get_download_url() }}">
Download Download
</a> </a>
</div> </div>

@ -133,7 +133,7 @@ def post_bot_message(package: Package, title: str, message: str):
db.session.add(reply) db.session.add(reply)
addNotification(thread.watchers, system_user, NotificationType.BOT, addNotification(thread.watchers, system_user, NotificationType.BOT,
title, thread.getViewURL(), thread.package) title, thread.get_view_url(), thread.package)
thread.replies.append(reply) thread.replies.append(reply)

@ -55,12 +55,12 @@ You can also use a file search. For example, to find the package edit endpoint,
## Users and Permissions ## Users and Permissions
Many routes need to check whether a user can do a particular thing. Rather than hard coding this, Many routes need to check whether a user can do a particular thing. Rather than hard coding this,
models tend to have a `checkPerm` function which takes a user and a `Permission`. models tend to have a `check_perm` function which takes a user and a `Permission`.
A permission may be something like `Permission.EDIT_PACKAGE` or `Permission.DELETE_THREAD`. A permission may be something like `Permission.EDIT_PACKAGE` or `Permission.DELETE_THREAD`.
```bash ```bash
if not package.checkPerm(current_user, Permission.EDIT_PACKAGE): if not package.check_perm(current_user, Permission.EDIT_PACKAGE):
abort(403) abort(403)
``` ```