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

View File

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

View File

@ -15,8 +15,9 @@ def urlEncodeNonAscii(b):
class Profile: class Profile:
def __init__(self, username): def __init__(self, username):
self.username = username self.username = username
self.signature = "" self.signature = ""
self.avatar = None
self.properties = {} self.properties = {}
def set(self, key, value): def set(self, key, value):
@ -33,6 +34,11 @@ def __extract_properties(profile, soup):
if el is None: if el is None:
return 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") res = el.find_all("dl", class_ = "left-box details")
if len(res) != 1: if len(res) != 1:
return None return None

View File

@ -21,11 +21,13 @@
<h2 class="card-header">{{ user.display_name }}</h2> <h2 class="card-header">{{ user.display_name }}</h2>
<div class="card-body row"> <div class="card-body row">
<div class="col-md-2"> <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/"> <a href="https://en.gravatar.com/">
{% endif %} {% endif %}
<img class="img-responsive user-photo img-thumbnail img-thumbnail-1" src="{{ (user.email or '') | gravatar }}"> <img class="img-responsive user-photo img-thumbnail img-thumbnail-1" src="{{ user.getProfilePicURL() }}">
{% if user.email %} {% if user.forums_username or user.email %}
</a> </a>
{% endif %} {% endif %}
</div> </div>
@ -67,14 +69,23 @@
<tr> <tr>
<td>Profile Picture:</td> <td>Profile Picture:</td>
<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 %} {% if user.email %}
<a class="btn btn-primary" href="https://en.gravatar.com/"> <a class="btn btn-primary" href="https://en.gravatar.com/">
Gravatar Gravatar
</a> </a>
{% else %} {% else %}
<p> <a class="btn btn-primary disabled"
Please add an email to your profile. data-toggle="tooltip" data-placement="bottom"
</p> title="Please add an email address to use Gravatar"
style="pointer-events: all;">
Gravatar
</a>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@ -106,6 +106,26 @@ def user_profile_page(username):
return render_template("users/user_profile_page.html", return render_template("users/user_profile_page.html",
user=user, form=form, packages=packages, topics_to_add=topics_to_add) 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): class SetPasswordForm(FlaskForm):
email = StringField("Email", [Optional(), Email()]) email = StringField("Email", [Optional(), Email()])
password = PasswordField("New password", [InputRequired(), Length(2, 20)]) 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 ###