add hole mod logs (#380)
parent
57ce828af4
commit
09cc43060d
|
@ -6031,6 +6031,9 @@ g {
|
|||
.fa-hat-cowboy:before{content:"\f8c0"}
|
||||
.fa-cloud-rainbow:before{content:"\f73e"}
|
||||
.fa-telegram:before{content:"\f2c6"}
|
||||
.fa-css3-alt:before{content:"\f38b"}
|
||||
.fa-landscape:before{content:"\e1b5"}
|
||||
.fa-user-ninja:before{content:"\f504"}
|
||||
|
||||
.pronouns {
|
||||
font-size: 9px;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -26,3 +26,4 @@ from .hats import *
|
|||
from .marsey import *
|
||||
from .transactions import *
|
||||
from .streamers import *
|
||||
from .sub_logs import *
|
||||
|
|
|
@ -410,12 +410,12 @@ ACTIONTYPES = {
|
|||
"color": 'bg-success'
|
||||
},
|
||||
'unpin_comment': {
|
||||
"str": 'un-pinned a {self.target_link}',
|
||||
"str": 'unpinned a {self.target_link}',
|
||||
"icon": 'fa-thumbtack fa-rotate--45',
|
||||
"color": 'bg-muted'
|
||||
},
|
||||
'unpin_post': {
|
||||
"str": 'un-pinned post {self.target_link}',
|
||||
"str": 'unpinned post {self.target_link}',
|
||||
"icon": 'fa-thumbtack fa-rotate--45',
|
||||
"color": 'bg-muted'
|
||||
},
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import relationship
|
||||
from files.__main__ import Base
|
||||
import time
|
||||
from files.helpers.lazy import lazy
|
||||
from files.helpers.const import *
|
||||
from files.helpers.regex import censor_slurs
|
||||
|
||||
class SubAction(Base):
|
||||
__tablename__ = "subactions"
|
||||
id = Column(Integer, primary_key=True)
|
||||
sub = Column(String, ForeignKey("subs.name"))
|
||||
user_id = Column(Integer, ForeignKey("users.id"))
|
||||
kind = Column(String)
|
||||
target_user_id = Column(Integer, ForeignKey("users.id"))
|
||||
target_submission_id = Column(Integer, ForeignKey("submissions.id"))
|
||||
target_comment_id = Column(Integer, ForeignKey("comments.id"))
|
||||
_note=Column(String)
|
||||
created_utc = Column(Integer)
|
||||
|
||||
user = relationship("User", primaryjoin="User.id==SubAction.user_id")
|
||||
target_user = relationship("User", primaryjoin="User.id==SubAction.target_user_id")
|
||||
target_post = relationship("Submission")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<SubAction(id={self.id})>"
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def age_string(self):
|
||||
|
||||
age = int(time.time()) - self.created_utc
|
||||
|
||||
if age < 60:
|
||||
return "just now"
|
||||
elif age < 3600:
|
||||
minutes = int(age / 60)
|
||||
return f"{minutes}m ago"
|
||||
elif age < 86400:
|
||||
hours = int(age / 3600)
|
||||
return f"{hours}hr ago"
|
||||
elif age < 2678400:
|
||||
days = int(age / 86400)
|
||||
return f"{days}d ago"
|
||||
|
||||
now = time.gmtime()
|
||||
ctd = time.gmtime(self.created_utc)
|
||||
|
||||
months = now.tm_mon - ctd.tm_mon + 12 * (now.tm_year - ctd.tm_year)
|
||||
if now.tm_mday < ctd.tm_mday:
|
||||
months -= 1
|
||||
|
||||
if months < 12:
|
||||
return f"{months}mo ago"
|
||||
else:
|
||||
years = int(months / 12)
|
||||
return f"{years}yr ago"
|
||||
|
||||
@property
|
||||
def note(self):
|
||||
|
||||
if self.kind=="ban_user":
|
||||
if self.target_post: return f'for <a href="{self.target_post.permalink}">post</a>'
|
||||
else: return self._note
|
||||
else:
|
||||
return self._note or ""
|
||||
|
||||
@note.setter
|
||||
def note(self, x):
|
||||
self._note=x
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def string(self):
|
||||
|
||||
output = ACTIONTYPES[self.kind]["str"].format(self=self, cc=CC_TITLE)
|
||||
|
||||
if self.note: output += f" <i>({self.note})</i>"
|
||||
|
||||
return output
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def target_link(self):
|
||||
if self.target_user: return f'<a href="{self.target_user.url}">{self.target_user.username}</a>'
|
||||
elif self.target_post:
|
||||
if self.target_post.club: return f'<a href="{self.target_post.permalink}">{CC} ONLY</a>'
|
||||
return censor_slurs(f'<a href="{self.target_post.permalink}">{self.target_post.title_html}</a>', None)
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def icon(self):
|
||||
return ACTIONTYPES[self.kind]['icon']
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def color(self):
|
||||
return ACTIONTYPES[self.kind]['color']
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def permalink(self):
|
||||
return f"{SITE_FULL}/log/{self.id}"
|
||||
|
||||
ACTIONTYPES = {
|
||||
'exile_user': {
|
||||
"str": 'exiled user {self.target_link}',
|
||||
"icon": 'fa-user-slash',
|
||||
"color": 'bg-danger'
|
||||
},
|
||||
'unexile_user': {
|
||||
"str": 'unexile user {self.target_link}',
|
||||
"icon": 'fa-user',
|
||||
"color": 'bg-success'
|
||||
},
|
||||
'make_mod': {
|
||||
"str": 'made {self.target_link} mod',
|
||||
"icon": 'fa-user-crown',
|
||||
"color": 'bg-success'
|
||||
},
|
||||
'remove_mod': {
|
||||
"str": 'removed {self.target_link} as mod',
|
||||
"icon": 'fa-user-crown',
|
||||
"color": 'bg-danger'
|
||||
},
|
||||
'kick_post': {
|
||||
"str": 'kicked post {self.target_link}',
|
||||
"icon": 'fa-feather-alt',
|
||||
"color": 'bg-danger'
|
||||
},
|
||||
'move_chudrama': {
|
||||
"str": 'moved post {self.target_link} to /h/chudrama',
|
||||
"icon": 'fa-feather-alt',
|
||||
"color": 'bg-danger'
|
||||
},
|
||||
'flair_post': {
|
||||
"str": 'set a flair on {self.target_link}',
|
||||
"icon": 'fa-tag',
|
||||
"color": 'bg-primary'
|
||||
},
|
||||
'change_sidebar': {
|
||||
"str": 'changed the sidebar',
|
||||
"icon": 'fa-columns',
|
||||
"color": 'bg-primary'
|
||||
},
|
||||
'change_css': {
|
||||
"str": 'changed the css',
|
||||
"icon": 'fa-css3-alt',
|
||||
"color": 'bg-primary'
|
||||
},
|
||||
'change_banner': {
|
||||
"str": 'changed the banner',
|
||||
"icon": 'fa-landscape',
|
||||
"color": 'bg-primary'
|
||||
},
|
||||
'change_sidebar_image': {
|
||||
"str": 'changed the sidebar image',
|
||||
"icon": 'fa-image',
|
||||
"color": 'bg-primary'
|
||||
},
|
||||
'change_marsey': {
|
||||
"str": 'changed the hole marsey',
|
||||
"icon": 'fa-cat',
|
||||
"color": 'bg-primary'
|
||||
},
|
||||
'pin_post': {
|
||||
"str": 'pinned post {self.target_link}',
|
||||
"icon": 'fa-thumbtack fa-rotate--45',
|
||||
"color": 'bg-success'
|
||||
},
|
||||
'unpin_post': {
|
||||
"str": 'unpinned post {self.target_link}',
|
||||
"icon": 'fa-thumbtack fa-rotate--45',
|
||||
"color": 'bg-muted'
|
||||
},
|
||||
'pin_comment': {
|
||||
"str": 'pinned comment {self.target_link}',
|
||||
"icon": 'fa-thumbtack fa-rotate--45',
|
||||
"color": 'bg-success'
|
||||
},
|
||||
'unpin_comment': {
|
||||
"str": 'unpinned comment {self.target_link}',
|
||||
"icon": 'fa-thumbtack fa-rotate--45',
|
||||
"color": 'bg-muted'
|
||||
},
|
||||
'enable_stealth': {
|
||||
"str": 'enabled stealth mode',
|
||||
"icon": 'fa-user-ninja',
|
||||
"color": 'bg-primary'
|
||||
},
|
||||
'disable_stealth': {
|
||||
"str": 'disabled stealth mode',
|
||||
"icon": 'fa-user-ninja',
|
||||
"color": 'bg-muted'
|
||||
}
|
||||
}
|
|
@ -281,6 +281,7 @@ class User(Base):
|
|||
|
||||
@lazy
|
||||
def mods(self, sub):
|
||||
if self.is_suspended or self.shadowbanned: return False
|
||||
return self.admin_level > 2 or bool(g.db.query(Mod.user_id).filter_by(user_id=self.id, sub=sub).one_or_none())
|
||||
|
||||
@lazy
|
||||
|
|
|
@ -817,45 +817,6 @@ def unpin_comment(cid, v):
|
|||
return {"message": "Comment unpinned!"}
|
||||
|
||||
|
||||
@app.post("/mod_pin/<cid>")
|
||||
@auth_required
|
||||
def mod_pin(cid, v):
|
||||
if not FEATURES['PINS']:
|
||||
abort(403)
|
||||
comment = get_comment(cid, v=v)
|
||||
|
||||
if not comment.stickied:
|
||||
if not (comment.post.sub and v.mods(comment.post.sub)): abort(403)
|
||||
|
||||
comment.stickied = v.username + " (Mod)"
|
||||
|
||||
g.db.add(comment)
|
||||
|
||||
if v.id != comment.author_id:
|
||||
message = f"@{v.username} (Mod) has pinned your [comment]({comment.shortlink})!"
|
||||
send_repeatable_notification(comment.author_id, message)
|
||||
|
||||
return {"message": "Comment pinned!"}
|
||||
|
||||
|
||||
@app.post("/mod_unpin/<cid>")
|
||||
@auth_required
|
||||
def mod_unpin(cid, v):
|
||||
|
||||
comment = get_comment(cid, v=v)
|
||||
|
||||
if comment.stickied:
|
||||
if not (comment.post.sub and v.mods(comment.post.sub)): abort(403)
|
||||
|
||||
comment.stickied = None
|
||||
g.db.add(comment)
|
||||
|
||||
if v.id != comment.author_id:
|
||||
message = f"@{v.username} (Mod) has unpinned your [comment]({comment.shortlink})!"
|
||||
send_repeatable_notification(comment.author_id, message)
|
||||
return {"message": "Comment unpinned!"}
|
||||
|
||||
|
||||
@app.post("/save_comment/<cid>")
|
||||
@limiter.limit("1/second;30/minute;200/hour;1000/day")
|
||||
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
|
||||
|
|
|
@ -22,7 +22,7 @@ def front_all(v, sub=None, subdomain=None):
|
|||
if sub:
|
||||
sub = sub.strip().lower()
|
||||
if sub == 'chudrama' and not (v and v.can_see_chudrama): abort(403)
|
||||
sub = g.db.query(Sub).filter_by(name=sub).one_or_none()
|
||||
sub = g.db.get(Sub, sub)
|
||||
|
||||
if (request.path.startswith('/h/') or request.path.startswith('/s/')) and not sub: abort(404)
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ def publish(pid, v):
|
|||
@app.get("/h/<sub>/submit")
|
||||
@auth_required
|
||||
def submit_get(v, sub=None):
|
||||
if sub: sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
if sub: sub = g.db.get(Sub, sub.strip().lower())
|
||||
|
||||
if request.path.startswith('/h/') and not sub: abort(404)
|
||||
|
||||
|
|
|
@ -40,11 +40,19 @@ def flag_post(pid, v):
|
|||
_note=f'"{post.flair}"'
|
||||
)
|
||||
g.db.add(ma)
|
||||
else:
|
||||
ma = SubAction(
|
||||
kind="flair_post",
|
||||
user_id=v.id,
|
||||
target_submission_id=post.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
elif reason.startswith('/h/') and (v.admin_level >= 2 or v.id == post.author_id or (reason == '/h/chudrama' and v.mods(post.sub))):
|
||||
|
||||
sub_from = post.sub
|
||||
sub_to = reason[3:].strip().lower()
|
||||
sub_to = g.db.query(Sub).filter_by(name=sub_to).one_or_none()
|
||||
sub_to = g.db.get(Sub, sub_to)
|
||||
sub_to = sub_to.name if sub_to else None
|
||||
|
||||
if sub_from == sub_to: {"error": f"Post is already in /h/{sub_to}"}, 400
|
||||
|
@ -61,7 +69,8 @@ def flag_post(pid, v):
|
|||
post.sub = sub_to
|
||||
g.db.add(post)
|
||||
|
||||
if v.admin_level and v.id != post.author_id:
|
||||
if v.id != post.author_id:
|
||||
if v.admin_level:
|
||||
sub_from_str = 'main feed' if sub_from is None else \
|
||||
f'<a href="/h/{sub_from}">/h/{sub_from}</a>'
|
||||
sub_to_str = 'main feed' if sub_to is None else \
|
||||
|
@ -73,6 +82,13 @@ def flag_post(pid, v):
|
|||
_note=f'{sub_from_str} → {sub_to_str}',
|
||||
)
|
||||
g.db.add(ma)
|
||||
else:
|
||||
ma = SubAction(
|
||||
kind='move_chudrama',
|
||||
user_id=v.id,
|
||||
target_submission_id=post.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
if v.id != post.author_id:
|
||||
if v.admin_level >= 3: position = 'Admin'
|
||||
|
|
|
@ -30,6 +30,13 @@ def exile_post(v, pid):
|
|||
|
||||
send_notification(u.id, f"@{v.username} has exiled you from /h/{sub} for [{p.title}]({p.shortlink})")
|
||||
|
||||
ma = SubAction(
|
||||
kind='exile_user',
|
||||
user_id=v.id,
|
||||
target_user_id=u.id,
|
||||
_note=f'for <a href="{p.permalink}">{p.title_html}</a>'
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return {"message": f"@{u.username} has been exiled from /h/{sub} successfully!"}
|
||||
|
||||
|
@ -57,6 +64,13 @@ def exile_comment(v, cid):
|
|||
|
||||
send_notification(u.id, f"@{v.username} has exiled you from /h/{sub} for [{c.permalink}]({c.shortlink})")
|
||||
|
||||
ma = SubAction(
|
||||
kind='exile_user',
|
||||
user_id=v.id,
|
||||
target_user_id=u.id,
|
||||
_note=f'for <a href="/comment/{c.id}?context=8#context">comment</a>'
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return {"message": f"@{u.username} has been exiled from /h/{sub} successfully!"}
|
||||
|
||||
|
@ -74,7 +88,12 @@ def unexile(v, sub, uid):
|
|||
|
||||
send_notification(u.id, f"@{v.username} has revoked your exile from /h/{sub}")
|
||||
|
||||
|
||||
ma = SubAction(
|
||||
kind='unexile_user',
|
||||
user_id=v.id,
|
||||
target_user_id=u.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
if request.headers.get("Authorization") or request.headers.get("xhr"):
|
||||
return {"message": f"@{u.username} has been unexiled from /h/{sub} successfully!"}
|
||||
|
@ -84,14 +103,10 @@ def unexile(v, sub, uid):
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@app.post("/h/<sub>/block")
|
||||
@auth_required
|
||||
def block_sub(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
sub = sub.name
|
||||
|
||||
|
@ -108,7 +123,7 @@ def block_sub(v, sub):
|
|||
@app.post("/h/<sub>/unblock")
|
||||
@auth_required
|
||||
def unblock_sub(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
sub = sub.name
|
||||
|
||||
|
@ -124,7 +139,7 @@ def unblock_sub(v, sub):
|
|||
@app.post("/h/<sub>/subscribe")
|
||||
@auth_required
|
||||
def subscribe_sub(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
sub = sub.name
|
||||
|
||||
|
@ -140,7 +155,7 @@ def subscribe_sub(v, sub):
|
|||
@app.post("/h/<sub>/unsubscribe")
|
||||
@auth_required
|
||||
def unsubscribe_sub(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
sub = sub.name
|
||||
|
||||
|
@ -155,7 +170,7 @@ def unsubscribe_sub(v, sub):
|
|||
@app.post("/h/<sub>/follow")
|
||||
@auth_required
|
||||
def follow_sub(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
existing = g.db.query(SubSubscription).filter_by(user_id=v.id, sub=sub.name).one_or_none()
|
||||
|
@ -170,7 +185,7 @@ def follow_sub(v, sub):
|
|||
@app.post("/h/<sub>/unfollow")
|
||||
@auth_required
|
||||
def unfollow_sub(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
subscription = g.db.query(SubSubscription).filter_by(user_id=v.id, sub=sub.name).one_or_none()
|
||||
|
@ -184,7 +199,7 @@ def unfollow_sub(v, sub):
|
|||
@app.get("/h/<sub>/mods")
|
||||
@auth_required
|
||||
def mods(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
users = g.db.query(User, Mod).join(Mod).filter_by(sub=sub.name).order_by(Mod.created_utc).all()
|
||||
|
@ -195,7 +210,7 @@ def mods(v, sub):
|
|||
@app.get("/h/<sub>/exilees")
|
||||
@auth_required
|
||||
def sub_exilees(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
users = g.db.query(User, Exile).join(Exile, Exile.user_id==User.id) \
|
||||
|
@ -208,7 +223,7 @@ def sub_exilees(v, sub):
|
|||
@app.get("/h/<sub>/blockers")
|
||||
@auth_required
|
||||
def sub_blockers(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
users = g.db.query(User).join(SubBlock) \
|
||||
|
@ -222,7 +237,7 @@ def sub_blockers(v, sub):
|
|||
@app.get("/h/<sub>/followers")
|
||||
@auth_required
|
||||
def sub_followers(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
users = g.db.query(User).join(SubSubscription) \
|
||||
|
@ -240,7 +255,7 @@ def sub_followers(v, sub):
|
|||
def add_mod(v, sub):
|
||||
if SITE_NAME == 'WPD': abort(403)
|
||||
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
sub = sub.name
|
||||
|
||||
|
@ -264,6 +279,12 @@ def add_mod(v, sub):
|
|||
if v.id != user.id:
|
||||
send_repeatable_notification(user.id, f"@{v.username} has added you as a mod to /h/{sub}")
|
||||
|
||||
ma = SubAction(
|
||||
kind='make_mod',
|
||||
user_id=v.id,
|
||||
target_user_id=user.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{sub}/mods')
|
||||
|
||||
|
@ -271,7 +292,7 @@ def add_mod(v, sub):
|
|||
@app.post("/h/<sub>/remove_mod")
|
||||
@is_not_permabanned
|
||||
def remove_mod(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
sub = sub.name
|
||||
|
||||
|
@ -298,6 +319,12 @@ def remove_mod(v, sub):
|
|||
if v.id != user.id:
|
||||
send_repeatable_notification(user.id, f"@{v.username} has removed you as a mod from /h/{sub}")
|
||||
|
||||
ma = SubAction(
|
||||
kind='remove_mod',
|
||||
user_id=v.id,
|
||||
target_user_id=user.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{sub}/mods')
|
||||
|
||||
|
@ -322,7 +349,7 @@ def create_sub2(v):
|
|||
if not valid_sub_regex.fullmatch(name):
|
||||
return render_template("sub/create_hole.html", v=v, cost=HOLE_COST, error=f"{HOLE_NAME.capitalize()} name not allowed."), 400
|
||||
|
||||
sub = g.db.query(Sub).filter_by(name=name).one_or_none()
|
||||
sub = g.db.get(Sub, name)
|
||||
if not sub:
|
||||
if v.coins < HOLE_COST:
|
||||
return render_template("sub/create_hole.html", v=v, cost=HOLE_COST, error="You don't have enough coins!"), 403
|
||||
|
@ -366,6 +393,13 @@ def kick(v, pid):
|
|||
_note=f'{old_str} → main feed',
|
||||
)
|
||||
g.db.add(ma)
|
||||
else:
|
||||
ma = SubAction(
|
||||
kind='kick_post',
|
||||
user_id=v.id,
|
||||
target_submission_id=post.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
if v.id != post.author_id:
|
||||
if v.admin_level >= 3: position = 'Admin'
|
||||
|
@ -382,7 +416,7 @@ def kick(v, pid):
|
|||
@app.get('/h/<sub>/settings')
|
||||
@is_not_permabanned
|
||||
def sub_settings(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
if not v.mods(sub.name): abort(403)
|
||||
|
@ -395,7 +429,7 @@ def sub_settings(v, sub):
|
|||
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
|
||||
@is_not_permabanned
|
||||
def post_sub_sidebar(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
if not v.mods(sub.name): abort(403)
|
||||
|
@ -406,6 +440,11 @@ def post_sub_sidebar(v, sub):
|
|||
|
||||
g.db.add(sub)
|
||||
|
||||
ma = SubAction(
|
||||
kind='change_sidebar',
|
||||
user_id=v.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{sub}/settings')
|
||||
|
||||
|
@ -415,7 +454,7 @@ def post_sub_sidebar(v, sub):
|
|||
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}')
|
||||
@is_not_permabanned
|
||||
def post_sub_css(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
css = request.values.get('css', '').strip()
|
||||
|
||||
if not sub: abort(404)
|
||||
|
@ -432,6 +471,12 @@ def post_sub_css(v, sub):
|
|||
sub.css = css
|
||||
g.db.add(sub)
|
||||
|
||||
ma = SubAction(
|
||||
kind='change_css',
|
||||
user_id=v.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{sub}/settings')
|
||||
|
||||
|
||||
|
@ -451,7 +496,7 @@ def get_sub_css(sub):
|
|||
def sub_banner(v, sub):
|
||||
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
|
||||
|
||||
sub = g.db.query(Sub).filter_by(name=sub.lower().strip()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.lower().strip())
|
||||
if not sub: abort(404)
|
||||
|
||||
if not v.mods(sub.name): abort(403)
|
||||
|
@ -469,6 +514,12 @@ def sub_banner(v, sub):
|
|||
sub.bannerurl = bannerurl
|
||||
g.db.add(sub)
|
||||
|
||||
ma = SubAction(
|
||||
kind='change_banner',
|
||||
user_id=v.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{sub}/settings')
|
||||
|
||||
@app.post("/h/<sub>/sidebar_image")
|
||||
|
@ -478,7 +529,7 @@ def sub_banner(v, sub):
|
|||
def sub_sidebar(v, sub):
|
||||
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
|
||||
|
||||
sub = g.db.query(Sub).filter_by(name=sub.lower().strip()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.lower().strip())
|
||||
if not sub: abort(404)
|
||||
|
||||
if not v.mods(sub.name): abort(403)
|
||||
|
@ -495,6 +546,12 @@ def sub_sidebar(v, sub):
|
|||
sub.sidebarurl = sidebarurl
|
||||
g.db.add(sub)
|
||||
|
||||
ma = SubAction(
|
||||
kind='change_sidebar_image',
|
||||
user_id=v.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{sub}/settings')
|
||||
|
||||
@app.post("/h/<sub>/marsey_image")
|
||||
|
@ -504,7 +561,7 @@ def sub_sidebar(v, sub):
|
|||
def sub_marsey(v, sub):
|
||||
if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403
|
||||
|
||||
sub = g.db.query(Sub).filter_by(name=sub.lower().strip()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.lower().strip())
|
||||
if not sub: abort(404)
|
||||
|
||||
if not v.mods(sub.name): abort(403)
|
||||
|
@ -521,6 +578,12 @@ def sub_marsey(v, sub):
|
|||
sub.marseyurl = marseyurl
|
||||
g.db.add(sub)
|
||||
|
||||
ma = SubAction(
|
||||
kind='change_marsey',
|
||||
user_id=v.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{sub}/settings')
|
||||
|
||||
@app.get("/holes")
|
||||
|
@ -545,6 +608,13 @@ def hole_pin(v, pid):
|
|||
message = f"@{v.username} (Mod) has pinned [{p.title}]({p.shortlink}) in /h/{p.sub}"
|
||||
send_repeatable_notification(p.author_id, message)
|
||||
|
||||
ma = SubAction(
|
||||
kind='pin_post',
|
||||
user_id=v.id,
|
||||
target_submission_id=p.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return {"message": f"Post pinned to /h/{p.sub} successfully!"}
|
||||
|
||||
@app.post("/hole_unpin/<pid>")
|
||||
|
@ -563,13 +633,20 @@ def hole_unpin(v, pid):
|
|||
message = f"@{v.username} (Mod) has unpinned [{p.title}]({p.shortlink}) in /h/{p.sub}"
|
||||
send_repeatable_notification(p.author_id, message)
|
||||
|
||||
ma = SubAction(
|
||||
kind='unpin_post',
|
||||
user_id=v.id,
|
||||
target_submission_id=p.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return {"message": f"Post unpinned from /h/{p.sub} successfully!"}
|
||||
|
||||
|
||||
@app.post('/h/<sub>/stealth')
|
||||
@is_not_permabanned
|
||||
def sub_stealth(v, sub):
|
||||
sub = g.db.query(Sub).filter_by(name=sub.strip().lower()).one_or_none()
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
|
||||
if sub.name == 'braincels': abort(403)
|
||||
|
@ -581,6 +658,130 @@ def sub_stealth(v, sub):
|
|||
cache.delete_memoized(frontlist)
|
||||
|
||||
if sub.stealth:
|
||||
ma = SubAction(
|
||||
kind='enable_stealth',
|
||||
user_id=v.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
return {"message": f"Stealth mode has been enabled for /h/{sub} successfully!"}
|
||||
else:
|
||||
ma = SubAction(
|
||||
kind='disable_stealth',
|
||||
user_id=v.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
return {"message": f"Stealth mode has been disabled for /h/{sub} successfully!"}
|
||||
|
||||
|
||||
@app.post("/mod_pin/<cid>")
|
||||
@is_not_permabanned
|
||||
def mod_pin(cid, v):
|
||||
if not FEATURES['PINS']:
|
||||
abort(403)
|
||||
comment = get_comment(cid, v=v)
|
||||
|
||||
if not comment.stickied:
|
||||
if not (comment.post.sub and v.mods(comment.post.sub)): abort(403)
|
||||
|
||||
comment.stickied = v.username + " (Mod)"
|
||||
|
||||
g.db.add(comment)
|
||||
|
||||
ma = SubAction(
|
||||
kind="pin_comment",
|
||||
user_id=v.id,
|
||||
target_comment_id=comment.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
if v.id != comment.author_id:
|
||||
message = f"@{v.username} (Mod) has pinned your [comment]({comment.shortlink})!"
|
||||
send_repeatable_notification(comment.author_id, message)
|
||||
|
||||
return {"message": "Comment pinned!"}
|
||||
|
||||
@app.post("/mod_unpin/<cid>")
|
||||
@is_not_permabanned
|
||||
def mod_unpin(cid, v):
|
||||
|
||||
comment = get_comment(cid, v=v)
|
||||
|
||||
if comment.stickied:
|
||||
if not (comment.post.sub and v.mods(comment.post.sub)): abort(403)
|
||||
|
||||
comment.stickied = None
|
||||
g.db.add(comment)
|
||||
|
||||
ma = SubAction(
|
||||
kind="unpin_comment",
|
||||
user_id=v.id,
|
||||
target_comment_id=comment.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
if v.id != comment.author_id:
|
||||
message = f"@{v.username} (Mod) has unpinned your [comment]({comment.shortlink})!"
|
||||
send_repeatable_notification(comment.author_id, message)
|
||||
return {"message": "Comment unpinned!"}
|
||||
|
||||
|
||||
@app.get("/h/<sub>/log")
|
||||
@app.get("/h/<sub>/modlog")
|
||||
@auth_required
|
||||
def hole_log(v, sub):
|
||||
sub = g.db.get(Sub, sub.strip().lower())
|
||||
if not sub: abort(404)
|
||||
sub = sub.name
|
||||
|
||||
try: page = max(int(request.values.get("page", 1)), 1)
|
||||
except: page = 1
|
||||
|
||||
mod = request.values.get("mod")
|
||||
if mod: mod_id = get_id(mod)
|
||||
else: mod_id = 0
|
||||
|
||||
kind = request.values.get("kind")
|
||||
|
||||
types = ACTIONTYPES
|
||||
|
||||
if kind and kind not in types:
|
||||
kind = None
|
||||
actions = []
|
||||
else:
|
||||
actions = g.db.query(SubAction).filter_by(sub=sub)
|
||||
|
||||
if mod_id:
|
||||
actions = actions.filter_by(user_id=mod_id)
|
||||
kinds = set([x.kind for x in actions])
|
||||
types2 = {}
|
||||
for k,val in types.items():
|
||||
if k in kinds: types2[k] = val
|
||||
types = types2
|
||||
if kind: actions = actions.filter_by(kind=kind)
|
||||
|
||||
actions = actions.order_by(SubAction.id.desc()).offset(25*(page-1)).limit(26).all()
|
||||
|
||||
next_exists=len(actions)>25
|
||||
actions=actions[:25]
|
||||
mods = [x[0] for x in g.db.query(Mod.user_id).filter_by(sub=sub).all()]
|
||||
mods = [x[0] for x in g.db.query(User.username).filter(User.id.in_(mods)).order_by(User.username).all()]
|
||||
|
||||
return render_template("log.html", v=v, mods=mods, types=types, mod=mod, type=kind, actions=actions, next_exists=next_exists, page=page)
|
||||
|
||||
@app.get("/h/<sub>/log/<id>")
|
||||
@auth_required
|
||||
def hole_log_item(id, v, sub):
|
||||
|
||||
try: id = int(id)
|
||||
except: abort(404)
|
||||
|
||||
action=g.db.get(SubAction, id)
|
||||
|
||||
if not action: abort(404)
|
||||
|
||||
mods = [x[0] for x in g.db.query(Mod.user_id).filter_by(sub=sub).all()]
|
||||
mods = [x[0] for x in g.db.query(User.username).filter(User.id.in_(mods)).order_by(User.username).all()]
|
||||
|
||||
types = ACTIONTYPES
|
||||
|
||||
return render_template("log.html", v=v, actions=[action], next_exists=False, page=1, action=action, mods=mods, types=types)
|
||||
|
|
|
@ -127,7 +127,7 @@
|
|||
|
||||
|
||||
{% else %}
|
||||
<div class="px-3">There's nothing here right now.</div>
|
||||
<div class="p-3">There's nothing here right now.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
{% if sub.sidebar_html %}
|
||||
<div class="mb-4">{{sub.sidebar_html|safe}}</div>
|
||||
{% endif %}
|
||||
<a class="btn btn-primary btn-block" href="/h/{{sub}}/log">{{HOLE_NAME|upper}} LOG</a>
|
||||
{% if v and v.mods(sub.name) %}
|
||||
<a class="btn btn-primary btn-block" href="/h/{{sub}}/settings">{{HOLE_NAME|upper}} SETTINGS</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
{% if v.can_create_hole -%}
|
||||
<a class="btn btn-primary btn-block mb-3" href="/create_hole">CREATE {{HOLE_NAME|upper}}</a>
|
||||
{%- endif %}
|
||||
<a class="btn btn-primary btn-block mb-3" href="/h/{{sub}}/log">{{HOLE_NAME|upper}} LOG</a>
|
||||
{% if v.mods(sub.name) %}
|
||||
<a class="btn btn-primary btn-block mb-3" href="/h/{{sub}}/settings">{{HOLE_NAME|upper}} SETTINGS</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
CREATE TABLE public.subactions (
|
||||
id integer PRIMARY KEY,
|
||||
sub character varying(25) NOT NULL,
|
||||
user_id integer,
|
||||
target_user_id integer,
|
||||
target_submission_id integer,
|
||||
target_comment_id integer,
|
||||
created_utc integer NOT NULL,
|
||||
kind character varying(32) DEFAULT NULL::character varying,
|
||||
_note character varying(500) DEFAULT NULL::character varying
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.subactions_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE public.subactions_id_seq OWNED BY public.subactions.id;
|
||||
|
||||
ALTER TABLE ONLY public.subactions ALTER COLUMN id SET DEFAULT nextval('public.subactions_id_seq'::regclass);
|
||||
|
||||
CREATE INDEX fki_subactions_user_fkey ON public.subactions USING btree (target_user_id);
|
||||
|
||||
CREATE INDEX modaction_action_idx ON public.subactions USING btree (kind);
|
||||
|
||||
CREATE INDEX modaction_pid_idx ON public.subactions USING btree (target_submission_id);
|
||||
|
||||
ALTER TABLE ONLY public.subactions
|
||||
ADD CONSTRAINT subactions_submission_fkey FOREIGN KEY (target_submission_id) REFERENCES public.submissions(id);
|
||||
|
||||
CREATE INDEX modaction_cid_idx ON public.subactions USING btree (target_comment_id);
|
||||
|
||||
ALTER TABLE ONLY public.subactions
|
||||
ADD CONSTRAINT subactions_comment_fkey FOREIGN KEY (target_comment_id) REFERENCES public.comments(id);
|
||||
|
||||
ALTER TABLE ONLY public.subactions
|
||||
ADD CONSTRAINT subactions_user_fkey FOREIGN KEY (target_user_id) REFERENCES public.users(id);
|
||||
|
||||
ALTER TABLE ONLY public.subactions
|
||||
ADD CONSTRAINT subactions_sub_fkey FOREIGN KEY (sub) REFERENCES public.subs(name);
|
Loading…
Reference in New Issue