2024-01-25 20:16:08 +13:00
|
|
|
from flask import request
|
|
|
|
from flask_login import current_user
|
2023-08-29 22:01:06 +12:00
|
|
|
from flask_wtf import FlaskForm
|
2023-09-17 21:19:51 +12:00
|
|
|
from wtforms import StringField, SubmitField, TextAreaField, BooleanField, HiddenField, SelectField, FileField
|
|
|
|
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo, Length, Optional
|
2023-08-29 22:01:06 +12:00
|
|
|
from flask_babel import _, lazy_gettext as _l
|
|
|
|
|
2024-01-25 20:16:08 +13:00
|
|
|
from app import db
|
2023-12-13 21:04:11 +13:00
|
|
|
from app.utils import domain_from_url, MultiCheckboxField
|
2024-01-25 20:16:08 +13:00
|
|
|
from PIL import Image, ImageOps
|
|
|
|
from io import BytesIO
|
|
|
|
import pytesseract
|
2023-09-17 21:19:51 +12:00
|
|
|
|
2023-08-29 22:01:06 +12:00
|
|
|
|
2023-09-03 16:30:20 +12:00
|
|
|
class AddLocalCommunity(FlaskForm):
|
|
|
|
community_name = StringField(_l('Name'), validators=[DataRequired()])
|
2023-12-08 17:13:38 +13:00
|
|
|
url = StringField(_l('Url'))
|
2023-08-29 22:01:06 +12:00
|
|
|
description = TextAreaField(_l('Description'))
|
2023-12-08 17:13:38 +13:00
|
|
|
icon_file = FileField(_('Icon image'))
|
|
|
|
banner_file = FileField(_('Banner image'))
|
2023-08-29 22:01:06 +12:00
|
|
|
rules = TextAreaField(_l('Rules'))
|
2023-12-31 12:09:20 +13:00
|
|
|
nsfw = BooleanField('NSFW')
|
2023-08-29 22:01:06 +12:00
|
|
|
submit = SubmitField(_l('Create'))
|
|
|
|
|
2023-11-16 22:31:14 +13:00
|
|
|
def validate(self, extra_validators=None):
|
|
|
|
if not super().validate():
|
|
|
|
return False
|
|
|
|
if self.url.data.strip() == '':
|
|
|
|
self.url.errors.append(_('Url is required.'))
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
if '-' in self.url.data.strip():
|
|
|
|
self.url.errors.append(_('- cannot be in Url. Use _ instead?'))
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2023-08-29 22:01:06 +12:00
|
|
|
|
|
|
|
class SearchRemoteCommunity(FlaskForm):
|
2023-11-17 22:02:44 +13:00
|
|
|
address = StringField(_l('Community address'), render_kw={'placeholder': 'e.g. !name@server'}, validators=[DataRequired()])
|
2023-09-17 21:19:51 +12:00
|
|
|
submit = SubmitField(_l('Search'))
|
|
|
|
|
|
|
|
|
2023-11-30 20:57:51 +13:00
|
|
|
class CreatePostForm(FlaskForm):
|
2023-09-17 21:19:51 +12:00
|
|
|
communities = SelectField(_l('Community'), validators=[DataRequired()], coerce=int)
|
2024-01-25 20:16:08 +13:00
|
|
|
post_type = HiddenField() # https://getbootstrap.com/docs/4.6/components/navs/#tabs
|
2023-09-17 21:19:51 +12:00
|
|
|
discussion_title = StringField(_l('Title'), validators={Optional(), Length(min=3, max=255)})
|
2023-12-27 14:38:41 +13:00
|
|
|
discussion_body = TextAreaField(_l('Body'), validators={Optional(), Length(min=3, max=5000)}, render_kw={'placeholder': 'Text (optional)'})
|
2023-09-17 21:19:51 +12:00
|
|
|
link_title = StringField(_l('Title'), validators={Optional(), Length(min=3, max=255)})
|
2024-01-07 18:30:27 +13:00
|
|
|
link_body = TextAreaField(_l('Body'), validators={Optional(), Length(min=3, max=5000)},
|
|
|
|
render_kw={'placeholder': 'Text (optional)'})
|
2023-09-17 21:19:51 +12:00
|
|
|
link_url = StringField(_l('URL'), render_kw={'placeholder': 'https://...'})
|
|
|
|
image_title = StringField(_l('Title'), validators={Optional(), Length(min=3, max=255)})
|
2024-01-26 17:15:43 +13:00
|
|
|
image_alt_text = StringField(_l('Alt text'), validators={Optional(), Length(min=3, max=255)})
|
2024-01-07 18:30:27 +13:00
|
|
|
image_body = TextAreaField(_l('Body'), validators={Optional(), Length(min=3, max=5000)},
|
|
|
|
render_kw={'placeholder': 'Text (optional)'})
|
2023-09-17 21:19:51 +12:00
|
|
|
image_file = FileField(_('Image'))
|
|
|
|
# flair = SelectField(_l('Flair'), coerce=int)
|
|
|
|
nsfw = BooleanField(_l('NSFW'))
|
2023-12-31 12:09:20 +13:00
|
|
|
nsfl = BooleanField(_l('Content warning'))
|
2023-11-30 23:21:37 +13:00
|
|
|
notify_author = BooleanField(_l('Notify about replies'))
|
2023-12-03 22:41:15 +13:00
|
|
|
submit = SubmitField(_l('Save'))
|
2023-09-17 21:19:51 +12:00
|
|
|
|
|
|
|
def validate(self, extra_validators=None) -> bool:
|
|
|
|
if not super().validate():
|
|
|
|
return False
|
2024-01-25 20:16:08 +13:00
|
|
|
if self.post_type.data is None or self.post_type.data == '':
|
|
|
|
self.post_type.data = 'discussion'
|
2023-09-17 21:19:51 +12:00
|
|
|
|
2024-01-25 20:16:08 +13:00
|
|
|
if self.post_type.data == 'discussion':
|
2023-09-17 21:19:51 +12:00
|
|
|
if self.discussion_title.data == '':
|
|
|
|
self.discussion_title.errors.append(_('Title is required.'))
|
|
|
|
return False
|
2024-01-25 20:16:08 +13:00
|
|
|
elif self.post_type.data == 'link':
|
2023-09-17 21:19:51 +12:00
|
|
|
if self.link_title.data == '':
|
|
|
|
self.link_title.errors.append(_('Title is required.'))
|
|
|
|
return False
|
|
|
|
if self.link_url.data == '':
|
|
|
|
self.link_url.errors.append(_('URL is required.'))
|
|
|
|
return False
|
2023-11-22 20:48:27 +13:00
|
|
|
domain = domain_from_url(self.link_url.data, create=False)
|
2023-10-23 17:22:21 +13:00
|
|
|
if domain and domain.banned:
|
2023-09-17 21:19:51 +12:00
|
|
|
self.link_url.errors.append(_(f"Links to %s are not allowed.".format(domain.name)))
|
|
|
|
return False
|
2024-01-25 20:16:08 +13:00
|
|
|
elif self.post_type.data == 'image':
|
2023-09-17 21:19:51 +12:00
|
|
|
if self.image_title.data == '':
|
|
|
|
self.image_title.errors.append(_('Title is required.'))
|
|
|
|
return False
|
|
|
|
if self.image_file.data == '':
|
|
|
|
self.image_file.errors.append(_('File is required.'))
|
|
|
|
return False
|
2024-01-25 20:16:08 +13:00
|
|
|
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':
|
2023-09-17 21:19:51 +12:00
|
|
|
self.discussion_title.errors.append(_('Poll not implemented yet.'))
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
2023-10-02 22:16:44 +13:00
|
|
|
|
2023-12-13 21:04:11 +13:00
|
|
|
|
|
|
|
class ReportCommunityForm(FlaskForm):
|
|
|
|
reason_choices = [('1', _l('Breaks instance rules')),
|
|
|
|
('2', _l('Abandoned by moderators')),
|
|
|
|
('3', _l('Cult')),
|
|
|
|
('4', _l('Scam')),
|
|
|
|
('5', _l('Alt-right pipeline')),
|
|
|
|
('6', _l('Hate / genocide')),
|
|
|
|
('7', _l('Other')),
|
|
|
|
]
|
|
|
|
reasons = MultiCheckboxField(_l('Reason'), choices=reason_choices)
|
|
|
|
description = StringField(_l('More info'))
|
|
|
|
report_remote = BooleanField('Also send report to originating instance')
|
|
|
|
submit = SubmitField(_l('Report'))
|
2023-12-21 22:14:43 +13:00
|
|
|
|
|
|
|
|
|
|
|
class DeleteCommunityForm(FlaskForm):
|
|
|
|
submit = SubmitField(_l('Delete community'))
|