1
1
Fork 0

User settings,

moving endpoints,
restructuring forms
and some other BS
dev
Jan Kužílek 5 years ago
parent 6770ab7ec5
commit afe26e10e3

@ -0,0 +1,47 @@
"""empty message
Revision ID: b1a3eed8e38f
Revises: f4e1b4727000
Create Date: 2020-03-09 23:58:53.716242
"""
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utc
# revision identifiers, used by Alembic.
revision = 'b1a3eed8e38f'
down_revision = 'f4e1b4727000'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('user_tags_blacklist',
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('tag_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['tag_id'], ['tag.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
)
op.add_column('user', sa.Column('ban_reason', sa.String(length=512), nullable=True))
op.add_column('user', sa.Column('ban_until', sqlalchemy_utc.sqltypes.UtcDateTime(timezone=True), nullable=True))
op.add_column('user', sa.Column('biography', sa.String(length=512), nullable=True))
op.add_column('user', sa.Column('rating', sa.Enum('safe', 'questionable', 'explicit', name='rating'), server_default='safe', nullable=False))
op.alter_column('user', 'op_level', server_default='user')
op.alter_column('user', 'user_status', server_default='active')
op.alter_column('post', 'status', server_default='pending')
op.alter_column('tag', 'category', server_default='general')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user', 'rating')
op.drop_column('user', 'biography')
op.drop_column('user', 'ban_until')
op.drop_column('user', 'ban_reason')
op.drop_table('user_tags_blacklist')
# ### end Alembic commands ###

@ -114,10 +114,11 @@ def modify_post():
pass pass
else: else:
el = Post.query.filter_by(id=form.id.data).first() el = Post.query.filter_by(id=form.id.data).first()
if not current_user.is_moderator or not el.author.is_current: if not current_user.is_moderator and not (el.author.is_current if el.author is not None else None):
flash("You don't have sufficient rights to do this.") flash("You don't have sufficient rights to do this.")
return redirect(url_for('main.index')) return redirect(url_for('main.index'))
if form.delete.data: if form.delete.data:
el.remove_image_files()
db.session.delete(el) db.session.delete(el)
db.session.commit() db.session.commit()
flash('{} deleted.'.format(str(el))) flash('{} deleted.'.format(str(el)))
@ -128,6 +129,16 @@ def modify_post():
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.approve.data:
if not current_user.is_moderator:
flash("You don't have sufficient rights to do this.")
return redirect(url_for('main.index'))
post.status = POST_STATUS.active
post.approver = current_user
db.session.commit()
flash('Approved post {}'.format(str(post)))
redirect(url_for('post.post_show', id=post.id))
return redirect(url_for('.manage_posts')) return redirect(url_for('.manage_posts'))
@ -179,7 +190,7 @@ def modify_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 or not el.user.is_current: 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.") flash("You don't have sufficient rights to do this.")
return redirect(url_for('main.index')) return redirect(url_for('main.index'))
if form.delete.data: if form.delete.data:

@ -4,13 +4,12 @@ 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, session, jsonify) 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 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, PostForm from yadc.forms import UploadForm, CommentForm, PostForm
from yadc.models import FILETYPE, RATING, Post, Tag, Comment from yadc.models import FILETYPE, RATING, POST_STATUS, Post, Tag, Comment, moderator_required
from yadc.utils import query_replace from yadc.utils import query_replace
bp = Blueprint('post', __name__) bp = Blueprint('post', __name__)
@ -35,6 +34,10 @@ def posts(page):
f_rating = {r.name : r for r in RATING}.get(request.args.get('rating'), RATING.safe) f_rating = {r.name : r for r in RATING}.get(request.args.get('rating'), RATING.safe)
m_ratings = f_rating.matched m_ratings = f_rating.matched
# filter user's blacklist
if current_user.is_authenticated:
f_tags = list(t for t in f_tags if t not in [tb.content for tb in current_user.tag_blacklist])
posts_query = Post.query posts_query = Post.query
if f_tags: 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.join(Post.tags).group_by(Post.id).filter(Tag.content.in_(f_tags)).having(func.count(Post.id)==len(f_tags))
@ -66,6 +69,11 @@ from yadc.bp import manage
def comment(): def comment():
return manage.modify_comment() return manage.modify_comment()
@bp.route('/editpost', methods=['POST'])
@login_required
def editpost():
return manage.modify_post()
@bp.route('/upload', methods=['GET', 'POST']) @bp.route('/upload', methods=['GET', 'POST'])
@login_required @login_required
def upload(): def upload():
@ -78,33 +86,12 @@ def upload():
tags = Tag.query.filter(Tag.content.in_(f_tags)).all() tags = Tag.query.filter(Tag.content.in_(f_tags)).all()
post = Post(file, source=form.sauce.data, tags=tags, rating=RATING[form.rating.data], author=current_user) post = Post(file, source=form.sauce.data, tags=tags, rating=RATING[form.rating.data], author=current_user)
with open(post.image_path, "wb") as f:
f.write(file.data.getbuffer())
# file.seek(0)
# file.save(post.image_path)
with Image.open(file.data) as im:
im = im.convert('RGB')
if post.jpeg_path is not None:
im.save(post.jpeg_path, 'JPEG', quality=80)
sim = im.copy()
sim.thumbnail([800,800])
sim.save(post.sample_path, 'JPEG', quality=80)
im.thumbnail([512,512])
im.save(post.thumb_path, 'JPEG', quality=80)
db.session.add(post) db.session.add(post)
db.session.commit() db.session.commit()
flash('Successfully submitted {}'.format(str(post))) flash('Successfully submitted {}'.format(str(post)))
#return redirect(url_for('.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)
@ -151,21 +138,21 @@ def posts_api():
source=p.source, source=p.source,
md5=p.md5, md5=p.md5,
file_size=p.filesize, file_size=p.filesize,
file_url=p.url(path=p.image_url, endpoint='img'), file_url=p.url(path=p.file_uri, endpoint='img'),
preview_url=p.url(path=p.image_url, endpoint='thumb'), preview_url=p.url(path=p.file_uri, endpoint='thumb'),
preview_width=0, preview_width=0,
preview_height=0, preview_height=0,
actual_preview_width=0, actual_preview_width=0,
actual_preview_height=0, actual_preview_height=0,
sample_url=p.url(path=p.image_url, endpoint='sample'), sample_url=p.url(path=p.file_uri, endpoint='sample'),
sample_width=0, sample_width=0,
sample_height=0, sample_height=0,
sample_file_size=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_url=p.url(path=p.file_uri, endpoint='img') if p.filetype is FILETYPE.jpeg else p.url(path=p.file_uri, endpoint='jpeg'),
jpeg_width=0, jpeg_width=0,
jpeg_height=0, jpeg_height=0,

@ -1,10 +1,10 @@
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)
from flask_login import current_user, login_required from flask_login import current_user, login_required
from yadc.forms import ChangePassForm from yadc.forms import ChangeUserInfoForm, ChangePassForm, ChangeMailForm, ChangeUserRatingForm, ChangeTagBlacklistForm, DeleteUserDataForm
from yadc import db from yadc import db
from yadc.models import User from yadc.models import User, Post, Comment
bp = Blueprint('user', __name__) bp = Blueprint('user', __name__)
@ -15,23 +15,102 @@ def profile(username):
if user is not None: if user is not None:
return render_template('user/profile.html', user=user) return render_template('user/profile.html', user=user)
return "FUCK YOU, THIS USER DOES NOT EXIST" return redirect(url_for('main.index'))
@bp.route('/settings', methods=['GET','POST']) @bp.route('/settings')
@login_required @login_required
def settings(): def settings():
form = ChangePassForm(request.form) return render_template(
if request.method == 'POST' and form.validate(): 'user/settings.html',
user = current_user userinfo_form=ChangeUserInfoForm(bio=current_user.biography),
pass_form=ChangePassForm(),
mail_form=ChangeMailForm(),
rating_form=ChangeUserRatingForm(rating=current_user.rating.name),
tags_form=ChangeTagBlacklistForm(),
delete_form=DeleteUserDataForm()
)
@bp.route('/change_info', methods=['POST'])
@login_required
def change_info():
form = ChangeUserInfoForm(request.form)
if form.validate():
current_user.biography = form.bio.data
db.session.commit()
flash('Your biography was updated.')
return redirect(url_for('.settings'))
if not user.check_password(form.password_current.data): @bp.route('/change_pass', methods=['POST'])
@login_required
def change_pass():
form = ChangePassForm(request.form)
if form.validate():
if not current_user.check_password(form.password_current.data):
flash('Incorrect password') flash('Incorrect password')
return redirect(url_for('.settings')) return redirect(url_for('.settings'))
user.create_password(form.password.data) current_user.create_password(form.password.data)
db.session.commit()
db.session.commit()
flash('Password changed successfully.') flash('Password changed successfully.')
return redirect(url_for('.settings'))
return render_template('user/settings.html', form=form) return redirect(url_for('.settings'))
@bp.route('/change_mail', methods=['POST'])
@login_required
def change_mail():
form = ChangeMailForm(request.form)
if form.validate():
current_user.email = form.email.data
db.session.commit()
flash('Your email was updated.')
return redirect(url_for('.settings'))
@bp.route('/change_rating', methods=['POST'])
@login_required
def change_rating():
form = ChangeUserRatingForm(request.form)
if form.validate():
current_user.rating = form.rating.data
db.session.commit()
flash('Your rating preference was updated.')
return redirect(url_for('.settings'))
@bp.route('/change_tagblacklist', methods=['POST'])
@login_required
def change_tagblacklist():
form = ChangeTagBlacklistForm(request.form)
if form.validate():
f_tags = form.tags.data.split()
tags = Tag.query.filter(Tag.content.in_(f_tags)).all()
current_user.tag_blacklist = tags
db.session.commit()
flash('Your tag blacklist was updated.')
return redirect(url_for('.settings'))
@bp.route('/delete_data', methods=['POST'])
@login_required
def delete_data():
form = DeleteUserDataForm(request.form)
if form.validate():
if form.all_posts:
Post.query.filter_by(author_id=current_user.id).delete()
if form.all_comments:
Comment.query.filter_by(user_id=current_user.id).delete()
db.session.delete(current_user)
db.session.commit()
flash('Thank you for using our service.')
return redirect(url_for('main.index'))
return redirect(url_for('.settings'))

@ -7,6 +7,8 @@ from werkzeug.utils import cached_property
from flask import current_app from flask import current_app
from flask_wtf.csrf import _FlaskFormCSRF from flask_wtf.csrf import _FlaskFormCSRF
from yadc.models import USER_STATUS, OP_LEVEL, RATING, POST_STATUS, TAG_CATEGORY
class CSRFForm(Form): class CSRFForm(Form):
class Meta: class Meta:
csrf = True csrf = True
@ -66,8 +68,8 @@ class UploadForm(CSRFForm):
sauce = StringField('Sauce', validators=[DataRequired()], render_kw={'placeholder':'Source URL','autocomplete':'off'}) sauce = StringField('Sauce', validators=[DataRequired()], render_kw={'placeholder':'Source URL','autocomplete':'off'})
tags = StringField('Tags', validators=[DataRequired()], render_kw={'placeholder':'Tags','autocomplete':'off'}) # CUSTOM VALIDATOR (also for Post edits) tags = StringField('Tags', validators=[DataRequired()], render_kw={'placeholder':'Tags','autocomplete':'off'}) # CUSTOM VALIDATOR (also for Post edits)
rating = RadioField('Rating', rating = RadioField('Rating',
choices=[('safe', 'Safe'), ('questionable', 'Questionable'), ('explicit', 'Explicit')], choices=[(e.name, e.name.capitalize()) for e in RATING],
default='safe', default=RATING.safe.name,
validators=[DataRequired()]) validators=[DataRequired()])
submit = SubmitField('Upload') submit = SubmitField('Upload')
@ -80,15 +82,43 @@ class UploadForm(CSRFForm):
if client_mimetype not in ['image/png','image/jpeg']: if client_mimetype not in ['image/png','image/jpeg']:
raise ValidationError('Please select an image file of PNG or JPEG format') raise ValidationError('Please select an image file of PNG or JPEG format')
# Change user section
class ChangeUserInfoForm(CSRFForm):
bio = TextAreaField('Biography')
userinfo_submit = SubmitField('Change your info')
class ChangePassForm(CSRFForm): class ChangePassForm(CSRFForm):
password_current = PasswordField('Current password', validators=[DataRequired()]) password_current = PasswordField('Current password', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()])
password_again = PasswordField('Repeat password', validators=[DataRequired(), EqualTo('password')]) password_again = PasswordField('Repeat password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Change password') pass_submit = SubmitField('Change password')
class ChangeMailForm(CSRFForm):
email = StringField('E-mail', validators=[DataRequired(), Email()], render_kw=dict(placeholder="E-mail"))
email_again = StringField('Repeat e-mail', validators=[DataRequired(), EqualTo('email')], render_kw=dict(placeholder="Repeat e-mail"))
mail_submit = SubmitField('Change email')
class ChangeUserRatingForm(CSRFForm):
rating = SelectField('Rating',
choices=[(e.name, e.name.capitalize()) for e in RATING],
validators=[DataRequired()],
render_kw=dict(onchange="submit()"))
rating_submit = SubmitField('Change default rating')
class ChangeTagBlacklistForm(CSRFForm):
tags = StringField('Tags', render_kw={'placeholder':'Tags','autocomplete':'off'})
tags_submit = SubmitField('Change blacklisted tags', validators=[DataRequired()])
class DeleteUserDataForm(CSRFForm):
all_posts = BooleanField('Delete all posts')
all_comments = BooleanField('Delete all comments')
delete_submit = SubmitField(
'Delete your data',
validators=[DataRequired()],
render_kw=dict(onclick="return confirm('Do you really want to delete all your data?')")
)
from yadc.models import USER_STATUS, OP_LEVEL, RATING, POST_STATUS, TAG_CATEGORY
class EditForm(CSRFForm): class EditForm(CSRFForm):
id = HiddenField('ID') id = HiddenField('ID')
@ -125,6 +155,8 @@ class PostForm(EditForm):
validators=[optional()]) validators=[optional()])
source = StringField('Source', render_kw={'autocomplete':'off'}) source = StringField('Source', render_kw={'autocomplete':'off'})
approve = SubmitField('Approve')
class TagForm(EditForm): class TagForm(EditForm):
content = StringField('Content', validators=[validate_create_required], render_kw={'autocomplete':'off'}) content = StringField('Content', validators=[validate_create_required], render_kw={'autocomplete':'off'})
category = SelectField('Category', category = SelectField('Category',

@ -70,21 +70,27 @@ class TimestampMixin(object):
def natural_updated(self): def natural_updated(self):
return humanize.naturaltime(datetime.now().astimezone(self.updated.tzinfo) - self.updated) return humanize.naturaltime(datetime.now().astimezone(self.updated.tzinfo) - self.updated)
user_tags_blacklist = db.Table('user_tags_blacklist', db.metadata,
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
)
class User(UserMixin, TimestampMixin, db.Model): class User(UserMixin, TimestampMixin, db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(128), unique=True, nullable=False) username = db.Column(db.String(128), unique=True, nullable=False)
email = db.Column(db.String(256), unique=True) email = db.Column(db.String(256), unique=True)
pass_hash = db.Column(db.String(128)) pass_hash = db.Column(db.String(128))
op_level = db.Column(db.Enum(OP_LEVEL), default=OP_LEVEL.user, nullable=False) op_level = db.Column(db.Enum(OP_LEVEL), server_default=OP_LEVEL.user.name, nullable=False)
user_status = db.Column(db.Enum(USER_STATUS), default=USER_STATUS.active, nullable=False) user_status = db.Column(db.Enum(USER_STATUS), server_default=USER_STATUS.active.name, nullable=False)
last_login = db.Column(UtcDateTime) last_login = db.Column(UtcDateTime)
ban_status = None ban_until = db.Column(UtcDateTime)
ban_until = None ban_reason = db.Column(db.String(512))
ban_reason = None
biography = db.Column(db.String(512))
#authored_posts = db.relationship('Post', back_populates='author') rating = db.Column(db.Enum(RATING), server_default=RATING.safe.name, nullable=False)
#approved_posts = db.relationship('Post', back_populates='approver') tag_blacklist = db.relationship('Tag', secondary=user_tags_blacklist, backref=db.backref('user_blacklisted'))
def __repr__(self): def __repr__(self):
return '<User {}>'.format(self.username) return '<User {}>'.format(self.username)
@ -93,8 +99,10 @@ class User(UserMixin, TimestampMixin, db.Model):
self.pass_hash = generate_password_hash(password, salt_length=16) self.pass_hash = generate_password_hash(password, salt_length=16)
def check_password(self, password): def check_password(self, password):
# if self.pass_hash is None:
# return True
if self.pass_hash is None: if self.pass_hash is None:
return True return False
return check_password_hash(self.pass_hash, password) return check_password_hash(self.pass_hash, password)
def login(self, remember): def login(self, remember):
@ -145,9 +153,6 @@ def admin_required(func):
# 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))
# rating = db.Column(db.Enum(RATING), default=RATING.safe, nullable=False)
# tag_blacklist = None
post_tags = db.Table('post_tags', db.metadata, post_tags = db.Table('post_tags', db.metadata,
@ -160,7 +165,7 @@ class Post(TimestampMixin, db.Model):
md5 = db.Column(db.String(32), unique=True, nullable=False) md5 = db.Column(db.String(32), unique=True, nullable=False)
filetype = db.Column(db.Enum(FILETYPE), nullable=False) filetype = db.Column(db.Enum(FILETYPE), nullable=False)
rating = db.Column(db.Enum(RATING), nullable=False) rating = db.Column(db.Enum(RATING), nullable=False)
status = db.Column(db.Enum(POST_STATUS), default=POST_STATUS.pending, nullable=False) status = db.Column(db.Enum(POST_STATUS), server_default=POST_STATUS.pending.name, nullable=False)
width = db.Column(db.Integer, default=0) width = db.Column(db.Integer, default=0)
height = db.Column(db.Integer, default=0) height = db.Column(db.Integer, default=0)
@ -189,6 +194,8 @@ class Post(TimestampMixin, db.Model):
self.origin_filename = file.filename self.origin_filename = file.filename
self.generate_image_files(file)
def __repr__(self): def __repr__(self):
return('<Post #{} by {}, {} of {} bytes>'.format(self.id, self.author, self.filetype.name, self.filesize)) return('<Post #{} by {}, {} of {} bytes>'.format(self.id, self.author, self.filetype.name, self.filesize))
@ -197,7 +204,7 @@ class Post(TimestampMixin, db.Model):
return "flex: {0:.2f} 1 {0:.2f}px;".format(self.width/self.height*current_app.config.get('POST_LIST_THUMB_HEIGHT', 240)) return "flex: {0:.2f} 1 {0:.2f}px;".format(self.width/self.height*current_app.config.get('POST_LIST_THUMB_HEIGHT', 240))
@property @property
def image_url(self): def file_uri(self):
# filename = "{}.{}".format('maybe_later_generated_cute_filename', 'jpg' if self.filetype is FILETYPE.jpeg else 'png') # filename = "{}.{}".format('maybe_later_generated_cute_filename', 'jpg' if self.filetype is FILETYPE.jpeg else 'png')
# filename = 'maybe_later_generated_cute_filename' # filename = 'maybe_later_generated_cute_filename'
filename = "{} - {} {}".format(current_app.config.get('INSTANCE_NAME'), self.id, " ".join(tag.content.replace(' ', '_') for tag in self.tags)) filename = "{} - {} {}".format(current_app.config.get('INSTANCE_NAME'), self.id, " ".join(tag.content.replace(' ', '_') for tag in self.tags))
@ -213,29 +220,40 @@ class Post(TimestampMixin, db.Model):
elif endpoint == 'thumb': elif endpoint == 'thumb':
return url_for('main.uploaded_thumb', path="{}.{}".format(path,'jpg')) return url_for('main.uploaded_thumb', path="{}.{}".format(path,'jpg'))
@property
def image_path(self):
filename = "{}.{}".format(self.md5, 'jpg' if self.filetype is FILETYPE.jpeg else 'png')
return os.path.join(current_app.config.get('POST_UPLOADS'), 'img', filename)
@property def generate_image_files(self, file):
def jpeg_path(self): with open(self.image_files['image'], "wb") as f:
if self.filetype is FILETYPE.jpeg: f.write(file.data.getbuffer())
return None # file.seek(0)
# file.save(post.image_path)
with Image.open(file.data) as im:
im = im.convert('RGB')
jpeg_filename = "{}.{}".format(self.md5, 'jpg') if self.image_files['jpeg'] is not None:
return os.path.join(current_app.config.get('POST_UPLOADS'), 'jpeg', jpeg_filename) im.save(self.image_files['jpeg'], 'JPEG', quality=80)
sim = im.copy()
sim.thumbnail([800,800])
sim.save(self.image_files['sample'], 'JPEG', quality=80)
@property sim = im.copy()
def sample_path(self): sim.thumbnail([512,512])
jpeg_filename = "{}.{}".format(self.md5, 'jpg') sim.save(self.image_files['thumb'], 'JPEG', quality=80)
return os.path.join(current_app.config.get('POST_UPLOADS'), 'sample', jpeg_filename)
def remove_image_files(self):
for im in self.image_files.values():
os.remove(im)
@property @property
def thumb_path(self): def image_files(self):
jpeg_filename = "{}.{}".format(self.md5, 'jpg') return dict(
return os.path.join(current_app.config.get('POST_UPLOADS'), 'thumb', jpeg_filename) image=os.path.join(current_app.config.get('POST_UPLOADS'), 'img', "{}.{}".format(self.md5, 'jpg' if self.filetype is FILETYPE.jpeg else 'png')),
jpeg=(None if self.filetype is FILETYPE.jpeg else os.path.join(current_app.config.get('POST_UPLOADS'), 'jpeg', "{}.{}".format(self.md5, 'jpg'))),
sample=os.path.join(current_app.config.get('POST_UPLOADS'), 'sample', "{}.{}".format(self.md5, 'jpg')),
thumb=os.path.join(current_app.config.get('POST_UPLOADS'), 'thumb', "{}.{}".format(self.md5, 'jpg'))
)
@cached_property @cached_property
def image_resolution(self): def image_resolution(self):
return "{}x{}".format(self.width, self.height) return "{}x{}".format(self.width, self.height)
@ -257,7 +275,7 @@ class Post(TimestampMixin, db.Model):
class Tag(TimestampMixin, db.Model): class Tag(TimestampMixin, db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(128), unique=True, nullable=False) content = db.Column(db.String(128), unique=True, nullable=False)
category = db.Column(db.Enum(TAG_CATEGORY), default=TAG_CATEGORY.general, nullable=False) category = db.Column(db.Enum(TAG_CATEGORY), server_default=TAG_CATEGORY.general.name, nullable=False)
@property @property
def content_deser(self): def content_deser(self):

@ -30,7 +30,9 @@
<label class="show to-edit"><span class="fa fa-edit"></span></label> <label class="show to-edit"><span class="fa fa-edit"></span></label>
<label class="edit to-close"><span class="fa fa-close"></span></label> <label class="edit to-close"><span class="fa fa-close"></span></label>
<label class="edit"><span class="fa fa-check"></span>{{ user.editform.edit() }}</label> <label class="edit"><span class="fa fa-check"></span>{{ user.editform.edit() }}</label>
<label><span class="fa fa-trash-o"></span>{{ user.editform.delete() }}</label> <label><span class="fa fa-trash-o"></span>{{ user.editform.delete(
onclick="return confirm('Do you really want to delete this user?')"
)}}</label>
</td> </td>
</tr> </tr>
</form> </form>

@ -9,7 +9,7 @@
{% for post in posts.items %} {% for post in posts.items %}
<figure style="{{ post.flex }}"> <figure style="{{ post.flex }}">
<a href="{{ url_for('post.post_show', id=post.id) }}"> <a href="{{ url_for('post.post_show', id=post.id) }}">
<img src="{{ post.url(path=post.image_url, endpoint='thumb') }}" srcset="{{ post.url(path=post.image_url, endpoint='thumb') }} 512w, {{ post.url(path=post.image_url, endpoint='sample') }} 800w" alt=""> <!--sizes="(min-width: 513px) 1000w" --> <img src="{{ post.url(path=post.file_uri, endpoint='thumb') }}" srcset="{{ post.url(path=post.file_uri, endpoint='thumb') }} 512w, {{ post.url(path=post.file_uri, endpoint='sample') }} 800w" alt=""> <!--sizes="(min-width: 513px) 1000w" -->
</a> </a>
</figure> </figure>
{% endfor %} {% endfor %}

@ -21,10 +21,10 @@
<div class="source">Source: <a href="{{ post.source }}">{{ post.source }}</a></div> <div class="source">Source: <a href="{{ post.source }}">{{ post.source }}</a></div>
<div class="size">File size: {{ post.filesize_human }}</div> <div class="size">File size: {{ post.filesize_human }}</div>
<div class="resolution">Image res: {{ post.image_resolution }}</div> <div class="resolution">Image res: {{ post.image_resolution }}</div>
<div class="jpeg"><a href="{{ post.url(path=post.image_url, endpoint='jpeg') }}">Enlarge image</a></div> <div class="jpeg"><a href="{{ post.url(path=post.file_uri, endpoint='jpeg') }}">Enlarge image</a></div>
{% if post.filetype.name == 'png' %} {% if post.filetype.name == 'png' %}
<div class="png"><a href="{{ post.url(path=post.image_url) }}">Original PNG file</a></div> <div class="png"><a href="{{ post.url(path=post.file_uri) }}">Original PNG file</a></div>
{% endif %} {% endif %}
</article> </article>
@ -33,7 +33,7 @@
{% if post.can_edit %} {% if post.can_edit %}
<article class="edit"> <article class="edit">
<h3>Edit</h3> <h3>Edit</h3>
<form action="{{ url_for('manage.modify_post') }}" method="post"> <form action="{{ url_for('post.editpost') }}" method="post">
{{ editform.csrf_token }} {{ editform.csrf_token }}
{{ editform.id() }} {{ editform.id() }}
<div> <div>
@ -54,7 +54,7 @@
{% block main_content %} {% block main_content %}
<section class="post_single"> <section class="post_single">
<div class="image"> <div class="image">
<img src="{{ post.url(path=post.image_url, endpoint='jpeg') }}" alt=""> <img src="{{ post.url(path=post.file_uri, endpoint='jpeg') }}" alt="">
</div> </div>
</section> </section>
{% endblock %} {% endblock %}
@ -77,7 +77,7 @@
{% if not comment.deleted %} {% if not comment.deleted %}
<form class="comment_editform editingable" action="{{ url_for('manage.modify_comment') }}" method="post"> <form class="comment_editform editingable" action="{{ url_for('post.comment') }}" method="post">
{{ comment.editform.csrf_token }} {{ comment.editform.csrf_token }}
{{ comment.editform.id() }} {{ comment.editform.id() }}
<p class="comment_content notedit">{{ comment.content }}</p> <p class="comment_content notedit">{{ comment.content }}</p>
@ -103,7 +103,7 @@
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
<div class="form"> <div class="form">
<h3>Reply</h3> <h3>Reply</h3>
<form action="{{ url_for('manage.modify_comment') }}" method="post"> <form action="{{ url_for('post.comment') }}" method="post">
{{ comment_form.csrf_token }} {{ comment_form.csrf_token }}
{{ comment_form.post_id() }} {{ comment_form.post_id() }}
<div> <div>

@ -6,5 +6,5 @@
{% endblock %} {% endblock %}
{% block main_content %} {% block main_content %}
<p class="bio">{{ user.biography }}</p>
{% endblock %} {% endblock %}

@ -3,23 +3,75 @@
{% block main_content %} {% block main_content %}
<section class="manage_profile"> <section class="manage_profile">
<form action="" method="post"> <form action="{{ url_for('user.change_info') }}" method="post">
<h3>Change user info</h3>
{{ userinfo_form.csrf_token }}
<div>
{{ userinfo_form.bio() }}
{{ errors(userinfo_form.bio) }}
</div>
<div>
{{ userinfo_form.userinfo_submit() }}
</div>
</form>
<form action="{{ url_for('user.change_pass') }}" method="post">
<h3>Change password</h3> <h3>Change password</h3>
{{ form.csrf_token }} {{ pass_form.csrf_token }}
<div>
{{ pass_form.password_current(placeholder="Current password") }}
{{ errors(pass_form.password_current) }}
</div>
<div>
{{ pass_form.password(placeholder="Password") }}
{{ errors(pass_form.password) }}
</div>
<div>
{{ pass_form.password_again(placeholder="Repeat password") }}
{{ errors(pass_form.password_again) }}
</div>
<div>
{{ pass_form.pass_submit() }}
</div>
</form>
<form action="{{ url_for('user.change_mail') }}" method="post">
<h3>Change e-mail address</h3>
{{ mail_form.csrf_token }}
<div>
{{ mail_form.email() }}
{{ errors(mail_form.email) }}
</div>
<div>
{{ mail_form.email_again() }}
{{ errors(mail_form.email_again) }}
</div>
<div> <div>
{{ form.password_current(placeholder="Current password") }} {{ mail_form.mail_submit() }}
{{ errors(form.password_current) }}
</div> </div>
</form>
<form action="{{ url_for('user.change_rating') }}" method="post">
<h3>Preferred rating</h3>
{{ rating_form.csrf_token }}
<div>
{{ rating_form.rating() }}
{{ errors(rating_form.rating) }}
</div>
<div>
{{ rating_form.rating_submit() }}
</div>
</form>
<form action="{{ url_for('user.delete_data') }}" method="post">
<h3>Delete user data</h3>
{{ delete_form.csrf_token }}
<div> <div>
{{ form.password(placeholder="Password") }} {{ delete_form.all_comments() }}{{ delete_form.all_comments.label }}
{{ errors(form.password) }} {{ errors(delete_form.all_posts) }}
</div> </div>
<div> <div>
{{ form.password_again(placeholder="Repeat password") }} {{ delete_form.all_posts() }}{{ delete_form.all_posts.label }}
{{ errors(form.password_again) }} {{ errors(delete_form.all_posts) }}
</div> </div>
<div> <div>
{{ form.submit() }} {{ delete_form.delete_submit() }}
</div> </div>
</form> </form>
</section> </section>

Loading…
Cancel
Save