1
1
Fork 0
few permission fixes,
prepares for post edit form,
few frontend quirks including sidebar in management
dev
Jan Kužílek 5 years ago
parent 3ebe1c0b67
commit 3302de5668

@ -56,7 +56,7 @@ def create_app():
app.register_blueprint(auth.bp, url_prefix='/auth') app.register_blueprint(auth.bp, url_prefix='/auth')
app.register_blueprint(manage.bp, url_prefix='/manage') app.register_blueprint(manage.bp, url_prefix='/manage')
app.register_blueprint(user.bp, url_prefix='/user') 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' login.login_view = 'auth.login'

@ -6,18 +6,7 @@ from yadc.models import Tag
bp = Blueprint('api', __name__) bp = Blueprint('api', __name__)
@bp.route('/posts.json') @bp.route('/posts.json')
@bp.route('/post/index.json')
def post_index(): def post_index():
return jsonify( return jsonify(
get=request.args, get=request.args,
post=request.form) 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', '')

@ -4,13 +4,14 @@ from flask_login import login_required, current_user
from yadc.forms import UserForm, PostForm, TagForm, CommentForm from yadc.forms import UserForm, PostForm, TagForm, CommentForm
from yadc import db 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 = Blueprint('manage', __name__)
@bp.route('/users', defaults={'page': 1}) @bp.route('/users', defaults={'page': 1})
@bp.route('/users/<int:page>') @bp.route('/users/<int:page>')
@login_required @login_required
@admin_required
def manage_users(page): def manage_users(page):
users = User.query.order_by(User.id.desc()).paginate(page, current_app.config.get('MANAGE_PER_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', defaults={'page': 1})
@bp.route('/posts/<int:page>') @bp.route('/posts/<int:page>')
@login_required @login_required
@moderator_required
def manage_posts(page): def manage_posts(page):
posts = Post.query.order_by(Post.id.desc()).paginate(page, current_app.config.get('MANAGE_PER_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', defaults={'page': 1})
@bp.route('/tags/<int:page>') @bp.route('/tags/<int:page>')
@login_required @login_required
@moderator_required
def manage_tags(page): def manage_tags(page):
tags = Tag.query.order_by(Tag.content).paginate(page, current_app.config.get('MANAGE_PER_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']) @bp.route('/modify_user', methods=['POST'])
@login_required @login_required
@admin_required
def modify_user(): def modify_user():
form = UserForm(request.form) form = UserForm(request.form)
flash(str(request.form)) flash(str(request.form))
@ -93,6 +97,7 @@ def modify_user():
@bp.route('/modify_post', methods=['POST']) @bp.route('/modify_post', methods=['POST'])
@login_required @login_required
@moderator_required
def modify_post(): def modify_post():
form = PostForm(request.form) form = PostForm(request.form)
flash(str(request.form)) flash(str(request.form))
@ -118,6 +123,7 @@ def modify_post():
# Example perfect create/edit/delete form endpoint # Example perfect create/edit/delete form endpoint
@bp.route('/modify_tag', methods=['POST']) @bp.route('/modify_tag', methods=['POST'])
@login_required @login_required
@moderator_required
def modify_tag(): def modify_tag():
form = TagForm(request.form) form = TagForm(request.form)
flash(str(request.form)) flash(str(request.form))
@ -144,7 +150,6 @@ def modify_tag():
flash('Changes to {} have been applied.'.format(str(el))) flash('Changes to {} have been applied.'.format(str(el)))
return redirect(url_for('.manage_tags')) return redirect(url_for('.manage_tags'))
# return redirect(url_for('main.index')) # return redirect(url_for('main.index'))
# Creation/editing only through post page # Creation/editing only through post page
@ -163,6 +168,8 @@ def modify_comment():
return redirect(url_for('.post_show', id=form.post_id.data)) return redirect(url_for('.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 or not el.is_current:
return abort(403)
if form.delete.data: if form.delete.data:
db.session.delete(el) db.session.delete(el)
db.session.commit() db.session.commit()

@ -2,14 +2,14 @@ import io
import os import os
from flask import (Blueprint, abort, current_app, flash, redirect, 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 flask_login import current_user, login_required
from PIL import Image from PIL import Image
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
from yadc import db 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.models import FILETYPE, RATING, Post, Tag, Comment
from yadc.utils import query_replace from yadc.utils import query_replace
@ -33,7 +33,9 @@ def posts(page):
# PARSING ARGUMENTS # PARSING ARGUMENTS
f_tags = request.args.get('tags', '').split() 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 posts_query = Post.query
if f_tags: if f_tags:
@ -45,6 +47,8 @@ def posts(page):
# flash(f_tags) # flash(f_tags)
# flash(m_ratings) # 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) return render_template('post/index.html', posts=posts, tags=tags)
@ -60,7 +64,7 @@ def post_show(id):
for comment in post.comments: for comment in post.comments:
comment.editform = CommentForm(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=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 from yadc.bp import manage
@bp.route('/comment', methods=['POST']) @bp.route('/comment', methods=['POST'])
@ -110,3 +114,81 @@ def upload():
return redirect(url_for('.posts')) return redirect(url_for('.posts'))
return render_template('post/upload.html', form=form) 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])

@ -34,4 +34,4 @@ def settings():
flash('Password changed successfully.') flash('Password changed successfully.')
return redirect(url_for('.settings')) return redirect(url_for('.settings'))
return render_template('manage/profile.html', form=form) return render_template('user/settings.html', form=form)

@ -116,6 +116,7 @@ class UserForm(EditForm):
validators=[optional()]) validators=[optional()])
class PostForm(EditForm): class PostForm(EditForm):
tags = StringField('Tags', render_kw={'placeholder':'Tags','autocomplete':'off'})
rating = SelectField('Rating', rating = SelectField('Rating',
choices=[(e.name, e.name.capitalize()) for e in RATING], choices=[(e.name, e.name.capitalize()) for e in RATING],
validators=[optional()]) validators=[optional()])

@ -3,8 +3,9 @@ import hashlib
import humanize import humanize
import os import os
from datetime import datetime, timezone 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 flask_login import UserMixin, login_user, logout_user, current_user
from PIL import Image from PIL import Image
from sqlalchemy_utc import UtcDateTime, utcnow from sqlalchemy_utc import UtcDateTime, utcnow
@ -33,10 +34,14 @@ class RATING(enum.Enum):
questionable = 1 questionable = 1
explicit = 2 explicit = 2
@staticmethod # @staticmethod
def matched(rating=None): # def matched(rating=None):
rat = {r.name : r for r in RATING}.get(rating, RATING.safe) # rat = {r.name : r for r in RATING}.get(rating, RATING.safe)
return [r for r in RATING if r.value<=rat.value] # 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): class POST_STATUS(enum.Enum):
pending = 0 pending = 0
@ -108,10 +113,33 @@ class User(UserMixin, TimestampMixin, db.Model):
def is_admin(self): def is_admin(self):
return self.op_level is OP_LEVEL.admin return self.op_level is OP_LEVEL.admin
@property
def is_current(self):
return self == current_user
@login.user_loader @login.user_loader
def load_user(id): def load_user(id):
return User.query.get(int(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): # class UserPreferences(db.Model):
# user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True) # user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
# user = db.relationship('User', backref=db.backref('profile', uselist=False)) # user = db.relationship('User', backref=db.backref('profile', uselist=False))

@ -13,7 +13,8 @@ if (suggest_input.hasAttribute('required')) {
suggest_input.removeAttribute('required') suggest_input.removeAttribute('required')
suggest_hidden.setAttribute('required', '') suggest_hidden.setAttribute('required', '')
} }
suggest_input.value = '' // suggest_input.value = ''
suggest_input.removeAttribute('value')
let tag_suggest_dropdown = suggestroot.querySelector('.tag_suggest_dropdown') let tag_suggest_dropdown = suggestroot.querySelector('.tag_suggest_dropdown')
@ -152,7 +153,7 @@ suggest_input.addEventListener('input', (event) => {
} }
ajax_timeout = setTimeout(() => { ajax_timeout = setTimeout(() => {
fetch('/api/tags?q='+value).then((response) => { fetch('/post/tags?q='+value).then((response) => {
console.log(response) console.log(response)
return response.json() return response.json()
}).then((data) => { }).then((data) => {

@ -32,7 +32,7 @@
<a href="{{ url_for('auth.login') }}">Login</a> <a href="{{ url_for('auth.login') }}">Login</a>
<a href="{{ url_for('auth.register') }}">Register</a> <a href="{{ url_for('auth.register') }}">Register</a>
{% else %} {% else %}
<a href="#">Profile</a> <a href="{{ url_for('user.profile', username=current_user.username) }}">Profile</a>
<a href="{{ url_for('user.settings') }}">Settings</a> <a href="{{ url_for('user.settings') }}">Settings</a>
<a href="{{ url_for('auth.logout') }}">Log out</a> <a href="{{ url_for('auth.logout') }}">Log out</a>
{% endif%} {% endif%}

@ -12,9 +12,7 @@
<div class="rating"> <div class="rating">
{% for rating in ('safe', 'questionable', 'explicit') %} {% for rating in ('safe', 'questionable', 'explicit') %}
<input type="radio" name="rating" id="rating-{{ rating }}" value="{{ rating }}" {% <input type="radio" name="rating" id="rating-{{ rating }}" value="{{ rating }}" {% if rating == session.get('index_settings', {}).get('rating', 'safe') %}checked{% endif %} onclick="submit()">
if (rating == request.args.get('rating') if request.args.get('rating') in ('safe', 'explicit', 'questionable') else rating == 'safe')
%}checked{% endif %}>
<label for="rating-{{ rating }}">{{ rating.capitalize() }}</label> <label for="rating-{{ rating }}">{{ rating.capitalize() }}</label>
<br> <br>
{% endfor %} {% endfor %}

@ -1,6 +1,11 @@
{% extends 'layout/base_sidebar.html' %} {% extends 'layout/base_sidebar.html' %}
{% from '_includes.html' import render_sidenav with context %}
{% block sidebar %} {% block sidebar %}
{{ render_sidenav({"speedtest": ("Speedtest", "http://speedtest.cesnet.cz")}) }} <article class="sidenav">
{#<!-- <a href="{{ url_for('user.profile') }}">Profile</a> -->#}
<a href="{{ url_for('user.settings') }}">Settings</a>
<a href="{{ url_for('manage.manage_posts') }}">Post management</a>
<a href="{{ url_for('manage.manage_tags') }}">Tag management</a>
<a href="{{ url_for('manage.manage_users') }}">User management</a>
</article>
{% endblock %} {% endblock %}

@ -1,5 +1,5 @@
{% extends 'layout/base_sidebar_tags.html' %} {% 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 %} {% from "_formhelpers.html" import errors %}
{% block sidebar %} {% block sidebar %}
@ -33,7 +33,20 @@
{% if post.can_edit %} {% if post.can_edit %}
<article class="edit"> <article class="edit">
<h3>Edit</h3> <h3>Edit</h3>
<a href="">edit?</a> <form action="{{ url_for('manage.modify_post') }}" method="post">
{{ editform.csrf_token }}
{{ editform.id() }}
<div>
{{ render_tag_input(editform.tags) }}
{{ errors(editform.tags) }}
</div>
<div>
{{ editform.source }}
</div>
<div>
{{ editform.edit() }}
</div>
</form>
</article> </article>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
@ -54,7 +67,7 @@
{% for comment in comments %} {% for comment in comments %}
<article class="comment"> <article class="comment">
<div class="comment_head"> <div class="comment_head">
<h4>{{ comment.user.username }}</h4> <h4>{{ comment.user.username or "Deleted account" }}</h4>
{% if comment.can_edit %} {% if comment.can_edit %}
<span class="controls"> <span class="controls">
<span class="control_edit">edit</span> <span class="control_edit">edit</span>

Loading…
Cancel
Save