diff --git a/app/blueprints/admin/actions.py b/app/blueprints/admin/actions.py
index 8a742f65..d584e392 100644
--- a/app/blueprints/admin/actions.py
+++ b/app/blueprints/admin/actions.py
@@ -20,10 +20,11 @@ from typing import List
import requests
from celery import group
-from flask import *
+from flask import redirect, url_for, flash, current_app
from sqlalchemy import or_, and_
-from app.models import *
+from app.models import PackageRelease, db, Package, PackageState, PackageScreenshot, MetaPackage, User, \
+ NotificationType, PackageUpdateConfig, License, UserRank, PackageType
from app.tasks.forumtasks import importTopicList, checkAllForumAccounts
from app.tasks.importtasks import importRepoScreenshot, checkZipRelease, check_for_updates
from app.utils import addNotification, get_system_user
@@ -31,6 +32,7 @@ from app.utils.image import get_image_size
actions = {}
+
def action(title: str):
def func(f):
name = f.__name__
@@ -43,13 +45,15 @@ def action(title: str):
return func
+
@action("Delete stuck releases")
def del_stuck_releases():
- PackageRelease.query.filter(PackageRelease.task_id != None).delete()
+ PackageRelease.query.filter(PackageRelease.task_id.isnot(None)).delete()
db.session.commit()
return redirect(url_for("admin.admin_page"))
-@action("Check releases")
+
+@action("Check ZIP releases")
def check_releases():
releases = PackageRelease.query.filter(PackageRelease.url.like("/uploads/%")).all()
@@ -65,10 +69,11 @@ def check_releases():
return redirect(url_for("todo.view_editor"))
-@action("Reimport packages")
+
+@action("Check the first release of all packages")
def reimport_packages():
tasks = []
- 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()
if release:
tasks.append(checkZipRelease.s(release.id, release.file_path))
@@ -81,42 +86,46 @@ def reimport_packages():
return redirect(url_for("todo.view_editor"))
-@action("Import topic list")
+
+@action("Import forum topic list")
def import_topic_list():
task = importTopicList.delay()
return redirect(url_for("tasks.check", id=task.id, r=url_for("todo.topics")))
+
@action("Check all forum accounts")
def check_all_forum_accounts():
task = checkAllForumAccounts.delay()
return redirect(url_for("tasks.check", id=task.id, r=url_for("admin.admin_page")))
+
@action("Import screenshots")
def import_screenshots():
packages = Package.query \
- .filter(Package.state!=PackageState.DELETED) \
- .outerjoin(PackageScreenshot, Package.id==PackageScreenshot.package_id) \
- .filter(PackageScreenshot.id==None) \
+ .filter(Package.state != PackageState.DELETED) \
+ .outerjoin(PackageScreenshot, Package.id == PackageScreenshot.package_id) \
+ .filter(PackageScreenshot.id.is_(None)) \
.all()
for package in packages:
importRepoScreenshot.delay(package.id)
return redirect(url_for("admin.admin_page"))
-@action("Clean uploads")
+
+@action("Remove unused uploads")
def clean_uploads():
- upload_dir = app.config['UPLOAD_DIR']
+ upload_dir = current_app.config['UPLOAD_DIR']
(_, _, filenames) = next(os.walk(upload_dir))
existing_uploads = set(filenames)
if len(existing_uploads) != 0:
- def getURLsFromDB(column):
- results = db.session.query(column).filter(column != None, column != "").all()
+ def get_filenames_from_column(column):
+ results = db.session.query(column).filter(column.isnot(None), column != "").all()
return set([os.path.basename(x[0]) for x in results])
- release_urls = getURLsFromDB(PackageRelease.url)
- screenshot_urls = getURLsFromDB(PackageScreenshot.url)
+ release_urls = get_filenames_from_column(PackageRelease.url)
+ screenshot_urls = get_filenames_from_column(PackageScreenshot.url)
db_urls = release_urls.union(screenshot_urls)
unreachable = existing_uploads.difference(db_urls)
@@ -135,7 +144,8 @@ def clean_uploads():
return redirect(url_for("admin.admin_page"))
-@action("Delete metapackages")
+
+@action("Delete unused metapackages")
def del_meta_packages():
query = MetaPackage.query.filter(~MetaPackage.dependencies.any(), ~MetaPackage.packages.any())
count = query.count()
@@ -145,6 +155,7 @@ def del_meta_packages():
flash("Deleted " + str(count) + " unused meta packages", "success")
return redirect(url_for("admin.admin_page"))
+
@action("Delete removed packages")
def del_removed_packages():
query = Package.query.filter_by(state=PackageState.DELETED)
@@ -157,24 +168,6 @@ def del_removed_packages():
flash("Deleted {} soft deleted packages packages".format(count), "success")
return redirect(url_for("admin.admin_page"))
-@action("Add update config")
-def add_update_config():
- added = 0
- for pkg in Package.query.filter(Package.repo != None, Package.releases.any(), Package.update_config == None).all():
- pkg.update_config = PackageUpdateConfig()
- pkg.update_config.auto_created = True
-
- release: PackageRelease = pkg.releases.first()
- if release and release.commit_hash:
- pkg.update_config.last_commit = release.commit_hash
-
- db.session.add(pkg.update_config)
- added += 1
-
- db.session.commit()
-
- flash("Added {} update configs".format(added), "success")
- return redirect(url_for("admin.admin_page"))
@action("Run update configs")
def run_update_config():
@@ -183,6 +176,7 @@ def run_update_config():
flash("Started update configs", "success")
return redirect(url_for("admin.admin_page"))
+
def _package_list(packages: List[str]):
# Who needs translations?
if len(packages) >= 3:
@@ -192,14 +186,15 @@ def _package_list(packages: List[str]):
packages_list = " and ".join(packages)
return packages_list
+
@action("Send WIP package notification")
def remind_wip():
- users = User.query.filter(User.packages.any(or_(Package.state==PackageState.WIP, Package.state==PackageState.CHANGES_NEEDED)))
+ users = User.query.filter(User.packages.any(or_(Package.state == PackageState.WIP, Package.state == PackageState.CHANGES_NEEDED)))
system_user = get_system_user()
for user in users:
packages = db.session.query(Package.title).filter(
- Package.author_id==user.id,
- or_(Package.state==PackageState.WIP, Package.state==PackageState.CHANGES_NEEDED)) \
+ Package.author_id == user.id,
+ or_(Package.state == PackageState.WIP, Package.state==PackageState.CHANGES_NEEDED)) \
.all()
packages = [pkg[0] for pkg in packages]
@@ -213,6 +208,7 @@ def remind_wip():
url_for('todo.view_user', username=user.username))
db.session.commit()
+
@action("Send outdated package notification")
def remind_outdated():
users = User.query.filter(User.maintained_packages.any(
@@ -233,6 +229,7 @@ def remind_outdated():
db.session.commit()
+
@action("Import licenses from SPDX")
def import_licenses():
renames = {
@@ -283,7 +280,8 @@ def import_licenses():
@action("Delete inactive users")
def delete_inactive_users():
- users = User.query.filter(User.is_active==False, User.packages==None, User.forum_topics==None, User.rank==UserRank.NOT_JOINED).all()
+ users = User.query.filter(User.is_active == False, User.packages.is_(None), User.forum_topics.is_(None),
+ User.rank == UserRank.NOT_JOINED).all()
for user in users:
db.session.delete(user)
db.session.commit()
@@ -313,7 +311,7 @@ def remind_video_url():
@action("Update screenshot sizes")
-def remind_video_url():
+def update_screenshot_sizes():
import sys
for screenshot in PackageScreenshot.query.all():
diff --git a/app/blueprints/admin/admin.py b/app/blueprints/admin/admin.py
index 8345dfbf..eea4d952 100644
--- a/app/blueprints/admin/admin.py
+++ b/app/blueprints/admin/admin.py
@@ -14,10 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from flask import *
+from flask import redirect, render_template, url_for, request, flash
from flask_login import current_user, login_user
from flask_wtf import FlaskForm
-from wtforms import *
+from wtforms import StringField, SubmitField
from wtforms.validators import InputRequired, Length
from app.utils import rank_required, addAuditLog, addNotification, get_system_user
from . import bp
@@ -48,9 +48,10 @@ def admin_page():
else:
flash("Unknown action: " + action, "danger")
- deleted_packages = Package.query.filter(Package.state==PackageState.DELETED).all()
+ deleted_packages = Package.query.filter(Package.state == PackageState.DELETED).all()
return render_template("admin/list.html", deleted_packages=deleted_packages, actions=actions)
+
class SwitchUserForm(FlaskForm):
username = StringField("Username")
submit = SubmitField("Switch")
@@ -69,14 +70,13 @@ def switch_user():
else:
flash("Unable to login as user", "danger")
-
# Process GET or invalid POST
return render_template("admin/switch_user.html", form=form)
class SendNotificationForm(FlaskForm):
- title = StringField("Title", [InputRequired(), Length(1, 300)])
- url = StringField("URL", [InputRequired(), Length(1, 100)], default="/")
+ title = StringField("Title", [InputRequired(), Length(1, 300)])
+ url = StringField("URL", [InputRequired(), Length(1, 100)], default="/")
submit = SubmitField("Send")
@@ -86,7 +86,7 @@ def send_bulk_notification():
form = SendNotificationForm(request.form)
if form.validate_on_submit():
addAuditLog(AuditSeverity.MODERATION, current_user,
- "Sent bulk notification", None, 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()
addNotification(users, get_system_user(), NotificationType.OTHER, form.title.data, form.url.data, None)
@@ -121,5 +121,10 @@ def restore():
db.session.commit()
return redirect(package.getURL("packages.view"))
- deleted_packages = Package.query.filter(Package.state==PackageState.DELETED).join(Package.author).order_by(db.asc(User.username), db.asc(Package.name)).all()
- return render_template("admin/restore.html", deleted_packages=deleted_packages)
\ No newline at end of file
+ deleted_packages = Package.query \
+ .filter(Package.state == PackageState.DELETED) \
+ .join(Package.author) \
+ .order_by(db.asc(User.username), db.asc(Package.name)) \
+ .all()
+
+ return render_template("admin/restore.html", deleted_packages=deleted_packages)
diff --git a/app/blueprints/admin/audit.py b/app/blueprints/admin/audit.py
index cf8a8f92..26c20ef5 100644
--- a/app/blueprints/admin/audit.py
+++ b/app/blueprints/admin/audit.py
@@ -41,6 +41,6 @@ def audit():
@bp.route("/admin/audit//")
@rank_required(UserRank.MODERATOR)
-def audit_view(id):
- entry = AuditLogEntry.query.get(id)
+def audit_view(id_):
+ entry = AuditLogEntry.query.get(id_)
return render_template("admin/audit_view.html", entry=entry)
diff --git a/app/blueprints/admin/email.py b/app/blueprints/admin/email.py
index 99b8aada..b0b54a05 100644
--- a/app/blueprints/admin/email.py
+++ b/app/blueprints/admin/email.py
@@ -14,18 +14,17 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-
-from flask import *
+from flask import request, abort, url_for, redirect, render_template, flash
from flask_login import current_user
from flask_wtf import FlaskForm
-from wtforms import *
-from wtforms.validators import *
+from wtforms import TextAreaField, SubmitField, StringField
+from wtforms.validators import InputRequired, Length
from app.markdown import render_markdown
-from app.models import *
from app.tasks.emails import send_user_email
from app.utils import rank_required, addAuditLog
from . import bp
+from ...models import UserRank, User, AuditSeverity
class SendEmailForm(FlaskForm):
@@ -67,11 +66,11 @@ def send_bulk_email():
form = SendEmailForm(request.form)
if form.validate_on_submit():
addAuditLog(AuditSeverity.MODERATION, current_user,
- "Sent bulk email", None, None, form.text.data)
+ "Sent bulk email", url_for("admin.admin_page"), None, form.text.data)
text = form.text.data
html = render_markdown(text)
- for user in User.query.filter(User.email != None).all():
+ for user in User.query.filter(User.email.isnot(None)).all():
send_user_email.delay(user.email, user.locale or "en", form.subject.data, text, html)
return redirect(url_for("admin.admin_page"))
diff --git a/app/blueprints/admin/licenseseditor.py b/app/blueprints/admin/licenseseditor.py
index 40ef3c66..e4a5be13 100644
--- a/app/blueprints/admin/licenseseditor.py
+++ b/app/blueprints/admin/licenseseditor.py
@@ -15,15 +15,15 @@
# along with this program. If not, see .
-from flask import *
+from flask import redirect, render_template, abort, url_for, request, flash
from flask_wtf import FlaskForm
-from wtforms import *
+from wtforms import StringField, BooleanField, SubmitField
from wtforms.fields.html5 import URLField
-from wtforms.validators import *
+from wtforms.validators import InputRequired, Length, Optional
-from app.models import *
from app.utils import rank_required, nonEmptyOrNone
from . import bp
+from ...models import UserRank, License, db
@bp.route("/licenses/")
@@ -31,11 +31,13 @@ from . import bp
def license_list():
return render_template("admin/licenses/list.html", licenses=License.query.order_by(db.asc(License.name)).all())
+
class LicenseForm(FlaskForm):
- name = StringField("Name", [InputRequired(), Length(3,100)])
- is_foss = BooleanField("Is FOSS")
- url = URLField("URL", [Optional], filters=[nonEmptyOrNone])
- submit = SubmitField("Save")
+ name = StringField("Name", [InputRequired(), Length(3, 100)])
+ is_foss = BooleanField("Is FOSS")
+ url = URLField("URL", [Optional], filters=[nonEmptyOrNone])
+ submit = SubmitField("Save")
+
@bp.route("/licenses/new/", methods=["GET", "POST"])
@bp.route("/licenses//edit/", methods=["GET", "POST"])
diff --git a/app/blueprints/admin/tagseditor.py b/app/blueprints/admin/tagseditor.py
index ad0d731f..02f22912 100644
--- a/app/blueprints/admin/tagseditor.py
+++ b/app/blueprints/admin/tagseditor.py
@@ -15,14 +15,14 @@
# along with this program. If not, see .
-from flask import *
+from flask import redirect, render_template, abort, url_for, request
from flask_login import current_user, login_required
from flask_wtf import FlaskForm
-from wtforms import *
-from wtforms.validators import *
+from wtforms import StringField, TextAreaField, BooleanField, SubmitField
+from wtforms.validators import InputRequired, Length, Optional, Regexp
-from app.models import *
from . import bp
+from ...models import Permission, Tag, db
@bp.route("/tags/")
@@ -40,12 +40,14 @@ def tag_list():
return render_template("admin/tags/list.html", tags=query.all())
+
class TagForm(FlaskForm):
- title = StringField("Title", [InputRequired(), Length(3,100)])
+ title = StringField("Title", [InputRequired(), Length(3, 100)])
description = TextAreaField("Description", [Optional(), Length(0, 500)])
- name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
+ name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
is_protected = BooleanField("Is Protected")
- submit = SubmitField("Save")
+ submit = SubmitField("Save")
+
@bp.route("/tags/new/", methods=["GET", "POST"])
@bp.route("/tags//edit/", methods=["GET", "POST"])
diff --git a/app/blueprints/admin/versioneditor.py b/app/blueprints/admin/versioneditor.py
index 62e1f1af..b3063b49 100644
--- a/app/blueprints/admin/versioneditor.py
+++ b/app/blueprints/admin/versioneditor.py
@@ -15,14 +15,14 @@
# along with this program. If not, see .
-from flask import *
+from flask import redirect, render_template, abort, url_for, request, flash
from flask_wtf import FlaskForm
-from wtforms import *
-from wtforms.validators import *
+from wtforms import StringField, IntegerField, SubmitField
+from wtforms.validators import InputRequired, Length
-from app.models import *
from app.utils import rank_required
from . import bp
+from ...models import UserRank, MinetestRelease, db
@bp.route("/versions/")
@@ -30,10 +30,12 @@ from . import bp
def version_list():
return render_template("admin/versions/list.html", versions=MinetestRelease.query.order_by(db.asc(MinetestRelease.id)).all())
+
class VersionForm(FlaskForm):
- name = StringField("Name", [InputRequired(), Length(3,100)])
+ name = StringField("Name", [InputRequired(), Length(3, 100)])
protocol = IntegerField("Protocol")
- submit = SubmitField("Save")
+ submit = SubmitField("Save")
+
@bp.route("/versions/new/", methods=["GET", "POST"])
@bp.route("/versions//edit/", methods=["GET", "POST"])
diff --git a/app/blueprints/admin/warningseditor.py b/app/blueprints/admin/warningseditor.py
index f23b8e91..94a8df62 100644
--- a/app/blueprints/admin/warningseditor.py
+++ b/app/blueprints/admin/warningseditor.py
@@ -15,14 +15,14 @@
# along with this program. If not, see .
-from flask import *
+from flask import redirect, render_template, abort, url_for, request, flash
from flask_wtf import FlaskForm
-from wtforms import *
-from wtforms.validators import *
+from wtforms import StringField, TextAreaField, SubmitField
+from wtforms.validators import InputRequired, Length, Optional, Regexp
-from app.models import *
from app.utils import rank_required
from . import bp
+from ...models import UserRank, ContentWarning, db
@bp.route("/admin/warnings/")
@@ -30,11 +30,14 @@ from . import bp
def warning_list():
return render_template("admin/warnings/list.html", warnings=ContentWarning.query.order_by(db.asc(ContentWarning.title)).all())
+
class WarningForm(FlaskForm):
- title = StringField("Title", [InputRequired(), Length(3,100)])
+ title = StringField("Title", [InputRequired(), Length(3, 100)])
description = TextAreaField("Description", [Optional(), Length(0, 500)])
- name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
- submit = SubmitField("Save")
+ name = StringField("Name", [Optional(), Length(1, 20),
+ Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
+ submit = SubmitField("Save")
+
@bp.route("/admin/warnings/new/", methods=["GET", "POST"])
@bp.route("/admin/warnings//edit/", methods=["GET", "POST"])