From 32e7c37158fbdf5e125eb80ebe695a26afef4e62 Mon Sep 17 00:00:00 2001 From: Mattia Succurro Date: Thu, 17 Oct 2019 14:34:55 +0200 Subject: [PATCH] Adding profiles and adminship --- CHANGELOG.md | 7 +++ app.py | 57 +++++++++++++++++++++++-- static/style.css | 5 +++ templates/edit_profile.html | 13 ++++++ templates/includes/infobox_profile.html | 30 +++++++++++++ templates/join.html | 26 +++++++++-- templates/user_detail.html | 11 +---- 7 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 templates/edit_profile.html create mode 100644 templates/includes/infobox_profile.html diff --git a/CHANGELOG.md b/CHANGELOG.md index b7823b4..a63f553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## 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 * 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. diff --git a/app.py b/app.py index cd5cb29..9c04f8d 100644 --- a/app.py +++ b/app.py @@ -93,6 +93,38 @@ class User(BaseModel): .where( (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. MSGPRV_PUBLIC = 0 # everyone @@ -118,11 +150,11 @@ class Message(BaseModel): privacy = self.privacy if user == cur_user: # short path - return True + # also: don't show user's messages in public timeline + return not is_public_timeline elif privacy == MSGPRV_PUBLIC: return True elif privacy == MSGPRV_UNLISTED: - # TODO user's posts may appear the same in public timeline, # even if unlisted return not is_public_timeline elif privacy == MSGPRV_FRIENDS: @@ -172,7 +204,8 @@ class Notification(BaseModel): def create_tables(): with database: database.create_tables([ - User, Message, Relationship, Upload, Notification]) + User, UserAdminship, UserProfile, Message, Relationship, + Upload, Notification]) if not os.path.isdir(UPLOAD_DIRECTORY): os.makedirs(UPLOAD_DIRECTORY) @@ -384,6 +417,11 @@ def register(): username = request.form['username'].lower() if not is_username(username): 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: with database.atomic(): # Attempt to create the user. If the username is taken, due to the @@ -394,6 +432,10 @@ def register(): email=request.form['email'], birthday=birthday, 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 login_user(user) @@ -562,6 +604,15 @@ def edit(id): #def confirm_delete(id): # 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/') @login_required def notifications(): diff --git a/static/style.css b/static/style.css index baa794c..650f108 100644 --- a/static/style.css +++ b/static/style.css @@ -8,7 +8,12 @@ body{margin:0} .metanav{float:right} .header h1{margin:0;display:inline-block} .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} +.field_desc{display:block} .message-visual img{max-width:100%;max-height:8em} .message-options-showhide::before{content:'\2026'} .message-options{display:none} diff --git a/templates/edit_profile.html b/templates/edit_profile.html new file mode 100644 index 0000000..7475d4f --- /dev/null +++ b/templates/edit_profile.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block body %} +

Edit Profile

+ +
+
+
Username:
+
+
+
+
+{% endblock %} diff --git a/templates/includes/infobox_profile.html b/templates/includes/infobox_profile.html new file mode 100644 index 0000000..730db0c --- /dev/null +++ b/templates/includes/infobox_profile.html @@ -0,0 +1,30 @@ +{% set profile = user.profile %} +
+

{{ profile.full_name }}

+

{{ profile.biography|enrich }}

+ {% if profile.location %} +

Location: {{ profile.location }}

+ {% endif %} + {% if profile.year %} +

Year: {{ profile.year }}

+ {% endif %} + {% if profile.website %} +

Website: {{ profile.website|urlize }}

+ {% endif %} + {% if profile.instagram %} +

Instagram: {{ profile.instagram }}

+ {% endif %} + {% if profile.facebook %} +

Facebook: {{ profile.facebook }}

+ {% endif %} +

+ {{ user.messages|count }} messages + - + {{ user.followers()|count }} followers + - + {{ user.following()|count }} following +

+ {% if user == current_user %} +

Edit profile

+ {% endif %} +
diff --git a/templates/join.html b/templates/join.html index 29411a6..ad980de 100644 --- a/templates/join.html +++ b/templates/join.html @@ -1,16 +1,34 @@ {% extends "base.html" %} {% block body %}

Join {{ site_name }}

-
+
Username:
-
+
+
Full name:
+
+ If not given, defaults to your username. + +
Password:
Email:
-
Birthday: -
+
Birthday:
+
+ Your birthday won't be shown to anyone. + +
+ {% if not current_user.is_anonymous %} +
+ + +
+ {% endif %} +
+ + +
diff --git a/templates/user_detail.html b/templates/user_detail.html index 7f93604..9316432 100644 --- a/templates/user_detail.html +++ b/templates/user_detail.html @@ -1,13 +1,7 @@ {% extends "base.html" %} {% block body %} + {% include "includes/infobox_profile.html" %}

Messages from {{ user.username }}

-

- {{ user.messages|count }} messages - - - {{ user.followers()|count }} followers - - - {{ user.following()|count }} following -

{% if not current_user.is_anonymous %} {% if user.username != current_user.username %} {% if current_user|is_following(user) %} @@ -21,8 +15,7 @@ {% endif %}

Mention this user in a message

{% else %} - - Create a status + Create a message {% endif %} {% endif %}