Compare commits
5 Commits
631ef2b10a
...
7bfb183578
Author | SHA1 | Date |
---|---|---|
rubenwardy | 7bfb183578 | |
rubenwardy | 906ec3885a | |
rubenwardy | f649fa57e6 | |
rubenwardy | b4208f2dda | |
rubenwardy | 1d36f7d12b |
|
@ -119,7 +119,7 @@ def check_for_ban():
|
|||
logout_user()
|
||||
return redirect(url_for("users.login"))
|
||||
elif current_user.rank == models.UserRank.NOT_JOINED:
|
||||
current_user.rank = models.UserRank.MEMBER
|
||||
current_user.rank = models.UserRank.NEW_MEMBER
|
||||
models.db.session.commit()
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# 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/>.
|
||||
|
||||
|
||||
import datetime
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
|
@ -25,7 +25,7 @@ from sqlalchemy import or_, and_
|
|||
|
||||
from app.logic.game_support import GameSupportResolver
|
||||
from app.models import PackageRelease, db, Package, PackageState, PackageScreenshot, MetaPackage, User, \
|
||||
NotificationType, PackageUpdateConfig, License, UserRank, PackageType, PackageGameSupport
|
||||
NotificationType, PackageUpdateConfig, License, UserRank, PackageType, ThreadReply
|
||||
from app.tasks.emails import send_pending_digests
|
||||
from app.tasks.forumtasks import importTopicList, checkAllForumAccounts
|
||||
from app.tasks.importtasks import importRepoScreenshot, checkZipRelease, check_for_updates, updateAllGameSupport
|
||||
|
@ -147,14 +147,14 @@ def clean_uploads():
|
|||
return redirect(url_for("admin.admin_page"))
|
||||
|
||||
|
||||
@action("Delete unused metapackages")
|
||||
def del_meta_packages():
|
||||
@action("Delete unused mod names")
|
||||
def del_mod_names():
|
||||
query = MetaPackage.query.filter(~MetaPackage.dependencies.any(), ~MetaPackage.packages.any())
|
||||
count = query.count()
|
||||
query.delete(synchronize_session=False)
|
||||
db.session.commit()
|
||||
|
||||
flash("Deleted " + str(count) + " unused meta packages", "success")
|
||||
flash("Deleted " + str(count) + " unused mod names", "success")
|
||||
return redirect(url_for("admin.admin_page"))
|
||||
|
||||
|
||||
|
@ -335,3 +335,14 @@ def detect_game_support():
|
|||
@action("Send pending notif digests")
|
||||
def do_send_pending_digests():
|
||||
send_pending_digests.delay()
|
||||
|
||||
|
||||
@action("Set users to new member")
|
||||
def set_new_members():
|
||||
threshold = datetime.datetime.now() - datetime.timedelta(days=7)
|
||||
|
||||
User.query.filter(User.rank == UserRank.MEMBER,
|
||||
~User.replies.any(ThreadReply.created_at < threshold),
|
||||
~User.packages.any(Package.approved_at < threshold)).update({"rank": UserRank.NEW_MEMBER}, synchronize_session=False)
|
||||
|
||||
db.session.commit()
|
||||
|
|
|
@ -19,22 +19,27 @@ from flask import *
|
|||
from sqlalchemy import func
|
||||
from app.models import MetaPackage, Package, db, Dependency, PackageState, ForumTopic
|
||||
|
||||
bp = Blueprint("metapackages", __name__)
|
||||
bp = Blueprint("modnames", __name__)
|
||||
|
||||
|
||||
@bp.route("/metapackages/")
|
||||
@bp.route("/metapackages/<path:path>")
|
||||
def mp_redirect(path):
|
||||
return redirect("/modnames/" + path)
|
||||
|
||||
|
||||
@bp.route("/modnames/")
|
||||
def list_all():
|
||||
mpackages = db.session.query(MetaPackage, func.count(Package.id)) \
|
||||
modnames = db.session.query(MetaPackage, func.count(Package.id)) \
|
||||
.select_from(MetaPackage).outerjoin(MetaPackage.packages) \
|
||||
.order_by(db.asc(MetaPackage.name)) \
|
||||
.group_by(MetaPackage.id).all()
|
||||
return render_template("metapackages/list.html", mpackages=mpackages)
|
||||
return render_template("modnames/list.html", modnames=modnames)
|
||||
|
||||
|
||||
@bp.route("/metapackages/<name>/")
|
||||
@bp.route("/modnames/<name>/")
|
||||
def view(name):
|
||||
mpackage = MetaPackage.query.filter_by(name=name).first()
|
||||
if mpackage is None:
|
||||
modname = MetaPackage.query.filter_by(name=name).first()
|
||||
if modname is None:
|
||||
abort(404)
|
||||
|
||||
dependers = db.session.query(Package) \
|
||||
|
@ -59,6 +64,6 @@ def view(name):
|
|||
.order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \
|
||||
.all()
|
||||
|
||||
return render_template("metapackages/view.html", mpackage=mpackage,
|
||||
return render_template("modnames/view.html", modname=modname,
|
||||
dependers=dependers, optional_dependers=optional_dependers,
|
||||
similar_topics=similar_topics)
|
|
@ -370,7 +370,7 @@ def create_edit(author=None, name=None):
|
|||
return render_template("packages/create_edit.html", package=package,
|
||||
form=form, author=author, enable_wizard=enableWizard,
|
||||
packages=package_query.all(),
|
||||
mpackages=MetaPackage.query.order_by(db.asc(MetaPackage.name)).all(),
|
||||
modnames=MetaPackage.query.order_by(db.asc(MetaPackage.name)).all(),
|
||||
tabs=get_package_tabs(current_user, package), current_tab="edit")
|
||||
|
||||
|
||||
|
@ -613,10 +613,10 @@ def share(package):
|
|||
@is_package_page
|
||||
def similar(package):
|
||||
packages_modnames = {}
|
||||
for metapackage in package.provides:
|
||||
packages_modnames[metapackage] = Package.query.filter(Package.id != package.id,
|
||||
for mname in package.provides:
|
||||
packages_modnames[mname] = Package.query.filter(Package.id != package.id,
|
||||
Package.state != PackageState.DELETED) \
|
||||
.filter(Package.provides.any(PackageProvides.c.metapackage_id == metapackage.id)) \
|
||||
.filter(Package.provides.any(PackageProvides.c.metapackage_id == mname.id)) \
|
||||
.order_by(db.desc(Package.score)) \
|
||||
.all()
|
||||
|
||||
|
|
|
@ -143,15 +143,15 @@ def tags_user():
|
|||
return redirect(url_for('todo.tags', author=current_user.username))
|
||||
|
||||
|
||||
@bp.route("/todo/metapackages/")
|
||||
@bp.route("/todo/modnames/")
|
||||
@login_required
|
||||
def metapackages():
|
||||
mpackages = MetaPackage.query \
|
||||
def modnames():
|
||||
mnames = MetaPackage.query \
|
||||
.filter(~ MetaPackage.packages.any(state=PackageState.APPROVED)) \
|
||||
.filter(MetaPackage.dependencies.any(Dependency.depender.has(state=PackageState.APPROVED), optional=False)) \
|
||||
.order_by(db.asc(MetaPackage.name)).all()
|
||||
|
||||
return render_template("todo/metapackages.html", mpackages=mpackages)
|
||||
return render_template("todo/modnames.html", modnames=mnames)
|
||||
|
||||
|
||||
@bp.route("/user/todo/")
|
||||
|
|
|
@ -104,7 +104,7 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
|||
* `name`: Package name.
|
||||
* `provides`: List of technical mod names inside the package.
|
||||
* `depends`: List of hard dependencies.
|
||||
* Each dep will either be a metapackage dependency (`name`), or a
|
||||
* Each dep will either be a modname dependency (`name`), or a
|
||||
package dependency (`author/name`).
|
||||
* `optional_depends`: list of optional dependencies
|
||||
* Same as above.
|
||||
|
|
|
@ -22,12 +22,14 @@ The filename of the `.conf` file depends on the content type:
|
|||
The `.conf` uses a key-value format, separated using equals. Here's a simple example:
|
||||
|
||||
name = mymod
|
||||
title = My Mod
|
||||
description = A short description to show in the client.
|
||||
|
||||
### Understood values
|
||||
|
||||
ContentDB understands the following information:
|
||||
|
||||
* `title` - A human-readable title.
|
||||
* `description` - A short description to show in the client.
|
||||
* `depends` - Comma-separated hard dependencies.
|
||||
* `optional_depends` - Comma-separated soft dependencies.
|
||||
|
|
|
@ -34,7 +34,7 @@ get_game_support(package):
|
|||
return support
|
||||
|
||||
get_meta_package_support(meta):
|
||||
for package implementing meta package:
|
||||
for package implementing mod name:
|
||||
support = support OR get_game_support(package)
|
||||
|
||||
return support
|
||||
|
@ -58,9 +58,9 @@ mtg_mod_blacklist = {
|
|||
class GameSupportResolver:
|
||||
session: sqlalchemy.orm.Session
|
||||
checked_packages = set()
|
||||
checked_metapackages = set()
|
||||
checked_modnames = set()
|
||||
resolved_packages: Dict[int, set[int]] = {}
|
||||
resolved_metapackages: Dict[int, set[int]] = {}
|
||||
resolved_modnames: Dict[int, set[int]] = {}
|
||||
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
@ -69,14 +69,14 @@ class GameSupportResolver:
|
|||
print(f"Resolving for {meta.name}", file=sys.stderr)
|
||||
|
||||
key = meta.name
|
||||
if key in self.resolved_metapackages:
|
||||
return self.resolved_metapackages.get(key)
|
||||
if key in self.resolved_modnames:
|
||||
return self.resolved_modnames.get(key)
|
||||
|
||||
if key in self.checked_metapackages:
|
||||
if key in self.checked_modnames:
|
||||
print(f"Error, cycle found: {','.join(history)}", file=sys.stderr)
|
||||
return set()
|
||||
|
||||
self.checked_metapackages.add(key)
|
||||
self.checked_modnames.add(key)
|
||||
|
||||
retval = set()
|
||||
|
||||
|
@ -94,7 +94,7 @@ class GameSupportResolver:
|
|||
|
||||
retval.update(ret)
|
||||
|
||||
self.resolved_metapackages[key] = retval
|
||||
self.resolved_modnames[key] = retval
|
||||
return retval
|
||||
|
||||
def resolve(self, package: Package, history: List[str]) -> set[int]:
|
||||
|
|
|
@ -666,14 +666,14 @@ class Package(db.Model):
|
|||
isApprover = user.rank.atLeast(UserRank.APPROVER)
|
||||
|
||||
if perm == Permission.CREATE_THREAD:
|
||||
return user.rank.atLeast(UserRank.MEMBER)
|
||||
return user.rank.atLeast(UserRank.NEW_MEMBER)
|
||||
|
||||
# Members can edit their own packages, and editors can edit any packages
|
||||
elif perm == Permission.MAKE_RELEASE or perm == Permission.ADD_SCREENSHOTS:
|
||||
return isMaintainer
|
||||
|
||||
elif perm == Permission.EDIT_PACKAGE:
|
||||
return isMaintainer and user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)
|
||||
return isMaintainer and user.rank.atLeast(UserRank.NEW_MEMBER)
|
||||
|
||||
elif perm == Permission.APPROVE_RELEASE:
|
||||
return (isMaintainer or isApprover) and user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)
|
||||
|
@ -688,7 +688,7 @@ class Package(db.Model):
|
|||
|
||||
elif perm == Permission.APPROVE_SCREENSHOT:
|
||||
return (isMaintainer or isApprover) and \
|
||||
user.rank.atLeast(UserRank.TRUSTED_MEMBER if self.approved else UserRank.NEW_MEMBER)
|
||||
user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)
|
||||
|
||||
elif perm == Permission.EDIT_MAINTAINERS or perm == Permission.DELETE_PACKAGE:
|
||||
return isOwner or user.rank.atLeast(UserRank.EDITOR)
|
||||
|
|
|
@ -153,7 +153,7 @@ class ThreadReply(db.Model):
|
|||
raise Exception("Unknown permission given to ThreadReply.checkPerm()")
|
||||
|
||||
if perm == Permission.EDIT_REPLY:
|
||||
return user.rank.atLeast(UserRank.MEMBER if user == self.author else UserRank.MODERATOR) and not self.thread.locked
|
||||
return user.rank.atLeast(UserRank.NEW_MEMBER if user == self.author else UserRank.MODERATOR) and not self.thread.locked
|
||||
|
||||
elif perm == Permission.DELETE_REPLY:
|
||||
return user.rank.atLeast(UserRank.MODERATOR) and self.thread.replies[0] != self
|
||||
|
|
|
@ -227,7 +227,7 @@ class User(db.Model, UserMixin):
|
|||
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))
|
||||
elif perm == Permission.CHANGE_DISPLAY_NAME:
|
||||
return user.rank.atLeast(UserRank.MEMBER if user == self else UserRank.MODERATOR)
|
||||
return user.rank.atLeast(UserRank.NEW_MEMBER if user == self else UserRank.MODERATOR)
|
||||
elif perm == Permission.CREATE_TOKEN:
|
||||
if user == self:
|
||||
return user.rank.atLeast(UserRank.MEMBER)
|
||||
|
@ -243,16 +243,18 @@ class User(db.Model, UserMixin):
|
|||
if self.rank.atLeast(UserRank.ADMIN):
|
||||
return True
|
||||
elif self.rank.atLeast(UserRank.TRUSTED_MEMBER):
|
||||
factor *= 2
|
||||
factor = 3
|
||||
elif self.rank.atLeast(UserRank.MEMBER):
|
||||
factor = 2
|
||||
|
||||
one_min_ago = datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
|
||||
if ThreadReply.query.filter_by(author=self) \
|
||||
.filter(ThreadReply.created_at > one_min_ago).count() >= 3 * factor:
|
||||
.filter(ThreadReply.created_at > one_min_ago).count() >= 2 * factor:
|
||||
return False
|
||||
|
||||
hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
|
||||
if ThreadReply.query.filter_by(author=self) \
|
||||
.filter(ThreadReply.created_at > hour_ago).count() >= 20 * factor:
|
||||
.filter(ThreadReply.created_at > hour_ago).count() >= 10 * factor:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -264,7 +266,9 @@ class User(db.Model, UserMixin):
|
|||
if self.rank.atLeast(UserRank.ADMIN):
|
||||
return True
|
||||
elif self.rank.atLeast(UserRank.TRUSTED_MEMBER):
|
||||
factor *= 5
|
||||
factor = 5
|
||||
elif self.rank.atLeast(UserRank.MEMBER):
|
||||
factor = 2
|
||||
|
||||
hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
|
||||
return Thread.query.filter_by(author=self) \
|
||||
|
|
|
@ -76,7 +76,7 @@ CELERYBEAT_SCHEDULE = {
|
|||
},
|
||||
'check_for_updates': {
|
||||
'task': 'app.tasks.importtasks.check_for_updates',
|
||||
'schedule': crontab(minute=10, hour=1), # 0110
|
||||
'schedule': crontab(minute=10, hour=2), # 0210
|
||||
},
|
||||
'send_pending_notifications': {
|
||||
'task': 'app.tasks.emails.send_pending_notifications',
|
||||
|
@ -90,6 +90,10 @@ CELERYBEAT_SCHEDULE = {
|
|||
'task': 'app.tasks.usertasks.delete_inactive_users',
|
||||
'schedule': crontab(minute=15), # every hour at quarter past
|
||||
},
|
||||
'upgrade_new_members': {
|
||||
'task': 'app.tasks.usertasks.upgrade_new_members',
|
||||
'schedule': crontab(minute=10, hour=3), # 0310
|
||||
},
|
||||
}
|
||||
celery.conf.beat_schedule = CELERYBEAT_SCHEDULE
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
|||
package.provides.clear()
|
||||
package.provides.extend(getMetaPackages(tree.getModNames()))
|
||||
|
||||
# Delete all meta package dependencies
|
||||
# Delete all mod name dependencies
|
||||
package.dependencies.filter(Dependency.meta_package != None).delete()
|
||||
|
||||
# Get raw dependencies
|
||||
|
|
|
@ -16,16 +16,31 @@
|
|||
|
||||
|
||||
import datetime
|
||||
from app.models import User, db, UserRank
|
||||
|
||||
from sqlalchemy import or_, and_
|
||||
|
||||
from app.models import User, db, UserRank, ThreadReply, Package
|
||||
from app.tasks import celery
|
||||
|
||||
|
||||
@celery.task()
|
||||
def delete_inactive_users():
|
||||
threshold = datetime.datetime.now() - datetime.timedelta(hours=5)
|
||||
threshold = datetime.datetime.now() - datetime.timedelta(hours=5)
|
||||
|
||||
users = User.query.filter(User.is_active==False, User.packages==None, User.forum_topics==None, User.created_at<=threshold, User.rank==UserRank.NOT_JOINED).all()
|
||||
for user in users:
|
||||
db.session.delete(user)
|
||||
users = User.query.filter(User.is_active == False, User.packages == None, User.forum_topics == None,
|
||||
User.created_at <= threshold, User.rank == UserRank.NOT_JOINED).all()
|
||||
for user in users:
|
||||
db.session.delete(user)
|
||||
|
||||
db.session.commit()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@celery.task()
|
||||
def upgrade_new_members():
|
||||
threshold = datetime.datetime.now() - datetime.timedelta(days=7)
|
||||
|
||||
User.query.filter(and_(User.rank == UserRank.NEW_MEMBER, or_(
|
||||
User.replies.any(ThreadReply.created_at < threshold),
|
||||
User.packages.any(Package.approved_at < threshold)))).update({"rank": UserRank.MEMBER})
|
||||
|
||||
db.session.commit()
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
{% macro package_lists() -%}
|
||||
<script>
|
||||
meta_packages = [
|
||||
{% for m in mpackages %}
|
||||
{% for m in modnames %}
|
||||
{# This is safe as name can only contain `[a-z0-9_]` #}
|
||||
{
|
||||
id: "{{ m.name }}",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{{ _("Meta Packages") }}
|
||||
{{ _("Mod Names") }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -18,11 +18,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% for pair in mpackages %}
|
||||
{% for pair in modnames %}
|
||||
{% set meta = pair[0] %}
|
||||
{% set count = pair[1] %}
|
||||
<a class="list-group-item list-group-item-action"
|
||||
href="{{ url_for('metapackages.view', name=meta.name) }}">
|
||||
href="{{ url_for('modnames.view', name=meta.name) }}">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
{{ meta.name }}
|
||||
|
@ -34,7 +34,7 @@
|
|||
</div>
|
||||
</a>
|
||||
{% else %}
|
||||
<li><i>{{ _("No meta packages found.") }}</i></li>
|
||||
<li><i>{{ _("No mod names found.") }}</i></li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,21 +1,21 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{{ mpackage.name }} - {{ _("Meta Packages") }}
|
||||
{{ modname.name }} - {{ _("Mod Names") }}
|
||||
{% endblock %}
|
||||
|
||||
{% from "macros/packagegridtile.html" import render_pkggrid %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ _("Meta Package \"%(name)s\"", name=mpackage.name) }}</h1>
|
||||
<h1>{{ _("Mod Name \"%(name)s\"", name=modname.name) }}</h1>
|
||||
|
||||
<h2>{{ _("Provided By") }}</h2>
|
||||
|
||||
<h3>{{ _("Games") }}</h3>
|
||||
{{ render_pkggrid(mpackage.packages.filter_by(type="GAME", state="APPROVED").all()) }}
|
||||
|
||||
<h3>{{ _("Mods") }}</h3>
|
||||
{{ render_pkggrid(mpackage.packages.filter_by(type="MOD", state="APPROVED").all()) }}
|
||||
{{ render_pkggrid(modname.packages.filter_by(type="MOD", state="APPROVED").all()) }}
|
||||
|
||||
<h3>{{ _("Games") }}</h3>
|
||||
{{ render_pkggrid(modname.packages.filter_by(type="GAME", state="APPROVED").all()) }}
|
||||
|
||||
{% if similar_topics %}
|
||||
<h3>{{ _("Forum Topics") }}</h3>
|
|
@ -13,8 +13,8 @@
|
|||
<p class="text-muted">
|
||||
{{ _("This package contains modnames that are present in the following packages:") }}
|
||||
</p>
|
||||
{% for metapackage, packages in packages_modnames.items() %}
|
||||
<h4>{{ metapackage.name }}</h4>
|
||||
{% for modname, packages in packages_modnames.items() %}
|
||||
<h4>{{ modname.name }}</h4>
|
||||
<ul>
|
||||
{% for pkg in packages %}
|
||||
<li>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{{ _("Add a screenshot") }} - {{ package.title }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ _("Add a screenshot") }}</h1>
|
||||
|
||||
{% from "macros/forms.html" import render_field, render_submit_field %}
|
||||
<form method="POST" action="" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{{ render_field(form.title) }}
|
||||
{{ render_field(form.file_upload, fieldclass="form-control-file", accept="image/png,image/jpeg") }}
|
||||
{{ render_checkbox_field
|
||||
{{ render_submit_field(form.submit) }}
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -398,7 +398,7 @@
|
|||
</a>
|
||||
{% elif dep.meta_package %}
|
||||
<a class="badge badge-primary"
|
||||
href="{{ url_for('metapackages.view', name=dep.meta_package.name) }}">
|
||||
href="{{ url_for('modnames.view', name=dep.meta_package.name) }}">
|
||||
{{ dep.meta_package.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
|
@ -421,7 +421,7 @@
|
|||
title=dep.package.title, display_name=dep.package.author.display_name) }}
|
||||
{% elif dep.meta_package %}
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ url_for('metapackages.view', name=dep.meta_package.name) }}">
|
||||
href="{{ url_for('modnames.view', name=dep.meta_package.name) }}">
|
||||
{{ dep.meta_package.name }}
|
||||
{% else %}
|
||||
{{ "Expected package or meta_package in dep!" | throw }}
|
||||
|
@ -507,7 +507,7 @@
|
|||
<dt>{{ _("Provides") }}</dt>
|
||||
<dd>{% for meta in package.provides %}
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ url_for('metapackages.view', name=meta.name) }}">{{ meta.name }}</a>
|
||||
href="{{ url_for('modnames.view', name=meta.name) }}">{{ meta.name }}</a>
|
||||
{% endfor %}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
|
|
|
@ -148,10 +148,10 @@
|
|||
</h2>
|
||||
|
||||
<p>
|
||||
{{ _("Meta packages that have hard dependers, but no packages providing them.") }}
|
||||
{{ _("Mod names that have hard dependers, but no packages providing them.") }}
|
||||
</p>
|
||||
|
||||
<a class="btn btn-primary" href="{{ url_for('todo.metapackages') }}">
|
||||
<a class="btn btn-primary" href="{{ url_for('todo.modnames') }}">
|
||||
{{ _("View") }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Unfulfilled Meta Packages
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Unfulfilled Meta Packages</h1>
|
||||
|
||||
<p>
|
||||
Meta packages that have hard dependers, but are not fulfilled.
|
||||
</p>
|
||||
|
||||
<div class="list-group">
|
||||
{% for meta in mpackages %}
|
||||
<a class="list-group-item list-group-item-action"
|
||||
href="{{ url_for('metapackages.view', name=meta.name) }}">
|
||||
{{ meta.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
<li><i>No meta packages found.</i></li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,24 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Unfulfilled Mod Names
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Unfulfilled Mod Names</h1>
|
||||
|
||||
<p>
|
||||
Mod names that have hard dependers, but are not fulfilled.
|
||||
</p>
|
||||
|
||||
<div class="list-group">
|
||||
{% for meta in modnames %}
|
||||
<a class="list-group-item list-group-item-action"
|
||||
href="{{ url_for('modnames.view', name=meta.name) }}">
|
||||
{{ meta.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
<li><i>No mod names found.</i></li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -59,7 +59,7 @@ def post_login(user: User, next_url):
|
|||
|
||||
def login_user_set_active(user: User, next_url: str = None, *args, **kwargs):
|
||||
if user.rank == UserRank.NOT_JOINED and user.email is None:
|
||||
user.rank = UserRank.MEMBER
|
||||
user.rank = UserRank.NEW_MEMBER
|
||||
user.notification_preferences = UserNotificationPreferences(user)
|
||||
user.is_active = True
|
||||
db.session.commit()
|
||||
|
|
Loading…
Reference in New Issue