mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-23 06:22:24 +01:00
Add email to email tab, merge settings into settings.py file
This commit is contained in:
parent
d976269f1a
commit
c46430c663
@ -25,7 +25,7 @@ from wtforms.validators import *
|
|||||||
from app.models import db, User, APIToken, Package, Permission
|
from app.models import db, User, APIToken, Package, Permission
|
||||||
from app.utils import randomString
|
from app.utils import randomString
|
||||||
from . import bp
|
from . import bp
|
||||||
from ..users.profile import get_setting_tabs
|
from ..users.settings import get_setting_tabs
|
||||||
|
|
||||||
|
|
||||||
class CreateAPIToken(FlaskForm):
|
class CreateAPIToken(FlaskForm):
|
||||||
|
@ -17,10 +17,7 @@
|
|||||||
|
|
||||||
from flask import Blueprint, render_template, redirect, url_for
|
from flask import Blueprint, render_template, redirect, url_for
|
||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
from flask_wtf import FlaskForm
|
from app.models import db, Notification
|
||||||
from wtforms import BooleanField, SubmitField
|
|
||||||
from app.blueprints.users.profile import get_setting_tabs
|
|
||||||
from app.models import db, Notification, UserNotificationPreferences, NotificationType
|
|
||||||
|
|
||||||
bp = Blueprint("notifications", __name__)
|
bp = Blueprint("notifications", __name__)
|
||||||
|
|
||||||
@ -37,45 +34,3 @@ def clear():
|
|||||||
Notification.query.filter_by(user=current_user).delete()
|
Notification.query.filter_by(user=current_user).delete()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect(url_for("notifications.list_all"))
|
return redirect(url_for("notifications.list_all"))
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/notifications/settings/", methods=["GET", "POST"])
|
|
||||||
@login_required
|
|
||||||
def settings():
|
|
||||||
is_new = False
|
|
||||||
prefs = current_user.notification_preferences
|
|
||||||
if prefs is None:
|
|
||||||
is_new = True
|
|
||||||
prefs = UserNotificationPreferences(current_user)
|
|
||||||
|
|
||||||
attrs = {
|
|
||||||
"submit": SubmitField("Save")
|
|
||||||
}
|
|
||||||
|
|
||||||
data = {}
|
|
||||||
types = []
|
|
||||||
for notificationType in NotificationType:
|
|
||||||
key = "pref_" + notificationType.toName()
|
|
||||||
types.append(notificationType)
|
|
||||||
attrs[key] = BooleanField("")
|
|
||||||
data[key] = getattr(prefs, key) == 2
|
|
||||||
|
|
||||||
SettingsForm = type("SettingsForm", (FlaskForm,), attrs)
|
|
||||||
|
|
||||||
form = SettingsForm(data=data)
|
|
||||||
if form.validate_on_submit():
|
|
||||||
for notificationType in NotificationType:
|
|
||||||
key = "pref_" + notificationType.toName()
|
|
||||||
field = getattr(form, key)
|
|
||||||
value = 2 if field.data else 0
|
|
||||||
setattr(prefs, key, value)
|
|
||||||
|
|
||||||
if is_new:
|
|
||||||
db.session.add(prefs)
|
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
return redirect(url_for("notifications.settings"))
|
|
||||||
|
|
||||||
return render_template("notifications/settings.html",
|
|
||||||
form=form, user=current_user, types=types, is_new=is_new,
|
|
||||||
tabs=get_setting_tabs(current_user), current_tab="notifications")
|
|
||||||
|
@ -2,4 +2,4 @@ from flask import Blueprint
|
|||||||
|
|
||||||
bp = Blueprint("users", __name__)
|
bp = Blueprint("users", __name__)
|
||||||
|
|
||||||
from . import profile, claim, account
|
from . import profile, claim, account, settings
|
||||||
|
@ -24,24 +24,12 @@ from wtforms.validators import *
|
|||||||
|
|
||||||
from app.markdown import render_markdown
|
from app.markdown import render_markdown
|
||||||
from app.models import *
|
from app.models import *
|
||||||
from app.tasks.emails import sendVerifyEmail, sendEmailRaw
|
from app.tasks.emails import sendEmailRaw
|
||||||
from app.tasks.forumtasks import checkForumAccount
|
from app.tasks.forumtasks import checkForumAccount
|
||||||
from app.utils import randomString, rank_required, nonEmptyOrNone, addAuditLog, make_flask_login_password
|
from app.utils import rank_required, addAuditLog
|
||||||
from . import bp
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
# Define the User profile form
|
|
||||||
class UserProfileForm(FlaskForm):
|
|
||||||
display_name = StringField("Display name", [Optional(), Length(2, 100)])
|
|
||||||
forums_username = StringField("Forums Username", [Optional(), Length(2, 50)])
|
|
||||||
github_username = StringField("GitHub Username", [Optional(), Length(2, 50)])
|
|
||||||
email = StringField("Email", [Optional(), Email()], filters = [lambda x: x or None])
|
|
||||||
website_url = StringField("Website URL", [Optional(), URL()], filters = [lambda x: x or None])
|
|
||||||
donate_url = StringField("Donation URL", [Optional(), URL()], filters = [lambda x: x or None])
|
|
||||||
rank = SelectField("Rank", [Optional()], choices=UserRank.choices(), coerce=UserRank.coerce, default=UserRank.NEW_MEMBER)
|
|
||||||
submit = SubmitField("Save")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/users/", methods=["GET"])
|
@bp.route("/users/", methods=["GET"])
|
||||||
def list_all():
|
def list_all():
|
||||||
users = db.session.query(User, func.count(Package.id)) \
|
users = db.session.query(User, func.count(Package.id)) \
|
||||||
@ -76,93 +64,6 @@ def profile(username):
|
|||||||
user=user, packages=packages, topics_to_add=topics_to_add)
|
user=user, packages=packages, topics_to_add=topics_to_add)
|
||||||
|
|
||||||
|
|
||||||
def get_setting_tabs(user):
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
"id": "edit_profile",
|
|
||||||
"title": "Edit Profile",
|
|
||||||
"url": url_for("users.profile_edit", username=user.username)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "notifications",
|
|
||||||
"title": "Emails and Notifications",
|
|
||||||
"url": url_for("notifications.settings")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "api_tokens",
|
|
||||||
"title": "API Tokens",
|
|
||||||
"url": url_for("api.list_tokens", username=user.username)
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/users/<username>/edit/", methods=["GET", "POST"])
|
|
||||||
@login_required
|
|
||||||
def profile_edit(username):
|
|
||||||
user : User = User.query.filter_by(username=username).first()
|
|
||||||
if not user:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
if not user.can_see_edit_profile(current_user):
|
|
||||||
flash("Permission denied", "danger")
|
|
||||||
return redirect(url_for("users.profile", username=username))
|
|
||||||
|
|
||||||
|
|
||||||
form = UserProfileForm(formdata=request.form, obj=user)
|
|
||||||
|
|
||||||
# Process valid POST
|
|
||||||
if request.method=="POST" and form.validate():
|
|
||||||
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
|
||||||
addAuditLog(severity, current_user, "Edited {}'s profile".format(user.display_name),
|
|
||||||
url_for("users.profile", username=username))
|
|
||||||
|
|
||||||
# Copy form fields to user_profile fields
|
|
||||||
if user.checkPerm(current_user, Permission.CHANGE_USERNAMES):
|
|
||||||
user.display_name = form.display_name.data
|
|
||||||
user.forums_username = nonEmptyOrNone(form.forums_username.data)
|
|
||||||
user.github_username = nonEmptyOrNone(form.github_username.data)
|
|
||||||
|
|
||||||
if user.checkPerm(current_user, Permission.CHANGE_PROFILE_URLS):
|
|
||||||
user.website_url = form["website_url"].data
|
|
||||||
user.donate_url = form["donate_url"].data
|
|
||||||
|
|
||||||
if user.checkPerm(current_user, Permission.CHANGE_RANK):
|
|
||||||
newRank = form["rank"].data
|
|
||||||
if current_user.rank.atLeast(newRank):
|
|
||||||
if newRank != user.rank:
|
|
||||||
user.rank = form["rank"].data
|
|
||||||
msg = "Set rank of {} to {}".format(user.display_name, user.rank.getTitle())
|
|
||||||
addAuditLog(AuditSeverity.MODERATION, current_user, msg, url_for("users.profile", username=username))
|
|
||||||
else:
|
|
||||||
flash("Can't promote a user to a rank higher than yourself!", "danger")
|
|
||||||
|
|
||||||
if user.checkPerm(current_user, Permission.CHANGE_EMAIL):
|
|
||||||
newEmail = form["email"].data
|
|
||||||
if newEmail and newEmail != user.email and newEmail.strip() != "":
|
|
||||||
token = randomString(32)
|
|
||||||
|
|
||||||
msg = "Changed email of {}".format(user.display_name)
|
|
||||||
addAuditLog(severity, current_user, msg, url_for("users.profile", username=username))
|
|
||||||
|
|
||||||
ver = UserEmailVerification()
|
|
||||||
ver.user = user
|
|
||||||
ver.token = token
|
|
||||||
ver.email = newEmail
|
|
||||||
db.session.add(ver)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
task = sendVerifyEmail.delay(newEmail, token)
|
|
||||||
return redirect(url_for("tasks.check", id=task.id, r=url_for("users.profile", username=username)))
|
|
||||||
|
|
||||||
# Save user_profile
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return redirect(url_for("users.profile", username=username))
|
|
||||||
|
|
||||||
# Process GET or invalid POST
|
|
||||||
return render_template("users/profile_edit.html", user=user, form=form, tabs=get_setting_tabs(user), current_tab="edit_profile")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/users/<username>/check/", methods=["POST"])
|
@bp.route("/users/<username>/check/", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def user_check(username):
|
def user_check(username):
|
||||||
@ -188,7 +89,7 @@ class SendEmailForm(FlaskForm):
|
|||||||
submit = SubmitField("Send")
|
submit = SubmitField("Send")
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/users/<username>/email/", methods=["GET", "POST"])
|
@bp.route("/users/<username>/send-email/", methods=["GET", "POST"])
|
||||||
@rank_required(UserRank.MODERATOR)
|
@rank_required(UserRank.MODERATOR)
|
||||||
def send_email(username):
|
def send_email(username):
|
||||||
user = User.query.filter_by(username=username).first()
|
user = User.query.filter_by(username=username).first()
|
||||||
|
165
app/blueprints/users/settings.py
Normal file
165
app/blueprints/users/settings.py
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
from flask import *
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import *
|
||||||
|
from wtforms.validators import *
|
||||||
|
|
||||||
|
from app.models import *
|
||||||
|
from app.utils import nonEmptyOrNone, addAuditLog, randomString
|
||||||
|
from app.tasks.emails import sendVerifyEmail
|
||||||
|
from . import bp
|
||||||
|
|
||||||
|
|
||||||
|
def get_setting_tabs(user):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"id": "edit_profile",
|
||||||
|
"title": "Edit Profile",
|
||||||
|
"url": url_for("users.profile_edit", username=user.username)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "notifications",
|
||||||
|
"title": "Email and Notifications",
|
||||||
|
"url": url_for("users.email_notifications", username=user.username)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "api_tokens",
|
||||||
|
"title": "API Tokens",
|
||||||
|
"url": url_for("api.list_tokens", username=user.username)
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Define the User profile form
|
||||||
|
class UserProfileForm(FlaskForm):
|
||||||
|
display_name = StringField("Display name", [Optional(), Length(2, 100)])
|
||||||
|
forums_username = StringField("Forums Username", [Optional(), Length(2, 50)])
|
||||||
|
github_username = StringField("GitHub Username", [Optional(), Length(2, 50)])
|
||||||
|
website_url = StringField("Website URL", [Optional(), URL()], filters = [lambda x: x or None])
|
||||||
|
donate_url = StringField("Donation URL", [Optional(), URL()], filters = [lambda x: x or None])
|
||||||
|
rank = SelectField("Rank", [Optional()], choices=UserRank.choices(), coerce=UserRank.coerce, default=UserRank.NEW_MEMBER)
|
||||||
|
submit = SubmitField("Save")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/users/<username>/settings/profile/", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def profile_edit(username):
|
||||||
|
user : User = User.query.filter_by(username=username).first()
|
||||||
|
if not user:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
if not user.can_see_edit_profile(current_user):
|
||||||
|
flash("Permission denied", "danger")
|
||||||
|
return redirect(url_for("users.profile", username=username))
|
||||||
|
|
||||||
|
|
||||||
|
form = UserProfileForm(formdata=request.form, obj=user)
|
||||||
|
|
||||||
|
# Process valid POST
|
||||||
|
if request.method=="POST" and form.validate():
|
||||||
|
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
||||||
|
addAuditLog(severity, current_user, "Edited {}'s profile".format(user.display_name),
|
||||||
|
url_for("users.profile", username=username))
|
||||||
|
|
||||||
|
# Copy form fields to user_profile fields
|
||||||
|
if user.checkPerm(current_user, Permission.CHANGE_USERNAMES):
|
||||||
|
user.display_name = form.display_name.data
|
||||||
|
user.forums_username = nonEmptyOrNone(form.forums_username.data)
|
||||||
|
user.github_username = nonEmptyOrNone(form.github_username.data)
|
||||||
|
|
||||||
|
if user.checkPerm(current_user, Permission.CHANGE_PROFILE_URLS):
|
||||||
|
user.website_url = form["website_url"].data
|
||||||
|
user.donate_url = form["donate_url"].data
|
||||||
|
|
||||||
|
if user.checkPerm(current_user, Permission.CHANGE_RANK):
|
||||||
|
newRank = form["rank"].data
|
||||||
|
if current_user.rank.atLeast(newRank):
|
||||||
|
if newRank != user.rank:
|
||||||
|
user.rank = form["rank"].data
|
||||||
|
msg = "Set rank of {} to {}".format(user.display_name, user.rank.getTitle())
|
||||||
|
addAuditLog(AuditSeverity.MODERATION, current_user, msg, url_for("users.profile", username=username))
|
||||||
|
else:
|
||||||
|
flash("Can't promote a user to a rank higher than yourself!", "danger")
|
||||||
|
|
||||||
|
# Save user_profile
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return redirect(url_for("users.profile", username=username))
|
||||||
|
|
||||||
|
# Process GET or invalid POST
|
||||||
|
return render_template("users/profile_edit.html", user=user, form=form, tabs=get_setting_tabs(user), current_tab="edit_profile")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def make_settings_form():
|
||||||
|
attrs = {
|
||||||
|
"email": StringField("Email", [Optional(), Email()]),
|
||||||
|
"submit": SubmitField("Save")
|
||||||
|
}
|
||||||
|
|
||||||
|
for notificationType in NotificationType:
|
||||||
|
key = "pref_" + notificationType.toName()
|
||||||
|
attrs[key] = BooleanField("")
|
||||||
|
|
||||||
|
return type("SettingsForm", (FlaskForm,), attrs)
|
||||||
|
|
||||||
|
SettingsForm = make_settings_form()
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/users/<username>/settings/email/", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def email_notifications(username):
|
||||||
|
user: User = User.query.filter_by(username=username).first()
|
||||||
|
if not user:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
is_new = False
|
||||||
|
prefs = user.notification_preferences
|
||||||
|
if prefs is None:
|
||||||
|
is_new = True
|
||||||
|
prefs = UserNotificationPreferences(user)
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
types = []
|
||||||
|
for notificationType in NotificationType:
|
||||||
|
types.append(notificationType)
|
||||||
|
data["pref_" + notificationType.toName()] = prefs.get_can_email(notificationType)
|
||||||
|
|
||||||
|
data["email"] = user.email
|
||||||
|
|
||||||
|
form = SettingsForm(data=data)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
for notificationType in NotificationType:
|
||||||
|
field = getattr(form, "pref_" + notificationType.toName())
|
||||||
|
prefs.set_can_email(notificationType, field.data)
|
||||||
|
|
||||||
|
if is_new:
|
||||||
|
db.session.add(prefs)
|
||||||
|
|
||||||
|
if user.checkPerm(current_user, Permission.CHANGE_EMAIL):
|
||||||
|
newEmail = form.email.data
|
||||||
|
if newEmail and newEmail != user.email and newEmail.strip() != "":
|
||||||
|
token = randomString(32)
|
||||||
|
|
||||||
|
severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION
|
||||||
|
|
||||||
|
msg = "Changed email of {}".format(user.display_name)
|
||||||
|
addAuditLog(severity, current_user, msg, url_for("users.profile", username=username))
|
||||||
|
|
||||||
|
ver = UserEmailVerification()
|
||||||
|
ver.user = user
|
||||||
|
ver.token = token
|
||||||
|
ver.email = newEmail
|
||||||
|
db.session.add(ver)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
task = sendVerifyEmail.delay(newEmail, token)
|
||||||
|
return redirect(url_for("tasks.check", id=task.id, r=url_for("users.profile", username=username)))
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for("notifications.settings"))
|
||||||
|
|
||||||
|
return render_template("users/settings_email.html",
|
||||||
|
form=form, user=user, types=types, is_new=is_new,
|
||||||
|
tabs=get_setting_tabs(current_user), current_tab="notifications")
|
@ -414,6 +414,13 @@ class UserNotificationPreferences(db.Model):
|
|||||||
self.pref_editor_misc = 0
|
self.pref_editor_misc = 0
|
||||||
self.pref_other = 0
|
self.pref_other = 0
|
||||||
|
|
||||||
|
def get_can_email(self, type):
|
||||||
|
return getattr(self, "pref_" + type.toName()) == 2
|
||||||
|
|
||||||
|
def set_can_email(self, type, value):
|
||||||
|
value = 2 if value else 0
|
||||||
|
setattr(self, "pref_" + type.toName(), value)
|
||||||
|
|
||||||
|
|
||||||
class License(db.Model):
|
class License(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
@ -139,11 +139,6 @@
|
|||||||
{{ render_field(form.donate_url, tabindex=233) }}
|
{{ render_field(form.donate_url, tabindex=233) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if user.checkPerm(current_user, "CHANGE_EMAIL") %}
|
|
||||||
{{ render_field(form.email, tabindex=240) }}
|
|
||||||
<i>We'll send you an email to verify it if changed.</i>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if user.checkPerm(current_user, "CHANGE_RANK") %}
|
{% if user.checkPerm(current_user, "CHANGE_RANK") %}
|
||||||
{{ render_field(form.rank, tabindex=250) }}
|
{{ render_field(form.rank, tabindex=250) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -7,16 +7,29 @@
|
|||||||
{% block pane %}
|
{% block pane %}
|
||||||
<h2 class="mt-0">{{ _("Email and Notifications") }}</h2>
|
<h2 class="mt-0">{{ _("Email and Notifications") }}</h2>
|
||||||
|
|
||||||
{% if is_new %}
|
|
||||||
<p class="alert alert-info">
|
|
||||||
{{ _("Email notifications are currently turned off. Click 'Save' to apply recommended settings.") }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% from "macros/forms.html" import render_field, render_submit_field, render_checkbox_field %}
|
{% from "macros/forms.html" import render_field, render_submit_field, render_checkbox_field %}
|
||||||
<form action="" method="POST" class="form" role="form">
|
<form action="" method="POST" class="form" role="form">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
|
|
||||||
|
<h3>Email Address</h3>
|
||||||
|
|
||||||
|
{{ render_field(form.email, tabindex=100) }}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Your email is needed to recover your account if you forget your
|
||||||
|
password, and to optionally send notifications.
|
||||||
|
Your email will never be shared to a third-party.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Notification Settings</h3>
|
||||||
|
|
||||||
|
{% if is_new %}
|
||||||
|
<p class="alert alert-info">
|
||||||
|
{{ _("Email notifications are currently turned off. Click 'Save' to apply recommended settings.") }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Event</th>
|
<th>Event</th>
|
||||||
@ -32,7 +45,8 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<p class="mt-5">
|
||||||
{{ render_submit_field(form.submit, tabindex=280) }}
|
{{ render_submit_field(form.submit, tabindex=280) }}
|
||||||
|
</p>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user