diff --git a/app/__init__.py b/app/__init__.py
index 11d7cca..506ed27 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -1,13 +1,12 @@
from flask import *
from flask_user import *
import flask_menu as menu
+from flask_mail import Mail
from flask.ext import markdown
from flask_github import GitHub
from flask_wtf.csrf import CsrfProtect
import os
-
-
app = Flask(__name__)
app.config.from_pyfile(os.environ["FLASK_CONFIG"])
@@ -15,6 +14,7 @@ menu.Menu(app=app)
markdown.Markdown(app, extensions=["fenced_code"], safe_mode=True, output_format="html5")
github = GitHub(app)
csrf = CsrfProtect(app)
+mail = Mail(app)
from . import models, tasks
from .views import *
diff --git a/app/models.py b/app/models.py
index e0bcb2e..eaa44e8 100644
--- a/app/models.py
+++ b/app/models.py
@@ -49,6 +49,7 @@ class Permission(enum.Enum):
APPROVE_NEW = "APPROVE_NEW"
CHANGE_RELEASE_URL = "CHANGE_RELEASE_URL"
CHANGE_RANK = "CHANGE_RANK"
+ CHANGE_EMAIL = "CHANGE_EMAIL"
EDIT_EDITREQUEST = "EDIT_EDITREQUEST"
# Only return true if the permission is valid for *all* contexts
@@ -119,9 +120,17 @@ class User(db.Model, UserMixin):
return user.rank.atLeast(UserRank.EDITOR)
elif perm == Permission.CHANGE_RANK:
return user.rank.atLeast(UserRank.MODERATOR)
+ elif perm == Permission.CHANGE_EMAIL:
+ return user == self or user.rank.atLeast(UserRank.MODERATOR)
else:
raise Exception("Permission {} is not related to users".format(perm.name))
+class UserEmailVerification(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
+ email = db.Column(db.String(100))
+ token = db.Column(db.String(32))
+ user = db.relationship("User", foreign_keys=[user_id])
class Notification(db.Model):
id = db.Column(db.Integer, primary_key=True)
diff --git a/app/tasks/__init__.py b/app/tasks/__init__.py
index 9ee293e..40a824c 100644
--- a/app/tasks/__init__.py
+++ b/app/tasks/__init__.py
@@ -41,4 +41,4 @@ def make_celery(app):
celery = make_celery(app)
-from . import importtasks, forumtasks
+from . import importtasks, forumtasks, emails
diff --git a/app/tasks/emails.py b/app/tasks/emails.py
new file mode 100644
index 0000000..7040d38
--- /dev/null
+++ b/app/tasks/emails.py
@@ -0,0 +1,11 @@
+from flask import *
+from flask_mail import Message
+from app import mail
+from app.tasks import celery
+
+@celery.task()
+def sendVerifyEmail(newEmail, token):
+ msg = Message("Verify email address", recipients=[newEmail])
+ msg.body = "This is a verification email!"
+ msg.html = render_template("emails/verify.html", token=token)
+ mail.send(msg)
diff --git a/app/templates/emails/verify.html b/app/templates/emails/verify.html
new file mode 100644
index 0000000..a08b174
--- /dev/null
+++ b/app/templates/emails/verify.html
@@ -0,0 +1,17 @@
+
Hello!
+
+
+ This email has been sent to you because someone (hopefully you)
+ has entered your email address as a user's email.
+
+
+
+ If this was you, then please click this link to verify the address:
+
+ {{ url_for('verify_email_page', token=token, _external=True) }}
+
+
+
+
+ If it wasn't you, then just delete this email.
+
diff --git a/app/templates/users/user_profile_page.html b/app/templates/users/user_profile_page.html
index 7e280e1..2d429f1 100644
--- a/app/templates/users/user_profile_page.html
+++ b/app/templates/users/user_profile_page.html
@@ -42,24 +42,6 @@
{% endif %}
- {% if user == current_user %}
-
- Email: |
-
- {{ user.email }} |
- {% if user.email %}change{% else %}add{% endif %}
- 🔒
- |
-
-
- Password: |
-
-
- {% if user.password %}Change password{% else %}Add password{% endif %}
- 🔒
- |
-
- {% endif %}
@@ -90,10 +72,14 @@
{{ form.hidden_tag() }}
- {{ render_field(form.display_name, tabindex=240) }}
+ {{ render_field(form.display_name, tabindex=230) }}
+
+ {% if user.checkPerm(current_user, "CHANGE_EMAIL") %}
+ {{ render_field(form.email, tabindex=240) }}
+ {% endif %}
{% if user.checkPerm(current_user, "CHANGE_RANK") %}
- {{ render_field(form.rank, tabindex=240) }}
+ {{ render_field(form.rank, tabindex=250) }}
{% endif %}
{{ render_submit_field(form.submit, tabindex=280) }}
diff --git a/app/views/users.py b/app/views/users.py
index e7306c1..a4fb90b 100644
--- a/app/views/users.py
+++ b/app/views/users.py
@@ -10,10 +10,12 @@ from wtforms import *
from wtforms.validators import *
from .utils import rank_required, randomString
from app.tasks.forumtasks import checkForumAccount
+from app.tasks.emails import sendVerifyEmail
# Define the User profile form
class UserProfileForm(FlaskForm):
- display_name = StringField("Display name")
+ display_name = StringField("Display name", [InputRequired(), Length(2, 20)])
+ email = StringField("Email")
rank = SelectField("Rank", [InputRequired()], choices=UserRank.choices(), coerce=UserRank.coerce, default=UserRank.NEW_MEMBER)
submit = SubmitField("Save")
@@ -48,6 +50,21 @@ def user_profile_page(username):
else:
flash("Can't promote a user to a rank higher than yourself!", "error")
+ if user.checkPerm(current_user, Permission.CHANGE_EMAIL):
+ newEmail = form["email"].data
+ if newEmail != user.email:
+ token = randomString(32)
+
+ 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("check_task", id=task.id, r=url_for("user_profile_page", username=username)))
+
# Save user_profile
db.session.commit()
@@ -96,3 +113,19 @@ def user_claim_page():
flash("Unknown claim type", "error")
return render_template("users/claim.html", username=username, key=randomString(32))
+
+@app.route("/users/verify/")
+def verify_email_page():
+ token = request.args.get("token")
+ ver = UserEmailVerification.query.filter_by(token=token).first()
+ if ver is None:
+ flash("Unknown verification token!", "error")
+ else:
+ ver.user.email = ver.email
+ db.session.delete(ver)
+ db.session.commit()
+
+ if current_user.is_authenticated:
+ return redirect(url_for("user_profile_page", username=current_user.username))
+ else:
+ return redirect(url_for("home_page"))
diff --git a/config.example.cfg b/config.example.cfg
index 925da5c..53313c6 100644
--- a/config.example.cfg
+++ b/config.example.cfg
@@ -1,4 +1,6 @@
USER_APP_NAME="Content DB"
+SERVER_NAME="content.minetest.net"
+BASE_URL="http://" + SERVER_NAME
SECRET_KEY=""
WTF_CSRF_SECRET_KEY=""
@@ -8,9 +10,17 @@ SQLALCHEMY_DATABASE_URI = "sqlite:///../db.sqlite"
GITHUB_CLIENT_ID = ""
GITHUB_CLIENT_SECRET = ""
-BASE_URL="http://localhost:5000/"
+CELERY_BROKER_URL='redis://localhost:6379'
+CELERY_RESULT_BACKEND='redis://localhost:6379'
UPLOAD_FOLDER="tmp"
USER_ENABLE_REGISTER = False
USER_ENABLE_CHANGE_USERNAME = False
+s
+MAIL_USERNAME=""
+MAIL_PASSWORD=""
+MAIL_DEFAULT_SENDER=""
+MAIL_SERVER=""
+MAIL_PORT=587
+MAIL_USE_TLS=True