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