Projects implementation
* Separated routes into single files for better management * Added sql for streamlined example data * Streamlined function names, streamlined template file names - Removed 2 functions with unnecessary code * Added javascript for project overview * New library: tippy.js for tooltips * Added new configuration options for projects * Added cookie secure setting to flask
This commit is contained in:
parent
edd9ce7585
commit
fdec8f74c8
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,9 +1,9 @@
|
|||||||
.idea
|
.idea
|
||||||
__pycache__/
|
__pycache__/
|
||||||
venv
|
venv
|
||||||
db/labertasche.db-shm
|
db/*.db
|
||||||
db/labertasche.db-wal
|
db/*.db-shm
|
||||||
|
db/*.db-wal
|
||||||
output
|
output
|
||||||
/output/
|
/output/
|
||||||
*.sql
|
|
||||||
*.old
|
*.old
|
||||||
|
1
__implementation_example/data/blog/article-3.json
Normal file
1
__implementation_example/data/blog/article-3.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"comments": [{"comment_id": 9, "email": "commenter9@", "content": "9 This is a test comment and has no actual value. Please test all methods on this.", "created_on": "2020-12-16 23:37:00", "replied_to": null, "gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"}], "replies": [{"comment_id": 10, "email": "commenter10@", "content": "10 This is a reply to the previous comment and has no actual value. Please test all methods on this.", "created_on": "2020-12-16 23:37:00", "replied_to": 9, "gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"}]}
|
@ -1,22 +1 @@
|
|||||||
{
|
{"comments": [], "replies": [{"comment_id": 2, "email": "commenter2@", "content": "2 This is a reply to the previous comment and has no actual value. Please test all methods on this.", "created_on": "2020-12-16 23:37:00", "replied_to": 1, "gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"}]}
|
||||||
"comments": [
|
|
||||||
{
|
|
||||||
"comment_id": 1,
|
|
||||||
"email": "commenter1@",
|
|
||||||
"content": "This is an example comment with over 40 characters.",
|
|
||||||
"created_on": "2020-12-04 12:23:14",
|
|
||||||
"replied_to": null,
|
|
||||||
"gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"replies": [
|
|
||||||
{
|
|
||||||
"comment_id": 2,
|
|
||||||
"email": "commenter2@",
|
|
||||||
"content": "This is an example reply, to test if this works.",
|
|
||||||
"created_on": "2020-12-04 12:24:19",
|
|
||||||
"replied_to": 1,
|
|
||||||
"gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
108
db/example-data.sql
Normal file
108
db/example-data.sql
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/**********************************************************************************
|
||||||
|
* _author : Domeniko Gentner
|
||||||
|
* _mail : code@tuxstash.de
|
||||||
|
* _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
* _license : This project is under MIT License
|
||||||
|
**********************************************************************************
|
||||||
|
*
|
||||||
|
* This script generates sample data for the example implementation.
|
||||||
|
* Feed it into the automatically created database with either DBBeaver
|
||||||
|
* or the sqlite command line tool.
|
||||||
|
*
|
||||||
|
* Please note: Labertasche must have run once to create the database!
|
||||||
|
*
|
||||||
|
**********************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* delete old data */
|
||||||
|
DELETE FROM t_comments;
|
||||||
|
DELETE FROM t_projects;
|
||||||
|
DELETE FROM t_email;
|
||||||
|
DELETE FROM t_comments;
|
||||||
|
DELETE FROM t_location;
|
||||||
|
|
||||||
|
/* Create example projects */
|
||||||
|
INSERT INTO t_projects (id_project, name)
|
||||||
|
VALUES
|
||||||
|
(1, 'default'),
|
||||||
|
(2, 'example.com'),
|
||||||
|
(3, 'tuxstash.de'),
|
||||||
|
(4, 'beispiel.de'),
|
||||||
|
(5, 'labertasche.tuxstash.de')
|
||||||
|
;
|
||||||
|
|
||||||
|
/* Create existing locations for each project */
|
||||||
|
INSERT INTO t_location (id_location, location, project_id)
|
||||||
|
VALUES
|
||||||
|
(1, '/blog/stramine/', 1),
|
||||||
|
(2, '/blog/readme/', 1),
|
||||||
|
(3, '/blog/article-1/', 2),
|
||||||
|
(4, '/blog/article-2/', 2),
|
||||||
|
(5, '/blog/article-3/', 3),
|
||||||
|
(6, '/blog/article-4/', 3),
|
||||||
|
(7, '/blog/article-5/', 4),
|
||||||
|
(8, '/blog/article-6/', 4),
|
||||||
|
(9, '/blog/article-7/', 5),
|
||||||
|
(10, '/blog/article-8/', 5)
|
||||||
|
;
|
||||||
|
|
||||||
|
/* Create some emails that are blocked and allowed */
|
||||||
|
INSERT INTO t_email (id_email, email, is_allowed, is_blocked, project_id)
|
||||||
|
VALUES
|
||||||
|
(1, "commenter1@example.com", true, false, 1),
|
||||||
|
(2, "commenter2@example.com", false, true, 1),
|
||||||
|
(3, "commenter3@example.com", true, false, 2),
|
||||||
|
(4, "commenter4@example.com", false, true, 2),
|
||||||
|
(5, "commenter5@example.com", true, false, 3),
|
||||||
|
(6, "commenter6@example.com", false, true, 3),
|
||||||
|
(7, "commenter7@example.com", true, false, 4),
|
||||||
|
(8, "commenter8@example.com", false, true, 4),
|
||||||
|
(9, "commenter9@example.com", true, false, 5),
|
||||||
|
(10, "commenter10@example.com", false, true, 5)
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
/* Create some comments */
|
||||||
|
INSERT INTO t_comments (comments_id, location_id, email, content, created_on, is_published, is_spam, spam_score, replied_to, confirmation, deletion, gravatar, project_id)
|
||||||
|
VALUES
|
||||||
|
(1, 1, 'commenter1@example.com', '1 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 1),
|
||||||
|
(2, 1, 'commenter2@example.com', '2 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 1, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 1),
|
||||||
|
(3, 2, 'commenter3@example.com', '3 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 1),
|
||||||
|
(4, 2, 'commenter4@example.com', '4 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 3, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 1),
|
||||||
|
|
||||||
|
(5, 3, 'commenter5@example.com', '5 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 2),
|
||||||
|
(6, 3, 'commenter6@example.com', '6 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 5, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 2),
|
||||||
|
(7, 4, 'commenter7@example.com', '7 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 2),
|
||||||
|
(8, 4, 'commenter8@example.com', '8 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 7, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 2),
|
||||||
|
|
||||||
|
(9, 5, 'commenter9@example.com', '9 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', false, true, 0.09, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 3),
|
||||||
|
(10, 5, 'commenter10@example.com', '10 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 9, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 3),
|
||||||
|
(11, 6, 'commenter11@example.com', '11 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.09, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 3),
|
||||||
|
(12, 6, 'commenter12@example.com', '12 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 11, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 3),
|
||||||
|
|
||||||
|
(13, 7, 'commenter13@example.com', '13 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 4),
|
||||||
|
(14, 7, 'commenter14@example.com', '14 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 13, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 4),
|
||||||
|
(15, 8, 'commenter15@example.com', '15 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 4),
|
||||||
|
(16, 8, 'commenter16@example.com', '16 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 16, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 4),
|
||||||
|
|
||||||
|
(17, 9, 'commenter17@example.com', '17 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 5),
|
||||||
|
(18, 9, 'commenter18@example.com', '18 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 18, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 5),
|
||||||
|
(19, 10, 'commenter19@example.com', '19 This is a test comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, NULL, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 5),
|
||||||
|
(20, 10, 'commenter20@example.com', '20 This is a reply to the previous comment and has no actual value. Please test all methods on this.', '2020-12-16 23:37:00.000000', true, false, 0.99, 19, NULL, NULL, 'd9eef4df0ae5bfc1a9a9b1e39a99c07f', 5)
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -8,19 +8,32 @@
|
|||||||
system:
|
system:
|
||||||
web_url: "http://dev.localhost:1314/" # Url where the comment system is served
|
web_url: "http://dev.localhost:1314/" # Url where the comment system is served
|
||||||
blog_url: "http://dev.localhost:1313/" # Url of your website
|
blog_url: "http://dev.localhost:1313/" # Url of your website
|
||||||
cookie-domain: "dev.localhost" # Url where the comment system is served
|
cookie_domain: "dev.localhost" # hostname where the comment system is served
|
||||||
database_uri: "sqlite:///db/labertasche.db" # Database URI. See documentation. Default is sqlite.
|
database_uri: "sqlite:///db/labertasche.db" # Database URI. See documentation. Default is sqlite.
|
||||||
secret: "6Gxvb52bIJCm2vfDsmWKzShKp1omrzVG" # CHANGE ME! THIS IS IMPORTANT!
|
secret: "6Gxvb52bIJCm2vfDsmWKzShKp1omrzVG" # CHANGE ME! THIS IS IMPORTANT!
|
||||||
output: "./__implementation_example/data/" # Base path for the output json
|
output: "./__implementation_example/data/" # Base path for the output json
|
||||||
debug: false # Leave this as is, this is for development.
|
debug: false # Leave this as is, this is for development.
|
||||||
send_otp_to_publish: true # Disables confirmation w/ OTP via mail
|
send_otp_to_publish: true # Disables confirmation w/ OTP via mail
|
||||||
|
cookie_secure: false
|
||||||
|
|
||||||
|
projects:
|
||||||
|
- default:
|
||||||
|
web_url: "http://dev.localhost:1314/"
|
||||||
|
blog_url: "http://dev.localhost:1313/"
|
||||||
|
output: "./__implementation_example/data/"
|
||||||
|
send_otp_to_publish: true
|
||||||
|
|
||||||
|
- example.com:
|
||||||
|
web_url: "http://comments.example.com/"
|
||||||
|
blog_url: "http://blog.example.com/"
|
||||||
|
output: "./example/data/"
|
||||||
|
send_otp_to_publish: true
|
||||||
|
|
||||||
gravatar:
|
gravatar:
|
||||||
cache: true # Enable caching of gravatar images
|
cache: true # Enable caching of gravatar images
|
||||||
static_dir: "./__implementation_example/static/images/gravatar/" # Where to store cached images, must exist!
|
static_dir: "./__implementation_example/static/images/gravatar/" # Where to store cached images, must exist!
|
||||||
size: 256 # only applies if images are cached,
|
size: 256 # only applies if images are cached,
|
||||||
# otherwise use ?s=size at the end of the gravatar url
|
# otherwise use ?s=size at the end of the gravatar url
|
||||||
|
|
||||||
dashboard:
|
dashboard:
|
||||||
username: "admin" # CHANGE ME!
|
username: "admin" # CHANGE ME!
|
||||||
password: "admin" # CHANGE ME!
|
password: "admin" # CHANGE ME!
|
||||||
|
@ -9,4 +9,4 @@
|
|||||||
from .bp_comments import bp_comments
|
from .bp_comments import bp_comments
|
||||||
from .bp_login import bp_login
|
from .bp_login import bp_login
|
||||||
from .bp_dashboard import bp_dashboard
|
from .bp_dashboard import bp_dashboard
|
||||||
|
from .bp_jsconnector import bp_jsconnector
|
||||||
|
@ -108,7 +108,7 @@ def check_and_insert_new_comment():
|
|||||||
is_spam = False
|
is_spam = False
|
||||||
|
|
||||||
# Look for location
|
# Look for location
|
||||||
loc_query = db.session.query(TLocation)\
|
loc_query = db.session.query(TLocation) \
|
||||||
.filter(TLocation.location == new_comment['location'])
|
.filter(TLocation.location == new_comment['location'])
|
||||||
|
|
||||||
if loc_query.first():
|
if loc_query.first():
|
||||||
@ -131,6 +131,7 @@ def check_and_insert_new_comment():
|
|||||||
new_comment.pop("location")
|
new_comment.pop("location")
|
||||||
|
|
||||||
# insert comment
|
# insert comment
|
||||||
|
# noinspection PyBroadException
|
||||||
try:
|
try:
|
||||||
new_comment.update({"is_published": False})
|
new_comment.update({"is_published": False})
|
||||||
new_comment.update({"created_on": default_timestamp()})
|
new_comment.update({"created_on": default_timestamp()})
|
||||||
@ -154,11 +155,7 @@ def check_and_insert_new_comment():
|
|||||||
print(f"Duplicate from {request.environ['REMOTE_ADDR']}, error is:\n{e}", file=stderr)
|
print(f"Duplicate from {request.environ['REMOTE_ADDR']}, error is:\n{e}", file=stderr)
|
||||||
return make_response(jsonify(status="post-duplicate"), 400)
|
return make_response(jsonify(status="post-duplicate"), 400)
|
||||||
|
|
||||||
except Exception as e: # must be at bottom
|
except Exception: # must be at bottom
|
||||||
# mail(f"check_and_insert_new_comment has thrown an error: {e}", )
|
|
||||||
print("---------------------------------------------")
|
|
||||||
print(e, file=stderr)
|
|
||||||
print("---------------------------------------------")
|
|
||||||
return make_response(jsonify(status="post-internal-server-error"), 400)
|
return make_response(jsonify(status="post-internal-server-error"), 400)
|
||||||
|
|
||||||
export_location(t_comment.location_id)
|
export_location(t_comment.location_id)
|
@ -1,240 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# /**********************************************************************************
|
|
||||||
# * _author : Domeniko Gentner
|
|
||||||
# * _mail : code@tuxstash.de
|
|
||||||
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
|
||||||
# * _license : This project is under MIT License
|
|
||||||
# *********************************************************************************/
|
|
||||||
from flask import Blueprint, render_template, request, redirect, make_response, jsonify
|
|
||||||
from flask_login import login_required
|
|
||||||
from labertasche.database import labertasche_db as db
|
|
||||||
from labertasche.models import TLocation, TComments, TEmail, TProjects
|
|
||||||
from labertasche.helper import dates_of_the_week, export_location, get_id_from_project_name
|
|
||||||
from sqlalchemy import func
|
|
||||||
import re
|
|
||||||
|
|
||||||
# Blueprint
|
|
||||||
bp_dashboard = Blueprint("bp_dashboard", __name__, url_prefix='/dashboard')
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route("/")
|
|
||||||
@login_required
|
|
||||||
def dashboard_index():
|
|
||||||
t_projects = db.session.query(TProjects).all()
|
|
||||||
projects = list()
|
|
||||||
for each in t_projects:
|
|
||||||
comments = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
|
|
||||||
.filter(TComments.is_published == True) \
|
|
||||||
.filter(TComments.is_spam == False).count()
|
|
||||||
unpub_comments = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
|
|
||||||
.filter(TComments.is_spam == False) \
|
|
||||||
.filter(TComments.is_published == False).count()
|
|
||||||
spam = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
|
|
||||||
.filter(TComments.is_spam == True).count()
|
|
||||||
|
|
||||||
projects.append(dict({
|
|
||||||
"id_project": each.id_project,
|
|
||||||
"name": each.name,
|
|
||||||
"total_comments": comments,
|
|
||||||
"total_spam": spam,
|
|
||||||
"total_unpublished": unpub_comments
|
|
||||||
}))
|
|
||||||
return render_template('project_overview.html', projects=projects)
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route("/project/new", methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
def dashboard_new_project():
|
|
||||||
return make_response(jsonify(status='too-short'), 200)
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/<project>/')
|
|
||||||
@login_required
|
|
||||||
def dashboard_project_index(project: str):
|
|
||||||
proj_id = get_id_from_project_name(project)
|
|
||||||
dates = dates_of_the_week()
|
|
||||||
spam = list()
|
|
||||||
published = list()
|
|
||||||
unpublished = list()
|
|
||||||
for each in dates:
|
|
||||||
spam_comments = db.session.query(TComments).filter(TComments.project_id == proj_id)\
|
|
||||||
.filter(func.DATE(TComments.created_on) == each.date())\
|
|
||||||
.filter(TComments.is_spam == True).all()
|
|
||||||
|
|
||||||
pub_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date()) \
|
|
||||||
.filter(TComments.is_spam == False)\
|
|
||||||
.filter(TComments.is_published == True).all()
|
|
||||||
|
|
||||||
unpub_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date()) \
|
|
||||||
.filter(TComments.is_spam == False)\
|
|
||||||
.filter(TComments.is_published == False).all()
|
|
||||||
|
|
||||||
published.append(len(pub_comments))
|
|
||||||
spam.append(len(spam_comments))
|
|
||||||
unpublished.append(len(unpub_comments))
|
|
||||||
|
|
||||||
return render_template('dashboard.html', dates=dates, spam=spam, published=published, unpublished=unpublished)
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/review-spam/', methods=["POST", "GET"])
|
|
||||||
@bp_dashboard.route('/review-spam/<int:location_id>', methods=["POST", "GET"])
|
|
||||||
@login_required
|
|
||||||
def dashboard_review_spam(location_id=None):
|
|
||||||
all_locations = db.session.query(TLocation).all()
|
|
||||||
|
|
||||||
# Check post
|
|
||||||
if request.method == "POST":
|
|
||||||
location_id = request.form.get('selected_location')
|
|
||||||
|
|
||||||
# no parameters found
|
|
||||||
if location_id is None:
|
|
||||||
return render_template("review-spam.html", locations=all_locations, selected=location_id)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if int(location_id) >= 1:
|
|
||||||
spam_comments = db.session.query(TComments).filter(TComments.location_id == location_id)\
|
|
||||||
.filter(TComments.is_spam == True)
|
|
||||||
return render_template("review-spam.html", locations=all_locations, selected=location_id,
|
|
||||||
spam_comments=spam_comments)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
export_location(location_id)
|
|
||||||
return render_template("review-spam.html", locations=all_locations, selected=location_id)
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/manage-comments/', methods=["POST", "GET"])
|
|
||||||
@bp_dashboard.route('/manage-comments/<int:location_id>', methods=["POST", "GET"])
|
|
||||||
@login_required
|
|
||||||
def dashboard_manage_regular_comments(location_id=None):
|
|
||||||
all_locations = db.session.query(TLocation).all()
|
|
||||||
|
|
||||||
# Check post
|
|
||||||
if request.method == "POST":
|
|
||||||
location_id = request.form.get('selected_location')
|
|
||||||
|
|
||||||
# no parameters found
|
|
||||||
if location_id is None:
|
|
||||||
return render_template("manage-comments.html", locations=all_locations, selected=location_id)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if int(location_id) >= 1:
|
|
||||||
spam_comments = db.session.query(TComments).filter(TComments.location_id == location_id) \
|
|
||||||
.filter(TComments.is_spam == False)
|
|
||||||
return render_template("manage-comments.html", locations=all_locations, selected=location_id,
|
|
||||||
spam_comments=spam_comments)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
export_location(location_id)
|
|
||||||
return render_template("manage-comments.html", locations=all_locations, selected=location_id)
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/manage-mail/')
|
|
||||||
@login_required
|
|
||||||
def dashboard_allow_email():
|
|
||||||
addresses = db.session.query(TEmail).all()
|
|
||||||
return render_template("manage_mail_addresses.html", addresses=addresses)
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/toggle-mail-allowed/<int:id_email>')
|
|
||||||
@login_required
|
|
||||||
def dashboard_allow_email_toggle(id_email):
|
|
||||||
address = db.session.query(TEmail).filter(TEmail.id_email == id_email).first()
|
|
||||||
if address:
|
|
||||||
setattr(address, "is_allowed", (not address.is_allowed))
|
|
||||||
setattr(address, "is_blocked", (not address.is_blocked))
|
|
||||||
db.session.commit()
|
|
||||||
return redirect(request.referrer)
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/reset-mail-reputation/<int:id_email>')
|
|
||||||
@login_required
|
|
||||||
def dashboard_reset_mail_reputation(id_email):
|
|
||||||
db.session.query(TEmail).filter(TEmail.id_email == id_email).delete()
|
|
||||||
db.session.commit()
|
|
||||||
return redirect(request.referrer)
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/delete-comment/<int:location_id>/<int:comment_id>', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
def dashboard_review_spam_delete_comment(location_id, comment_id):
|
|
||||||
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
|
||||||
db.session.delete(comment)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
# Remove after last slash, to keep the location but get rid of the comment id
|
|
||||||
url = re.match("^(.*[/])", request.referrer)[0]
|
|
||||||
export_location(location_id)
|
|
||||||
return redirect(f"{url}/{location_id}")
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/allow-comment/<int:location_id>/<int:comment_id>', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
def dashboard_review_spam_allow_comment(comment_id, location_id):
|
|
||||||
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
|
||||||
if comment:
|
|
||||||
setattr(comment, 'is_published', True)
|
|
||||||
setattr(comment, 'is_spam', False)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
url = re.match("^(.*[/])", request.referrer)[0]
|
|
||||||
export_location(location_id)
|
|
||||||
return redirect(f"{url}/{location_id}")
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/block-mail/<int:location_id>/<int:comment_id>', methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def dashboard_review_spam_block_mail(location_id, comment_id):
|
|
||||||
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
|
||||||
if comment:
|
|
||||||
mail = db.session.query(TEmail).filter(TEmail.email == comment.email).first()
|
|
||||||
if mail:
|
|
||||||
setattr(mail, 'is_allowed', False)
|
|
||||||
setattr(mail, 'is_blocked', True)
|
|
||||||
else:
|
|
||||||
new_mail = {
|
|
||||||
"email": comment.first().email,
|
|
||||||
"is_allowed": False,
|
|
||||||
"is_blocked": True
|
|
||||||
}
|
|
||||||
db.session.add(TEmail(**new_mail))
|
|
||||||
|
|
||||||
# Delete all comments made by this mail address
|
|
||||||
db.session.query(TComments).filter(TComments.email == comment.email).delete()
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
url = re.match("^(.*[/])", request.referrer)[0]
|
|
||||||
export_location(location_id)
|
|
||||||
return redirect(f"{url}/{location_id}")
|
|
||||||
|
|
||||||
|
|
||||||
@bp_dashboard.route('/allow-user/<int:location_id>/<int:comment_id>', methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def dashboard_review_spam_allow_user(location_id, comment_id):
|
|
||||||
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
|
||||||
if comment:
|
|
||||||
mail = db.session.query(TEmail).filter(TEmail.email == comment.email).first()
|
|
||||||
if mail:
|
|
||||||
setattr(mail, 'is_allowed', True)
|
|
||||||
setattr(mail, 'is_blocked', False)
|
|
||||||
else:
|
|
||||||
new_mail = {
|
|
||||||
"email": comment.email,
|
|
||||||
"is_allowed": True,
|
|
||||||
"is_blocked": False
|
|
||||||
}
|
|
||||||
db.session.add(TEmail(**new_mail))
|
|
||||||
|
|
||||||
# Allow all comments made by this mail address
|
|
||||||
all_comments = db.session.query(TComments).filter(TComments.email == comment.email).all()
|
|
||||||
if all_comments:
|
|
||||||
for comment in all_comments:
|
|
||||||
setattr(comment, 'is_published', True)
|
|
||||||
setattr(comment, 'is_spam', False)
|
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
url = re.match("^(.*[/])", request.referrer)[0]
|
|
||||||
export_location(location_id)
|
|
||||||
return redirect(f"{url}/{location_id}")
|
|
18
labertasche/blueprints/bp_dashboard/__init__.py
Normal file
18
labertasche/blueprints/bp_dashboard/__init__.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
# Blueprint
|
||||||
|
bp_dashboard = Blueprint("bp_dashboard", __name__, url_prefix='/dashboard')
|
||||||
|
|
||||||
|
# Files with routes
|
||||||
|
from .projects import dashboard_project_list
|
||||||
|
from .mail import dashboard_manage_mail
|
||||||
|
from .spam import dashboard_review_spam
|
||||||
|
from .comments import dashboard_manage_regular_comments
|
51
labertasche/blueprints/bp_dashboard/comments.py
Normal file
51
labertasche/blueprints/bp_dashboard/comments.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from . import bp_dashboard
|
||||||
|
from flask import render_template, request, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from labertasche.database import labertasche_db as db
|
||||||
|
from labertasche.models import TLocation, TComments
|
||||||
|
from labertasche.helper import export_location, get_id_from_project_name
|
||||||
|
|
||||||
|
|
||||||
|
@bp_dashboard.route('<project>/manage-comments/', methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def dashboard_manage_regular_comments(project: str):
|
||||||
|
location_id = 0
|
||||||
|
proj_id = get_id_from_project_name(project)
|
||||||
|
all_locations = db.session.query(TLocation).filter(TLocation.project_id == proj_id).all()
|
||||||
|
|
||||||
|
# Project does not exist, error code is used by Javascript, not Flask
|
||||||
|
if proj_id == -1:
|
||||||
|
return redirect(url_for("bp_dashboard.dashboard_project_list", error=404))
|
||||||
|
|
||||||
|
if request.args.get('location'):
|
||||||
|
location_id = request.args.get('location')
|
||||||
|
|
||||||
|
# no parameters found
|
||||||
|
if location_id is None:
|
||||||
|
return render_template("manage-comments.html", locations=all_locations,
|
||||||
|
selected=location_id, title="Manage Comments",
|
||||||
|
action="comments")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if int(location_id) >= 1:
|
||||||
|
spam_comments = db.session.query(TComments).filter(TComments.location_id == location_id) \
|
||||||
|
.filter(TComments.is_spam == False)
|
||||||
|
return render_template("manage-comments.html", locations=all_locations, selected=location_id,
|
||||||
|
spam_comments=spam_comments, project=project,
|
||||||
|
title="Manage Comments", action="comments")
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
export_location(location_id)
|
||||||
|
return render_template("manage-comments.html", locations=all_locations,
|
||||||
|
selected=location_id, project=project, title="Manage Comments",
|
||||||
|
action="comments")
|
||||||
|
|
32
labertasche/blueprints/bp_dashboard/mail.py
Normal file
32
labertasche/blueprints/bp_dashboard/mail.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from . import bp_dashboard
|
||||||
|
from flask import render_template, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from labertasche.database import labertasche_db as db
|
||||||
|
from labertasche.models import TEmail
|
||||||
|
from labertasche.helper import get_id_from_project_name
|
||||||
|
|
||||||
|
|
||||||
|
@bp_dashboard.route('<project>/manage-mail/')
|
||||||
|
@login_required
|
||||||
|
def dashboard_manage_mail(project: str):
|
||||||
|
"""
|
||||||
|
Shows the panel to manage email addresses
|
||||||
|
:param project: The project name to manage
|
||||||
|
:return: The template used to display the route
|
||||||
|
"""
|
||||||
|
proj_id = get_id_from_project_name(project)
|
||||||
|
|
||||||
|
# Project does not exist, error code is used by Javascript, not Flask
|
||||||
|
if proj_id == -1:
|
||||||
|
return redirect(url_for("bp_dashboard.dashboard_project_list", error=404))
|
||||||
|
|
||||||
|
addresses = db.session.query(TEmail).filter(TEmail.project_id == proj_id).all()
|
||||||
|
return render_template("manage-mail.html", addresses=addresses, project=project)
|
87
labertasche/blueprints/bp_dashboard/projects.py
Normal file
87
labertasche/blueprints/bp_dashboard/projects.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from . import bp_dashboard
|
||||||
|
from flask import render_template, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from sqlalchemy import func
|
||||||
|
from labertasche.database import labertasche_db as db
|
||||||
|
from labertasche.models import TComments, TProjects
|
||||||
|
from labertasche.helper import get_id_from_project_name, dates_of_the_week
|
||||||
|
|
||||||
|
|
||||||
|
@bp_dashboard.route("/")
|
||||||
|
@login_required
|
||||||
|
def dashboard_project_list():
|
||||||
|
"""
|
||||||
|
Displays an overview of all projects.
|
||||||
|
:return: The overview template.
|
||||||
|
"""
|
||||||
|
t_projects = db.session.query(TProjects).all()
|
||||||
|
projects = list()
|
||||||
|
for each in t_projects:
|
||||||
|
comments = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
|
||||||
|
.filter(TComments.is_published == True) \
|
||||||
|
.filter(TComments.is_spam == False).count()
|
||||||
|
unpub_comments = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
|
||||||
|
.filter(TComments.is_spam == False) \
|
||||||
|
.filter(TComments.is_published == False).count()
|
||||||
|
spam = db.session.query(TComments).filter(TComments.project_id == each.id_project) \
|
||||||
|
.filter(TComments.is_spam == True).count()
|
||||||
|
|
||||||
|
projects.append(dict({
|
||||||
|
"id_project": each.id_project,
|
||||||
|
"name": each.name,
|
||||||
|
"total_comments": comments,
|
||||||
|
"total_spam": spam,
|
||||||
|
"total_unpublished": unpub_comments
|
||||||
|
}))
|
||||||
|
return render_template('project-list.html', projects=projects)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_dashboard.route('/<project>/')
|
||||||
|
@login_required
|
||||||
|
def dashboard_project_stats(project: str):
|
||||||
|
"""
|
||||||
|
Displays the project dashboard
|
||||||
|
|
||||||
|
:param project: The project to show
|
||||||
|
:return: The template for the route
|
||||||
|
"""
|
||||||
|
proj_id = get_id_from_project_name(project)
|
||||||
|
|
||||||
|
# Project does not exist, error code is used by Javascript, not Flask
|
||||||
|
if proj_id == -1:
|
||||||
|
return redirect(url_for("bp_dashboard.dashboard_project_list", error=404))
|
||||||
|
|
||||||
|
dates = dates_of_the_week()
|
||||||
|
spam = list()
|
||||||
|
published = list()
|
||||||
|
unpublished = list()
|
||||||
|
for each in dates:
|
||||||
|
spam_comments = db.session.query(TComments).filter(TComments.project_id == proj_id) \
|
||||||
|
.filter(func.DATE(TComments.created_on) == each.date()) \
|
||||||
|
.filter(TComments.is_spam == True).all()
|
||||||
|
|
||||||
|
pub_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date()) \
|
||||||
|
.filter(TComments.project_id == proj_id) \
|
||||||
|
.filter(TComments.is_spam == False) \
|
||||||
|
.filter(TComments.is_published == True).all()
|
||||||
|
|
||||||
|
unpub_comments = db.session.query(TComments).filter(func.DATE(TComments.created_on) == each.date()) \
|
||||||
|
.filter(TComments.project_id == proj_id) \
|
||||||
|
.filter(TComments.is_spam == False) \
|
||||||
|
.filter(TComments.is_published == False).all()
|
||||||
|
|
||||||
|
published.append(len(pub_comments))
|
||||||
|
spam.append(len(spam_comments))
|
||||||
|
unpublished.append(len(unpub_comments))
|
||||||
|
|
||||||
|
return render_template('project-stats.html', dates=dates, spam=spam, project=project,
|
||||||
|
published=published, unpublished=unpublished)
|
||||||
|
|
56
labertasche/blueprints/bp_dashboard/spam.py
Normal file
56
labertasche/blueprints/bp_dashboard/spam.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from . import bp_dashboard
|
||||||
|
from flask import render_template, request, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from labertasche.database import labertasche_db as db
|
||||||
|
from labertasche.models import TLocation, TComments
|
||||||
|
from labertasche.helper import export_location, get_id_from_project_name
|
||||||
|
|
||||||
|
|
||||||
|
@bp_dashboard.route('<project>/manage-spam/', methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def dashboard_review_spam(project: str):
|
||||||
|
"""
|
||||||
|
Shows the manage spam template
|
||||||
|
:param project: The project used for displaying data
|
||||||
|
:return: The template to display for this rouet
|
||||||
|
"""
|
||||||
|
location_id = 0
|
||||||
|
proj_id = get_id_from_project_name(project)
|
||||||
|
all_locations = db.session.query(TLocation).filter(TLocation.project_id == proj_id).all()
|
||||||
|
|
||||||
|
# Project does not exist, error code is used by Javascript, not Flask
|
||||||
|
if proj_id == -1:
|
||||||
|
return redirect(url_for("bp_dashboard.dashboard_project_list", error=404))
|
||||||
|
|
||||||
|
if request.args.get('location'):
|
||||||
|
location_id = request.args.get('location')
|
||||||
|
|
||||||
|
# no parameters found
|
||||||
|
if location_id is None:
|
||||||
|
return render_template("manage-comments.html", locations=all_locations,
|
||||||
|
selected=location_id, title="Review Spam", action="spam")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if int(location_id) >= 1:
|
||||||
|
spam_comments = db.session.query(TComments) \
|
||||||
|
.filter(TComments.project_id == proj_id) \
|
||||||
|
.filter(TComments.location_id == location_id) \
|
||||||
|
.filter(TComments.is_spam == True)
|
||||||
|
|
||||||
|
return render_template("manage-comments.html", locations=all_locations, selected=location_id,
|
||||||
|
spam_comments=spam_comments, project=project, title="Review Spam", action="spam")
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
export_location(location_id)
|
||||||
|
return render_template("manage-comments.html", locations=all_locations,
|
||||||
|
selected=location_id, project=project, title="Review Spam", action="spam")
|
||||||
|
|
17
labertasche/blueprints/bp_jsconnector/__init__.py
Normal file
17
labertasche/blueprints/bp_jsconnector/__init__.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
# Blueprint
|
||||||
|
bp_jsconnector = Blueprint("bp_jsconnector", __name__, url_prefix='/api/')
|
||||||
|
|
||||||
|
from .projects import api_create_project, api_delete_project, api_edit_project_name
|
||||||
|
from .mail import api_toggle_email_reputation, api_reset_mail_reputation
|
||||||
|
from .comments import api_comment_allow_user, api_comment_allow_comment, \
|
||||||
|
api_comment_block_mail, api_comments_delete_comment
|
109
labertasche/blueprints/bp_jsconnector/comments.py
Normal file
109
labertasche/blueprints/bp_jsconnector/comments.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from . import bp_jsconnector
|
||||||
|
from flask import request, redirect
|
||||||
|
from flask_login import login_required
|
||||||
|
from labertasche.database import labertasche_db as db
|
||||||
|
from labertasche.helper import export_location
|
||||||
|
from labertasche.models import TComments, TEmail
|
||||||
|
|
||||||
|
# This file contains the routes for the manage comments menu point.
|
||||||
|
# They are called via GET
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route('/comment-delete/<int:comment_id>', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def api_comments_delete_comment(comment_id):
|
||||||
|
db.session.query(TComments).filter(TComments.comments_id == comment_id).delete()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Get location id from get params
|
||||||
|
location_id = request.args.get('location')
|
||||||
|
|
||||||
|
export_location(location_id)
|
||||||
|
return redirect(request.referrer)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route('/comment-allow/<int:comment_id>', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def api_comment_allow_comment(comment_id):
|
||||||
|
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
||||||
|
if comment:
|
||||||
|
setattr(comment, 'is_published', True)
|
||||||
|
setattr(comment, 'is_spam', False)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Get location id from get params
|
||||||
|
location_id = request.args.get('location')
|
||||||
|
|
||||||
|
export_location(location_id)
|
||||||
|
return redirect(request.referrer)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route('/comment-allow-user/<int:comment_id>', methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def api_comment_allow_user(comment_id):
|
||||||
|
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
||||||
|
if comment:
|
||||||
|
addr = db.session.query(TEmail).filter(TEmail.email == comment.email).first()
|
||||||
|
if addr:
|
||||||
|
setattr(addr, 'is_allowed', True)
|
||||||
|
setattr(addr, 'is_blocked', False)
|
||||||
|
else:
|
||||||
|
new_mail = {
|
||||||
|
"email": comment.email,
|
||||||
|
"is_allowed": True,
|
||||||
|
"is_blocked": False
|
||||||
|
}
|
||||||
|
db.session.add(TEmail(**new_mail))
|
||||||
|
|
||||||
|
# Allow all comments made by this mail address
|
||||||
|
all_comments = db.session.query(TComments).filter(TComments.email == comment.email).all()
|
||||||
|
if all_comments:
|
||||||
|
for comment in all_comments:
|
||||||
|
setattr(comment, 'is_published', True)
|
||||||
|
setattr(comment, 'is_spam', False)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Get location id from get params
|
||||||
|
location_id = request.args.get('location')
|
||||||
|
|
||||||
|
export_location(location_id)
|
||||||
|
return redirect(request.referrer)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route('/comment-block-mail/<int:comment_id>', methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def api_comment_block_mail(comment_id):
|
||||||
|
comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
||||||
|
if comment:
|
||||||
|
addr = db.session.query(TEmail).filter(TEmail.email == comment.email).first()
|
||||||
|
if addr:
|
||||||
|
setattr(addr, 'is_allowed', False)
|
||||||
|
setattr(addr, 'is_blocked', True)
|
||||||
|
else:
|
||||||
|
new_mail = {
|
||||||
|
"email": comment.first().email,
|
||||||
|
"is_allowed": False,
|
||||||
|
"is_blocked": True
|
||||||
|
}
|
||||||
|
db.session.add(TEmail(**new_mail))
|
||||||
|
|
||||||
|
# Delete all comments made by this mail address
|
||||||
|
db.session.query(TComments).filter(TComments.email == comment.email).delete()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Get location id from get params
|
||||||
|
location_id = request.args.get('location')
|
||||||
|
|
||||||
|
export_location(location_id)
|
||||||
|
return redirect(request.referrer)
|
||||||
|
|
||||||
|
|
36
labertasche/blueprints/bp_jsconnector/mail.py
Normal file
36
labertasche/blueprints/bp_jsconnector/mail.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from . import bp_jsconnector
|
||||||
|
from flask import request, redirect
|
||||||
|
from flask_login import login_required
|
||||||
|
from labertasche.database import labertasche_db as db
|
||||||
|
from labertasche.helper import get_id_from_project_name, export_location
|
||||||
|
from labertasche.models import TEmail, TComments
|
||||||
|
from re import match
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route('/mail-toggle-status/<int:id_email>')
|
||||||
|
@login_required
|
||||||
|
def api_toggle_email_reputation(id_email):
|
||||||
|
address = db.session.query(TEmail).filter(TEmail.id_email == id_email).first()
|
||||||
|
if address:
|
||||||
|
setattr(address, "is_allowed", (not address.is_allowed))
|
||||||
|
setattr(address, "is_blocked", (not address.is_blocked))
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(request.referrer)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route('/mail-reset-reputation/<int:id_email>')
|
||||||
|
@login_required
|
||||||
|
def api_reset_mail_reputation(id_email):
|
||||||
|
db.session.query(TEmail).filter(TEmail.id_email == id_email).delete()
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(request.referrer)
|
||||||
|
|
||||||
|
|
91
labertasche/blueprints/bp_jsconnector/projects.py
Normal file
91
labertasche/blueprints/bp_jsconnector/projects.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from . import bp_jsconnector
|
||||||
|
from flask import request, make_response, jsonify, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from labertasche.database import labertasche_db as db
|
||||||
|
from labertasche.helper import get_id_from_project_name
|
||||||
|
from labertasche.models import TProjects, TComments, TEmail, TLocation
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route("/project/new", methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def api_create_project():
|
||||||
|
"""
|
||||||
|
Called on dashboard project overview to create a new project.
|
||||||
|
|
||||||
|
:return: A string with an error code and 'ok' as string on success.
|
||||||
|
"""
|
||||||
|
# TODO: Project name exists?
|
||||||
|
name = request.json['name']
|
||||||
|
|
||||||
|
if not len(name):
|
||||||
|
return make_response(jsonify(status='too-short'), 400)
|
||||||
|
if not re.match('^\\w+$', name):
|
||||||
|
return make_response(jsonify(status='invalid-name'), 400)
|
||||||
|
|
||||||
|
proj = TProjects(name=name)
|
||||||
|
db.session.add(proj)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return make_response(jsonify(status='ok'), 200)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route('project/edit/<name>', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def api_edit_project_name(name: str):
|
||||||
|
"""
|
||||||
|
Renames the project.
|
||||||
|
:param name:
|
||||||
|
:return: A string with an error code and 'ok' as string on success.
|
||||||
|
"""
|
||||||
|
# TODO: Project name exists?
|
||||||
|
new_name = request.json['name']
|
||||||
|
|
||||||
|
if not len(new_name):
|
||||||
|
return make_response(jsonify(status='too-short'), 400)
|
||||||
|
if not re.match('^\\w+$', new_name):
|
||||||
|
return make_response(jsonify(status='invalid-name'), 400)
|
||||||
|
|
||||||
|
proj_id = get_id_from_project_name(name)
|
||||||
|
project = db.session.query(TProjects).filter(TProjects.id_project == proj_id)
|
||||||
|
setattr(project, 'name', new_name)
|
||||||
|
db.session.upate(project)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return make_response(jsonify(status='ok'), 200)
|
||||||
|
|
||||||
|
|
||||||
|
@bp_jsconnector.route('project/delete/<project>', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def api_delete_project(project: str):
|
||||||
|
"""
|
||||||
|
Deletes a project from the database and all associated data
|
||||||
|
|
||||||
|
:param project: The name of the project
|
||||||
|
:return: A string with an error code and 'ok' as string on success.
|
||||||
|
"""
|
||||||
|
# TODO: Javascript
|
||||||
|
proj_id = get_id_from_project_name(project)
|
||||||
|
if proj_id == -1:
|
||||||
|
return make_response(jsonify(status='not-found'), 400)
|
||||||
|
|
||||||
|
# noinspection PyBroadException
|
||||||
|
try:
|
||||||
|
db.session.query(TComments).filter(TComments.project_id == proj_id).delete()
|
||||||
|
db.session.query(TLocation).filter(TLocation.project_id == proj_id).delete()
|
||||||
|
db.session.query(TEmail).filter(TEmail.project_id == proj_id).delete()
|
||||||
|
db.session.query(TProjects).filter(TProjects.id_project == proj_id).delete()
|
||||||
|
db.session.commit()
|
||||||
|
db.session.flush()
|
||||||
|
except Exception:
|
||||||
|
return make_response(jsonify(status='exception'), 400)
|
||||||
|
|
||||||
|
return make_response(jsonify(status='ok'), 200)
|
71
labertasche/blueprints/bp_jsconnector/spam.py
Normal file
71
labertasche/blueprints/bp_jsconnector/spam.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# /**********************************************************************************
|
||||||
|
# * _author : Domeniko Gentner
|
||||||
|
# * _mail : code@tuxstash.de
|
||||||
|
# * _repo : https://git.tuxstash.de/gothseidank/labertasche
|
||||||
|
# * _license : This project is under MIT License
|
||||||
|
# *********************************************************************************/
|
||||||
|
from . import bp_jsconnector
|
||||||
|
from flask import request, redirect
|
||||||
|
from flask_login import login_required
|
||||||
|
from labertasche.database import labertasche_db as db
|
||||||
|
from labertasche.models import TComments, TEmail
|
||||||
|
from labertasche.helper import export_location
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
# @bp_jsconnector.route('/block-mail/<int:location_id>/<int:comment_id>', methods=["GET"])
|
||||||
|
# @login_required
|
||||||
|
# def dashboard_review_spam_block_mail(location_id, comment_id):
|
||||||
|
# comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
||||||
|
# if comment:
|
||||||
|
# addr = db.session.query(TEmail).filter(TEmail.email == comment.email).first()
|
||||||
|
# if addr:
|
||||||
|
# setattr(addr, 'is_allowed', False)
|
||||||
|
# setattr(addr, 'is_blocked', True)
|
||||||
|
# else:
|
||||||
|
# new_mail = {
|
||||||
|
# "email": comment.first().email,
|
||||||
|
# "is_allowed": False,
|
||||||
|
# "is_blocked": True
|
||||||
|
# }
|
||||||
|
# db.session.add(TEmail(**new_mail))
|
||||||
|
#
|
||||||
|
# # Delete all comments made by this mail address
|
||||||
|
# db.session.query(TComments).filter(TComments.email == comment.email).delete()
|
||||||
|
# db.session.commit()
|
||||||
|
#
|
||||||
|
# url = re.match("^(.*[/])", request.referrer)[0]
|
||||||
|
# export_location(location_id)
|
||||||
|
# return redirect(f"{url}/{location_id}")
|
||||||
|
#
|
||||||
|
|
||||||
|
# @bp_jsconnector.route('/allow-user/<int:location_id>/<int:comment_id>', methods=["GET"])
|
||||||
|
# @login_required
|
||||||
|
# def dashboard_review_spam_allow_user(location_id, comment_id):
|
||||||
|
# comment = db.session.query(TComments).filter(TComments.comments_id == comment_id).first()
|
||||||
|
# if comment:
|
||||||
|
# addr = db.session.query(TEmail).filter(TEmail.email == comment.email).first()
|
||||||
|
# if addr:
|
||||||
|
# setattr(addr, 'is_allowed', True)
|
||||||
|
# setattr(addr, 'is_blocked', False)
|
||||||
|
# else:
|
||||||
|
# new_mail = {
|
||||||
|
# "email": comment.email,
|
||||||
|
# "is_allowed": True,
|
||||||
|
# "is_blocked": False
|
||||||
|
# }
|
||||||
|
# db.session.add(TEmail(**new_mail))
|
||||||
|
#
|
||||||
|
# # Allow all comments made by this mail address
|
||||||
|
# all_comments = db.session.query(TComments).filter(TComments.email == comment.email).all()
|
||||||
|
# if all_comments:
|
||||||
|
# for comment in all_comments:
|
||||||
|
# setattr(comment, 'is_published', True)
|
||||||
|
# setattr(comment, 'is_spam', False)
|
||||||
|
#
|
||||||
|
# db.session.commit()
|
||||||
|
# url = re.match("^(.*[/])", request.referrer)[0]
|
||||||
|
# export_location(location_id)
|
||||||
|
# return redirect(f"{url}/{location_id}")
|
@ -19,7 +19,7 @@ bp_login = Blueprint("bp_login", __name__)
|
|||||||
@bp_login.route('/', methods=['GET'])
|
@bp_login.route('/', methods=['GET'])
|
||||||
def show_login():
|
def show_login():
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
return redirect(url_for('bp_dashboard.dashboard_index'))
|
return redirect(url_for('bp_dashboard.dashboard_project_list'))
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ def login():
|
|||||||
|
|
||||||
if check_auth(username, password):
|
if check_auth(username, password):
|
||||||
login_user(User(0), remember=True)
|
login_user(User(0), remember=True)
|
||||||
return redirect(url_for('bp_dashboard.dashboard_index'))
|
return redirect(url_for('bp_dashboard.dashboard_project_list'))
|
||||||
|
|
||||||
# Redirect get request to the login page
|
# Redirect get request to the login page
|
||||||
return redirect(url_for('bp_login.show_login'))
|
return redirect(url_for('bp_login.show_login'))
|
@ -201,4 +201,8 @@ def get_id_from_project_name(name: str) -> int:
|
|||||||
:return: the ID of the project
|
:return: the ID of the project
|
||||||
"""
|
"""
|
||||||
proj = db.session.query(TProjects).filter(TProjects.name == name).first()
|
proj = db.session.query(TProjects).filter(TProjects.name == name).first()
|
||||||
|
|
||||||
|
if proj is None:
|
||||||
|
return -1
|
||||||
|
|
||||||
return proj.id_project
|
return proj.id_project
|
||||||
|
@ -28,3 +28,4 @@ class Settings:
|
|||||||
self.gravatar = conf['gravatar']
|
self.gravatar = conf['gravatar']
|
||||||
self.addons = conf['addons']
|
self.addons = conf['addons']
|
||||||
self.smileys = conf['smileys']
|
self.smileys = conf['smileys']
|
||||||
|
self.projects = conf['projects']
|
||||||
|
@ -14,7 +14,7 @@ from sqlalchemy import event
|
|||||||
from sqlalchemy.engine import Engine
|
from sqlalchemy.engine import Engine
|
||||||
from labertasche.settings import Settings
|
from labertasche.settings import Settings
|
||||||
from labertasche.database import labertasche_db
|
from labertasche.database import labertasche_db
|
||||||
from labertasche.blueprints import bp_comments, bp_login, bp_dashboard
|
from labertasche.blueprints import bp_comments, bp_login, bp_dashboard, bp_jsconnector
|
||||||
from labertasche.models import TProjects
|
from labertasche.models import TProjects
|
||||||
from labertasche.helper import User
|
from labertasche.helper import User
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
@ -27,7 +27,9 @@ settings = Settings()
|
|||||||
# Flask App
|
# Flask App
|
||||||
laberflask = Flask(__name__)
|
laberflask = Flask(__name__)
|
||||||
laberflask.config.update(dict(
|
laberflask.config.update(dict(
|
||||||
SESSION_COOKIE_DOMAIN=settings.system['cookie-domain'],
|
SESSION_COOKIE_DOMAIN=settings.system['cookie_domain'],
|
||||||
|
SESSION_COOKIE_SECURE=settings.system['cookie_secure'],
|
||||||
|
REMEMBER_COOKIE_SECURE=settings.system['cookie_secure'],
|
||||||
DEBUG=settings.system['debug'],
|
DEBUG=settings.system['debug'],
|
||||||
SECRET_KEY=settings.system['secret'],
|
SECRET_KEY=settings.system['secret'],
|
||||||
TEMPLATES_AUTO_RELOAD=True,
|
TEMPLATES_AUTO_RELOAD=True,
|
||||||
@ -58,6 +60,7 @@ CORS(laberflask, resources={r"/comments": {"origins": settings.system['blog_url'
|
|||||||
laberflask.register_blueprint(bp_comments)
|
laberflask.register_blueprint(bp_comments)
|
||||||
laberflask.register_blueprint(bp_dashboard)
|
laberflask.register_blueprint(bp_dashboard)
|
||||||
laberflask.register_blueprint(bp_login)
|
laberflask.register_blueprint(bp_login)
|
||||||
|
laberflask.register_blueprint(bp_jsconnector)
|
||||||
|
|
||||||
# Disable Werkzeug's verbosity during development
|
# Disable Werkzeug's verbosity during development
|
||||||
log = logging.getLogger('werkzeug')
|
log = logging.getLogger('werkzeug')
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
// # * _license : This project is under MIT License
|
// # * _license : This project is under MIT License
|
||||||
// # *********************************************************************************/
|
// # *********************************************************************************/
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Called when search for mail addresses in manage mail
|
||||||
|
// ------------------------------------------------------
|
||||||
function dashboard_mailsearch(search_txt)
|
function dashboard_mailsearch(search_txt)
|
||||||
{
|
{
|
||||||
let el = document.getElementById('mail-table');
|
let el = document.getElementById('mail-table');
|
||||||
@ -20,19 +23,23 @@ function dashboard_mailsearch(search_txt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Called when a new project is created,
|
||||||
|
// posts it to the server
|
||||||
|
// ------------------------------------------------------
|
||||||
function new_project_save() {
|
function new_project_save() {
|
||||||
let modal_ok = document.getElementById('modal-ok');
|
let modal_ok = document.getElementById('modal-ok');
|
||||||
let modal_cancel = document.getElementById('modal-cancel');
|
let modal_cancel = document.getElementById('modal-cancel');
|
||||||
let short_help = document.getElementById('new-project-too-short');
|
let short_help_short = document.getElementById('new-project-too-short');
|
||||||
let short_help_invalid = document.getElementById('new-project-invalid-name');
|
let short_help_invalid = document.getElementById('new-project-invalid-name');
|
||||||
let name = document.getElementById('project-name').value
|
let name = document.getElementById('project-name').value
|
||||||
|
|
||||||
short_help.classList.add('is-hidden');
|
short_help_short.classList.add('is-hidden');
|
||||||
short_help_invalid.classList.add('is-hidden');
|
short_help_invalid.classList.add('is-hidden');
|
||||||
|
|
||||||
// Validate input
|
// Validate input
|
||||||
if (name.length === 0) {
|
if (name.length === 0) {
|
||||||
short_help.classList.remove('is-hidden');
|
short_help_short.classList.remove('is-hidden');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (/^\w+$/.test(name) === false){
|
if (/^\w+$/.test(name) === false){
|
||||||
@ -42,7 +49,7 @@ function new_project_save() {
|
|||||||
|
|
||||||
modal_ok.classList.add('is-loading');
|
modal_ok.classList.add('is-loading');
|
||||||
modal_cancel.classList.add('is-hidden');
|
modal_cancel.classList.add('is-hidden');
|
||||||
fetch(window.location.protocol + "//" + window.location.host + '/dashboard/project/new',
|
fetch(window.location.protocol + "//" + window.location.host + '/api/project/new',
|
||||||
{
|
{
|
||||||
mode: "cors",
|
mode: "cors",
|
||||||
headers: {
|
headers: {
|
||||||
@ -63,9 +70,10 @@ function new_project_save() {
|
|||||||
modal_cancel.classList.remove('is-hidden');
|
modal_cancel.classList.remove('is-hidden');
|
||||||
if (result === "ok"){
|
if (result === "ok"){
|
||||||
hide_modal('modal-new-project');
|
hide_modal('modal-new-project');
|
||||||
|
window.location.reload(true);
|
||||||
}
|
}
|
||||||
if (result === "too-short"){
|
if (result === "too-short"){
|
||||||
short_help.classList.remove('is-hidden');
|
short_help_short.classList.remove('is-hidden');
|
||||||
}
|
}
|
||||||
if (result === "invalid-name"){
|
if (result === "invalid-name"){
|
||||||
short_help_invalid.classList.remove('is-hidden');
|
short_help_invalid.classList.remove('is-hidden');
|
||||||
@ -76,12 +84,19 @@ function new_project_save() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Hides any modal
|
||||||
|
// ------------------------------------------------------
|
||||||
function hide_modal(id_name)
|
function hide_modal(id_name)
|
||||||
{
|
{
|
||||||
let el = document.getElementById(id_name);
|
let el = document.getElementById(id_name);
|
||||||
el.classList.remove("is-active");
|
el.classList.remove("is-active");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Shows any modal
|
||||||
|
// ------------------------------------------------------
|
||||||
function show_modal(id_name)
|
function show_modal(id_name)
|
||||||
{
|
{
|
||||||
let el = document.getElementById(id_name);
|
let el = document.getElementById(id_name);
|
||||||
|
@ -25,18 +25,20 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="navbar-start"></div>
|
<div class="navbar-start"></div>
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
<a class="navbar-item" href="/dashboard/review-spam/">
|
{% if project is defined %}
|
||||||
|
<a class="navbar-item" href="/dashboard/{{ project }}/manage-spam/">
|
||||||
<span class="icon"><i class="mdi mdi-24px mdi-space-invaders"></i></span>
|
<span class="icon"><i class="mdi mdi-24px mdi-space-invaders"></i></span>
|
||||||
REVIEW SPAM
|
MANAGE SPAM
|
||||||
</a>
|
</a>
|
||||||
<a class="navbar-item" href="/dashboard/manage-comments/">
|
<a class="navbar-item" href="/dashboard/{{ project }}/manage-comments/">
|
||||||
<span class="icon"><i class="mdi mdi-24px mdi-comment"></i></span>
|
<span class="icon"><i class="mdi mdi-24px mdi-comment"></i></span>
|
||||||
MANAGE COMMENTS
|
MANAGE COMMENTS
|
||||||
</a>
|
</a>
|
||||||
<a class="navbar-item" href="/dashboard/manage-mail">
|
<a class="navbar-item" href="/dashboard/{{ project }}/manage-mail">
|
||||||
<span class="icon"><i class="mdi mdi-24px mdi-email"></i></span>
|
<span class="icon"><i class="mdi mdi-24px mdi-email"></i></span>
|
||||||
MANAGE EMAIL ADDRESSES
|
MANAGE EMAIL ADDRESSES
|
||||||
</a>
|
</a>
|
||||||
|
{% endif %}
|
||||||
<a class="navbar-item" href="/logout/">
|
<a class="navbar-item" href="/logout/">
|
||||||
<span class="icon"><i class="mdi mdi-24px mdi-account-cancel"></i></span>
|
<span class="icon"><i class="mdi mdi-24px mdi-account-cancel"></i></span>
|
||||||
LOGOUT
|
LOGOUT
|
||||||
@ -49,5 +51,19 @@
|
|||||||
</section>
|
</section>
|
||||||
<script defer src="/static/js/dashboard.js"></script>
|
<script defer src="/static/js/dashboard.js"></script>
|
||||||
<script defer src="/static/js/Chart.bundle.min.js"></script>
|
<script defer src="/static/js/Chart.bundle.min.js"></script>
|
||||||
|
<script defer src="https://unpkg.com/@popperjs/core@2"></script>
|
||||||
|
<script defer src="https://unpkg.com/tippy.js@6"></script>
|
||||||
|
<script defer>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
if (urlParams.get("error") === "404"){
|
||||||
|
show_modal('modal-project-not-found');
|
||||||
|
}
|
||||||
|
{% block javascript %}
|
||||||
|
{% endblock %}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<section class="hero bg-yayellow is-fullheight">
|
<section class="hero bg-yayellow is-fullheight">
|
||||||
<div class="hero-head">
|
<div class="hero-head">
|
||||||
<a target="_blank" rel="noopener nofollow noreferrer"
|
<a target="_blank" href="https://github.com/domeniko-gentner/labertasche" rel="noopener nofollow noreferrer"
|
||||||
class="button is-info is-inverted is-medium">
|
class="button is-info is-inverted is-medium">
|
||||||
<span class="icon mr-2">
|
<span class="icon mr-2">
|
||||||
<i class="mdi mdi-36px mdi-github"></i>
|
<i class="mdi mdi-36px mdi-github"></i>
|
||||||
@ -30,10 +30,13 @@
|
|||||||
<div class="hero-body has-text-black">
|
<div class="hero-body has-text-black">
|
||||||
<div class="container has-text-centered">
|
<div class="container has-text-centered">
|
||||||
<p class="title">Labertasche Login</p>
|
<p class="title">Labertasche Login</p>
|
||||||
|
<!--suppress HtmlUnknownTarget -->
|
||||||
<form method="POST" action="/login">
|
<form method="POST" action="/login">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control is-expanded has-icons-left">
|
<div class="control is-expanded has-icons-left">
|
||||||
<input class="input" name="username" type="text">
|
<label for="username" class="help has-text-left has-text-black">
|
||||||
|
<input class="input" name="username" type="text" placeholder="username">
|
||||||
|
</label>
|
||||||
<span class="icon is-small is-left">
|
<span class="icon is-small is-left">
|
||||||
<span class="mdi mdi-24px mdi-shield-account"></span>
|
<span class="mdi mdi-24px mdi-shield-account"></span>
|
||||||
</span>
|
</span>
|
||||||
@ -41,7 +44,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control is-expanded has-icons-left">
|
<div class="control is-expanded has-icons-left">
|
||||||
<input class="input" name="password" type="password">
|
<label for="password">
|
||||||
|
<input class="input" name="password" type="password" placeholder="password">
|
||||||
|
</label>
|
||||||
<span class="icon is-small is-left">
|
<span class="icon is-small is-left">
|
||||||
<span class="mdi mdi-24px mdi-shield-key"></span>
|
<span class="mdi mdi-24px mdi-shield-key"></span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div style="min-height: 100vh;" class="container bg-deepmatte p-6 brdr-yayellow">
|
<div style="min-height: 100vh;" class="container bg-deepmatte p-6 brdr-yayellow">
|
||||||
<h1 class="title has-text-white has-text-centered">Manage Comments</h1>
|
<h1 class="title has-text-white has-text-centered">{{ title }}</h1>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<form method="post" action="/dashboard/manage-comments/">
|
<form method="GET" action="/dashboard/{{ project }}/manage-{{action}}/">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select name="selected_location" onchange="this.form.submit();">
|
<label for="location">
|
||||||
|
<select name="location" onchange="this.form.submit();">
|
||||||
<option value="-1">Select the article</option>
|
<option value="-1">Select the article</option>
|
||||||
{% for each in locations %}
|
{% for each in locations %}
|
||||||
{% if selected is defined %}
|
{% if selected is defined %}
|
||||||
@ -20,6 +21,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -48,22 +50,22 @@
|
|||||||
<nav class="level is-mobile">
|
<nav class="level is-mobile">
|
||||||
<a title="Delete this comment"
|
<a title="Delete this comment"
|
||||||
class="level-item"
|
class="level-item"
|
||||||
href="/dashboard/delete-comment/{{ selected }}/{{ comment.comments_id }}">
|
href="/api/comment-delete/{{ comment.comments_id }}?location={{ selected }}">
|
||||||
<span class="icon is-medium"><i class="mdi mdi-24px mdi-trash-can"></i></span>
|
<span class="icon is-medium"><i class="mdi mdi-24px mdi-trash-can"></i></span>
|
||||||
</a>
|
</a>
|
||||||
<a title="Delete comment and block mail address"
|
<a title="Delete comment and block mail address"
|
||||||
class="level-item"
|
class="level-item"
|
||||||
href="/dashboard/block-mail/{{ selected }}/{{ comment.comments_id }}">
|
href="/api/comment-block-mail/{{ comment.comments_id }}?location={{ selected }}">
|
||||||
<span class="icon is-medium"><i class="mdi mdi-24px mdi-shield-lock"></i></span>
|
<span class="icon is-medium"><i class="mdi mdi-24px mdi-shield-lock"></i></span>
|
||||||
</a>
|
</a>
|
||||||
<a title="Publish this comment, don't allow mail"
|
<a title="Publish this comment, don't allow mail"
|
||||||
class="level-item"
|
class="level-item"
|
||||||
href="/dashboard/allow-comment/{{ selected }}/{{ comment.comments_id }}">
|
href="/api/comment-allow/{{ comment.comments_id }}?location={{ selected }}">
|
||||||
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check"></i></span>
|
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check"></i></span>
|
||||||
</a>
|
</a>
|
||||||
<a title="Allow email to bypass spam detection and allow comment"
|
<a title="Allow email to bypass spam detection and allow comment"
|
||||||
class="level-item"
|
class="level-item"
|
||||||
href="/dashboard/allow-user/{{ selected }}/{{ comment.comments_id }}">
|
href="/api/comment-allow-user/{{ comment.comments_id }}?location={{ selected }}">
|
||||||
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check-all"></i></span>
|
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check-all"></i></span>
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
{% if each.is_blocked %}
|
{% if each.is_blocked %}
|
||||||
<a title="Email is currently blocked. Click to unblock."
|
<a title="Email is currently blocked. Click to unblock."
|
||||||
class="has-text-black"
|
class="has-text-black"
|
||||||
href="/dashboard/toggle-mail-allowed/{{ each.id_email }}">
|
href="/api/mail-toggle-status/{{ each.id_email }}">
|
||||||
<i class="mdi mdi-24px mdi-check"></i>
|
<i class="mdi mdi-24px mdi-check"></i>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -49,7 +49,7 @@
|
|||||||
<p class="has-text-centered">
|
<p class="has-text-centered">
|
||||||
<a title="Delete entry, this resets the reputation"
|
<a title="Delete entry, this resets the reputation"
|
||||||
class="has-text-danger-dark"
|
class="has-text-danger-dark"
|
||||||
href="/dashboard/reset-mail-reputation/{{ each.id_email }}">
|
href="/api/mail-reset-reputation/{{ each.id_email }}">
|
||||||
<i class="mdi mdi-24px mdi-trash-can"></i>
|
<i class="mdi mdi-24px mdi-trash-can"></i>
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
@ -38,18 +38,19 @@
|
|||||||
<div class="card-footer-item">
|
<div class="card-footer-item">
|
||||||
<a class="has-text-weight-bold has-text-black is-uppercase"
|
<a class="has-text-weight-bold has-text-black is-uppercase"
|
||||||
onclick="show_modal('modal-new-project');"
|
onclick="show_modal('modal-new-project');"
|
||||||
|
data-tippy-content="Create a new project"
|
||||||
href="#">NEW</a>
|
href="#">NEW</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% for project in projects %}
|
{% for each in projects %}
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
<div class="card my-shadow-subtle brdr-darkslate">
|
<div class="card my-shadow-subtle brdr-darkslate">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="card-header-title">
|
<div class="card-header-title">
|
||||||
<p class="is-size-4 is-uppercase">
|
<p class="is-size-4 is-uppercase">
|
||||||
{{ project['name'] }}
|
{{ each['name'] }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -58,33 +59,43 @@
|
|||||||
<div class="level-item has-text-centered">
|
<div class="level-item has-text-centered">
|
||||||
<div>
|
<div>
|
||||||
<p class="heading is-capitalized">Comments</p>
|
<p class="heading is-capitalized">Comments</p>
|
||||||
<p class="is-size-4 has-text-weight-bold has-text-white">{{ project['total_comments'] }}</p>
|
<p class="is-size-4 has-text-weight-bold has-text-white">{{ each['total_comments'] }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item has-text-centered">
|
<div class="level-item has-text-centered">
|
||||||
<div>
|
<div>
|
||||||
<p class="heading is-capitalized">Unpublished</p>
|
<p class="heading is-capitalized">Unpublished</p>
|
||||||
<p class="is-size-4 has-text-weight-bold has-text-white">{{ project['total_unpublished'] }}</p>
|
<p class="is-size-4 has-text-weight-bold has-text-white">{{ each['total_unpublished'] }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item has-text-centered">
|
<div class="level-item has-text-centered">
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<p class="heading is-capitalized">Spam</p>
|
<p class="heading is-capitalized">Spam</p>
|
||||||
<p class="is-size-4 has-text-weight-bold has-text-white">{{ project['total_spam'] }}</p>
|
<p class="is-size-4 has-text-weight-bold has-text-white">{{ each['total_spam'] }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<div class="card-footer-item">
|
<div class="card-footer-item has-background-danger-dark">
|
||||||
<a class="has-text-weight-bold has-text-black is-uppercase" href="#">EDIT</a>
|
<a class="has-text-weight-bold has-text-white is-uppercase"
|
||||||
</div>
|
data-tippy-content="Delete the project and all of its content"
|
||||||
<div class="card-footer-item">
|
href="{{ url_for('bp_jsconnector.api_delete_project', project=each['name']) }}">DELETE</a>
|
||||||
<a class="has-text-weight-bold has-text-black is-uppercase" href="#">DELETE</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer-item">
|
<div class="card-footer-item">
|
||||||
<a class="has-text-weight-bold has-text-black is-uppercase"
|
<a class="has-text-weight-bold has-text-black is-uppercase"
|
||||||
href="{{ url_for('bp_dashboard.dashboard_project_index', project=project['name'])}}">VIEW</a>
|
data-tippy-content="Edit the name of the project and it's properties"
|
||||||
|
href="#">EDIT</a>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<a class="has-text-weight-bold has-text-black is-uppercase"
|
||||||
|
data-tippy-content="Export all comments to Hugo.<br>This is normally not needed."
|
||||||
|
href="#">EXPORT</a>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<a class="has-text-weight-bold has-text-black is-uppercase"
|
||||||
|
data-tippy-content="Manage this project"
|
||||||
|
href="{{ url_for('bp_dashboard.dashboard_project_stats', project=each['name']) }}">VIEW</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -92,7 +103,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal is-active" id="modal-new-project">
|
<div class="modal" id="modal-new-project">
|
||||||
<div class="modal-background"></div>
|
<div class="modal-background"></div>
|
||||||
<div class="modal-card">
|
<div class="modal-card">
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
@ -117,4 +128,33 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal" id="modal-project-not-found">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head has-background-danger">
|
||||||
|
<p class="modal-card-title has-text-white">ERROR</p>
|
||||||
|
<button class="delete" aria-label="close"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body has-text-black">
|
||||||
|
The specified project was not found! Did you delete it? Try refreshing the page.<br>
|
||||||
|
If you believe this to be a bug, please report it
|
||||||
|
<a class="has-text-info"
|
||||||
|
href="https://github.com/domeniko-gentner/labertasche/issues"
|
||||||
|
target="_blank" rel="nofollow noopener norefferer">
|
||||||
|
here
|
||||||
|
</a>.
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<button id="modal-ok" onclick="hide_modal('modal-project-not-found')" class="button is-success">
|
||||||
|
OK
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block javascript %}
|
||||||
|
tippy('[data-tippy-content]', {
|
||||||
|
allowHTML: true,
|
||||||
|
delay: 500
|
||||||
|
});
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,78 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% block main %}
|
|
||||||
<div style="min-height: 100vh;" class="container bg-deepmatte p-6 brdr-yayellow">
|
|
||||||
<h1 class="title has-text-white has-text-centered">Review Spam</h1>
|
|
||||||
<div class="field">
|
|
||||||
<form method="post" action="/dashboard/review-spam/">
|
|
||||||
<div class="control">
|
|
||||||
<div class="select">
|
|
||||||
<select name="selected_location" onchange="this.form.submit();">
|
|
||||||
<option value="-1">Select the article</option>
|
|
||||||
{% for each in locations %}
|
|
||||||
{% if selected is defined %}
|
|
||||||
{% if selected | string() == each.id_location | string() %}
|
|
||||||
<option selected="selected" value="{{ each.id_location }}">{{ each.location }}</option>
|
|
||||||
{% else %}
|
|
||||||
<option value="{{ each.id_location }}">{{ each.location }}</option>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<option value="{{ each.id_location }}">{{ each.location }}</option>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{% if spam_comments is defined %}
|
|
||||||
{% for comment in spam_comments %}
|
|
||||||
<article>
|
|
||||||
<div class="media mb-5 brdr-yayellow my-shadow-subtle bg-compliment">
|
|
||||||
<figure class="media-left ml-0 mb-0">
|
|
||||||
<p class="image is-128x128">
|
|
||||||
<img alt="gravatar portrait" src="https://gravatar.com/avatar/{{comment.gravatar}}?size=128">
|
|
||||||
</p>
|
|
||||||
</figure>
|
|
||||||
<div class="media-content">
|
|
||||||
<div class="content mr-5 mt-2">
|
|
||||||
<a title="comment ID" id="comment_{{comment.comments_id}}" href="#comment_{{comment.comments_id}}">#{{comment.comments_id}}</a>
|
|
||||||
Posted by <span class="fg-yellow">{{comment.email}}</span>
|
|
||||||
on <small class="fg-yellow">{{comment.created_on}}</small>
|
|
||||||
spam score: <span title="The higher this is, the less likely it is spam" class="fg-yellow">{{ comment.spam_score }}</span>
|
|
||||||
<br><br>
|
|
||||||
<span class="mt-5">
|
|
||||||
{{comment.content}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<nav class="level is-mobile">
|
|
||||||
<a title="Delete this comment"
|
|
||||||
class="level-item"
|
|
||||||
href="/dashboard/delete-comment/{{ selected }}/{{ comment.comments_id }}">
|
|
||||||
<span class="icon is-medium"><i class="mdi mdi-24px mdi-trash-can"></i></span>
|
|
||||||
</a>
|
|
||||||
<a title="Delete comment and block mail address"
|
|
||||||
class="level-item"
|
|
||||||
href="/dashboard/block-mail/{{ selected }}/{{ comment.comments_id }}">
|
|
||||||
<span class="icon is-medium"><i class="mdi mdi-24px mdi-shield-lock"></i></span>
|
|
||||||
</a>
|
|
||||||
<a title="Publish this comment, don't allow mail"
|
|
||||||
class="level-item"
|
|
||||||
href="/dashboard/allow-comment/{{ selected }}/{{ comment.comments_id }}">
|
|
||||||
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check"></i></span>
|
|
||||||
</a>
|
|
||||||
<a title="Allow email to bypass spam detection and allow comment"
|
|
||||||
class="level-item"
|
|
||||||
href="/dashboard/allow-user/{{ selected }}/{{ comment.comments_id }}">
|
|
||||||
<span class="icon is-medium"><i class="mdi mdi-24px mdi-check-all"></i></span>
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
Loading…
x
Reference in New Issue
Block a user