Adding profiles and adminship
This commit is contained in:
parent
156d58e549
commit
32e7c37158
7 changed files with 133 additions and 16 deletions
|
|
@ -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
57
app.py
|
|
@ -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():
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
13
templates/edit_profile.html
Normal file
13
templates/edit_profile.html
Normal 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 %}
|
||||||
30
templates/includes/infobox_profile.html
Normal file
30
templates/includes/infobox_profile.html
Normal 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>
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue