various small bugfixes

This commit is contained in:
rimu 2024-01-25 20:16:08 +13:00
parent 686ac36ac7
commit 41f6a29e33
10 changed files with 133 additions and 94 deletions

View file

@ -408,7 +408,10 @@ def process_inbox_request(request_json, activitypublog_id, ip_address):
# Announce is new content and votes that happened on a remote server. # Announce is new content and votes that happened on a remote server.
if request_json['type'] == 'Announce': if request_json['type'] == 'Announce':
if request_json['object']['type'] == 'Create': if isinstance(request_json['object'], str):
activity_log.activity_json = json.dumps(request_json)
activity_log.exception_message = 'invalid json?'
elif request_json['object']['type'] == 'Create':
activity_log.activity_type = request_json['object']['type'] activity_log.activity_type = request_json['object']['type']
user_ap_id = request_json['object']['object']['attributedTo'] user_ap_id = request_json['object']['object']['attributedTo']
try: try:

View file

@ -536,6 +536,7 @@ def make_image_sizes_async(file_id, thumbnail_width, medium_width, directory):
final_place_thumbnail = os.path.join(directory, new_filename + '_thumbnail.webp') final_place_thumbnail = os.path.join(directory, new_filename + '_thumbnail.webp')
# Load image data into Pillow # Load image data into Pillow
Image.MAX_IMAGE_PIXELS = 89478485
image = Image.open(BytesIO(source_image)) image = Image.open(BytesIO(source_image))
image = ImageOps.exif_transpose(image) image = ImageOps.exif_transpose(image)
img_width = image.width img_width = image.width
@ -563,11 +564,11 @@ def make_image_sizes_async(file_id, thumbnail_width, medium_width, directory):
# Alert regarding fascist meme content # Alert regarding fascist meme content
try: try:
image_text = pytesseract.image_to_string(Image.open(BytesIO(source_image)).convert('L')) image_text = pytesseract.image_to_string(Image.open(BytesIO(source_image)).convert('L'), timeout=30)
except FileNotFoundError as e: except FileNotFoundError as e:
image_text = '' image_text = ''
if 'Anonymous' in image_text and ('No.' in image_text or ' N0' in image_text): # chan posts usually contain the text 'Anonymous' and ' No.12345' if 'Anonymous' in image_text and ('No.' in image_text or ' N0' in image_text): # chan posts usually contain the text 'Anonymous' and ' No.12345'
post = Post.query.filter(image_id=file.id).first() post = Post.query.filter_by(image_id=file.id).first()
notification = Notification(title='Review this', notification = Notification(title='Review this',
user_id=1, user_id=1,
author_id=post.user_id, author_id=post.user_id,

View file

@ -1,9 +1,15 @@
from flask import request
from flask_login import current_user
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, TextAreaField, BooleanField, HiddenField, SelectField, FileField from wtforms import StringField, SubmitField, TextAreaField, BooleanField, HiddenField, SelectField, FileField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo, Length, Optional from wtforms.validators import ValidationError, DataRequired, Email, EqualTo, Length, Optional
from flask_babel import _, lazy_gettext as _l from flask_babel import _, lazy_gettext as _l
from app import db
from app.utils import domain_from_url, MultiCheckboxField from app.utils import domain_from_url, MultiCheckboxField
from PIL import Image, ImageOps
from io import BytesIO
import pytesseract
class AddLocalCommunity(FlaskForm): class AddLocalCommunity(FlaskForm):
@ -36,7 +42,7 @@ class SearchRemoteCommunity(FlaskForm):
class CreatePostForm(FlaskForm): class CreatePostForm(FlaskForm):
communities = SelectField(_l('Community'), validators=[DataRequired()], coerce=int) communities = SelectField(_l('Community'), validators=[DataRequired()], coerce=int)
type = HiddenField() # https://getbootstrap.com/docs/4.6/components/navs/#tabs post_type = HiddenField() # https://getbootstrap.com/docs/4.6/components/navs/#tabs
discussion_title = StringField(_l('Title'), validators={Optional(), Length(min=3, max=255)}) discussion_title = StringField(_l('Title'), validators={Optional(), Length(min=3, max=255)})
discussion_body = TextAreaField(_l('Body'), validators={Optional(), Length(min=3, max=5000)}, render_kw={'placeholder': 'Text (optional)'}) discussion_body = TextAreaField(_l('Body'), validators={Optional(), Length(min=3, max=5000)}, render_kw={'placeholder': 'Text (optional)'})
link_title = StringField(_l('Title'), validators={Optional(), Length(min=3, max=255)}) link_title = StringField(_l('Title'), validators={Optional(), Length(min=3, max=255)})
@ -56,14 +62,14 @@ class CreatePostForm(FlaskForm):
def validate(self, extra_validators=None) -> bool: def validate(self, extra_validators=None) -> bool:
if not super().validate(): if not super().validate():
return False return False
if self.type.data is None or self.type.data == '': if self.post_type.data is None or self.post_type.data == '':
self.type.data = 'discussion' self.post_type.data = 'discussion'
if self.type.data == 'discussion': if self.post_type.data == 'discussion':
if self.discussion_title.data == '': if self.discussion_title.data == '':
self.discussion_title.errors.append(_('Title is required.')) self.discussion_title.errors.append(_('Title is required.'))
return False return False
elif self.type.data == 'link': elif self.post_type.data == 'link':
if self.link_title.data == '': if self.link_title.data == '':
self.link_title.errors.append(_('Title is required.')) self.link_title.errors.append(_('Title is required.'))
return False return False
@ -74,14 +80,27 @@ class CreatePostForm(FlaskForm):
if domain and domain.banned: if domain and domain.banned:
self.link_url.errors.append(_(f"Links to %s are not allowed.".format(domain.name))) self.link_url.errors.append(_(f"Links to %s are not allowed.".format(domain.name)))
return False return False
elif self.type.data == 'image': elif self.post_type.data == 'image':
if self.image_title.data == '': if self.image_title.data == '':
self.image_title.errors.append(_('Title is required.')) self.image_title.errors.append(_('Title is required.'))
return False return False
if self.image_file.data == '': if self.image_file.data == '':
self.image_file.errors.append(_('File is required.')) self.image_file.errors.append(_('File is required.'))
return False return False
elif self.type.data == 'poll': uploaded_file = request.files['image_file']
if uploaded_file and uploaded_file.filename != '':
Image.MAX_IMAGE_PIXELS = 89478485
# Do not allow fascist meme content
try:
image_text = pytesseract.image_to_string(Image.open(BytesIO(uploaded_file.read())).convert('L'))
except FileNotFoundError as e:
image_text = ''
if 'Anonymous' in image_text and ('No.' in image_text or ' N0' in image_text): # chan posts usually contain the text 'Anonymous' and ' No.12345'
self.image_file.errors.append(f"This image is an invalid file type.") # deliberately misleading error message
current_user.reputation -= 1
db.session.commit()
return False
elif self.post_type.data == 'poll':
self.discussion_title.errors.append(_('Poll not implemented yet.')) self.discussion_title.errors.append(_('Poll not implemented yet.'))
return False return False

View file

@ -1,3 +1,4 @@
from io import BytesIO
from random import randint from random import randint
from flask import redirect, url_for, flash, request, make_response, session, Markup, current_app, abort, g, json from flask import redirect, url_for, flash, request, make_response, session, Markup, current_app, abort, g, json
@ -19,7 +20,7 @@ from app.models import User, Community, CommunityMember, CommunityJoinRequest, C
File, PostVote, utcnow, Report, Notification, InstanceBlock, ActivityPubLog File, PostVote, utcnow, Report, Notification, InstanceBlock, ActivityPubLog
from app.community import bp from app.community import bp
from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required, \ from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required, \
shorten_string, markdown_to_text, domain_from_url, validate_image, gibberish, community_membership, ap_datetime, \ shorten_string, gibberish, community_membership, ap_datetime, \
request_etag_matches, return_304, instance_banned, can_create, can_upvote, can_downvote, user_filters_posts, \ request_etag_matches, return_304, instance_banned, can_create, can_upvote, can_downvote, user_filters_posts, \
joined_communities, moderating_communities joined_communities, moderating_communities
from feedgen.feed import FeedGenerator from feedgen.feed import FeedGenerator
@ -459,13 +460,16 @@ def add_post(actor):
return redirect(f"/c/{community.link()}") return redirect(f"/c/{community.link()}")
else: else:
# when request.form has some data in it, it means form validation failed. Set the post_type so the correct tab is shown. See setupPostTypeTabs() in scripts.js
if request.form.get('post_type', None):
form.post_type.data = request.form.get('post_type')
form.communities.data = community.id form.communities.data = community.id
form.notify_author.data = True form.notify_author.data = True
return render_template('community/add_post.html', title=_('Add post to community'), form=form, community=community, return render_template('community/add_post.html', title=_('Add post to community'), form=form, community=community,
images_disabled=images_disabled, markdown_editor=True, low_bandwidth=request.cookies.get('low_bandwidth', '0') == '1', images_disabled=images_disabled, markdown_editor=True, low_bandwidth=request.cookies.get('low_bandwidth', '0') == '1',
moderating_communities=moderating_communities(current_user.get_id()), moderating_communities=moderating_communities(current_user.get_id()),
joined_communities = joined_communities(current_user.id), joined_communities=joined_communities(current_user.id),
inoculation=inoculation[randint(0, len(inoculation) - 1)] inoculation=inoculation[randint(0, len(inoculation) - 1)]
) )

View file

@ -14,7 +14,7 @@ from app.activitypub.util import find_actor_or_create, actor_json_to_model, post
from app.constants import POST_TYPE_ARTICLE, POST_TYPE_LINK, POST_TYPE_IMAGE from app.constants import POST_TYPE_ARTICLE, POST_TYPE_LINK, POST_TYPE_IMAGE
from app.models import Community, File, BannedInstances, PostReply, PostVote, Post, utcnow, CommunityMember, Site, \ from app.models import Community, File, BannedInstances, PostReply, PostVote, Post, utcnow, CommunityMember, Site, \
Instance, Notification, User Instance, Notification, User
from app.utils import get_request, gibberish, markdown_to_html, domain_from_url, validate_image, allowlist_html, \ from app.utils import get_request, gibberish, markdown_to_html, domain_from_url, allowlist_html, \
html_to_markdown, is_image_url, ensure_directory_exists, inbox_domain, post_ranking, shorten_string html_to_markdown, is_image_url, ensure_directory_exists, inbox_domain, post_ranking, shorten_string
from sqlalchemy import desc, text from sqlalchemy import desc, text
import os import os
@ -152,6 +152,7 @@ def url_to_thumbnail_file(filename) -> File:
with open(final_place, 'wb') as f: with open(final_place, 'wb') as f:
f.write(response.content) f.write(response.content)
response.close() response.close()
Image.MAX_IMAGE_PIXELS = 89478485
with Image.open(final_place) as img: with Image.open(final_place) as img:
img = ImageOps.exif_transpose(img) img = ImageOps.exif_transpose(img)
img.thumbnail((150, 150)) img.thumbnail((150, 150))
@ -167,12 +168,12 @@ def save_post(form, post: Post):
post.nsfw = form.nsfw.data post.nsfw = form.nsfw.data
post.nsfl = form.nsfl.data post.nsfl = form.nsfl.data
post.notify_author = form.notify_author.data post.notify_author = form.notify_author.data
if form.type.data == '' or form.type.data == 'discussion': if form.post_type.data == '' or form.post_type.data == 'discussion':
post.title = form.discussion_title.data post.title = form.discussion_title.data
post.body = form.discussion_body.data post.body = form.discussion_body.data
post.body_html = markdown_to_html(post.body) post.body_html = markdown_to_html(post.body)
post.type = POST_TYPE_ARTICLE post.type = POST_TYPE_ARTICLE
elif form.type.data == 'link': elif form.post_type.data == 'link':
post.title = form.link_title.data post.title = form.link_title.data
post.body = form.link_body.data post.body = form.link_body.data
post.body_html = markdown_to_html(post.body) post.body_html = markdown_to_html(post.body)
@ -187,10 +188,10 @@ def save_post(form, post: Post):
if post.image_id: if post.image_id:
remove_old_file(post.image_id) remove_old_file(post.image_id)
post.image_id = None post.image_id = None
valid_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.webp'}
unused, file_extension = os.path.splitext(form.link_url.data) # do not use _ here instead of 'unused' unused, file_extension = os.path.splitext(form.link_url.data) # do not use _ here instead of 'unused'
# this url is a link to an image - generate a thumbnail of it # this url is a link to an image - generate a thumbnail of it
if file_extension.lower() in valid_extensions: if file_extension.lower() in allowed_extensions:
file = url_to_thumbnail_file(form.link_url.data) file = url_to_thumbnail_file(form.link_url.data)
if file: if file:
post.image = file post.image = file
@ -200,16 +201,15 @@ def save_post(form, post: Post):
opengraph = opengraph_parse(form.link_url.data) opengraph = opengraph_parse(form.link_url.data)
if opengraph and opengraph.get('og:image', '') != '': if opengraph and opengraph.get('og:image', '') != '':
filename = opengraph.get('og:image') filename = opengraph.get('og:image')
valid_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.webp'}
unused, file_extension = os.path.splitext(filename) unused, file_extension = os.path.splitext(filename)
if file_extension.lower() in valid_extensions: if file_extension.lower() in allowed_extensions:
file = url_to_thumbnail_file(filename) file = url_to_thumbnail_file(filename)
if file: if file:
file.alt_text = opengraph.get('og:title') file.alt_text = opengraph.get('og:title')
post.image = file post.image = file
db.session.add(file) db.session.add(file)
elif form.type.data == 'image': elif form.post_type.data == 'image':
post.title = form.image_title.data post.title = form.image_title.data
post.body = form.image_body.data post.body = form.image_body.data
post.body_html = markdown_to_html(post.body) post.body_html = markdown_to_html(post.body)
@ -222,7 +222,7 @@ def save_post(form, post: Post):
# check if this is an allowed type of file # check if this is an allowed type of file
file_ext = os.path.splitext(uploaded_file.filename)[1] file_ext = os.path.splitext(uploaded_file.filename)[1]
if file_ext.lower() not in allowed_extensions or file_ext.lower() != validate_image(uploaded_file.stream): if file_ext.lower() not in allowed_extensions:
abort(400) abort(400)
new_filename = gibberish(15) new_filename = gibberish(15)
@ -233,35 +233,39 @@ def save_post(form, post: Post):
# save the file # save the file
final_place = os.path.join(directory, new_filename + file_ext) final_place = os.path.join(directory, new_filename + file_ext)
final_place_thumbnail = os.path.join(directory, new_filename + '_thumbnail.webp') final_place_thumbnail = os.path.join(directory, new_filename + '_thumbnail.webp')
uploaded_file.seek(0)
uploaded_file.save(final_place) uploaded_file.save(final_place)
if file_ext.lower() == '.heic': if file_ext.lower() == '.heic':
register_heif_opener() register_heif_opener()
#Image.MAX_IMAGE_PIXELS = 89478485
# resize if necessary # resize if necessary
img = Image.open(final_place) img = Image.open(final_place)
img = ImageOps.exif_transpose(img) if '.' + img.format.lower() in allowed_extensions:
img_width = img.width img = ImageOps.exif_transpose(img)
img_height = img.height
if img.width > 2000 or img.height > 2000:
img.thumbnail((2000, 2000))
img.save(final_place)
img_width = img.width img_width = img.width
img_height = img.height img_height = img.height
# save a second, smaller, version as a thumbnail if img.width > 2000 or img.height > 2000:
img.thumbnail((256, 256)) img.thumbnail((2000, 2000))
img.save(final_place_thumbnail, format="WebP", quality=93) img.save(final_place)
thumbnail_width = img.width img_width = img.width
thumbnail_height = img.height img_height = img.height
# save a second, smaller, version as a thumbnail
img.thumbnail((256, 256))
img.save(final_place_thumbnail, format="WebP", quality=93)
thumbnail_width = img.width
thumbnail_height = img.height
file = File(file_path=final_place, file_name=new_filename + file_ext, alt_text=form.image_title.data, file = File(file_path=final_place, file_name=new_filename + file_ext, alt_text=form.image_title.data,
width=img_width, height=img_height, thumbnail_width=thumbnail_width, width=img_width, height=img_height, thumbnail_width=thumbnail_width,
thumbnail_height=thumbnail_height, thumbnail_path=final_place_thumbnail, thumbnail_height=thumbnail_height, thumbnail_path=final_place_thumbnail,
source_url=final_place.replace('app/static/', f"https://{current_app.config['SERVER_NAME']}/static/")) source_url=final_place.replace('app/static/', f"https://{current_app.config['SERVER_NAME']}/static/"))
post.image = file post.image = file
db.session.add(file) db.session.add(file)
elif form.type.data == 'poll': elif form.post_type.data == 'poll':
... ...
else: else:
raise Exception('invalid post type') raise Exception('invalid post type')
@ -285,7 +289,7 @@ def remove_old_file(file_id):
def save_icon_file(icon_file, directory='communities') -> File: def save_icon_file(icon_file, directory='communities') -> File:
# check if this is an allowed type of file # check if this is an allowed type of file
file_ext = os.path.splitext(icon_file.filename)[1] file_ext = os.path.splitext(icon_file.filename)[1]
if file_ext.lower() not in allowed_extensions or file_ext.lower() != validate_image(icon_file.stream): if file_ext.lower() not in allowed_extensions:
abort(400) abort(400)
new_filename = gibberish(15) new_filename = gibberish(15)
@ -302,33 +306,36 @@ def save_icon_file(icon_file, directory='communities') -> File:
register_heif_opener() register_heif_opener()
# resize if necessary # resize if necessary
Image.MAX_IMAGE_PIXELS = 89478485
img = Image.open(final_place) img = Image.open(final_place)
img = ImageOps.exif_transpose(img) if '.' + img.format.lower() in allowed_extensions:
img_width = img.width img = ImageOps.exif_transpose(img)
img_height = img.height
if img.width > 250 or img.height > 250:
img.thumbnail((250, 250))
img.save(final_place)
img_width = img.width img_width = img.width
img_height = img.height img_height = img.height
# save a second, smaller, version as a thumbnail if img.width > 250 or img.height > 250:
img.thumbnail((40, 40)) img.thumbnail((250, 250))
img.save(final_place_thumbnail, format="WebP", quality=93) img.save(final_place)
thumbnail_width = img.width img_width = img.width
thumbnail_height = img.height img_height = img.height
# save a second, smaller, version as a thumbnail
img.thumbnail((40, 40))
img.save(final_place_thumbnail, format="WebP", quality=93)
thumbnail_width = img.width
thumbnail_height = img.height
file = File(file_path=final_place, file_name=new_filename + file_ext, alt_text=f'{directory} icon', file = File(file_path=final_place, file_name=new_filename + file_ext, alt_text=f'{directory} icon',
width=img_width, height=img_height, thumbnail_width=thumbnail_width, width=img_width, height=img_height, thumbnail_width=thumbnail_width,
thumbnail_height=thumbnail_height, thumbnail_path=final_place_thumbnail) thumbnail_height=thumbnail_height, thumbnail_path=final_place_thumbnail)
db.session.add(file) db.session.add(file)
return file return file
else:
abort(400)
def save_banner_file(banner_file, directory='communities') -> File: def save_banner_file(banner_file, directory='communities') -> File:
# check if this is an allowed type of file # check if this is an allowed type of file
file_ext = os.path.splitext(banner_file.filename)[1] file_ext = os.path.splitext(banner_file.filename)[1]
if file_ext.lower() not in allowed_extensions or file_ext.lower() != validate_image( if file_ext.lower() not in allowed_extensions:
banner_file.stream):
abort(400) abort(400)
new_filename = gibberish(15) new_filename = gibberish(15)
@ -345,26 +352,30 @@ def save_banner_file(banner_file, directory='communities') -> File:
register_heif_opener() register_heif_opener()
# resize if necessary # resize if necessary
Image.MAX_IMAGE_PIXELS = 89478485
img = Image.open(final_place) img = Image.open(final_place)
img = ImageOps.exif_transpose(img) if '.' + img.format.lower() in allowed_extensions:
img_width = img.width img = ImageOps.exif_transpose(img)
img_height = img.height
if img.width > 1600 or img.height > 600:
img.thumbnail((1600, 600))
img.save(final_place)
img_width = img.width img_width = img.width
img_height = img.height img_height = img.height
if img.width > 1600 or img.height > 600:
img.thumbnail((1600, 600))
img.save(final_place)
img_width = img.width
img_height = img.height
# save a second, smaller, version as a thumbnail # save a second, smaller, version as a thumbnail
img.thumbnail((700, 500)) img.thumbnail((700, 500))
img.save(final_place_thumbnail, format="WebP", quality=93) img.save(final_place_thumbnail, format="WebP", quality=93)
thumbnail_width = img.width thumbnail_width = img.width
thumbnail_height = img.height thumbnail_height = img.height
file = File(file_path=final_place, file_name=new_filename + file_ext, alt_text=f'{directory} banner', file = File(file_path=final_place, file_name=new_filename + file_ext, alt_text=f'{directory} banner',
width=img_width, height=img_height, thumbnail_width=thumbnail_width, thumbnail_height=thumbnail_height) width=img_width, height=img_height, thumbnail_width=thumbnail_width, thumbnail_height=thumbnail_height)
db.session.add(file) db.session.add(file)
return file return file
else:
abort(400)
# NB this always signs POSTs as the community so is only suitable for Announce activities # NB this always signs POSTs as the community so is only suitable for Announce activities

View file

@ -19,7 +19,7 @@ from app.models import Post, PostReply, \
PostReplyVote, PostVote, Notification, utcnow, UserBlock, DomainBlock, InstanceBlock, Report, Site, Community PostReplyVote, PostVote, Notification, utcnow, UserBlock, DomainBlock, InstanceBlock, Report, Site, Community
from app.post import bp from app.post import bp
from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required, \ from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required, \
shorten_string, markdown_to_text, domain_from_url, validate_image, gibberish, ap_datetime, return_304, \ shorten_string, markdown_to_text, gibberish, ap_datetime, return_304, \
request_etag_matches, ip_address, user_ip_banned, instance_banned, can_downvote, can_upvote, post_ranking, \ request_etag_matches, ip_address, user_ip_banned, instance_banned, can_downvote, can_upvote, post_ranking, \
reply_already_exists, reply_is_just_link_to_gif_reaction, confidence, moderating_communities, joined_communities reply_already_exists, reply_is_just_link_to_gif_reaction, confidence, moderating_communities, joined_communities
@ -643,16 +643,16 @@ def post_edit(post_id: int):
return redirect(url_for('activitypub.post_ap', post_id=post.id)) return redirect(url_for('activitypub.post_ap', post_id=post.id))
else: else:
if post.type == constants.POST_TYPE_ARTICLE: if post.type == constants.POST_TYPE_ARTICLE:
form.type.data = 'discussion' form.post_type.data = 'discussion'
form.discussion_title.data = post.title form.discussion_title.data = post.title
form.discussion_body.data = post.body form.discussion_body.data = post.body
elif post.type == constants.POST_TYPE_LINK: elif post.type == constants.POST_TYPE_LINK:
form.type.data = 'link' form.post_type.data = 'link'
form.link_title.data = post.title form.link_title.data = post.title
form.link_body.data = post.body form.link_body.data = post.body
form.link_url.data = post.url form.link_url.data = post.url
elif post.type == constants.POST_TYPE_IMAGE: elif post.type == constants.POST_TYPE_IMAGE:
form.type.data = 'image' form.post_type.data = 'image'
form.image_title.data = post.title form.image_title.data = post.title
form.image_body.data = post.body form.image_body.data = post.body
form.notify_author.data = post.notify_author form.notify_author.data = post.notify_author

View file

@ -234,27 +234,37 @@ function setupPostTypeTabs() {
const tabEl = document.querySelector('#discussion-tab') const tabEl = document.querySelector('#discussion-tab')
if(tabEl) { if(tabEl) {
tabEl.addEventListener('show.bs.tab', event => { tabEl.addEventListener('show.bs.tab', event => {
document.getElementById('type').value = 'discussion'; document.getElementById('post_type ').value = 'discussion';
}); });
} }
const tabE2 = document.querySelector('#link-tab') const tabE2 = document.querySelector('#link-tab')
if(tabE2) { if(tabE2) {
tabE2.addEventListener('show.bs.tab', event => { tabE2.addEventListener('show.bs.tab', event => {
document.getElementById('type').value = 'link'; document.getElementById('post_type').value = 'link';
}); });
} }
const tabE3 = document.querySelector('#image-tab') const tabE3 = document.querySelector('#image-tab')
if(tabE3) { if(tabE3) {
tabE3.addEventListener('show.bs.tab', event => { tabE3.addEventListener('show.bs.tab', event => {
document.getElementById('type').value = 'image'; document.getElementById('post_type').value = 'image';
}); });
} }
const tabE4 = document.querySelector('#poll-tab') const tabE4 = document.querySelector('#poll-tab')
if(tabE4) { if(tabE4) {
tabE4.addEventListener('show.bs.tab', event => { tabE4.addEventListener('show.bs.tab', event => {
document.getElementById('type').value = 'poll'; document.getElementById('post_type').value = 'poll';
}); });
} }
// Check if there is a hidden field with the name 'type'. This is set if server-side validation of the form fails
var typeField = document.getElementById('post_type');
if (typeField && typeField.tagName === 'INPUT' && typeField.type === 'hidden') {
var typeVal = typeField.value;
if(typeVal) {
const tab = document.getElementById(typeVal + '-tab');
if(tab)
tab.click();
}
}
} }

View file

@ -75,7 +75,7 @@
Poll Poll
</div> </div>
</div> </div>
{{ render_field(form.type) }} {{ render_field(form.post_type) }}
<div class="row mt-4"> <div class="row mt-4">
<div class="col-md-3"> <div class="col-md-3">
{{ render_field(form.notify_author) }} {{ render_field(form.notify_author) }}

View file

@ -95,7 +95,7 @@
Poll Poll
</div> </div>
</div> </div>
{{ render_field(form.type) }} {{ render_field(form.post_type) }}
<div class="row mt-4"> <div class="row mt-4">
<div class="col-md-3"> <div class="col-md-3">
{{ render_field(form.notify_author) }} {{ render_field(form.notify_author) }}

View file

@ -312,15 +312,6 @@ def ensure_directory_exists(directory):
rebuild_directory += '/' rebuild_directory += '/'
def validate_image(stream):
header = stream.read(512)
stream.seek(0)
format = imghdr.what(None, header)
if not format:
return None
return '.' + (format if format != 'jpeg' else 'jpg')
def validation_required(func): def validation_required(func):
@wraps(func) @wraps(func)
def decorated_view(*args, **kwargs): def decorated_view(*args, **kwargs):
@ -609,9 +600,9 @@ def _confidence(ups, downs):
def confidence(ups, downs) -> float: def confidence(ups, downs) -> float:
if ups is None: if ups is None or ups < 0:
ups = 0 ups = 0
if downs is None: if downs is None or downs < 0:
downs = 0 downs = 0
if ups + downs == 0: if ups + downs == 0:
return 0.0 return 0.0