For Private page, add feature 'take note'
This makes this app much more meaningful. For this feature, a new database table is added. More complicated logics are also needed, for example, we use hash(user ID + timestamp + note contant) as the unique id of each note (it's necessary when we try to delete a note)
This commit is contained in:
32
app.py
32
app.py
@@ -1,5 +1,6 @@
|
|||||||
from flask import Flask, session, url_for, redirect, render_template, request, abort, flash
|
from flask import Flask, session, url_for, redirect, render_template, request, abort, flash
|
||||||
from database import list_users, verify, delete_user_from_db, add_user
|
from database import list_users, verify, delete_user_from_db, add_user
|
||||||
|
from database import read_note_from_db, write_note_into_db, delete_note_from_db, match_user_id_with_note_id
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_object('config')
|
app.config.from_object('config')
|
||||||
@@ -10,6 +11,10 @@ app.config.from_object('config')
|
|||||||
def FUN_401(error):
|
def FUN_401(error):
|
||||||
return render_template("page_401.html"), 401
|
return render_template("page_401.html"), 401
|
||||||
|
|
||||||
|
@app.errorhandler(403)
|
||||||
|
def FUN_403(error):
|
||||||
|
return render_template("page_403.html"), 403
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def FUN_404(error):
|
def FUN_404(error):
|
||||||
return render_template("page_404.html"), 404
|
return render_template("page_404.html"), 404
|
||||||
@@ -33,7 +38,12 @@ def FUN_public():
|
|||||||
@app.route("/private/")
|
@app.route("/private/")
|
||||||
def FUN_private():
|
def FUN_private():
|
||||||
if "current_user" in session.keys():
|
if "current_user" in session.keys():
|
||||||
return render_template("private_page.html")
|
notes_list = read_note_from_db(session['current_user'])
|
||||||
|
notes_table = zip([x[0] for x in notes_list],\
|
||||||
|
[x[1] for x in notes_list],\
|
||||||
|
[x[2] for x in notes_list],\
|
||||||
|
["/delete_note/" + x[0] for x in notes_list])
|
||||||
|
return render_template("private_page.html", notes = notes_table)
|
||||||
else:
|
else:
|
||||||
return abort(401)
|
return abort(401)
|
||||||
|
|
||||||
@@ -53,6 +63,26 @@ def FUN_admin():
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/write_note", methods = ["POST"])
|
||||||
|
def FUN_write_note():
|
||||||
|
text_to_write = request.form.get("text_note_to_take")
|
||||||
|
write_note_into_db(session['current_user'], text_to_write)
|
||||||
|
|
||||||
|
return(redirect(url_for("FUN_private")))
|
||||||
|
|
||||||
|
@app.route("/delete_note/<note_id>", methods = ["GET"])
|
||||||
|
def FUN_delete_note(note_id):
|
||||||
|
if session.get("current_user", None) == match_user_id_with_note_id(note_id): # Ensure the current user is NOT operating on other users' note.
|
||||||
|
delete_note_from_db(note_id)
|
||||||
|
else:
|
||||||
|
return abort(401)
|
||||||
|
|
||||||
|
return(redirect(url_for("FUN_private")))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/login", methods = ["POST"])
|
@app.route("/login", methods = ["POST"])
|
||||||
def FUN_login():
|
def FUN_login():
|
||||||
id_submitted = request.form.get("id").upper()
|
id_submitted = request.form.get("id").upper()
|
||||||
|
|||||||
71
database.py
71
database.py
@@ -1,10 +1,12 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import datetime
|
||||||
|
|
||||||
db_file_location = "database_file/users.db"
|
user_db_file_location = "database_file/users.db"
|
||||||
|
note_db_file_location = "database_file/notes.db"
|
||||||
|
|
||||||
def list_users():
|
def list_users():
|
||||||
_conn = sqlite3.connect(db_file_location)
|
_conn = sqlite3.connect(user_db_file_location)
|
||||||
_c = _conn.cursor()
|
_c = _conn.cursor()
|
||||||
|
|
||||||
_c.execute("select id from users;")
|
_c.execute("select id from users;")
|
||||||
@@ -15,7 +17,7 @@ def list_users():
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def verify(id, pw):
|
def verify(id, pw):
|
||||||
_conn = sqlite3.connect(db_file_location)
|
_conn = sqlite3.connect(user_db_file_location)
|
||||||
_c = _conn.cursor()
|
_c = _conn.cursor()
|
||||||
|
|
||||||
_c.execute("select pw from users where id = '" + id + "';")
|
_c.execute("select pw from users where id = '" + id + "';")
|
||||||
@@ -26,16 +28,21 @@ def verify(id, pw):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def delete_user_from_db(id):
|
def delete_user_from_db(id):
|
||||||
_conn = sqlite3.connect(db_file_location)
|
_conn = sqlite3.connect(user_db_file_location)
|
||||||
_c = _conn.cursor()
|
_c = _conn.cursor()
|
||||||
|
|
||||||
_c.execute("delete from users where id = '" + id + "';")
|
_c.execute("delete from users where id = '" + id + "';")
|
||||||
|
_conn.commit()
|
||||||
|
_conn.close()
|
||||||
|
|
||||||
|
# when we delete a user from database USERS, we also need to delete all his or her notes data from database NOTES
|
||||||
|
_conn = sqlite3.connect(note_db_file_location)
|
||||||
|
_c = _conn.cursor()
|
||||||
|
_c.execute("delete from notes where user = '" + id + "';")
|
||||||
_conn.commit()
|
_conn.commit()
|
||||||
_conn.close()
|
_conn.close()
|
||||||
|
|
||||||
def add_user(id, pw):
|
def add_user(id, pw):
|
||||||
_conn = sqlite3.connect(db_file_location)
|
_conn = sqlite3.connect(user_db_file_location)
|
||||||
_c = _conn.cursor()
|
_c = _conn.cursor()
|
||||||
|
|
||||||
command = "insert into users values('" + id.upper() + "', '" + hashlib.sha256(pw).hexdigest() + "');"
|
command = "insert into users values('" + id.upper() + "', '" + hashlib.sha256(pw).hexdigest() + "');"
|
||||||
@@ -44,6 +51,58 @@ def add_user(id, pw):
|
|||||||
_conn.commit()
|
_conn.commit()
|
||||||
_conn.close()
|
_conn.close()
|
||||||
|
|
||||||
|
def read_note_from_db(id):
|
||||||
|
_conn = sqlite3.connect(note_db_file_location)
|
||||||
|
_c = _conn.cursor()
|
||||||
|
|
||||||
|
command = "select note_id, timestamp, note from notes where user = '" + id.upper() + "';"
|
||||||
|
_c.execute(command)
|
||||||
|
result = _c.fetchall()
|
||||||
|
|
||||||
|
_conn.commit()
|
||||||
|
_conn.close()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def match_user_id_with_note_id(note_id):
|
||||||
|
# Given the note id, confirm if the current user is the owner of the note which is being operated.
|
||||||
|
_conn = sqlite3.connect(note_db_file_location)
|
||||||
|
_c = _conn.cursor()
|
||||||
|
|
||||||
|
command = "select user from notes where note_id = '" + note_id + "';"
|
||||||
|
_c.execute(command)
|
||||||
|
result = _c.fetchone()[0]
|
||||||
|
|
||||||
|
_conn.commit()
|
||||||
|
_conn.close()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def write_note_into_db(id, note_to_write):
|
||||||
|
_conn = sqlite3.connect(note_db_file_location)
|
||||||
|
_c = _conn.cursor()
|
||||||
|
|
||||||
|
current_timestamp = str(datetime.datetime.now())
|
||||||
|
command = "insert into notes values('" + id.upper() + "', '" + \
|
||||||
|
current_timestamp + "', '" + note_to_write + \
|
||||||
|
"', '" + hashlib.sha1(id.upper() + current_timestamp + note_to_write).hexdigest() + "');"
|
||||||
|
_c.execute(command)
|
||||||
|
|
||||||
|
_conn.commit()
|
||||||
|
_conn.close()
|
||||||
|
|
||||||
|
def delete_note_from_db(note_id):
|
||||||
|
_conn = sqlite3.connect(note_db_file_location)
|
||||||
|
_c = _conn.cursor()
|
||||||
|
|
||||||
|
command = "delete from notes where note_id = '" + note_id + "';"
|
||||||
|
_c.execute(command)
|
||||||
|
|
||||||
|
_conn.commit()
|
||||||
|
_conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
database_file/notes.db
Normal file
BIN
database_file/notes.db
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 77 KiB |
@@ -5,14 +5,16 @@
|
|||||||
|
|
||||||
{# only invoked when failed adding new ID due to duplication #}
|
{# only invoked when failed adding new ID due to duplication #}
|
||||||
{% if id_to_add_is_duplicated %}
|
{% if id_to_add_is_duplicated %}
|
||||||
<div class="text-danger">
|
<div class="alert alert-dismissible alert-danger">
|
||||||
|
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||||
<strong>Warning!</strong> The account name already exists.
|
<strong>Warning!</strong> The account name already exists.
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# only invoked when failed adding new ID due to invalid character #}
|
{# only invoked when failed adding new ID due to invalid character #}
|
||||||
{% if id_to_add_is_invalid %}
|
{% if id_to_add_is_invalid %}
|
||||||
<div class="text-danger">
|
<div class="alert alert-dismissible alert-danger">
|
||||||
|
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||||
<strong>Warning!</strong> The account name is invalid.
|
<strong>Warning!</strong> The account name is invalid.
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -63,10 +65,4 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
6
templates/page_403.html
Normal file
6
templates/page_403.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block page_title %}Forbidden(403){% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
{{ super() }}
|
||||||
|
This operation is forbidden.
|
||||||
|
{% endblock %}
|
||||||
@@ -2,6 +2,44 @@
|
|||||||
{% block page_title %}Private Page{% endblock %}
|
{% block page_title %}Private Page{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<img src="{{ url_for('static', filename='img/private.jpg') }}" class="img-circle" alt="Cinque Terre" width="304" height="236">
|
|
||||||
Only logged-in users, like you, can access this page.
|
<h4>You can take notes here. Only yourself can access them. They will be removed when your account is removed.</h4>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="textArea" class="col-lg-3 control-label">Note to Take</label>
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<form action="/write_note" method="post">
|
||||||
|
<input class="form-control" name="text_note_to_take"></input>
|
||||||
|
<button type="submit" class="btn btn-success">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
{% if notes %}
|
||||||
|
<h2>Your Notes</h2>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Note ID</th>
|
||||||
|
<th>Timestamp</th>
|
||||||
|
<th>Note</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for note_id, timestamp, note, act in notes %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ note_id }} </td>
|
||||||
|
<td> {{ timestamp }} </td>
|
||||||
|
<td> {{ note }} </td>
|
||||||
|
<td><a href={{act}}>Delete</a></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user