diff --git a/files/assets/js/comments_admin.js b/files/assets/js/comments_admin.js index 87d9478d3..4de331e8b 100644 --- a/files/assets/js/comments_admin.js +++ b/files/assets/js/comments_admin.js @@ -71,4 +71,31 @@ function approveComment2(post_id,button1,button2) { document.getElementById(button1).classList.toggle("d-none"); document.getElementById(button2).classList.toggle("d-none"); } -} \ No newline at end of file +} + +function adminMuteUser(userId, muteStatus, buttonId) { + let form = new FormData(); + form.append("formkey", formkey()); + + const xhr = new XMLHttpRequest(); + xhr.open("POST", `/mute_user/${userId}/${muteStatus}`); + xhr.setRequestHeader('xhr', 'xhr'); + + xhr.onload = function() { + let data + try {data = JSON.parse(xhr.response)} + catch(e) {console.log(e)} + if (xhr.status >= 200 && xhr.status < 300 && data && data['message']) { + document.getElementById('toast-post-success-text').innerText = data["message"]; + bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-success')).show(); + } else { + document.getElementById('toast-post-error-text').innerText = "Error, please try again later." + if (data && data["error"]) document.getElementById('toast-post-error-text').innerText = data["error"]; + bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show(); + } + }; + + xhr.send(form); + + document.getElementById('mute-user-' + buttonId).classList.add("d-none"); +} diff --git a/files/classes/mod_logs.py b/files/classes/mod_logs.py index 1f64dc843..9e71d383e 100644 --- a/files/classes/mod_logs.py +++ b/files/classes/mod_logs.py @@ -279,6 +279,16 @@ ACTIONTYPES = { "icon": 'fa-user-crown', "color": 'bg-success' }, + 'mod_mute_user': { + "str": 'muted reports from user {self.target_link}', + "icon": 'fa-file-signature', + "color": 'bg-danger' + }, + 'mod_unmute_user': { + "str": 'unmuted reports from user {self.target_link}', + "icon": 'fa-file-signature', + "color": 'bg-success' + }, 'monthly': { "str": 'distributed monthly marseybux', "icon": 'fa-sack-dollar', diff --git a/files/classes/user.py b/files/classes/user.py index 820b34a20..86595dc80 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -108,6 +108,7 @@ class User(Base): is_banned = Column(Integer, default=0) unban_utc = Column(Integer, default=0) ban_reason = deferred(Column(String)) + is_muted = Column(Boolean, default=False, nullable=False) club_allowed = Column(Boolean) login_nonce = Column(Integer, default=0) reserved = deferred(Column(String)) diff --git a/files/routes/admin.py b/files/routes/admin.py index 8249c1a9c..8bac78ea3 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -1093,6 +1093,37 @@ def unban_user(user_id, v): if "@" in request.referrer: return redirect(user.url) else: return {"message": f"@{user.username} was unbanned!"} +@app.post("/mute_user//") +@limiter.limit("1/second;30/minute;200/hour;1000/day") +@admin_level_required(2) +def mute_user(v, user_id, mute_status): + user = get_account(user_id) + if not user: + abort(400) + + if mute_status != 0 and not user.is_muted: + user.is_muted = True + log_action = 'mod_mute_user' + success_msg = f"@{user.username} was muted!" + elif mute_status == 0 and user.is_muted: + user.is_muted = False + log_action = 'mod_unmute_user' + success_msg = f"@{user.username} was un-muted!" + else: + abort(400) + + ma = ModAction( + kind=log_action, + user_id=v.id, + target_user_id=user.id, + ) + + g.db.add(user) + g.db.add(ma) + if 'redir' in request.values: + return redirect(user.url) + else: + return {"message": success_msg} @app.post("/remove_post/") @limiter.limit("1/second;30/minute;200/hour;1000/day") diff --git a/files/routes/reporting.py b/files/routes/reporting.py index 8d8eb9d39..a3c465aae 100644 --- a/files/routes/reporting.py +++ b/files/routes/reporting.py @@ -20,6 +20,9 @@ def flag_post(pid, v): v.shadowbanned = 'AutoJanny' send_repeatable_notification(CARP_ID, f"reports on {post.permalink}") + if v.is_muted: + return {"error": "You are forbidden from making reports."} + reason = reason[:100] reason = filter_emojis_only(reason) diff --git a/files/routes/static.py b/files/routes/static.py index 89a850f05..da10000a8 100644 --- a/files/routes/static.py +++ b/files/routes/static.py @@ -194,6 +194,9 @@ def submit_contact(v): body = request.values.get("message") if not body: abort(400) + if v.is_muted: + abort(403) + body = f'This message has been sent automatically to all admins via [/contact](/contact)\n\nMessage:\n\n' + body body += process_files() diff --git a/files/routes/users.py b/files/routes/users.py index 1bf374133..7618d8960 100644 --- a/files/routes/users.py +++ b/files/routes/users.py @@ -735,6 +735,8 @@ def messagereply(v): if v.is_suspended_permanently and parent.sentto != 2: return {"error": "You are permabanned and may not reply to messages."} + elif v.is_muted and parent.sentto == 2: + return {"error": "You are forbidden from replying to modmail."} if parent.sentto == 2: user_id = None elif v.id == user_id: user_id = parent.sentto diff --git a/files/templates/comments.html b/files/templates/comments.html index 4342e57ad..885088021 100644 --- a/files/templates/comments.html +++ b/files/templates/comments.html @@ -617,6 +617,10 @@ {% endif %} {% if request.path.startswith('/notifications') and c.level == 1 and c.sentto and not c.parent_submission and c.author_id != AUTOJANNY_ID %} + {% if (v and v.admin_level >= 2) and (c.sentto == 2) and not c.author.is_muted %} + Mute + {% endif %} + Reply