diff --git a/yadc/__init__.py b/yadc/__init__.py index 8175138..b10e2b9 100644 --- a/yadc/__init__.py +++ b/yadc/__init__.py @@ -56,7 +56,7 @@ def create_app(): 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) + # app.register_blueprint(api.bp) # now deprecated login.login_view = 'auth.login' diff --git a/yadc/bp/api.py b/yadc/bp/api.py index a87a056..993c612 100644 --- a/yadc/bp/api.py +++ b/yadc/bp/api.py @@ -6,18 +6,7 @@ from yadc.models import Tag bp = Blueprint('api', __name__) @bp.route('/posts.json') -@bp.route('/post/index.json') def post_index(): return jsonify( get=request.args, post=request.form) - -@bp.route('/api/tags') -def tag_autocomplete(): - query = request.args.get('q', '') - if query != '': - tags = Tag.query.filter(Tag.content.like("%{}%".format(query))).limit(15).all() - - return jsonify([{"id": t.id, "content": t.content, "content_deser": t.content_deser, "category": {"id": t.category.value, "name": t.category.name}} for t in tags]) - return jsonify() - # tags = request.args.get('tags', '') \ No newline at end of file diff --git a/yadc/bp/manage.py b/yadc/bp/manage.py index 4dad24a..ea5fc77 100644 --- a/yadc/bp/manage.py +++ b/yadc/bp/manage.py @@ -4,13 +4,14 @@ 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 +from yadc.models import User, USER_STATUS, moderator_required, admin_required, Post, Tag, TAG_CATEGORY, Comment bp = Blueprint('manage', __name__) @bp.route('/users', defaults={'page': 1}) @bp.route('/users/') @login_required +@admin_required def manage_users(page): users = User.query.order_by(User.id.desc()).paginate(page, current_app.config.get('MANAGE_PER_PAGE')) @@ -27,6 +28,7 @@ def manage_users(page): @bp.route('/posts', defaults={'page': 1}) @bp.route('/posts/') @login_required +@moderator_required def manage_posts(page): posts = Post.query.order_by(Post.id.desc()).paginate(page, current_app.config.get('MANAGE_PER_PAGE')) @@ -42,6 +44,7 @@ def manage_posts(page): @bp.route('/tags', defaults={'page': 1}) @bp.route('/tags/') @login_required +@moderator_required def manage_tags(page): tags = Tag.query.order_by(Tag.content).paginate(page, current_app.config.get('MANAGE_PER_PAGE')) @@ -57,6 +60,7 @@ def manage_tags(page): @bp.route('/modify_user', methods=['POST']) @login_required +@admin_required def modify_user(): form = UserForm(request.form) flash(str(request.form)) @@ -93,6 +97,7 @@ def modify_user(): @bp.route('/modify_post', methods=['POST']) @login_required +@moderator_required def modify_post(): form = PostForm(request.form) flash(str(request.form)) @@ -118,6 +123,7 @@ def modify_post(): # Example perfect create/edit/delete form endpoint @bp.route('/modify_tag', methods=['POST']) @login_required +@moderator_required def modify_tag(): form = TagForm(request.form) flash(str(request.form)) @@ -144,7 +150,6 @@ def modify_tag(): 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 @@ -163,6 +168,8 @@ def modify_comment(): return redirect(url_for('.post_show', id=form.post_id.data)) else: el = Comment.query.filter_by(id=form.id.data).first() + if not current_user.is_moderator or not el.is_current: + return abort(403) if form.delete.data: db.session.delete(el) db.session.commit() diff --git a/yadc/bp/post.py b/yadc/bp/post.py index c173894..e1560ed 100644 --- a/yadc/bp/post.py +++ b/yadc/bp/post.py @@ -2,14 +2,14 @@ import io import os from flask import (Blueprint, abort, current_app, flash, redirect, - render_template, request, send_from_directory, url_for) + render_template, request, send_from_directory, url_for, session, jsonify) from flask_login import current_user, login_required from PIL import Image 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, PostForm from yadc.models import FILETYPE, RATING, Post, Tag, Comment from yadc.utils import query_replace @@ -33,7 +33,9 @@ def posts(page): # PARSING ARGUMENTS f_tags = request.args.get('tags', '').split() - m_ratings = RATING.matched(request.args.get('rating')) + # m_ratings = RATING.matched(request.args.get('rating')) + f_rating = {r.name : r for r in RATING}.get(request.args.get('rating'), RATING.safe) + m_ratings = f_rating.matched posts_query = Post.query if f_tags: @@ -45,6 +47,8 @@ def posts(page): # flash(f_tags) # flash(m_ratings) + # session['index_settings'] = dict(tags=f_tags, rating=f_rating) + session['index_settings'] = dict(tags=request.args.get('tags', ''), rating=f_rating.name) return render_template('post/index.html', posts=posts, tags=tags) @@ -60,7 +64,7 @@ def post_show(id): for comment in post.comments: 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=CommentForm(post_id=post.id)) + return render_template('post/post.html', post=post, tags=post.tags, comments=post.comments, editform=PostForm(id=post.id), comment_form=CommentForm(post_id=post.id)) from yadc.bp import manage @bp.route('/comment', methods=['POST']) @@ -109,4 +113,82 @@ def upload(): #return redirect(url_for('.upload')) return redirect(url_for('.posts')) - return render_template('post/upload.html', form=form) \ No newline at end of file + return render_template('post/upload.html', form=form) + +@bp.route('/tags') +def tag_autocomplete(): + query = request.args.get('q', '') + if query != '': + tags = Tag.query.filter(Tag.content.like("%{}%".format(query))).limit(15).all() + + return jsonify([{"id": t.id, "content": t.content, "content_deser": t.content_deser, "category": {"id": t.category.value, "name": t.category.name}} for t in tags]) + return jsonify() + +# A TRY TO MAKE A DANBOORU COMPATIBLE API +# import json +@bp.route('/index.json') +def post_index(): + # return jsonify(json.load(open('index.json', 'r'))) + # return jsonify(json.load(open('test.json', 'r'))) + + f_tags = request.args.get('tags', '').split() + + # rating = None + for t in f_tags: + if t.startswith('rating:') or t.startswith('-rating:'): + f_tags.remove(t) + # rating = t.split(':')[1] + # f_rating = {r.name : r for r in RATING}.get(rating, RATING.safe) + # m_ratings = f_rating.matched + + 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.desc()) + posts_query = posts_query.order_by(Post.created.desc()) + + posts = posts_query.paginate(int(request.args.get('page', 0))+1, int(request.args.get('limit') or current_app.config.get('POSTS_PER_PAGE'))) + + return jsonify([dict( + id=p.id, + tags=" ".join([t.content for t in p.tags]), + created_at=int(p.created.timestamp()), + creator_id=p.author_id, + source=p.source, + md5=p.md5, + file_size=p.filesize, + file_url=p.url(path=p.image_url, endpoint='img'), + preview_url=p.url(path=p.image_url, endpoint='thumb'), + + preview_width=0, + preview_height=0, + actual_preview_width=0, + actual_preview_height=0, + + sample_url=p.url(path=p.image_url, endpoint='sample'), + + sample_width=0, + sample_height=0, + sample_file_size=0, + + jpeg_url=p.url(path=p.image_url, endpoint='img') if p.filetype is FILETYPE.jpeg else p.url(path=p.image_url, endpoint='jpeg'), + + jpeg_width=0, + jpeg_height=0, + jpeg_file_size=0, + + rating=str(p.rating.name)[:1], + + has_children=False, + parent_id=None, + + status=p.status.name, + width=p.width, + height=p.height, + + is_held=False, + frames_pending_string='', + frames_pending=[], + frames_string='', + frames=[] + ) for p in posts.items]) \ No newline at end of file diff --git a/yadc/bp/user.py b/yadc/bp/user.py index 220e4b8..dbfc87a 100644 --- a/yadc/bp/user.py +++ b/yadc/bp/user.py @@ -34,4 +34,4 @@ def settings(): flash('Password changed successfully.') return redirect(url_for('.settings')) - return render_template('manage/profile.html', form=form) + return render_template('user/settings.html', form=form) diff --git a/yadc/forms.py b/yadc/forms.py index a466f96..9430d44 100644 --- a/yadc/forms.py +++ b/yadc/forms.py @@ -116,6 +116,7 @@ class UserForm(EditForm): validators=[optional()]) class PostForm(EditForm): + tags = StringField('Tags', render_kw={'placeholder':'Tags','autocomplete':'off'}) rating = SelectField('Rating', choices=[(e.name, e.name.capitalize()) for e in RATING], validators=[optional()]) diff --git a/yadc/models.py b/yadc/models.py index 0cf60a5..23d0b2d 100644 --- a/yadc/models.py +++ b/yadc/models.py @@ -3,8 +3,9 @@ import hashlib import humanize import os from datetime import datetime, timezone +from functools import wraps -from flask import current_app, url_for +from flask import current_app, url_for, abort from flask_login import UserMixin, login_user, logout_user, current_user from PIL import Image from sqlalchemy_utc import UtcDateTime, utcnow @@ -33,10 +34,14 @@ class RATING(enum.Enum): questionable = 1 explicit = 2 - @staticmethod - def matched(rating=None): - rat = {r.name : r for r in RATING}.get(rating, RATING.safe) - return [r for r in RATING if r.value<=rat.value] + # @staticmethod + # def matched(rating=None): + # rat = {r.name : r for r in RATING}.get(rating, RATING.safe) + # return [r for r in RATING if r.value<=rat.value] + + @property + def matched(self): + return [r for r in RATING if r.value<=self.value] class POST_STATUS(enum.Enum): pending = 0 @@ -108,10 +113,33 @@ class User(UserMixin, TimestampMixin, db.Model): def is_admin(self): return self.op_level is OP_LEVEL.admin + @property + def is_current(self): + return self == current_user + + @login.user_loader def load_user(id): return User.query.get(int(id)) +def moderator_required(func): + @wraps(func) + def dec_view(*args, **kwargs): + if current_user: + if not current_user.is_moderator or not current_user.is_admin: + return abort(403) + return func(*args, **kwargs) + return dec_view + +def admin_required(func): + @wraps(func) + def dec_view(*args, **kwargs): + if current_user: + if not current_user.is_admin: + return abort(403) + return func(*args, **kwargs) + return dec_view + # class UserPreferences(db.Model): # user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True) # user = db.relationship('User', backref=db.backref('profile', uselist=False)) diff --git a/yadc/static/search.js b/yadc/static/search.js index daaf998..9eee35e 100644 --- a/yadc/static/search.js +++ b/yadc/static/search.js @@ -13,7 +13,8 @@ if (suggest_input.hasAttribute('required')) { suggest_input.removeAttribute('required') suggest_hidden.setAttribute('required', '') } -suggest_input.value = '' +// suggest_input.value = '' +suggest_input.removeAttribute('value') let tag_suggest_dropdown = suggestroot.querySelector('.tag_suggest_dropdown') @@ -152,7 +153,7 @@ suggest_input.addEventListener('input', (event) => { } ajax_timeout = setTimeout(() => { - fetch('/api/tags?q='+value).then((response) => { + fetch('/post/tags?q='+value).then((response) => { console.log(response) return response.json() }).then((data) => { diff --git a/yadc/templates/layout/base.html b/yadc/templates/layout/base.html index b4aebfb..b1fd100 100644 --- a/yadc/templates/layout/base.html +++ b/yadc/templates/layout/base.html @@ -32,7 +32,7 @@ Login Register {% else %} - Profile + Profile Settings Log out {% endif%} diff --git a/yadc/templates/layout/base_sidebar_tags.html b/yadc/templates/layout/base_sidebar_tags.html index a7b9bb1..a1c4138 100644 --- a/yadc/templates/layout/base_sidebar_tags.html +++ b/yadc/templates/layout/base_sidebar_tags.html @@ -12,9 +12,7 @@
{% for rating in ('safe', 'questionable', 'explicit') %} - +
{% endfor %} diff --git a/yadc/templates/layout/settings.html b/yadc/templates/layout/settings.html index e173252..5e38e8e 100644 --- a/yadc/templates/layout/settings.html +++ b/yadc/templates/layout/settings.html @@ -1,6 +1,11 @@ {% extends 'layout/base_sidebar.html' %} -{% from '_includes.html' import render_sidenav with context %} {% block sidebar %} - {{ render_sidenav({"speedtest": ("Speedtest", "http://speedtest.cesnet.cz")}) }} + {% endblock %} diff --git a/yadc/templates/post/post.html b/yadc/templates/post/post.html index 3eeefb2..98fd3b7 100644 --- a/yadc/templates/post/post.html +++ b/yadc/templates/post/post.html @@ -1,5 +1,5 @@ {% extends 'layout/base_sidebar_tags.html' %} -{% from '_includes.html' import render_post_edit with context %} +{% from '_includes.html' import render_post_edit, render_tag_input with context %} {% from "_formhelpers.html" import errors %} {% block sidebar %} @@ -33,7 +33,20 @@ {% if post.can_edit %}

Edit

- edit? +
+ {{ editform.csrf_token }} + {{ editform.id() }} +
+ {{ render_tag_input(editform.tags) }} + {{ errors(editform.tags) }} +
+
+ {{ editform.source }} +
+
+ {{ editform.edit() }} +
+
{% endif %} {% endblock %} @@ -54,7 +67,7 @@ {% for comment in comments %}
-

{{ comment.user.username }}

+

{{ comment.user.username or "Deleted account" }}

{% if comment.can_edit %} edit diff --git a/yadc/templates/manage/profile.html b/yadc/templates/user/settings.html similarity index 100% rename from yadc/templates/manage/profile.html rename to yadc/templates/user/settings.html