Adding profiles and adminship

This commit is contained in:
Yusur 2019-10-17 14:34:55 +02:00
parent 156d58e549
commit 32e7c37158
7 changed files with 133 additions and 16 deletions

View file

@ -2,6 +2,13 @@
## 0.6-dev ## 0.6-dev
* Added user adminship. Admins are users with very high privileges. Adminship can be assigned only at script level (not from the web).
* Now one's messages won't show up in public timeline.
* Added user profile info. Now you can specify your full name, biography, location, birth year, website, Facebook and Instagram. Of course this is totally optional.
* Added reference to terms of service and privacy policy on signup page.
* When visiting signup page as logged in, user should confirm he wants to create another account in order to do it.
* Moved user stats inside profile info.
## 0.5.0 ## 0.5.0
* Removed `type` and `info` fields from `Message` table and merged `privacy` field, previously into a separate table, into that table. In order to make the app work, when upgrading you should run the `migrate_0_4_to_0_5.py` script. * Removed `type` and `info` fields from `Message` table and merged `privacy` field, previously into a separate table, into that table. In order to make the app work, when upgrading you should run the `migrate_0_4_to_0_5.py` script.

57
app.py
View file

@ -93,6 +93,38 @@ class User(BaseModel):
.where( .where(
(Notification.target == self) & (Notification.seen == 0) (Notification.target == self) & (Notification.seen == 0)
)) ))
# user adminship is stored into a separate table; new in 0.6
@property
def is_admin(self):
return UserAdminship.select().where(UserAdminship.user == self).exists()
# user profile info; new in 0.6
@property
def profile(self):
# lazy initialization; I don't want (and don't know how)
# to do schema changes.
try:
return UserProfile.get(UserProfile.user == self)
except UserProfile.DoesNotExist:
return UserProfile.create(user=self, full_name=self.username)
# User adminship.
# A very high privilege where users can review posts.
# For very few users only; new in 0.6
class UserAdminship(BaseModel):
user = ForeignKeyField(User, primary_key=True)
# User profile.
# Additional info for identifying users.
# New in 0.6
class UserProfile(BaseModel):
user = ForeignKeyField(User, primary_key=True)
full_name = TextField()
biography = TextField(default='')
location = IntegerField(null=True)
year = IntegerField(null=True)
website = TextField(null=True)
instagram = TextField(null=True)
facebook = TextField(null=True)
# The message privacy values. # The message privacy values.
MSGPRV_PUBLIC = 0 # everyone MSGPRV_PUBLIC = 0 # everyone
@ -118,11 +150,11 @@ class Message(BaseModel):
privacy = self.privacy privacy = self.privacy
if user == cur_user: if user == cur_user:
# short path # short path
return True # also: don't show user's messages in public timeline
return not is_public_timeline
elif privacy == MSGPRV_PUBLIC: elif privacy == MSGPRV_PUBLIC:
return True return True
elif privacy == MSGPRV_UNLISTED: elif privacy == MSGPRV_UNLISTED:
# TODO user's posts may appear the same in public timeline,
# even if unlisted # even if unlisted
return not is_public_timeline return not is_public_timeline
elif privacy == MSGPRV_FRIENDS: elif privacy == MSGPRV_FRIENDS:
@ -172,7 +204,8 @@ class Notification(BaseModel):
def create_tables(): def create_tables():
with database: with database:
database.create_tables([ database.create_tables([
User, Message, Relationship, Upload, Notification]) User, UserAdminship, UserProfile, Message, Relationship,
Upload, Notification])
if not os.path.isdir(UPLOAD_DIRECTORY): if not os.path.isdir(UPLOAD_DIRECTORY):
os.makedirs(UPLOAD_DIRECTORY) os.makedirs(UPLOAD_DIRECTORY)
@ -384,6 +417,11 @@ def register():
username = request.form['username'].lower() username = request.form['username'].lower()
if not is_username(username): if not is_username(username):
flash('This username is invalid') flash('This username is invalid')
return render_template('join.html')
if username == getattr(get_current_user(), 'username', None) and not request.form.get('confirm_another'):
flash('You are already logged in. Please confirm you want to '
'create another account by checking the option.')
return render_template('join.html')
try: try:
with database.atomic(): with database.atomic():
# Attempt to create the user. If the username is taken, due to the # Attempt to create the user. If the username is taken, due to the
@ -394,6 +432,10 @@ def register():
email=request.form['email'], email=request.form['email'],
birthday=birthday, birthday=birthday,
join_date=datetime.datetime.now()) join_date=datetime.datetime.now())
UserProfile.create(
user=user,
full_name=request.form.get('full_name') or username
)
# mark the user as being 'authenticated' by setting the session vars # mark the user as being 'authenticated' by setting the session vars
login_user(user) login_user(user)
@ -562,6 +604,15 @@ def edit(id):
#def confirm_delete(id): #def confirm_delete(id):
# return render_template('confirm_delete.html') # return render_template('confirm_delete.html')
@app.route('/edit_profile/', methods=['GET', 'POST'])
def edit_profile():
if request.method == 'POST':
user = get_current_user()
username = request.form['username']
if username != user.username:
User.update(username=username).where(User.id == user.id).execute()
return render_template('edit_profile.html')
@app.route('/notifications/') @app.route('/notifications/')
@login_required @login_required
def notifications(): def notifications():

View file

@ -8,7 +8,12 @@ body{margin:0}
.metanav{float:right} .metanav{float:right}
.header h1{margin:0;display:inline-block} .header h1{margin:0;display:inline-block}
.flash{background-color:#ff9;border:yellow 1px solid} .flash{background-color:#ff9;border:yellow 1px solid}
.infobox{padding:12px;border:#ccc 1px solid}
@media (min-width:640px) {
.infobox{float:right;width:320px}
}
.weak{opacity:.5} .weak{opacity:.5}
.field_desc{display:block}
.message-visual img{max-width:100%;max-height:8em} .message-visual img{max-width:100%;max-height:8em}
.message-options-showhide::before{content:'\2026'} .message-options-showhide::before{content:'\2026'}
.message-options{display:none} .message-options{display:none}

View file

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block body %}
<h2>Edit Profile</h2>
<form method="POST">
<dl>
<dt>Username:</dt>
<dd><input type="text" class="username_input" name="username" required value="{{ current_user.username }}" autocomplete="off"></dd>
<dd><input type="submit" value="Save"></dd>
</dl>
</form>
{% endblock %}

View file

@ -0,0 +1,30 @@
{% set profile = user.profile %}
<div class="infobox">
<h3>{{ profile.full_name }}</h3>
<p>{{ profile.biography|enrich }}</p>
{% if profile.location %}
<p><span class="weak">Location:</span> {{ profile.location }}</p>
{% endif %}
{% if profile.year %}
<p><span class="weak">Year:</span> {{ profile.year }}</p>
{% endif %}
{% if profile.website %}
<p><span class="weak">Website:</span> {{ profile.website|urlize }}</p>
{% endif %}
{% if profile.instagram %}
<p><span class="weak">Instagram:</span> <a href="https://www.instagram.com/{{ profile.instagram }}">{{ profile.instagram }}</a></p>
{% endif %}
{% if profile.facebook %}
<p><span class="weak">Facebook:</span> <a href="https://facebook.com/{{ profile.facebook }}">{{ profile.facebook }}</a></p>
{% endif %}
<p>
<strong>{{ user.messages|count }}</strong> messages
-
<strong>{{ user.followers()|count }}</strong> followers
-
<strong>{{ user.following()|count }}</strong> following
</p>
{% if user == current_user %}
<p><a href="/edit_profile/">Edit profile</a></p>
{% endif %}
</div>

View file

@ -1,16 +1,34 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block body %} {% block body %}
<h2>Join {{ site_name }}</h2> <h2>Join {{ site_name }}</h2>
<form action="{{ url_for('register') }}" method="post"> <form action="{{ url_for('register') }}" method="POST">
<dl> <dl>
<dt>Username:</dt> <dt>Username:</dt>
<dd><input type="text" class="username-input" name="username"></dd> <dd><input type="text" class="username-input" name="username" autocomplete="off"></dd>
<dt>Full name:</dt>
<dd>
<small class="field_desc">If not given, defaults to your username.</small>
<input type="text" name="full_name">
</dd>
<dt>Password:</dt> <dt>Password:</dt>
<dd><input type="password" name="password"></dd> <dd><input type="password" name="password"></dd>
<dt>Email:</dt> <dt>Email:</dt>
<dd><input type="text" name="email"></dd> <dd><input type="text" name="email"></dd>
<dt>Birthday: <dt>Birthday:</dt>
<dd><input type="text" name="birthday" placeholder="yyyy-mm-dd"> <dd>
<small class="field_desc">Your birthday won't be shown to anyone.</small>
<input type="text" name="birthday" placeholder="yyyy-mm-dd">
</dd>
{% if not current_user.is_anonymous %}
<dd>
<input type="checkbox" name="confirm_another" value="1">
<label for="confirm_another">I want to create another account</label>
</dd>
{% endif %}
<dd>
<input type="checkbox" name="legal" value="1">
<label for="legal">I've read the <a href="/terms/">Terms of Service</a> and <a href="/privacy/">Privacy Policy</a>.</label>
</dd>
<dd><input type="submit" value="Join"> <dd><input type="submit" value="Join">
</dl> </dl>
</form> </form>

View file

@ -1,13 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block body %} {% block body %}
{% include "includes/infobox_profile.html" %}
<h2>Messages from {{ user.username }}</h2> <h2>Messages from {{ user.username }}</h2>
<p>
<strong>{{ user.messages|count }}</strong> messages
-
<strong>{{ user.followers()|count }}</strong> followers
-
<strong>{{ user.following()|count }}</strong> following
</p>
{% if not current_user.is_anonymous %} {% if not current_user.is_anonymous %}
{% if user.username != current_user.username %} {% if user.username != current_user.username %}
{% if current_user|is_following(user) %} {% if current_user|is_following(user) %}
@ -21,8 +15,7 @@
{% endif %} {% endif %}
<p><a href="/create/?preload=%2B{{ user.username }}">Mention this user in a message</a></p> <p><a href="/create/?preload=%2B{{ user.username }}">Mention this user in a message</a></p>
{% else %} {% else %}
<!-- here should go the "edit profile" button --> <a href="/create/">Create a message</a>
<a href="/create/">Create a status</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
<ul> <ul>