Aevann 2023-08-08 21:08:29 +03:00
parent 8b1f7baa74
commit 6e02ea8abb
7 changed files with 89 additions and 8 deletions

View File

@ -7321,6 +7321,10 @@ input[type=number] {
height: 50px; height: 50px;
} }
.group-mod {
height: 40px;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.award-tab { .award-tab {
font-size: 10px !important; font-size: 10px !important;
@ -7343,6 +7347,10 @@ input[type=number] {
.group-owner { .group-owner {
height: 30px; height: 30px;
} }
.group-mod {
height: 23px;
}
} }
#input-message, #input-message-mobile { #input-message, #input-message-mobile {

View File

@ -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(`kick-${uid}`).classList.remove('d-none')
document.getElementById(`time-${uid}`).innerHTML = formatDate(new Date()); document.getElementById(`time-${uid}`).innerHTML = formatDate(new Date());
document.getElementById(`counter-${uid}`).innerHTML = parseInt(members_tbody.lastElementChild.firstElementChild.innerHTML) + 1 document.getElementById(`counter-${uid}`).innerHTML = parseInt(members_tbody.lastElementChild.firstElementChild.innerHTML) + 1

View File

@ -2,7 +2,7 @@ import time
from sqlalchemy import Column, ForeignKey from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.types import Integer, String from sqlalchemy.types import Integer, String, Boolean
from files.classes import Base from files.classes import Base
@ -12,6 +12,7 @@ class GroupMembership(Base):
group_name = Column(String, ForeignKey("groups.name"), primary_key=True) group_name = Column(String, ForeignKey("groups.name"), primary_key=True)
created_utc = Column(Integer) created_utc = Column(Integer)
approved_utc = Column(Integer) approved_utc = Column(Integer)
is_mod = Column(Boolean, default=False)
user = relationship("User", uselist=False) user = relationship("User", uselist=False)

View File

@ -393,6 +393,13 @@ class User(Base):
except: except:
return bool(g.db.query(Mod.user_id).filter_by(user_id=self.id, sub=sub).one_or_none()) 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 @lazy
def exiler_username(self, sub): 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() exile = g.db.query(Exile).options(load_only(Exile.exiler_id)).filter_by(user_id=self.id, sub=sub).one_or_none()

View File

@ -152,8 +152,8 @@ def group_approve(v, group_name, user_id):
group = g.db.get(Group, group_name) group = g.db.get(Group, group_name)
if not group: abort(404) if not group: abort(404)
if v.id != group.owner.id and v.admin_level < PERMS['MODS_EVERY_GROUP']: if not v.mods_group(group):
abort(403, f"Only the group owner (@{group.owner.username}) can approve applications!") 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() application = g.db.query(GroupMembership).filter_by(user_id=user_id, group_name=group.name).one_or_none()
if not application: if not application:
@ -178,8 +178,8 @@ def group_reject(v, group_name, user_id):
group = g.db.get(Group, group_name) group = g.db.get(Group, group_name)
if not group: abort(404) if not group: abort(404)
if v.id != group.owner.id and v.admin_level < PERMS['MODS_EVERY_GROUP']: if not v.mods_group(group):
abort(403, f"Only the group owner (@{group.owner.username}) can reject memberships!") 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() membership = g.db.query(GroupMembership).filter_by(user_id=user_id, group_name=group.name).one_or_none()
if not membership: if not membership:
@ -204,3 +204,55 @@ def group_reject(v, group_name, user_id):
g.db.delete(group) g.db.delete(group)
return {"message": msg} return {"message": msg}
@app.post("/!<group_name>/<user_id>/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("/!<group_name>/<user_id>/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!'}

View File

@ -31,7 +31,7 @@
<th>Applied on</th> <th>Applied on</th>
{% endif %} {% endif %}
{% if v.id == group.owner.id or v.admin_level >= PERMS['MODS_EVERY_GROUP'] %} {% if v.mods_group(group) %}
<th class="disable-sort-click"></th> <th class="disable-sort-click"></th>
{% endif %} {% endif %}
</tr> </tr>
@ -47,6 +47,8 @@
{% endwith %} {% endwith %}
{% if owner %} {% if owner %}
<img class="mx-2 group-owner" data-bs-toggle="tooltip" alt="Owner" title="Owner" src="{{SITE_FULL_IMAGES}}/e/marseykingretard.webp"> <img class="mx-2 group-owner" data-bs-toggle="tooltip" alt="Owner" title="Owner" src="{{SITE_FULL_IMAGES}}/e/marseykingretard.webp">
{% elif membership.is_mod %}
<img class="mx-2 group-mod" data-bs-toggle="tooltip" alt="Mod" title="Mod" src="{{SITE_FULL_IMAGES}}/e/marseyjanny.webp">
{% endif %} {% endif %}
</td> </td>
@ -56,8 +58,15 @@
<td id="time-{{membership.user_id}}" data-time="{{membership.created_utc}}"></td> <td id="time-{{membership.user_id}}" data-time="{{membership.created_utc}}"></td>
{% endif %} {% endif %}
{% if v.id == group.owner.id or v.admin_level >= PERMS['MODS_EVERY_GROUP'] %} {% if v.mods_group(group) %}
<td> <td>
{% if v.id == group.owner.id and v.id != membership.user_id %}
<div id="mod-{{membership.user_id}}" class="mb-2 {% if name == 'applications' %}d-none{% endif %}">
<button id="add-mod-{{membership.user_id}}" type="button" class="btn btn-success btn-block {% if membership.is_mod %}d-none{% endif %}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/!{{group.name}}/{{membership.user_id}}/add_mod','add-mod-{{membership.user_id}}','remove-mod-{{membership.user_id}}','d-none')">Add as Mod</button>
<button id="remove-mod-{{membership.user_id}}" type="button" class="btn btn-danger btn-block {% if not membership.is_mod %}d-none{% endif %}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/!{{group.name}}/{{membership.user_id}}/remove_mod','add-mod-{{membership.user_id}}','remove-mod-{{membership.user_id}}','d-none')">Remove as Mod</button>
</div>
{% endif %}
<div id="kick-{{membership.user_id}}" {% if name == 'applications' %}class="d-none"{% endif %}> <div id="kick-{{membership.user_id}}" {% if name == 'applications' %}class="d-none"{% endif %}>
<button type="button" class="btn btn-primary btn-block" data-nonce="{{g.nonce}}" data-onclick="reject_membership(this,'{{group}}','{{membership.user_id}}')">{% if v.id == membership.user_id %}Leave{% else %}Kick{% endif %}</button> <button type="button" class="btn btn-primary btn-block" data-nonce="{{g.nonce}}" data-onclick="reject_membership(this,'{{group}}','{{membership.user_id}}')">{% if v.id == membership.user_id %}Leave{% else %}Kick{% endif %}</button>
</div> </div>
@ -75,7 +84,7 @@
</div> </div>
{% endmacro %} {% 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(applications, 'applications')}}
{{process_memberships(members, 'members')}} {{process_memberships(members, 'members')}}
<script defer src="{{'js/group_members_owner.js' | asset}}"></script> <script defer src="{{'js/group_members_owner.js' | asset}}"></script>

View File

@ -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;