Projects Update 2020-12-26
* The new comment path now sends `sendotp` in json, which reflects the project setting * Removed unneccessary export_location in bp_dashboard/comments.py * Stored Mail addresses in the block and allow list are considered global and not part of projects. Trust is universal. * added missing cross_origin decorators * added redirect to dbv2 update template * now using a jinja2 template for the html mail * location and project_id are now a composite unique in t_location * corrected total comments graph
This commit is contained in:
parent
1071acfc4c
commit
01d20f4641
@ -9,4 +9,4 @@
|
|||||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||||
*/@font-face{font-family:'Font Awesome 5 Brands';font-style:normal;font-weight:400;font-display:swap;src:url("/css/fa-brands-400.eot");src:url("/css/fa-brands-400.eot?#iefix") format("embedded-opentype"),url("/css/fa-brands-400.woff2") format("woff2"),url("/css/fa-brands-400.woff") format("woff"),url("/css/fa-brands-400.ttf") format("truetype"),url("/css/fa-brands-400.svg#fontawesome") format("svg")}.fab{font-family:'Font Awesome 5 Brands';font-weight:400}
|
*/@font-face{font-family:'Font Awesome 5 Brands';font-style:normal;font-weight:400;font-display:swap;src:url("/css/fa-brands-400.eot");src:url("/css/fa-brands-400.eot?#iefix") format("embedded-opentype"),url("/css/fa-brands-400.woff2") format("woff2"),url("/css/fa-brands-400.woff") format("woff"),url("/css/fa-brands-400.ttf") format("truetype"),url("/css/fa-brands-400.svg#fontawesome") format("svg")}.fab{font-family:'Font Awesome 5 Brands';font-weight:400}
|
||||||
|
|
||||||
/*# sourceMappingURL=tuxstash.css.map */@font-face{font-family:fira code;font-style:normal;font-weight:500;font-display:swap;font-variant:common-ligatures;src:local(''),url(fira-code-v9-latin-500.woff2)format('woff2')}@font-face{font-family:open sans;font-style:normal;font-weight:400;font-display:swap;src:local('Open Sans Regular'),local('OpenSans-Regular'),url(open-sans-v18-latin-regular.woff2)format('woff2')}figcaption{background-color:#feda6a;color:#000;border-top:2px #000 solid;padding-top:5px;padding-bottom:5px}pre,code{background:#2d2d2d;color:#fff;border:none;font-family:fira code,monospace!important;font-weight:500}.vert-middle{vertical-align:middle}.bg-yayellow{background-color:#feda6a}.bg-deepmatte{background-color:#393f4d}.bg-darkslate{background-color:#1d1e22}.brdr-yayellow{border:2px solid #feda6a}.bg-compliment{background-color:#384667}.title-image-container{content:"";height:0;overflow:hidden;padding-top:calc(((191/2)/781 )* 100%);position:relative;background-color:#feda6a}.title-image{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;object-position:top;border:.1rem solid #feda6a}.table-center{width:60%;margin:0 auto}.fg-red{color:red}.fg-green{color:green}.fg-yellow{color:#feda6a}.my-shadow{-webkit-box-shadow:6px 6px 15px 2px rgba(0,0,0,.75);-moz-box-shadow:6px 6px 15px 2px rgba(0,0,0,.75);box-shadow:6px 6px 15px 2px rgba(0,0,0,.75)}.my-shadow-subtle{-webkit-box-shadow:2px 2px 7px 2px rgba(0,0,0,.75);-moz-box-shadow:2px 2px 7px 2px rgba(0,0,0,.75);box-shadow:2px 2px 7px 2px rgba(0,0,0,.75)}.ribbon{width:100%;height:auto;margin-left:-10px;margin-right:-10px;background:#feda6a}#cookie-bar{position:fixed;z-index:999;bottom:0;left:0;width:100%;height:auto;background-color:rgba(0,0,0,.85);color:#fff}.twitter-hr{height:0;opacity:.75;margin-top:1vh;margin-bottom:1vh;border:1px solid #feda6a}.border-top{border-top:2px solid #1d1e22}.chroma{color:#d0d0d0;background-color:#202020}.chroma .x{}.chroma .err{color:#a61717;background-color:#e3d2d2}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:#ffc}.chroma .lnt{margin-right:.4em;padding:0 .4em;color:#686868}.chroma .ln{margin-right:.4em;padding:0 .4em;color:#686868}.chroma .k{color:#6ab825;font-weight:700}.chroma .kc{color:#6ab825;font-weight:700}.chroma .kd{color:#6ab825;font-weight:700}.chroma .kn{color:#6ab825;font-weight:700}.chroma .kp{color:#6ab825}.chroma .kr{color:#6ab825;font-weight:700}.chroma .kt{color:#6ab825;font-weight:700}.chroma .n{}.chroma .na{color:#bbb}.chroma .nb{color:#24909d}.chroma .bp{}.chroma .nc{color:#447fcf;text-decoration:underline}.chroma .no{color:#40ffff}.chroma .nd{color:orange}.chroma .ni{}.chroma .ne{color:#bbb}.chroma .nf{color:#447fcf}.chroma .fm{}.chroma .nl{}.chroma .nn{color:#447fcf;text-decoration:underline}.chroma .nx{}.chroma .py{}.chroma .nt{color:#6ab825;font-weight:700}.chroma .nv{color:#40ffff}.chroma .vc{}.chroma .vg{}.chroma .vi{}.chroma .vm{}.chroma .l{}.chroma .ld{}.chroma .s{color:#ed9d13}.chroma .sa{color:#ed9d13}.chroma .sb{color:#ed9d13}.chroma .sc{color:#ed9d13}.chroma .dl{color:#ed9d13}.chroma .sd{color:#ed9d13}.chroma .s2{color:#ed9d13}.chroma .se{color:#ed9d13}.chroma .sh{color:#ed9d13}.chroma .si{color:#ed9d13}.chroma .sx{color:orange}.chroma .sr{color:#ed9d13}.chroma .s1{color:#ed9d13}.chroma .ss{color:#ed9d13}.chroma .m{color:#3677a9}.chroma .mb{color:#3677a9}.chroma .mf{color:#3677a9}.chroma .mh{color:#3677a9}.chroma .mi{color:#3677a9}.chroma .il{color:#3677a9}.chroma .mo{color:#3677a9}.chroma .o{}.chroma .ow{color:#6ab825;font-weight:700}.chroma .p{}.chroma .c{color:#999;font-style:italic}.chroma .ch{color:#999;font-style:italic}.chroma .cm{color:#999;font-style:italic}.chroma .c1{color:#999;font-style:italic}.chroma .cs{color:#e50808;background-color:#520000;font-weight:700}.chroma .cp{color:#cd2828;font-weight:700}.chroma .cpf{color:#cd2828;font-weight:700}.chroma .g{}.chroma .gd{color:#d22323}.chroma .ge{font-style:italic}.chroma .gr{color:#d22323}.chroma .gh{color:#fff;font-weight:700}.chroma .gi{color:#589819}.chroma .go{color:#ccc}.chroma .gp{color:#aaa}.chroma .gs{font-weight:700}.chroma .gu{color:#fff;text-decoration:underline}.chroma .gt{color:#d22323}.chroma .gl{text-decoration:underline}.chroma .w{color:#666}.margin-left-128{margin-left:128px;}
|
/*# sourceMappingURL=tuxstash.css.map */@font-face{font-family:open sans;font-style:normal;font-weight:400;font-display:swap;src:local('Open Sans Regular'),local('OpenSans-Regular'),url(open-sans-v18-latin-regular.woff2)format('woff2')}figcaption{background-color:#feda6a;color:#000;border-top:2px #000 solid;padding-top:5px;padding-bottom:5px}pre,code{background:#2d2d2d;color:#fff;border:none;font-family:fira code,monospace!important;font-weight:500}.vert-middle{vertical-align:middle}.bg-yayellow{background-color:#feda6a}.bg-deepmatte{background-color:#393f4d}.bg-darkslate{background-color:#1d1e22}.brdr-yayellow{border:2px solid #feda6a}.bg-compliment{background-color:#384667}.title-image-container{content:"";height:0;overflow:hidden;padding-top:calc(((191/2)/781 )* 100%);position:relative;background-color:#feda6a}.title-image{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;object-position:top;border:.1rem solid #feda6a}.table-center{width:60%;margin:0 auto}.fg-red{color:red}.fg-green{color:green}.fg-yellow{color:#feda6a}.my-shadow{-webkit-box-shadow:6px 6px 15px 2px rgba(0,0,0,.75);-moz-box-shadow:6px 6px 15px 2px rgba(0,0,0,.75);box-shadow:6px 6px 15px 2px rgba(0,0,0,.75)}.my-shadow-subtle{-webkit-box-shadow:2px 2px 7px 2px rgba(0,0,0,.75);-moz-box-shadow:2px 2px 7px 2px rgba(0,0,0,.75);box-shadow:2px 2px 7px 2px rgba(0,0,0,.75)}.ribbon{width:100%;height:auto;margin-left:-10px;margin-right:-10px;background:#feda6a}#cookie-bar{position:fixed;z-index:999;bottom:0;left:0;width:100%;height:auto;background-color:rgba(0,0,0,.85);color:#fff}.twitter-hr{height:0;opacity:.75;margin-top:1vh;margin-bottom:1vh;border:1px solid #feda6a}.border-top{border-top:2px solid #1d1e22}.chroma{color:#d0d0d0;background-color:#202020}.chroma .x{}.chroma .err{color:#a61717;background-color:#e3d2d2}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:#ffc}.chroma .lnt{margin-right:.4em;padding:0 .4em;color:#686868}.chroma .ln{margin-right:.4em;padding:0 .4em;color:#686868}.chroma .k{color:#6ab825;font-weight:700}.chroma .kc{color:#6ab825;font-weight:700}.chroma .kd{color:#6ab825;font-weight:700}.chroma .kn{color:#6ab825;font-weight:700}.chroma .kp{color:#6ab825}.chroma .kr{color:#6ab825;font-weight:700}.chroma .kt{color:#6ab825;font-weight:700}.chroma .n{}.chroma .na{color:#bbb}.chroma .nb{color:#24909d}.chroma .bp{}.chroma .nc{color:#447fcf;text-decoration:underline}.chroma .no{color:#40ffff}.chroma .nd{color:orange}.chroma .ni{}.chroma .ne{color:#bbb}.chroma .nf{color:#447fcf}.chroma .fm{}.chroma .nl{}.chroma .nn{color:#447fcf;text-decoration:underline}.chroma .nx{}.chroma .py{}.chroma .nt{color:#6ab825;font-weight:700}.chroma .nv{color:#40ffff}.chroma .vc{}.chroma .vg{}.chroma .vi{}.chroma .vm{}.chroma .l{}.chroma .ld{}.chroma .s{color:#ed9d13}.chroma .sa{color:#ed9d13}.chroma .sb{color:#ed9d13}.chroma .sc{color:#ed9d13}.chroma .dl{color:#ed9d13}.chroma .sd{color:#ed9d13}.chroma .s2{color:#ed9d13}.chroma .se{color:#ed9d13}.chroma .sh{color:#ed9d13}.chroma .si{color:#ed9d13}.chroma .sx{color:orange}.chroma .sr{color:#ed9d13}.chroma .s1{color:#ed9d13}.chroma .ss{color:#ed9d13}.chroma .m{color:#3677a9}.chroma .mb{color:#3677a9}.chroma .mf{color:#3677a9}.chroma .mh{color:#3677a9}.chroma .mi{color:#3677a9}.chroma .il{color:#3677a9}.chroma .mo{color:#3677a9}.chroma .o{}.chroma .ow{color:#6ab825;font-weight:700}.chroma .p{}.chroma .c{color:#999;font-style:italic}.chroma .ch{color:#999;font-style:italic}.chroma .cm{color:#999;font-style:italic}.chroma .c1{color:#999;font-style:italic}.chroma .cs{color:#e50808;background-color:#520000;font-weight:700}.chroma .cp{color:#cd2828;font-weight:700}.chroma .cpf{color:#cd2828;font-weight:700}.chroma .g{}.chroma .gd{color:#d22323}.chroma .ge{font-style:italic}.chroma .gr{color:#d22323}.chroma .gh{color:#fff;font-weight:700}.chroma .gi{color:#589819}.chroma .go{color:#ccc}.chroma .gp{color:#aaa}.chroma .gs{font-weight:700}.chroma .gu{color:#fff;text-decoration:underline}.chroma .gt{color:#d22323}.chroma .gl{text-decoration:underline}.chroma .w{color:#666}.margin-left-128{margin-left:128px;}
|
||||||
|
@ -41,7 +41,7 @@ function labertasche_validate_mail()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function labertasche_modal_hide()
|
function labertasche_modal_hide(url=null)
|
||||||
{
|
{
|
||||||
let modal = document.getElementById('labertasche-modal');
|
let modal = document.getElementById('labertasche-modal');
|
||||||
if (modal != null){
|
if (modal != null){
|
||||||
@ -49,7 +49,12 @@ function labertasche_modal_hide()
|
|||||||
modal.classList.remove('is-active');
|
modal.classList.remove('is-active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!modal.dataset.url) {
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
window.location = modal.dataset.url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function labertasche_comment_not_found()
|
function labertasche_comment_not_found()
|
||||||
@ -57,6 +62,7 @@ function labertasche_comment_not_found()
|
|||||||
let modal = document.getElementById('labertasche-modal');
|
let modal = document.getElementById('labertasche-modal');
|
||||||
let modal_text = document.getElementById('labertasche-modal-text');
|
let modal_text = document.getElementById('labertasche-modal-text');
|
||||||
modal_text.innerText = "The link you followed was not valid. It either doesn't exist or was already used.";
|
modal_text.innerText = "The link you followed was not valid. It either doesn't exist or was already used.";
|
||||||
|
modal.setAttribute('data-url', window.location.protocol + "//" + window.location.host)
|
||||||
modal.classList.add('is-active');
|
modal.classList.add('is-active');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +71,7 @@ function labertasche_comment_deleted()
|
|||||||
let modal = document.getElementById('labertasche-modal');
|
let modal = document.getElementById('labertasche-modal');
|
||||||
let modal_text = document.getElementById('labertasche-modal-text');
|
let modal_text = document.getElementById('labertasche-modal-text');
|
||||||
modal_text.innerText = "Your comment has been deleted. Thank you for being here.";
|
modal_text.innerText = "Your comment has been deleted. Thank you for being here.";
|
||||||
|
modal.setAttribute('data-url', window.location.protocol + "//" + window.location.host)
|
||||||
modal.classList.add('is-active');
|
modal.classList.add('is-active');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,16 +19,25 @@ from labertasche.models import TComments, TLocation, TEmail, TProjects
|
|||||||
from labertasche.settings import Smileys
|
from labertasche.settings import Smileys
|
||||||
from secrets import compare_digest
|
from secrets import compare_digest
|
||||||
|
|
||||||
|
|
||||||
# Blueprint
|
# Blueprint
|
||||||
bp_comments = Blueprint("bp_comments", __name__, url_prefix='/comments')
|
bp_comments = Blueprint("bp_comments", __name__, url_prefix='/comments')
|
||||||
|
|
||||||
|
|
||||||
# Route for adding new comments
|
# Route for adding new comments
|
||||||
@bp_comments.route("/<name>/new", methods=['POST'])
|
@bp_comments.route("/<name>/new", methods=['POST'])
|
||||||
@cross_origin()
|
|
||||||
def check_and_insert_new_comment(name):
|
def check_and_insert_new_comment(name):
|
||||||
if request.method == 'POST':
|
|
||||||
|
# Get project
|
||||||
|
project = db.session.query(TProjects).filter(TProjects.name == name).first()
|
||||||
|
|
||||||
|
# Check refferer, this is not bullet proof
|
||||||
|
if not compare_digest(request.origin, project.blogurl):
|
||||||
|
return make_response(jsonify(status="not-allowed"), 403)
|
||||||
|
|
||||||
|
if not project:
|
||||||
|
return make_response(jsonify(status="post-project-not-found"), 400)
|
||||||
|
|
||||||
|
if compare_digest(request.method, "POST"):
|
||||||
smileys = Smileys()
|
smileys = Smileys()
|
||||||
sender = mail()
|
sender = mail()
|
||||||
|
|
||||||
@ -57,11 +66,6 @@ def check_and_insert_new_comment(name):
|
|||||||
content = re.sub(tags, '', new_comment['content']).strip()
|
content = re.sub(tags, '', new_comment['content']).strip()
|
||||||
content = re.sub(special, '', content).strip()
|
content = re.sub(special, '', content).strip()
|
||||||
|
|
||||||
# Get project
|
|
||||||
project = db.session.query(TProjects).filter(TProjects.name == name).first()
|
|
||||||
if not project:
|
|
||||||
return make_response(jsonify(status="post-project-not-found"), 400)
|
|
||||||
|
|
||||||
# Convert smileys if enabled
|
# Convert smileys if enabled
|
||||||
if project.addon_smileys:
|
if project.addon_smileys:
|
||||||
for key, value in smileys.smileys.items():
|
for key, value in smileys.smileys.items():
|
||||||
@ -168,11 +172,13 @@ def check_and_insert_new_comment(name):
|
|||||||
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)
|
||||||
return make_response(jsonify(status="post-success", comment_id=t_comment.comments_id), 200)
|
return make_response(jsonify(status="post-success",
|
||||||
|
comment_id=t_comment.comments_id,
|
||||||
|
sendotp=project.sendotp), 200)
|
||||||
|
|
||||||
|
|
||||||
# Route for confirming comments
|
# Route for confirming comments
|
||||||
@bp_comments.route("/confirm/<name>/<email_hash>", methods=['GET'])
|
@bp_comments.route("/<name>/confirm/<email_hash>", methods=['GET'])
|
||||||
@cross_origin()
|
@cross_origin()
|
||||||
def check_confirmation_link(name, email_hash):
|
def check_confirmation_link(name, email_hash):
|
||||||
comment = db.session.query(TComments).filter(TComments.confirmation == email_hash).first()
|
comment = db.session.query(TComments).filter(TComments.confirmation == email_hash).first()
|
||||||
@ -193,7 +199,7 @@ def check_confirmation_link(name, email_hash):
|
|||||||
|
|
||||||
|
|
||||||
# Route for deleting comments
|
# Route for deleting comments
|
||||||
@bp_comments.route("/delete/<name>/<email_hash>", methods=['GET'])
|
@bp_comments.route("<name>/delete/<email_hash>", methods=['GET'])
|
||||||
@cross_origin()
|
@cross_origin()
|
||||||
def check_deletion_link(name, email_hash):
|
def check_deletion_link(name, email_hash):
|
||||||
project = db.session.query(TProjects).filter(TProjects.name == name).first()
|
project = db.session.query(TProjects).filter(TProjects.name == name).first()
|
||||||
@ -202,7 +208,8 @@ def check_deletion_link(name, email_hash):
|
|||||||
if comment:
|
if comment:
|
||||||
location = db.session.query(TLocation).filter(TLocation.id_location == comment.location_id).first()
|
location = db.session.query(TLocation).filter(TLocation.id_location == comment.location_id).first()
|
||||||
if compare_digest(comment.deletion, email_hash):
|
if compare_digest(comment.deletion, email_hash):
|
||||||
comment.delete()
|
print("True")
|
||||||
|
db.session.delete(comment)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
url = f"{project.blogurl}?deleted=true"
|
url = f"{project.blogurl}?deleted=true"
|
||||||
export_location(location.id_location)
|
export_location(location.id_location)
|
||||||
|
@ -16,12 +16,28 @@ from labertasche.helper import export_location, get_id_from_project_name
|
|||||||
|
|
||||||
|
|
||||||
@cross_origin
|
@cross_origin
|
||||||
@bp_dashboard.route('<project>/manage-comments/', methods=["GET"])
|
@bp_dashboard.route('/<project>/manage-comments/', methods=["GET"])
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard_manage_regular_comments(project: str):
|
def dashboard_manage_regular_comments(project: str):
|
||||||
location_id = 0
|
location_id = 0
|
||||||
proj_id = get_id_from_project_name(project)
|
proj_id = get_id_from_project_name(project)
|
||||||
all_locations = db.session.query(TLocation).filter(TLocation.project_id == proj_id).all()
|
all_locations = db.session.query(TLocation)\
|
||||||
|
.filter(TLocation.project_id == proj_id)\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
# Check if there is a comment, otherwise don't show on management page
|
||||||
|
# This can happen when the last comment was deleted, the location
|
||||||
|
# won't be removed.
|
||||||
|
tmp_list = list()
|
||||||
|
for each in all_locations:
|
||||||
|
comment_count = db.session.query(TComments.comments_id)\
|
||||||
|
.filter(TComments.location_id == each.id_location)\
|
||||||
|
.filter(TComments.is_spam == False) \
|
||||||
|
.count()
|
||||||
|
if comment_count > 0:
|
||||||
|
tmp_list.append(each)
|
||||||
|
|
||||||
|
all_locations = tmp_list
|
||||||
|
|
||||||
# Project does not exist, error code is used by Javascript, not Flask
|
# Project does not exist, error code is used by Javascript, not Flask
|
||||||
if proj_id == -1:
|
if proj_id == -1:
|
||||||
@ -46,7 +62,6 @@ def dashboard_manage_regular_comments(project: str):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
export_location(location_id)
|
|
||||||
return render_template("manage-comments.html", locations=all_locations,
|
return render_template("manage-comments.html", locations=all_locations,
|
||||||
selected=location_id, project=project, title="Manage Comments",
|
selected=location_id, project=project, title="Manage Comments",
|
||||||
action="comments")
|
action="comments")
|
||||||
|
@ -7,28 +7,24 @@
|
|||||||
# * _license : This project is under MIT License
|
# * _license : This project is under MIT License
|
||||||
# *********************************************************************************/
|
# *********************************************************************************/
|
||||||
from . import bp_dashboard
|
from . import bp_dashboard
|
||||||
from flask import render_template, redirect, url_for
|
from flask import render_template
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
from flask_cors import cross_origin
|
from flask_cors import cross_origin
|
||||||
from labertasche.database import labertasche_db as db
|
from labertasche.database import labertasche_db as db
|
||||||
from labertasche.models import TEmail
|
from labertasche.models import TEmail
|
||||||
from labertasche.helper import get_id_from_project_name
|
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
@cross_origin()
|
@cross_origin()
|
||||||
@bp_dashboard.route('<project>/manage-mail/')
|
@bp_dashboard.route('/manage-mail/')
|
||||||
|
@bp_dashboard.route('/<project>/manage-mail/')
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard_manage_mail(project: str):
|
def dashboard_manage_mail(project: str = None):
|
||||||
"""
|
"""
|
||||||
Shows the panel to manage email addresses
|
Shows the panel to manage email addresses
|
||||||
:param project: The project name to manage
|
:param project: Not used
|
||||||
:return: The template used to display the route
|
: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
|
addresses = db.session.query(TEmail).all()
|
||||||
if proj_id == -1:
|
return render_template("manage-mail.html", addresses=addresses)
|
||||||
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)
|
|
||||||
|
@ -7,8 +7,9 @@
|
|||||||
# * _license : This project is under MIT License
|
# * _license : This project is under MIT License
|
||||||
# *********************************************************************************/
|
# *********************************************************************************/
|
||||||
from . import bp_dashboard
|
from . import bp_dashboard
|
||||||
from flask import render_template, redirect, url_for
|
from flask import render_template, redirect, url_for, request
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
from flask_cors import cross_origin
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from sqlalchemy.exc import OperationalError
|
from sqlalchemy.exc import OperationalError
|
||||||
from labertasche.database import labertasche_db as db
|
from labertasche.database import labertasche_db as db
|
||||||
@ -16,6 +17,7 @@ from labertasche.models import TComments, TProjects
|
|||||||
from labertasche.helper import get_id_from_project_name, dates_of_the_week
|
from labertasche.helper import get_id_from_project_name, dates_of_the_week
|
||||||
|
|
||||||
|
|
||||||
|
@cross_origin
|
||||||
@bp_dashboard.route("/")
|
@bp_dashboard.route("/")
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard_project_list():
|
def dashboard_project_list():
|
||||||
@ -50,6 +52,7 @@ def dashboard_project_list():
|
|||||||
return render_template('project-list.html', projects=projects)
|
return render_template('project-list.html', projects=projects)
|
||||||
|
|
||||||
|
|
||||||
|
@cross_origin
|
||||||
@bp_dashboard.route('/<project>/')
|
@bp_dashboard.route('/<project>/')
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard_project_stats(project: str):
|
def dashboard_project_stats(project: str):
|
||||||
@ -65,6 +68,17 @@ def dashboard_project_stats(project: str):
|
|||||||
if proj_id == -1:
|
if proj_id == -1:
|
||||||
return redirect(url_for("bp_dashboard.dashboard_project_list", error=404))
|
return redirect(url_for("bp_dashboard.dashboard_project_list", error=404))
|
||||||
|
|
||||||
|
# Total graphs
|
||||||
|
total_spam = db.session.query(TComments).filter(TComments.is_spam == True).count()
|
||||||
|
|
||||||
|
total_comments = db.session.query(TComments) \
|
||||||
|
.filter(TComments.is_spam == False)\
|
||||||
|
.filter(TComments.is_published == True).count()
|
||||||
|
|
||||||
|
total_unpublished = db.session.query(TComments).filter(TComments.is_spam == False)\
|
||||||
|
.filter(TComments.is_published == False).count()
|
||||||
|
|
||||||
|
# 7 day graph
|
||||||
dates = dates_of_the_week()
|
dates = dates_of_the_week()
|
||||||
spam = list()
|
spam = list()
|
||||||
published = list()
|
published = list()
|
||||||
@ -89,5 +103,7 @@ def dashboard_project_stats(project: str):
|
|||||||
unpublished.append(len(unpub_comments))
|
unpublished.append(len(unpub_comments))
|
||||||
|
|
||||||
return render_template('project-stats.html', dates=dates, spam=spam, project=project,
|
return render_template('project-stats.html', dates=dates, spam=spam, project=project,
|
||||||
published=published, unpublished=unpublished)
|
published=published, unpublished=unpublished,
|
||||||
|
total_spam=total_spam, total_comments=total_comments,
|
||||||
|
total_unpublished=total_unpublished)
|
||||||
|
|
||||||
|
@ -28,6 +28,20 @@ def dashboard_review_spam(project: str):
|
|||||||
proj_id = get_id_from_project_name(project)
|
proj_id = get_id_from_project_name(project)
|
||||||
all_locations = db.session.query(TLocation).filter(TLocation.project_id == proj_id).all()
|
all_locations = db.session.query(TLocation).filter(TLocation.project_id == proj_id).all()
|
||||||
|
|
||||||
|
# Check if there is a comment, otherwise don't show on management page
|
||||||
|
# This can happen when the last comment was deleted, the location
|
||||||
|
# won't be removed.
|
||||||
|
tmp_list = list()
|
||||||
|
for each in all_locations:
|
||||||
|
comment_count = db.session.query(TComments.comments_id) \
|
||||||
|
.filter(TComments.location_id == each.id_location) \
|
||||||
|
.filter(TComments.is_spam == True) \
|
||||||
|
.count()
|
||||||
|
if comment_count > 0:
|
||||||
|
tmp_list.append(each)
|
||||||
|
|
||||||
|
all_locations = tmp_list
|
||||||
|
|
||||||
# Project does not exist, error code is used by Javascript, not Flask
|
# Project does not exist, error code is used by Javascript, not Flask
|
||||||
if proj_id == -1:
|
if proj_id == -1:
|
||||||
return redirect(url_for("bp_dashboard.dashboard_project_list", error=404))
|
return redirect(url_for("bp_dashboard.dashboard_project_list", error=404))
|
||||||
|
@ -15,17 +15,18 @@ from labertasche.helper import get_id_from_project_name
|
|||||||
from labertasche.models import TProjects, TComments, TEmail, TLocation
|
from labertasche.models import TProjects, TComments, TEmail, TLocation
|
||||||
from validators import url as validate_url
|
from validators import url as validate_url
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from secrets import compare_digest
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def validate_project(project):
|
def validate_project(project, is_edit=False):
|
||||||
"""
|
"""
|
||||||
Validates important bits of a project database entry
|
Validates important bits of a project database entry
|
||||||
|
|
||||||
:param project: The json from the request, containing the data for a project.
|
:param project: The json from the request, containing the data for a project.
|
||||||
|
:param is_edit: If we are updating the database, we need to know, so we don't check for dupes on urls.
|
||||||
:return: A response with the error or None if the project is valid.
|
:return: A response with the error or None if the project is valid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Validate length
|
# Validate length
|
||||||
if not len(project['name']) and \
|
if not len(project['name']) and \
|
||||||
not len(project['blogurl']) and \
|
not len(project['blogurl']) and \
|
||||||
@ -34,16 +35,18 @@ def validate_project(project):
|
|||||||
|
|
||||||
# Validate project name
|
# Validate project name
|
||||||
if not re.match('^\\w+$', project['name']):
|
if not re.match('^\\w+$', project['name']):
|
||||||
print(project['name'])
|
|
||||||
return make_response(jsonify(status='invalid-project-name'), 400)
|
return make_response(jsonify(status='invalid-project-name'), 400)
|
||||||
|
|
||||||
# Check if project name already exists
|
# Check if project name already exists
|
||||||
name_check = db.session.query(TProjects.name).filter(TProjects.name == project['name']).first()
|
name_check = db.session.query(TProjects.name).filter(TProjects.name == project['name']).first()
|
||||||
if name_check:
|
if name_check and not is_edit:
|
||||||
return make_response(jsonify(status='project-exists'), 400)
|
return make_response(jsonify(status='project-exists'), 400)
|
||||||
|
|
||||||
# Validate url
|
# Validate existing only if we are not editing
|
||||||
|
url_exists = False
|
||||||
|
if not is_edit:
|
||||||
url_exists = db.session.query(TProjects.blogurl).filter(TProjects.blogurl == project['blogurl']).first()
|
url_exists = db.session.query(TProjects.blogurl).filter(TProjects.blogurl == project['blogurl']).first()
|
||||||
|
|
||||||
if not validate_url(project['blogurl']) or url_exists:
|
if not validate_url(project['blogurl']) or url_exists:
|
||||||
return make_response(jsonify(status='invalid-blog-url'), 400)
|
return make_response(jsonify(status='invalid-blog-url'), 400)
|
||||||
|
|
||||||
@ -78,7 +81,12 @@ def api_create_project():
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.session.add(TProjects(**request.json))
|
new_project = request.json
|
||||||
|
# Remove trailing slash
|
||||||
|
if compare_digest(new_project['blogurl'][-1], '/'):
|
||||||
|
new_project['blogurl'] = new_project['blogurl'][:-1]
|
||||||
|
|
||||||
|
db.session.add(TProjects(**new_project))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
print(str(e))
|
||||||
@ -97,21 +105,26 @@ def api_edit_project_name(name: str):
|
|||||||
:param name: The previous name of the project to edit, must exist
|
:param name: The previous name of the project to edit, must exist
|
||||||
:return: A string with an error code and 'ok' as string on success.
|
:return: A string with an error code and 'ok' as string on success.
|
||||||
"""
|
"""
|
||||||
response = validate_project(request.json)
|
response = validate_project(request.json, is_edit=True)
|
||||||
if response is not None:
|
if response is not None:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
project = db.session.query(TProjects).filter(TProjects.name == name).first()
|
project = db.session.query(TProjects).filter(TProjects.name == name).first()
|
||||||
|
|
||||||
|
project_json = request.json
|
||||||
|
# Remove trailing slash to streamline it
|
||||||
|
if compare_digest(project_json['blogurl'][-1], '/'):
|
||||||
|
setattr(project, "blogurl", project_json['blogurl'][:-1].strip())
|
||||||
|
|
||||||
setattr(project, "id_project", project.id_project)
|
setattr(project, "id_project", project.id_project)
|
||||||
setattr(project, "name", request.json['name'])
|
setattr(project, "name", project_json['name'])
|
||||||
setattr(project, "blogurl", request.json['blogurl'].strip())
|
setattr(project, "output", project_json['output'].strip())
|
||||||
setattr(project, "output", request.json['output'].strip())
|
setattr(project, "sendotp", project_json['sendotp'])
|
||||||
setattr(project, "sendotp", request.json['sendotp'])
|
setattr(project, "gravatar_cache", project_json['gravatar_cache'])
|
||||||
setattr(project, "gravatar_cache", request.json['gravatar_cache'])
|
setattr(project, "gravatar_cache_dir", project_json['gravatar_cache_dir'])
|
||||||
setattr(project, "gravatar_cache_dir", request.json['gravatar_cache_dir'])
|
setattr(project, "gravatar_size", project_json['gravatar_size'])
|
||||||
setattr(project, "gravatar_size", request.json['gravatar_size'])
|
setattr(project, "addon_smileys", project_json['addon_smileys'])
|
||||||
setattr(project, "addon_smileys", request.json['addon_smileys'])
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
print(str(e))
|
||||||
@ -138,7 +151,6 @@ def api_delete_project(name: str):
|
|||||||
try:
|
try:
|
||||||
db.session.query(TComments).filter(TComments.project_id == proj_id).delete()
|
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(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.query(TProjects).filter(TProjects.id_project == proj_id).delete()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
from . import bp_dbupgrades
|
from . import bp_dbupgrades
|
||||||
from flask_cors import cross_origin
|
from flask_cors import cross_origin
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
from flask import render_template, jsonify, make_response
|
from flask import render_template, jsonify, make_response, redirect, url_for
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from labertasche.database import labertasche_db as db
|
from labertasche.database import labertasche_db as db
|
||||||
from labertasche.models import TProjects, TComments, TLocation, TEmail, TVersion
|
from labertasche.models import TProjects, TComments, TLocation, TEmail, TVersion
|
||||||
@ -36,6 +36,8 @@ def upgrade_db_to_v2():
|
|||||||
version = db.session.query(TVersion).first()
|
version = db.session.query(TVersion).first()
|
||||||
if version:
|
if version:
|
||||||
status = True
|
status = True
|
||||||
|
return redirect(url_for('bp_dashboard.dashboard_project_list'))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e.__class__)
|
print(e.__class__)
|
||||||
pass
|
pass
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
# *********************************************************************************/
|
# *********************************************************************************/
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from sqlalchemy import MetaData
|
from sqlalchemy import MetaData
|
||||||
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
# naming conventions
|
# naming conventions
|
||||||
convention = {
|
convention = {
|
||||||
@ -20,4 +21,6 @@ convention = {
|
|||||||
metadata = MetaData(naming_convention=convention)
|
metadata = MetaData(naming_convention=convention)
|
||||||
|
|
||||||
# Create SQLAlchemy
|
# Create SQLAlchemy
|
||||||
labertasche_db = SQLAlchemy(metadata=metadata)
|
labertasche_db = SQLAlchemy(metadata=metadata, engine_options={
|
||||||
|
'poolclass': NullPool
|
||||||
|
})
|
||||||
|
@ -18,6 +18,8 @@ from secrets import token_urlsafe
|
|||||||
from labertasche.models import TProjects
|
from labertasche.models import TProjects
|
||||||
from labertasche.database import labertasche_db as db
|
from labertasche.database import labertasche_db as db
|
||||||
from labertasche.settings import Settings
|
from labertasche.settings import Settings
|
||||||
|
from flask import render_template
|
||||||
|
|
||||||
|
|
||||||
class mail:
|
class mail:
|
||||||
|
|
||||||
@ -78,22 +80,19 @@ class mail:
|
|||||||
confirm_digest = token_urlsafe(48)
|
confirm_digest = token_urlsafe(48)
|
||||||
delete_digest = token_urlsafe(48)
|
delete_digest = token_urlsafe(48)
|
||||||
|
|
||||||
confirm_url = f"{settings.weburl}/comments/confirm/{confirm_digest}"
|
confirm_url = f"{settings.weburl}/comments/{project.name}/confirm/{confirm_digest}"
|
||||||
delete_url = f"{settings.weburl}/comments/delete/{delete_digest}"
|
delete_url = f"{settings.weburl}/comments/{project.name}/delete/{delete_digest}"
|
||||||
|
|
||||||
txt_what = f"Hey there. You have made a comment on {project.blogurl}. Please confirm it by " \
|
txt_what = f"Hey there. You have made a comment on {project.blogurl}. Please confirm it by " \
|
||||||
f"copying this link into your browser:\n{confirm_url}\nIf you want to delete your comment for,"\
|
f"copying this link into your browser:\n{confirm_url}\n" \
|
||||||
f"whatever reason, please use this link:\n{delete_url}"
|
f"If you want to delete your comment for whatever reason, please use this link:\n{delete_url}"
|
||||||
|
|
||||||
html_what = f"Hey there. You have made a comment on {project.blogurl}.<br>Please confirm it by " \
|
html_what = render_template("comment_confirmation.html",
|
||||||
f"clicking on this <a href='{confirm_url}'>link</a>.<br>"\
|
blogurl=project.blogurl,
|
||||||
f"In case you want to delete your comment later, please click <a href='{delete_url}'>here</a>."\
|
confirmation_url=confirm_url,
|
||||||
f"<br><br>If you think this is in error or someone made this comment in your name, please "\
|
deletion_url=delete_url)
|
||||||
f"write me a <a href='mailto:contact@tuxstash.de'>mail</a> to discuss options such as " \
|
|
||||||
f"blocking your mail from being used."
|
|
||||||
|
|
||||||
self.send(txt_what, html_what, email)
|
self.send(txt_what, html_what, email)
|
||||||
|
|
||||||
return confirm_digest, delete_digest
|
return confirm_digest, delete_digest
|
||||||
|
|
||||||
def validate(self, addr):
|
def validate(self, addr):
|
||||||
|
@ -22,4 +22,3 @@ class TEmail(db.Model):
|
|||||||
email = db.Column(db.Integer, unique=True)
|
email = db.Column(db.Integer, unique=True)
|
||||||
is_blocked = db.Column(db.Boolean)
|
is_blocked = db.Column(db.Boolean)
|
||||||
is_allowed = db.Column(db.Boolean)
|
is_allowed = db.Column(db.Boolean)
|
||||||
project_id = db.Column(db.Integer, ForeignKey('t_projects.id_project'), nullable=False)
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
# * _license : This project is under MIT License
|
# * _license : This project is under MIT License
|
||||||
# *********************************************************************************/
|
# *********************************************************************************/
|
||||||
from labertasche.database import labertasche_db as db
|
from labertasche.database import labertasche_db as db
|
||||||
from sqlalchemy import ForeignKey
|
from sqlalchemy import ForeignKey, UniqueConstraint
|
||||||
|
|
||||||
|
|
||||||
class TLocation(db.Model):
|
class TLocation(db.Model):
|
||||||
@ -19,5 +19,8 @@ class TLocation(db.Model):
|
|||||||
id_location = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
id_location = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||||
|
|
||||||
# data
|
# data
|
||||||
location = db.Column(db.Text, nullable=False, unique=True)
|
location = db.Column(db.Text, nullable=False)
|
||||||
project_id = db.Column(db.Integer, ForeignKey('t_projects.id_project'), nullable=False)
|
project_id = db.Column(db.Integer, ForeignKey('t_projects.id_project'), nullable=False)
|
||||||
|
|
||||||
|
# Unique constraint
|
||||||
|
UniqueConstraint('location', 'project_id', name="unique_per_project")
|
||||||
|
@ -155,6 +155,18 @@ async function show_modal_with_project(id_name, proj_name)
|
|||||||
document.getElementById('edit-project-gravatar-size').value = r['gravatar_size'];
|
document.getElementById('edit-project-gravatar-size').value = r['gravatar_size'];
|
||||||
document.getElementById('edit-project-send-otp').checked = r['sendotp'];
|
document.getElementById('edit-project-send-otp').checked = r['sendotp'];
|
||||||
document.getElementById('edit-project-addons-smileys').checked = r['addon_smileys'];
|
document.getElementById('edit-project-addons-smileys').checked = r['addon_smileys'];
|
||||||
|
|
||||||
|
let cache = document.getElementById('edit-project-gravatar-cache-dir');
|
||||||
|
let size = document.getElementById('edit-project-gravatar-size');
|
||||||
|
|
||||||
|
if(!r['gravatar_cache']){
|
||||||
|
cache.setAttribute('disabled', '');
|
||||||
|
size.setAttribute('disabled', '');
|
||||||
|
cache.placeholder = "disabled";
|
||||||
|
cache.value = "";
|
||||||
|
size.placeholder = "disabled";
|
||||||
|
size.value = "";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set project name
|
// Set project name
|
||||||
@ -253,8 +265,10 @@ function toggle_gravatar_settings(chkbx)
|
|||||||
if(!chkbx.checked){
|
if(!chkbx.checked){
|
||||||
cache.setAttribute('disabled', '');
|
cache.setAttribute('disabled', '');
|
||||||
size.setAttribute('disabled', '');
|
size.setAttribute('disabled', '');
|
||||||
cache.value = "disabled";
|
cache.placeholder = "disabled";
|
||||||
size.value = "disabled";
|
cache.value = "";
|
||||||
|
size.placeholder = "disabled";
|
||||||
|
size.value = "";
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
cache.removeAttribute('disabled');
|
cache.removeAttribute('disabled');
|
||||||
|
40
templates/comment_confirmation.html
Normal file
40
templates/comment_confirmation.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ i18n['html_language'] }}">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Your comment on {{ blogurl }}</title>
|
||||||
|
</head>
|
||||||
|
<body style="background-color: #393f4d;color:white;width:70%;margin:0 auto;">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" width='100%'>
|
||||||
|
<tr>
|
||||||
|
<td style="background-color:#feda6a;font-size:1.3em;color:black;padding:2%;">
|
||||||
|
<h1>Your comment on {{ blogurl }}</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<p style="font-size: 1.2em;line-height: 1.3em;padding-left:2%;">
|
||||||
|
Hey there, <br><br>
|
||||||
|
You are receiving this mail, because you have recently made a comment on {{ blogurl }}.
|
||||||
|
<br><br>
|
||||||
|
If you wish to publish your comment, please click this <a style="color:#feda6a;" href="{{ confirmation_url }}">link</a>.
|
||||||
|
<br>
|
||||||
|
If you wish to delete your comment, please click this <a style="color:#feda6a;" href="{{ deletion_url }}">link</a>.
|
||||||
|
<br><br>
|
||||||
|
If this was in error or if someone used your mail address without your knowledge, please contact the site
|
||||||
|
adminitrator at {{ blogurl }}.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="border-top: 1px dashed #feda6a;padding-left:2%;">
|
||||||
|
<p style="font-size: 0.8em">
|
||||||
|
Powered by <a style="color:#feda6a;" href="https://labertasche.tuxstash.de">Labertasche</a>,
|
||||||
|
licensed via MIT license.
|
||||||
|
</p>
|
||||||
|
<td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
@ -1,11 +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 is-capitalized">
|
||||||
{% if action == "spam" %}
|
{% if action == "spam" %}
|
||||||
<h1 class="title has-text-white has-text-centered">{{ i18n['manage_spam'] }}</h1>
|
{{ i18n['manage_spam'] }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<h1 class="title has-text-white has-text-centered">{{ i18n['manage_comments'] }}</h1>
|
{{ i18n['manage_comments'] }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</h1>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<form method="GET" action="/dashboard/{{ project }}/manage-{{action}}/">
|
<form method="GET" action="/dashboard/{{ project }}/manage-{{action}}/">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
@ -28,22 +28,12 @@
|
|||||||
let total_spam = parseInt(chart_total.dataset.spam);
|
let total_spam = parseInt(chart_total.dataset.spam);
|
||||||
let total_comments = parseInt(chart_total.dataset.comments);
|
let total_comments = parseInt(chart_total.dataset.comments);
|
||||||
let total_unpublished = parseInt(chart_total.dataset.unpublished);
|
let total_unpublished = parseInt(chart_total.dataset.unpublished);
|
||||||
let total = (total_spam + total_comments + total_unpublished);
|
|
||||||
|
|
||||||
let spam_perc = (total_spam/total) * 100;
|
|
||||||
let comm_perc = (total_comments/total) * 100;
|
|
||||||
let unpub_perc = (total_unpublished/total) * 100;
|
|
||||||
|
|
||||||
console.log(total);
|
|
||||||
console.log(spam_perc);
|
|
||||||
console.log(comm_perc);
|
|
||||||
console.log(unpub_perc);
|
|
||||||
|
|
||||||
new Chart(document.getElementById('chart-total'), {
|
new Chart(document.getElementById('chart-total'), {
|
||||||
type: "pie",
|
type: "pie",
|
||||||
data: {
|
data: {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: [spam_perc, comm_perc, unpub_perc],
|
data: [total_spam, total_comments, total_unpublished],
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
"rgba(182, 106, 254, 0.8)",
|
"rgba(182, 106, 254, 0.8)",
|
||||||
"rgba(254, 218, 106, 0.8)",
|
"rgba(254, 218, 106, 0.8)",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user