diasporadiaries

a platform for writing stories with personal accounts and messages
git clone git://parazyd.org/diasporadiaries.git
Log | Files | Refs | Submodules | README | LICENSE

commit 7ece93e2c759a388a02f079904f0a09003a4bfda
parent ebb0236cc4974d465f0b179382c32239b18a4326
Author: parazyd <parazyd@dyne.org>
Date:   Sun, 20 Jan 2019 22:16:59 +0100

Implement support for messages and apply linting.

Diffstat:
Mdiaspora.py | 60++++++++++++++++++++++++++++++++++++++++++++++--------------
Atemplates/messages.html | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtemplates/nav.html | 2++
Mtemplates/profile.html | 9++++++++-
Mutils.py | 126++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
5 files changed, 232 insertions(+), 28 deletions(-)

diff --git a/diaspora.py b/diaspora.py @@ -32,7 +32,8 @@ from db import (sql_delete_row_where, sql_update_row_where, sql_insert, from utils import (get_story, makenav, randomstring, getcountryname, get_multiple_stories, get_multiple_stories_filtered, make_profile, find_user_by_id, find_user_by_email, - validate_user, get_multiple_users) + validate_user, get_multiple_users, get_messages, + get_latest_messages, send_message) app = Flask(__name__) @@ -112,7 +113,8 @@ def login(): nxt = url_for('main') return redirect(nxt) else: - return render_template('fail_login.html', msg='Incorrect password.') + return render_template('fail_login.html', + msg='Incorrect password.') return render_template('login.html') @@ -127,6 +129,32 @@ def logout(): return render_template('logout.html') +@app.route('/messages') +@login_required +def messages(): + """ + Route for users' messages. + """ + id_from = request.args.get('from') + if id_from: + msgs = get_messages(current_user.username, id_from) + return render_template('messages.html', messages=msgs[::-1], + single=True, them=find_user_by_id(id_from)) + + msgs = get_latest_messages(current_user.username) + return render_template('messages.html', messages=msgs) + +@app.route('/sendmsg', methods=['POST']) +@login_required +def sendmsg(): + """ + Route for sending a single message. + """ + send_message(request.form['Id'], request.form['Message'], + request.form['Us']) + return redirect('/messages?from=%s' % request.form['Id']) + + @app.route('/write', methods=['GET', 'POST']) def write(): """ @@ -228,17 +256,17 @@ def country(): Route for viewing stories for a specific country. If no country is given, it will show a random country. """ - cc = request.args.get('id') - if not cc: - cc = choice(sql_select_col('disembark'))[0] + ccode = request.args.get('id') + if not ccode: + ccode = choice(sql_select_col('disembark'))[0] ccfrom = request.args.get('from') filtered = None if ccfrom: - filtered = get_multiple_stories_filtered('disembark', cc, + filtered = get_multiple_stories_filtered('disembark', ccode, ('embark', ccfrom)) - stories = get_multiple_stories('disembark', cc) + stories = get_multiple_stories('disembark', ccode) clist = [(i['embark'], i['embarkname']) for i in stories] clist = list(set(clist)) @@ -250,8 +278,8 @@ def country(): table='users') profiles['email'] = uid[0][0] - return render_template('country.html', country=getcountryname(cc), - cc=cc, stories=stories, filtered_stories=filtered, + return render_template('country.html', country=getcountryname(ccode), + cc=ccode, stories=stories, filtered_stories=filtered, clist=clist, profiles=profiles) @@ -270,7 +298,8 @@ def profile(): ('visible', 1)) if stories: name = stories[0]['name'] - return render_template('profile.html', stories=stories, name=name) + return render_template('profile.html', stories=stories, + name=name, uid=uid) return render_template('fail_profile.html') @@ -301,7 +330,8 @@ def dashboard(): ccto = request.args.get('to') filtered = None if ccto: - filtered = get_multiple_stories_filtered('visible', fparam[act], ('disembark', ccto)) + filtered = get_multiple_stories_filtered('visible', fparam[act], + ('disembark', ccto)) stories = get_multiple_stories('visible', fparam[act]) clist = [(i['disembark'], i['disembarkname']) for i in stories] @@ -325,7 +355,7 @@ def users(): if request.form['password']: vals.append(('password', hashpw(request.form['password'].encode(), - gensalt()))) + gensalt()))) if request.form['cap']: vals.append(('cap', request.form['cap'])) @@ -337,8 +367,8 @@ def users(): if request.args.get('delid'): sql_delete_row_where('id', request.args.get('delid'), table='users') - users = get_multiple_users('is_active', 1) - return render_template('users.html', users=users) + userlist = get_multiple_users('is_active', 1) + return render_template('users.html', users=userlist) @app.route('/') @@ -359,4 +389,6 @@ if __name__ == '__main__': help='Debug mode') args = parser.parse_args() + make_profile('Diaspora Diaries', 'admin@diasporadiaries.eu') + app.run(host=args.l, port=args.p, threaded=True, debug=args.d) diff --git a/templates/messages.html b/templates/messages.html @@ -0,0 +1,63 @@ +{% include 'header.html' %} + + <title>Your messages | Diaspora Diaries</title> + +{% include 'nav.html' %} + + <main role="main" class="container cover"> + + {% if them %} + <h1 class="cover-heading">Messages with {{ them['name'] }}</h1> + {% else %} + <h1 class="cover-heading">Messages</h1> + {% endif %} + + {% if not single %} + <table class="table"> + <thead> + <tr> + <th scope="col">From</th> + <th scope="col">Latest</th> + <th scope="col">At</th> + </tr> + </thead> + <tbody> + {% for i in messages %} + <tr> + <td><a href="/messages?from={{ i[1] }}">{{ i[0] }}</a></td> + <td><a href="/messages?from={{ i[1] }}">{{ i[2]['message'] }}</a></td> + <td>{{ i[2]['time'] }}</td> + </tr> + {% endfor %} + </tbody> + </table> + {% else %} + + <form action="/sendmsg" method="POST" id="msgform"> + <p class="lead">Reply:</p> + <input type="hidden" name="Id" value="{{ them['id'] }}"> + <input type="hidden" name="Us" value="{{ current_user.id }}"> + <textarea name="Message" form="msgform" required></textarea><br> + <button form="msgform" type="submit" class="btn btn-outline-success">Send</button> + </p> + </form> + + {% if not messages %} + <p class="lead">No messages.</p> + {% endif %} + + {% for i in messages %} + <hr style="width: 100%; height: 2px;"> + <blockquote class="blockquote"> + <p>{{ i['message']}}</p> + <footer class="blockquote-footer">{{ i['from'] }} - {{ i['time'] }}</footer> + </blockquote> + {% endfor %} + + <hr style="width: 100%; height: 2px;"> + <a href="/messages">&lt; back</a> + {% endif %} + + </main> + +{% include 'footer.html' %} diff --git a/templates/nav.html b/templates/nav.html @@ -59,6 +59,8 @@ <a class="nav-link" href="/profile?id={{ current_user.id }}">Profile</a> </li> <li class="nav-item"> + <a class="nav-link" href="/messages">Messages</a> + <li class="nav-item"> <a class="nav-link" href="/logout">Logout</a> </li> {% else %} diff --git a/templates/profile.html b/templates/profile.html @@ -1,6 +1,6 @@ {% include 'header.html' %} - <title>Diaries by {{ name }}| Diaspora Diaries</title> + <title>Diaries by {{ name }} | Diaspora Diaries</title> {% include 'nav.html' %} @@ -50,6 +50,13 @@ </div> </div> + {% if current_user.is_active %} + <hr style="width: 100%; height: 2px;"> + <div class="container text-center"> + <a href="/messages?from={{ uid }}" class="btn btn-outline-primary">Send PM</a> + </div> + {% endif %} + </main> {% include 'footer.html' %} diff --git a/utils.py b/utils.py @@ -17,24 +17,26 @@ """ Utility functions. """ -from json import load +import json +from os import makedirs, listdir +from os.path import join from random import SystemRandom, shuffle from string import ascii_lowercase, digits from time import gmtime, strftime, time from bcrypt import gensalt, hashpw +from flask import Markup from db import sql_select_col_where, sql_insert -countrymap = {} +COUNTRYMAP = {} with open('world.json') as worldfile: - json_data = load(worldfile) -for cname in json_data: - countrymap[cname['alpha2']] = cname['name'] -countrymap['xk'] = 'Kosovo' -del json_data -del worldfile + JSON_DATA = json.load(worldfile) +for cname in JSON_DATA: + COUNTRYMAP[cname['alpha2']] = cname['name'] +COUNTRYMAP['xk'] = 'Kosovo' +del JSON_DATA def randomstring(length): @@ -45,11 +47,11 @@ def randomstring(length): for _ in range(length)) -def getcountryname(cc): +def getcountryname(ccode): """ Returns a country name matching the given country code. """ - return countrymap.get(cc, 'Unknown countries') + return COUNTRYMAP.get(ccode, 'Unknown countries') def fill_story(row): @@ -190,11 +192,11 @@ def find_user_by_email(email): return fill_user_dict(row[0]) -def validate_user(user, pw): +def validate_user(user, password): """ Validates a user login. """ - if hashpw(pw.encode(), user['password']) == user['password']: + if hashpw(password.encode(), user['password']) == user['password']: return True return False @@ -220,5 +222,103 @@ def make_profile(name, email): int(time()), 1, ] - sql_insert(userargs) + + msgpath = join('messages', email) + makedirs(msgpath, exist_ok=True) + + with open(join(msgpath, 'admin@diasporadiaries.eu'), 'w') as msgfile: + json.dump([{'from': 'Diaspora Diaries', 'message': 'Welcome!', + 'time': int(time())}], msgfile) + + +def get_latest_messages(user_id): + """ + Gets the latest messages of a user to list them in a table. + """ + msgpath = join('messages', user_id) + ppl = listdir(msgpath) + latest = [] + for i in ppl: + with open(join(msgpath, i)) as msgfile: + data = json.load(msgfile) + user = find_user_by_email(i) + data[-1]['time'] = strftime('%d.%m.%Y. %H:%M UTC', + gmtime(data[-1]['time'])) + data[-1]['message'] = data[-1]['message'][:64] + "..." + latest.append([user['name'], user['id'], data[-1]]) + + return latest + + +def get_messages(user_id, id_from): + """ + Gets all messages in a single conversation. + """ + email = sql_select_col_where('email', 'id', id_from, table='users') + if email: + email = email[0][0] + else: + return [] + + msgpath = join('messages', user_id, email) + + data = [] + try: + with open(msgpath) as msgfile: + try: + data = json.load(msgfile) + except json.decoder.JSONDecodeError: + data = [] + except FileNotFoundError: + open(msgpath, 'w').close() + return [] + + messages = [] + for i in data: + i['time'] = strftime('%d.%m.%Y. %H:%M UTC', gmtime(i['time'])) + messages.append(i) + + return messages + + +def send_message(id_to, msg, id_us): + """ + Function for sending/recieving a message. + """ + ours = find_user_by_id(id_us) + them = find_user_by_id(id_to) + makedirs(join('messages', ours['email']), exist_ok=True) + our_msgpath = join('messages', ours['email'], them['email']) + makedirs(join('messages', them['email']), exist_ok=True) + their_msgpath = join('messages', them['email'], ours['email']) + + msgdict = { + 'from': ours['name'], + 'message': Markup(msg).striptags(), + 'time': int(time()), + } + + ourdata = [] + try: + with open(our_msgpath) as ourmsgs: + ourdata = json.load(ourmsgs) + except json.decoder.JSONDecodeError: + ourdata = [] + + ourdata.append(msgdict) + + with open(our_msgpath, 'w') as ourmsgs: + json.dump(ourdata, ourmsgs) + + theirdata = [] + try: + with open(their_msgpath) as theirmsgs: + theirdata = json.load(theirmsgs) + except json.decoder.JSONDecodeError: + theirdata = [] + + theirdata.append(msgdict) + + with open(their_msgpath, 'w') as theirmsgs: + json.dump(theirdata, theirmsgs)