1
1
Fork 0

A load of rubbish - comments,

little tweaks regarding user permissions
dev
Jan Kužílek 5 years ago
parent 51f2f85123
commit 1f62b1708d

@ -0,0 +1,31 @@
"""comment deletion and visibility
Revision ID: 2534cc5059a5
Revises: 4b6c6a23c639
Create Date: 2020-03-20 23:56:02.150560
"""
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utc
# revision identifiers, used by Alembic.
revision = '2534cc5059a5'
down_revision = '4b6c6a23c639'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('comment', sa.Column('visible', sa.Boolean(), nullable=True))
op.drop_column('comment', 'delete_reason')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('comment', sa.Column('delete_reason', sa.VARCHAR(length=512), autoincrement=False, nullable=True))
op.drop_column('comment', 'visible')
# ### end Alembic commands ###

@ -32,6 +32,8 @@ def create_app():
app.config.from_pyfile('config.py') app.config.from_pyfile('config.py')
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
db.init_app(app) db.init_app(app)
migrate.init_app(app, db) migrate.init_app(app, db)

@ -90,12 +90,12 @@ h4 {
.editingable { .editingable {
&:not(.time-to-edit){ &:not(.time-to-edit){
.edit { .edit {
display: none; display: none !important;
} }
} }
&.time-to-edit { &.time-to-edit {
.notedit { .notedit {
display: none; display: none !important;
} }
} }
} }
@ -306,6 +306,37 @@ label {
} }
} }
.comment-container {
padding: 0 10px;
max-width: 500px;
article.comment {
overflow: hidden;
// margin-bottom: 1em;
margin: .4em 0;
p, textarea {
// margin-top: .5em;
margin-top: 0;
// margin-bottom: 1em;
&.deleted {
color: red;
}
}
.comment-head {
display: flex;
justify-content: space-between;
a, label {
cursor: pointer;
}
.controls > * { display: inline-block; }
}
}
}
// form { // form {
// margin: 0 auto; // margin: 0 auto;
// width: 300px; // width: 300px;

@ -180,30 +180,6 @@
section.comments { section.comments {
padding: 10px; padding: 10px;
> .comment-container {
padding: 0 10px;
max-width: 500px;
article.comment {
overflow: hidden;
margin-bottom: 1em;
p, textarea {
// margin-top: .5em;
margin-top: 0;
// margin-bottom: 1em;
&.deleted {
color: red;
}
}
.comment-head {
display: flex;
justify-content: space-between;
}
}
}
} }
section.management-table { section.management-table {

@ -6,26 +6,55 @@ function switchEdit(el) {
} }
} }
function commentSwitcher() { // function commentSwitcher() {
let comments = document.querySelectorAll('section.comments article.comment') // let comments = document.querySelectorAll('section.comments article.comment')
comments.forEach(comment => comment // comments.forEach(comment => comment
.querySelectorAll('.comment-head .control-edit') // .querySelectorAll('.comment-head .control-edit')
// .forEach(el => el
// .addEventListener('click', ev => switchEdit(comment))
// )
// )
// // comment.target.closest('form.editingable')
// }
// function mgmtSwitcher() {
// let rows = document.querySelectorAll("section.management-table tbody > tr")
// rows.forEach(row => row
// .querySelectorAll('label.to-edit, label.to-close')
// .forEach(el => el
// .addEventListener('click', ev => switchEdit(row))
// )
// )
// }
// commentSwitcher()
// mgmtSwitcher()
function toggleSwitcher(rootSelector, triggerSelector) {
let rootelements = document.querySelectorAll(rootSelector)
rootelements.forEach(rootelement => rootelement
.querySelectorAll(triggerSelector)
.forEach(el => el .forEach(el => el
.addEventListener('click', ev => switchEdit(comment.querySelector('.editingable'))) .addEventListener('click', ev => switchEdit(rootelement))
) )
) )
// comment.target.closest('form.editingable')
} }
function mgmtSwitcher() { function doubleclickSwitcher(rootSelector, triggerSelector, dblSelector) {
let rows = document.querySelectorAll("section.management-table tbody > tr") let rootelements = document.querySelectorAll(rootSelector)
rows.forEach(row => row rootelements.forEach(rootelement => {
.querySelectorAll('label.to-edit, label.to-close') rootelement
.querySelectorAll(triggerSelector)
.forEach(el => el .forEach(el => el
.addEventListener('click', ev => switchEdit(row)) .addEventListener('click', ev => switchEdit(rootelement))
) )
rootelement
.querySelectorAll(dblSelector)
.forEach(el => el
.addEventListener('dblclick', ev => switchEdit(rootelement))
) )
})
} }
commentSwitcher() toggleSwitcher('section.comments article.comment', '.comment-head .control-edit', '.comment-content')
mgmtSwitcher() doubleclickSwitcher('section.management-table tbody > tr', 'label.to-edit, label.to-close', '.notedit')

@ -22,7 +22,8 @@ def manage_users(page):
username=user.username, username=user.username,
email=user.email, email=user.email,
op_level=user.op_level.name, op_level=user.op_level.name,
user_status=user.user_status.name) user_status=user.user_status.name
)
return render_template('manage/users.html', users=users, elements=users.items, createform=UserForm()) return render_template('manage/users.html', users=users, elements=users.items, createform=UserForm())
@ -57,10 +58,27 @@ def manage_tags(page):
tag.editform = TagForm( tag.editform = TagForm(
id=tag.id, id=tag.id,
content=tag.content_deser, content=tag.content_deser,
category=tag.category.name) category=tag.category.name
)
return render_template('manage/tags.html', tags=tags, elements=tags.items, createform=TagForm()) return render_template('manage/tags.html', tags=tags, elements=tags.items, createform=TagForm())
@bp.route('/comments', defaults={'page': 1})
@bp.route('/comments/<int:page>')
@login_required
@moderator_required
def manage_comments(page):
comments = Comment.query.order_by(Comment.updated).paginate(page, current_app.config.get('MANAGE_PER_PAGE'))
# for comment in comments.items:
# comment.editform = CommentForm(
# id=comment.id,
# content=comment.content,
# )
return render_template('manage/comments.html', tags=comments, elements=comments.items)
# ONLY THROUGH MANAGEMENT # ONLY THROUGH MANAGEMENT
@bp.route('/modify_user', methods=['POST']) @bp.route('/modify_user', methods=['POST'])
@ -199,3 +217,48 @@ def modify_tag():
flasherrors(form) flasherrors(form)
return redirect(url_for('.manage_tags')) return redirect(url_for('.manage_tags'))
# return redirect(url_for('main.index')) # return redirect(url_for('main.index'))
@bp.route('/modify_comment', methods=['POST'])
@login_required
@moderator_required
def modify_comment():
form = CommentForm(request.form)
# flash(str(request.form))
if form.validate():
if form.create.data:
pass
else:
el = Comment.query.get(form.id.data)
if form.delete.data:
db.session.delete(el)
db.session.commit()
flash('{} deleted.'.format(str(el)))
elif form.edit.data:
# if form.rating.raw_data and form.rating.data: el.rating = form.rating.data
# if form.status.raw_data and form.status.data: el.status = form.status.data
# if form.source.raw_data: el.source = form.source.data
# db.session.commit()
flash('Changes to {} have been applied.'.format(str(el)))
elif form.ban.data:
el.ban_reason = form.ban_reason.data
el.banned = True
db.session.commit()
flash('Comment {} has been banned.'.format(str(el)))
elif form.unban.data:
el.ban_reason = None
el.banned = False
db.session.commit()
flash('Comment {} has been unbanned.'.format(str(el)))
flasherrors(form)
if form.referer.data == 'post_show':
return redirect(url_for('post.post_show', id=Comment.query.get(form.id.data).post_id))
return redirect(url_for('.manage_comments'))

@ -76,14 +76,16 @@ def post_show(id):
# tag.endpoint = query_replace(dict(**session_rating(), tags=tag.content), url_for('.posts')) # tag.endpoint = query_replace(dict(**session_rating(), tags=tag.content), url_for('.posts'))
pass pass
for comment in post.comments: comments = db.session.query(Comment).filter_by(post=post).filter_by(visible=True).order_by(Comment.id).all()
for comment in comments:
comment.editform = CommentForm(id=comment.id, content=comment.content) comment.editform = CommentForm(id=comment.id, content=comment.content)
return render_template( return render_template(
'post/post.html', 'post/post.html',
post=post, post=post,
tags=post.tags, tags=post.tags,
comments=post.comments, comments=comments,
editform=PostForm( editform=PostForm(
id=post.id, id=post.id,
referer=post_show.__name__, referer=post_show.__name__,
@ -100,7 +102,7 @@ def post_show(id):
@login_required @login_required
def comment(): def comment():
form = CommentForm(request.form) form = CommentForm(request.form)
# flash(str(request.form)) flash(str(request.form))
if form.validate(): if form.validate():
if form.create.data: if form.create.data:
el = Comment(content=form.content.data.strip(), post_id=form.post_id.data, user=current_user) el = Comment(content=form.content.data.strip(), post_id=form.post_id.data, user=current_user)
@ -111,9 +113,12 @@ def comment():
return redirect(url_for('post.post_show', id=form.post_id.data)) return redirect(url_for('post.post_show', id=form.post_id.data))
else: else:
el = Comment.query.filter_by(id=form.id.data).first() el = Comment.query.filter_by(id=form.id.data).first()
if not current_user.is_moderator and not (el.user.is_current if el.user is not None else None):
flash("You don't have sufficient rights to do this.") # only authors allowed to edit
return redirect(url_for('main.index')) # if not (el.user and el.user.is_current):
# flash("You don't have sufficient rights to do this.")
# return redirect(url_for('main.index'))
if form.delete.data: if form.delete.data:
db.session.delete(el) db.session.delete(el)
db.session.commit() db.session.commit()
@ -124,9 +129,30 @@ def comment():
db.session.commit() db.session.commit()
flash('Changes to {} have been applied.'.format(str(el))) flash('Changes to {} have been applied.'.format(str(el)))
elif form.ban.data:
if not current_user.is_moderator:
flash("You don't have sufficient rights to do this.")
return redirect(url_for('main.index'))
el.deleted = True
db.session.commit()
flash('Comment {} has been banned.'.format(str(el)))
elif form.unban.data:
if not current_user.is_moderator:
flash("You don't have sufficient rights to do this.")
return redirect(url_for('main.index'))
el.deleted = False
db.session.commit()
flash('Comment {} has been unbanned.'.format(str(el)))
return redirect(url_for('post.post_show', id=el.post_id)) return redirect(url_for('post.post_show', id=el.post_id))
return redirect(url_for('main.posts')) flasherrors(form)
return redirect(url_for('main.index'))
@bp.route('/upload', methods=['GET', 'POST']) @bp.route('/upload', methods=['GET', 'POST'])
@login_required @login_required

@ -142,7 +142,8 @@ class EditForm(CSRFForm):
delete = SubmitField('Delete') delete = SubmitField('Delete')
def validate_id(form, field): def validate_id(form, field):
if (form.edit.data or form.delete.data) and not field.data: # if (form.edit.data or form.delete.data) and not field.data:
if not form.create.data and not field.data:
raise ValidationError('ID must be defined to be able to modify.') raise ValidationError('ID must be defined to be able to modify.')
def validate_create_required(form, field): def validate_create_required(form, field):
@ -192,3 +193,12 @@ class CommentForm(EditForm):
def validate_content(form, field): def validate_content(form, field):
if (form.create.data or form.edit.data) and not field.data: if (form.create.data or form.edit.data) and not field.data:
raise ValidationError('Please fill out this field.') raise ValidationError('Please fill out this field.')
# referer = HiddenField()
ban = SubmitField('Ban')
unban = SubmitField('Unban')
# def validate_id(form, field):
# if form.ban.data and not field.data:
# raise ValidationError('ID must be defined to be able to ban.')
# super().validate_id(form, field)

@ -127,6 +127,14 @@ class User(UserMixin, TimestampMixin, db.Model):
def logout(self): def logout(self):
logout_user() logout_user()
@property
def can_upload(self):
self.user_status is USER_STATUS.active
@property
def is_author(self):
return len(self.authored_posts) > 0
@property @property
def is_moderator(self): def is_moderator(self):
# return self.op_level in [OP_LEVEL.moderator, OP_LEVEL.admin] # return self.op_level in [OP_LEVEL.moderator, OP_LEVEL.admin]
@ -329,10 +337,10 @@ class Tag(TimestampMixin, db.Model):
class Comment(TimestampMixin, db.Model): class Comment(TimestampMixin, db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(512), nullable=False) content = db.Column(db.String(512), nullable=False)
visible = db.Column(db.Boolean, default=True)
# status = db.Column(db.Enum(COMMENT_STATUS), default=COMMENT_STATUS.visible) # status = db.Column(db.Enum(COMMENT_STATUS), default=COMMENT_STATUS.visible)
deleted = db.Column(db.Boolean, default=False) deleted = db.Column(db.Boolean, default=False)
delete_reason = db.Column(db.String(512))
post_id = db.Column(db.Integer, db.ForeignKey('post.id')) post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
post = db.relationship('Post', backref=db.backref('comments', order_by='Comment.id', lazy=True)) post = db.relationship('Post', backref=db.backref('comments', order_by='Comment.id', lazy=True))
@ -340,7 +348,11 @@ class Comment(TimestampMixin, db.Model):
user = db.relationship('User', backref=db.backref('comments', lazy=True)) user = db.relationship('User', backref=db.backref('comments', lazy=True))
@property @property
def can_edit(self): def can_modify(self):
author = current_user == self.user # author = current_user == self.user
moderator = current_user.is_moderator moderator = current_user.is_moderator
return author or moderator return self.is_author or moderator
@property
def is_author(self):
return current_user == self.user

File diff suppressed because one or more lines are too long

@ -6,29 +6,58 @@ function switchEdit(el) {
} }
} }
function commentSwitcher() { // function commentSwitcher() {
let comments = document.querySelectorAll('section.comments article.comment') // let comments = document.querySelectorAll('section.comments article.comment')
comments.forEach(comment => comment // comments.forEach(comment => comment
.querySelectorAll('.comment-head .control-edit') // .querySelectorAll('.comment-head .control-edit')
// .forEach(el => el
// .addEventListener('click', ev => switchEdit(comment))
// )
// )
// // comment.target.closest('form.editingable')
// }
// function mgmtSwitcher() {
// let rows = document.querySelectorAll("section.management-table tbody > tr")
// rows.forEach(row => row
// .querySelectorAll('label.to-edit, label.to-close')
// .forEach(el => el
// .addEventListener('click', ev => switchEdit(row))
// )
// )
// }
// commentSwitcher()
// mgmtSwitcher()
function toggleSwitcher(rootSelector, triggerSelector) {
let rootelements = document.querySelectorAll(rootSelector)
rootelements.forEach(rootelement => rootelement
.querySelectorAll(triggerSelector)
.forEach(el => el .forEach(el => el
.addEventListener('click', ev => switchEdit(comment.querySelector('.editingable'))) .addEventListener('click', ev => switchEdit(rootelement))
) )
) )
// comment.target.closest('form.editingable')
} }
function mgmtSwitcher() { function doubleclickSwitcher(rootSelector, triggerSelector, dblSelector) {
let rows = document.querySelectorAll("section.management-table tbody > tr") let rootelements = document.querySelectorAll(rootSelector)
rows.forEach(row => row rootelements.forEach(rootelement => {
.querySelectorAll('label.to-edit, label.to-close') rootelement
.querySelectorAll(triggerSelector)
.forEach(el => el .forEach(el => el
.addEventListener('click', ev => switchEdit(row)) .addEventListener('click', ev => switchEdit(rootelement))
) )
rootelement
.querySelectorAll(dblSelector)
.forEach(el => el
.addEventListener('dblclick', ev => switchEdit(rootelement))
) )
})
} }
commentSwitcher() toggleSwitcher('section.comments article.comment', '.comment-head .control-edit', '.comment-content')
mgmtSwitcher() doubleclickSwitcher('section.management-table tbody > tr', 'label.to-edit, label.to-close', '.notedit')
let nav_menu = document.querySelectorAll("#nav-menu, nav#main-nav > ._overlay") let nav_menu = document.querySelectorAll("#nav-menu, nav#main-nav > ._overlay")
function nav_menu_event(event) { function nav_menu_event(event) {
let drop = document.getElementById("main-nav") let drop = document.getElementById("main-nav")

@ -21,13 +21,6 @@
</div> </div>
{% endmacro %} {% endmacro %}
<!-- {% macro rating(rating) %}
<article class="rating">
<h3>Rating</h3>
<input type="checkbox" name="" id="">
</article>
{% endmacro %} -->
<!-- DEPRECATED! --> <!-- DEPRECATED! -->
{% macro render_comments() %} {% macro render_comments() %}
<div class="comment_container"> <div class="comment_container">
@ -52,23 +45,6 @@
</div> </div>
{% endmacro %} {% endmacro %}
<!-- DEPRECATED! -->
{% macro render_post_edit() %}
<article class="edit">
<h3>Edit</h3>
<a href="">edit?</a>
</article>
{% endmacro %}
<!-- DEPRECATED? -->
{% macro render_sidenav(sidenav_links) %}
<article class="sidenav">
{% for class, (name, link) in sidenav_links.items() %}
<a href="{{ link }}">{{ name }}</a>
{% endfor %}
</article>
{% endmacro %}
{% macro render_tag_input(input_field, param_dict={}) %} {% macro render_tag_input(input_field, param_dict={}) %}
<div class="tag-input"{% for key,value in param_dict.items() %} data-{{key}}="{{value}}"{% endfor %}> <div class="tag-input"{% for key,value in param_dict.items() %} data-{{key}}="{{value}}"{% endfor %}>
<div class="tag-suggester" data-inputname="{{ input_field.name }}"> <div class="tag-suggester" data-inputname="{{ input_field.name }}">
@ -80,70 +56,3 @@
<div class="tag-container tags-selected"></div> <div class="tag-container tags-selected"></div>
</div> </div>
{% endmacro %} {% endmacro %}
<!-- EFFECTIVELY DEPRECATED NOW (AS OF management.html) -->
{#{% from "_formhelpers.html" import errors %}#}
{% macro render_management_table(elements) %}
<table>
<thead>
<tr>
{% for heading in elements[0][1].keys() %}
<th>{{ heading }}</th>
{% endfor %}
<th>Manage</th>
</tr>
</thead>
<tbody>
{% for element,fields in elements %}
<tr>
<form class="editingable" action="{{ url_for('manage.modify_tag') }}" method="post">
{{ element.editform.csrf_token }}
{{ element.editform.id() }}
{% for field,formfield in fields.values() %}
{% if not formfield %}
<td>
<span>{{ field }}</span>
</td>
{% else %}
<td>
<span class="notedit">{{ field }}</span>
<span class="edit">{{ formfield() }}</span>
{#{{ errors(formfield) }}#}
</td>
{% endif %}
{% endfor %}
<td>
<label class="notedit to-edit">
<span class="fa fa-edit"></span>
</label>
<label class="edit to-close">
<span class="fa fa-close"></span>
</label>
<label class="edit">
<span class="fa fa-check"></span>
{{ element.editform.edit() }}
</label>
<label>
<span class="fa fa-trash-o"></span>
{{ element.editform.delete() }}
</label>
</td>
</form>
</tr>
{% endfor %}
{{ caller() }}
{#<!-- <tr>
<form action="{{ url_for('manage.modify_tag') }}" method="post">
{{ createform.csrf_token }}
<td>{{ createform.content() }}</td>
<td>{{ createform.category() }}</td>
<td>{{ createform.create() }}</td>
</form>
</tr> -->#}
</tbody>
</table>
{% endmacro %}

@ -2,10 +2,15 @@
{% block sidebar %} {% block sidebar %}
<article class="sidenav"> <article class="sidenav">
{#<!-- <a href="{{ url_for('user.profile') }}">Profile</a> -->#}
<a href="{{ url_for('user.settings') }}">Settings</a> <a href="{{ url_for('user.settings') }}">Settings</a>
{% if current_user.is_author or current_user.is_moderator -%}
<a href="{{ url_for('manage.manage_posts') }}">Post management</a> <a href="{{ url_for('manage.manage_posts') }}">Post management</a>
{%- endif %}
{% if current_user.is_moderator -%}
<a href="{{ url_for('manage.manage_tags') }}">Tag management</a> <a href="{{ url_for('manage.manage_tags') }}">Tag management</a>
{%- endif %}
{% if current_user.is_admin -%}
<a href="{{ url_for('manage.manage_users') }}">User management</a> <a href="{{ url_for('manage.manage_users') }}">User management</a>
{%- endif %}
</article> </article>
{% endblock %} {% endblock %}

@ -76,31 +76,40 @@
<h3>Comments</h3> <h3>Comments</h3>
<div class="comment-container"> <div class="comment-container">
{% for comment in comments %} {% for comment in comments %}
<article class="comment"> <article class="comment editingable">
<form class="editingable" action="{{ url_for('post.comment') }}" method="post"> <form action="{{ url_for('post.comment') }}" method="post">
{{ comment.editform.csrf_token }}
{{ comment.editform.id() }}
<div class="comment-head"> <div class="comment-head">
<h4>{{ comment.user.username or "Deleted account" }}</h4> <h4>{{ comment.user.username or "Deleted account" }}</h4>
<span class="controls"> <span class="controls">
{% if comment.can_edit %} {% if current_user.is_moderator %}
<span class="control-edit">edit</span> {% if not comment.deleted %}
<a class="control-moderate"><label>ban{{ comment.editform.ban(style="display: none;") }}</label></a>
{% else %}
<a class="control-moderate"><label>unban{{ comment.editform.unban(style="display: none;") }}</label></a>
{% endif %}
{% endif %}
{% if comment.is_author %}
<a class="notedit control-edit"><label>edit</label></a>
<a class="edit control-edit"><label>cancel</label></a>
{% endif %} {% endif %}
</span> </span>
</div> </div>
<div class="comment-editform baseform"> <div class="comment-editform baseform">
{{ comment.editform.csrf_token }} {% if not comment.deleted or comment.is_author or current_user.is_moderator %}
{{ comment.editform.id() }}
{% if not comment.deleted %}
<p class="comment-content notedit">{{ comment.content }}</p> <p class="comment-content notedit">{{ comment.content }}</p>
{{ comment.editform.content(class="edit") }} {% endif %}
{% else %} {% if comment.deleted %}
<p class="deleted notedit">{{ comment.delete_reason }}</p> <p class="deleted">[Comment banned]</p>
{#<!-- {{ comment.editform.delete_reason(class="edit") }} -->#}
{% endif %} {% endif %}
{% if comment.is_author %}
{{ comment.editform.content(class="edit") }}
{{ comment.editform.edit(class="edit") }} {{ comment.editform.edit(class="edit") }}
{{ comment.editform.delete(class="edit") }} {{ comment.editform.delete(class="edit") }}
{% endif %}
</div> </div>
</form> </form>
</article> </article>
@ -110,10 +119,8 @@
<p>No comments so far.</p> <p>No comments so far.</p>
{% endif %} {% endif %}
</div> </div>
{#<script src="{{ url_for('static', filename="comment.js") }}"></script>#}
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
<h3>Reply</h3> <h3>Reply</h3>
<div class="comment-form baseform"> <div class="comment-form baseform">
<form action="{{ url_for('post.comment') }}" method="post"> <form action="{{ url_for('post.comment') }}" method="post">
@ -126,7 +133,6 @@
</form> </form>
</div> </div>
{% else %} {% else %}
<h4>To comment, please log in.</h3> <h4>To comment, please log in.</h3>
{% endif %} {% endif %}
</section> </section>

@ -1,10 +1,10 @@
{% extends 'layout/base_sidebar.html' %} {% extends 'layout/base.html' %}
{% block sidebar %} {#{% block sidebar %}
{{ super()}} {{ super()}}
{% endblock %} {% endblock %}#}
{% block main_content %} {% block content %}
<p class="bio">{{ user.biography }}</p> <p class="bio">{{ user.biography }}</p>
{% endblock %} {% endblock %}
Loading…
Cancel
Save