diff --git a/files/assets/js/submit_emojis.js b/files/assets/js/submit_emojis.js
index f22f33c46..3b648af40 100644
--- a/files/assets/js/submit_emojis.js
+++ b/files/assets/js/submit_emojis.js
@@ -4,6 +4,7 @@ function approve_emoji(t, name) {
"tags": document.getElementById(`${name}-tags`).value,
"name": document.getElementById(`${name}-name`).value,
"kind": document.getElementById(`${name}-kind`).value,
+ "over_18": document.getElementById(`${name}-over-18`).value,
},
() => {
document.getElementById(`${name}-emoji`).remove()
diff --git a/files/classes/emoji.py b/files/classes/emoji.py
index 073be9ee4..cf3939711 100644
--- a/files/classes/emoji.py
+++ b/files/classes/emoji.py
@@ -15,6 +15,7 @@ class Emoji(Base):
count = Column(Integer, default=0)
submitter_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
+ over_18 = Column(Boolean, default=False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
diff --git a/files/helpers/const_stateful.py b/files/helpers/const_stateful.py
index 03dd75e2e..fe6061f00 100644
--- a/files/helpers/const_stateful.py
+++ b/files/helpers/const_stateful.py
@@ -12,15 +12,16 @@ SNAPPY_QUOTES = []
SNAPPY_QUOTES_FISTMAS = []
SNAPPY_QUOTES_HOMOWEEN = []
STEALTH_HOLES = []
+OVER_18_EMOJIS = []
def const_initialize():
- global MARSEYS_CONST, MARSEYS_CONST2, MARSEY_MAPPINGS, SNAPPY_KONGS, SNAPPY_MARSEYS, SNAPPY_QUOTES, SNAPPY_QUOTES_FISTMAS, SNAPPY_QUOTES_HOMOWEEN, STEALTH_HOLES
+ global MARSEYS_CONST, MARSEYS_CONST2, MARSEY_MAPPINGS, SNAPPY_KONGS, SNAPPY_MARSEYS, SNAPPY_QUOTES, SNAPPY_QUOTES_FISTMAS, SNAPPY_QUOTES_HOMOWEEN, STEALTH_HOLES, OVER_18_EMOJIS
db = db_session()
- MARSEYS_CONST = [x[0] for x in db.query(Emoji.name).filter(Emoji.kind=="Marsey", Emoji.submitter_id==None, Emoji.name!='chudsey')]
+ MARSEYS_CONST = [x[0] for x in db.query(Emoji.name).filter(Emoji.kind == "Marsey", Emoji.submitter_id == None, Emoji.name != 'chudsey', Emoji.over_18 == False)]
MARSEYS_CONST2 = MARSEYS_CONST + ['chudsey','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','exclamationpoint','period','questionmark']
- marseys = db.query(Emoji).filter(Emoji.kind=="Marsey", Emoji.submitter_id==None).all()
+ marseys = db.query(Emoji).filter(Emoji.kind=="Marsey", Emoji.submitter_id == None, Emoji.over_18 == False).all()
for marsey in marseys:
for tag in marsey.tags.split():
if tag in MARSEY_MAPPINGS:
@@ -28,11 +29,13 @@ def const_initialize():
else:
MARSEY_MAPPINGS[tag] = [marsey.name]
- SNAPPY_KONGS = db.query(Emoji.name).filter(Emoji.kind=="Donkey Kong", Emoji.submitter_id==None).all()
+ SNAPPY_KONGS = db.query(Emoji.name).filter(Emoji.kind=="Donkey Kong", Emoji.submitter_id==None, Emoji.over_18 == False).all()
SNAPPY_KONGS = [f':#{x[0]}:' for x in SNAPPY_KONGS]
STEALTH_HOLES = [x[0] for x in db.query(Sub.name).filter_by(stealth=True)]
+ OVER_18_EMOJIS = [x[0] for x in db.query(Emoji.name).filter_by(over_18=True)]
+
db.commit()
db.close()
diff --git a/files/helpers/sanitize.py b/files/helpers/sanitize.py
index 4c3f3ff90..f426d322b 100644
--- a/files/helpers/sanitize.py
+++ b/files/helpers/sanitize.py
@@ -218,58 +218,58 @@ def execute_blackjack(v, target, body, kind):
send_repeatable_notification_duplicated(id, f"Blackjack by @{v.username}: {extra_info}")
return True
-def find_all_emote_endings(word):
+def find_all_emote_endings(emoji):
endings = []
- if path.isfile(f'files/assets/images/emojis/{word}.webp'):
- return endings, word
+ if path.isfile(f'files/assets/images/emojis/{emoji}.webp'):
+ return endings, emoji
is_non_ending_found = False
while not is_non_ending_found:
- if word.endswith('pat'):
+ if emoji.endswith('pat'):
if 'pat' in endings:
is_non_ending_found = True
continue
endings.append('pat')
- word = word[:-3]
+ emoji = emoji[:-3]
continue
- if word.endswith('talking'):
+ if emoji.endswith('talking'):
if 'talking' in endings:
is_non_ending_found = True
continue
endings.append('talking')
- word = word[:-7]
+ emoji = emoji[:-7]
continue
- if word.endswith('genocide'):
+ if emoji.endswith('genocide'):
if 'genocide' in endings:
is_non_ending_found = True
continue
endings.append('genocide')
- word = word[:-8]
+ emoji = emoji[:-8]
continue
- if word.endswith('love'):
+ if emoji.endswith('love'):
if 'love' in endings:
is_non_ending_found = True
continue
endings.append('love')
- word = word[:-4]
+ emoji = emoji[:-4]
continue
is_non_ending_found = True
- if word.endswith('random'):
- kind = word.split('random')[0].title()
+ if emoji.endswith('random'):
+ kind = emoji.split('random')[0].title()
if kind == 'Donkeykong': kind = 'Donkey Kong'
elif kind == 'Marseyflag': kind = 'Marsey Flags'
elif kind == 'Marseyalphabet': kind = 'Marsey Alphabet'
if kind in EMOJI_KINDS:
- word = g.db.query(Emoji.name).filter_by(kind=kind).order_by(func.random()).first()[0]
+ emoji = g.db.query(Emoji.name).filter_by(kind=kind, over_18=False).order_by(func.random()).first()[0]
- return endings, word
+ return endings, emoji
def render_emoji(html, regexp, golden, emojis_used, b=False, is_title=False):
@@ -411,7 +411,7 @@ def handle_youtube_links(url):
return html
@with_sigalrm_timeout(10)
-def sanitize(sanitized, golden=True, limit_pings=0, showmore=False, count_emojis=False, snappy=False, chat=False, blackjack=None, post_mention_notif=False, commenters_ping_post_id=None):
+def sanitize(sanitized, golden=True, limit_pings=0, showmore=False, count_emojis=False, snappy=False, chat=False, blackjack=None, post_mention_notif=False, commenters_ping_post_id=None, obj=None):
def error(error):
if chat:
return error, 403
@@ -563,6 +563,12 @@ def sanitize(sanitized, golden=True, limit_pings=0, showmore=False, count_emojis
emoji.count += 1
g.db.add(emoji)
+ if obj:
+ for emoji in emojis_used:
+ if emoji in OVER_18_EMOJIS:
+ obj.over_18 = True
+ break
+
sanitized = sanitized.replace('
', '')
allowed_css_properties = allowed_styles.copy()
@@ -704,7 +710,7 @@ def allowed_attributes_emojis(tag, name, value):
@with_sigalrm_timeout(2)
-def filter_emojis_only(title, golden=True, count_emojis=False):
+def filter_emojis_only(title, golden=True, count_emojis=False, obj=None):
title = title.replace("\n", "").replace("\r", "").replace("\t", "").replace('<','<').replace('>','>')
@@ -719,6 +725,12 @@ def filter_emojis_only(title, golden=True, count_emojis=False):
emoji.count += 1
g.db.add(emoji)
+ if obj:
+ for emoji in emojis_used:
+ if emoji in OVER_18_EMOJIS:
+ obj.over_18 = True
+ break
+
title = strikethrough_regex.sub(r'\1\2', title)
title = bleach.clean(title, tags=['img','del','span'], attributes=allowed_attributes_emojis, protocols=['http','https']).replace('\n','')
diff --git a/files/routes/asset_submissions.py b/files/routes/asset_submissions.py
index 572149b7a..76a073a96 100644
--- a/files/routes/asset_submissions.py
+++ b/files/routes/asset_submissions.py
@@ -48,6 +48,7 @@ def submit_emoji(v):
tags = request.values.get('tags', '').lower().strip()
username = request.values.get('author', '').lower().strip()
kind = request.values.get('kind', '').strip()
+ over_18 = request.values.get("over_18", False, bool)
for modifier in emoji_modifiers:
if name.endswith(modifier):
@@ -91,7 +92,15 @@ def submit_emoji(v):
copyfile(highquality, filename)
process_image(filename, v, resize=300, trim=True)
- emoji = Emoji(name=name, kind=kind, author_id=author.id, tags=tags, count=0, submitter_id=v.id)
+ emoji = Emoji(
+ name=name,
+ kind=kind,
+ author_id=author.id,
+ tags=tags,
+ count=0,
+ submitter_id=v.id,
+ over_18=over_18,
+ )
g.db.add(emoji)
return {"message": f"'{name}' submitted successfully!"}
@@ -139,10 +148,12 @@ def approve_emoji(v, name):
if new_kind not in EMOJI_KINDS:
abort(400, "Invalid kind!")
+ over_18 = request.values.get("over_18", False, bool)
emoji.name = new_name
emoji.kind = new_kind
emoji.tags = tags
+ emoji.over_18 = over_18
g.db.add(emoji)
author = get_account(emoji.author_id)
@@ -177,9 +188,9 @@ def approve_emoji(v, name):
badge_grant(badge_id=113, user=author)
badge_grant(badge_id=112, user=author)
-
- cache.delete("emojis")
- cache.delete(f"emoji_list_{emoji.kind}")
+ if not Emoji.over_18:
+ cache.delete("emojis")
+ cache.delete(f"emoji_list_{emoji.kind}")
purge_files_in_cloudflare_cache(f"{SITE_FULL_IMAGES}/e/{emoji.name}/webp")
@@ -210,6 +221,9 @@ def approve_emoji(v, name):
)
g.db.add(ma)
+ if emoji.over_18:
+ OVER_18_EMOJIS.append(emoji.name)
+
return {"message": f"'{emoji.name}' approved!"}
def remove_asset(cls, type_name, v, name):
@@ -474,8 +488,9 @@ def update_emoji(v):
)
g.db.add(ma)
- cache.delete("emojis")
- cache.delete(f"emoji_list_{existing.kind}")
+ if not Emoji.over_18:
+ cache.delete("emojis")
+ cache.delete(f"emoji_list_{existing.kind}")
return {"message": f"'{name}' updated successfully!"}
diff --git a/files/routes/comments.py b/files/routes/comments.py
index be4011292..4092841c6 100644
--- a/files/routes/comments.py
+++ b/files/routes/comments.py
@@ -260,7 +260,29 @@ def comment(v):
if v.marsify and not v.chud: body_for_sanitize = marsify(body_for_sanitize)
if v.sharpen: body_for_sanitize = sharpen(body_for_sanitize)
- body_html = sanitize(body_for_sanitize, limit_pings=5, showmore=(not v.marseyawarded), count_emojis=not v.marsify, commenters_ping_post_id=commenters_ping_post_id)
+ is_bot = v.client is not None and v.id not in BOT_SYMBOL_HIDDEN
+
+ chudded = v.chud and not (posting_to_post and post_target.sub == 'chudrama')
+
+ c = Comment(author_id=v.id,
+ parent_post=post_target.id if posting_to_post else None,
+ wall_user_id=post_target.id if not posting_to_post else None,
+ parent_comment_id=parent_comment_id,
+ level=level,
+ over_18=post_target.over_18 if posting_to_post else False,
+ is_bot=is_bot,
+ app_id=v.client.application.id if v.client else None,
+ body=body,
+ ghost=ghost,
+ chudded=chudded,
+ rainbowed=bool(v.rainbow),
+ queened=bool(v.queen),
+ sharpened=bool(v.sharpen),
+ )
+
+ c.upvotes = 1
+
+ body_html = sanitize(body_for_sanitize, limit_pings=5, showmore=(not v.marseyawarded), count_emojis=not v.marsify, commenters_ping_post_id=commenters_ping_post_id, obj=c)
if post_target.id not in ADMIGGER_THREADS and not (v.chud and v.chud_phrase in body.lower()):
existing = g.db.query(Comment.id).filter(
@@ -282,28 +304,8 @@ def comment(v):
if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT:
abort(400, "Comment too long!")
- is_bot = v.client is not None and v.id not in BOT_SYMBOL_HIDDEN
+ c.body_html = body_html
- chudded = v.chud and not (posting_to_post and post_target.sub == 'chudrama')
-
- c = Comment(author_id=v.id,
- parent_post=post_target.id if posting_to_post else None,
- wall_user_id=post_target.id if not posting_to_post else None,
- parent_comment_id=parent_comment_id,
- level=level,
- over_18=post_target.over_18 if posting_to_post else False,
- is_bot=is_bot,
- app_id=v.client.application.id if v.client else None,
- body_html=body_html,
- body=body,
- ghost=ghost,
- chudded=chudded,
- rainbowed=bool(v.rainbow),
- queened=bool(v.queen),
- sharpened=bool(v.sharpen),
- )
-
- c.upvotes = 1
g.db.add(c)
g.db.flush()
@@ -683,7 +685,7 @@ def edit_comment(cid, v):
if c.sharpened:
body_for_sanitize = sharpen(body_for_sanitize)
- body_html = sanitize(body_for_sanitize, golden=False, limit_pings=5, showmore=(not v.marseyawarded), commenters_ping_post_id=c.parent_post)
+ body_html = sanitize(body_for_sanitize, golden=False, limit_pings=5, showmore=(not v.marseyawarded), commenters_ping_post_id=c.parent_post, obj=c)
if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: abort(400)
diff --git a/files/routes/posts.py b/files/routes/posts.py
index 34301dde6..1ec52524c 100644
--- a/files/routes/posts.py
+++ b/files/routes/posts.py
@@ -457,11 +457,6 @@ def submit_post(v, sub=None):
if SITE == 'rdrama.net' and v.id == 10947:
sub = 'mnn'
- title_html = filter_emojis_only(title, count_emojis=True)
-
- if v.marseyawarded and not marseyaward_title_regex.fullmatch(title_html):
- abort(400, "You can only type marseys!")
-
if sub == 'changelog':
abort(400, "/h/changelog is archived")
@@ -541,14 +536,6 @@ def submit_post(v, sub=None):
body_for_sanitize = body
if v.sharpen: body_for_sanitize = sharpen(body_for_sanitize)
- body_html = sanitize(body_for_sanitize, count_emojis=True, limit_pings=100)
-
- if v.marseyawarded and marseyaward_body_regex.search(body_html):
- abort(400, "You can only type marseys!")
-
- if len(body_html) > POST_BODY_HTML_LENGTH_LIMIT:
- abort(400, "Post body_html too long!")
-
flag_notify = (request.values.get("notify", "on") == "on")
flag_new = request.values.get("new", False, bool) or 'megathread' in title.lower()
flag_over_18 = FEATURES['NSFW_MARKING'] and request.values.get("over_18", False, bool)
@@ -577,10 +564,8 @@ def submit_post(v, sub=None):
is_bot=(v.client is not None),
url=url,
body=body,
- body_html=body_html,
embed=embed,
title=title,
- title_html=title_html,
sub=sub,
ghost=flag_ghost,
chudded=flag_chudded,
@@ -589,6 +574,23 @@ def submit_post(v, sub=None):
sharpened=bool(v.sharpen),
)
+ title_html = filter_emojis_only(title, count_emojis=True, obj=p)
+
+ if v.marseyawarded and not marseyaward_title_regex.fullmatch(title_html):
+ abort(400, "You can only type marseys!")
+
+ p.title_html = title_html
+
+ body_html = sanitize(body_for_sanitize, count_emojis=True, limit_pings=100, obj=p)
+
+ if v.marseyawarded and marseyaward_body_regex.search(body_html):
+ abort(400, "You can only type marseys!")
+
+ if len(body_html) > POST_BODY_HTML_LENGTH_LIMIT:
+ abort(400, "Post body_html too long!")
+
+ p.body_html = body_html
+
g.db.add(p)
g.db.flush()
@@ -1015,7 +1017,7 @@ def edit_post(pid, v):
if title != p.title:
- title_html = filter_emojis_only(title, golden=False)
+ title_html = filter_emojis_only(title, golden=False, obj=p)
if p.author.marseyawarded and not marseyaward_title_regex.fullmatch(title_html):
abort(403, "You can only type marseys!")
@@ -1033,7 +1035,7 @@ def edit_post(pid, v):
body_for_sanitize = body
if p.sharpened: body_for_sanitize = sharpen(body_for_sanitize)
- body_html = sanitize(body_for_sanitize, golden=False, limit_pings=100)
+ body_html = sanitize(body_for_sanitize, golden=False, limit_pings=100, obj=p)
if p.author.marseyawarded and marseyaward_body_regex.search(body_html):
abort(403, "You can only type marseys!")
diff --git a/files/routes/static.py b/files/routes/static.py
index 98fa1e3f6..de5666019 100644
--- a/files/routes/static.py
+++ b/files/routes/static.py
@@ -45,7 +45,7 @@ def reddit_post(subreddit, v, path):
@cache.cached(make_cache_key=lambda kind:f"emoji_list_{kind}")
def get_emoji_list(kind):
emojis = []
- for emoji, author in g.db.query(Emoji, User).join(User, Emoji.author_id == User.id).filter(Emoji.submitter_id == None, Emoji.kind == kind).order_by(Emoji.count.desc()):
+ for emoji, author in g.db.query(Emoji, User).join(User, Emoji.author_id == User.id).filter(Emoji.submitter_id == None, Emoji.kind == kind, Emoji.over_18 == False).order_by(Emoji.count.desc()):
emoji.author = author.username if FEATURES['ASSET_SUBMISSIONS'] else None
emojis.append(emoji)
return emojis
@@ -83,7 +83,7 @@ def emoji_list(v, kind):
@cache.cached(make_cache_key=lambda:"emojis")
def get_emojis():
- emojis = g.db.query(Emoji, User).join(User, Emoji.author_id == User.id).filter(Emoji.submitter_id == None)
+ emojis = g.db.query(Emoji, User).join(User, Emoji.author_id == User.id).filter(Emoji.submitter_id == None, Emoji.over_18 == False)
emojis1 = emojis.filter(Emoji.kind != 'Marsey Alphabet').order_by(Emoji.count.desc()).all()
emojis2 = emojis.filter(Emoji.kind == 'Marsey Alphabet').order_by(func.length(Emoji.name), Emoji.name).all()
emojis = emojis1 + emojis2
diff --git a/files/templates/submit_emojis.html b/files/templates/submit_emojis.html
index de0549b38..b348d3efa 100644
--- a/files/templates/submit_emojis.html
+++ b/files/templates/submit_emojis.html
@@ -43,6 +43,13 @@
+ {% if FEATURES['NSFW_MARKING'] %}
+
+
+
+
+ {% endif %}
+
{% if v.admin_level >= PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'] or v.id == emoji.submitter_id %}
diff --git a/migrations/20230929-add-over-18-emojis.sql b/migrations/20230929-add-over-18-emojis.sql
new file mode 100644
index 000000000..eecc07b5a
--- /dev/null
+++ b/migrations/20230929-add-over-18-emojis.sql
@@ -0,0 +1,2 @@
+alter table emojis add column over_18 bool default false not null;
+alter table emojis alter column over_18 drop default;