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 132393e741148b2fd28b57e1f96f57f242ad6177
parent 1d131ae8a95976f26140c25a3e5818c57bc8b54e
Author: parazyd <parazyd@dyne.org>
Date:   Wed, 16 Jan 2019 00:05:57 +0100

Add basic dashboard functionality.

Diffstat:
Mdiaspora.py | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Atemplates/approved.html | 15+++++++++++++++
Atemplates/dashboard.html | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atemplates/edit.html | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atemplates/redacted.html | 15+++++++++++++++
Mtemplates/submit.html | 4----
6 files changed, 270 insertions(+), 10 deletions(-)

diff --git a/diaspora.py b/diaspora.py @@ -24,7 +24,7 @@ from time import gmtime, strftime, time import json import sqlite3 -from flask import (Flask, render_template, request, Markup) +from flask import (Flask, render_template, request, Markup, redirect) app = Flask(__name__) @@ -84,6 +84,14 @@ def query_all_where(col, val): return db.fetchall() +def query_col_where(col0, col1, val): + """ + Queries a specific column where col1 = val + """ + db.execute("SELECT %s FROM stories WHERE %s = '%s';" % (col0, col1, val)) + return db.fetchall() + + def query_col(col): """ Executes a SELECT query and returns the entire col. @@ -92,6 +100,18 @@ def query_col(col): return db.fetchall() +def approvestory(storyid): + """ + Makes a story visible on the index. + """ + db.execute(""" + UPDATE stories + SET visible = 1 + WHERE id = '%s'; + """ % storyid) + dbctx.commit() + + def fillstory(row): """ Returns a story dict filled with the given row from the db. @@ -156,7 +176,7 @@ def makenav(): Queries the disembark column for filling up the by-country dropdown navigation. """ - rows = query_col('disembark') + rows = query_col_where('disembark', 'visible', 1) havec = [] for j in rows: @@ -186,7 +206,16 @@ def submit(): """ if request.method == 'POST': db.execute(""" - INSERT INTO stories VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + INSERT INTO stories VALUES ( + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?); """, (None, request.form['Name'], request.form['Embark'], @@ -195,10 +224,8 @@ def submit(): request.form['Contact'], request.form['Story'], int(time()), - 1)), + 0)), dbctx.commit() - - return render_template('submitted.html', navlist=makenav()) return render_template('submit.html', navlist=makenav()) @@ -246,6 +273,62 @@ def view(): return render_template('view.html', navlist=makenav(), story=story) +@app.route('/dashboard', methods=['GET', 'POST']) +def dashboard(): + """ + Route for dashboard/admin view. + """ + approve = request.args.get('approveid') + if approve: + approvestory(approve) + return render_template('approved.html', navlist=makenav()) + + query = query_all_where('visible', 0) + print(query) + stories = [] + for i in query: + sinstory = fillstory(i) + sinstory['story'] = Markup(sinstory['story']).striptags()[:128] + sinstory['story'] += '...' + stories.append(sinstory) + + return render_template('dashboard.html', navlist=makenav(), + stories=stories) + + +@app.route('/edit', methods=['GET', 'POST']) +def edit(): + """ + Route for editing and redacting. + """ + if request.method == 'POST': + db.execute(""" + UPDATE stories + SET name = '%s', + embark = '%s', + disembark = '%s', + email = '%s', + contact = '%s', + story = '%s' + WHERE id = '%s'; + """ % (request.form['Name'], + request.form['Embark'], + request.form['Disembark'], + request.form['Email'], + request.form['Contact'], + request.form['Story'], + request.form['Id'])) + dbctx.commit() + return render_template('redacted.html', navlist=makenav()) + + storyid = request.args.get('id') + if storyid: + story = getstory(storyid) + return render_template('edit.html', navlist=makenav(), story=story) + + return redirect('/dashboard') + + if __name__ == '__main__': parser = ArgumentParser() parser.add_argument('-l', default='0.0.0.0', diff --git a/templates/approved.html b/templates/approved.html @@ -0,0 +1,15 @@ +{% include 'header.html' %} + + <title>Approved | Diaspora Diaries</title> + +{% include 'nav.html' %} + + <main role="main" class="container cover"> + + <h1 class="cover-heading">Story approved!</h1> + + <p class="lead">You can return to the <a href="/dashboard">dashboard</a> now.</p> + + </main> + +{% include 'footer.html' %} diff --git a/templates/dashboard.html b/templates/dashboard.html @@ -0,0 +1,55 @@ +{% include 'header.html' %} + + <title>Dashboard | Diaspora Diaries</title> + +{% include 'nav.html' %} + + <main role="main" class="container cover"> + + <h1 class="cover-heading">Dashboard</h1> + + <div class="album py-5 bg-light"> + <div class="container"> + + {% for i in stories %} + {% if loop.index % 3 == 1 %} + <div class="row"> + {% endif %} + + <div class="col-md-4"> + <div class="card-body"> + <p class="card-text"> + {{ i['story'] }} + </p> + + <div class="d-flex justify-content-between align-items-center"> + <div class="btn-group"> + <a href="/edit?id={{ i['id'] }}" class="btn btn-sm btn-outline-secondary">Edit</a> + <a href="/dashboard?approveid={{ i['id'] }}" class="btn btn-sm btn-outline-secondary">Add</a> + </div> + + <img style="height: 1em;" src="/static/img/flags/{{ i['embark'] }}.png" alt="{{ i['embarkname'] }}" title="{{ i['embarkname' ]}}"><span class="fa fa-anchor"></span><img style="height: 1em;" src="/static/img/flags/{{ i['disembark'] }}.png" alt="{{ i['disembarkname'] }}" title="{{ i['disembarkname'] }}"> + + <small class="text-muted"> + {{ i['name'] }}<br> + {{ i['date'] }}<br> + {{ i['time'] }} + </small> + </div> + </div> + </div> + + {% if loop.index % 3 == 0 or loop.last %} + </div> + {% if not loop.last %} + <hr style="width: 100%; height: 2px;"> + {% endif %} + {% endif %} + + {% endfor %} + </div> + </div> + + </main> + +{% include 'footer.html' %} diff --git a/templates/edit.html b/templates/edit.html @@ -0,0 +1,96 @@ +{% include 'header.html' %} + + <title>Edit | Diaspora Diaries</title> + + <link href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.11/summernote-bs4.css" rel="stylesheet"> + <script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.11/summernote-bs4.js"></script> + <script src="/static/js/summernote-cleaner.js"></script> + + <script> + function countryCheck(that, idname) { + if (that.value == "nn") { + document.getElementById(idname).style.display = "inline"; + } else { + document.getElementById(idname).style.display = "none"; + } + } + </script> + +{% include 'nav.html' %} + + <main role="main" class="container"> + + <form action="/edit" method="POST" id="storyform"> + <input type="text" name="Id" value="{{ story['id'] }}" style="display: none;"> + <p class="lead">Name:<br> + <input type="text" name="Name" required value="{{ story['name'] }}"> + </p> + + <p class="lead">Country of embarkment:<br> + <select name="Embark" form="storyform" onchange="countryCheck(this, 'embarkother');" id="embarkbox"> + <option value="{{ story['embark']}}">{{ story['embarkname'] }}</option> + </select> + <input type="text" name="EmbarkOther" id="embarkother" style="display: none;" placeholder="Type country here"> + </p> + + + <p class="lead">Country of disembarkment:<br> + <select name="Disembark" form="storyform" onchange="countryCheck(this, 'disembarkother');" selected="{{ story['disembark'] }}"> + <option value="{{ story['disembark'] }}">{{ story['disembarkname'] }}</option> + </select> + <input type="text" name="DisembarkOther" id="disembarkother" style="display: none;" placeholder="Type country here"> + </p> + + <p class="lead">Email:<br> + <input type="email" name="Email" value="{{ story['email'] }}"> + </p> + + <p class="lead">Contact info:<br> + <textarea name="Contact" form="storyform">{{ story['contact'] }}</textarea> + </p> + + <p class="lead">Write your story:<br> + <textarea id="storytext" name="Story" form="storyform" required>{{ story['story'] }}</textarea> + </p> + + <input type="submit" value="Submit"> + </form> + + </main> + + <script> + $('.summernote').summernote({ + toolbar:[ + ['cleaner',['cleaner']], // The Button + ['style',['style']], + ['font',['bold','underline','clear']], + ['fontname',['fontname']], + ['color',['color']], + ['para',['ul','ol','paragraph']], + ['height',['height']], + ['table',['table']], + ['insert',['media','link','hr']], + ['view',['fullscreen','codeview']], + ['help',['help']] + ], + cleaner:{ + action: 'both', // both|button|paste + newline: '<br>', // Summernote's default is to use '<p><br></p>' + notStyle: 'position:absolute;top:0;left:0;right:0', // Position of Notification + icon: '<i class="note-icon">[Your Button]</i>', + keepHtml: false, // Remove all Html formats + keepOnlyTags: ['<p>', '<br>', '<ul>', '<li>', '<b>', '<strong>','<i>', '<a>'], // If keepHtml is true, remove all tags except these + keepClasses: false, // Remove Classes + badTags: ['style', 'script', 'applet', 'embed', 'noframes', 'noscript', 'html'], // Remove full tags with contents + badAttributes: ['style', 'start'], // Remove attributes from remaining tags + limitChars: false, // 0/false|# 0/false disables option + limitDisplay: 'both', // text|html|both + limitStop: false // true/false + } + }); + + $(document).ready(function() { + $('#storytext').summernote(); + }); + </script> +{% include 'footer.html' %} diff --git a/templates/redacted.html b/templates/redacted.html @@ -0,0 +1,15 @@ +{% include 'header.html' %} + + <title>Redacted | Diaspora Diaries</title> + +{% include 'nav.html' %} + + <main role="main" class="container cover"> + + <h1 class="cover-heading">Story redacted!</h1> + + <p class="lead">You can return to the <a href="/dashboard">dashboard</a> now.</p> + + </main> + +{% include 'footer.html' %} diff --git a/templates/submit.html b/templates/submit.html @@ -29,18 +29,14 @@ <select name="Embark" form="storyform" onchange="countryCheck(this, 'embarkother');"> {% include 'cc.html' %} </select> - <p class="lead"> <input type="text" name="EmbarkOther" id="embarkother" style="display: none;" placeholder="Type country here"> - </p> </p> <p class="lead">Country of disembarkment:<br> <select name="Disembark" form="storyform" onchange="countryCheck(this, 'disembarkother');"> {% include 'cc.html' %} </select> - <p class="lead"> <input type="text" name="DisembarkOther" id="disembarkother" style="display: none;" placeholder="Type country here"> - </p> </p> <p class="lead">Email:<br>