forked from MarseyWorld/MarseyWorld
make it possible to position poll options
parent
0ff2a35736
commit
55c8a51a1d
|
@ -276,20 +276,29 @@ class Comment(Base):
|
|||
|
||||
for o in self.options:
|
||||
input_type = 'radio' if o.exclusive else 'checkbox'
|
||||
body += f'<div class="custom-control"><input type="{input_type}" class="custom-control-input" id="comment-{o.id}" name="option-{self.id}"'
|
||||
if o.voted(v): body += " checked"
|
||||
option_body = f'<div class="custom-control"><input type="{input_type}" class="custom-control-input" id="comment-{o.id}" name="option-{self.id}"'
|
||||
if o.voted(v): option_body += " checked"
|
||||
|
||||
if v:
|
||||
if self.parent_submission:
|
||||
sub = self.post.sub
|
||||
if sub in {'furry','vampire','racist','femboy'} and not v.house.lower().startswith(sub): body += ' disabled '
|
||||
body += f''' data-nonce="{g.nonce}" data-onclick="poll_vote_{o.exclusive}('{o.id}', '{self.id}', 'comment')"'''
|
||||
if sub in {'furry','vampire','racist','femboy'} and not v.house.lower().startswith(sub): option_body += ' disabled '
|
||||
option_body += f''' data-nonce="{g.nonce}" data-onclick="poll_vote_{o.exclusive}('{o.id}', '{self.id}', 'comment')"'''
|
||||
else:
|
||||
body += f''' data-nonce="{g.nonce}" data-onclick="poll_vote_no_v()"'''
|
||||
option_body += f''' data-nonce="{g.nonce}" data-onclick="poll_vote_no_v()"'''
|
||||
|
||||
body += f'''><label class="custom-control-label" for="comment-{o.id}">{o.body_html}<span class="presult-{self.id}'''
|
||||
if not self.total_poll_voted(v): body += ' d-none'
|
||||
body += f'"> - <a href="/votes/comment/option/{o.id}"><span id="score-comment-{o.id}">{o.upvotes}</span> votes</a></label></div>'''
|
||||
option_body += f'''><label class="custom-control-label" for="comment-{o.id}">{o.body_html}<span class="presult-{self.id}'''
|
||||
if not self.total_poll_voted(v): option_body += ' d-none'
|
||||
option_body += f'"> - <a href="/votes/comment/option/{o.id}"><span id="score-comment-{o.id}">{o.upvotes}</span> votes</a></label></div>'''
|
||||
|
||||
if o.exclusive > 1: s = '!!'
|
||||
elif o.exclusive: s = '&&'
|
||||
else: s = '$$'
|
||||
|
||||
if f'{s}{o.body}{s}' in body:
|
||||
body = body.replace(f'{s}{o.body}{s}', option_body)
|
||||
else:
|
||||
body += option_body
|
||||
|
||||
if not self.ghost and self.author.show_sig(v):
|
||||
body += f'<section id="signature-{self.author.id}" class="user-signature"><hr>{self.author.sig_html}</section>'
|
||||
|
|
|
@ -11,7 +11,8 @@ class SubmissionOption(Base):
|
|||
__tablename__ = "submission_options"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
submission_id = Column(Integer, ForeignKey("submissions.id"))
|
||||
parent_id = Column(Integer, ForeignKey("submissions.id"))
|
||||
body = Column(Text)
|
||||
body_html = Column(Text)
|
||||
exclusive = Column(Integer)
|
||||
created_utc = Column(Integer)
|
||||
|
@ -66,7 +67,8 @@ class CommentOption(Base):
|
|||
__tablename__ = "comment_options"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
comment_id = Column(Integer, ForeignKey("comments.id"))
|
||||
parent_id = Column(Integer, ForeignKey("comments.id"))
|
||||
body = Column(Text)
|
||||
body_html = Column(Text)
|
||||
exclusive = Column(Integer)
|
||||
created_utc = Column(Integer)
|
||||
|
|
|
@ -285,39 +285,49 @@ class Submission(Base):
|
|||
winner = [x for x in self.options if x.exclusive == 3]
|
||||
|
||||
for o in self.options:
|
||||
if o.exclusive > 1:
|
||||
body += f'''<div class="custom-control mt-2"><input name="option-{self.id}" autocomplete="off" class="custom-control-input bet" type="radio" id="{o.id}" data-nonce="{g.nonce}" data-onclick="bet_vote(this,'{o.id}')"'''
|
||||
if o.voted(v): body += " checked "
|
||||
if not (v and v.coins >= POLL_BET_COINS) or self.total_bet_voted(v): body += " disabled "
|
||||
option_body = ''
|
||||
|
||||
body += f'''><label class="custom-control-label" for="{o.id}">{o.body_html}<span class="presult-{self.id}'''
|
||||
body += f'"> - <a href="/votes/post/option/{o.id}"><span id="option-{o.id}">{o.upvotes}</span> bets</a>'
|
||||
if o.exclusive > 1:
|
||||
option_body += f'''<div class="custom-control mt-2"><input name="option-{self.id}" autocomplete="off" class="custom-control-input bet" type="radio" id="{o.id}" data-nonce="{g.nonce}" data-onclick="bet_vote(this,'{o.id}')"'''
|
||||
if o.voted(v): option_body += " checked "
|
||||
if not (v and v.coins >= POLL_BET_COINS) or self.total_bet_voted(v): option_body += " disabled "
|
||||
|
||||
option_body += f'''><label class="custom-control-label" for="{o.id}">{o.body_html}<span class="presult-{self.id}'''
|
||||
option_body += f'"> - <a href="/votes/post/option/{o.id}"><span id="option-{o.id}">{o.upvotes}</span> bets</a>'
|
||||
if not self.total_bet_voted(v):
|
||||
body += f'''<span class="cost"> (cost of entry: {POLL_BET_COINS} coins)</span>'''
|
||||
body += "</label>"
|
||||
option_body += f'''<span class="cost"> (cost of entry: {POLL_BET_COINS} coins)</span>'''
|
||||
option_body += "</label>"
|
||||
|
||||
if o.exclusive == 3:
|
||||
body += " - <b>WINNER!</b>"
|
||||
option_body += " - <b>WINNER!</b>"
|
||||
|
||||
if not winner and v and v.admin_level >= PERMS['POST_BETS_DISTRIBUTE']:
|
||||
body += f'''<button class="btn btn-primary distribute" data-areyousure="postToastReload(this,'/distribute/{o.id}')" data-nonce="{g.nonce}" data-onclick="areyousure(this)">Declare winner</button>'''
|
||||
body += "</div>"
|
||||
option_body += f'''<button class="btn btn-primary distribute" data-areyousure="postToastReload(this,'/distribute/{o.id}')" data-nonce="{g.nonce}" data-onclick="areyousure(this)">Declare winner</button>'''
|
||||
option_body += "</div>"
|
||||
else:
|
||||
input_type = 'radio' if o.exclusive else 'checkbox'
|
||||
body += f'<div class="custom-control mt-2"><input type="{input_type}" class="custom-control-input" id="post-{o.id}" name="option-{self.id}"'
|
||||
if o.voted(v): body += " checked"
|
||||
option_body += f'<div class="custom-control mt-2"><input type="{input_type}" class="custom-control-input" id="post-{o.id}" name="option-{self.id}"'
|
||||
if o.voted(v): option_body += " checked"
|
||||
|
||||
if v:
|
||||
sub = self.sub
|
||||
if sub in {'furry','vampire','racist','femboy'} and not v.house.lower().startswith(sub): body += ' disabled '
|
||||
body += f''' data-nonce="{g.nonce}" data-onclick="poll_vote_{o.exclusive}('{o.id}', '{self.id}', 'post')"'''
|
||||
if sub in {'furry','vampire','racist','femboy'} and not v.house.lower().startswith(sub): option_body += ' disabled '
|
||||
option_body += f''' data-nonce="{g.nonce}" data-onclick="poll_vote_{o.exclusive}('{o.id}', '{self.id}', 'post')"'''
|
||||
else:
|
||||
body += f''' data-nonce="{g.nonce}" data-onclick="poll_vote_no_v()"'''
|
||||
option_body += f''' data-nonce="{g.nonce}" data-onclick="poll_vote_no_v()"'''
|
||||
|
||||
body += f'''><label class="custom-control-label" for="post-{o.id}">{o.body_html}<span class="presult-{self.id}'''
|
||||
if not self.total_poll_voted(v): body += ' d-none'
|
||||
body += f'"> - <a href="/votes/post/option/{o.id}"><span id="score-post-{o.id}">{o.upvotes}</span> votes</a></label></div>'''
|
||||
option_body += f'''><label class="custom-control-label" for="post-{o.id}">{o.body_html}<span class="presult-{self.id}'''
|
||||
if not self.total_poll_voted(v): option_body += ' d-none'
|
||||
option_body += f'"> - <a href="/votes/post/option/{o.id}"><span id="score-post-{o.id}">{o.upvotes}</span> votes</a></label></div>'''
|
||||
|
||||
if o.exclusive > 1: s = '!!'
|
||||
elif o.exclusive: s = '&&'
|
||||
else: s = '$$'
|
||||
|
||||
if f'{s}{o.body}{s}' in body:
|
||||
body = body.replace(f'{s}{o.body}{s}', option_body)
|
||||
else:
|
||||
body += option_body
|
||||
|
||||
if not listing and not self.ghost and self.author.show_sig(v):
|
||||
body += f'<section id="signature-{self.author.id}" class="user-signature"><hr>{self.author.sig_html}</section>'
|
||||
|
|
|
@ -466,28 +466,52 @@ def execute_lawlz_actions(v:User, p:Submission):
|
|||
g.db.add(ma_2)
|
||||
g.db.add(ma_3)
|
||||
|
||||
def process_poll_options(target:Union[Submission, Comment],
|
||||
cls:Union[Type[SubmissionOption], Type[CommentOption]],
|
||||
options:Iterable[str], exclusive:int, friendly_name:str,
|
||||
db:scoped_session) -> None:
|
||||
for option in options:
|
||||
if len(option) > 500: abort(400, f"{friendly_name} option too long!")
|
||||
if cls is SubmissionOption:
|
||||
option = cls(
|
||||
submission_id=target.id,
|
||||
body_html=option,
|
||||
exclusive=exclusive,
|
||||
)
|
||||
else:
|
||||
option = cls(
|
||||
comment_id=target.id,
|
||||
body_html=option,
|
||||
exclusive=exclusive,
|
||||
)
|
||||
db.add(option)
|
||||
|
||||
def execute_wordle(c:Comment, body:str):
|
||||
if not FEATURES['WORDLE']: return
|
||||
if not "!wordle" in body: return
|
||||
answer = random.choice(WORDLE_LIST)
|
||||
c.wordle_result = f'_active_{answer}'
|
||||
|
||||
|
||||
def process_poll_options(v:User, target:Union[Submission, Comment]):
|
||||
|
||||
patterns = [(poll_regex, 0), (choice_regex, 1)]
|
||||
|
||||
if isinstance(target, Submission) and v and v.admin_level >= PERMS['POST_BETS']:
|
||||
patterns.append((bet_regex, 2))
|
||||
|
||||
option_count = 0
|
||||
|
||||
for pattern, exclusive in patterns:
|
||||
for i in pattern.finditer(target.body):
|
||||
option_count += 1
|
||||
|
||||
if option_count > POLL_MAX_OPTIONS:
|
||||
abort(400, f"Max number of poll options is {POLL_MAX_OPTIONS}")
|
||||
|
||||
body = i.group(1)
|
||||
|
||||
if len(body) > 500:
|
||||
abort(400, f"Poll option body too long! (Max 500 characters)")
|
||||
|
||||
if isinstance(target, Submission):
|
||||
cls = SubmissionOption
|
||||
else:
|
||||
cls = CommentOption
|
||||
|
||||
g.db.flush()
|
||||
existing = g.db.query(cls).filter_by(
|
||||
parent_id=target.id,
|
||||
body=body,
|
||||
exclusive=exclusive,
|
||||
).one_or_none()
|
||||
|
||||
if not existing:
|
||||
option = cls(
|
||||
parent_id=target.id,
|
||||
body=body,
|
||||
body_html=filter_emojis_only(body),
|
||||
exclusive=exclusive,
|
||||
)
|
||||
g.db.add(option)
|
||||
|
|
|
@ -30,7 +30,7 @@ valid_sub_regex = re.compile("^[a-zA-Z0-9_\-]{3,25}$", flags=re.A)
|
|||
query_regex = re.compile("(\w+):(\S+)", flags=re.A)
|
||||
|
||||
poll_regex = re.compile("\s*\$\$([^\$\n]+)\$\$\s*(?!([^<]*<\/(code|pre|a)>|[^`]*`))", flags=re.A)
|
||||
bet_regex = re.compile("\s*\$\$\$([^\$\n]+)\$\$\$\s*(?!([^<]*<\/(code|pre|a)>|[^`]*`))", flags=re.A)
|
||||
bet_regex = re.compile("\s*!!([^\$\n]+)!!\s*(?!([^<]*<\/(code|pre|a)>|[^`]*`))", flags=re.A)
|
||||
choice_regex = re.compile("\s*&&([^\$\n]+)&&\s*(?!([^<]*<\/(code|pre|a)>|[^`]*`))", flags=re.A)
|
||||
|
||||
html_comment_regex = re.compile("<!--.*-->", flags=re.A)
|
||||
|
|
|
@ -647,18 +647,3 @@ def validate_css(css):
|
|||
return False, f"The domain '{domain}' is not allowed, please use one of these domains\n\n{approved_embed_hosts}."
|
||||
|
||||
return True, ""
|
||||
|
||||
def sanitize_poll_options(v:User, body:str, allow_bets:bool) -> tuple[str, List[Any], List[Any], List[Any]]:
|
||||
def sanitize_poll_type(body:str, re:re.Pattern) -> tuple[str, List[str]]:
|
||||
opts = []
|
||||
for i in list(re.finditer(body))[:POLL_MAX_OPTIONS] if POLL_MAX_OPTIONS else list(re.finditer(body)):
|
||||
opts.append(filter_emojis_only(i.group(1)))
|
||||
body = body.replace(i.group(0), "")
|
||||
return (body, opts)
|
||||
|
||||
bets = []
|
||||
if allow_bets and v and v.admin_level >= PERMS['POST_BETS']:
|
||||
body, bets = sanitize_poll_type(body, bet_regex)
|
||||
body, options = sanitize_poll_type(body, poll_regex)
|
||||
body, choices = sanitize_poll_type(body, choice_regex)
|
||||
return (body, bets, options, choices)
|
||||
|
|
|
@ -150,8 +150,6 @@ def comment(v:User):
|
|||
if not v.admin_level >= PERMS['POST_COMMENT_MODERATION'] and parent_user.any_block_exists(v):
|
||||
abort(403, "You can't reply to users who have blocked you or users that you have blocked!")
|
||||
|
||||
body, _, options, choices = sanitize_poll_options(v, body, False)
|
||||
|
||||
if request.files.get("file") and not g.is_tor:
|
||||
files = request.files.getlist('file')[:20]
|
||||
|
||||
|
@ -264,15 +262,14 @@ def comment(v:User):
|
|||
g.db.add(c)
|
||||
g.db.flush()
|
||||
|
||||
process_poll_options(v, c)
|
||||
|
||||
execute_blackjack(v, c, c.body, "comment")
|
||||
execute_under_siege(v, c, c.body, "comment")
|
||||
|
||||
if c.level == 1: c.top_comment_id = c.id
|
||||
else: c.top_comment_id = parent.top_comment_id
|
||||
|
||||
process_poll_options(c, CommentOption, options, 0, "Poll", g.db)
|
||||
process_poll_options(c, CommentOption, choices, 1, "Poll", g.db)
|
||||
|
||||
if post_target.id not in ADMIGGER_THREADS and v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in c.body.lower() and not (posting_to_submission and post_target.sub == 'chudrama'):
|
||||
c.is_banned = True
|
||||
c.ban_reason = "AutoJanny"
|
||||
|
@ -403,10 +400,6 @@ def edit_comment(cid, v):
|
|||
elif v.bird and len(body) > 140:
|
||||
abort(403, "You have to type less than 140 characters!")
|
||||
|
||||
body, _, options, choices = sanitize_poll_options(v, body, False)
|
||||
process_poll_options(c, CommentOption, options, 0, "Poll", g.db)
|
||||
process_poll_options(c, CommentOption, choices, 1, "Poll", g.db)
|
||||
|
||||
execute_antispam_comment_check(body, v)
|
||||
|
||||
body = process_files(request.files, v, body)
|
||||
|
@ -428,6 +421,9 @@ def edit_comment(cid, v):
|
|||
abort(403, "You can only type marseys!")
|
||||
|
||||
c.body = body
|
||||
|
||||
process_poll_options(v, c)
|
||||
|
||||
c.body_html = body_html
|
||||
|
||||
execute_blackjack(v, c, c.body, "comment")
|
||||
|
|
|
@ -35,7 +35,7 @@ def vote_option(option_id, v):
|
|||
if option.exclusive:
|
||||
vote = g.db.query(SubmissionOptionVote).join(SubmissionOption).filter(
|
||||
SubmissionOptionVote.user_id==v.id,
|
||||
SubmissionOptionVote.submission_id==option.submission_id,
|
||||
SubmissionOptionVote.submission_id==option.parent_id,
|
||||
SubmissionOption.exclusive==option.exclusive).all()
|
||||
if vote:
|
||||
if option.exclusive == 2: abort(400, "You already voted on this bet!")
|
||||
|
@ -47,7 +47,7 @@ def vote_option(option_id, v):
|
|||
vote = SubmissionOptionVote(
|
||||
option_id=option_id,
|
||||
user_id=v.id,
|
||||
submission_id=option.submission_id,
|
||||
submission_id=option.parent_id,
|
||||
)
|
||||
g.db.add(vote)
|
||||
elif existing and not option.exclusive:
|
||||
|
@ -107,7 +107,7 @@ def vote_option_comment(option_id, v):
|
|||
if option.exclusive:
|
||||
vote = g.db.query(CommentOptionVote).join(CommentOption).filter(
|
||||
CommentOptionVote.user_id==v.id,
|
||||
CommentOptionVote.comment_id==option.comment_id,
|
||||
CommentOptionVote.comment_id==option.parent_id,
|
||||
CommentOption.exclusive==1).one_or_none()
|
||||
if vote:
|
||||
g.db.delete(vote)
|
||||
|
@ -117,7 +117,7 @@ def vote_option_comment(option_id, v):
|
|||
vote = CommentOptionVote(
|
||||
option_id=option_id,
|
||||
user_id=v.id,
|
||||
comment_id=option.comment_id,
|
||||
comment_id=option.parent_id,
|
||||
)
|
||||
g.db.add(vote)
|
||||
elif existing:
|
||||
|
|
|
@ -294,11 +294,6 @@ def edit_post(pid, v):
|
|||
body = body.strip()[:POST_BODY_LENGTH_LIMIT(v)] # process_files() may be adding stuff to the body
|
||||
|
||||
if body != p.body:
|
||||
body, bets, options, choices = sanitize_poll_options(v, body, False)
|
||||
process_poll_options(p, SubmissionOption, bets, 2, "Bet", g.db)
|
||||
process_poll_options(p, SubmissionOption, options, 0, "Poll", g.db)
|
||||
process_poll_options(p, SubmissionOption, choices, 1, "Poll", g.db)
|
||||
|
||||
torture = (v.agendaposter and not v.marseyawarded and p.sub != 'chudrama' and v.id == p.author_id)
|
||||
|
||||
body_html = sanitize(body, golden=False, limit_pings=100, showmore=False, torture=torture)
|
||||
|
@ -309,6 +304,8 @@ def edit_post(pid, v):
|
|||
|
||||
p.body = body
|
||||
|
||||
process_poll_options(v, p)
|
||||
|
||||
execute_under_siege(v, p, p.body, 'submission')
|
||||
|
||||
for text in [p.body, p.title, p.url]:
|
||||
|
@ -649,8 +646,6 @@ def submit_post(v:User, sub=None):
|
|||
if len(url) > 2048:
|
||||
abort(400, "There's a 2048 character limit for URLs!")
|
||||
|
||||
body, bets, options, choices = sanitize_poll_options(v, body, True)
|
||||
|
||||
body = process_files(request.files, v, body)
|
||||
body = body.strip()[:POST_BODY_LENGTH_LIMIT(v)] # process_files() adds content to the body, so we need to re-strip
|
||||
|
||||
|
@ -699,13 +694,11 @@ def submit_post(v:User, sub=None):
|
|||
g.db.add(post)
|
||||
g.db.flush()
|
||||
|
||||
process_poll_options(v, post)
|
||||
|
||||
for text in {post.body, post.title, post.url}:
|
||||
if execute_blackjack(v, post, text, 'submission'): break
|
||||
|
||||
process_poll_options(post, SubmissionOption, bets, 2, "Bet", g.db)
|
||||
process_poll_options(post, SubmissionOption, options, 0, "Poll", g.db)
|
||||
process_poll_options(post, SubmissionOption, choices, 1, "Poll", g.db)
|
||||
|
||||
vote = Vote(user_id=v.id,
|
||||
vote_type=1,
|
||||
submission_id=post.id
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
alter table submission_options add column body character varying(500);
|
||||
update submission_options set body=body_html;
|
||||
alter table submission_options alter column body set not null;
|
||||
|
||||
alter table comment_options add column body character varying(500);
|
||||
update comment_options set body=body_html;
|
||||
alter table comment_options alter column body set not null;
|
||||
|
||||
alter table submission_options rename column submission_id to parent_id;
|
||||
|
||||
alter table comment_options rename column comment_id to parent_id;
|
|
@ -327,7 +327,8 @@ CREATE TABLE public.comment_option_votes (
|
|||
|
||||
CREATE TABLE public.comment_options (
|
||||
id integer DEFAULT nextval('public.comment_option_id_seq'::regclass) NOT NULL,
|
||||
comment_id integer NOT NULL,
|
||||
parent_id integer NOT NULL,
|
||||
body character varying(500) NOT NULL,
|
||||
body_html character varying(500) NOT NULL,
|
||||
exclusive integer NOT NULL,
|
||||
created_utc integer
|
||||
|
@ -870,7 +871,8 @@ CREATE TABLE public.submission_option_votes (
|
|||
|
||||
CREATE TABLE public.submission_options (
|
||||
id integer DEFAULT nextval('public.submission_option_id_seq'::regclass) NOT NULL,
|
||||
submission_id integer NOT NULL,
|
||||
parent_id integer NOT NULL,
|
||||
body character varying(500) NOT NULL,
|
||||
body_html character varying(500) NOT NULL,
|
||||
exclusive integer NOT NULL,
|
||||
created_utc integer
|
||||
|
@ -2872,4 +2874,3 @@ ALTER TABLE ONLY public.comments
|
|||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
|
|
Loading…
Reference in New Issue