2024-02-16 06:17:13 +13:00
from flask import request , g
2024-01-25 20:16:08 +13:00
from flask_login import current_user
2023-08-29 22:01:06 +12:00
from flask_wtf import FlaskForm
2024-03-15 14:24:45 +13:00
from wtforms import StringField , SubmitField , TextAreaField , BooleanField , HiddenField , SelectField , FileField , \
DateField
2024-03-26 17:29:08 +01:00
from wtforms . validators import ValidationError , DataRequired , Email , EqualTo , Length , Regexp , 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
2024-03-15 14:24:45 +13:00
from app . models import Community , utcnow
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
2024-03-13 16:40:20 +13:00
class AddCommunityForm ( FlaskForm ) :
2023-09-03 16:30:20 +12:00
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 ' )
2024-01-27 11:19:23 +13:00
local_only = BooleanField ( ' Local only ' )
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
2024-03-13 16:40:20 +13:00
class EditCommunityForm ( FlaskForm ) :
title = StringField ( _l ( ' Title ' ) , validators = [ DataRequired ( ) ] )
description = TextAreaField ( _l ( ' Description ' ) )
icon_file = FileField ( _ ( ' Icon image ' ) )
banner_file = FileField ( _ ( ' Banner image ' ) )
rules = TextAreaField ( _l ( ' Rules ' ) )
nsfw = BooleanField ( _l ( ' Porn community ' ) )
local_only = BooleanField ( _l ( ' Only accept posts from current instance ' ) )
restricted_to_mods = BooleanField ( _l ( ' Only moderators can post ' ) )
new_mods_wanted = BooleanField ( _l ( ' New moderators wanted ' ) )
topic = SelectField ( _l ( ' Topic ' ) , coerce = int , validators = [ Optional ( ) ] )
layouts = [ ( ' ' , _l ( ' List ' ) ) ,
( ' masonry ' , _l ( ' Masonry ' ) ) ,
( ' masonry_wide ' , _l ( ' Wide masonry ' ) ) ]
default_layout = SelectField ( _l ( ' Layout ' ) , coerce = str , choices = layouts , validators = [ Optional ( ) ] )
submit = SubmitField ( _l ( ' Save ' ) )
class AddModeratorForm ( FlaskForm ) :
user_name = StringField ( _l ( ' User name ' ) , validators = [ DataRequired ( ) ] )
submit = SubmitField ( _l ( ' Add ' ) )
2023-08-29 22:01:06 +12:00
class SearchRemoteCommunity ( FlaskForm ) :
2024-01-29 22:18:06 +13:00
address = StringField ( _l ( ' Community address ' ) , render_kw = { ' placeholder ' : ' e.g. !name@server ' , ' autofocus ' : True } , validators = [ DataRequired ( ) ] )
2023-09-17 21:19:51 +12:00
submit = SubmitField ( _l ( ' Search ' ) )
2024-03-15 14:24:45 +13:00
class BanUserCommunityForm ( FlaskForm ) :
reason = StringField ( _l ( ' Reason ' ) , render_kw = { ' autofocus ' : True } , validators = [ DataRequired ( ) ] )
2024-03-21 21:19:50 +13:00
ban_until = DateField ( _l ( ' Ban until ' ) , validators = [ Optional ( ) ] )
2024-03-15 14:24:45 +13:00
delete_posts = BooleanField ( _l ( ' Also delete all their posts ' ) )
delete_post_replies = BooleanField ( _l ( ' Also delete all their comments ' ) )
submit = SubmitField ( _l ( ' Ban ' ) )
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) ' } )
2024-03-26 17:29:08 +01:00
link_url = StringField ( _l ( ' URL ' ) , validators = [ Regexp ( r ' ^https?:// ' , message = ' Submitted links need to start with " http:// " " or " https:// " ' ) ] , render_kw = { ' placeholder ' : ' https://... ' } )
2023-09-17 21:19:51 +12:00
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)
2024-03-20 20:41:45 +13:00
sticky = BooleanField ( _l ( ' Sticky ' ) )
2023-09-17 21:19:51 +12:00
nsfw = BooleanField ( _l ( ' NSFW ' ) )
2024-02-16 06:17:13 +13:00
nsfl = BooleanField ( _l ( ' Gore/gross ' ) )
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 :
2024-03-08 22:58:38 +13:00
self . link_url . errors . append ( _ ( " Links to %(domain)s are not allowed. " , domain = domain . name ) )
2023-09-17 21:19:51 +12:00
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
2024-03-24 20:58:25 +13:00
if self . communities :
community = Community . query . get ( self . communities . data )
if community . is_local ( ) and g . site . allow_local_image_posts is False :
self . communities . errors . append ( _ ( ' Images cannot be posted to local communities. ' ) )
2024-01-25 20:16:08 +13:00
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
2024-03-12 09:53:34 +13:00
def reasons_to_string ( self , reason_data ) - > str :
result = [ ]
for reason_id in reason_data :
for choice in self . reason_choices :
if choice [ 0 ] == reason_id :
result . append ( str ( choice [ 1 ] ) )
return ' , ' . join ( result )
2023-12-21 22:14:43 +13:00
class DeleteCommunityForm ( FlaskForm ) :
submit = SubmitField ( _l ( ' Delete community ' ) )