Add support for using forum profile pictures

This commit is contained in:
rubenwardy 2018-12-25 19:28:32 +00:00
parent f94885a58f
commit 21960f2404
6 changed files with 94 additions and 18 deletions

View File

@ -19,7 +19,7 @@ from flask import Flask, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from urllib.parse import urlparse
from app import app
from app import app, gravatar
from datetime import datetime
from sqlalchemy.orm import validates
from flask_user import login_required, UserManager, UserMixin, SQLAlchemyAdapter
@ -97,26 +97,27 @@ class Permission(enum.Enum):
raise Exception("Non-global permission checked globally. Use Package.checkPerm or User.checkPerm instead.")
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
id = db.Column(db.Integer, primary_key=True)
# User authentication information
username = db.Column(db.String(50, collation="NOCASE"), nullable=False, unique=True, index=True)
password = db.Column(db.String(255), nullable=True)
username = db.Column(db.String(50, collation="NOCASE"), nullable=False, unique=True, index=True)
password = db.Column(db.String(255), nullable=True)
reset_password_token = db.Column(db.String(100), nullable=False, server_default="")
rank = db.Column(db.Enum(UserRank))
rank = db.Column(db.Enum(UserRank))
# Account linking
github_username = db.Column(db.String(50, collation="NOCASE"), nullable=True, unique=True)
forums_username = db.Column(db.String(50, collation="NOCASE"), nullable=True, unique=True)
# User email information
email = db.Column(db.String(255), nullable=True, unique=True)
confirmed_at = db.Column(db.DateTime())
email = db.Column(db.String(255), nullable=True, unique=True)
confirmed_at = db.Column(db.DateTime())
# User information
active = db.Column("is_active", db.Boolean, nullable=False, server_default="0")
display_name = db.Column(db.String(100), nullable=False, server_default="")
profile_pic = db.Column(db.String(255), nullable=True, server_default=None)
active = db.Column("is_active", db.Boolean, nullable=False, server_default="0")
display_name = db.Column(db.String(100), nullable=False, server_default="")
# Content
notifications = db.relationship("Notification", primaryjoin="User.id==Notification.user_id")
@ -146,6 +147,12 @@ class User(db.Model, UserMixin):
def isClaimed(self):
return self.rank.atLeast(UserRank.NEW_MEMBER)
def getProfilePicURL(self):
if self.profile_pic:
return self.profile_pic
else:
return gravatar(self.email or "")
def checkPerm(self, user, perm):
if not user.is_authenticated:
return False

View File

@ -25,7 +25,7 @@ import urllib.request
from urllib.parse import urlparse, quote_plus
@celery.task()
def checkForumAccount(username, token=None):
def checkForumAccount(username):
try:
profile = getProfile("https://forum.minetest.net", username)
except OSError:
@ -47,6 +47,10 @@ def checkForumAccount(username, token=None):
user.github_username = github_username
needsSaving = True
pic = profile.avatar
needsSaving = needsSaving or pic != user.profile_pic
user.profile_pic = pic
# Save
if needsSaving:
db.session.commit()

View File

@ -15,8 +15,9 @@ def urlEncodeNonAscii(b):
class Profile:
def __init__(self, username):
self.username = username
self.signature = ""
self.username = username
self.signature = ""
self.avatar = None
self.properties = {}
def set(self, key, value):
@ -33,6 +34,11 @@ def __extract_properties(profile, soup):
if el is None:
return None
res1 = el.find_all("dl")
imgs = res1[0].find_all("img")
if len(imgs) == 1:
profile.avatar = imgs[0]["src"]
res = el.find_all("dl", class_ = "left-box details")
if len(res) != 1:
return None

View File

@ -21,11 +21,13 @@
<h2 class="card-header">{{ user.display_name }}</h2>
<div class="card-body row">
<div class="col-md-2">
{% if user.email %}
{% if user.forums_username %}
<a href="https://forum.minetest.net/ucp.php?i=profile&mode=avatar">
{% elif user.email %}
<a href="https://en.gravatar.com/">
{% endif %}
<img class="img-responsive user-photo img-thumbnail img-thumbnail-1" src="{{ (user.email or '') | gravatar }}">
{% if user.email %}
<img class="img-responsive user-photo img-thumbnail img-thumbnail-1" src="{{ user.getProfilePicURL() }}">
{% if user.forums_username or user.email %}
</a>
{% endif %}
</div>
@ -67,14 +69,23 @@
<tr>
<td>Profile Picture:</td>
<td>
{% if user.forums_username %}
<form method="post" action="{{ url_for('user_check', username=user.username) }}" class="" style="display:inline-block;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="submit" class="btn btn-primary" value="Sync with Forums" />
</form>
{% endif %}
{% if user.email %}
<a class="btn btn-primary" href="https://en.gravatar.com/">
Gravatar
</a>
{% else %}
<p>
Please add an email to your profile.
</p>
<a class="btn btn-primary disabled"
data-toggle="tooltip" data-placement="bottom"
title="Please add an email address to use Gravatar"
style="pointer-events: all;">
Gravatar
</a>
{% endif %}
</td>
</tr>

View File

@ -106,6 +106,26 @@ def user_profile_page(username):
return render_template("users/user_profile_page.html",
user=user, form=form, packages=packages, topics_to_add=topics_to_add)
@app.route("/users/<username>/check/", methods=["POST"])
@login_required
def user_check(username):
user = User.query.filter_by(username=username).first()
if user is None:
abort(404)
if current_user != user and not current_user.rank.atLeast(UserRank.MODERATOR):
abort(403)
if user.forums_username is None:
abort(404)
task = checkForumAccount.delay(user.forums_username)
next_url = url_for("user_profile_page", username=username)
return redirect(url_for("check_task", id=task.id, r=next_url))
class SetPasswordForm(FlaskForm):
email = StringField("Email", [Optional(), Email()])
password = PasswordField("New password", [InputRequired(), Length(2, 20)])

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: dce69ad1e4eb
Revises: a791b9b74a4c
Create Date: 2018-12-25 18:57:44.575501
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'dce69ad1e4eb'
down_revision = 'a791b9b74a4c'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("user", sa.Column('profile_pic', sa.String(length=255), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("user", "profile_pic")
# ### end Alembic commands ###