From 3ebe1c0b67a649e58930b8647cc4ac6db7575595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C5=BE=C3=ADlek?= Date: Sat, 7 Mar 2020 15:08:50 +0100 Subject: [PATCH] Completely rewritten basically all form endpoints, some minor changes --- yadc/__init__.py | 3 +- yadc/bp/manage.py | 178 +++++++++++++++++++ yadc/bp/post.py | 52 ++---- yadc/bp/user.py | 91 +--------- yadc/config.def.py | 3 +- yadc/forms.py | 61 ++++--- yadc/models.py | 10 +- yadc/static/all.css | 2 +- yadc/static/comment.js | 17 ++ yadc/static/default.scss | 10 +- yadc/templates/_formhelpers.html | 7 +- yadc/templates/_includes.html | 2 +- yadc/templates/layout/base_sidebar_tags.html | 4 +- yadc/templates/manage/posts.html | 10 +- yadc/templates/manage/tags.html | 47 +++++ yadc/templates/manage/users.html | 22 ++- yadc/templates/post/index.html | 6 +- yadc/templates/post/post.html | 20 ++- 18 files changed, 351 insertions(+), 194 deletions(-) create mode 100644 yadc/bp/manage.py create mode 100644 yadc/static/comment.js create mode 100644 yadc/templates/manage/tags.html diff --git a/yadc/__init__.py b/yadc/__init__.py index 04a9a31..8175138 100644 --- a/yadc/__init__.py +++ b/yadc/__init__.py @@ -50,10 +50,11 @@ def create_app(): # admin.add_view(ModelView(models.Tag, db.session, endpoint='admin_tag')) # admin.add_view(ModelView(models.Comment, db.session, endpoint='admin_comment')) - from yadc.bp import main, post, auth, user, api + from yadc.bp import main, post, auth, manage, user, api app.register_blueprint(main.bp) app.register_blueprint(post.bp, url_prefix='/post') app.register_blueprint(auth.bp, url_prefix='/auth') + app.register_blueprint(manage.bp, url_prefix='/manage') app.register_blueprint(user.bp, url_prefix='/user') app.register_blueprint(api.bp) diff --git a/yadc/bp/manage.py b/yadc/bp/manage.py new file mode 100644 index 0000000..4dad24a --- /dev/null +++ b/yadc/bp/manage.py @@ -0,0 +1,178 @@ +from flask import (Blueprint, abort, current_app, flash, redirect, + render_template, request, send_from_directory, url_for) +from flask_login import login_required, current_user +from yadc.forms import UserForm, PostForm, TagForm, CommentForm + +from yadc import db +from yadc.models import User, USER_STATUS, Post, Tag, TAG_CATEGORY, Comment + +bp = Blueprint('manage', __name__) + +@bp.route('/users', defaults={'page': 1}) +@bp.route('/users/') +@login_required +def manage_users(page): + users = User.query.order_by(User.id.desc()).paginate(page, current_app.config.get('MANAGE_PER_PAGE')) + + for user in users.items: + user.editform = UserForm( + id=user.id, + username=user.username, + email=user.email, + op_level=user.op_level.name, + user_status=user.user_status.name) + + return render_template('manage/users.html', users=users, createform=UserForm()) + +@bp.route('/posts', defaults={'page': 1}) +@bp.route('/posts/') +@login_required +def manage_posts(page): + posts = Post.query.order_by(Post.id.desc()).paginate(page, current_app.config.get('MANAGE_PER_PAGE')) + + for post in posts.items: + post.editform = PostForm( + id=post.id, + rating=post.rating.name, + status=post.status.name, + source=post.source) + + return render_template('manage/posts.html', posts=posts) + +@bp.route('/tags', defaults={'page': 1}) +@bp.route('/tags/') +@login_required +def manage_tags(page): + tags = Tag.query.order_by(Tag.content).paginate(page, current_app.config.get('MANAGE_PER_PAGE')) + + for tag in tags.items: + tag.editform = TagForm( + id=tag.id, + content=tag.content_deser, + category=tag.category.name) + + return render_template('manage/tags.html', tags=tags, createform=TagForm()) + + + +@bp.route('/modify_user', methods=['POST']) +@login_required +def modify_user(): + form = UserForm(request.form) + flash(str(request.form)) + if form.validate(): + if form.create.data: + el = User(username=form.username.data) + db.session.add(el) + + if form.email.data: el.email = form.email.data + if form.user_status.data: el.user_status = form.user_status.data + if form.op_level.data: el.op_level = form.op_level.data + + db.session.commit() + flash('New {} has been created.'.format(str(el))) + else: + el = User.query.filter_by(id=form.id.data).first() + if form.delete.data: + db.session.delete(el) + db.session.commit() + flash('{} deleted.'.format(str(el))) + elif form.edit.data: + print(form.username) + if form.username.data: el.username = form.username.data + + if form.email.data: el.email = form.email.data + if form.user_status.data: el.user_status = form.user_status.data + if form.op_level.data: el.op_level = form.op_level.data + + db.session.commit() + flash('Changes to {} have been applied.'.format(str(el))) + + return redirect(url_for('.manage_users')) + + +@bp.route('/modify_post', methods=['POST']) +@login_required +def modify_post(): + form = PostForm(request.form) + flash(str(request.form)) + if form.validate(): + if form.create.data: + pass + else: + el = Post.query.filter_by(id=form.id.data).first() + if form.delete.data: + db.session.delete(el) + db.session.commit() + flash('{} deleted.'.format(str(el))) + elif form.edit.data: + if form.rating.data: el.rating = form.rating.data + if form.status.data: el.status = form.status.data + if form.source.data: el.source = form.source.data + + db.session.commit() + flash('Changes to {} have been applied.'.format(str(el))) + + return redirect(url_for('.manage_posts')) + +# Example perfect create/edit/delete form endpoint +@bp.route('/modify_tag', methods=['POST']) +@login_required +def modify_tag(): + form = TagForm(request.form) + flash(str(request.form)) + if form.validate(): + if form.create.data: + el = Tag(content_deser=form.content.data) + db.session.add(el) + + if form.category.data: el.category = form.category.data + + db.session.commit() + flash('New {} has been created.'.format(str(el))) + else: + el = Tag.query.filter_by(id=form.id.data).first() + if form.delete.data: + db.session.delete(el) + db.session.commit() + flash('{} deleted.'.format(str(el))) + elif form.edit.data: + # if form.content.data: el.content = form.content.data + if form.category.data: el.category = form.category.data + + db.session.commit() + flash('Changes to {} have been applied.'.format(str(el))) + + return redirect(url_for('.manage_tags')) + + # return redirect(url_for('main.index')) + +# Creation/editing only through post page +@bp.route('/modify_comment', methods=['POST']) +@login_required +def modify_comment(): + form = CommentForm(request.form) + flash(str(request.form)) + if form.validate(): + if form.create.data: + el = Comment(content=form.content.data, post_id=form.post_id.data, user=current_user) + db.session.add(el) + + db.session.commit() + flash('Successfully submitted {}'.format(str(el))) + return redirect(url_for('.post_show', id=form.post_id.data)) + else: + el = Comment.query.filter_by(id=form.id.data).first() + if form.delete.data: + db.session.delete(el) + db.session.commit() + flash('{} deleted.'.format(str(el))) + elif form.edit.data: + if form.content.data: el.content = form.content.data + + db.session.commit() + flash('Changes to {} have been applied.'.format(str(el))) + + return redirect(url_for('.post_show', id=el.post_id)) + + return redirect(url_for('main.posts')) \ No newline at end of file diff --git a/yadc/bp/post.py b/yadc/bp/post.py index 0735bda..c173894 100644 --- a/yadc/bp/post.py +++ b/yadc/bp/post.py @@ -9,7 +9,7 @@ from sqlalchemy import func from sqlalchemy.orm import aliased from yadc import db -from yadc.forms import UploadForm, CommentForm, EditCommentForm +from yadc.forms import UploadForm, CommentForm#, EditCommentForm from yadc.models import FILETYPE, RATING, Post, Tag, Comment from yadc.utils import query_replace @@ -38,17 +38,15 @@ def posts(page): posts_query = Post.query if f_tags: posts_query = posts_query.join(Post.tags).group_by(Post.id).filter(Tag.content.in_(f_tags)).having(func.count(Post.id)==len(f_tags)) - posts_query = posts_query.filter(Post.rating.in_(m_ratings)).order_by(Post.created) #.offset((page-1)*posts_on_page).limit(posts_on_page) + posts_query = posts_query.filter(Post.rating.in_(m_ratings)).order_by(Post.created.desc()) posts = posts_query.paginate(page, current_app.config.get('POSTS_PER_PAGE')) tags = tags_prepare(posts.items) - flash(f_tags) - flash(m_ratings) - # flash(posts.items) - # flash(tags) + # flash(f_tags) + # flash(m_ratings) - return render_template('post/index.html', posts=posts.items, tags=tags, pagination=posts) + return render_template('post/index.html', posts=posts, tags=tags) @bp.route('/show/') def post_show(id): @@ -59,46 +57,16 @@ def post_show(id): for tag,tag.count in tags_count: tag.endpoint = query_replace({'tags': tag.content}, url_for('.posts')) - form = CommentForm(post_id=post.id) - for comment in post.comments: - comment.editform = EditCommentForm(comment_id=comment.id, content=comment.content) + comment.editform = CommentForm(id=comment.id, content=comment.content) - return render_template('post/post.html', post=post, tags=post.tags, comments=post.comments, comment_form=form) + return render_template('post/post.html', post=post, tags=post.tags, comments=post.comments, comment_form=CommentForm(post_id=post.id)) -@bp.route('comment', methods=['POST']) +from yadc.bp import manage +@bp.route('/comment', methods=['POST']) @login_required def comment(): - form = CommentForm(request.form) - if request.method == 'POST' and form.validate(): - comment = Comment(content=form.content.data, post_id=form.post_id.data, user=current_user) - - db.session.add(comment) - db.session.commit() - - flash('Successfully submitted {}'.format(str(comment))) - - return redirect(url_for('.post_show', id=form.post_id.data)) - - return redirect(url_for('.posts')) - -@bp.route('edit_comment', methods=['POST']) -@login_required -def comment_edit(): - form = EditCommentForm(request.form) - flash(str(request.form)) - if request.method == 'POST' and form.validate(): - comment = Comment.query.filter_by(id=form.comment_id.data).first() - comment.content = form.content.data - db.session.commit() - - flash('Successfully edited {}'.format(str(comment))) - - return redirect(url_for('.post_show', id=comment.post_id)) - - return redirect(url_for('.posts')) - - + return manage.modify_comment() @bp.route('/upload', methods=['GET', 'POST']) @login_required diff --git a/yadc/bp/user.py b/yadc/bp/user.py index b8ec992..220e4b8 100644 --- a/yadc/bp/user.py +++ b/yadc/bp/user.py @@ -1,10 +1,10 @@ from flask import (Blueprint, abort, current_app, flash, redirect, render_template, request, send_from_directory, url_for) from flask_login import current_user, login_required -from yadc.forms import ChangePassForm, EditUserForm, EditPostForm +from yadc.forms import ChangePassForm from yadc import db -from yadc.models import User, USER_STATUS, Post +from yadc.models import User bp = Blueprint('user', __name__) @@ -35,90 +35,3 @@ def settings(): return redirect(url_for('.settings')) return render_template('manage/profile.html', form=form) - -@bp.route('/manage_users', defaults={'page': 1}) -@bp.route('/manage_users/') -@login_required -def manage_users(page): - users = User.query.order_by(User.created.desc()).paginate(page, current_app.config.get('MANAGE_USERS_PER_PAGE')) - - for user in users.items: - user.editform = EditUserForm( - user_id=user.id, - username=user.username, - email=user.email, - op_level=user.op_level.name, - user_status=user.user_status.name) - - return render_template('manage/users.html', users=users.items, pagination=users) - -@bp.route('/manage_posts', defaults={'page': 1}) -@bp.route('/manage_posts/') -@login_required -def manage_posts(page): - posts = Post.query.order_by(Post.created.desc()).paginate(page, current_app.config.get('MANAGE_POSTS_PER_PAGE')) - - for post in posts.items: - post.editform = EditPostForm( - post_id=post.id, - rating=post.rating.name, - status=post.status.name, - source=post.source) - - return render_template('manage/posts.html', posts=posts.items, pagination=posts) - -@bp.route('user_modify', methods=['POST']) -@login_required -def modify_user(): - form = EditUserForm(request.form) - flash(str(request.form)) - if request.method == 'POST' and form.validate(): - user = User.query.filter_by(id=form.user_id.data).first() - if form.delete.data: - # user.user_status = USER_STATUS.inactive - #db.session.delete(user) - #db.session.commit() - flash('User {} deleted.'.format(str(user))) - elif form.edit.data: - if form.username.data: - user.username = form.username.data - if form.email.data: - user.email = form.email.data - if form.user_status.data: - user.user_status = form.user_status.data - if form.op_level.data: - user.op_level = form.op_level.data - - db.session.commit() - flash('Changes to {} has been applied.'.format(str(user))) - - return redirect(url_for('.manage_users')) - - return redirect(url_for('main.index')) - -@bp.route('post_modify', methods=['POST']) -@login_required -def modify_post(): - form = EditPostForm(request.form) - flash(str(request.form)) - if request.method == 'POST' and form.validate(): - post = Post.query.filter_by(id=form.post_id.data).first() - if form.delete.data: - # user.user_status = USER_STATUS.inactive - #db.session.delete(post) - #db.session.commit() - flash('User {} deleted.'.format(str(user))) - elif form.edit.data: - if form.rating.data: - post.rating = form.rating.data - if form.status.data: - post.status = form.status.data - if form.source.data: - post.source = form.source.data - - db.session.commit() - flash('Changes to {} has been applied.') - - return redirect(url_for('.manage_posts')) - - return redirect(url_for('main.index')) diff --git a/yadc/config.def.py b/yadc/config.def.py index ba7897d..30267ca 100644 --- a/yadc/config.def.py +++ b/yadc/config.def.py @@ -10,5 +10,4 @@ MAX_CONTENT_LENGTH = 10*1024*1024 POST_LIST_THUMB_HEIGHT = 200 POSTS_PER_PAGE = 8 -MANAGE_USERS_PER_PAGE = 8 -MANAGE_POSTS_PER_PAGE = 8 \ No newline at end of file +MANAGE_PER_PAGE = 8 \ No newline at end of file diff --git a/yadc/forms.py b/yadc/forms.py index 15fc69f..a466f96 100644 --- a/yadc/forms.py +++ b/yadc/forms.py @@ -1,6 +1,6 @@ from wtforms import Form from wtforms import StringField, PasswordField, BooleanField, SubmitField, FileField, MultipleFileField, ValidationError, RadioField, TextAreaField, HiddenField, SelectField -from wtforms.validators import DataRequired, InputRequired, Email, EqualTo, AnyOf, optional +from wtforms.validators import DataRequired, InputRequired, Email, EqualTo, AnyOf, optional, StopValidation from werkzeug.utils import cached_property @@ -80,10 +80,6 @@ class UploadForm(CSRFForm): if client_mimetype not in ['image/png','image/jpeg']: raise ValidationError('Please select an image file of PNG or JPEG format') -class CommentForm(CSRFForm): - post_id = HiddenField(validators=[DataRequired()]) - content = TextAreaField('Comment', validators=[DataRequired()], render_kw={'autocomplete':'off'}) - submit = SubmitField('Send') class ChangePassForm(CSRFForm): password_current = PasswordField('Current password', validators=[DataRequired()]) @@ -92,36 +88,53 @@ class ChangePassForm(CSRFForm): submit = SubmitField('Change password') -class EditUserForm(CSRFForm): - user_id = HiddenField(validators=[DataRequired()]) +from yadc.models import USER_STATUS, OP_LEVEL, RATING, POST_STATUS, TAG_CATEGORY - username = StringField('Username') +class EditForm(CSRFForm): + id = HiddenField('ID') + + create = SubmitField('Create') + edit = SubmitField('Modify') + delete = SubmitField('Delete') + + def validate_id(form, field): + if (form.edit.data or form.delete.data) and not field.data: + raise ValidationError('ID must be defined to be able to modify.') + +def validate_create_required(form, field): + if form.create.data and not field.data: + raise ValidationError('Please fill out this field.') + +class UserForm(EditForm): + username = StringField('Username', validators=[validate_create_required]) email = StringField('E-mail', validators=[optional(), Email()]) user_status = SelectField('User status', - choices=[('active', 'Active'), ('inactive', 'Inactive'), ('banned', 'Banned')], + choices=[(e.name, e.name.capitalize()) for e in USER_STATUS], validators=[optional()]) op_level = SelectField('Permission level', - choices=[('user', 'User'), ('creator', 'Creator'), ('moderator', 'Moderator'), ('admin', 'Admin')], + choices=[(e.name, e.name.capitalize()) for e in OP_LEVEL], validators=[optional()]) - edit = SubmitField('Modify') - delete = SubmitField('Delete') - -class EditPostForm(CSRFForm): - post_id = HiddenField(validators=[DataRequired()]) - +class PostForm(EditForm): rating = SelectField('Rating', - choices=[('safe', 'Safe'), ('questionable', 'Questionable'), ('explicit', 'Explicit')], + choices=[(e.name, e.name.capitalize()) for e in RATING], validators=[optional()]) status = SelectField('Status', - choices=[('pending', 'Pending'), ('active', 'Active'), ('deleted', 'Deleted')], + choices=[(e.name, e.name.capitalize()) for e in POST_STATUS], validators=[optional()]) source = StringField('Source', render_kw={'autocomplete':'off'}) - edit = SubmitField('Modify') - delete = SubmitField('Delete') +class TagForm(EditForm): + content = StringField('Content', validators=[validate_create_required], render_kw={'autocomplete':'off'}) + category = SelectField('Category', + choices=[(e.name, e.name.capitalize()) for e in TAG_CATEGORY], + validators=[optional()]) + +# Creation/editing only through post page +class CommentForm(EditForm): + post_id = HiddenField(validators=[validate_create_required]) + content = TextAreaField('Comment', render_kw={'autocomplete':'off'}) -class EditCommentForm(CSRFForm): - comment_id = HiddenField(validators=[DataRequired()]) - content = TextAreaField('Comment', validators=[DataRequired()]) - submit = SubmitField('Edit') + def validate_content(form, field): + if (form.create.data or form.edit.data) and not field.data: + raise ValidationError('Please fill out this field.') \ No newline at end of file diff --git a/yadc/models.py b/yadc/models.py index 3a20177..0cf60a5 100644 --- a/yadc/models.py +++ b/yadc/models.py @@ -227,14 +227,16 @@ class Post(TimestampMixin, db.Model): class Tag(TimestampMixin, db.Model): id = db.Column(db.Integer, primary_key=True) content = db.Column(db.String(128), unique=True, nullable=False) - category = db.Column(db.Enum(TAG_CATEGORY), default=TAG_CATEGORY.general) - - #posts = db.relationship('Post', secondary=post_tags, back_populates='tags') + category = db.Column(db.Enum(TAG_CATEGORY), default=TAG_CATEGORY.general, nullable=False) @property def content_deser(self): return self.content.replace('_',' ') + @content_deser.setter + def content_deser(self, value): + self.content = value.replace(' ', '_') + def __repr__(self): return ''.format(self.content, self.category.name) @@ -247,7 +249,7 @@ class Comment(TimestampMixin, db.Model): delete_reason = db.Column(db.String(512)) post_id = db.Column(db.Integer, db.ForeignKey('post.id')) - post = db.relationship('Post', backref=db.backref('comments', lazy=True)) + post = db.relationship('Post', backref=db.backref('comments', order_by='Comment.id', lazy=True)) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) user = db.relationship('User', backref=db.backref('comments', lazy=True)) diff --git a/yadc/static/all.css b/yadc/static/all.css index ac0976c..e9a1173 100644 --- a/yadc/static/all.css +++ b/yadc/static/all.css @@ -1 +1 @@ -*{box-sizing:border-box}body{margin:0;font-family:Verdana,Geneva,Tahoma,sans-serif;background-color:#222;color:#fff}a{color:#bbb;text-decoration:none}a:hover{color:#909090;text-decoration:none}header{display:flex;align-items:baseline;padding:0 10px;background-color:#222}header>a.logo{font-size:2em;margin:6px}@media(max-width:559px){header{position:relative}}header>nav{flex-grow:1;display:flex}header>nav>._overlay{display:none}@media(max-width:559px){header>nav{display:none}header>nav._drop{display:flex;flex-flow:column nowrap;position:absolute;margin:0;top:100%;left:0;right:0;z-index:10;background-color:#111d}header>nav>a,header>nav a#user-menu,header>nav .user_dropdown>a{padding:12px;padding-left:24px}header>nav>a:hover,header>nav>a:active,header>nav a#user-menu:hover,header>nav a#user-menu:active,header>nav .user_dropdown>a:hover,header>nav .user_dropdown>a:active{background-color:#333}header>nav .user::before{content:"";display:block;border-top:1px solid grey;margin:2px 12px}header>nav .user #user-menu{display:block}header>nav .user .user_dropdown{display:flex;flex-flow:column nowrap}header>nav>._overlay{display:block;position:fixed;width:100%;height:100%;z-index:-1;background-color:#0006}html.oh{overflow:hidden}}@media(min-width:560px){header>nav{margin:0 5px;align-items:center}header>nav>*{margin:0 5px;padding:6px 0}header>nav>.user{padding:0;margin-left:auto;margin-right:0;position:relative}header>nav>.user #user-menu{display:block;padding:6px 10px}header>nav>.user .user_dropdown{display:none}header>nav>.user .user_dropdown a{padding:10px}header>nav>.user .user_dropdown a:hover{background-color:#333}header>nav>.user:hover>.user_dropdown{display:flex;flex-flow:column nowrap;position:absolute;margin:0;top:100%;right:0;z-index:10;background-color:#111d}}header>#nav-menu{display:none}@media(max-width:559px){header>#nav-menu{display:block;margin:5px;margin-left:auto;padding:0 2px;align-self:center;font-size:2em;cursor:pointer}}.flash_msgs{display:flex;flex-flow:column-reverse nowrap;position:fixed;z-index:20;bottom:0;right:0;margin:0;padding:10px;font-size:.9em;pointer-events:none}.flash_msgs>li{width:250px;padding:10px;background-color:#000c;overflow:hidden;opacity:0;animation:fade 7s normal}.flash_msgs>li:not(:first-child){margin-bottom:10px}@keyframes fade{0%,100%{opacity:0}20%,80%{opacity:1}}.main_wrap{margin:0 auto;max-width:1300px;padding:8px;padding-top:0;padding-bottom:0}.main_wrap h3{margin:0;margin-bottom:10px;font-size:1.3em}.main_wrap form .tag_input .tag_suggester,.main_wrap .tag_input form .tag_suggester{width:calc(100% - 50px);position:relative}.main_wrap form .tag_input .tag_suggester>.tag_suggest_dropdown,.main_wrap .tag_input form .tag_suggester>.tag_suggest_dropdown{display:flex;flex-flow:column nowrap;align-items:start;top:100%;left:0;position:absolute;z-index:10;overflow:auto;background-color:#2d2d2de0}.main_wrap form .tag_input .tag_container,.main_wrap .tag_input form .tag_container{display:flex}.main_wrap form .tag_input .tag_container>a,.main_wrap .tag_input form .tag_container>a{margin:2px 2px;padding:.35em .6em;border-radius:4px;background-color:#121212ff;cursor:pointer}.main_wrap form .tag_input .tag_container>a>*,.main_wrap .tag_input form .tag_container>a>*{pointer-events:none}.main_wrap form .tag_input .tag_container>a>.fa-tag,.main_wrap .tag_input form .tag_container>a>.fa-tag{font-size:.9em;margin-right:2px}.main_wrap form .tag_input .tag_container>a>.count,.main_wrap .tag_input form .tag_container>a>.count{font-size:.8em}.main_wrap form .tag_input .tag_container>a:not(:hover)>span.close,.main_wrap .tag_input form .tag_container>a:not(:hover)>span.close{display:none}.main_wrap form .tag_input .tag_container>a:not(:hover)>span.plus,.main_wrap .tag_input form .tag_container>a:not(:hover)>span.plus{display:none}.main_wrap form .tag_input .tag_container>a:hover>span.count,.main_wrap .tag_input form .tag_container>a:hover>span.count{display:none}.main_wrap form .tag_input .tag_container>a.tagselected,.main_wrap .tag_input form .tag_container>a.tagselected{background-color:#400808ff}.main_wrap form .tag_input .tag_container>a.tag_hide,.main_wrap .tag_input form .tag_container>a.tag_hide{display:none}.main_wrap .important_subwrap{display:flex;overflow:visible}@media(max-width:899px){.main_wrap .important_subwrap{flex-flow:column nowrap}}@media(min-width:900px){.main_wrap .important_subwrap{flex-flow:row nowrap}}.main_wrap .important_subwrap>section.side_panel{flex-shrink:0;padding:10px}@media(min-width:900px){.main_wrap .important_subwrap>section.side_panel{width:18rem;height:0}}.main_wrap .important_subwrap>section.side_panel article{margin-bottom:10px}@media(max-width:899px){.main_wrap .important_subwrap>section.side_panel article.tags .tag_container{flex-flow:row wrap}}@media(min-width:900px){.main_wrap .important_subwrap>section.side_panel article.tags .tag_container{flex-flow:column nowrap;align-items:start}}.main_wrap .important_subwrap>section.side_panel article.post_desc{display:flex;flex-flow:column nowrap;font-size:.9em}.main_wrap .important_subwrap>section.side_panel article.post_desc>img{display:block;max-width:128px}@media(min-width:900px){.main_wrap .important_subwrap>section:not(.side_panel){width:100%}}.main_wrap section.post_list{overflow:hidden}@media(max-width:559px){.main_wrap section.post_list{margin-left:-8px;margin-right:-8px}}.main_wrap section.post_list .posts{display:flex;flex-flow:row wrap}@media(max-width:899px){.main_wrap section.post_list .posts{margin-left:-8px;margin-right:-8px}}.main_wrap section.post_list .posts::after{content:"";flex:10000 0 350px}.main_wrap section.post_list .posts>figure{position:relative;margin:8px}.main_wrap section.post_list .posts>figure img{display:block;width:100%;height:100%;transition:.2s ease}.main_wrap section.post_list .posts>figure:hover img{opacity:.7}@media(min-width:900px){.main_wrap section.post_single{padding:8px}}@media(max-width:899px){.main_wrap section.post_single{order:-1;margin-bottom:8px}}@media(max-width:559px){.main_wrap section.post_single{margin-left:-8px;margin-right:-8px}}.main_wrap section.post_single img{display:block;max-width:100%}.main_wrap section.comments{padding:10px}@media(min-width:900px){.main_wrap section.comments{margin-left:18rem}}.main_wrap section.comments>.comment_container{padding:0 10px;max-width:500px}.main_wrap section.comments>.comment_container article{overflow:hidden;margin-bottom:1em}.main_wrap section.comments>.comment_container h4{margin:0;margin-bottom:.5em}.main_wrap section.comments>.comment_container p,.main_wrap section.comments>.comment_container textarea{margin:0}.main_wrap section.comments>.comment_container p.deleted,.main_wrap section.comments>.comment_container textarea.deleted{color:red}.main_wrap section.comments>.comment_container .head{display:flex;justify-content:space-between}.main_wrap section.comments>.form>form{margin-left:10px;max-width:500px}.main_wrap section.comments>.form>form input:required{box-shadow:none}.main_wrap section.comments>.form>form textarea{width:100%;background:#444a;border:none;color:inherit;font:inherit}.main_wrap section.management_table table{margin:0 auto}.main_wrap section.management_table tr th{background-color:#606060}.main_wrap section.management_table tr:nth-child(even){background-color:#303030}.main_wrap section.management_table tr th,.main_wrap section.management_table tr td{padding:6px 10px}.main_wrap section.management_table tr:not(.edit) td>.edit{display:none}.main_wrap section.management_table tr.edit td>.show{display:none}.main_wrap section.management_table tr input[type=submit]{display:none}.main_wrap section.management_table tr .fa{padding:4px 4px;align-self:center;font-size:1.5em;cursor:pointer}.main_wrap form.editingable:not(.time-to-edit) .edit{display:none}.main_wrap form.editingable.time-to-edit .notedit{display:none}.main_wrap form.editingable input[type=text],.main_wrap form.editingable textarea{resize:vertical;width:100%}.main_wrap form.editingable input[type=text].edit,.edit .main_wrap form.editingable input[type=text],.main_wrap form.editingable textarea.edit,.edit .main_wrap form.editingable textarea{background:#444a;border:none;color:inherit;font:inherit}.main_wrap form input{margin:5px 0}.main_wrap .pagin{margin:10px 0;display:flex;flex-flow:row nowrap;justify-content:center}.main_wrap .pagin>a{display:block;background-color:#0005;padding:8px 12px;margin:0 2px}footer{padding:10px;text-align:center} \ No newline at end of file +*{box-sizing:border-box}body{margin:0;font-family:Verdana,Geneva,Tahoma,sans-serif;background-color:#222;color:#fff}a{color:#bbb;text-decoration:none}a:hover{color:#909090;text-decoration:none}header{display:flex;align-items:baseline;padding:0 10px;background-color:#222}header>a.logo{font-size:2em;margin:6px}@media(max-width:559px){header{position:relative}}header>nav{flex-grow:1;display:flex}header>nav>._overlay{display:none}@media(max-width:559px){header>nav{display:none}header>nav._drop{display:flex;flex-flow:column nowrap;position:absolute;margin:0;top:100%;left:0;right:0;z-index:10;background-color:#111d}header>nav>a,header>nav a#user-menu,header>nav .user_dropdown>a{padding:12px;padding-left:24px}header>nav>a:hover,header>nav>a:active,header>nav a#user-menu:hover,header>nav a#user-menu:active,header>nav .user_dropdown>a:hover,header>nav .user_dropdown>a:active{background-color:#333}header>nav .user::before{content:"";display:block;border-top:1px solid grey;margin:2px 12px}header>nav .user #user-menu{display:block}header>nav .user .user_dropdown{display:flex;flex-flow:column nowrap}header>nav>._overlay{display:block;position:fixed;width:100%;height:100%;z-index:-1;background-color:#0006}html.oh{overflow:hidden}}@media(min-width:560px){header>nav{margin:0 5px;align-items:center}header>nav>*{margin:0 5px;padding:6px 0}header>nav>.user{padding:0;margin-left:auto;margin-right:0;position:relative}header>nav>.user #user-menu{display:block;padding:6px 10px}header>nav>.user .user_dropdown{display:none}header>nav>.user .user_dropdown a{padding:10px}header>nav>.user .user_dropdown a:hover{background-color:#333}header>nav>.user:hover>.user_dropdown{display:flex;flex-flow:column nowrap;position:absolute;margin:0;top:100%;right:0;z-index:10;background-color:#111d}}header>#nav-menu{display:none}@media(max-width:559px){header>#nav-menu{display:block;margin:5px;margin-left:auto;padding:0 2px;align-self:center;font-size:2em;cursor:pointer}}.flash_msgs{display:flex;flex-flow:column-reverse nowrap;position:fixed;z-index:20;bottom:0;right:0;margin:0;padding:10px;font-size:.9em;pointer-events:none}.flash_msgs>li{width:250px;padding:10px;background-color:#000c;overflow:hidden;opacity:0;animation:fade 7s normal}.flash_msgs>li:not(:first-child){margin-bottom:10px}@keyframes fade{0%,100%{opacity:0}20%,80%{opacity:1}}.main_wrap{margin:0 auto;max-width:1300px;padding:8px;padding-top:0;padding-bottom:0}.main_wrap h3{margin:0;margin-bottom:10px;font-size:1.3em}.main_wrap form .tag_input .tag_suggester,.main_wrap .tag_input form .tag_suggester{width:calc(100% - 50px);position:relative}.main_wrap form .tag_input .tag_suggester>.tag_suggest_dropdown,.main_wrap .tag_input form .tag_suggester>.tag_suggest_dropdown{display:flex;flex-flow:column nowrap;align-items:start;top:100%;left:0;position:absolute;z-index:10;overflow:auto;background-color:#2d2d2de0}.main_wrap form .tag_input .tag_container,.main_wrap .tag_input form .tag_container{display:flex}.main_wrap form .tag_input .tag_container>a,.main_wrap .tag_input form .tag_container>a{margin:2px 2px;padding:.35em .6em;border-radius:4px;background-color:#121212ff;cursor:pointer}.main_wrap form .tag_input .tag_container>a>*,.main_wrap .tag_input form .tag_container>a>*{pointer-events:none}.main_wrap form .tag_input .tag_container>a>.fa-tag,.main_wrap .tag_input form .tag_container>a>.fa-tag{font-size:.9em;margin-right:2px}.main_wrap form .tag_input .tag_container>a>.count,.main_wrap .tag_input form .tag_container>a>.count{font-size:.8em}.main_wrap form .tag_input .tag_container>a:not(:hover)>span.close,.main_wrap .tag_input form .tag_container>a:not(:hover)>span.close{display:none}.main_wrap form .tag_input .tag_container>a:not(:hover)>span.plus,.main_wrap .tag_input form .tag_container>a:not(:hover)>span.plus{display:none}.main_wrap form .tag_input .tag_container>a:hover>span.count,.main_wrap .tag_input form .tag_container>a:hover>span.count{display:none}.main_wrap form .tag_input .tag_container>a.tagselected,.main_wrap .tag_input form .tag_container>a.tagselected{background-color:#400808ff}.main_wrap form .tag_input .tag_container>a.tag_hide,.main_wrap .tag_input form .tag_container>a.tag_hide{display:none}.main_wrap .important_subwrap{display:flex;overflow:visible}@media(max-width:899px){.main_wrap .important_subwrap{flex-flow:column nowrap}}@media(min-width:900px){.main_wrap .important_subwrap{flex-flow:row nowrap}}.main_wrap .important_subwrap>section.side_panel{flex-shrink:0;padding:10px}@media(min-width:900px){.main_wrap .important_subwrap>section.side_panel{width:18rem;height:0}}.main_wrap .important_subwrap>section.side_panel article{margin-bottom:10px}@media(max-width:899px){.main_wrap .important_subwrap>section.side_panel article.tags .tag_container{flex-flow:row wrap}}@media(min-width:900px){.main_wrap .important_subwrap>section.side_panel article.tags .tag_container{flex-flow:column nowrap;align-items:start}}.main_wrap .important_subwrap>section.side_panel article.post_desc{display:flex;flex-flow:column nowrap;font-size:.9em}.main_wrap .important_subwrap>section.side_panel article.post_desc>img{display:block;max-width:128px}@media(min-width:900px){.main_wrap .important_subwrap>section:not(.side_panel){width:100%}}.main_wrap section.post_list{overflow:hidden}@media(max-width:559px){.main_wrap section.post_list{margin-left:-8px;margin-right:-8px}}.main_wrap section.post_list .posts{display:flex;flex-flow:row wrap}@media(max-width:899px){.main_wrap section.post_list .posts{margin-left:-8px;margin-right:-8px}}.main_wrap section.post_list .posts::after{content:"";flex:10000 0 350px}.main_wrap section.post_list .posts>figure{position:relative;margin:8px}.main_wrap section.post_list .posts>figure img{display:block;width:100%;height:100%;transition:.2s ease}.main_wrap section.post_list .posts>figure:hover img{opacity:.7}@media(min-width:900px){.main_wrap section.post_single{padding:8px}}@media(max-width:899px){.main_wrap section.post_single{order:-1;margin-bottom:8px}}@media(max-width:559px){.main_wrap section.post_single{margin-left:-8px;margin-right:-8px}}.main_wrap section.post_single img{display:block;max-width:100%}.main_wrap section.comments{padding:10px}@media(min-width:900px){.main_wrap section.comments{margin-left:18rem}}.main_wrap section.comments>.comment_container{padding:0 10px;max-width:500px}.main_wrap section.comments>.comment_container article.comment{overflow:hidden;margin-bottom:1em}.main_wrap section.comments>.comment_container h4{margin:0;margin-bottom:.5em}.main_wrap section.comments>.comment_container p,.main_wrap section.comments>.comment_container textarea{margin:0}.main_wrap section.comments>.comment_container p.deleted,.main_wrap section.comments>.comment_container textarea.deleted{color:red}.main_wrap section.comments>.comment_container .comment_head{display:flex;justify-content:space-between}.main_wrap section.comments>.form>form{margin-left:10px;max-width:500px}.main_wrap section.comments>.form>form input:required{box-shadow:none}.main_wrap section.comments>.form>form textarea{width:100%;background:#444a;border:none;color:inherit;font:inherit}.main_wrap section.management_table table{margin:0 auto}.main_wrap section.management_table tr{background-color:#303030}.main_wrap section.management_table tr th{background-color:#606060}.main_wrap section.management_table tr th,.main_wrap section.management_table tr td{padding:6px 10px}.main_wrap section.management_table tr:not(.edit) td>.edit{display:none}.main_wrap section.management_table tr.edit td>.show{display:none}.main_wrap section.management_table tr input[type=submit]:not([name=create]){display:none}.main_wrap section.management_table tr .fa{padding:4px 4px;align-self:center;font-size:1.5em;cursor:pointer}.main_wrap form.editingable:not(.time-to-edit) .edit{display:none}.main_wrap form.editingable.time-to-edit .notedit{display:none}.main_wrap form.editingable input[type=text],.main_wrap form.editingable textarea{resize:vertical;width:100%}.main_wrap form.editingable input[type=text].edit,.edit .main_wrap form.editingable input[type=text],.main_wrap form.editingable textarea.edit,.edit .main_wrap form.editingable textarea{background:#444a;border:none;color:inherit;font:inherit}.main_wrap form input{margin:5px 0}.main_wrap .pagin{margin:10px 0;display:flex;flex-flow:row nowrap;justify-content:center}.main_wrap .pagin>a{display:block;background-color:#0005;padding:8px 12px;margin:0 2px}footer{padding:10px;text-align:center} \ No newline at end of file diff --git a/yadc/static/comment.js b/yadc/static/comment.js new file mode 100644 index 0000000..3ceb422 --- /dev/null +++ b/yadc/static/comment.js @@ -0,0 +1,17 @@ +let comment_container = document.querySelector('section.comments .comment_container') + +let comments = comment_container.querySelectorAll('.comment') + +function triggerEdit(comment) { + let form = comment.querySelector('form.editingable') + if (!form.classList.contains('time-to-edit')) { + form.classList.add('time-to-edit') + } else { + form.classList.remove('time-to-edit') + } +} + +comments.forEach((comment) => { + // comment.querySelector('p.comment_content').addEventListener('dblclick', (event) => triggerEdit(comment)) + comment.querySelector('.comment_head .control_edit').addEventListener('click', (event) => triggerEdit(comment)) +}) \ No newline at end of file diff --git a/yadc/static/default.scss b/yadc/static/default.scss index d85fbc5..e21c42c 100644 --- a/yadc/static/default.scss +++ b/yadc/static/default.scss @@ -482,7 +482,7 @@ header { max-width: 500px; - article { + article.comment { overflow: hidden; margin-bottom: 1em; } @@ -501,7 +501,7 @@ header { } } - .head { + .comment_head { display: flex; justify-content: space-between; } @@ -542,9 +542,9 @@ header { th { background-color: #606060; } - &:nth-child(even) { + // &:nth-child(even) { background-color: #303030; - } + // } th, td { padding: 6px 10px; @@ -564,7 +564,7 @@ header { display: none; } } - input[type=submit] { + input[type=submit]:not([name=create]) { display: none; } diff --git a/yadc/templates/_formhelpers.html b/yadc/templates/_formhelpers.html index ae118e2..4be005f 100644 --- a/yadc/templates/_formhelpers.html +++ b/yadc/templates/_formhelpers.html @@ -10,5 +10,10 @@ {% macro management_gen_line(field, formfield) %} {{ field }} -{{ formfield() }}{{ errors(formfield) }} +{{ formfield() }} +{{ errors(formfield) }} +{% endmacro %} + +{% macro management_row_gen(field_formfield) %} + {% endmacro %} \ No newline at end of file diff --git a/yadc/templates/_includes.html b/yadc/templates/_includes.html index 99ef5fc..ec5dabd 100644 --- a/yadc/templates/_includes.html +++ b/yadc/templates/_includes.html @@ -1,5 +1,5 @@ -{% macro render_pagination(endpoint) %} +{% macro render_pagination(endpoint, pagination) %}
{% if pagination.has_prev %} diff --git a/yadc/templates/layout/base_sidebar_tags.html b/yadc/templates/layout/base_sidebar_tags.html index 059b373..a7b9bb1 100644 --- a/yadc/templates/layout/base_sidebar_tags.html +++ b/yadc/templates/layout/base_sidebar_tags.html @@ -2,10 +2,10 @@ {% block sidebar %}
-
+

Search

- +
diff --git a/yadc/templates/manage/posts.html b/yadc/templates/manage/posts.html index ac8d9d1..ecc5997 100644 --- a/yadc/templates/manage/posts.html +++ b/yadc/templates/manage/posts.html @@ -12,22 +12,20 @@ Rating Status Source - Original Filename Manage - {% for post in posts %} - + {% for post in posts.items %} + {{ post.editform.csrf_token }} - {{ post.editform.post_id() }} + {{ post.editform.id() }} {{ post.id }}
{{ post.md5[:7] }}
{{ management_gen_line(post.rating.name.capitalize(), post.editform.rating) }} {{ management_gen_line(post.status.name.capitalize(), post.editform.status) }} {{ management_gen_line(post.source, post.editform.source) }} - {{ post.origin_filename }} @@ -39,6 +37,6 @@ {% endfor %} - {{ render_pagination('user.manage_posts') }} + {{ render_pagination('manage.manage_posts', posts) }} {% endblock %} \ No newline at end of file diff --git a/yadc/templates/manage/tags.html b/yadc/templates/manage/tags.html new file mode 100644 index 0000000..965147f --- /dev/null +++ b/yadc/templates/manage/tags.html @@ -0,0 +1,47 @@ +{% extends 'layout/settings.html' %} +{% from '_includes.html' import render_pagination %} +{% from "_formhelpers.html" import management_gen_line, errors %} + +{% block main_content %} +
+ + + + + + + + + + + {% for tag in tags.items %} + + {{ tag.editform.csrf_token }} + {{ tag.editform.id() }} + + + + + + + + {% endfor %} + + {{ createform.csrf_token }} + + + + + + + + +
ContentCategoryManage
{{ tag.content_deser }}{{ management_gen_line(tag.category.name.capitalize(), tag.editform.category) }} + + + + +
{{ createform.content() }}{{ createform.category() }}{{ createform.create() }}
+ {{ render_pagination('manage.manage_tags', tags) }} +
+{% endblock %} \ No newline at end of file diff --git a/yadc/templates/manage/users.html b/yadc/templates/manage/users.html index 1a0cd25..be01190 100644 --- a/yadc/templates/manage/users.html +++ b/yadc/templates/manage/users.html @@ -16,16 +16,16 @@ - {% for user in users %} -
+ {% for user in users.items %} + {{ user.editform.csrf_token }} - {{ user.editform.user_id() }} + {{ user.editform.id() }} {{ user.id }} {{ management_gen_line(user.username, user.editform.username) }} {{ management_gen_line(user.user_status.name.capitalize(), user.editform.user_status) }} {{ management_gen_line(user.op_level.name.capitalize(), user.editform.op_level) }} - {{ user.last_login.strftime('%I:%M %p %d %b, %Y') }} + {{ user.last_login.strftime('%I:%M %p %d %b, %Y') if user.last_login else 'Not yet logged in' }} @@ -35,8 +35,20 @@
{% endfor %} + {##} - {{ render_pagination('user.manage_users') }} + {{ render_pagination('manage.manage_users', users) }} {% endblock %} \ No newline at end of file diff --git a/yadc/templates/post/index.html b/yadc/templates/post/index.html index 2b3883e..0ec5253 100644 --- a/yadc/templates/post/index.html +++ b/yadc/templates/post/index.html @@ -1,10 +1,12 @@ {% extends 'layout/base_sidebar_tags.html' %} {% from '_includes.html' import render_pagination with context %} +{% block search_endpoint %}{% endblock %} + {% block main_content %}
- {% for post in posts %} + {% for post in posts.items %}
@@ -23,6 +25,6 @@
-->
- {{ render_pagination('post.posts') }} + {{ render_pagination('post.posts', posts) }} {% endblock %} \ No newline at end of file diff --git a/yadc/templates/post/post.html b/yadc/templates/post/post.html index f1f93de..3eeefb2 100644 --- a/yadc/templates/post/post.html +++ b/yadc/templates/post/post.html @@ -52,23 +52,25 @@

Comments

{% for comment in comments %} -
-
+
+

{{ comment.user.username }}

{% if comment.can_edit %} - edit + + edit + {% endif %}
{% if not comment.deleted %} -
+ {{ comment.editform.csrf_token }} - {{ comment.editform.comment_id() }} -

{{ comment.content }}

+ {{ comment.editform.id() }} +

{{ comment.content }}

{{ comment.editform.content(class="edit") }} - {{ comment.editform.submit(class="edit") }} + {{ comment.editform.edit(class="edit") }}
{% else %} @@ -82,7 +84,7 @@

No comments so far.

{% endif %}
- + {% if current_user.is_authenticated %}
@@ -94,7 +96,7 @@ {{ comment_form.content() }} {{ errors(comment_form.content) }}
-
{{ comment_form.submit() }}
+
{{ comment_form.create() }}
{% else %}