mirror of
https://github.com/minetest/contentdb.git
synced 2025-01-03 11:47:28 +01:00
Clean up admin blueprint
This commit is contained in:
parent
cb2d9d4b07
commit
e2708933d3
@ -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,7 +69,8 @@ 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():
|
||||
@ -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(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,6 +186,7 @@ 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)))
|
||||
@ -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():
|
||||
|
@ -14,10 +14,10 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
@ -51,6 +51,7 @@ def admin_page():
|
||||
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,7 +70,6 @@ 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)
|
||||
|
||||
@ -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()
|
||||
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)
|
@ -41,6 +41,6 @@ def audit():
|
||||
|
||||
@bp.route("/admin/audit/<int:id>/")
|
||||
@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)
|
||||
|
@ -14,18 +14,17 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
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"))
|
||||
|
@ -15,15 +15,15 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
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,12 +31,14 @@ 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")
|
||||
|
||||
|
||||
@bp.route("/licenses/new/", methods=["GET", "POST"])
|
||||
@bp.route("/licenses/<name>/edit/", methods=["GET", "POST"])
|
||||
@rank_required(UserRank.MODERATOR)
|
||||
|
@ -15,14 +15,14 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
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,6 +40,7 @@ def tag_list():
|
||||
|
||||
return render_template("admin/tags/list.html", tags=query.all())
|
||||
|
||||
|
||||
class TagForm(FlaskForm):
|
||||
title = StringField("Title", [InputRequired(), Length(3, 100)])
|
||||
description = TextAreaField("Description", [Optional(), Length(0, 500)])
|
||||
@ -47,6 +48,7 @@ class TagForm(FlaskForm):
|
||||
is_protected = BooleanField("Is Protected")
|
||||
submit = SubmitField("Save")
|
||||
|
||||
|
||||
@bp.route("/tags/new/", methods=["GET", "POST"])
|
||||
@bp.route("/tags/<name>/edit/", methods=["GET", "POST"])
|
||||
@login_required
|
||||
|
@ -15,14 +15,14 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
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,11 +30,13 @@ 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)])
|
||||
protocol = IntegerField("Protocol")
|
||||
submit = SubmitField("Save")
|
||||
|
||||
|
||||
@bp.route("/versions/new/", methods=["GET", "POST"])
|
||||
@bp.route("/versions/<name>/edit/", methods=["GET", "POST"])
|
||||
@rank_required(UserRank.MODERATOR)
|
||||
|
@ -15,14 +15,14 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
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,12 +30,15 @@ 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)])
|
||||
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")])
|
||||
submit = SubmitField("Save")
|
||||
|
||||
|
||||
@bp.route("/admin/warnings/new/", methods=["GET", "POST"])
|
||||
@bp.route("/admin/warnings/<name>/edit/", methods=["GET", "POST"])
|
||||
@rank_required(UserRank.ADMIN)
|
||||
|
Loading…
Reference in New Issue
Block a user