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:
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">< 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)