mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-23 06:22:24 +01:00
Use snake_case for method names
This commit is contained in:
parent
16f93b3e13
commit
45ed12ddf0
@ -129,13 +129,13 @@ def check_for_ban():
|
|||||||
models.db.session.commit()
|
models.db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
from .utils import clearNotifications, is_safe_url, create_session
|
from .utils import clear_notifications, is_safe_url, create_session
|
||||||
|
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def check_for_notifications():
|
def check_for_notifications():
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
clearNotifications(request.path)
|
clear_notifications(request.path)
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
|
@ -25,9 +25,9 @@ from sqlalchemy import or_, and_
|
|||||||
from app.models import PackageRelease, db, Package, PackageState, PackageScreenshot, MetaPackage, User, \
|
from app.models import PackageRelease, db, Package, PackageState, PackageScreenshot, MetaPackage, User, \
|
||||||
NotificationType, PackageUpdateConfig, License, UserRank, PackageType
|
NotificationType, PackageUpdateConfig, License, UserRank, PackageType
|
||||||
from app.tasks.emails import send_pending_digests
|
from app.tasks.emails import send_pending_digests
|
||||||
from app.tasks.forumtasks import importTopicList, checkAllForumAccounts
|
from app.tasks.forumtasks import import_topic_list, check_all_forum_accounts
|
||||||
from app.tasks.importtasks import importRepoScreenshot, checkZipRelease, check_for_updates, updateAllGameSupport
|
from app.tasks.importtasks import import_repo_screenshot, check_zip_release, check_for_updates, update_all_game_support
|
||||||
from app.utils import addNotification, get_system_user
|
from app.utils import add_notification, get_system_user
|
||||||
|
|
||||||
actions = {}
|
actions = {}
|
||||||
|
|
||||||
@ -54,13 +54,13 @@ def del_stuck_releases():
|
|||||||
|
|
||||||
@action("Import forum topic list")
|
@action("Import forum topic list")
|
||||||
def import_topic_list():
|
def import_topic_list():
|
||||||
task = importTopicList.delay()
|
task = import_topic_list.delay()
|
||||||
return redirect(url_for("tasks.check", id=task.id, r=url_for("todo.topics")))
|
return redirect(url_for("tasks.check", id=task.id, r=url_for("todo.topics")))
|
||||||
|
|
||||||
|
|
||||||
@action("Check all forum accounts")
|
@action("Check all forum accounts")
|
||||||
def check_all_forum_accounts():
|
def check_all_forum_accounts():
|
||||||
task = checkAllForumAccounts.delay()
|
task = check_all_forum_accounts.delay()
|
||||||
return redirect(url_for("tasks.check", id=task.id, r=url_for("admin.admin_page")))
|
return redirect(url_for("tasks.check", id=task.id, r=url_for("admin.admin_page")))
|
||||||
|
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ def remind_wip():
|
|||||||
packages_list = _package_list(packages)
|
packages_list = _package_list(packages)
|
||||||
havent = "haven't" if len(packages) > 1 else "hasn't"
|
havent = "haven't" if len(packages) > 1 else "hasn't"
|
||||||
|
|
||||||
addNotification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
add_notification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
||||||
f"Did you forget? {packages_list} {havent} been submitted for review yet",
|
f"Did you forget? {packages_list} {havent} been submitted for review yet",
|
||||||
url_for('todo.view_user', username=user.username))
|
url_for('todo.view_user', username=user.username))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -162,7 +162,7 @@ def remind_outdated():
|
|||||||
packages = [pkg[0] for pkg in packages]
|
packages = [pkg[0] for pkg in packages]
|
||||||
packages_list = _package_list(packages)
|
packages_list = _package_list(packages)
|
||||||
|
|
||||||
addNotification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
add_notification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
||||||
f"The following packages may be outdated: {packages_list}",
|
f"The following packages may be outdated: {packages_list}",
|
||||||
url_for('todo.view_user', username=user.username))
|
url_for('todo.view_user', username=user.username))
|
||||||
|
|
||||||
@ -241,7 +241,7 @@ def remind_video_url():
|
|||||||
packages = [pkg[0] for pkg in packages]
|
packages = [pkg[0] for pkg in packages]
|
||||||
packages_list = _package_list(packages)
|
packages_list = _package_list(packages)
|
||||||
|
|
||||||
addNotification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
add_notification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
||||||
f"You should add a video to {packages_list}",
|
f"You should add a video to {packages_list}",
|
||||||
url_for('users.profile', username=user.username))
|
url_for('users.profile', username=user.username))
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ def remind_missing_game_support():
|
|||||||
packages = [pkg[0] for pkg in packages]
|
packages = [pkg[0] for pkg in packages]
|
||||||
packages_list = _package_list(packages)
|
packages_list = _package_list(packages)
|
||||||
|
|
||||||
addNotification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
add_notification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
||||||
f"You need to confirm whether the following packages support all games: {packages_list}",
|
f"You need to confirm whether the following packages support all games: {packages_list}",
|
||||||
url_for('todo.all_game_support', username=user.username))
|
url_for('todo.all_game_support', username=user.username))
|
||||||
|
|
||||||
@ -280,7 +280,7 @@ def remind_missing_game_support():
|
|||||||
@action("Detect game support")
|
@action("Detect game support")
|
||||||
def detect_game_support():
|
def detect_game_support():
|
||||||
task_id = uuid()
|
task_id = uuid()
|
||||||
updateAllGameSupport.apply_async((), task_id=task_id)
|
update_all_game_support.apply_async((), task_id=task_id)
|
||||||
return redirect(url_for("tasks.check", id=task_id, r=url_for("admin.admin_page")))
|
return redirect(url_for("tasks.check", id=task_id, r=url_for("admin.admin_page")))
|
||||||
|
|
||||||
|
|
||||||
@ -308,7 +308,7 @@ def check_releases():
|
|||||||
|
|
||||||
tasks = []
|
tasks = []
|
||||||
for release in releases:
|
for release in releases:
|
||||||
tasks.append(checkZipRelease.s(release.id, release.file_path))
|
tasks.append(check_zip_release.s(release.id, release.file_path))
|
||||||
|
|
||||||
result = group(tasks).apply_async()
|
result = group(tasks).apply_async()
|
||||||
|
|
||||||
@ -325,7 +325,7 @@ def reimport_packages():
|
|||||||
for package in Package.query.filter(Package.state != PackageState.DELETED).all():
|
for package in Package.query.filter(Package.state != PackageState.DELETED).all():
|
||||||
release = package.releases.first()
|
release = package.releases.first()
|
||||||
if release:
|
if release:
|
||||||
tasks.append(checkZipRelease.s(release.id, release.file_path))
|
tasks.append(check_zip_release.s(release.id, release.file_path))
|
||||||
|
|
||||||
result = group(tasks).apply_async()
|
result = group(tasks).apply_async()
|
||||||
|
|
||||||
@ -344,6 +344,6 @@ def import_screenshots():
|
|||||||
.filter(PackageScreenshot.id == None) \
|
.filter(PackageScreenshot.id == None) \
|
||||||
.all()
|
.all()
|
||||||
for package in packages:
|
for package in packages:
|
||||||
importRepoScreenshot.delay(package.id)
|
import_repo_screenshot.delay(package.id)
|
||||||
|
|
||||||
return redirect(url_for("admin.admin_page"))
|
return redirect(url_for("admin.admin_page"))
|
||||||
|
@ -19,7 +19,7 @@ from flask_login import current_user, login_user
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import StringField, SubmitField
|
from wtforms import StringField, SubmitField
|
||||||
from wtforms.validators import InputRequired, Length
|
from wtforms.validators import InputRequired, Length
|
||||||
from app.utils import rank_required, addAuditLog, addNotification, get_system_user
|
from app.utils import rank_required, add_audit_log, add_notification, get_system_user
|
||||||
from . import bp
|
from . import bp
|
||||||
from .actions import actions
|
from .actions import actions
|
||||||
from app.models import UserRank, Package, db, PackageState, User, AuditSeverity, NotificationType
|
from app.models import UserRank, Package, db, PackageState, User, AuditSeverity, NotificationType
|
||||||
@ -75,11 +75,11 @@ class SendNotificationForm(FlaskForm):
|
|||||||
def send_bulk_notification():
|
def send_bulk_notification():
|
||||||
form = SendNotificationForm(request.form)
|
form = SendNotificationForm(request.form)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user,
|
add_audit_log(AuditSeverity.MODERATION, current_user,
|
||||||
"Sent bulk notification", url_for("admin.admin_page"), None, form.title.data)
|
"Sent bulk notification", url_for("admin.admin_page"), None, form.title.data)
|
||||||
|
|
||||||
users = User.query.filter(User.rank >= UserRank.NEW_MEMBER).all()
|
users = User.query.filter(User.rank >= UserRank.NEW_MEMBER).all()
|
||||||
addNotification(users, get_system_user(), NotificationType.OTHER, form.title.data, form.url.data, None)
|
add_notification(users, get_system_user(), NotificationType.OTHER, form.title.data, form.url.data, None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return redirect(url_for("admin.admin_page"))
|
return redirect(url_for("admin.admin_page"))
|
||||||
@ -105,7 +105,7 @@ def restore():
|
|||||||
else:
|
else:
|
||||||
package.state = target
|
package.state = target
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.EDITOR, current_user, f"Restored package to state {target.value}",
|
add_audit_log(AuditSeverity.EDITOR, current_user, f"Restored package to state {target.value}",
|
||||||
package.get_url("packages.view"), package)
|
package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -22,7 +22,7 @@ from wtforms.validators import InputRequired, Length
|
|||||||
|
|
||||||
from app.markdown import render_markdown
|
from app.markdown import render_markdown
|
||||||
from app.tasks.emails import send_user_email, send_bulk_email as task_send_bulk
|
from app.tasks.emails import send_user_email, send_bulk_email as task_send_bulk
|
||||||
from app.utils import rank_required, addAuditLog
|
from app.utils import rank_required, add_audit_log
|
||||||
from . import bp
|
from . import bp
|
||||||
from app.models import UserRank, User, AuditSeverity
|
from app.models import UserRank, User, AuditSeverity
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ def send_single_email():
|
|||||||
|
|
||||||
form = SendEmailForm(request.form)
|
form = SendEmailForm(request.form)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user,
|
add_audit_log(AuditSeverity.MODERATION, current_user,
|
||||||
"Sent email to {}".format(user.display_name), url_for("users.profile", username=username))
|
"Sent email to {}".format(user.display_name), url_for("users.profile", username=username))
|
||||||
|
|
||||||
text = form.text.data
|
text = form.text.data
|
||||||
@ -65,7 +65,7 @@ def send_single_email():
|
|||||||
def send_bulk_email():
|
def send_bulk_email():
|
||||||
form = SendEmailForm(request.form)
|
form = SendEmailForm(request.form)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user,
|
add_audit_log(AuditSeverity.MODERATION, current_user,
|
||||||
"Sent bulk email", url_for("admin.admin_page"), None, form.text.data)
|
"Sent bulk email", url_for("admin.admin_page"), None, form.text.data)
|
||||||
|
|
||||||
text = form.text.data
|
text = form.text.data
|
||||||
|
@ -21,7 +21,7 @@ from flask_wtf import FlaskForm
|
|||||||
from wtforms import StringField, BooleanField, SubmitField, URLField
|
from wtforms import StringField, BooleanField, SubmitField, URLField
|
||||||
from wtforms.validators import InputRequired, Length, Optional
|
from wtforms.validators import InputRequired, Length, Optional
|
||||||
|
|
||||||
from app.utils import rank_required, nonEmptyOrNone, addAuditLog
|
from app.utils import rank_required, nonempty_or_none, add_audit_log
|
||||||
from . import bp
|
from . import bp
|
||||||
from app.models import UserRank, License, db, AuditSeverity
|
from app.models import UserRank, License, db, AuditSeverity
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ def license_list():
|
|||||||
class LicenseForm(FlaskForm):
|
class LicenseForm(FlaskForm):
|
||||||
name = StringField("Name", [InputRequired(), Length(3, 100)])
|
name = StringField("Name", [InputRequired(), Length(3, 100)])
|
||||||
is_foss = BooleanField("Is FOSS")
|
is_foss = BooleanField("Is FOSS")
|
||||||
url = URLField("URL", [Optional()], filters=[nonEmptyOrNone])
|
url = URLField("URL", [Optional()], filters=[nonempty_or_none])
|
||||||
submit = SubmitField("Save")
|
submit = SubmitField("Save")
|
||||||
|
|
||||||
|
|
||||||
@ -58,12 +58,12 @@ def create_edit_license(name=None):
|
|||||||
db.session.add(license)
|
db.session.add(license)
|
||||||
flash("Created license " + form.name.data, "success")
|
flash("Created license " + form.name.data, "success")
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, f"Created license {license.name}",
|
add_audit_log(AuditSeverity.MODERATION, current_user, f"Created license {license.name}",
|
||||||
url_for("admin.license_list"))
|
url_for("admin.license_list"))
|
||||||
else:
|
else:
|
||||||
flash("Updated license " + form.name.data, "success")
|
flash("Updated license " + form.name.data, "success")
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, f"Edited license {license.name}",
|
add_audit_log(AuditSeverity.MODERATION, current_user, f"Edited license {license.name}",
|
||||||
url_for("admin.license_list"))
|
url_for("admin.license_list"))
|
||||||
|
|
||||||
form.populate_obj(license)
|
form.populate_obj(license)
|
||||||
|
@ -23,7 +23,7 @@ from wtforms.validators import InputRequired, Length, Optional, Regexp
|
|||||||
|
|
||||||
from . import bp
|
from . import bp
|
||||||
from app.models import Permission, Tag, db, AuditSeverity
|
from app.models import Permission, Tag, db, AuditSeverity
|
||||||
from app.utils import addAuditLog
|
from app.utils import add_audit_log
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/tags/")
|
@bp.route("/tags/")
|
||||||
@ -72,12 +72,12 @@ def create_edit_tag(name=None):
|
|||||||
tag.is_protected = form.is_protected.data
|
tag.is_protected = form.is_protected.data
|
||||||
db.session.add(tag)
|
db.session.add(tag)
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.EDITOR, current_user, f"Created tag {tag.name}",
|
add_audit_log(AuditSeverity.EDITOR, current_user, f"Created tag {tag.name}",
|
||||||
url_for("admin.create_edit_tag", name=tag.name))
|
url_for("admin.create_edit_tag", name=tag.name))
|
||||||
else:
|
else:
|
||||||
form.populate_obj(tag)
|
form.populate_obj(tag)
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.EDITOR, current_user, f"Edited tag {tag.name}",
|
add_audit_log(AuditSeverity.EDITOR, current_user, f"Edited tag {tag.name}",
|
||||||
url_for("admin.create_edit_tag", name=tag.name))
|
url_for("admin.create_edit_tag", name=tag.name))
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -21,7 +21,7 @@ from flask_wtf import FlaskForm
|
|||||||
from wtforms import StringField, IntegerField, SubmitField
|
from wtforms import StringField, IntegerField, SubmitField
|
||||||
from wtforms.validators import InputRequired, Length
|
from wtforms.validators import InputRequired, Length
|
||||||
|
|
||||||
from app.utils import rank_required, addAuditLog
|
from app.utils import rank_required, add_audit_log
|
||||||
from . import bp
|
from . import bp
|
||||||
from app.models import UserRank, MinetestRelease, db, AuditSeverity
|
from app.models import UserRank, MinetestRelease, db, AuditSeverity
|
||||||
|
|
||||||
@ -56,12 +56,12 @@ def create_edit_version(name=None):
|
|||||||
db.session.add(version)
|
db.session.add(version)
|
||||||
flash("Created version " + form.name.data, "success")
|
flash("Created version " + form.name.data, "success")
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, f"Created version {version.name}",
|
add_audit_log(AuditSeverity.MODERATION, current_user, f"Created version {version.name}",
|
||||||
url_for("admin.license_list"))
|
url_for("admin.license_list"))
|
||||||
else:
|
else:
|
||||||
flash("Updated version " + form.name.data, "success")
|
flash("Updated version " + form.name.data, "success")
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, f"Edited version {version.name}",
|
add_audit_log(AuditSeverity.MODERATION, current_user, f"Edited version {version.name}",
|
||||||
url_for("admin.version_list"))
|
url_for("admin.version_list"))
|
||||||
|
|
||||||
form.populate_obj(version)
|
form.populate_obj(version)
|
||||||
|
@ -30,7 +30,7 @@ from app.markdown import render_markdown
|
|||||||
from app.models import Tag, PackageState, PackageType, Package, db, PackageRelease, Permission, ForumTopic, \
|
from app.models import Tag, PackageState, PackageType, Package, db, PackageRelease, Permission, ForumTopic, \
|
||||||
MinetestRelease, APIToken, PackageScreenshot, License, ContentWarning, User, PackageReview, Thread
|
MinetestRelease, APIToken, PackageScreenshot, License, ContentWarning, User, PackageReview, Thread
|
||||||
from app.querybuilder import QueryBuilder
|
from app.querybuilder import QueryBuilder
|
||||||
from app.utils import is_package_page, get_int_or_abort, url_set_query, abs_url, isYes, get_request_date
|
from app.utils import is_package_page, get_int_or_abort, url_set_query, abs_url, is_yes, get_request_date
|
||||||
from . import bp
|
from . import bp
|
||||||
from .auth import is_api_authd
|
from .auth import is_api_authd
|
||||||
from .support import error, api_create_vcs_release, api_create_zip_release, api_create_screenshot, \
|
from .support import error, api_create_vcs_release, api_create_zip_release, api_create_screenshot, \
|
||||||
@ -66,19 +66,19 @@ def cached(max_age: int):
|
|||||||
@cached(300)
|
@cached(300)
|
||||||
def packages():
|
def packages():
|
||||||
qb = QueryBuilder(request.args)
|
qb = QueryBuilder(request.args)
|
||||||
query = qb.buildPackageQuery()
|
query = qb.build_package_query()
|
||||||
|
|
||||||
if request.args.get("fmt") == "keys":
|
if request.args.get("fmt") == "keys":
|
||||||
return jsonify([pkg.as_key_dict() for pkg in query.all()])
|
return jsonify([pkg.as_key_dict() for pkg in query.all()])
|
||||||
|
|
||||||
pkgs = qb.convertToDictionary(query.all())
|
pkgs = qb.convert_to_dictionary(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:
|
||||||
pkgs = [pkg for pkg in pkgs if pkg.get("release")]
|
pkgs = [pkg for pkg in pkgs if pkg.get("release")]
|
||||||
|
|
||||||
# Promote featured packages
|
# Promote featured packages
|
||||||
if "sort" not in request.args and "order" not in request.args and "q" not in request.args:
|
if "sort" not in request.args and "order" not in request.args and "q" not in request.args:
|
||||||
featured_lut = set()
|
featured_lut = set()
|
||||||
featured = qb.convertToDictionary(query.filter(Package.tags.any(name="featured")).all())
|
featured = qb.convert_to_dictionary(query.filter(Package.tags.any(name="featured")).all())
|
||||||
for pkg in featured:
|
for pkg in featured:
|
||||||
featured_lut.add(f"{pkg['author']}/{pkg['name']}")
|
featured_lut.add(f"{pkg['author']}/{pkg['name']}")
|
||||||
pkg["short_description"] = "Featured. " + pkg["short_description"]
|
pkg["short_description"] = "Featured. " + pkg["short_description"]
|
||||||
@ -101,7 +101,7 @@ def package_view(package):
|
|||||||
@cors_allowed
|
@cors_allowed
|
||||||
def package_hypertext(package):
|
def package_hypertext(package):
|
||||||
formspec_version = request.args["formspec_version"]
|
formspec_version = request.args["formspec_version"]
|
||||||
include_images = isYes(request.args.get("include_images", "true"))
|
include_images = is_yes(request.args.get("include_images", "true"))
|
||||||
html = render_markdown(package.desc)
|
html = render_markdown(package.desc)
|
||||||
return jsonify(html_to_minetest(html, formspec_version, include_images))
|
return jsonify(html_to_minetest(html, formspec_version, include_images))
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ def package_dependencies(package):
|
|||||||
@cors_allowed
|
@cors_allowed
|
||||||
def topics():
|
def topics():
|
||||||
qb = QueryBuilder(request.args)
|
qb = QueryBuilder(request.args)
|
||||||
query = qb.buildTopicQuery(show_added=True)
|
query = qb.build_topic_query(show_added=True)
|
||||||
return jsonify([t.as_dict() for t in query.all()])
|
return jsonify([t.as_dict() for t in query.all()])
|
||||||
|
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ def create_screenshot(token: APIToken, package: Package):
|
|||||||
if file is None:
|
if file is None:
|
||||||
error(400, "Missing 'file' in multipart body")
|
error(400, "Missing 'file' in multipart body")
|
||||||
|
|
||||||
return api_create_screenshot(token, package, data["title"], file, isYes(data.get("is_cover_image")))
|
return api_create_screenshot(token, package, data["title"], file, is_yes(data.get("is_cover_image")))
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/api/packages/<author>/<name>/screenshots/<int:id>/")
|
@bp.route("/api/packages/<author>/<name>/screenshots/<int:id>/")
|
||||||
@ -454,7 +454,7 @@ def list_all_reviews():
|
|||||||
query = query.filter(PackageReview.author.has(User.username == request.args.get("author")))
|
query = query.filter(PackageReview.author.has(User.username == request.args.get("author")))
|
||||||
|
|
||||||
if request.args.get("is_positive"):
|
if request.args.get("is_positive"):
|
||||||
if isYes(request.args.get("is_positive")):
|
if is_yes(request.args.get("is_positive")):
|
||||||
query = query.filter(PackageReview.rating > 3)
|
query = query.filter(PackageReview.rating > 3)
|
||||||
else:
|
else:
|
||||||
query = query.filter(PackageReview.rating <= 3)
|
query = query.filter(PackageReview.rating <= 3)
|
||||||
@ -499,7 +499,7 @@ def all_package_stats():
|
|||||||
@cached(300)
|
@cached(300)
|
||||||
def package_scores():
|
def package_scores():
|
||||||
qb = QueryBuilder(request.args)
|
qb = QueryBuilder(request.args)
|
||||||
query = qb.buildPackageQuery()
|
query = qb.build_package_query()
|
||||||
|
|
||||||
pkgs = [package.as_score_dict() for package in query.all()]
|
pkgs = [package.as_score_dict() for package in query.all()]
|
||||||
return jsonify(pkgs)
|
return jsonify(pkgs)
|
||||||
@ -601,7 +601,7 @@ def versions():
|
|||||||
@cors_allowed
|
@cors_allowed
|
||||||
def all_deps():
|
def all_deps():
|
||||||
qb = QueryBuilder(request.args)
|
qb = QueryBuilder(request.args)
|
||||||
query = qb.buildPackageQuery()
|
query = qb.build_package_query()
|
||||||
|
|
||||||
def format_pkg(pkg: Package):
|
def format_pkg(pkg: Package):
|
||||||
return {
|
return {
|
||||||
|
@ -24,7 +24,7 @@ from wtforms.validators import InputRequired, Length
|
|||||||
from wtforms_sqlalchemy.fields import QuerySelectField
|
from wtforms_sqlalchemy.fields import QuerySelectField
|
||||||
|
|
||||||
from app.models import db, User, APIToken, Permission
|
from app.models import db, User, APIToken, Permission
|
||||||
from app.utils import randomString
|
from app.utils import random_string
|
||||||
from . import bp
|
from . import bp
|
||||||
from ..users.settings import get_setting_tabs
|
from ..users.settings import get_setting_tabs
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ def create_edit_token(username, id=None):
|
|||||||
token = APIToken()
|
token = APIToken()
|
||||||
db.session.add(token)
|
db.session.add(token)
|
||||||
token.owner = user
|
token.owner = user
|
||||||
token.access_token = randomString(32)
|
token.access_token = random_string(32)
|
||||||
|
|
||||||
form.populate_obj(token)
|
form.populate_obj(token)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -117,7 +117,7 @@ def reset_token(username, id):
|
|||||||
elif token.owner != user:
|
elif token.owner != user:
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
token.access_token = randomString(32)
|
token.access_token = random_string(32)
|
||||||
|
|
||||||
db.session.commit() # save
|
db.session.commit() # save
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ from flask_login import current_user
|
|||||||
from sqlalchemy import func, or_, and_
|
from sqlalchemy import func, or_, and_
|
||||||
from app import github, csrf
|
from app import github, csrf
|
||||||
from app.models import db, User, APIToken, Package, Permission, AuditSeverity, PackageState
|
from app.models import db, User, APIToken, Package, Permission, AuditSeverity, PackageState
|
||||||
from app.utils import abs_url_for, addAuditLog, login_user_set_active
|
from app.utils import abs_url_for, add_audit_log, login_user_set_active
|
||||||
from app.blueprints.api.support import error, api_create_vcs_release
|
from app.blueprints.api.support import error, api_create_vcs_release
|
||||||
import hmac, requests
|
import hmac, requests
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ def callback(oauth_token):
|
|||||||
flash(gettext("Authorization failed [err=gh-login-failed]"), "danger")
|
flash(gettext("Authorization failed [err=gh-login-failed]"), "danger")
|
||||||
return redirect(url_for("users.login"))
|
return redirect(url_for("users.login"))
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.USER, userByGithub, "Logged in using GitHub OAuth",
|
add_audit_log(AuditSeverity.USER, userByGithub, "Logged in using GitHub OAuth",
|
||||||
url_for("users.profile", username=userByGithub.username))
|
url_for("users.profile", username=userByGithub.username))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return ret
|
return ret
|
||||||
|
@ -34,7 +34,7 @@ from app.logic.LogicError import LogicError
|
|||||||
from app.logic.packages import do_edit_package
|
from app.logic.packages import do_edit_package
|
||||||
from app.querybuilder import QueryBuilder
|
from app.querybuilder import QueryBuilder
|
||||||
from app.rediscache import has_key, set_key
|
from app.rediscache import has_key, set_key
|
||||||
from app.tasks.importtasks import importRepoScreenshot, checkZipRelease
|
from app.tasks.importtasks import import_repo_screenshot, check_zip_release
|
||||||
from app.tasks.webhooktasks import post_discord_webhook
|
from app.tasks.webhooktasks import post_discord_webhook
|
||||||
from app.logic.game_support import GameSupportResolver
|
from app.logic.game_support import GameSupportResolver
|
||||||
|
|
||||||
@ -43,14 +43,14 @@ from app.models import Package, Tag, db, User, Tags, PackageState, Permission, P
|
|||||||
Dependency, Thread, UserRank, PackageReview, PackageDevState, ContentWarning, License, AuditSeverity, \
|
Dependency, Thread, UserRank, PackageReview, PackageDevState, ContentWarning, License, AuditSeverity, \
|
||||||
PackageScreenshot, NotificationType, AuditLogEntry, PackageAlias, PackageProvides, PackageGameSupport, \
|
PackageScreenshot, NotificationType, AuditLogEntry, PackageAlias, PackageProvides, PackageGameSupport, \
|
||||||
PackageDailyStats
|
PackageDailyStats
|
||||||
from app.utils import is_user_bot, get_int_or_abort, is_package_page, abs_url_for, addAuditLog, getPackageByInfo, \
|
from app.utils import is_user_bot, get_int_or_abort, is_package_page, abs_url_for, add_audit_log, get_package_by_info, \
|
||||||
addNotification, get_system_user, rank_required, get_games_from_csv, get_daterange_options
|
add_notification, get_system_user, rank_required, get_games_from_csv, get_daterange_options
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/packages/")
|
@bp.route("/packages/")
|
||||||
def list_all():
|
def list_all():
|
||||||
qb = QueryBuilder(request.args)
|
qb = QueryBuilder(request.args)
|
||||||
query = qb.buildPackageQuery()
|
query = qb.build_package_query()
|
||||||
title = qb.title
|
title = qb.title
|
||||||
|
|
||||||
query = query.options(
|
query = query.options(
|
||||||
@ -78,7 +78,7 @@ def list_all():
|
|||||||
if package:
|
if package:
|
||||||
return redirect(package.get_url("packages.view"))
|
return redirect(package.get_url("packages.view"))
|
||||||
|
|
||||||
topic = qb.buildTopicQuery().first()
|
topic = qb.build_topic_query().first()
|
||||||
if qb.search and topic:
|
if qb.search and topic:
|
||||||
return redirect("https://forum.minetest.net/viewtopic.php?t=" + str(topic.topic_id))
|
return redirect("https://forum.minetest.net/viewtopic.php?t=" + str(topic.topic_id))
|
||||||
|
|
||||||
@ -100,12 +100,12 @@ def list_all():
|
|||||||
topics = None
|
topics = None
|
||||||
if qb.search and not query.has_next:
|
if qb.search and not query.has_next:
|
||||||
qb.show_discarded = True
|
qb.show_discarded = True
|
||||||
topics = qb.buildTopicQuery().all()
|
topics = qb.build_topic_query().all()
|
||||||
|
|
||||||
tags_query = db.session.query(func.count(Tags.c.tag_id), Tag) \
|
tags_query = db.session.query(func.count(Tags.c.tag_id), Tag) \
|
||||||
.select_from(Tag).join(Tags).join(Package).filter(Package.state==PackageState.APPROVED) \
|
.select_from(Tag).join(Tags).join(Package).filter(Package.state==PackageState.APPROVED) \
|
||||||
.group_by(Tag.id).order_by(db.asc(Tag.title))
|
.group_by(Tag.id).order_by(db.asc(Tag.title))
|
||||||
tags = qb.filterPackageQuery(tags_query).all()
|
tags = qb.filter_package_query(tags_query).all()
|
||||||
|
|
||||||
selected_tags = set(qb.tags)
|
selected_tags = set(qb.tags)
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ def list_all():
|
|||||||
authors=authors, packages_count=query.total, topics=topics, noindex=qb.noindex)
|
authors=authors, packages_count=query.total, topics=topics, noindex=qb.noindex)
|
||||||
|
|
||||||
|
|
||||||
def getReleases(package):
|
def get_releases(package):
|
||||||
if package.check_perm(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:
|
||||||
@ -158,7 +158,7 @@ def view(package):
|
|||||||
Dependency.meta_package_id.in_([p.id for p in package.provides]))) \
|
Dependency.meta_package_id.in_([p.id for p in package.provides]))) \
|
||||||
.order_by(db.desc(Package.score)).limit(6).all()
|
.order_by(db.desc(Package.score)).limit(6).all()
|
||||||
|
|
||||||
releases = getReleases(package)
|
releases = get_releases(package)
|
||||||
|
|
||||||
review_thread = package.review_thread
|
review_thread = package.review_thread
|
||||||
if review_thread is not None and not review_thread.check_perm(current_user, Permission.SEE_THREAD):
|
if review_thread is not None and not review_thread.check_perm(current_user, Permission.SEE_THREAD):
|
||||||
@ -185,7 +185,7 @@ def view(package):
|
|||||||
threads = Thread.query.filter_by(package_id=package.id, review_id=None)
|
threads = Thread.query.filter_by(package_id=package.id, review_id=None)
|
||||||
if not current_user.is_authenticated:
|
if not current_user.is_authenticated:
|
||||||
threads = threads.filter_by(private=False)
|
threads = threads.filter_by(private=False)
|
||||||
elif not current_user.rank.atLeast(UserRank.APPROVER) and not current_user == package.author:
|
elif not current_user.rank.at_least(UserRank.APPROVER) and not current_user == package.author:
|
||||||
threads = threads.filter(or_(Thread.private == False, Thread.author == current_user))
|
threads = threads.filter(or_(Thread.private == False, Thread.author == current_user))
|
||||||
|
|
||||||
has_review = current_user.is_authenticated and \
|
has_review = current_user.is_authenticated and \
|
||||||
@ -310,10 +310,10 @@ 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.get_url("packages.view"), package)
|
add_audit_log(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)
|
import_repo_screenshot.delay(package.id)
|
||||||
|
|
||||||
next_url = package.get_url("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):
|
||||||
@ -347,7 +347,7 @@ def create_edit(author=None, name=None):
|
|||||||
return redirect(url_for("packages.create_edit"))
|
return redirect(url_for("packages.create_edit"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
package = getPackageByInfo(author, name)
|
package = get_package_by_info(author, name)
|
||||||
if package is None:
|
if package is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
if not package.check_perm(current_user, Permission.EDIT_PACKAGE):
|
if not package.check_perm(current_user, Permission.EDIT_PACKAGE):
|
||||||
@ -422,9 +422,9 @@ def move_to_state(package):
|
|||||||
"Ready for Review: {}".format(package.get_url("packages.view", absolute=True)), True,
|
"Ready for Review: {}".format(package.get_url("packages.view", absolute=True)), True,
|
||||||
package.title, package.short_desc, package.get_thumb_url(2, True))
|
package.title, package.short_desc, package.get_thumb_url(2, True))
|
||||||
|
|
||||||
addNotification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.get_url("packages.view"), package)
|
add_notification(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.get_url("packages.view"), package)
|
add_audit_log(severity, current_user, msg, package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -457,8 +457,8 @@ def remove(package):
|
|||||||
|
|
||||||
url = url_for("users.profile", username=package.author.username)
|
url = url_for("users.profile", username=package.author.username)
|
||||||
msg = "Deleted {}, reason={}".format(package.title, reason)
|
msg = "Deleted {}, reason={}".format(package.title, reason)
|
||||||
addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, url, package)
|
add_notification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, url, package)
|
||||||
addAuditLog(AuditSeverity.EDITOR, current_user, msg, url, package)
|
add_audit_log(AuditSeverity.EDITOR, current_user, msg, url, package)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
flash(gettext("Deleted package"), "success")
|
flash(gettext("Deleted package"), "success")
|
||||||
@ -472,8 +472,8 @@ def remove(package):
|
|||||||
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.get_url("packages.view"), package)
|
add_notification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.get_url("packages.view"), package)
|
||||||
addAuditLog(AuditSeverity.EDITOR, current_user, msg, package.get_url("packages.view"), package)
|
add_audit_log(AuditSeverity.EDITOR, current_user, msg, package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -512,12 +512,12 @@ def edit_maintainers(package):
|
|||||||
if not user in package.maintainers:
|
if not user in package.maintainers:
|
||||||
if thread:
|
if thread:
|
||||||
thread.watchers.append(user)
|
thread.watchers.append(user)
|
||||||
addNotification(user, current_user, NotificationType.MAINTAINER,
|
add_notification(user, current_user, NotificationType.MAINTAINER,
|
||||||
"Added you as a maintainer of {}".format(package.title), package.get_url("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,
|
add_notification(user, current_user, NotificationType.MAINTAINER,
|
||||||
"Removed you as a maintainer of {}".format(package.title), package.get_url("packages.view"), package)
|
"Removed you as a maintainer of {}".format(package.title), package.get_url("packages.view"), package)
|
||||||
|
|
||||||
package.maintainers.clear()
|
package.maintainers.clear()
|
||||||
@ -526,9 +526,9 @@ 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.get_url("packages.view"), package)
|
add_notification(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.get_url("packages.view"), package)
|
add_audit_log(severity, current_user, msg, package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -553,7 +553,7 @@ def remove_self_maintainers(package):
|
|||||||
else:
|
else:
|
||||||
package.maintainers.remove(current_user)
|
package.maintainers.remove(current_user)
|
||||||
|
|
||||||
addNotification(package.author, current_user, NotificationType.MAINTAINER,
|
add_notification(package.author, current_user, NotificationType.MAINTAINER,
|
||||||
"Removed themself as a maintainer of {}".format(package.title), package.get_url("packages.view"), package)
|
"Removed themself as a maintainer of {}".format(package.title), package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -715,7 +715,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.get_url("packages.game_support"), package)
|
add_audit_log(AuditSeverity.NORMAL, current_user, "Edited game support", package.get_url("packages.game_support"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -723,7 +723,7 @@ def game_support(package):
|
|||||||
release = package.releases.first()
|
release = package.releases.first()
|
||||||
if release:
|
if release:
|
||||||
task_id = uuid()
|
task_id = uuid()
|
||||||
checkZipRelease.apply_async((release.id, release.file_path), task_id=task_id)
|
check_zip_release.apply_async((release.id, release.file_path), task_id=task_id)
|
||||||
next_url = url_for("tasks.check", id=task_id, r=next_url)
|
next_url = url_for("tasks.check", id=task_id, r=next_url)
|
||||||
|
|
||||||
return redirect(next_url)
|
return redirect(next_url)
|
||||||
|
@ -27,7 +27,7 @@ from app.models import Package, db, User, PackageState, Permission, UserRank, Pa
|
|||||||
PackageRelease, PackageUpdateTrigger, PackageUpdateConfig
|
PackageRelease, PackageUpdateTrigger, PackageUpdateConfig
|
||||||
from app.rediscache import has_key, set_key, make_download_key
|
from app.rediscache import has_key, set_key, make_download_key
|
||||||
from app.tasks.importtasks import check_update_config
|
from app.tasks.importtasks import check_update_config
|
||||||
from app.utils import is_user_bot, is_package_page, nonEmptyOrNone
|
from app.utils import is_user_bot, is_package_page, nonempty_or_none
|
||||||
from . import bp, get_package_tabs
|
from . import bp, get_package_tabs
|
||||||
|
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ def set_update_config(package, form):
|
|||||||
db.session.add(package.update_config)
|
db.session.add(package.update_config)
|
||||||
|
|
||||||
form.populate_obj(package.update_config)
|
form.populate_obj(package.update_config)
|
||||||
package.update_config.ref = nonEmptyOrNone(form.ref.data)
|
package.update_config.ref = nonempty_or_none(form.ref.data)
|
||||||
package.update_config.make_release = form.action.data == "make_release"
|
package.update_config.make_release = form.action.data == "make_release"
|
||||||
|
|
||||||
if package.update_config.trigger == PackageUpdateTrigger.COMMIT:
|
if package.update_config.trigger == PackageUpdateTrigger.COMMIT:
|
||||||
@ -349,7 +349,7 @@ def bulk_update_config(username=None):
|
|||||||
if not user:
|
if not user:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
if current_user != user and not current_user.rank.atLeast(UserRank.EDITOR):
|
if current_user != user and not current_user.rank.at_least(UserRank.EDITOR):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
form = PackageUpdateConfigFrom()
|
form = PackageUpdateConfigFrom()
|
||||||
|
@ -26,8 +26,8 @@ from wtforms.validators import InputRequired, Length
|
|||||||
from app.models import db, PackageReview, Thread, ThreadReply, NotificationType, PackageReviewVote, Package, UserRank, \
|
from app.models import db, PackageReview, Thread, ThreadReply, NotificationType, PackageReviewVote, Package, UserRank, \
|
||||||
Permission, AuditSeverity, PackageState
|
Permission, AuditSeverity, PackageState
|
||||||
from app.tasks.webhooktasks import post_discord_webhook
|
from app.tasks.webhooktasks import post_discord_webhook
|
||||||
from app.utils import is_package_page, addNotification, get_int_or_abort, isYes, is_safe_url, rank_required, \
|
from app.utils import is_package_page, add_notification, get_int_or_abort, is_yes, is_safe_url, rank_required, \
|
||||||
addAuditLog, has_blocked_domains
|
add_audit_log, has_blocked_domains
|
||||||
from . import bp
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ def review(package):
|
|||||||
notif_msg = "Updated review '{}'".format(form.title.data)
|
notif_msg = "Updated review '{}'".format(form.title.data)
|
||||||
type = NotificationType.OTHER
|
type = NotificationType.OTHER
|
||||||
|
|
||||||
addNotification(package.maintainers, current_user, type, notif_msg,
|
add_notification(package.maintainers, current_user, type, notif_msg,
|
||||||
url_for("threads.view", id=thread.id), package)
|
url_for("threads.view", id=thread.id), package)
|
||||||
|
|
||||||
if was_new:
|
if was_new:
|
||||||
@ -163,11 +163,11 @@ def delete_review(package, reviewer):
|
|||||||
thread.review = None
|
thread.review = None
|
||||||
|
|
||||||
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,
|
add_audit_log(AuditSeverity.MODERATION if current_user.username != reviewer else AuditSeverity.NORMAL,
|
||||||
current_user, msg, thread.get_view_url(), 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)
|
add_notification(package.maintainers, current_user, NotificationType.OTHER, notif_msg, url_for("threads.view", id=thread.id), package)
|
||||||
|
|
||||||
db.session.delete(review)
|
db.session.delete(review)
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ def handle_review_vote(package: Package, review_id: int):
|
|||||||
flash(gettext("You can't vote on your own reviews!"), "danger")
|
flash(gettext("You can't vote on your own reviews!"), "danger")
|
||||||
return
|
return
|
||||||
|
|
||||||
is_positive = isYes(request.form["is_positive"])
|
is_positive = is_yes(request.form["is_positive"])
|
||||||
|
|
||||||
vote = PackageReviewVote.query.filter_by(review=review, user=current_user).first()
|
vote = PackageReviewVote.query.filter_by(review=review, user=current_user).first()
|
||||||
if vote is None:
|
if vote is None:
|
||||||
|
@ -25,7 +25,7 @@ from wtforms.validators import InputRequired, Length
|
|||||||
from app.models import User, UserRank
|
from app.models import User, UserRank
|
||||||
from app.tasks.emails import send_user_email
|
from app.tasks.emails import send_user_email
|
||||||
from app.tasks.webhooktasks import post_discord_webhook
|
from app.tasks.webhooktasks import post_discord_webhook
|
||||||
from app.utils import isNo, abs_url_samesite
|
from app.utils import is_no, abs_url_samesite
|
||||||
|
|
||||||
bp = Blueprint("report", __name__)
|
bp = Blueprint("report", __name__)
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ class ReportForm(FlaskForm):
|
|||||||
|
|
||||||
@bp.route("/report/", methods=["GET", "POST"])
|
@bp.route("/report/", methods=["GET", "POST"])
|
||||||
def report():
|
def report():
|
||||||
is_anon = not current_user.is_authenticated or not isNo(request.args.get("anon"))
|
is_anon = not current_user.is_authenticated or not is_no(request.args.get("anon"))
|
||||||
|
|
||||||
url = request.args.get("url")
|
url = request.args.get("url")
|
||||||
if url:
|
if url:
|
||||||
|
@ -20,8 +20,8 @@ from flask_login import login_required, current_user
|
|||||||
from app import csrf
|
from app import csrf
|
||||||
from app.models import UserRank
|
from app.models import UserRank
|
||||||
from app.tasks import celery
|
from app.tasks import celery
|
||||||
from app.tasks.importtasks import getMeta
|
from app.tasks.importtasks import get_meta
|
||||||
from app.utils import shouldReturnJson
|
from app.utils import should_return_json
|
||||||
|
|
||||||
bp = Blueprint("tasks", __name__)
|
bp = Blueprint("tasks", __name__)
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ def start_getmeta():
|
|||||||
from flask import request
|
from flask import request
|
||||||
author = request.args.get("author")
|
author = request.args.get("author")
|
||||||
author = current_user.forums_username if author is None else author
|
author = current_user.forums_username if author is None else author
|
||||||
aresult = getMeta.delay(request.args.get("url"), author)
|
aresult = get_meta.delay(request.args.get("url"), author)
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"poll_url": url_for("tasks.check", id=aresult.id),
|
"poll_url": url_for("tasks.check", id=aresult.id),
|
||||||
})
|
})
|
||||||
@ -52,7 +52,7 @@ def check(id):
|
|||||||
'status': status,
|
'status': status,
|
||||||
}
|
}
|
||||||
|
|
||||||
if current_user.is_authenticated and current_user.rank.atLeast(UserRank.ADMIN):
|
if current_user.is_authenticated and current_user.rank.at_least(UserRank.ADMIN):
|
||||||
info["error"] = str(traceback)
|
info["error"] = str(traceback)
|
||||||
elif str(result)[1:12] == "TaskError: ":
|
elif str(result)[1:12] == "TaskError: ":
|
||||||
info["error"] = str(result)[12:-1]
|
info["error"] = str(result)[12:-1]
|
||||||
@ -65,7 +65,7 @@ def check(id):
|
|||||||
'result': result,
|
'result': result,
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldReturnJson():
|
if should_return_json():
|
||||||
return jsonify(info)
|
return jsonify(info)
|
||||||
else:
|
else:
|
||||||
r = request.args.get("r")
|
r = request.args.get("r")
|
||||||
|
@ -25,7 +25,7 @@ bp = Blueprint("threads", __name__)
|
|||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
from app.models import Package, db, User, Permission, Thread, UserRank, AuditSeverity, \
|
from app.models import Package, db, User, Permission, Thread, UserRank, AuditSeverity, \
|
||||||
NotificationType, ThreadReply
|
NotificationType, ThreadReply
|
||||||
from app.utils import addNotification, isYes, addAuditLog, get_system_user, rank_required, has_blocked_domains
|
from app.utils import add_notification, is_yes, add_audit_log, get_system_user, rank_required, has_blocked_domains
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import StringField, TextAreaField, SubmitField, BooleanField
|
from wtforms import StringField, TextAreaField, SubmitField, BooleanField
|
||||||
from wtforms.validators import InputRequired, Length
|
from wtforms.validators import InputRequired, Length
|
||||||
@ -99,7 +99,7 @@ def set_lock(id_):
|
|||||||
if thread is None or not thread.check_perm(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 = is_yes(request.args.get("lock"))
|
||||||
if thread.locked is None:
|
if thread.locked is None:
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
@ -110,8 +110,8 @@ 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.get_view_url(), thread.package)
|
add_notification(thread.watchers, current_user, NotificationType.OTHER, msg, thread.get_view_url(), thread.package)
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, msg, thread.get_view_url(), thread.package)
|
add_audit_log(AuditSeverity.MODERATION, current_user, msg, thread.get_view_url(), thread.package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ def delete_thread(id_):
|
|||||||
|
|
||||||
db.session.delete(thread)
|
db.session.delete(thread)
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, msg, None, thread.package, summary)
|
add_audit_log(AuditSeverity.MODERATION, current_user, msg, None, thread.package, summary)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ def delete_reply(id_):
|
|||||||
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.get_view_url(), thread.package, reply.comment)
|
add_audit_log(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()
|
||||||
@ -206,8 +206,8 @@ 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.get_view_url(), thread.package)
|
add_notification(reply.author, current_user, NotificationType.OTHER, msg, thread.get_view_url(), thread.package)
|
||||||
addAuditLog(severity, current_user, msg, thread.get_view_url(), thread.package, reply.comment)
|
add_audit_log(severity, current_user, msg, thread.get_view_url(), thread.package, reply.comment)
|
||||||
|
|
||||||
reply.comment = comment
|
reply.comment = comment
|
||||||
|
|
||||||
@ -253,17 +253,17 @@ def view(id_):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
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,
|
add_notification(mentioned, current_user, NotificationType.THREAD_REPLY,
|
||||||
msg, thread.get_view_url(), 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.get_view_url(), thread.package)
|
add_notification(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,
|
add_notification(approvers, current_user, NotificationType.EDITOR_MISC, msg,
|
||||||
thread.get_view_url(), 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.get_view_url(absolute=True)), True)
|
"Replied to bot messages: {}".format(thread.get_view_url(absolute=True)), True)
|
||||||
@ -294,7 +294,7 @@ def new():
|
|||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
def_is_private = request.args.get("private") or False
|
def_is_private = request.args.get("private") or False
|
||||||
if package is None and not current_user.rank.atLeast(UserRank.APPROVER):
|
if package is None and not current_user.rank.at_least(UserRank.APPROVER):
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
allow_private_change = not package or package.approved
|
allow_private_change = not package or package.approved
|
||||||
@ -359,17 +359,17 @@ def new():
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
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,
|
add_notification(mentioned, current_user, NotificationType.NEW_THREAD,
|
||||||
msg, thread.get_view_url(), 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.get_view_url(), package)
|
add_notification(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.get_view_url(), package)
|
add_notification(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,
|
||||||
|
@ -22,7 +22,7 @@ from sqlalchemy import or_
|
|||||||
from app.models import Package, PackageState, PackageScreenshot, PackageUpdateConfig, ForumTopic, db, \
|
from app.models import Package, PackageState, PackageScreenshot, PackageUpdateConfig, ForumTopic, db, \
|
||||||
PackageRelease, Permission, UserRank, License, MetaPackage, Dependency, AuditLogEntry, Tag, MinetestRelease
|
PackageRelease, Permission, UserRank, License, MetaPackage, Dependency, AuditLogEntry, Tag, MinetestRelease
|
||||||
from app.querybuilder import QueryBuilder
|
from app.querybuilder import QueryBuilder
|
||||||
from app.utils import get_int_or_abort, isYes
|
from app.utils import get_int_or_abort, is_yes
|
||||||
from . import bp
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ def view_editor():
|
|||||||
|
|
||||||
return render_template("todo/editor.html", current_tab="editor",
|
return render_template("todo/editor.html", current_tab="editor",
|
||||||
packages=packages, wip_packages=wip_packages, releases=releases, screenshots=screenshots,
|
packages=packages, wip_packages=wip_packages, releases=releases, screenshots=screenshots,
|
||||||
canApproveNew=can_approve_new, canApproveRel=can_approve_rel, canApproveScn=can_approve_scn,
|
can_approve_new=can_approve_new, can_approve_rel=can_approve_rel, can_approve_scn=can_approve_scn,
|
||||||
license_needed=license_needed, total_packages=total_packages, total_to_tag=total_to_tag,
|
license_needed=license_needed, total_packages=total_packages, total_to_tag=total_to_tag,
|
||||||
unfulfilled_meta_packages=unfulfilled_meta_packages, audit_log=audit_log)
|
unfulfilled_meta_packages=unfulfilled_meta_packages, audit_log=audit_log)
|
||||||
|
|
||||||
@ -94,8 +94,8 @@ def view_editor():
|
|||||||
@login_required
|
@login_required
|
||||||
def topics():
|
def topics():
|
||||||
qb = QueryBuilder(request.args)
|
qb = QueryBuilder(request.args)
|
||||||
qb.setSortIfNone("date")
|
qb.set_sort_if_none("date")
|
||||||
query = qb.buildTopicQuery()
|
query = qb.build_topic_query()
|
||||||
|
|
||||||
tmp_q = ForumTopic.query
|
tmp_q = ForumTopic.query
|
||||||
if not qb.show_discarded:
|
if not qb.show_discarded:
|
||||||
@ -105,7 +105,7 @@ def topics():
|
|||||||
|
|
||||||
page = get_int_or_abort(request.args.get("page"), 1)
|
page = get_int_or_abort(request.args.get("page"), 1)
|
||||||
num = get_int_or_abort(request.args.get("n"), 100)
|
num = get_int_or_abort(request.args.get("n"), 100)
|
||||||
if num > 100 and not current_user.rank.atLeast(UserRank.APPROVER):
|
if num > 100 and not current_user.rank.at_least(UserRank.APPROVER):
|
||||||
num = 100
|
num = 100
|
||||||
|
|
||||||
query = query.paginate(page=page, per_page=num)
|
query = query.paginate(page=page, per_page=num)
|
||||||
@ -126,10 +126,10 @@ def topics():
|
|||||||
@login_required
|
@login_required
|
||||||
def tags():
|
def tags():
|
||||||
qb = QueryBuilder(request.args)
|
qb = QueryBuilder(request.args)
|
||||||
qb.setSortIfNone("score", "desc")
|
qb.set_sort_if_none("score", "desc")
|
||||||
query = qb.buildPackageQuery()
|
query = qb.build_package_query()
|
||||||
|
|
||||||
only_no_tags = isYes(request.args.get("no_tags"))
|
only_no_tags = is_yes(request.args.get("no_tags"))
|
||||||
if only_no_tags:
|
if only_no_tags:
|
||||||
query = query.filter(Package.tags == None)
|
query = query.filter(Package.tags == None)
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ def modnames():
|
|||||||
@bp.route("/todo/outdated/")
|
@bp.route("/todo/outdated/")
|
||||||
@login_required
|
@login_required
|
||||||
def outdated():
|
def outdated():
|
||||||
is_mtm_only = isYes(request.args.get("mtm"))
|
is_mtm_only = is_yes(request.args.get("mtm"))
|
||||||
|
|
||||||
query = db.session.query(Package).select_from(PackageUpdateConfig) \
|
query = db.session.query(Package).select_from(PackageUpdateConfig) \
|
||||||
.filter(PackageUpdateConfig.outdated_at.isnot(None)) \
|
.filter(PackageUpdateConfig.outdated_at.isnot(None)) \
|
||||||
@ -177,7 +177,7 @@ def outdated():
|
|||||||
@bp.route("/todo/screenshots/")
|
@bp.route("/todo/screenshots/")
|
||||||
@login_required
|
@login_required
|
||||||
def screenshots():
|
def screenshots():
|
||||||
is_mtm_only = isYes(request.args.get("mtm"))
|
is_mtm_only = is_yes(request.args.get("mtm"))
|
||||||
|
|
||||||
query = db.session.query(Package) \
|
query = db.session.query(Package) \
|
||||||
.filter(~Package.screenshots.any()) \
|
.filter(~Package.screenshots.any()) \
|
||||||
@ -200,7 +200,7 @@ def screenshots():
|
|||||||
@bp.route("/todo/mtver_support/")
|
@bp.route("/todo/mtver_support/")
|
||||||
@login_required
|
@login_required
|
||||||
def mtver_support():
|
def mtver_support():
|
||||||
is_mtm_only = isYes(request.args.get("mtm"))
|
is_mtm_only = is_yes(request.args.get("mtm"))
|
||||||
|
|
||||||
current_stable = MinetestRelease.query.filter(~MinetestRelease.name.like("%-dev")).order_by(db.desc(MinetestRelease.id)).first()
|
current_stable = MinetestRelease.query.filter(~MinetestRelease.name.like("%-dev")).order_by(db.desc(MinetestRelease.id)).first()
|
||||||
|
|
||||||
|
@ -22,8 +22,8 @@ from sqlalchemy import or_, and_
|
|||||||
|
|
||||||
from app.models import User, Package, PackageState, PackageScreenshot, PackageUpdateConfig, ForumTopic, db, \
|
from app.models import User, Package, PackageState, PackageScreenshot, PackageUpdateConfig, ForumTopic, db, \
|
||||||
PackageRelease, Permission, NotificationType, AuditSeverity, UserRank, PackageType
|
PackageRelease, Permission, NotificationType, AuditSeverity, UserRank, PackageType
|
||||||
from app.tasks.importtasks import makeVCSRelease
|
from app.tasks.importtasks import make_vcs_release
|
||||||
from app.utils import addNotification, addAuditLog
|
from app.utils import add_notification, add_audit_log
|
||||||
from . import bp
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ def view_user(username=None):
|
|||||||
if not user:
|
if not user:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
if current_user != user and not current_user.rank.atLeast(UserRank.APPROVER):
|
if current_user != user and not current_user.rank.at_least(UserRank.APPROVER):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
unapproved_packages = user.packages \
|
unapproved_packages = user.packages \
|
||||||
@ -97,7 +97,7 @@ def apply_all_updates(username):
|
|||||||
if not user:
|
if not user:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
if current_user != user and not current_user.rank.atLeast(UserRank.EDITOR):
|
if current_user != user and not current_user.rank.at_least(UserRank.EDITOR):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
outdated_packages = user.maintained_packages \
|
outdated_packages = user.maintained_packages \
|
||||||
@ -124,13 +124,13 @@ def apply_all_updates(username):
|
|||||||
db.session.add(rel)
|
db.session.add(rel)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
makeVCSRelease.apply_async((rel.id, ref),
|
make_vcs_release.apply_async((rel.id, ref),
|
||||||
task_id=rel.task_id)
|
task_id=rel.task_id)
|
||||||
|
|
||||||
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,
|
add_notification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg,
|
||||||
rel.get_url("packages.create_edit"), package)
|
rel.get_url("packages.create_edit"), package)
|
||||||
addAuditLog(AuditSeverity.NORMAL, current_user, msg, package.get_url("packages.view"), package)
|
add_audit_log(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))
|
||||||
@ -144,7 +144,7 @@ def all_game_support(username=None):
|
|||||||
return redirect(url_for("todo.all_game_support", username=current_user.username))
|
return redirect(url_for("todo.all_game_support", username=current_user.username))
|
||||||
|
|
||||||
user: User = User.query.filter_by(username=username).one_or_404()
|
user: User = User.query.filter_by(username=username).one_or_404()
|
||||||
if current_user != user and not current_user.rank.atLeast(UserRank.EDITOR):
|
if current_user != user and not current_user.rank.at_least(UserRank.EDITOR):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
packages = user.maintained_packages.filter(
|
packages = user.maintained_packages.filter(
|
||||||
@ -159,7 +159,7 @@ def all_game_support(username=None):
|
|||||||
@login_required
|
@login_required
|
||||||
def confirm_supports_all_games(username=None):
|
def confirm_supports_all_games(username=None):
|
||||||
user: User = User.query.filter_by(username=username).one_or_404()
|
user: User = User.query.filter_by(username=username).one_or_404()
|
||||||
if current_user != user and not current_user.rank.atLeast(UserRank.EDITOR):
|
if current_user != user and not current_user.rank.at_least(UserRank.EDITOR):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
packages = user.maintained_packages.filter(
|
packages = user.maintained_packages.filter(
|
||||||
@ -173,7 +173,7 @@ def confirm_supports_all_games(username=None):
|
|||||||
package.supports_all_games = True
|
package.supports_all_games = True
|
||||||
db.session.merge(package)
|
db.session.merge(package)
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.NORMAL, current_user, "Enabled 'Supports all games' (bulk)",
|
add_audit_log(AuditSeverity.NORMAL, current_user, "Enabled 'Supports all games' (bulk)",
|
||||||
package.get_url("packages.game_support"), package)
|
package.get_url("packages.game_support"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -25,8 +25,8 @@ from wtforms import StringField, SubmitField, BooleanField, PasswordField, valid
|
|||||||
from wtforms.validators import InputRequired, Length, Regexp, DataRequired, Optional, Email, EqualTo
|
from wtforms.validators import InputRequired, Length, Regexp, DataRequired, Optional, Email, EqualTo
|
||||||
|
|
||||||
from app.tasks.emails import send_verify_email, send_anon_email, send_unsubscribe_verify, send_user_email
|
from app.tasks.emails import send_verify_email, send_anon_email, send_unsubscribe_verify, send_user_email
|
||||||
from app.utils import randomString, make_flask_login_password, is_safe_url, check_password_hash, addAuditLog, \
|
from app.utils import random_string, make_flask_login_password, is_safe_url, check_password_hash, add_audit_log, \
|
||||||
nonEmptyOrNone, post_login, is_username_valid
|
nonempty_or_none, post_login, is_username_valid
|
||||||
from . import bp
|
from . import bp
|
||||||
from app.models import User, AuditSeverity, db, UserRank, PackageAlias, EmailSubscription, UserNotificationPreferences, \
|
from app.models import User, AuditSeverity, db, UserRank, PackageAlias, EmailSubscription, UserNotificationPreferences, \
|
||||||
UserEmailVerification
|
UserEmailVerification
|
||||||
@ -58,7 +58,7 @@ def handle_login(form):
|
|||||||
flash(gettext("You need to confirm the registration email"), "danger")
|
flash(gettext("You need to confirm the registration email"), "danger")
|
||||||
return
|
return
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.USER, user, "Logged in using password",
|
add_audit_log(AuditSeverity.USER, user, "Logged in using password",
|
||||||
url_for("users.profile", username=user.username))
|
url_for("users.profile", username=user.username))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ def logout():
|
|||||||
|
|
||||||
|
|
||||||
class RegisterForm(FlaskForm):
|
class RegisterForm(FlaskForm):
|
||||||
display_name = StringField(lazy_gettext("Display Name"), [Optional(), Length(1, 20)], filters=[nonEmptyOrNone])
|
display_name = StringField(lazy_gettext("Display Name"), [Optional(), Length(1, 20)], filters=[nonempty_or_none])
|
||||||
username = StringField(lazy_gettext("Username"), [InputRequired(),
|
username = StringField(lazy_gettext("Username"), [InputRequired(),
|
||||||
Regexp("^[a-zA-Z0-9._-]+$", message=lazy_gettext(
|
Regexp("^[a-zA-Z0-9._-]+$", message=lazy_gettext(
|
||||||
"Only alphabetic letters (A-Za-z), numbers (0-9), underscores (_), minuses (-), and periods (.) allowed"))])
|
"Only alphabetic letters (A-Za-z), numbers (0-9), underscores (_), minuses (-), and periods (.) allowed"))])
|
||||||
@ -154,10 +154,10 @@ def handle_register(form):
|
|||||||
user.display_name = form.display_name.data
|
user.display_name = form.display_name.data
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.USER, user, "Registered with email, display name=" + user.display_name,
|
add_audit_log(AuditSeverity.USER, user, "Registered with email, display name=" + user.display_name,
|
||||||
url_for("users.profile", username=user.username))
|
url_for("users.profile", username=user.username))
|
||||||
|
|
||||||
token = randomString(32)
|
token = random_string(32)
|
||||||
|
|
||||||
ver = UserEmailVerification()
|
ver = UserEmailVerification()
|
||||||
ver.user = user
|
ver.user = user
|
||||||
@ -194,9 +194,9 @@ def forgot_password():
|
|||||||
email = form.email.data
|
email = form.email.data
|
||||||
user = User.query.filter_by(email=email).first()
|
user = User.query.filter_by(email=email).first()
|
||||||
if user:
|
if user:
|
||||||
token = randomString(32)
|
token = random_string(32)
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.USER, user, "(Anonymous) requested a password reset",
|
add_audit_log(AuditSeverity.USER, user, "(Anonymous) requested a password reset",
|
||||||
url_for("users.profile", username=user.username), None)
|
url_for("users.profile", username=user.username), None)
|
||||||
|
|
||||||
ver = UserEmailVerification()
|
ver = UserEmailVerification()
|
||||||
@ -241,12 +241,12 @@ def handle_set_password(form):
|
|||||||
flash(gettext("Passwords do not match"), "danger")
|
flash(gettext("Passwords do not match"), "danger")
|
||||||
return
|
return
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.USER, current_user, "Changed their password", url_for("users.profile", username=current_user.username))
|
add_audit_log(AuditSeverity.USER, current_user, "Changed their password", url_for("users.profile", username=current_user.username))
|
||||||
|
|
||||||
current_user.password = make_flask_login_password(form.password.data)
|
current_user.password = make_flask_login_password(form.password.data)
|
||||||
|
|
||||||
if hasattr(form, "email"):
|
if hasattr(form, "email"):
|
||||||
new_email = nonEmptyOrNone(form.email.data)
|
new_email = nonempty_or_none(form.email.data)
|
||||||
if new_email and new_email != current_user.email:
|
if new_email and new_email != current_user.email:
|
||||||
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:
|
||||||
flash(gettext(u"That email address has been unsubscribed/blacklisted, and cannot be used"), "danger")
|
flash(gettext(u"That email address has been unsubscribed/blacklisted, and cannot be used"), "danger")
|
||||||
@ -258,7 +258,7 @@ def handle_set_password(form):
|
|||||||
gettext(u"We were unable to create the account as the email is already in use by %(display_name)s. Try a different email address.",
|
gettext(u"We were unable to create the account as the email is already in use by %(display_name)s. Try a different email address.",
|
||||||
display_name=user_by_email.display_name))
|
display_name=user_by_email.display_name))
|
||||||
else:
|
else:
|
||||||
token = randomString(32)
|
token = random_string(32)
|
||||||
|
|
||||||
ver = UserEmailVerification()
|
ver = UserEmailVerification()
|
||||||
ver.user = current_user
|
ver.user = current_user
|
||||||
@ -329,7 +329,7 @@ def verify_email():
|
|||||||
|
|
||||||
user = ver.user
|
user = ver.user
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.USER, user, "Confirmed their email",
|
add_audit_log(AuditSeverity.USER, user, "Confirmed their email",
|
||||||
url_for("users.profile", username=user.username))
|
url_for("users.profile", username=user.username))
|
||||||
|
|
||||||
was_activating = not user.is_active
|
was_activating = not user.is_active
|
||||||
@ -383,7 +383,7 @@ def unsubscribe_verify():
|
|||||||
sub = EmailSubscription(email)
|
sub = EmailSubscription(email)
|
||||||
db.session.add(sub)
|
db.session.add(sub)
|
||||||
|
|
||||||
sub.token = randomString(32)
|
sub.token = random_string(32)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
send_unsubscribe_verify.delay(form.email.data, get_locale().language)
|
send_unsubscribe_verify.delay(form.email.data, get_locale().language)
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ from flask_babel import gettext
|
|||||||
from . import bp
|
from . import bp
|
||||||
from flask import redirect, render_template, session, request, flash, url_for
|
from flask import redirect, render_template, session, request, flash, url_for
|
||||||
from app.models import db, User, UserRank
|
from app.models import db, User, UserRank
|
||||||
from app.utils import randomString, login_user_set_active, is_username_valid
|
from app.utils import random_string, login_user_set_active, is_username_valid
|
||||||
from app.tasks.forumtasks import checkForumAccount
|
from app.tasks.forumtasks import check_forum_account
|
||||||
from app.utils.phpbbparser import getProfile
|
from app.utils.phpbbparser import get_profile
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/user/claim/", methods=["GET", "POST"])
|
@bp.route("/user/claim/", methods=["GET", "POST"])
|
||||||
@ -42,7 +42,7 @@ def claim_forums():
|
|||||||
return redirect(url_for("users.claim_forums"))
|
return redirect(url_for("users.claim_forums"))
|
||||||
|
|
||||||
user = User.query.filter_by(forums_username=username).first()
|
user = User.query.filter_by(forums_username=username).first()
|
||||||
if user and user.rank.atLeast(UserRank.NEW_MEMBER):
|
if user and user.rank.at_least(UserRank.NEW_MEMBER):
|
||||||
flash(gettext("User has already been claimed"), "danger")
|
flash(gettext("User has already been claimed"), "danger")
|
||||||
return redirect(url_for("users.claim_forums"))
|
return redirect(url_for("users.claim_forums"))
|
||||||
elif method == "github":
|
elif method == "github":
|
||||||
@ -55,7 +55,7 @@ def claim_forums():
|
|||||||
if "forum_token" in session:
|
if "forum_token" in session:
|
||||||
token = session["forum_token"]
|
token = session["forum_token"]
|
||||||
else:
|
else:
|
||||||
token = randomString(12)
|
token = random_string(12)
|
||||||
session["forum_token"] = token
|
session["forum_token"] = token
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
@ -65,17 +65,17 @@ def claim_forums():
|
|||||||
if not is_username_valid(username):
|
if not is_username_valid(username):
|
||||||
flash(gettext("Invalid username, Only alphabetic letters (A-Za-z), numbers (0-9), underscores (_), minuses (-), and periods (.) allowed. Consider contacting an admin"), "danger")
|
flash(gettext("Invalid username, Only alphabetic letters (A-Za-z), numbers (0-9), underscores (_), minuses (-), and periods (.) allowed. Consider contacting an admin"), "danger")
|
||||||
elif ctype == "github":
|
elif ctype == "github":
|
||||||
task = checkForumAccount.delay(username)
|
task = check_forum_account.delay(username)
|
||||||
return redirect(url_for("tasks.check", id=task.id, r=url_for("users.claim_forums", username=username, method="github")))
|
return redirect(url_for("tasks.check", id=task.id, r=url_for("users.claim_forums", username=username, method="github")))
|
||||||
elif ctype == "forum":
|
elif ctype == "forum":
|
||||||
user = User.query.filter_by(forums_username=username).first()
|
user = User.query.filter_by(forums_username=username).first()
|
||||||
if user is not None and user.rank.atLeast(UserRank.NEW_MEMBER):
|
if user is not None and user.rank.at_least(UserRank.NEW_MEMBER):
|
||||||
flash(gettext("That user has already been claimed!"), "danger")
|
flash(gettext("That user has already been claimed!"), "danger")
|
||||||
return redirect(url_for("users.claim_forums"))
|
return redirect(url_for("users.claim_forums"))
|
||||||
|
|
||||||
# Get signature
|
# Get signature
|
||||||
try:
|
try:
|
||||||
profile = getProfile("https://forum.minetest.net", username)
|
profile = get_profile("https://forum.minetest.net", username)
|
||||||
sig = profile.signature if profile else None
|
sig = profile.signature if profile else None
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
if hasattr(e, 'message'):
|
if hasattr(e, 'message'):
|
||||||
|
@ -25,7 +25,7 @@ from wtforms.validators import Length, Optional, Email, URL
|
|||||||
from app.models import User, AuditSeverity, db, UserRank, PackageAlias, EmailSubscription, UserNotificationPreferences, \
|
from app.models import User, AuditSeverity, db, UserRank, PackageAlias, EmailSubscription, UserNotificationPreferences, \
|
||||||
UserEmailVerification, Permission, NotificationType, UserBan
|
UserEmailVerification, Permission, NotificationType, UserBan
|
||||||
from app.tasks.emails import send_verify_email
|
from app.tasks.emails import send_verify_email
|
||||||
from app.utils import nonEmptyOrNone, addAuditLog, randomString, rank_required, has_blocked_domains
|
from app.utils import nonempty_or_none, add_audit_log, random_string, rank_required, has_blocked_domains
|
||||||
from . import bp
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ def get_setting_tabs(user):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if current_user.rank.atLeast(UserRank.MODERATOR):
|
if current_user.rank.at_least(UserRank.MODERATOR):
|
||||||
ret.append({
|
ret.append({
|
||||||
"id": "modtools",
|
"id": "modtools",
|
||||||
"title": gettext("Moderator Tools"),
|
"title": gettext("Moderator Tools"),
|
||||||
@ -64,7 +64,7 @@ def get_setting_tabs(user):
|
|||||||
|
|
||||||
|
|
||||||
class UserProfileForm(FlaskForm):
|
class UserProfileForm(FlaskForm):
|
||||||
display_name = StringField(lazy_gettext("Display Name"), [Optional(), Length(1, 20)], filters=[lambda x: nonEmptyOrNone(x.strip())])
|
display_name = StringField(lazy_gettext("Display Name"), [Optional(), Length(1, 20)], filters=[lambda x: nonempty_or_none(x.strip())])
|
||||||
website_url = StringField(lazy_gettext("Website URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
website_url = StringField(lazy_gettext("Website URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||||
donate_url = StringField(lazy_gettext("Donation URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
donate_url = StringField(lazy_gettext("Donation URL"), [Optional(), URL()], filters = [lambda x: x or None])
|
||||||
submit = SubmitField(lazy_gettext("Save"))
|
submit = SubmitField(lazy_gettext("Save"))
|
||||||
@ -72,7 +72,7 @@ class UserProfileForm(FlaskForm):
|
|||||||
|
|
||||||
def handle_profile_edit(form: UserProfileForm, user: User, username: str):
|
def handle_profile_edit(form: UserProfileForm, user: User, username: str):
|
||||||
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
||||||
addAuditLog(severity, current_user, "Edited {}'s profile".format(user.display_name),
|
add_audit_log(severity, current_user, "Edited {}'s profile".format(user.display_name),
|
||||||
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
|
||||||
@ -95,7 +95,7 @@ def handle_profile_edit(form: UserProfileForm, user: User, username: str):
|
|||||||
user.display_name = display_name
|
user.display_name = display_name
|
||||||
|
|
||||||
severity = AuditSeverity.USER if current_user == user else AuditSeverity.MODERATION
|
severity = AuditSeverity.USER if current_user == user else AuditSeverity.MODERATION
|
||||||
addAuditLog(severity, current_user, "Changed display name of {} to {}"
|
add_audit_log(severity, current_user, "Changed display name of {} to {}"
|
||||||
.format(user.username, user.display_name),
|
.format(user.username, user.display_name),
|
||||||
url_for("users.profile", username=username))
|
url_for("users.profile", username=username))
|
||||||
|
|
||||||
@ -167,12 +167,12 @@ def handle_email_notifications(user, prefs: UserNotificationPreferences, is_new,
|
|||||||
flash(gettext("That email address has been unsubscribed/blacklisted, and cannot be used"), "danger")
|
flash(gettext("That email address has been unsubscribed/blacklisted, and cannot be used"), "danger")
|
||||||
return
|
return
|
||||||
|
|
||||||
token = randomString(32)
|
token = random_string(32)
|
||||||
|
|
||||||
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
||||||
|
|
||||||
msg = "Changed email of {}".format(user.display_name)
|
msg = "Changed email of {}".format(user.display_name)
|
||||||
addAuditLog(severity, current_user, msg, url_for("users.profile", username=user.username))
|
add_audit_log(severity, current_user, msg, url_for("users.profile", username=user.username))
|
||||||
|
|
||||||
ver = UserEmailVerification()
|
ver = UserEmailVerification()
|
||||||
ver.user = user
|
ver.user = user
|
||||||
@ -245,19 +245,19 @@ def delete(username):
|
|||||||
if not user:
|
if not user:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
if user.rank.atLeast(UserRank.MODERATOR):
|
if user.rank.at_least(UserRank.MODERATOR):
|
||||||
flash(gettext("Users with moderator rank or above cannot be deleted"), "danger")
|
flash(gettext("Users with moderator rank or above cannot be deleted"), "danger")
|
||||||
return redirect(url_for("users.account", username=username))
|
return redirect(url_for("users.account", username=username))
|
||||||
|
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return render_template("users/delete.html", user=user, can_delete=user.can_delete())
|
return render_template("users/delete.html", user=user, can_delete=user.can_delete())
|
||||||
|
|
||||||
if "delete" in request.form and (user.can_delete() or current_user.rank.atLeast(UserRank.ADMIN)):
|
if "delete" in request.form and (user.can_delete() or current_user.rank.at_least(UserRank.ADMIN)):
|
||||||
msg = "Deleted user {}".format(user.username)
|
msg = "Deleted user {}".format(user.username)
|
||||||
flash(msg, "success")
|
flash(msg, "success")
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, msg, None)
|
add_audit_log(AuditSeverity.MODERATION, current_user, msg, None)
|
||||||
|
|
||||||
if current_user.rank.atLeast(UserRank.ADMIN):
|
if current_user.rank.at_least(UserRank.ADMIN):
|
||||||
for pkg in user.packages.all():
|
for pkg in user.packages.all():
|
||||||
pkg.review_thread = None
|
pkg.review_thread = None
|
||||||
db.session.delete(pkg)
|
db.session.delete(pkg)
|
||||||
@ -273,7 +273,7 @@ def delete(username):
|
|||||||
|
|
||||||
msg = "Deactivated user {}".format(user.username)
|
msg = "Deactivated user {}".format(user.username)
|
||||||
flash(msg, "success")
|
flash(msg, "success")
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, msg, None)
|
add_audit_log(AuditSeverity.MODERATION, current_user, msg, None)
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
@ -308,7 +308,7 @@ def modtools(username):
|
|||||||
form = ModToolsForm(obj=user)
|
form = ModToolsForm(obj=user)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
||||||
addAuditLog(severity, current_user, "Edited {}'s account".format(user.display_name),
|
add_audit_log(severity, current_user, "Edited {}'s account".format(user.display_name),
|
||||||
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
|
||||||
@ -322,16 +322,16 @@ def modtools(username):
|
|||||||
user.username = form.username.data
|
user.username = form.username.data
|
||||||
|
|
||||||
user.display_name = form.display_name.data
|
user.display_name = form.display_name.data
|
||||||
user.forums_username = nonEmptyOrNone(form.forums_username.data)
|
user.forums_username = nonempty_or_none(form.forums_username.data)
|
||||||
user.github_username = nonEmptyOrNone(form.github_username.data)
|
user.github_username = nonempty_or_none(form.github_username.data)
|
||||||
|
|
||||||
if user.check_perm(current_user, Permission.CHANGE_RANK):
|
if user.check_perm(current_user, Permission.CHANGE_RANK):
|
||||||
new_rank = form["rank"].data
|
new_rank = form["rank"].data
|
||||||
if current_user.rank.atLeast(new_rank):
|
if current_user.rank.at_least(new_rank):
|
||||||
if new_rank != user.rank:
|
if new_rank != user.rank:
|
||||||
user.rank = form["rank"].data
|
user.rank = form["rank"].data
|
||||||
msg = "Set rank of {} to {}".format(user.display_name, user.rank.get_title())
|
msg = "Set rank of {} to {}".format(user.display_name, user.rank.get_title())
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, msg,
|
add_audit_log(AuditSeverity.MODERATION, current_user, msg,
|
||||||
url_for("users.profile", username=username))
|
url_for("users.profile", username=username))
|
||||||
else:
|
else:
|
||||||
flash(gettext("Can't promote a user to a rank higher than yourself!"), "danger")
|
flash(gettext("Can't promote a user to a rank higher than yourself!"), "danger")
|
||||||
@ -356,8 +356,8 @@ def modtools_set_email(username):
|
|||||||
user.email = request.form["email"]
|
user.email = request.form["email"]
|
||||||
user.is_active = False
|
user.is_active = False
|
||||||
|
|
||||||
token = randomString(32)
|
token = random_string(32)
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, f"Set email and sent a password reset on {user.username}",
|
add_audit_log(AuditSeverity.MODERATION, current_user, f"Set email and sent a password reset on {user.username}",
|
||||||
url_for("users.profile", username=user.username), None)
|
url_for("users.profile", username=user.username), None)
|
||||||
|
|
||||||
ver = UserEmailVerification()
|
ver = UserEmailVerification()
|
||||||
@ -396,7 +396,7 @@ def modtools_ban(username):
|
|||||||
else:
|
else:
|
||||||
user.rank = UserRank.BANNED
|
user.rank = UserRank.BANNED
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, f"Banned {user.username}, expires {user.ban.expires_at or '-'}, message: {message}",
|
add_audit_log(AuditSeverity.MODERATION, current_user, f"Banned {user.username}, expires {user.ban.expires_at or '-'}, message: {message}",
|
||||||
url_for("users.profile", username=user.username), None)
|
url_for("users.profile", username=user.username), None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -420,7 +420,7 @@ def modtools_unban(username):
|
|||||||
if user.rank == UserRank.BANNED:
|
if user.rank == UserRank.BANNED:
|
||||||
user.rank = UserRank.MEMBER
|
user.rank = UserRank.MEMBER
|
||||||
|
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, f"Unbanned {user.username}",
|
add_audit_log(AuditSeverity.MODERATION, current_user, f"Unbanned {user.username}",
|
||||||
url_for("users.profile", username=user.username), None)
|
url_for("users.profile", username=user.username), None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ from sqlalchemy import and_, or_
|
|||||||
|
|
||||||
from app.models import Package, PackageType, PackageState, PackageRelease
|
from app.models import Package, PackageType, PackageState, PackageRelease
|
||||||
|
|
||||||
|
|
||||||
ValidationError = namedtuple("ValidationError", "status message")
|
ValidationError = namedtuple("ValidationError", "status message")
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ from flask_babel import lazy_gettext, LazyString
|
|||||||
from app.logic.LogicError import LogicError
|
from app.logic.LogicError import LogicError
|
||||||
from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, \
|
from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, \
|
||||||
License, UserRank, PackageDevState
|
License, UserRank, PackageDevState
|
||||||
from app.utils import addAuditLog, has_blocked_domains, diff_dictionaries, describe_difference
|
from app.utils import add_audit_log, has_blocked_domains, diff_dictionaries, describe_difference
|
||||||
from app.utils.url import clean_youtube_url
|
from app.utils.url import clean_youtube_url
|
||||||
|
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool,
|
|||||||
if not was_web and tag.is_protected:
|
if not was_web and tag.is_protected:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if tag.is_protected and tag not in old_tags and not user.rank.atLeast(UserRank.EDITOR):
|
if tag.is_protected and tag not in old_tags and not user.rank.at_least(UserRank.EDITOR):
|
||||||
raise LogicError(400, lazy_gettext("Unable to add protected tag %(title)s to package", title=tag.title))
|
raise LogicError(400, lazy_gettext("Unable to add protected tag %(title)s to package", title=tag.title))
|
||||||
|
|
||||||
package.tags.append(tag)
|
package.tags.append(tag)
|
||||||
@ -208,7 +208,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.get_url("packages.view"), package, json.dumps(diff, indent=4))
|
add_audit_log(severity, user, msg, package.get_url("packages.view"), package, json.dumps(diff, indent=4))
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ from flask_babel import lazy_gettext
|
|||||||
from app.logic.LogicError import LogicError
|
from app.logic.LogicError import LogicError
|
||||||
from app.logic.uploads import upload_file
|
from app.logic.uploads import upload_file
|
||||||
from app.models import PackageRelease, db, Permission, User, Package, MinetestRelease
|
from app.models import PackageRelease, db, Permission, User, Package, MinetestRelease
|
||||||
from app.tasks.importtasks import makeVCSRelease, checkZipRelease
|
from app.tasks.importtasks import make_vcs_release, check_zip_release
|
||||||
from app.utils import AuditSeverity, addAuditLog, nonEmptyOrNone
|
from app.utils import AuditSeverity, add_audit_log, nonempty_or_none
|
||||||
|
|
||||||
|
|
||||||
def check_can_create_release(user: User, package: Package):
|
def check_can_create_release(user: User, package: Package):
|
||||||
@ -54,11 +54,11 @@ 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.get_url("packages.view"), package)
|
add_audit_log(AuditSeverity.NORMAL, user, msg, package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
makeVCSRelease.apply_async((rel.id, nonEmptyOrNone(ref)), task_id=rel.task_id)
|
make_vcs_release.apply_async((rel.id, nonempty_or_none(ref)), task_id=rel.task_id)
|
||||||
|
|
||||||
return rel
|
return rel
|
||||||
|
|
||||||
@ -89,10 +89,10 @@ 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.get_url("packages.view"), package)
|
add_audit_log(AuditSeverity.NORMAL, user, msg, package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
checkZipRelease.apply_async((rel.id, uploaded_path), task_id=rel.task_id)
|
check_zip_release.apply_async((rel.id, uploaded_path), task_id=rel.task_id)
|
||||||
|
|
||||||
return rel
|
return rel
|
||||||
|
@ -21,7 +21,7 @@ from flask_babel import lazy_gettext
|
|||||||
from app.logic.LogicError import LogicError
|
from app.logic.LogicError import LogicError
|
||||||
from app.logic.uploads import upload_file
|
from app.logic.uploads import upload_file
|
||||||
from app.models import User, Package, PackageScreenshot, Permission, NotificationType, db, AuditSeverity
|
from app.models import User, Package, PackageScreenshot, Permission, NotificationType, db, AuditSeverity
|
||||||
from app.utils import addNotification, addAuditLog
|
from app.utils import add_notification, add_audit_log
|
||||||
from app.utils.image import get_image_size
|
from app.utils.image import get_image_size
|
||||||
|
|
||||||
|
|
||||||
@ -58,8 +58,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.get_url("packages.view"), package)
|
add_notification(package.maintainers, user, NotificationType.PACKAGE_EDIT, msg, package.get_url("packages.view"), package)
|
||||||
addAuditLog(AuditSeverity.NORMAL, user, msg, package.get_url("packages.view"), package)
|
add_audit_log(AuditSeverity.NORMAL, user, msg, package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ from flask_babel import lazy_gettext
|
|||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
from app.logic.LogicError import LogicError
|
from app.logic.LogicError import LogicError
|
||||||
from app.utils import randomString
|
from app.utils import random_string
|
||||||
|
|
||||||
|
|
||||||
def get_extension(filename):
|
def get_extension(filename):
|
||||||
@ -59,7 +59,7 @@ def upload_file(file, file_type, file_type_desc):
|
|||||||
|
|
||||||
file.stream.seek(0)
|
file.stream.seek(0)
|
||||||
|
|
||||||
filename = randomString(10) + "." + ext
|
filename = random_string(10) + "." + ext
|
||||||
filepath = os.path.join(app.config["UPLOAD_DIR"], filename)
|
filepath = os.path.join(app.config["UPLOAD_DIR"], filename)
|
||||||
file.save(filepath)
|
file.save(filepath)
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ class AuditLogEntry(db.Model):
|
|||||||
raise Exception("Unknown permission given to AuditLogEntry.check_perm()")
|
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.at_least(UserRank.APPROVER if self.package is not None else UserRank.MODERATOR)
|
||||||
else:
|
else:
|
||||||
raise Exception("Permission {} is not related to audit log entries".format(perm.name))
|
raise Exception("Permission {} is not related to audit log entries".format(perm.name))
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ class ForumTopic(db.Model):
|
|||||||
raise Exception("Unknown permission given to ForumTopic.check_perm()")
|
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.at_least(UserRank.EDITOR)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Permission {} is not related to topics".format(perm.name))
|
raise Exception("Permission {} is not related to topics".format(perm.name))
|
||||||
|
@ -271,7 +271,7 @@ class Dependency(db.Model):
|
|||||||
else:
|
else:
|
||||||
raise Exception("Either meta or package must be given, but not both!")
|
raise Exception("Either meta or package must be given, but not both!")
|
||||||
|
|
||||||
def getName(self):
|
def get_name(self):
|
||||||
if self.meta_package:
|
if self.meta_package:
|
||||||
return self.meta_package.name
|
return self.meta_package.name
|
||||||
elif self.package:
|
elif self.package:
|
||||||
@ -484,7 +484,7 @@ class Package(db.Model):
|
|||||||
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.get_name())
|
||||||
return deps
|
return deps
|
||||||
|
|
||||||
def get_sorted_hard_dependencies(self):
|
def get_sorted_hard_dependencies(self):
|
||||||
@ -654,25 +654,25 @@ class Package(db.Model):
|
|||||||
raise Exception("Unknown permission given to Package.check_perm()")
|
raise Exception("Unknown permission given to Package.check_perm()")
|
||||||
|
|
||||||
is_owner = user == self.author
|
is_owner = user == self.author
|
||||||
is_maintainer = is_owner or user.rank.atLeast(UserRank.EDITOR) or user in self.maintainers
|
is_maintainer = is_owner or user.rank.at_least(UserRank.EDITOR) or user in self.maintainers
|
||||||
is_approver = user.rank.atLeast(UserRank.APPROVER)
|
is_approver = user.rank.at_least(UserRank.APPROVER)
|
||||||
|
|
||||||
if perm == Permission.CREATE_THREAD:
|
if perm == Permission.CREATE_THREAD:
|
||||||
return user.rank.atLeast(UserRank.NEW_MEMBER)
|
return user.rank.at_least(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 is_maintainer
|
return is_maintainer
|
||||||
|
|
||||||
elif perm == Permission.EDIT_PACKAGE:
|
elif perm == Permission.EDIT_PACKAGE:
|
||||||
return is_maintainer and user.rank.atLeast(UserRank.NEW_MEMBER)
|
return is_maintainer and user.rank.at_least(UserRank.NEW_MEMBER)
|
||||||
|
|
||||||
elif perm == Permission.APPROVE_RELEASE:
|
elif perm == Permission.APPROVE_RELEASE:
|
||||||
return (is_maintainer or is_approver) and user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)
|
return (is_maintainer or is_approver) and user.rank.at_least(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:
|
||||||
return not self.approved or user.rank.atLeast(UserRank.EDITOR)
|
return not self.approved or user.rank.at_least(UserRank.EDITOR)
|
||||||
|
|
||||||
# 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:
|
||||||
@ -680,16 +680,16 @@ class Package(db.Model):
|
|||||||
|
|
||||||
elif perm == Permission.APPROVE_SCREENSHOT:
|
elif perm == Permission.APPROVE_SCREENSHOT:
|
||||||
return (is_maintainer or is_approver) and \
|
return (is_maintainer or is_approver) and \
|
||||||
user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)
|
user.rank.at_least(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 is_owner or user.rank.atLeast(UserRank.EDITOR)
|
return is_owner or user.rank.at_least(UserRank.EDITOR)
|
||||||
|
|
||||||
elif perm == Permission.UNAPPROVE_PACKAGE:
|
elif perm == Permission.UNAPPROVE_PACKAGE:
|
||||||
return is_owner or user.rank.atLeast(UserRank.APPROVER)
|
return is_owner or user.rank.at_least(UserRank.APPROVER)
|
||||||
|
|
||||||
elif perm == Permission.CHANGE_RELEASE_URL:
|
elif perm == Permission.CHANGE_RELEASE_URL:
|
||||||
return user.rank.atLeast(UserRank.MODERATOR)
|
return user.rank.at_least(UserRank.MODERATOR)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Permission {} is not related to packages".format(perm.name))
|
raise Exception("Permission {} is not related to packages".format(perm.name))
|
||||||
@ -738,7 +738,7 @@ class Package(db.Model):
|
|||||||
|
|
||||||
elif state == PackageState.WIP:
|
elif state == PackageState.WIP:
|
||||||
return self.check_perm(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.at_least(UserRank.ADMIN))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -1018,10 +1018,10 @@ class PackageRelease(db.Model):
|
|||||||
is_maintainer = 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.at_least(UserRank.ADMIN):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not (is_maintainer or user.rank.atLeast(UserRank.EDITOR)):
|
if not (is_maintainer or user.rank.at_least(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:
|
||||||
@ -1033,8 +1033,8 @@ 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.at_least(UserRank.APPROVER) or \
|
||||||
(is_maintainer and user.rank.atLeast(
|
(is_maintainer and user.rank.at_least(
|
||||||
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))
|
||||||
|
@ -92,21 +92,21 @@ class Thread(db.Model):
|
|||||||
if self.package:
|
if self.package:
|
||||||
isMaintainer = isMaintainer or user in self.package.maintainers
|
isMaintainer = isMaintainer or user in self.package.maintainers
|
||||||
|
|
||||||
canSee = not self.private or isMaintainer or user.rank.atLeast(UserRank.APPROVER) or user in self.watchers
|
canSee = not self.private or isMaintainer or user.rank.at_least(UserRank.APPROVER) or user in self.watchers
|
||||||
|
|
||||||
if perm == Permission.SEE_THREAD:
|
if perm == Permission.SEE_THREAD:
|
||||||
return canSee
|
return canSee
|
||||||
|
|
||||||
elif perm == Permission.COMMENT_THREAD:
|
elif perm == Permission.COMMENT_THREAD:
|
||||||
return canSee and (not self.locked or user.rank.atLeast(UserRank.MODERATOR))
|
return canSee and (not self.locked or user.rank.at_least(UserRank.MODERATOR))
|
||||||
|
|
||||||
elif perm == Permission.LOCK_THREAD:
|
elif perm == Permission.LOCK_THREAD:
|
||||||
return user.rank.atLeast(UserRank.MODERATOR)
|
return user.rank.at_least(UserRank.MODERATOR)
|
||||||
|
|
||||||
elif perm == Permission.DELETE_THREAD:
|
elif perm == Permission.DELETE_THREAD:
|
||||||
from app.utils.models import get_system_user
|
from app.utils.models import get_system_user
|
||||||
return (self.author == get_system_user() and self.package and
|
return (self.author == get_system_user() and self.package and
|
||||||
user in self.package.maintainers) or user.rank.atLeast(UserRank.MODERATOR)
|
user in self.package.maintainers) or user.rank.at_least(UserRank.MODERATOR)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Permission {} is not related to threads".format(perm.name))
|
raise Exception("Permission {} is not related to threads".format(perm.name))
|
||||||
@ -157,10 +157,10 @@ class ThreadReply(db.Model):
|
|||||||
raise Exception("Unknown permission given to ThreadReply.check_perm()")
|
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.at_least(UserRank.NEW_MEMBER if user == self.author else UserRank.MODERATOR) and not self.thread.locked
|
||||||
|
|
||||||
elif perm == Permission.DELETE_REPLY:
|
elif perm == Permission.DELETE_REPLY:
|
||||||
return user.rank.atLeast(UserRank.MODERATOR) and self.thread.first_reply != self
|
return user.rank.at_least(UserRank.MODERATOR) and self.thread.first_reply != self
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Permission {} is not related to threads".format(perm.name))
|
raise Exception("Permission {} is not related to threads".format(perm.name))
|
||||||
@ -227,7 +227,7 @@ class PackageReview(db.Model):
|
|||||||
name=self.package.name,
|
name=self.package.name,
|
||||||
reviewer=self.author.username)
|
reviewer=self.author.username)
|
||||||
|
|
||||||
def getVoteUrl(self, next_url=None):
|
def get_vote_url(self, next_url=None):
|
||||||
return url_for("packages.review_vote",
|
return url_for("packages.review_vote",
|
||||||
author=self.package.author.username,
|
author=self.package.author.username,
|
||||||
name=self.package.name,
|
name=self.package.name,
|
||||||
@ -248,7 +248,7 @@ class PackageReview(db.Model):
|
|||||||
raise Exception("Unknown permission given to PackageReview.check_perm()")
|
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.at_least(UserRank.MODERATOR)
|
||||||
else:
|
else:
|
||||||
raise Exception("Permission {} is not related to reviews".format(perm.name))
|
raise Exception("Permission {} is not related to reviews".format(perm.name))
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class UserRank(enum.Enum):
|
|||||||
MODERATOR = 8
|
MODERATOR = 8
|
||||||
ADMIN = 9
|
ADMIN = 9
|
||||||
|
|
||||||
def atLeast(self, min):
|
def at_least(self, min):
|
||||||
return self.value >= min.value
|
return self.value >= min.value
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
@ -101,10 +101,10 @@ class Permission(enum.Enum):
|
|||||||
self == Permission.APPROVE_RELEASE or \
|
self == Permission.APPROVE_RELEASE or \
|
||||||
self == Permission.APPROVE_SCREENSHOT or \
|
self == Permission.APPROVE_SCREENSHOT or \
|
||||||
self == Permission.SEE_THREAD:
|
self == Permission.SEE_THREAD:
|
||||||
return user.rank.atLeast(UserRank.APPROVER)
|
return user.rank.at_least(UserRank.APPROVER)
|
||||||
|
|
||||||
elif self == Permission.EDIT_TAGS or self == Permission.CREATE_TAG:
|
elif self == Permission.EDIT_TAGS or self == Permission.CREATE_TAG:
|
||||||
return user.rank.atLeast(UserRank.EDITOR)
|
return user.rank.at_least(UserRank.EDITOR)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Non-global permission checked globally. Use Package.check_perm or User.check_perm instead.")
|
raise Exception("Non-global permission checked globally. Use Package.check_perm or User.check_perm instead.")
|
||||||
@ -234,20 +234,20 @@ class User(db.Model, UserMixin):
|
|||||||
|
|
||||||
# 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:
|
||||||
return user.rank.atLeast(UserRank.EDITOR)
|
return user.rank.at_least(UserRank.EDITOR)
|
||||||
elif perm == Permission.CHANGE_USERNAMES:
|
elif perm == Permission.CHANGE_USERNAMES:
|
||||||
return user.rank.atLeast(UserRank.MODERATOR)
|
return user.rank.at_least(UserRank.MODERATOR)
|
||||||
elif perm == Permission.CHANGE_RANK:
|
elif perm == Permission.CHANGE_RANK:
|
||||||
return user.rank.atLeast(UserRank.MODERATOR) and not self.rank.atLeast(user.rank)
|
return user.rank.at_least(UserRank.MODERATOR) and not self.rank.at_least(user.rank)
|
||||||
elif perm == Permission.CHANGE_EMAIL or perm == Permission.CHANGE_PROFILE_URLS:
|
elif perm == Permission.CHANGE_EMAIL or perm == Permission.CHANGE_PROFILE_URLS:
|
||||||
return user == self or (user.rank.atLeast(UserRank.MODERATOR) and not self.rank.atLeast(user.rank))
|
return user == self or (user.rank.at_least(UserRank.MODERATOR) and not self.rank.at_least(user.rank))
|
||||||
elif perm == Permission.CHANGE_DISPLAY_NAME:
|
elif perm == Permission.CHANGE_DISPLAY_NAME:
|
||||||
return user.rank.atLeast(UserRank.NEW_MEMBER if user == self else UserRank.MODERATOR)
|
return user.rank.at_least(UserRank.NEW_MEMBER if user == self else UserRank.MODERATOR)
|
||||||
elif perm == Permission.CREATE_TOKEN:
|
elif perm == Permission.CREATE_TOKEN:
|
||||||
if user == self:
|
if user == self:
|
||||||
return user.rank.atLeast(UserRank.NEW_MEMBER)
|
return user.rank.at_least(UserRank.NEW_MEMBER)
|
||||||
else:
|
else:
|
||||||
return user.rank.atLeast(UserRank.MODERATOR) and user.rank.atLeast(self.rank)
|
return user.rank.at_least(UserRank.MODERATOR) and user.rank.at_least(self.rank)
|
||||||
else:
|
else:
|
||||||
raise Exception("Permission {} is not related to users".format(perm.name))
|
raise Exception("Permission {} is not related to users".format(perm.name))
|
||||||
|
|
||||||
@ -255,11 +255,11 @@ class User(db.Model, UserMixin):
|
|||||||
from app.models import ThreadReply
|
from app.models import ThreadReply
|
||||||
|
|
||||||
factor = 1
|
factor = 1
|
||||||
if self.rank.atLeast(UserRank.ADMIN):
|
if self.rank.at_least(UserRank.ADMIN):
|
||||||
return True
|
return True
|
||||||
elif self.rank.atLeast(UserRank.TRUSTED_MEMBER):
|
elif self.rank.at_least(UserRank.TRUSTED_MEMBER):
|
||||||
factor = 3
|
factor = 3
|
||||||
elif self.rank.atLeast(UserRank.MEMBER):
|
elif self.rank.at_least(UserRank.MEMBER):
|
||||||
factor = 2
|
factor = 2
|
||||||
|
|
||||||
one_min_ago = datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
|
one_min_ago = datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
|
||||||
@ -278,11 +278,11 @@ class User(db.Model, UserMixin):
|
|||||||
from app.models import Thread
|
from app.models import Thread
|
||||||
|
|
||||||
factor = 1
|
factor = 1
|
||||||
if self.rank.atLeast(UserRank.ADMIN):
|
if self.rank.at_least(UserRank.ADMIN):
|
||||||
return True
|
return True
|
||||||
elif self.rank.atLeast(UserRank.TRUSTED_MEMBER):
|
elif self.rank.at_least(UserRank.TRUSTED_MEMBER):
|
||||||
factor = 5
|
factor = 5
|
||||||
elif self.rank.atLeast(UserRank.MEMBER):
|
elif self.rank.at_least(UserRank.MEMBER):
|
||||||
factor = 2
|
factor = 2
|
||||||
|
|
||||||
hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
|
hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
|
||||||
@ -293,9 +293,9 @@ class User(db.Model, UserMixin):
|
|||||||
from app.models import PackageReview
|
from app.models import PackageReview
|
||||||
|
|
||||||
factor = 1
|
factor = 1
|
||||||
if self.rank.atLeast(UserRank.ADMIN):
|
if self.rank.at_least(UserRank.ADMIN):
|
||||||
return True
|
return True
|
||||||
elif self.rank.atLeast(UserRank.TRUSTED_MEMBER):
|
elif self.rank.at_least(UserRank.TRUSTED_MEMBER):
|
||||||
factor *= 5
|
factor *= 5
|
||||||
|
|
||||||
five_mins_ago = datetime.datetime.utcnow() - datetime.timedelta(minutes=5)
|
five_mins_ago = datetime.datetime.utcnow() - datetime.timedelta(minutes=5)
|
||||||
|
@ -23,7 +23,7 @@ from sqlalchemy_searchable import search
|
|||||||
|
|
||||||
from .models import db, PackageType, Package, ForumTopic, License, MinetestRelease, PackageRelease, User, Tag, \
|
from .models import db, PackageType, Package, ForumTopic, License, MinetestRelease, PackageRelease, User, Tag, \
|
||||||
ContentWarning, PackageState, PackageDevState
|
ContentWarning, PackageState, PackageDevState
|
||||||
from .utils import isYes, get_int_or_abort
|
from .utils import is_yes, get_int_or_abort
|
||||||
|
|
||||||
|
|
||||||
class QueryBuilder:
|
class QueryBuilder:
|
||||||
@ -105,10 +105,10 @@ class QueryBuilder:
|
|||||||
else:
|
else:
|
||||||
self.version = None
|
self.version = None
|
||||||
|
|
||||||
self.show_discarded = isYes(args.get("show_discarded"))
|
self.show_discarded = is_yes(args.get("show_discarded"))
|
||||||
self.show_added = args.get("show_added")
|
self.show_added = args.get("show_added")
|
||||||
if self.show_added is not None:
|
if self.show_added is not None:
|
||||||
self.show_added = isYes(self.show_added)
|
self.show_added = is_yes(self.show_added)
|
||||||
|
|
||||||
if self.search is not None and self.search.strip() == "":
|
if self.search is not None and self.search.strip() == "":
|
||||||
self.search = None
|
self.search = None
|
||||||
@ -117,12 +117,12 @@ class QueryBuilder:
|
|||||||
if self.game:
|
if self.game:
|
||||||
self.game = Package.get_by_key(self.game)
|
self.game = Package.get_by_key(self.game)
|
||||||
|
|
||||||
def setSortIfNone(self, name, dir="desc"):
|
def set_sort_if_none(self, name, dir="desc"):
|
||||||
if self.order_by is None:
|
if self.order_by is None:
|
||||||
self.order_by = name
|
self.order_by = name
|
||||||
self.order_dir = dir
|
self.order_dir = dir
|
||||||
|
|
||||||
def getReleases(self):
|
def get_releases(self):
|
||||||
releases_query = db.session.query(PackageRelease.package_id, func.max(PackageRelease.id)) \
|
releases_query = db.session.query(PackageRelease.package_id, func.max(PackageRelease.id)) \
|
||||||
.select_from(PackageRelease).filter(PackageRelease.approved) \
|
.select_from(PackageRelease).filter(PackageRelease.approved) \
|
||||||
.group_by(PackageRelease.package_id)
|
.group_by(PackageRelease.package_id)
|
||||||
@ -136,18 +136,18 @@ class QueryBuilder:
|
|||||||
|
|
||||||
return releases_query.all()
|
return releases_query.all()
|
||||||
|
|
||||||
def convertToDictionary(self, packages):
|
def convert_to_dictionary(self, packages):
|
||||||
releases = {}
|
releases = {}
|
||||||
for [package_id, release_id] in self.getReleases():
|
for [package_id, release_id] in self.get_releases():
|
||||||
releases[package_id] = release_id
|
releases[package_id] = release_id
|
||||||
|
|
||||||
def toJson(package: Package):
|
def to_json(package: Package):
|
||||||
release_id = releases.get(package.id)
|
release_id = releases.get(package.id)
|
||||||
return package.as_short_dict(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 [to_json(pkg) for pkg in packages]
|
||||||
|
|
||||||
def buildPackageQuery(self):
|
def build_package_query(self):
|
||||||
if self.order_by == "last_release":
|
if self.order_by == "last_release":
|
||||||
query = db.session.query(Package).select_from(PackageRelease).join(Package) \
|
query = db.session.query(Package).select_from(PackageRelease).join(Package) \
|
||||||
.filter_by(state=PackageState.APPROVED)
|
.filter_by(state=PackageState.APPROVED)
|
||||||
@ -156,14 +156,14 @@ class QueryBuilder:
|
|||||||
|
|
||||||
query = query.options(subqueryload(Package.main_screenshot), subqueryload(Package.aliases))
|
query = query.options(subqueryload(Package.main_screenshot), subqueryload(Package.aliases))
|
||||||
|
|
||||||
query = self.orderPackageQuery(self.filterPackageQuery(query))
|
query = self.order_package_query(self.filter_package_query(query))
|
||||||
|
|
||||||
if self.limit:
|
if self.limit:
|
||||||
query = query.limit(self.limit)
|
query = query.limit(self.limit)
|
||||||
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def filterPackageQuery(self, query):
|
def filter_package_query(self, query):
|
||||||
if len(self.types) > 0:
|
if len(self.types) > 0:
|
||||||
query = query.filter(Package.type.in_(self.types))
|
query = query.filter(Package.type.in_(self.types))
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ class QueryBuilder:
|
|||||||
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def orderPackageQuery(self, query):
|
def order_package_query(self, query):
|
||||||
if self.search:
|
if self.search:
|
||||||
query = search(query, self.search, sort=self.order_by is None)
|
query = search(query, self.search, sort=self.order_by is None)
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ class QueryBuilder:
|
|||||||
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def buildTopicQuery(self, show_added=False):
|
def build_topic_query(self, show_added=False):
|
||||||
query = ForumTopic.query
|
query = ForumTopic.query
|
||||||
|
|
||||||
if not self.show_discarded:
|
if not self.show_discarded:
|
||||||
|
@ -75,11 +75,11 @@ celery = make_celery(app)
|
|||||||
|
|
||||||
CELERYBEAT_SCHEDULE = {
|
CELERYBEAT_SCHEDULE = {
|
||||||
'topic_list_import': {
|
'topic_list_import': {
|
||||||
'task': 'app.tasks.forumtasks.importTopicList',
|
'task': 'app.tasks.forumtasks.import_topic_list',
|
||||||
'schedule': crontab(minute=1, hour=1), # 0101
|
'schedule': crontab(minute=1, hour=1), # 0101
|
||||||
},
|
},
|
||||||
'package_score_update': {
|
'package_score_update': {
|
||||||
'task': 'app.tasks.pkgtasks.updatePackageScores',
|
'task': 'app.tasks.pkgtasks.update_package_scores',
|
||||||
'schedule': crontab(minute=10, hour=1), # 0110
|
'schedule': crontab(minute=10, hour=1), # 0110
|
||||||
},
|
},
|
||||||
'check_for_updates': {
|
'check_for_updates': {
|
||||||
|
@ -23,7 +23,7 @@ from flask_mail import Message
|
|||||||
from app import mail
|
from app import mail
|
||||||
from app.models import Notification, db, EmailSubscription, User
|
from app.models import Notification, db, EmailSubscription, User
|
||||||
from app.tasks import celery
|
from app.tasks import celery
|
||||||
from app.utils import abs_url_for, abs_url, randomString
|
from app.utils import abs_url_for, abs_url, random_string
|
||||||
|
|
||||||
|
|
||||||
def get_email_subscription(email):
|
def get_email_subscription(email):
|
||||||
@ -31,7 +31,7 @@ def get_email_subscription(email):
|
|||||||
ret = EmailSubscription.query.filter_by(email=email).first()
|
ret = EmailSubscription.query.filter_by(email=email).first()
|
||||||
if not ret:
|
if not ret:
|
||||||
ret = EmailSubscription(email)
|
ret = EmailSubscription(email)
|
||||||
ret.token = randomString(32)
|
ret.token = random_string(32)
|
||||||
db.session.add(ret)
|
db.session.add(ret)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -23,15 +23,15 @@ from urllib.parse import urljoin
|
|||||||
from app.models import User, db, PackageType, ForumTopic
|
from app.models import User, db, PackageType, ForumTopic
|
||||||
from app.tasks import celery
|
from app.tasks import celery
|
||||||
from app.utils import is_username_valid
|
from app.utils import is_username_valid
|
||||||
from app.utils.phpbbparser import getProfile, getTopicsFromForum
|
from app.utils.phpbbparser import get_profile, get_topics_from_forum
|
||||||
from .usertasks import set_profile_picture_from_url
|
from .usertasks import set_profile_picture_from_url
|
||||||
|
|
||||||
|
|
||||||
@celery.task()
|
@celery.task()
|
||||||
def checkForumAccount(forums_username):
|
def check_forum_account(forums_username):
|
||||||
print("### Checking " + forums_username, file=sys.stderr)
|
print("### Checking " + forums_username, file=sys.stderr)
|
||||||
try:
|
try:
|
||||||
profile = getProfile("https://forum.minetest.net", forums_username)
|
profile = get_profile("https://forum.minetest.net", forums_username)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(e, file=sys.stderr)
|
print(e, file=sys.stderr)
|
||||||
return
|
return
|
||||||
@ -42,7 +42,7 @@ def checkForumAccount(forums_username):
|
|||||||
user = User.query.filter_by(forums_username=forums_username).first()
|
user = User.query.filter_by(forums_username=forums_username).first()
|
||||||
|
|
||||||
# Create user
|
# Create user
|
||||||
needsSaving = False
|
needs_saving = False
|
||||||
if user is None:
|
if user is None:
|
||||||
user = User(forums_username)
|
user = User(forums_username)
|
||||||
user.forums_username = forums_username
|
user.forums_username = forums_username
|
||||||
@ -53,14 +53,14 @@ def checkForumAccount(forums_username):
|
|||||||
if github_username is not None and github_username.strip() != "":
|
if github_username is not None and github_username.strip() != "":
|
||||||
print("Updated GitHub username for " + user.display_name + " to " + github_username)
|
print("Updated GitHub username for " + user.display_name + " to " + github_username)
|
||||||
user.github_username = github_username
|
user.github_username = github_username
|
||||||
needsSaving = True
|
needs_saving = True
|
||||||
|
|
||||||
pic = profile.avatar
|
pic = profile.avatar
|
||||||
if pic and pic.startswith("http"):
|
if pic and pic.startswith("http"):
|
||||||
pic = None
|
pic = None
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
if needsSaving:
|
if needs_saving:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
if pic:
|
if pic:
|
||||||
@ -74,21 +74,21 @@ def checkForumAccount(forums_username):
|
|||||||
print(f"####### Queueing", file=sys.stderr)
|
print(f"####### Queueing", file=sys.stderr)
|
||||||
set_profile_picture_from_url.delay(user.username, pic)
|
set_profile_picture_from_url.delay(user.username, pic)
|
||||||
|
|
||||||
return needsSaving
|
return needs_saving
|
||||||
|
|
||||||
|
|
||||||
@celery.task()
|
@celery.task()
|
||||||
def checkAllForumAccounts():
|
def check_all_forum_accounts():
|
||||||
query = User.query.filter(User.forums_username.isnot(None))
|
query = User.query.filter(User.forums_username.isnot(None))
|
||||||
for user in query.all():
|
for user in query.all():
|
||||||
checkForumAccount(user.forums_username)
|
check_forum_account(user.forums_username)
|
||||||
|
|
||||||
|
|
||||||
regex_tag = re.compile(r"\[([a-z0-9_]+)\]")
|
regex_tag = re.compile(r"\[([a-z0-9_]+)\]")
|
||||||
BANNED_NAMES = ["mod", "game", "old", "outdated", "wip", "api", "beta", "alpha", "git"]
|
BANNED_NAMES = ["mod", "game", "old", "outdated", "wip", "api", "beta", "alpha", "git"]
|
||||||
|
|
||||||
|
|
||||||
def getNameFromTaglist(taglist):
|
def get_name_from_taglist(taglist):
|
||||||
for tag in reversed(regex_tag.findall(taglist)):
|
for tag in reversed(regex_tag.findall(taglist)):
|
||||||
if len(tag) < 30 and not tag in BANNED_NAMES and \
|
if len(tag) < 30 and not tag in BANNED_NAMES and \
|
||||||
not re.match(r"^[a-z]?[0-9]+$", tag):
|
not re.match(r"^[a-z]?[0-9]+$", tag):
|
||||||
@ -100,15 +100,16 @@ def getNameFromTaglist(taglist):
|
|||||||
regex_title = re.compile(r"^((?:\[[^\]]+\] *)*)([^\[]+) *((?:\[[^\]]+\] *)*)[^\[]*$")
|
regex_title = re.compile(r"^((?:\[[^\]]+\] *)*)([^\[]+) *((?:\[[^\]]+\] *)*)[^\[]*$")
|
||||||
|
|
||||||
|
|
||||||
def parseTitle(title):
|
def parse_title(title):
|
||||||
m = regex_title.match(title)
|
m = regex_title.match(title)
|
||||||
if m is None:
|
if m is None:
|
||||||
print("Invalid title format: " + title)
|
print("Invalid title format: " + title)
|
||||||
return title, getNameFromTaglist(title)
|
return title, get_name_from_taglist(title)
|
||||||
else:
|
else:
|
||||||
return m.group(2).strip(), getNameFromTaglist(m.group(3))
|
return m.group(2).strip(), get_name_from_taglist(m.group(3))
|
||||||
|
|
||||||
def getLinksFromModSearch():
|
|
||||||
|
def get_links_from_mod_search():
|
||||||
links = {}
|
links = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -127,15 +128,16 @@ def getLinksFromModSearch():
|
|||||||
|
|
||||||
return links
|
return links
|
||||||
|
|
||||||
|
|
||||||
@celery.task()
|
@celery.task()
|
||||||
def importTopicList():
|
def import_topic_list():
|
||||||
links_by_id = getLinksFromModSearch()
|
links_by_id = get_links_from_mod_search()
|
||||||
|
|
||||||
info_by_id = {}
|
info_by_id = {}
|
||||||
getTopicsFromForum(11, out=info_by_id, extra={ 'type': PackageType.MOD, 'wip': False })
|
get_topics_from_forum(11, out=info_by_id, extra={'type': PackageType.MOD, 'wip': False})
|
||||||
getTopicsFromForum(9, out=info_by_id, extra={ 'type': PackageType.MOD, 'wip': True })
|
get_topics_from_forum(9, out=info_by_id, extra={'type': PackageType.MOD, 'wip': True})
|
||||||
getTopicsFromForum(15, out=info_by_id, extra={ 'type': PackageType.GAME, 'wip': False })
|
get_topics_from_forum(15, out=info_by_id, extra={'type': PackageType.GAME, 'wip': False})
|
||||||
getTopicsFromForum(50, out=info_by_id, extra={ 'type': PackageType.GAME, 'wip': True })
|
get_topics_from_forum(50, out=info_by_id, extra={'type': PackageType.GAME, 'wip': True})
|
||||||
|
|
||||||
# Caches
|
# Caches
|
||||||
username_to_user = {}
|
username_to_user = {}
|
||||||
@ -182,7 +184,7 @@ def importTopicList():
|
|||||||
db.session.add(topic)
|
db.session.add(topic)
|
||||||
|
|
||||||
# Parse title
|
# Parse title
|
||||||
title, name = parseTitle(info["title"])
|
title, name = parse_title(info["title"])
|
||||||
|
|
||||||
# Get link
|
# Get link
|
||||||
link = links_by_id.get(id)
|
link = links_by_id.get(id)
|
||||||
|
@ -30,7 +30,7 @@ from kombu import uuid
|
|||||||
from app.models import AuditSeverity, db, NotificationType, PackageRelease, MetaPackage, Dependency, PackageType, \
|
from app.models import AuditSeverity, db, NotificationType, PackageRelease, MetaPackage, Dependency, PackageType, \
|
||||||
MinetestRelease, Package, PackageState, PackageScreenshot, PackageUpdateTrigger, PackageUpdateConfig
|
MinetestRelease, Package, PackageState, PackageScreenshot, PackageUpdateTrigger, PackageUpdateConfig
|
||||||
from app.tasks import celery, TaskError
|
from app.tasks import celery, TaskError
|
||||||
from app.utils import randomString, post_bot_message, addSystemNotification, addSystemAuditLog, get_games_from_csv
|
from app.utils import random_string, post_bot_message, add_system_notification, add_system_audit_log, get_games_from_csv
|
||||||
from app.utils.git import clone_repo, get_latest_tag, get_latest_commit, get_temp_dir
|
from app.utils.git import clone_repo, get_latest_tag, get_latest_commit, get_temp_dir
|
||||||
from .minetestcheck import build_tree, MinetestCheckError, ContentType
|
from .minetestcheck import build_tree, MinetestCheckError, ContentType
|
||||||
from app import app
|
from app import app
|
||||||
@ -41,7 +41,7 @@ from app.utils.image import get_image_size
|
|||||||
|
|
||||||
|
|
||||||
@celery.task()
|
@celery.task()
|
||||||
def getMeta(urlstr, author):
|
def get_meta(urlstr, author):
|
||||||
with clone_repo(urlstr, recursive=True) as repo:
|
with clone_repo(urlstr, recursive=True) as repo:
|
||||||
try:
|
try:
|
||||||
tree = build_tree(repo.working_tree_dir, author=author, repo=urlstr)
|
tree = build_tree(repo.working_tree_dir, author=author, repo=urlstr)
|
||||||
@ -82,13 +82,13 @@ def getMeta(urlstr, author):
|
|||||||
|
|
||||||
|
|
||||||
@celery.task()
|
@celery.task()
|
||||||
def updateAllGameSupport():
|
def update_all_game_support():
|
||||||
resolver = GameSupportResolver(db.session)
|
resolver = GameSupportResolver(db.session)
|
||||||
resolver.init_all()
|
resolver.init_all()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
def post_release_check_update(self, release: PackageRelease, path):
|
||||||
try:
|
try:
|
||||||
tree = build_tree(path, expected_type=ContentType[release.package.type.name],
|
tree = build_tree(path, expected_type=ContentType[release.package.type.name],
|
||||||
author=release.package.author.username, name=release.package.name)
|
author=release.package.author.username, name=release.package.name)
|
||||||
@ -97,14 +97,14 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
|||||||
raise MinetestCheckError(f"Expected {tree.relative} to have technical name {release.package.name}, instead has name {tree.name}")
|
raise MinetestCheckError(f"Expected {tree.relative} to have technical name {release.package.name}, instead has name {tree.name}")
|
||||||
|
|
||||||
cache = {}
|
cache = {}
|
||||||
def getMetaPackages(names):
|
def get_meta_packages(names):
|
||||||
return [ MetaPackage.GetOrCreate(x, cache) for x in names ]
|
return [ MetaPackage.GetOrCreate(x, cache) for x in names ]
|
||||||
|
|
||||||
provides = tree.get_mod_names()
|
provides = tree.get_mod_names()
|
||||||
|
|
||||||
package = release.package
|
package = release.package
|
||||||
package.provides.clear()
|
package.provides.clear()
|
||||||
package.provides.extend(getMetaPackages(tree.get_mod_names()))
|
package.provides.extend(get_meta_packages(tree.get_mod_names()))
|
||||||
|
|
||||||
# Delete all mod name dependencies
|
# Delete all mod name dependencies
|
||||||
package.dependencies.filter(Dependency.meta_package != None).delete()
|
package.dependencies.filter(Dependency.meta_package != None).delete()
|
||||||
@ -124,10 +124,10 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
|||||||
raise MinetestCheckError("Game has unresolved hard dependencies: " + deps)
|
raise MinetestCheckError("Game has unresolved hard dependencies: " + deps)
|
||||||
|
|
||||||
# Add dependencies
|
# Add dependencies
|
||||||
for meta in getMetaPackages(depends):
|
for meta in get_meta_packages(depends):
|
||||||
db.session.add(Dependency(package, meta=meta, optional=False))
|
db.session.add(Dependency(package, meta=meta, optional=False))
|
||||||
|
|
||||||
for meta in getMetaPackages(optional_depends):
|
for meta in get_meta_packages(optional_depends):
|
||||||
db.session.add(Dependency(package, meta=meta, optional=True))
|
db.session.add(Dependency(package, meta=meta, optional=True))
|
||||||
|
|
||||||
# Update min/max
|
# Update min/max
|
||||||
@ -191,7 +191,7 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
|||||||
|
|
||||||
|
|
||||||
@celery.task(bind=True)
|
@celery.task(bind=True)
|
||||||
def checkZipRelease(self, id, path):
|
def check_zip_release(self, id, path):
|
||||||
release = PackageRelease.query.get(id)
|
release = PackageRelease.query.get(id)
|
||||||
if release is None:
|
if release is None:
|
||||||
raise TaskError("No such release!")
|
raise TaskError("No such release!")
|
||||||
@ -202,7 +202,7 @@ def checkZipRelease(self, id, path):
|
|||||||
with ZipFile(path, 'r') as zip_ref:
|
with ZipFile(path, 'r') as zip_ref:
|
||||||
zip_ref.extractall(temp)
|
zip_ref.extractall(temp)
|
||||||
|
|
||||||
postReleaseCheckUpdate(self, release, temp)
|
post_release_check_update(self, release, temp)
|
||||||
|
|
||||||
release.task_id = None
|
release.task_id = None
|
||||||
release.approve(release.package.author)
|
release.approve(release.package.author)
|
||||||
@ -210,7 +210,7 @@ def checkZipRelease(self, id, path):
|
|||||||
|
|
||||||
|
|
||||||
@celery.task(bind=True)
|
@celery.task(bind=True)
|
||||||
def makeVCSRelease(self, id, branch):
|
def make_vcs_release(self, id, branch):
|
||||||
release = PackageRelease.query.get(id)
|
release = PackageRelease.query.get(id)
|
||||||
if release is None:
|
if release is None:
|
||||||
raise TaskError("No such release!")
|
raise TaskError("No such release!")
|
||||||
@ -218,9 +218,9 @@ def makeVCSRelease(self, id, branch):
|
|||||||
raise TaskError("No package attached to release")
|
raise TaskError("No package attached to release")
|
||||||
|
|
||||||
with clone_repo(release.package.repo, ref=branch, recursive=True) as repo:
|
with clone_repo(release.package.repo, ref=branch, recursive=True) as repo:
|
||||||
postReleaseCheckUpdate(self, release, repo.working_tree_dir)
|
post_release_check_update(self, release, repo.working_tree_dir)
|
||||||
|
|
||||||
filename = randomString(10) + ".zip"
|
filename = random_string(10) + ".zip"
|
||||||
destPath = os.path.join(app.config["UPLOAD_DIR"], filename)
|
destPath = os.path.join(app.config["UPLOAD_DIR"], filename)
|
||||||
|
|
||||||
assert(not os.path.isfile(destPath))
|
assert(not os.path.isfile(destPath))
|
||||||
@ -238,7 +238,7 @@ def makeVCSRelease(self, id, branch):
|
|||||||
|
|
||||||
|
|
||||||
@celery.task()
|
@celery.task()
|
||||||
def importRepoScreenshot(id):
|
def import_repo_screenshot(id):
|
||||||
package = Package.query.get(id)
|
package = Package.query.get(id)
|
||||||
if package is None or package.state == PackageState.DELETED:
|
if package is None or package.state == PackageState.DELETED:
|
||||||
raise Exception("Unexpected none package")
|
raise Exception("Unexpected none package")
|
||||||
@ -248,7 +248,7 @@ def importRepoScreenshot(id):
|
|||||||
for ext in ["png", "jpg", "jpeg"]:
|
for ext in ["png", "jpg", "jpeg"]:
|
||||||
sourcePath = repo.working_tree_dir + "/screenshot." + ext
|
sourcePath = repo.working_tree_dir + "/screenshot." + ext
|
||||||
if os.path.isfile(sourcePath):
|
if os.path.isfile(sourcePath):
|
||||||
filename = randomString(10) + "." + ext
|
filename = random_string(10) + "." + ext
|
||||||
destPath = os.path.join(app.config["UPLOAD_DIR"], filename)
|
destPath = os.path.join(app.config["UPLOAD_DIR"], filename)
|
||||||
shutil.copyfile(sourcePath, destPath)
|
shutil.copyfile(sourcePath, destPath)
|
||||||
|
|
||||||
@ -313,11 +313,11 @@ 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.get_url("packages.view"), package)
|
add_system_audit_log(AuditSeverity.NORMAL, msg, package.get_url("packages.view"), package)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
makeVCSRelease.apply_async((rel.id, commit), task_id=rel.task_id)
|
make_vcs_release.apply_async((rel.id, commit), task_id=rel.task_id)
|
||||||
|
|
||||||
elif config.outdated_at is None:
|
elif config.outdated_at is None:
|
||||||
config.set_outdated()
|
config.set_outdated()
|
||||||
@ -338,7 +338,7 @@ def check_update_config_impl(package):
|
|||||||
.format(tag, msg_last)
|
.format(tag, msg_last)
|
||||||
|
|
||||||
for user in package.maintainers:
|
for user in package.maintainers:
|
||||||
addSystemNotification(user, NotificationType.BOT,
|
add_system_notification(user, NotificationType.BOT,
|
||||||
msg, url_for("todo.view_user", username=user.username, _external=False), package)
|
msg, url_for("todo.view_user", username=user.username, _external=False), package)
|
||||||
|
|
||||||
config.last_commit = commit
|
config.last_commit = commit
|
||||||
|
@ -19,7 +19,7 @@ from app.models import Package, db
|
|||||||
from app.tasks import celery
|
from app.tasks import celery
|
||||||
|
|
||||||
@celery.task()
|
@celery.task()
|
||||||
def updatePackageScores():
|
def update_package_scores():
|
||||||
Package.query.update({ "score_downloads": Package.score_downloads * 0.95 })
|
Package.query.update({ "score_downloads": Package.score_downloads * 0.95 })
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ from sqlalchemy import or_, and_
|
|||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
from app.models import User, db, UserRank, ThreadReply, Package
|
from app.models import User, db, UserRank, ThreadReply, Package
|
||||||
from app.utils import randomString
|
from app.utils import random_string
|
||||||
from app.utils.models import create_session
|
from app.utils.models import create_session
|
||||||
from app.tasks import celery, TaskError
|
from app.tasks import celery, TaskError
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ def set_profile_picture_from_url(username: str, url: str):
|
|||||||
else:
|
else:
|
||||||
raise TaskError(f"Unacceptable content-type: {content_type}")
|
raise TaskError(f"Unacceptable content-type: {content_type}")
|
||||||
|
|
||||||
filename = randomString(10) + "." + ext
|
filename = random_string(10) + "." + ext
|
||||||
filepath = os.path.join(app.config["UPLOAD_DIR"], filename)
|
filepath = os.path.join(app.config["UPLOAD_DIR"], filename)
|
||||||
with open(filepath, "wb") as f:
|
with open(filepath, "wb") as f:
|
||||||
size = 0
|
size = 0
|
||||||
|
@ -147,10 +147,10 @@
|
|||||||
{{ _("Statistics") }}
|
{{ _("Statistics") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% if current_user.rank.atLeast(current_user.rank.EDITOR) or check_global_perm(current_user, "CREATE_TAG") %}
|
{% if current_user.rank.at_least(current_user.rank.EDITOR) or check_global_perm(current_user, "CREATE_TAG") %}
|
||||||
<li class="dropdown-divider"></li>
|
<li class="dropdown-divider"></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if current_user.rank.atLeast(current_user.rank.MODERATOR) %}
|
{% if current_user.rank.at_least(current_user.rank.MODERATOR) %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{{ url_for('admin.audit') }}">
|
<a class="nav-link" href="{{ url_for('admin.audit') }}">
|
||||||
{{ _("Audit Log") }}
|
{{ _("Audit Log") }}
|
||||||
@ -164,7 +164,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if current_user.rank.atLeast(current_user.rank.EDITOR) %}
|
{% if current_user.rank.at_least(current_user.rank.EDITOR) %}
|
||||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.restore') }}">{{ _("Restore Package") }}</a></li>
|
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.restore') }}">{{ _("Restore Package") }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if check_global_perm(current_user, "EDIT_TAGS") %}
|
{% if check_global_perm(current_user, "EDIT_TAGS") %}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% macro render_review_vote(review, current_user, next_url) %}
|
{% macro render_review_vote(review, current_user, next_url) %}
|
||||||
{% set (positive, negative, is_positive) = review.get_totals(current_user) %}
|
{% set (positive, negative, is_positive) = review.get_totals(current_user) %}
|
||||||
<form class="-group" method="post" action="{{ review.getVoteUrl(next_url) }}">
|
<form class="-group" method="post" action="{{ review.get_vote_url(next_url) }}">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn {% if is_positive == true %}btn-primary{% else %}btn-secondary{% endif %}" name="is_positive" value="yes">
|
<button class="btn {% if is_positive == true %}btn-primary{% else %}btn-secondary{% endif %}" name="is_positive" value="yes">
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<td class="btn-group">
|
<td class="btn-group">
|
||||||
{% if current_user == topic.author or topic.author.check_perm(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.get_repo_url(), forums=topic.topic_id, title=topic.title, bname=topic.name) }}">
|
||||||
{{ _("Create") }}
|
{{ _("Create") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -61,7 +61,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if topic.author == current_user or topic.author.check_perm(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.get_repo_url(), forums=topic.topic_id, title=topic.title, bname=topic.name) }}">
|
||||||
{{ _("Create") }}
|
{{ _("Create") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -323,7 +323,7 @@
|
|||||||
</p>
|
</p>
|
||||||
{% 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.at_least(current_user.rank.ADMIN) %}
|
||||||
<a href="{{ package.get_url('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 %}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2 class="mb-4">{{ _("Approval Queue") }}</h2>
|
<h2 class="mb-4">{{ _("Approval Queue") }}</h2>
|
||||||
{% if canApproveScn and screenshots %}
|
{% if can_approve_scn and screenshots %}
|
||||||
<div class="card my-4">
|
<div class="card my-4">
|
||||||
<h3 class="card-header">{{ _("Screenshots") }}
|
<h3 class="card-header">{{ _("Screenshots") }}
|
||||||
<form class="float-right" method="post" action="{{ url_for('todo.view_editor') }}">
|
<form class="float-right" method="post" action="{{ url_for('todo.view_editor') }}">
|
||||||
@ -40,7 +40,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% if canApproveNew and (packages or wip_packages) %}
|
{% if can_approve_new and (packages or wip_packages) %}
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3 class="card-header">{{ _("Packages") }}</h3>
|
<h3 class="card-header">{{ _("Packages") }}</h3>
|
||||||
@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if canApproveRel and releases %}
|
{% if can_approve_rel and releases %}
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3 class="card-header">{{ _("Releases") }}</h3>
|
<h3 class="card-header">{{ _("Releases") }}</h3>
|
||||||
@ -159,7 +159,7 @@
|
|||||||
|
|
||||||
<h2 class="mt-5">{{ _("WIP") }}</h2>
|
<h2 class="mt-5">{{ _("WIP") }}</h2>
|
||||||
|
|
||||||
{% if canApproveNew and (packages or wip_packages) %}
|
{% if can_approve_new and (packages or wip_packages) %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<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;">
|
||||||
@ -188,7 +188,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="mt-5"></div>
|
<div class="mt-5"></div>
|
||||||
{% if current_user.rank.atLeast(current_user.rank.MODERATOR) %}
|
{% if current_user.rank.at_least(current_user.rank.MODERATOR) %}
|
||||||
<a class="btn btn-secondary float-right" href="{{ url_for('admin.audit') }}">
|
<a class="btn btn-secondary float-right" href="{{ url_for('admin.audit') }}">
|
||||||
{{ _("View All") }}
|
{{ _("View All") }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block container %}
|
{% block container %}
|
||||||
{% if current_user.rank.atLeast(current_user.rank.APPROVER) %}
|
{% if current_user.rank.at_least(current_user.rank.APPROVER) %}
|
||||||
<nav class="pt-4 tabs-container">
|
<nav class="pt-4 tabs-container">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
@ -47,7 +47,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<main class="container mt-5">
|
<main class="container mt-5">
|
||||||
{% if not current_user.rank.atLeast(current_user.rank.APPROVER) %}
|
{% if not current_user.rank.at_least(current_user.rank.APPROVER) %}
|
||||||
<h1 class="mb-5">{{ self.title() }}</h1>
|
<h1 class="mb-5">{{ self.title() }}</h1>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ Topics to be Added
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group btn-group-sm">
|
<div class="btn-group btn-group-sm">
|
||||||
{% if current_user.rank.atLeast(current_user.rank.APPROVER) %}
|
{% if current_user.rank.at_least(current_user.rank.APPROVER) %}
|
||||||
{% if n >= 10000 %}
|
{% if n >= 10000 %}
|
||||||
<a class="btn btn-secondary"
|
<a class="btn btn-secondary"
|
||||||
href="{{ url_for('todo.topics', q=query, show_discarded=show_discarded, n=100, sort=sort_by) }}">
|
href="{{ url_for('todo.topics', q=query, show_discarded=show_discarded, n=100, sort=sort_by) }}">
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{% if current_user.rank.atLeast(current_user.rank.MODERATOR) %}
|
{% if current_user.rank.at_least(current_user.rank.MODERATOR) %}
|
||||||
<a class="btn btn-secondary float-right" href="{{ url_for('admin.audit', username=user.username) }}">
|
<a class="btn btn-secondary float-right" href="{{ url_for('admin.audit', username=user.username) }}">
|
||||||
{{ _("View All") }}
|
{{ _("View All") }}
|
||||||
</a>
|
</a>
|
||||||
@ -81,7 +81,7 @@
|
|||||||
|
|
||||||
<h3>{{ _("Account Deletion and Deactivation") }}</h3>
|
<h3>{{ _("Account Deletion and Deactivation") }}</h3>
|
||||||
|
|
||||||
{% if current_user.rank.atLeast(current_user.rank.ADMIN) %}
|
{% if current_user.rank.at_least(current_user.rank.ADMIN) %}
|
||||||
<a class="btn btn-danger" href="{{ url_for('users.delete', username=user.username) }}">
|
<a class="btn btn-danger" href="{{ url_for('users.delete', username=user.username) }}">
|
||||||
{{ _("Delete or Deactivate") }}</a>
|
{{ _("Delete or Deactivate") }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
name="deactivate" value="{{ _('Deactivate') }}"
|
name="deactivate" value="{{ _('Deactivate') }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
class="btn btn-danger" />
|
class="btn btn-danger" />
|
||||||
{% if not can_delete and current_user.rank.atLeast(current_user.rank.ADMIN) %}
|
{% if not can_delete and current_user.rank.at_least(current_user.rank.ADMIN) %}
|
||||||
<input type="submit" name="delete" value="{{ _('Delete Anyway') }}" class="btn btn-danger ml-3" />
|
<input type="submit" name="delete" value="{{ _('Delete Anyway') }}" class="btn btn-danger ml-3" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
<p class="text-danger">{{ _("Doesn't have password") }}</p>
|
<p class="text-danger">{{ _("Doesn't have password") }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if not user.rank.atLeast(current_user.rank) %}
|
{% if not user.rank.at_least(current_user.rank) %}
|
||||||
<h3>{{ _("Ban") }}</h3>
|
<h3>{{ _("Ban") }}</h3>
|
||||||
{% if user.ban %}
|
{% if user.ban %}
|
||||||
<p>
|
<p>
|
||||||
|
@ -28,8 +28,8 @@
|
|||||||
{{ _("Report") }}
|
{{ _("Report") }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{% if current_user.is_authenticated and current_user.rank.atLeast(current_user.rank.MODERATOR) %}
|
{% if current_user.is_authenticated and current_user.rank.at_least(current_user.rank.MODERATOR) %}
|
||||||
{% if not user.rank.atLeast(current_user.rank) %}
|
{% if not user.rank.at_least(current_user.rank) %}
|
||||||
<a class="btn btn-secondary float-right mr-3" href="{{ url_for('users.modtools', username=user.username) }}">
|
<a class="btn btn-secondary float-right mr-3" href="{{ url_for('users.modtools', username=user.username) }}">
|
||||||
<i class="fas fa-user-shield mr-1"></i>
|
<i class="fas fa-user-shield mr-1"></i>
|
||||||
{{ _("Moderator Tools") }}
|
{{ _("Moderator Tools") }}
|
||||||
@ -164,7 +164,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if current_user == user or (current_user.is_authenticated and current_user.rank.atLeast(current_user.rank.ADMIN)) %}
|
{% if current_user == user or (current_user.is_authenticated and current_user.rank.at_least(current_user.rank.ADMIN)) %}
|
||||||
{% for medal in medals_locked %}
|
{% for medal in medals_locked %}
|
||||||
{% set value = medal.progress[0] %}
|
{% set value = medal.progress[0] %}
|
||||||
{% set target = medal.progress[1] %}
|
{% set target = medal.progress[1] %}
|
||||||
@ -202,7 +202,7 @@
|
|||||||
{{ _("Create package") }}
|
{{ _("Create package") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if current_user == user or (current_user.is_authenticated and current_user.rank.atLeast(current_user.rank.EDITOR)) %}
|
{% if current_user == user or (current_user.is_authenticated and current_user.rank.at_least(current_user.rank.EDITOR)) %}
|
||||||
<a class="float-right btn btn-sm btn-secondary mr-2"
|
<a class="float-right btn btn-sm btn-secondary mr-2"
|
||||||
href="{{ url_for('todo.tags', author=user.username) }}">
|
href="{{ url_for('todo.tags', author=user.username) }}">
|
||||||
{{ _("View list of tags") }}
|
{{ _("View list of tags") }}
|
||||||
|
@ -33,27 +33,27 @@ def is_username_valid(username):
|
|||||||
re.match(r"^[A-Za-z0-9._-]*$", username) and not re.match(r"^\.*$", username)
|
re.match(r"^[A-Za-z0-9._-]*$", username) and not re.match(r"^\.*$", username)
|
||||||
|
|
||||||
|
|
||||||
def isYes(val):
|
def is_yes(val):
|
||||||
return val and val.lower() in YESES
|
return val and val.lower() in YESES
|
||||||
|
|
||||||
|
|
||||||
def isNo(val):
|
def is_no(val):
|
||||||
return val and not isYes(val)
|
return val and not is_yes(val)
|
||||||
|
|
||||||
|
|
||||||
def nonEmptyOrNone(str):
|
def nonempty_or_none(str):
|
||||||
if str is None or str == "":
|
if str is None or str == "":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return str
|
return str
|
||||||
|
|
||||||
|
|
||||||
def shouldReturnJson():
|
def should_return_json():
|
||||||
return "application/json" in request.accept_mimetypes and \
|
return "application/json" in request.accept_mimetypes and \
|
||||||
not "text/html" in request.accept_mimetypes
|
not "text/html" in request.accept_mimetypes
|
||||||
|
|
||||||
|
|
||||||
def randomString(n):
|
def random_string(n):
|
||||||
return secrets.token_hex(int(n / 2))
|
return secrets.token_hex(int(n / 2))
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ from urllib.parse import urlsplit
|
|||||||
from git import GitCommandError
|
from git import GitCommandError
|
||||||
|
|
||||||
from app.tasks import TaskError
|
from app.tasks import TaskError
|
||||||
from app.utils import randomString
|
from app.utils import random_string
|
||||||
|
|
||||||
|
|
||||||
def generate_git_url(urlstr):
|
def generate_git_url(urlstr):
|
||||||
@ -40,7 +40,7 @@ def generate_git_url(urlstr):
|
|||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def get_temp_dir():
|
def get_temp_dir():
|
||||||
temp = os.path.join(tempfile.gettempdir(), randomString(10))
|
temp = os.path.join(tempfile.gettempdir(), random_string(10))
|
||||||
yield temp
|
yield temp
|
||||||
shutil.rmtree(temp)
|
shutil.rmtree(temp)
|
||||||
|
|
||||||
@ -50,21 +50,21 @@ def get_temp_dir():
|
|||||||
# Throws `TaskError` on failure.
|
# Throws `TaskError` on failure.
|
||||||
# Caller is responsible for deleting returned directory.
|
# Caller is responsible for deleting returned directory.
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def clone_repo(urlstr, ref=None, recursive=False):
|
def clone_repo(url_str, ref=None, recursive=False):
|
||||||
gitDir = os.path.join(tempfile.gettempdir(), randomString(10))
|
git_dir = os.path.join(tempfile.gettempdir(), random_string(10))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gitUrl = generate_git_url(urlstr)
|
git_url = generate_git_url(url_str)
|
||||||
print("Cloning from " + gitUrl)
|
print("Cloning from " + git_url)
|
||||||
|
|
||||||
if ref is None:
|
if ref is None:
|
||||||
repo = git.Repo.clone_from(gitUrl, gitDir,
|
repo = git.Repo.clone_from(git_url, git_dir,
|
||||||
progress=None, env=None, depth=1, recursive=recursive, kill_after_timeout=15)
|
progress=None, env=None, depth=1, recursive=recursive, kill_after_timeout=15)
|
||||||
else:
|
else:
|
||||||
assert ref != ""
|
assert ref != ""
|
||||||
|
|
||||||
repo = git.Repo.init(gitDir)
|
repo = git.Repo.init(git_dir)
|
||||||
origin = repo.create_remote("origin", url=gitUrl)
|
origin = repo.create_remote("origin", url=git_url)
|
||||||
assert origin.exists()
|
assert origin.exists()
|
||||||
origin.fetch()
|
origin.fetch()
|
||||||
repo.git.checkout(ref)
|
repo.git.checkout(ref)
|
||||||
@ -72,7 +72,7 @@ def clone_repo(urlstr, ref=None, recursive=False):
|
|||||||
repo.git.submodule('update', '--init')
|
repo.git.submodule('update', '--init')
|
||||||
|
|
||||||
yield repo
|
yield repo
|
||||||
shutil.rmtree(gitDir)
|
shutil.rmtree(git_dir)
|
||||||
return
|
return
|
||||||
|
|
||||||
except GitCommandError as e:
|
except GitCommandError as e:
|
||||||
@ -83,7 +83,7 @@ def clone_repo(urlstr, ref=None, recursive=False):
|
|||||||
err = "Unable to find the reference " + (ref or "?") + "\n" + e.stderr
|
err = "Unable to find the reference " + (ref or "?") + "\n" + e.stderr
|
||||||
|
|
||||||
raise TaskError(err.replace("stderr: ", "") \
|
raise TaskError(err.replace("stderr: ", "") \
|
||||||
.replace("Cloning into '" + gitDir + "'...", "") \
|
.replace("Cloning into '" + git_dir + "'...", "") \
|
||||||
.strip())
|
.strip())
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ from sqlalchemy.orm import sessionmaker
|
|||||||
from app.models import User, NotificationType, Package, UserRank, Notification, db, AuditSeverity, AuditLogEntry, ThreadReply, Thread, PackageState, PackageType, PackageAlias
|
from app.models import User, NotificationType, Package, UserRank, Notification, db, AuditSeverity, AuditLogEntry, ThreadReply, Thread, PackageState, PackageType, PackageAlias
|
||||||
|
|
||||||
|
|
||||||
def getPackageByInfo(author, name):
|
def get_package_by_info(author, name):
|
||||||
user = User.query.filter_by(username=author).first()
|
user = User.query.filter_by(username=author).first()
|
||||||
if user is None:
|
if user is None:
|
||||||
return None
|
return None
|
||||||
@ -39,6 +39,7 @@ def getPackageByInfo(author, name):
|
|||||||
|
|
||||||
return package
|
return package
|
||||||
|
|
||||||
|
|
||||||
def is_package_page(f):
|
def is_package_page(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
@ -48,9 +49,9 @@ def is_package_page(f):
|
|||||||
author = kwargs["author"]
|
author = kwargs["author"]
|
||||||
name = kwargs["name"]
|
name = kwargs["name"]
|
||||||
|
|
||||||
package = getPackageByInfo(author, name)
|
package = get_package_by_info(author, name)
|
||||||
if package is None:
|
if package is None:
|
||||||
package = getPackageByInfo(author, name + "_game")
|
package = get_package_by_info(author, name + "_game")
|
||||||
if package and package.type == PackageType.GAME:
|
if package and package.type == PackageType.GAME:
|
||||||
args = dict(kwargs)
|
args = dict(kwargs)
|
||||||
args["name"] = name + "_game"
|
args["name"] = name + "_game"
|
||||||
@ -72,28 +73,28 @@ def is_package_page(f):
|
|||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
def addNotification(target, causer: User, type: NotificationType, title: str, url: str, package: Package = None):
|
def add_notification(target, causer: User, type: NotificationType, title: str, url: str, package: Package = None):
|
||||||
try:
|
try:
|
||||||
iter(target)
|
iter(target)
|
||||||
for x in target:
|
for x in target:
|
||||||
addNotification(x, causer, type, title, url, package)
|
add_notification(x, causer, type, title, url, package)
|
||||||
return
|
return
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if target.rank.atLeast(UserRank.NEW_MEMBER) and target != causer:
|
if target.rank.at_least(UserRank.NEW_MEMBER) and target != causer:
|
||||||
Notification.query.filter_by(user=target, causer=causer, type=type, title=title, url=url, package=package).delete()
|
Notification.query.filter_by(user=target, causer=causer, type=type, title=title, url=url, package=package).delete()
|
||||||
notif = Notification(target, causer, type, title, url, package)
|
notif = Notification(target, causer, type, title, url, package)
|
||||||
db.session.add(notif)
|
db.session.add(notif)
|
||||||
|
|
||||||
|
|
||||||
def addAuditLog(severity: AuditSeverity, causer: User, title: str, url: typing.Optional[str],
|
def add_audit_log(severity: AuditSeverity, causer: User, title: str, url: typing.Optional[str],
|
||||||
package: Package = None, description: str = None):
|
package: Package = None, description: str = None):
|
||||||
entry = AuditLogEntry(causer, severity, title, url, package, description)
|
entry = AuditLogEntry(causer, severity, title, url, package, description)
|
||||||
db.session.add(entry)
|
db.session.add(entry)
|
||||||
|
|
||||||
|
|
||||||
def clearNotifications(url):
|
def clear_notifications(url):
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
Notification.query.filter_by(user=current_user, url=url).delete()
|
Notification.query.filter_by(user=current_user, url=url).delete()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -105,12 +106,12 @@ def get_system_user():
|
|||||||
return system_user
|
return system_user
|
||||||
|
|
||||||
|
|
||||||
def addSystemNotification(target, type: NotificationType, title: str, url: str, package: Package = None):
|
def add_system_notification(target, type: NotificationType, title: str, url: str, package: Package = None):
|
||||||
return addNotification(target, get_system_user(), type, title, url, package)
|
return add_notification(target, get_system_user(), type, title, url, package)
|
||||||
|
|
||||||
|
|
||||||
def addSystemAuditLog(severity: AuditSeverity, title: str, url: str, package=None, description=None):
|
def add_system_audit_log(severity: AuditSeverity, title: str, url: str, package=None, description=None):
|
||||||
return addAuditLog(severity, get_system_user(), title, url, package, description)
|
return add_audit_log(severity, get_system_user(), title, url, package, description)
|
||||||
|
|
||||||
|
|
||||||
def post_bot_message(package: Package, title: str, message: str):
|
def post_bot_message(package: Package, title: str, message: str):
|
||||||
@ -133,8 +134,7 @@ def post_bot_message(package: Package, title: str, message: str):
|
|||||||
reply.comment = "**{}**\n\n{}\n\nThis is an automated message, but you can reply if you need help".format(title, message)
|
reply.comment = "**{}**\n\n{}\n\nThis is an automated message, but you can reply if you need help".format(title, message)
|
||||||
db.session.add(reply)
|
db.session.add(reply)
|
||||||
|
|
||||||
addNotification(thread.watchers, system_user, NotificationType.BOT,
|
add_notification(thread.watchers, system_user, NotificationType.BOT, title, thread.get_view_url(), thread.package)
|
||||||
title, thread.get_view_url(), thread.package)
|
|
||||||
|
|
||||||
thread.replies.append(reply)
|
thread.replies.append(reply)
|
||||||
|
|
||||||
|
@ -12,9 +12,10 @@ from urllib.parse import urlencode
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
def urlEncodeNonAscii(b):
|
def url_encode_non_ascii(b):
|
||||||
return re.sub('[\x80-\xFF]', lambda c: '%%%02x' % ord(c.group(0)), b)
|
return re.sub('[\x80-\xFF]', lambda c: '%%%02x' % ord(c.group(0)), b)
|
||||||
|
|
||||||
|
|
||||||
class Profile:
|
class Profile:
|
||||||
def __init__(self, username):
|
def __init__(self, username):
|
||||||
self.username = username
|
self.username = username
|
||||||
@ -31,6 +32,7 @@ class Profile:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.username + "\n" + str(self.signature) + "\n" + str(self.properties)
|
return self.username + "\n" + str(self.signature) + "\n" + str(self.properties)
|
||||||
|
|
||||||
|
|
||||||
def __extract_properties(profile, soup):
|
def __extract_properties(profile, soup):
|
||||||
el = soup.find(id="viewprofile")
|
el = soup.find(id="viewprofile")
|
||||||
if el is None:
|
if el is None:
|
||||||
@ -66,6 +68,7 @@ def __extract_properties(profile, soup):
|
|||||||
elif element and element.name is not None:
|
elif element and element.name is not None:
|
||||||
print("Unexpected other")
|
print("Unexpected other")
|
||||||
|
|
||||||
|
|
||||||
def __extract_signature(soup):
|
def __extract_signature(soup):
|
||||||
res = soup.find_all("div", class_="signature")
|
res = soup.find_all("div", class_="signature")
|
||||||
if len(res) != 1:
|
if len(res) != 1:
|
||||||
@ -74,7 +77,7 @@ def __extract_signature(soup):
|
|||||||
return str(res[0])
|
return str(res[0])
|
||||||
|
|
||||||
|
|
||||||
def getProfileURL(url, username):
|
def get_profile_url(url, username):
|
||||||
url = urlparse.urlparse(url)
|
url = urlparse.urlparse(url)
|
||||||
|
|
||||||
# Update path
|
# Update path
|
||||||
@ -89,8 +92,8 @@ def getProfileURL(url, username):
|
|||||||
return urlparse.urlunparse(url)
|
return urlparse.urlunparse(url)
|
||||||
|
|
||||||
|
|
||||||
def getProfile(url, username):
|
def get_profile(url, username):
|
||||||
url = getProfileURL(url, username)
|
url = get_profile_url(url, username)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = urllib.request.urlopen(url, timeout=15)
|
req = urllib.request.urlopen(url, timeout=15)
|
||||||
@ -114,7 +117,8 @@ def getProfile(url, username):
|
|||||||
|
|
||||||
regex_id = re.compile(r"^.*t=([0-9]+).*$")
|
regex_id = re.compile(r"^.*t=([0-9]+).*$")
|
||||||
|
|
||||||
def parseForumListPage(id, page, out, extra=None):
|
|
||||||
|
def parse_forum_list_page(id, page, out, extra=None):
|
||||||
num_per_page = 30
|
num_per_page = 30
|
||||||
start = page*num_per_page+1
|
start = page*num_per_page+1
|
||||||
print(" - Fetching page {} (topics {}-{})".format(page, start, start+num_per_page))
|
print(" - Fetching page {} (topics {}-{})".format(page, start, start+num_per_page))
|
||||||
@ -171,15 +175,11 @@ def parseForumListPage(id, page, out, extra=None):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def getTopicsFromForum(id, out, extra=None):
|
|
||||||
|
def get_topics_from_forum(id, out, extra=None):
|
||||||
print("Fetching all topics from forum {}".format(id))
|
print("Fetching all topics from forum {}".format(id))
|
||||||
page = 0
|
page = 0
|
||||||
while parseForumListPage(id, page, out, extra):
|
while parse_forum_list_page(id, page, out, extra):
|
||||||
page = page + 1
|
page = page + 1
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def dumpTitlesToFile(topics, path):
|
|
||||||
with open(path, "w") as out_file:
|
|
||||||
for topic in topics.values():
|
|
||||||
out_file.write(topic["title"] + "\n")
|
|
||||||
|
@ -76,7 +76,7 @@ def rank_required(rank):
|
|||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if not current_user.is_authenticated:
|
if not current_user.is_authenticated:
|
||||||
return redirect(url_for("users.login"))
|
return redirect(url_for("users.login"))
|
||||||
if not current_user.rank.atLeast(rank):
|
if not current_user.rank.at_least(rank):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
Loading…
Reference in New Issue
Block a user