diff --git a/files/classes/comment.py b/files/classes/comment.py index 41fc2af84..09d207620 100644 --- a/files/classes/comment.py +++ b/files/classes/comment.py @@ -228,7 +228,7 @@ class Comment(Base): @property @lazy - def shortlink_context(self): + def sl(self): return f"/comment/{self.id}?context=8#context" @property diff --git a/files/classes/exiles.py b/files/classes/exiles.py new file mode 100644 index 000000000..b2469e55a --- /dev/null +++ b/files/classes/exiles.py @@ -0,0 +1,12 @@ +from sqlalchemy import * +from sqlalchemy.orm import relationship +from files.__main__ import Base + +class Exile(Base): + + __tablename__ = "exiles" + user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) + sub = Column(String, ForeignKey("subs.name"), primary_key=True) + + def __repr__(self): + return f"" \ No newline at end of file diff --git a/files/classes/submission.py b/files/classes/submission.py index 0aacb5870..c300524f6 100644 --- a/files/classes/submission.py +++ b/files/classes/submission.py @@ -204,6 +204,13 @@ class Submission(Base): def fullname(self): return f"t2_{self.id}" + + @property + @lazy + def sl(self): + return f'/post/{self.id}' + + @property @lazy def shortlink(self): diff --git a/files/classes/user.py b/files/classes/user.py index 7220d0581..84e4f9dcd 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -14,6 +14,7 @@ from .badges import * from .clients import * from .mod_logs import * from .mod import * +from .exiles import * from .sub_block import * from files.__main__ import Base, cache from files.helpers.security import * @@ -153,7 +154,11 @@ class User(Base): @lazy def mods(self, sub): - return self.id == AEVANN_ID or g.db.query(Mod.user_id).filter_by(user_id=self.id, sub=sub).one_or_none() + return self.id == AEVANN_ID or bool(g.db.query(Mod.user_id).filter_by(user_id=self.id, sub=sub).one_or_none()) + + @lazy + def exiled_from(self, sub): + return self.admin_level < 2 and bool(g.db.query(Exile.user_id).filter_by(user_id=self.id, sub=sub).one_or_none()) @property @lazy diff --git a/files/helpers/sanitize.py b/files/helpers/sanitize.py index eed536fa4..c3b54eb58 100644 --- a/files/helpers/sanitize.py +++ b/files/helpers/sanitize.py @@ -117,9 +117,9 @@ def sanitize(sanitized, noimages=False, alert=False, comment=False, edit=False): if u: sanitized = sanitized.replace(i.group(0), f'''

@{u.username}'s profile picture@{u.username}''', 1) else: - sanitized = re.sub('(^|\s|\n|

)\/?((r|u)\/(\w|-){3,25})', r'\1\2', sanitized, re.A) + sanitized = re.sub('(^|\s|\n|

)\/?((r|u)\/(\w|-){3,25})', r'\1/\2', sanitized, re.A) - sanitized = re.sub('(^|\s|\n|

)\/?(s\/(\w|-){3,25})', r'\1\2', sanitized, re.A) + sanitized = re.sub('(^|\s|\n|

)\/?(s\/(\w|-){3,25})', r'\1/\2', sanitized, re.A) for i in re.finditer('(^|\s|\n|

)@((\w|-){1,25})', sanitized, re.A): u = get_user(i.group(2), graceful=True) diff --git a/files/routes/admin.py b/files/routes/admin.py index 8730f0f05..88009f961 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -85,13 +85,13 @@ def distribute(v, comment): votes = g.db.query(CommentVote).filter_by(comment_id=comment) coinsperperson = int(pool / votes.count()) - cid = notif_comment(f"You won {coinsperperson} coins betting on [{post.permalink}]({post.permalink}) :marseyparty:") + cid = notif_comment(f"You won {coinsperperson} coins betting on [{post.title}]({post.sl}) :marseyparty:") for vote in votes: u = vote.user u.coins += coinsperperson add_notif(cid, u.id) - cid = notif_comment(f"You lost the 200 coins you bet on [{post.permalink}]({post.permalink}) :marseylaugh:") + cid = notif_comment(f"You lost the 200 coins you bet on [{post.title}]({post.sl}) :marseylaugh:") cids = [x.id for x in post.bet_options] cids.remove(comment) votes = g.db.query(CommentVote).filter(CommentVote.comment_id.in_(cids)).all() @@ -1240,7 +1240,7 @@ def sticky_comment(cid, v): g.db.add(ma) if v.id != comment.author_id: - message = f"@{v.username} has pinned your [comment]({comment.permalink})!" + message = f"@{v.username} has pinned your [comment]({comment.sl})!" send_repeatable_notification(comment.author_id, message) g.db.commit() @@ -1267,7 +1267,7 @@ def unsticky_comment(cid, v): g.db.add(ma) if v.id != comment.author_id: - message = f"@{v.username} has unpinned your [comment]({comment.permalink})!" + message = f"@{v.username} has unpinned your [comment]({comment.sl})!" send_repeatable_notification(comment.author_id, message) g.db.commit() diff --git a/files/routes/awards.py b/files/routes/awards.py index b8f03f4d7..5f6f99319 100644 --- a/files/routes/awards.py +++ b/files/routes/awards.py @@ -227,7 +227,7 @@ def award_post(pid, v): author = post.author if v.id != author.id: - msg = f"@{v.username} has given your [post]({post.permalink}) the {AWARDS[kind]['title']} Award!" + msg = f"@{v.username} has given your [post]({post.sl}) the {AWARDS[kind]['title']} Award!" if note: msg += f"\n\n> {note}" send_repeatable_notification(author.id, msg) @@ -235,7 +235,7 @@ def award_post(pid, v): return {"error": "You can't use this award on yourself."}, 400 if kind == "ban": - link = f"[this post]({post.permalink})" + link = f"[this post]({post.sl})" if not author.is_suspended: author.ban(reason=f"1-Day ban award used by @{v.username} on /post/{post.id}", days=1) @@ -258,7 +258,7 @@ def award_post(pid, v): author.is_banned = AUTOJANNY_ID author.ban_reason = f"grass award used by @{v.username} on /post/{post.id}" author.unban_utc = int(time.time()) + 30 * 86400 - link = f"[this post]({post.permalink})" + link = f"[this post]({post.sl})" send_repeatable_notification(author.id, f"Your account has been suspended permanently for {link}. You must [provide the admins](/contact) a timestamped picture of you touching grass to get unbanned!") elif kind == "pin": if post.stickied and post.stickied_utc: @@ -461,7 +461,7 @@ def award_comment(cid, v): author = c.author if v.id != author.id: - msg = f"@{v.username} has given your [comment]({c.permalink}) the {AWARDS[kind]['title']} Award!" + msg = f"@{v.username} has given your [comment]({c.sl}) the {AWARDS[kind]['title']} Award!" if note: msg += f"\n\n> {note}" send_repeatable_notification(author.id, msg) @@ -469,7 +469,7 @@ def award_comment(cid, v): return {"error": "You can't use this award on yourself."}, 400 if kind == "ban": - link = f"[this comment]({c.permalink})" + link = f"[this comment]({c.sl})" if not author.is_suspended: author.ban(reason=f"1-Day ban award used by @{v.username} on /comment/{c.id}", days=1) @@ -492,7 +492,7 @@ def award_comment(cid, v): author.is_banned = AUTOJANNY_ID author.ban_reason = f"grass award used by @{v.username} on /comment/{c.id}" author.unban_utc = int(time.time()) + 30 * 86400 - link = f"[this comment]({c.permalink})" + link = f"[this comment]({c.sl})" send_repeatable_notification(author.id, f"Your account has been suspended permanently for {link}. You must [provide the admins](/contact) a timestamped picture of you touching grass to get unbanned!") elif kind == "pin": if c.is_pinned and c.is_pinned_utc: c.is_pinned_utc += 3600 diff --git a/files/routes/comments.py b/files/routes/comments.py index 82ac3fc13..b0ce382c7 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -148,6 +148,9 @@ def api_comment(v): parent_fullname = request.values.get("parent_fullname").strip() parent_post = get_post(parent_submission, v=v) + sub = parent_post.sub + if sub and v.exiled_from(sub): return {"error": f"You're exiled from /s/{sub}"}, 403 + if parent_post.club and not (v and (v.paid_dues or v.id == parent_post.author_id)): abort(403) if parent_fullname.startswith("t2_"): @@ -926,7 +929,7 @@ def pin_comment(cid, v): g.db.add(comment) if v.id != comment.author_id: - message = f"@{v.username} (OP) has pinned your [comment]({comment.permalink})!" + message = f"@{v.username} (OP) has pinned your [comment]({comment.sl})!" send_repeatable_notification(comment.author_id, message) g.db.commit() @@ -949,7 +952,7 @@ def unpin_comment(cid, v): g.db.add(comment) if v.id != comment.author_id: - message = f"@{v.username} (OP) has unpinned your [comment]({comment.permalink})!" + message = f"@{v.username} (OP) has unpinned your [comment]({comment.sl})!" send_repeatable_notification(comment.author_id, message) g.db.commit() return {"message": "Comment unpinned!"} @@ -969,7 +972,7 @@ def mod_pin(cid, v): g.db.add(comment) if v.id != comment.author_id: - message = f"@{v.username} (Mod) has pinned your [comment]({comment.permalink})!" + message = f"@{v.username} (Mod) has pinned your [comment]({comment.sl})!" send_repeatable_notification(comment.author_id, message) g.db.commit() @@ -989,7 +992,7 @@ def mod_unpin(cid, v): g.db.add(comment) if v.id != comment.author_id: - message = f"@{v.username} (Mod) has unpinned your [comment]({comment.permalink})!" + message = f"@{v.username} (Mod) has unpinned your [comment]({comment.sl})!" send_repeatable_notification(comment.author_id, message) g.db.commit() return {"message": "Comment unpinned!"} diff --git a/files/routes/posts.py b/files/routes/posts.py index 340af2ee0..2be4be84e 100644 --- a/files/routes/posts.py +++ b/files/routes/posts.py @@ -96,11 +96,11 @@ def publish(pid, v): if not post.ghost: notify_users = NOTIFY_USERS(post.body_html, v) | NOTIFY_USERS2(post.title, v) - cid = notif_comment(f"@{v.username} has mentioned you: [{post.title}]({post.permalink})") + cid = notif_comment(f"@{v.username} has mentioned you: [{post.title}]({post.sl})") for x in notify_users: add_notif(cid, x) - cid = notif_comment(f"@{v.username} has made a new post: [{post.title}]({post.permalink})", autojanny=True) + cid = notif_comment(f"@{v.username} has made a new post: [{post.title}]({post.sl})", autojanny=True) for follow in v.followers: user = get_account(follow.user_id) if post.club and not user.paid_dues: continue @@ -126,7 +126,7 @@ def submit_get(v, sub=None): if request.path.startswith('/s/') and not sub: abort(404) - return render_template("submit.html", SUBS=() if SITE_NAME == 'Drama' else tuple(x[0] for x in g.db.query(Sub.name).order_by(Sub.name).all()), v=v, sub=sub, ghost=submit_ghost(v,g.db)) + return render_template("submit.html", SUBS=tuple(x[0] for x in g.db.query(Sub.name).order_by(Sub.name).all()), v=v, sub=sub, ghost=submit_ghost(v,g.db)) @app.get("/post/") @app.get("/post//") @@ -603,7 +603,7 @@ def edit_post(pid, v): if not p.private and not p.ghost: notify_users = NOTIFY_USERS(body_html, v) | NOTIFY_USERS2(title, v) - cid = notif_comment(f"@{v.username} has mentioned you: [{p.title}]({p.permalink})") + cid = notif_comment(f"@{v.username} has mentioned you: [{p.title}]({p.sl})") for x in notify_users: add_notif(cid, x) @@ -844,13 +844,6 @@ def thumbnail_thread(pid): @limiter.limit("1/second;6/minute;200/hour;1000/day") @auth_required def submit_post(v, sub=None): - if not sub: sub = request.values.get("sub") - - if sub: - sub = g.db.query(Sub.name).filter_by(name=sub.strip().lower()).one_or_none() - if not sub: abort(404) - sub = sub[0] - else: sub = None title = request.values.get("title", "").strip()[:500].replace('‎','') @@ -859,15 +852,24 @@ def submit_post(v, sub=None): body = request.values.get("body", "").strip().replace('‎','') def error(error): - print(sub, flush=True) - if request.headers.get("Authorization") or request.headers.get("xhr"): error(error) - return render_template("submit.html", SUBS=() if SITE_NAME == 'Drama' else tuple(x[0] for x in g.db.query(Sub.name).order_by(Sub.name).all()), v=v, error=error, title=title, url=url, body=body, price=submit_ghost(v,g.db)), 400 + if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": error}, 403 + return render_template("submit.html", SUBS=tuple(x[0] for x in g.db.query(Sub.name).order_by(Sub.name).all()), v=v, error=error, title=title, url=url, body=body, ghost=submit_ghost(v,g.db)), 400 - if v.is_suspended: error( "You can't perform this action while banned.") + + if not sub: sub = request.values.get("sub") + + if sub: + sub = g.db.query(Sub.name).filter_by(name=sub.strip().lower()).one_or_none() + if not sub: abort(404) + sub = sub[0] + if v.exiled_from(sub): return error(f"You're exiled from /s/{sub}") + else: sub = None + + if v.is_suspended: return error("You can't perform this action while banned.") if v and v.patron: - if request.content_length > 8 * 1024 * 1024: error( "Max file size is 8 MB.") - elif request.content_length > 4 * 1024 * 1024: error( "Max file size is 4 MB.") + if request.content_length > 8 * 1024 * 1024: return error( "Max file size is 8 MB.") + elif request.content_length > 4 * 1024 * 1024: return error( "Max file size is 4 MB.") if v.agendaposter and not v.marseyawarded: title = torture_ap(title, v.username) @@ -1060,7 +1062,7 @@ def submit_post(v, sub=None): file.save("video.mp4") with open("video.mp4", 'rb') as f: try: url = requests.request("POST", "https://api.imgur.com/3/upload", headers={'Authorization': f'Client-ID {IMGUR_KEY}'}, files=[('video', f)]).json()['data']['link'] - except: error( "Imgur error") + except: return error( "Imgur error") if url.endswith('.'): url += 'mp4' body += f"\n\n{url}" else: @@ -1188,7 +1190,7 @@ def submit_post(v, sub=None): file.save("video.mp4") with open("video.mp4", 'rb') as f: try: url = requests.request("POST", "https://api.imgur.com/3/upload", headers={'Authorization': f'Client-ID {IMGUR_KEY}'}, files=[('video', f)]).json()['data']['link'] - except: error( "Imgur error") + except: return error( "Imgur error") if url.endswith('.'): url += 'mp4' new_post.url = url else: @@ -1207,11 +1209,11 @@ def submit_post(v, sub=None): notify_users = NOTIFY_USERS(body_html, v) | NOTIFY_USERS2(title, v) - cid = notif_comment(f"@{v.username} has mentioned you: [{title}]({new_post.permalink})") + cid = notif_comment(f"@{v.username} has mentioned you: [{title}]({new_post.sl})") for x in notify_users: add_notif(cid, x) - cid = notif_comment(f"@{v.username} has made a new post: [{title}]({new_post.permalink})", autojanny=True) + cid = notif_comment(f"@{v.username} has made a new post: [{title}]({new_post.sl})", autojanny=True) for follow in v.followers: user = get_account(follow.user_id) if new_post.club and not user.paid_dues: continue diff --git a/files/routes/subs.py b/files/routes/subs.py index fa96c06ec..b20814098 100644 --- a/files/routes/subs.py +++ b/files/routes/subs.py @@ -7,6 +7,119 @@ from .front import frontlist valid_sub_regex = re.compile("^[a-zA-Z0-9_\-]{3,20}$") + + + + +@app.post("/exile/post/") +@is_not_permabanned +def exile_post(v, pid): + try: pid = int(pid) + except: abort(400) + + p = get_post(pid) + sub = p.sub + if not sub: abort(400) + + if not v.mods(sub): abort(403) + + u = p.author + + if u.admin_level < 2 and not u.exiled_from(sub): + exile = Exile(user_id=u.id, sub=sub) + g.db.add(exile) + + send_notification(u.id, f"You have been exiled from /s/{sub} for [{p.title}]({p.sl})") + + g.db.commit() + + return {"message": "User exiled successfully!"} + + + +@app.post("/unexile/post/") +@is_not_permabanned +def unexile_post(v, pid): + try: pid = int(pid) + except: abort(400) + + p = get_post(pid) + sub = p.sub + if not sub: abort(400) + + if not v.mods(sub): abort(403) + + u = p.author + + if u.exiled_from(sub): + exile = g.db.query(Exile).filter_by(user_id=u.id, sub=sub).one_or_none() + g.db.delete(exile) + + send_notification(u.id, f"Your exile from /s/{sub} has been revoked!") + + g.db.commit() + + return {"message": "User unexiled successfully!"} + + + + +@app.post("/exile/comment/") +@is_not_permabanned +def exile_comment(v, cid): + try: cid = int(cid) + except: abort(400) + + c = get_comment(cid) + sub = c.post.sub + if not sub: abort(400) + + if not v.mods(sub): abort(403) + + u = c.author + + if u.admin_level < 2 and not u.exiled_from(sub): + exile = Exile(user_id=u.id, sub=sub) + g.db.add(exile) + + send_notification(u.id, f"You have been exiled from /s/{sub} for [{c.permalink}]({c.sl})") + + g.db.commit() + + return {"message": "User exiled successfully!"} + + + + +@app.post("/unexile/comment/") +@is_not_permabanned +def unexile_comment(v, cid): + try: cid = int(cid) + except: abort(400) + + c = get_comment(cid) + sub = c.post.sub + if not sub: abort(400) + + if not v.mods(sub): abort(403) + + u = c.author + + if u.exiled_from(sub): + exile = g.db.query(Exile).filter_by(user_id=u.id, sub=sub).one_or_none() + g.db.delete(exile) + + send_notification(u.id, f"Your exile from /s/{sub} has been revoked!") + + g.db.commit() + + return {"message": "User unexiled successfully!"} + + + + + + @app.post("/s//block") @auth_required def block_sub(v, sub): diff --git a/files/templates/authforms.html b/files/templates/authforms.html index 29490100f..063a4ff82 100644 --- a/files/templates/authforms.html +++ b/files/templates/authforms.html @@ -15,7 +15,7 @@ {% if v %} - + {% if v.agendaposter %} - + {% endif %} diff --git a/files/templates/comments.html b/files/templates/comments.html index 4edda0398..76a35e830 100644 --- a/files/templates/comments.html +++ b/files/templates/comments.html @@ -214,6 +214,13 @@ {% endfor %} {% endif %} + {% if c.post %} + {% set sub = c.post.sub %} + {% if sub and c.author.exiled_from(sub) %} + + {% endif %} + {% endif %} + {% if c.bannedfor %} {% endif %} @@ -490,7 +497,7 @@ Context - + {% if v %} @@ -571,14 +578,22 @@ {% if v.admin_level > 1 %} {% if "/reported/" in request.path %} - - + + {% else %} - - + + {% endif %} {% endif %} +{% if c.post %} + {% set sub = c.post.sub %} + {% if sub and v.mods(sub) %} + + + {% endif %} +{% endif %} + {% if c.parent_submission and (c.author_id==v.id or v.admin_level > 1) %} @@ -698,7 +713,7 @@ {% if not c.ghost %}

  • Votes
  • {% endif %} - Copy link + Copy link Context @@ -747,6 +762,14 @@ Unpin {% endif %} {% endif %} + + {% if c.post %} + {% set sub = c.post.sub %} + {% if sub and v.mods(sub) %} + Exile user + Unexile user + {% endif %} + {% endif %} {% endif %} diff --git a/files/templates/default.html b/files/templates/default.html index f4b8fda8c..df1d53a65 100644 --- a/files/templates/default.html +++ b/files/templates/default.html @@ -7,7 +7,7 @@ {% if v %} - + {% if v.agendaposter %} - + {% endif %} {% if sub and sub.css and not request.path.endswith('settings') %} diff --git a/files/templates/log.html b/files/templates/log.html index feac55bcb..a9d988f75 100644 --- a/files/templates/log.html +++ b/files/templates/log.html @@ -6,7 +6,7 @@ {% block content %} {% if v %} - + {% if v.agendaposter %} - + {% endif %}
    diff --git a/files/templates/login.html b/files/templates/login.html index bc2c6eb9e..229bd46b9 100644 --- a/files/templates/login.html +++ b/files/templates/login.html @@ -18,7 +18,7 @@ {% endblock %} - + diff --git a/files/templates/login_2fa.html b/files/templates/login_2fa.html index f5f0234a5..3b51a3971 100644 --- a/files/templates/login_2fa.html +++ b/files/templates/login_2fa.html @@ -14,7 +14,7 @@ 2-Step Login - {{SITE_NAME}} - + diff --git a/files/templates/settings.html b/files/templates/settings.html index 12639f59d..1d2d1f76b 100644 --- a/files/templates/settings.html +++ b/files/templates/settings.html @@ -34,7 +34,7 @@ - + {% if v.agendaposter %} - + {% else %} - + {% endif %} diff --git a/files/templates/sign_up.html b/files/templates/sign_up.html index 87a6e6d67..19a6fc7ee 100644 --- a/files/templates/sign_up.html +++ b/files/templates/sign_up.html @@ -31,7 +31,7 @@ {% if ref_user %}{{ref_user.username}} invites you to {{SITE_NAME}}{% else %}Sign up - {{SITE_NAME}}{% endif %} - + diff --git a/files/templates/sign_up_failed_ref.html b/files/templates/sign_up_failed_ref.html index a8b5ef6c8..6a6f80acb 100644 --- a/files/templates/sign_up_failed_ref.html +++ b/files/templates/sign_up_failed_ref.html @@ -32,7 +32,7 @@ {% if ref_user %}{{ref_user.username}} invites you to {{SITE_NAME}}{% else %}{{SITE_NAME}}{% endif %} - + diff --git a/files/templates/submission.html b/files/templates/submission.html index e8e9476b7..aa327a0b9 100644 --- a/files/templates/submission.html +++ b/files/templates/submission.html @@ -398,7 +398,10 @@ {% endif %} {% if p.sub and v.mods(p.sub) %} - + + + + {% endif %}
    @@ -489,6 +492,10 @@ /s/{{p.sub}}   {% endif %} + {% if p.sub and p.author.exiled_from(p.sub) %} + + {% endif %} + {% if p.bannedfor %} {% endif %} @@ -759,6 +766,10 @@ {% if p.sub and v.mods(p.sub) %} Kick + + Exile user + Unexile user + {% endif %} diff --git a/files/templates/submission_listing.html b/files/templates/submission_listing.html index 783a8af29..dd235724e 100644 --- a/files/templates/submission_listing.html +++ b/files/templates/submission_listing.html @@ -156,6 +156,10 @@ /s/{{p.sub}}   {% endif %} + {% if p.sub and p.author.exiled_from(p.sub) %} + + {% endif %} + {% if p.bannedfor %} {% endif %} @@ -165,6 +169,7 @@ {% endfor %} {% endif %} + {% if v and v.admin_level > 1 and p.author.shadowbanned %} {% endif %} @@ -297,6 +302,9 @@ {% if p.sub and v.mods(p.sub) %} Kick + + Exile user + Unexile user {% endif %} @@ -455,7 +463,10 @@ {% endif %} {% if p.sub and v.mods(p.sub) %} - + + + + {% endif %} diff --git a/files/templates/submit.html b/files/templates/submit.html index 194376f67..ceac83b73 100644 --- a/files/templates/submit.html +++ b/files/templates/submit.html @@ -26,7 +26,7 @@ {% block stylesheets %} {% if v %} - + {% if v.agendaposter %} - + {% endif %} {% endblock %} @@ -79,7 +79,7 @@ - {% if SITE_NAME == 'Ruqqus' %} + {% if SITE_NAME == 'Ruqqus' or sub %}