From 6e02ea8abbab20efc0888c7304a0931d217bf697 Mon Sep 17 00:00:00 2001 From: Aevann Date: Tue, 8 Aug 2023 21:08:29 +0300 Subject: [PATCH] add this https://rdrama.net/h/countryclub/post/79285/-/4744088#context --- files/assets/css/main.css | 8 +++ files/assets/js/group_members_owner.js | 2 + files/classes/group_membership.py | 3 +- files/classes/user.py | 7 +++ files/routes/groups.py | 60 +++++++++++++++++++++-- files/templates/group_memberships.html | 15 ++++-- migrations/20230808-add-group-modssql.sql | 2 + 7 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 migrations/20230808-add-group-modssql.sql diff --git a/files/assets/css/main.css b/files/assets/css/main.css index 6329317c3..8fcbd3f2a 100644 --- a/files/assets/css/main.css +++ b/files/assets/css/main.css @@ -7321,6 +7321,10 @@ input[type=number] { height: 50px; } +.group-mod { + height: 40px; +} + @media (max-width: 768px) { .award-tab { font-size: 10px !important; @@ -7343,6 +7347,10 @@ input[type=number] { .group-owner { height: 30px; } + + .group-mod { + height: 23px; + } } #input-message, #input-message-mobile { diff --git a/files/assets/js/group_members_owner.js b/files/assets/js/group_members_owner.js index a6da77158..6b0ee4f73 100644 --- a/files/assets/js/group_members_owner.js +++ b/files/assets/js/group_members_owner.js @@ -6,6 +6,8 @@ function approve_membership(t, group, uid) { { }, () => { + const mod = document.getElementById(`mod-${uid}`) + if (mod) mod.classList.remove('d-none') document.getElementById(`kick-${uid}`).classList.remove('d-none') document.getElementById(`time-${uid}`).innerHTML = formatDate(new Date()); document.getElementById(`counter-${uid}`).innerHTML = parseInt(members_tbody.lastElementChild.firstElementChild.innerHTML) + 1 diff --git a/files/classes/group_membership.py b/files/classes/group_membership.py index 177b6dec0..890d3c535 100644 --- a/files/classes/group_membership.py +++ b/files/classes/group_membership.py @@ -2,7 +2,7 @@ import time from sqlalchemy import Column, ForeignKey from sqlalchemy.orm import relationship -from sqlalchemy.types import Integer, String +from sqlalchemy.types import Integer, String, Boolean from files.classes import Base @@ -12,6 +12,7 @@ class GroupMembership(Base): group_name = Column(String, ForeignKey("groups.name"), primary_key=True) created_utc = Column(Integer) approved_utc = Column(Integer) + is_mod = Column(Boolean, default=False) user = relationship("User", uselist=False) diff --git a/files/classes/user.py b/files/classes/user.py index 3b42bb252..b22d02878 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -393,6 +393,13 @@ class User(Base): except: return bool(g.db.query(Mod.user_id).filter_by(user_id=self.id, sub=sub).one_or_none()) + @lazy + def mods_group(self, group): + if self.is_permabanned or self.shadowbanned: return False + if self.id == group.owner.id: return True + if self.admin_level >= PERMS['MODS_EVERY_GROUP']: return True + return bool(g.db.query(GroupMembership.user_id).filter_by(user_id=self.id, group_name=group.name, is_mod=True).one_or_none()) + @lazy def exiler_username(self, sub): exile = g.db.query(Exile).options(load_only(Exile.exiler_id)).filter_by(user_id=self.id, sub=sub).one_or_none() diff --git a/files/routes/groups.py b/files/routes/groups.py index 4d67ba75f..682704759 100644 --- a/files/routes/groups.py +++ b/files/routes/groups.py @@ -152,8 +152,8 @@ def group_approve(v, group_name, user_id): group = g.db.get(Group, group_name) if not group: abort(404) - if v.id != group.owner.id and v.admin_level < PERMS['MODS_EVERY_GROUP']: - abort(403, f"Only the group owner (@{group.owner.username}) can approve applications!") + if not v.mods_group(group): + abort(403, f"Only the group owner and its mods can approve applications!") application = g.db.query(GroupMembership).filter_by(user_id=user_id, group_name=group.name).one_or_none() if not application: @@ -178,8 +178,8 @@ def group_reject(v, group_name, user_id): group = g.db.get(Group, group_name) if not group: abort(404) - if v.id != group.owner.id and v.admin_level < PERMS['MODS_EVERY_GROUP']: - abort(403, f"Only the group owner (@{group.owner.username}) can reject memberships!") + if not v.mods_group(group): + abort(403, f"Only the group owner and its mods can reject memberships!") membership = g.db.query(GroupMembership).filter_by(user_id=user_id, group_name=group.name).one_or_none() if not membership: @@ -204,3 +204,55 @@ def group_reject(v, group_name, user_id): g.db.delete(group) return {"message": msg} + +@app.post("/!//add_mod") +@limiter.limit('1/second', scope=rpath) +@limiter.limit('1/second', scope=rpath, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400) +@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID) +@auth_required +def group_add_mod(v, group_name, user_id): + group_name = group_name.strip().lower() + + group = g.db.get(Group, group_name) + if not group: abort(404) + + if v.id != group.owner.id: + abort(403, f"Only the group owner (@{group.owner.username}) can add mods!") + + membership = g.db.query(GroupMembership).filter_by(user_id=user_id, group_name=group.name).one_or_none() + if not membership: + abort(404, "The user is not a member of the group!") + + membership.is_mod = True + g.db.add(membership) + + send_repeatable_notification(membership.user_id, f"@{v.username} (!{group}'s owner) has added you as a mod!") + + return {"message": f'You have added @{membership.user.username} as a mod successfully!'} + +@app.post("/!//remove_mod") +@limiter.limit('1/second', scope=rpath) +@limiter.limit('1/second', scope=rpath, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400) +@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID) +@auth_required +def group_remove_mod(v, group_name, user_id): + group_name = group_name.strip().lower() + + group = g.db.get(Group, group_name) + if not group: abort(404) + + if v.id != group.owner.id: + abort(403, f"Only the group owner (@{group.owner.username}) can remove mods!") + + membership = g.db.query(GroupMembership).filter_by(user_id=user_id, group_name=group.name).one_or_none() + if not membership: + abort(404, "The user is not a member of the group!") + + membership.is_mod = False + g.db.add(membership) + + send_repeatable_notification(membership.user_id, f"@{v.username} (!{group}'s owner) has removed you as a mod!") + + return {"message": f'You have removed @{membership.user.username} as a mod successfully!'} diff --git a/files/templates/group_memberships.html b/files/templates/group_memberships.html index fa8d1603d..cfa9c0fc7 100644 --- a/files/templates/group_memberships.html +++ b/files/templates/group_memberships.html @@ -31,7 +31,7 @@ Applied on {% endif %} - {% if v.id == group.owner.id or v.admin_level >= PERMS['MODS_EVERY_GROUP'] %} + {% if v.mods_group(group) %} {% endif %} @@ -47,6 +47,8 @@ {% endwith %} {% if owner %} Owner + {% elif membership.is_mod %} + Mod {% endif %} @@ -56,8 +58,15 @@ {% endif %} - {% if v.id == group.owner.id or v.admin_level >= PERMS['MODS_EVERY_GROUP'] %} + {% if v.mods_group(group) %} + {% if v.id == group.owner.id and v.id != membership.user_id %} +
+ + +
+ {% endif %} +
@@ -75,7 +84,7 @@ {% endmacro %} -{% if v.id == group.owner.id or v.admin_level >= PERMS['MODS_EVERY_GROUP'] %} +{% if v.mods_group(group) %} {{process_memberships(applications, 'applications')}} {{process_memberships(members, 'members')}} diff --git a/migrations/20230808-add-group-modssql.sql b/migrations/20230808-add-group-modssql.sql new file mode 100644 index 000000000..dcbb94319 --- /dev/null +++ b/migrations/20230808-add-group-modssql.sql @@ -0,0 +1,2 @@ +alter table group_memberships add column is_mod bool not null default false; +alter table group_memberships alter column is_mod drop default;