remotes/1693045480750635534/spooky-22
Aevann1 2021-07-25 23:59:20 +02:00
parent 1a4cee40ff
commit fac1bc4a38
67 changed files with 3375 additions and 2604 deletions

View File

@ -24,9 +24,6 @@ from redis import ConnectionPool
from werkzeug.middleware.proxy_fix import ProxyFix
_version = "2.37.4"
app = Flask(__name__,
template_folder='./templates',
static_folder='./static'
@ -56,7 +53,7 @@ app.config["SERVER_NAME"] = environ.get("domain", environ.get("SERVER_NAME", "")
app.config["SHORT_DOMAIN"]=environ.get("SHORT_DOMAIN","").strip()
app.config["SESSION_COOKIE_NAME"] = "session_drama"
app.config["VERSION"] = _version
app.config["VERSION"] = "1.0.0"
app.config['MAX_CONTENT_LENGTH'] = 64 * 1024 * 1024
app.config["SESSION_COOKIE_SECURE"] = bool(int(environ.get("FORCE_HTTPS", 1)))
app.config["SESSION_COOKIE_SAMESITE"] = "Lax"
@ -69,7 +66,7 @@ app.config["DISABLE_SIGNUPS"]=int(environ.get("DISABLE_SIGNUPS",0))
app.jinja_env.cache = {}
app.config["UserAgent"] = f"Content Aquisition for Pink message board v{_version}."
app.config["UserAgent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
if "localhost" in app.config["SERVER_NAME"]:
app.config["CACHE_TYPE"] = "null"

View File

@ -1,7 +1,5 @@
from .alts import *
from .badges import *
from .boards import *
from .board_relationships import *
from .clients import *
from .comment import *
from .domains import Domain

View File

@ -1,198 +0,0 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from drama.__main__ import Base, cache
from .mix_ins import *
import time
class ModRelationship(Base, Age_times):
__tablename__ = "mods"
id = Column(BigInteger, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
board_id = Column(Integer, ForeignKey("boards.id"))
created_utc = Column(Integer, default=0)
accepted = Column(Boolean, default=False)
invite_rescinded = Column(Boolean, default=False)
perm_content = Column(Boolean, default=False)
perm_appearance = Column(Boolean, default=False)
perm_config = Column(Boolean, default=False)
perm_access = Column(Boolean, default=False)
perm_full = Column(Boolean, default=False)
#permRules = Column(Boolean, default=False)
#permTitles = Column(Boolean, default=False)
#permLodges = Column(Boolean, default=False)
user = relationship("User", lazy="joined")
board = relationship("Board", lazy="joined")
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"<Mod(id={self.id}, uid={self.user_id}, board_id={self.board_id})>"
@property
def permlist(self):
if self.perm_full:
return "full"
output=[]
for p in ["access","appearance", "config","content"]:
if self.__dict__[f"perm_{p}"]:
output.append(p)
return ", ".join(output) if output else "none"
@property
def permchangelist(self):
output=[]
for p in ["full", "access","appearance", "config","content"]:
if self.__dict__.get(f"perm_{p}"):
output.append(f"+{p}")
else:
output.append(f"-{p}")
return ", ".join(output)
@property
def json_core(self):
return {
'user_id':self.user_id,
'board_id':self.board_id,
'created_utc':self.created_utc,
'accepted':self.accepted,
'invite_rescinded':self.invite_rescinded,
'perm_content':self.perm_full or self.perm_content,
'perm_config':self.perm_full or self.perm_config,
'perm_access':self.perm_full or self.perm_access,
'perm_appearance':self.perm_full or self.perm_appearance,
'perm_full':self.perm_full,
}
@property
def json(self):
data=self.json_core
data["user"]=self.user.json_core
#data["guild"]=self.board.json_core
return data
class BanRelationship(Base, Stndrd, Age_times):
__tablename__ = "bans"
id = Column(BigInteger, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
board_id = Column(Integer, ForeignKey("boards.id"))
created_utc = Column(BigInteger, default=0)
banning_mod_id = Column(Integer, ForeignKey("users.id"))
is_active = Column(Boolean, default=False)
mod_note = Column(String(128), default="")
user = relationship(
"User",
lazy="joined",
primaryjoin="User.id==BanRelationship.user_id")
banning_mod = relationship(
"User",
lazy="joined",
primaryjoin="User.id==BanRelationship.banning_mod_id")
board = relationship("Board")
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"<Ban(id={self.id}, uid={self.uid}, board_id={self.board_id})>"
@property
def json_core(self):
return {
'user_id':self.user_id,
'board_id':self.board_id,
'created_utc':self.created_utc,
'mod_id':self.banning_mod_id
}
@property
def json(self):
data=self.json_core
data["user"]=self.user.json_core
data["mod"]=self.banning_mod.json_core
data["guild"]=self.board.json_core
return data
class ContributorRelationship(Base, Stndrd, Age_times):
__tablename__ = "contributors"
id = Column(BigInteger, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
board_id = Column(Integer, ForeignKey("boards.id"))
created_utc = Column(BigInteger, default=0)
is_active = Column(Boolean, default=True)
approving_mod_id = Column(Integer, ForeignKey("users.id"))
user = relationship(
"User",
lazy="joined",
primaryjoin="User.id==ContributorRelationship.user_id")
approving_mod = relationship(
"User",
lazy='joined',
primaryjoin="User.id==ContributorRelationship.approving_mod_id")
board = relationship("Board", lazy="subquery")
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"<Contributor(id={self.id}, uid={self.uid}, board_id={self.board_id})>"
class PostRelationship(Base):
__tablename__ = "postrels"
id = Column(BigInteger, primary_key=True)
post_id = Column(Integer, ForeignKey("submissions.id"))
board_id = Column(Integer, ForeignKey("boards.id"))
post = relationship("Submission", lazy="subquery")
board = relationship("Board", lazy="subquery")
def __repr__(self):
return f"<PostRel(id={self.id}, pid={self.post_id}, board_id={self.board_id})>"
class BoardBlock(Base, Stndrd, Age_times):
__tablename__ = "boardblocks"
id = Column(BigInteger, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"))
board_id = Column(Integer, ForeignKey("boards.id"))
created_utc = Column(Integer)
user = relationship("User")
board = relationship("Board")
def __repr__(self):
return f"<BoardBlock(id={self.id}, uid={self.user_id}, board_id={self.board_id})>"

View File

@ -1,391 +0,0 @@
from sqlalchemy.orm import lazyload
from .userblock import *
from .submission import *
from .board_relationships import *
from .comment import Comment
from .mix_ins import *
from drama.__main__ import Base, cache
class Board(Base, Stndrd, Age_times):
__tablename__ = "boards"
id = Column(Integer, primary_key=True)
name = Column(String)
created_utc = Column(Integer)
description = Column(String)
description_html=Column(String)
over_18=Column(Boolean, default=False)
is_nsfl=Column(Boolean, default=False)
is_banned=Column(Boolean, default=False)
disablesignups=Column(Boolean, default=False)
has_banner=Column(Boolean, default=False)
creator_id=Column(Integer, ForeignKey("users.id"))
ban_reason=Column(String(256), default=None)
color=Column(String(8), default="FF66AC")
restricted_posting=Column(Boolean, default=False)
hide_banner_data=Column(Boolean, default=False)
profile_nonce=Column(Integer, default=0)
banner_nonce=Column(Integer, default=0)
is_private=Column(Boolean, default=False)
color_nonce=Column(Integer, default=0)
rank_trending=Column(Float, default=0)
stored_subscriber_count=Column(Integer, default=1)
all_opt_out=Column(Boolean, default=False)
is_siegable=Column(Boolean, default=True)
secondary_color=Column(String(6), default="cfcfcf")
motd = Column(String(1000), default='')
moderators=relationship("ModRelationship")
submissions=relationship("Submission", primaryjoin="Board.id==Submission.board_id")
contributors=relationship("ContributorRelationship", lazy="dynamic")
bans=relationship("BanRelationship", lazy="dynamic")
postrels=relationship("PostRelationship", lazy="dynamic")
trending_rank=deferred(Column(Float, server_default=FetchedValue()))
# db side functions
subscriber_count = deferred(Column(Integer, server_default=FetchedValue()))
def __init__(self, **kwargs):
kwargs["created_utc"] = int(time.time())
super().__init__(**kwargs)
def __repr__(self):
return f"<Board(name={self.name})>"
@property
def fullname(self):
return f"t4_{self.base36id}"
@property
def mods_list(self):
z = [x for x in self.moderators if x.accepted and not (
x.user.is_deleted or (x.user.is_banned and not x.user.unban_utc))]
z = sorted(z, key=lambda x: x.created_utc)
return z
@property
def mods(self):
z = [x for x in self.moderators if x.accepted]
z = sorted(z, key=lambda x: x.created_utc)
z = [x.user for x in z]
return z
@property
def invited_mods(self):
z = [x.user for x in self.moderators if x.accepted ==
False and x.invite_rescinded == False]
z = sorted(z, key=lambda x: x.created_utc)
return z
@property
def mod_invites(self):
z = [x for x in self.moderators if x.accepted ==
False and x.invite_rescinded == False]
z = sorted(z, key=lambda x: x.created_utc)
return z
@property
def mods_count(self):
return len(self.mods_list)
@property
def permalink(self):
return f"/+{self.name}"
def can_take(self, post):
if self.is_banned:
return False
return not self.postrels.filter_by(post_id=post.id).first()
def has_mod(self, user, perm=None):
if user is None:
return None
if self.is_banned:
return False
m=self.__dict__.get("_mod")
if not m:
for x in user.moderates:
if x.board_id == self.id and x.accepted and not x.invite_rescinded:
self.__dict__["mod"]=x
m=x
if not m:
return False
if perm:
return m if (m.perm_full or m.__dict__[f"perm_{perm}"]) else False
else:
return m
return False
def has_mod_record(self, user, perm=None):
if user is None:
return None
if self.is_banned:
return False
for x in user.moderates:
if x.board_id == self.id and not x.invite_rescinded:
if perm:
return x if x.__dict__[f"perm_{perm}"] else False
else:
return x
return False
def can_invite_mod(self, user):
return user.id not in [
x.user_id for x in self.moderators if not x.invite_rescinded]
def has_rescinded_invite(self, user):
return user.id in [
x.user_id for x in self.moderators if x.invite_rescinded == True]
def has_invite(self, user):
if user is None:
return None
for x in [
i for i in self.moderators if not i.invite_rescinded and not i.accepted]:
if x.user_id == user.id:
return x
return None
def has_ban(self, user):
if user is None:
return None
if user.admin_level >=2:
return None
return g.db.query(BanRelationship).filter_by(
board_id=self.id, user_id=user.id, is_active=True).first()
def has_contributor(self, user):
if user is None:
return False
return g.db.query(ContributorRelationship).filter_by(
user_id=user.id, board_id=self.id, is_active=True).first()
def can_submit(self, user):
if user is None:
return False
if user.admin_level >= 4:
return True
if self.has_ban(user):
return False
if self.has_contributor(user) or self.has_mod(user):
return True
if self.is_private or self.restricted_posting:
return False
return True
def can_comment(self, user):
if user is None:
return False
if user.admin_level >= 4:
return True
if self.has_ban(user):
return False
if self.has_contributor(user) or self.has_mod(user):
return True
if self.is_private:
return False
return True
def can_view(self, user):
if user is None:
return False
if user.admin_level >= 4:
return True
if self.has_contributor(user) or self.has_mod(
user) or self.has_invite(user):
return True
if self.is_private:
return False
@property
def banner_url(self):
return "/assets/images/preview.png"
@property
def profile_url(self):
return "/assets/images/favicon.png"
@property
def css_url(self):
return f"/assets/{self.fullname}/main/{self.color_nonce}.css"
@property
def css_dark_url(self):
return f"/assets/{self.fullname}/dark/{self.color_nonce}.css"
def has_participant(self, user):
return (g.db.query(Submission).filter_by(original_board_id=self.id, author_id=user.id).first() or
g.db.query(Comment).filter_by(
author_id=user.id, original_board_id=self.id).first()
)
@property
@lazy
def n_pins(self):
return g.db.query(Submission).filter_by(
board_id=self.id, is_pinned=True).count()
@property
def can_pin_another(self):
return self.n_pins < 4
@property
def json_core(self):
if self.is_banned:
return {'name': self.name,
'permalink': self.permalink,
'is_banned': True,
'ban_reason': self.ban_reason,
'id': self.base36id
}
return {'name': self.name,
'profile_url': self.profile_url,
'banner_url': self.banner_url,
'created_utc': self.created_utc,
'permalink': self.permalink,
'description': self.description,
'description_html': self.description_html,
'over_18': self.over_18,
'is_banned': False,
'is_private': self.is_private,
'is_restricted': self.restricted_posting,
'id': self.base36id,
'fullname': self.fullname,
'banner_url': self.banner_url,
'profile_url': self.profile_url,
'color': "#" + self.color,
'is_siege_protected': not self.is_siegable
}
@property
def json(self):
data=self.json_core
if self.is_banned:
return data
data['guildmasters']=[x.json_core for x in self.mods]
data['subscriber_count']= self.subscriber_count
return data
@property
def show_settings_icons(self):
return self.is_private or self.restricted_posting or self.over_18 or self.all_opt_out
@cache.memoize(600)
def comment_idlist(self, page=1, v=None, nsfw=False, **kwargs):
posts = g.db.query(Submission).options(
lazyload('*')).filter_by(board_id=self.id)
if not nsfw:
posts = posts.filter_by(over_18=False)
if v and not v.show_nsfl:
posts = posts.filter_by(is_nsfl=False)
if self.is_private:
if v and (self.can_view(v) or v.admin_level >= 4):
pass
elif v:
posts = posts.filter(or_(Submission.post_public == True,
Submission.author_id == v.id
)
)
else:
posts = posts.filter_by(post_public=True)
posts = posts.subquery()
comments = g.db.query(Comment).options(lazyload('*'))
if v and v.hide_offensive:
comments = comments.filter_by(is_offensive=False)
if v and v.hide_bot:
comments = comments.filter_by(is_bot=False)
if v and not self.has_mod(v) and v.admin_level <= 3:
# blocks
blocking = g.db.query(
UserBlock.target_id).filter_by(
user_id=v.id).subquery()
blocked = g.db.query(
UserBlock.user_id).filter_by(
target_id=v.id).subquery()
comments = comments.filter(
Comment.author_id.notin_(blocking),
Comment.author_id.notin_(blocked)
)
if not v or not v.admin_level >= 3:
comments = comments.filter_by(is_banned=False).filter(Comment.deleted_utc == 0)
comments = comments.join(
posts, Comment.parent_submission == posts.c.id)
comments = comments.order_by(Comment.created_utc.desc()).offset(
25 * (page - 1)).limit(26).all()
return [x.id for x in comments]

View File

@ -68,7 +68,6 @@ class ClientAuth(Base, Stndrd):
scope_update = Column(Boolean, default=False)
scope_delete = Column(Boolean, default=False)
scope_vote = Column(Boolean, default=False)
scope_guildmaster = Column(Boolean, default=False)
access_token = Column(String(128))
refresh_token = Column(String(128))
access_token_expire_utc = Column(Integer)
@ -86,7 +85,6 @@ class ClientAuth(Base, Stndrd):
output += "update," if self.scope_update else ""
output += "delete," if self.scope_delete else ""
output += "vote," if self.scope_vote else ""
output += "guildmaster," if self.scope_guildmaster else ""
output = output.rstrip(',')

View File

@ -34,14 +34,11 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
parent_submission = Column(Integer, ForeignKey("submissions.id"))
# this column is foreignkeyed to comment(id) but we can't do that yet as
# "comment" class isn't yet defined
parent_fullname = Column(Integer)
created_utc = Column(Integer, default=0)
edited_utc = Column(Integer, default=0)
is_banned = Column(Boolean, default=False)
shadowbanned = Column(Boolean, default=False)
distinguish_level = Column(Integer, default=0)
gm_distinguish = Column(Integer, ForeignKey("boards.id"), default=0)
distinguished_board = relationship("Board", lazy="joined", primaryjoin="Comment.gm_distinguish==Board.id")
deleted_utc = Column(Integer, default=0)
purged_utc = Column(Integer, default=0)
is_approved = Column(Integer, default=0)
@ -51,11 +48,8 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
score_top = Column(Integer, default=1)
level = Column(Integer, default=0)
parent_comment_id = Column(Integer, ForeignKey("comments.id"))
original_board_id = Column(Integer, ForeignKey("boards.id"))
over_18 = Column(Boolean, default=False)
is_offensive = Column(Boolean, default=False)
is_nsfl = Column(Boolean, default=False)
is_bot = Column(Boolean, default=False)
banaward = Column(String, default=None)
is_pinned = Column(Boolean, default=False)
@ -72,9 +66,6 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
lazy="joined",
innerjoin=True,
primaryjoin="User.id==Comment.author_id")
board = association_proxy("post", "board")
original_board = relationship(
"Board", primaryjoin="Board.id==Comment.original_board_id")
upvotes = Column(Integer, default=1)
downvotes = Column(Integer, default=0)
@ -95,8 +86,6 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
rank_fiery = deferred(Column(Float, server_default=FetchedValue()))
rank_hot = deferred(Column(Float, server_default=FetchedValue()))
board_id = deferred(Column(Integer, server_default=FetchedValue()))
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:
@ -115,28 +104,9 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
def score_disputed(self):
return (self.upvotes+1) * (self.downvotes+1)
@property
@lazy
def fullname(self):
return f"t3_{self.base36id}"
def children(self, v):
return sorted([x for x in self.child_comments if not x.author.shadowbanned or (v and v.id == x.author_id)], key=lambda x: x.score, reverse=True)
@property
@lazy
def is_deleted(self):
return bool(self.deleted_utc)
@property
@lazy
def is_top_level(self):
return self.parent_fullname and self.parent_fullname.startswith("t2_")
@property
def is_archived(self):
return self.post.is_archived
@property
@lazy
def parent(self):
@ -144,7 +114,7 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
if not self.parent_submission:
return None
if self.is_top_level:
if self.level == 1:
return self.post
else:
@ -229,27 +199,13 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
else:
return self.flag_count
def visibility_reason(self, v):
if not v or self.author_id == v.id:
return "this is your content."
elif not self.board:
return None
elif self.board.has_mod(v):
return f"you are a guildmaster of +{self.board.name}."
elif self.board.has_contributor(v):
return f"you are an approved contributor in +{self.board.name}."
elif self.parent.author_id == v.id:
return "this is a reply to your content."
elif v.admin_level >= 4:
return "you are a Drama admin."
@property
def json_raw(self):
data= {
'id': self.base36id,
'fullname': self.fullname,
'level': self.level,
'author_name': self.author.username if not self.author.is_deleted else None,
'author_name': self.author.username if not self.author.deleted_utc > 0 else None,
'body': self.body,
'body_html': self.body_html,
'is_archived': self.is_archived,
@ -257,10 +213,9 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
'created_utc': self.created_utc,
'edited_utc': self.edited_utc or 0,
'is_banned': bool(self.is_banned),
'is_deleted': self.is_deleted,
'deleted_utc': self.deleted_utc,
'is_nsfw': self.over_18,
'is_offensive': self.is_offensive,
'is_nsfl': self.is_nsfl,
'permalink': self.permalink,
'post_id': self.post.base36id,
'score': self.score_fuzzed,
@ -284,14 +239,12 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
'id': self.base36id,
'post': self.post.base36id,
'level': self.level,
'parent': self.parent_fullname
}
elif self.deleted_utc > 0:
data= {'deleted_utc': self.deleted_utc,
'id': self.base36id,
'post': self.post.base36id,
'level': self.level,
'parent': self.parent_fullname
}
else:
@ -315,7 +268,6 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
data["author"]=self.author.json_core
data["post"]=self.post.json_core
data["guild"]=self.post.board.json_core
if self.level >= 2:
data["parent"]=self.parent.json_core
@ -435,7 +387,6 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
"deleted_utc": self.deleted_utc,
'created_utc': self.created_utc,
'id': self.base36id,
'fullname': self.fullname,
'permalink': self.permalink,
'post_id': self.post.base36id,
'level': self.level
@ -461,7 +412,7 @@ class Comment(Base, Age_times, Scores, Stndrd, Fuzzing):
@property
@lazy
def is_op(self):
return self.author_id==self.post.author_id and not self.author.is_deleted and not self.post.author.is_deleted and not self.post.is_deleted
return self.author_id==self.post.author_id and not self.author.deleted_utc > 0 and not self.post.author.deleted_utc > 0 and not self.post.deleted_utc

View File

@ -45,8 +45,6 @@ class Report(Base):
post_id = Column(Integer, ForeignKey("submissions.id"))
user_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer)
board_id = Column(Integer, server_default=FetchedValue())
user = relationship("User", lazy = "joined", primaryjoin = "Report.user_id == User.id", uselist = False)

View File

@ -37,6 +37,7 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
uselist=False,
innerjoin=True,
primaryjoin="Submission.id==SubmissionAux.id")
gm_distinguish = Column(Integer, default=0)
author_id = Column(BigInteger, ForeignKey("users.id"))
repost_id = Column(BigInteger, ForeignKey("submissions.id"), default=0)
edited_utc = Column(BigInteger, default=0)
@ -48,8 +49,6 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
banaward = Column(String, default=None)
purged_utc = Column(Integer, default=0)
distinguish_level = Column(Integer, default=0)
gm_distinguish = Column(Integer, ForeignKey("boards.id"), default=0)
distinguished_board = relationship("Board", lazy="joined", primaryjoin="Board.id==Submission.gm_distinguish")
created_str = Column(String(255), default=None)
stickied = Column(Boolean, default=False)
is_pinned = Column(Boolean, default=False)
@ -64,11 +63,7 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
flags = relationship("Flag", backref="submission")
is_approved = Column(Integer, ForeignKey("users.id"), default=0)
approved_utc = Column(Integer, default=0)
board_id = Column(Integer, ForeignKey("boards.id"), default=None)
original_board_id = Column(Integer, ForeignKey("boards.id"), default=None)
over_18 = Column(Boolean, default=False)
original_board = relationship(
"Board", primaryjoin="Board.id==Submission.original_board_id")
creation_ip = Column(String(64), default="")
mod_approved = Column(Integer, default=None)
accepted_utc = Column(Integer, default=0)
@ -78,12 +73,6 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
score_top = Column(Float, default=1)
score_activity = Column(Float, default=0)
is_offensive = Column(Boolean, default=False)
is_nsfl = Column(Boolean, default=False)
board = relationship(
"Board",
lazy="joined",
innerjoin=True,
primaryjoin="Submission.board_id==Board.id")
author = relationship(
"User",
lazy="joined",
@ -138,16 +127,6 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
def __repr__(self):
return f"<Submission(id={self.id})>"
@property
@lazy
def board_base36id(self):
return base36encode(self.board_id)
@property
@lazy
def is_deleted(self):
return bool(self.deleted_utc)
@property
@lazy
@ -215,17 +194,12 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
if "replies" not in self.__dict__ and "_preloaded_comments" in self.__dict__:
self.tree_comments(comment=comment)
# return template
is_allowed_to_comment = self.board.can_comment(
v) and not self.is_archived
return render_template(template,
v=v,
p=self,
sort=sort,
linked_comment=comment,
comment_info=comment_info,
is_allowed_to_comment=is_allowed_to_comment,
render_replies=True,
)
@ -288,33 +262,18 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
else:
return None
def visibility_reason(self, v):
if not v or self.author_id == v.id:
return "this is your content."
elif self.is_pinned:
return "a guildmaster has pinned it."
elif self.board.has_mod(v):
return f"you are a guildmaster of +{self.board.name}."
elif self.board.has_contributor(v):
return f"you are an approved contributor in +{self.board.name}."
elif v.admin_level >= 4:
return "you are a Drama admin."
@property
def json_raw(self):
data = {'author_name': self.author.username if not self.author.is_deleted else None,
data = {'author_name': self.author.username if not self.author.deleted_utc > 0 else None,
'permalink': self.permalink,
'is_banned': bool(self.is_banned),
'is_deleted': self.is_deleted,
'deleted_utc': self.deleted_utc,
'created_utc': self.created_utc,
'id': self.base36id,
'fullname': self.fullname,
'title': self.title,
'is_nsfw': self.over_18,
'is_nsfl': self.is_nsfl,
'is_bot': self.is_bot,
'thumb_url': self.thumb_url,
'domain': self.domain,
@ -324,8 +283,6 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
'body_html': self.body_html,
'created_utc': self.created_utc,
'edited_utc': self.edited_utc or 0,
'guild_name': self.board.name,
'guild_id': base36encode(self.board_id),
'comment_count': self.comment_count,
'score': self.score_fuzzed,
'upvotes': self.upvotes_fuzzed,
@ -339,9 +296,6 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
if self.ban_reason:
data["ban_reason"]=self.ban_reason
if self.board_id != self.original_board_id and self.original_board:
data['original_guild_name'] = self.original_board.name
data['original_guild_id'] = base36encode(self.original_board_id)
return data
@property
@ -349,20 +303,18 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
if self.is_banned:
return {'is_banned': True,
'is_deleted': self.is_deleted,
'deleted_utc': self.deleted_utc,
'ban_reason': self.ban_reason,
'id': self.base36id,
'title': self.title,
'permalink': self.permalink,
'guild_name': self.board.name
}
elif self.is_deleted:
elif self.deleted_utc:
return {'is_banned': bool(self.is_banned),
'is_deleted': True,
'deleted_utc': True,
'id': self.base36id,
'title': self.title,
'permalink': self.permalink,
'guild_name': self.board.name
}
return self.json_raw
@ -376,8 +328,6 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
return data
data["author"]=self.author.json_core
data["guild"]=self.board.json_core
data["original_guild"]=self.original_board.json_core if not self.board_id==self.original_board_id else None
data["comment_count"]=self.comment_count
@ -506,10 +456,6 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
def is_blocking(self):
return self.__dict__.get('_is_blocking', False)
@property
def is_public(self):
return self.post_public or not self.board.is_private
@property
def flag_count(self):
return len(self.flags)
@ -553,15 +499,10 @@ class Submission(Base, Stndrd, Age_times, Scores, Fuzzing):
'created_utc': self.created_utc,
'id': self.base36id,
'fullname': self.fullname,
'guild_name': self.board.name,
'comment_count': self.comment_count,
'permalink': self.permalink
}
if self.original_board_id and (self.original_board_id!= self.board_id):
data['original_guild_name'] = self.original_board.name
return data
@property

View File

@ -8,13 +8,11 @@ class Subscription(Base):
__tablename__ = "subscriptions"
id = Column(BigInteger, primary_key=True)
user_id = Column(BigInteger, ForeignKey("users.id"))
board_id = Column(BigInteger, ForeignKey("boards.id"))
created_utc = Column(BigInteger, default=0)
is_active = Column(Boolean, default=True)
submission_id = Column(BigInteger, default=0)
user = relationship("User", uselist=False)
board = relationship("Board", uselist=False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:

View File

@ -8,8 +8,6 @@ from .alts import Alt
from .titles import Title
from .submission import SaveRelationship
from .comment import Notification
from .boards import Board
from .board_relationships import *
from .subscriptions import *
from .userblock import *
from .badges import *
@ -93,12 +91,11 @@ class User(Base, Stndrd, Age_times):
mfa_secret = deferred(Column(String(16), default=None))
hide_offensive = Column(Boolean, default=False)
hide_bot = Column(Boolean, default=False)
show_nsfl = Column(Boolean, default=False)
is_private = Column(Boolean, default=False)
read_announcement_utc = Column(Integer, default=0)
unban_utc = Column(Integer, default=0)
is_deleted = Column(Boolean, default=False)
deleted_utc = Column(Boolean, default=False)
delete_reason = Column(String(500), default='')
filter_nsfw = Column(Boolean, default=False)
stored_subscriber_count = Column(Integer, default=0)
@ -129,12 +126,6 @@ class User(Base, Stndrd, Age_times):
moderates = relationship("ModRelationship")
banned_from = relationship("BanRelationship", primaryjoin="BanRelationship.user_id==User.id")
subscriptions = relationship("Subscription")
boards_created = relationship("Board", lazy="dynamic")
contributes = relationship(
"ContributorRelationship",
lazy="dynamic",
primaryjoin="ContributorRelationship.user_id==User.id")
board_blocks = relationship("BoardBlock", lazy="dynamic")
following = relationship("Follow", primaryjoin="Follow.user_id==User.id")
followers = relationship("Follow", primaryjoin="Follow.target_id==User.id")
@ -262,21 +253,7 @@ class User(Base, Stndrd, Age_times):
m = g.db.query(ModRelationship).filter_by(user_id=v.id, invite_rescinded=False).subquery()
c = v.contributes.subquery()
comments = comments.join(m,
m.c.board_id == Submission.board_id,
isouter=True
).join(c,
c.c.board_id == Submission.board_id,
isouter=True
).join(Board, Board.id == Submission.board_id)
comments = comments.filter(or_(Comment.author_id == v.id,
Submission.post_public == True,
Board.is_private == False,
m.c.board_id != None,
c.c.board_id != None))
else:
comments = comments.join(Board, Board.id == Submission.board_id).filter(
or_(Submission.post_public == True, Board.is_private == False))
comments = comments.filter(or_(Comment.author_id == v.id))
comments = comments.options(contains_eager(Comment.post))
@ -310,20 +287,6 @@ class User(Base, Stndrd, Age_times):
secondrange = firstrange + 26
return [x.id for x in comments[firstrange:secondrange]]
@property
@lazy
def mods_anything(self):
return bool([i for i in self.moderates if i.accepted])
@property
def boards_modded(self):
z = [x.board for x in self.moderates if x and x.board and x.accepted and not x.board.is_banned]
z = sorted(z, key=lambda x: x.name)
return z
@property
def base36id(self):
return base36encode(self.id)
@ -332,15 +295,6 @@ class User(Base, Stndrd, Age_times):
def fullname(self):
return f"t1_{self.base36id}"
@property
@cache.memoize(timeout=60)
def has_report_queue(self):
board_ids = [
x.board_id for x in self.moderates.filter_by(
accepted=True).all()]
return bool(g.db.query(Submission).filter(Submission.board_id.in_(
board_ids), Submission.mod_approved == 0, Submission.is_banned == False).join(Submission.reports).first())
@property
def banned_by(self):
@ -617,44 +571,8 @@ class User(Base, Stndrd, Age_times):
else:
return self.defaultpicture()
@property
def available_titles(self):
locs = {"v": self,
"Board": Board,
"Submission": Submission
}
titles = [
i for i in g.db.query(Title).order_by(
text("id asc")).all() if eval(
i.qualification_expr, {}, locs)]
return titles
@property
def can_make_guild(self):
return False
@property
def can_join_gms(self):
return len([x for x in self.boards_modded if x.is_siegable]) < 10
@property
def can_siege(self):
if self.is_suspended:
return False
now = int(time.time())
return now - max(self.last_siege_utc,
self.created_utc) > 60 * 60 * 24 * 7
@property
def can_submit_image(self):
# Has premium
# Has 1000 Rep, or 500 for older accounts
# if connecting through Tor, must have verified email
return self.dramacoins >= 0
@property
@ -700,10 +618,10 @@ class User(Base, Stndrd, Age_times):
'id': self.base36id
}
elif self.is_deleted:
elif self.deleted_utc:
return {'username': self.username,
'permalink': self.permalink,
'is_deleted': True,
'deleted_utc': True,
'id': self.base36id
}
return self.json_raw
@ -712,7 +630,7 @@ class User(Base, Stndrd, Age_times):
def json(self):
data = self.json_core
if self.is_deleted or self.is_banned:
if self.deleted_utc > 0 or self.is_banned:
return data
data["badges"] = [x.json_core for x in self.badges]
@ -734,7 +652,7 @@ class User(Base, Stndrd, Age_times):
if self.is_banned and self.unban_utc == 0:
return False
elif self.is_deleted:
elif self.deleted_utc:
return False
else:
@ -869,90 +787,12 @@ class User(Base, Stndrd, Age_times):
return [x[0] for x in comments.offset(25 * (page - 1)).limit(26).all()]
def guild_rep(self, guild, recent=0):
posts = g.db.query(Submission.score).filter_by(
is_banned=False,
original_board_id=guild.id)
if recent:
cutoff = int(time.time()) - 60 * 60 * 24 * recent
posts = posts.filter(Submission.created_utc > cutoff)
posts = posts.all()
post_rep = sum([x[0] for x in posts]) - len(list(sum([x[0] for x in posts])))
comments = g.db.query(Comment.score).filter_by(
is_banned=False,
original_board_id=guild.id)
if recent:
cutoff = int(time.time()) - 60 * 60 * 24 * recent
comments = comments.filter(Comment.created_utc > cutoff)
comments = comments.all()
comment_rep = sum([x[0] for x in comments]) - len(list(sum([x[0] for x in comments])))
return int(post_rep + comment_rep)
@property
def has_premium(self):
now = int(time.time())
if self.negative_balance_cents:
return False
elif self.premium_expires_utc > now:
return True
elif self.coin_balance >= 1:
self.coin_balance -= 1
self.premium_expires_utc = now + 60 * 60 * 24 * 7
g.db.add(self)
return True
else:
if self.premium_expires_utc:
self.premium_expires_utc = 0
g.db.add(self)
return False
@property
def has_premium_no_renew(self):
now = int(time.time())
if self.negative_balance_cents:
return False
elif self.premium_expires_utc > now:
return True
elif self.coin_balance >= 1:
return True
else:
return False
@property
def renew_premium_time(self):
return time.strftime("%d %b %Y at %H:%M:%S",
time.gmtime(self.premium_expires_utc))
@property
def filter_words(self):
l = [i.strip() for i in self.custom_filter_list.split('\n')] if self.custom_filter_list else []
l = [i for i in l if i]
return l
@property
def boards_modded_ids(self):
return [x.id for x in self.boards_modded]
@property
def json_admin(self):
data = self.json_raw
@ -966,7 +806,7 @@ class User(Base, Stndrd, Age_times):
@property
def can_upload_comment_image(self):
return self.dramacoins >= 0 and (request.headers.get("cf-ipcountry") != "T1" or self.is_activated)
return self.dramacoins >= 0 and request.headers.get("cf-ipcountry") != "T1"
@property
def can_change_name(self):

View File

@ -3,18 +3,11 @@ import time
from .security import *
def session_over18(board):
def session_over18():
now = int(time.time())
return session.get('over_18', {}).get(board.base36id, 0) >= now
def session_isnsfl(board):
now = int(time.time())
return session.get('show_nsfl', {}).get(board.base36id, 0) >= now
return session.get('over_18', {}).get(1) >= now
def make_logged_out_formkey(t):

View File

@ -47,11 +47,9 @@ def get_logged_in_user(db=None):
nonce = session.get("login_nonce", 0)
if not uid:
x= (None, None)
v = db.query(User).options(
joinedload(User.moderates).joinedload(ModRelationship.board), #joinedload(Board.reports),
).filter_by(
v = db.query(User).filter_by(
id=uid,
is_deleted=False
deleted_utc=False
).first()
if v and v.agendaposter_expires_utc and v.agendaposter_expires_utc < g.timestamp:
@ -94,7 +92,6 @@ def check_ban_evade(v):
kind="ban_post",
user_id=2317,
target_submission_id=post.id,
board_id=post.board_id,
note="ban evasion"
)
g.db.add(ma)
@ -113,7 +110,6 @@ def check_ban_evade(v):
kind="ban_comment",
user_id=2317,
target_comment_id=comment.id,
board_id=comment.post.board_id,
note="ban evasion"
)
g.db.add(ma)

View File

@ -1,5 +1,4 @@
from .admin import *
from .boards import *
from .comments import *
from .discord import *
from .errors import *

View File

@ -126,8 +126,7 @@ def flagged_comments(v):
@app.route("/admin", methods=["GET"])
@admin_level_required(3)
def admin_home(v):
b = g.db.query(Board).filter_by(id=1).first()
return render_template("admin/admin_home.html", v=v, b=b)
return render_template("admin/admin_home.html", v=v)
@app.route("/admin/badge_grant", methods=["GET"])
@ -536,7 +535,6 @@ def agendaposter(user_id, v):
kind=kind,
user_id=v.id,
target_user_id=user.id,
board_id=1,
note = note
)
g.db.add(ma)
@ -546,14 +544,11 @@ def agendaposter(user_id, v):
else:
return redirect(user.url)
@app.route("/disablesignups", methods=["POST"])
@admin_level_required(6)
@validate_formkey
def disablesignups(v):
board = g.db.query(Board).filter_by(id=1).first()
board.disablesignups = not board.disablesignups
g.db.add(board)
return "", 204
# @app.route("/disablesignups", methods=["POST"])
# @admin_level_required(6)
# @validate_formkey
# def disablesignups(v):
# return "", 204
@app.route("/shadowban/<user_id>", methods=["POST"])
@ -572,7 +567,6 @@ def shadowban(user_id, v):
kind="shadowban",
user_id=v.id,
target_user_id=user.id,
board_id=1,
)
g.db.add(ma)
cache.delete_memoized(frontlist)
@ -595,7 +589,6 @@ def unshadowban(user_id, v):
kind="unshadowban",
user_id=v.id,
target_user_id=user.id,
board_id=1,
)
g.db.add(ma)
cache.delete_memoized(frontlist)
@ -629,7 +622,6 @@ def admin_title_change(user_id, v):
kind=kind,
user_id=v.id,
target_user_id=user.id,
board_id=1,
note=f'"{new_name}"'
)
g.db.add(ma)
@ -683,7 +675,6 @@ def ban_user(user_id, v):
kind="exile_user",
user_id=v.id,
target_user_id=user.id,
board_id=1,
note=f'reason: "{reason}", duration: {duration}'
)
g.db.add(ma)
@ -717,7 +708,6 @@ def unban_user(user_id, v):
kind="unexile_user",
user_id=v.id,
target_user_id=user.id,
board_id=1,
)
g.db.add(ma)
g.db.commit()
@ -757,7 +747,6 @@ def ban_post(post_id, v):
kind="ban_post",
user_id=v.id,
target_submission_id=post.id,
board_id=post.board_id,
)
g.db.add(ma)
return "", 204
@ -778,7 +767,6 @@ def unban_post(post_id, v):
kind="unban_post",
user_id=v.id,
target_submission_id=post.id,
board_id=post.board_id,
)
g.db.add(ma)
@ -857,7 +845,6 @@ def api_ban_comment(c_id, v):
kind="ban_comment",
user_id=v.id,
target_comment_id=comment.id,
board_id=comment.post.board_id,
)
g.db.add(ma)
return "", 204
@ -877,7 +864,6 @@ def api_unban_comment(c_id, v):
kind="unban_comment",
user_id=v.id,
target_comment_id=comment.id,
board_id=comment.post.board_id,
)
g.db.add(ma)
@ -911,7 +897,6 @@ def admin_distinguish_comment(c_id, v):
v=v,
comments=[comment],
render_replies=False,
is_allowed_to_comment=True
)
html=str(BeautifulSoup(html, features="html.parser").find(id=f"comment-{comment.base36id}-only"))
@ -987,7 +972,6 @@ def admin_nuke_user(v):
kind="nuke_user",
user_id=v.id,
target_user_id=user.id,
board_id=1,
)
g.db.add(ma)
@ -1018,7 +1002,6 @@ def admin_nunuke_user(v):
kind="unnuke_user",
user_id=v.id,
target_user_id=user.id,
board_id=1,
)
g.db.add(ma)
@ -1178,4 +1161,51 @@ def multiple_plots(**kwargs):
plt.savefig(name)
plt.clf()
return upload_from_file(name, name)
return upload_from_file(name, name)
@app.route("/admin/distinguish_post/<pid>", methods=["POST"])
@app.route("/api/v1/distinguish_post/<pid>", methods=["POST"])
@admin_level_required(6)
@api("update")
def distinguish_post(pid, v):
post = get_post(pid, v=v)
if post.author_id != v.id:
abort(403)
if post.gm_distinguish:
post.gm_distinguish = 0
else:
post.gm_distinguish = 1
g.db.add(post)
ma=ModAction(
kind="herald_post" if post.gm_distinguish else "unherald_post",
user_id=v.id,
target_submission_id=post.id,
)
g.db.add(ma)
return "", 204
@app.route("/admin/add_admin", methods=["POST"])
@auth_required
@validate_formkey
def invite_username(v):
username = request.form.get("username", '').lstrip('@')
user = get_user(username)
if not user:
return jsonify({"error": "That user doesn't exist."}), 404
user.admin_level = 6
g.db.add(user)
ma=ModAction(
kind="add_mod",
user_id=v.id,
target_user_id=user.id,
)
g.db.add(ma)
return "", 204

View File

@ -1,311 +0,0 @@
from drama.helpers.wrappers import *
from drama.helpers.alerts import *
from drama.classes import *
from flask import *
from drama.__main__ import app, limiter, cache
valid_board_regex = re.compile("^[a-zA-Z0-9][a-zA-Z0-9_]{2,24}$")
@app.route("/mod/distinguish_post/<bid>/<pid>", methods=["POST"])
@app.route("/api/v1/distinguish_post/<bid>/<pid>", methods=["POST"])
@auth_required
@api("guildmaster")
def mod_distinguish_post(bid, pid, board, v):
#print(pid, board, v)
post = get_post(pid, v=v)
if not post.board_id==board.id:
abort(400)
if post.author_id != v.id:
abort(403)
if post.gm_distinguish:
post.gm_distinguish = 0
else:
post.gm_distinguish = board.id
g.db.add(post)
ma=ModAction(
kind="herald_post" if post.gm_distinguish else "unherald_post",
user_id=v.id,
target_submission_id=post.id,
board_id=board.id
)
g.db.add(ma)
return "", 204
@app.route("/mod/invite_mod", methods=["POST"])
@auth_required
@validate_formkey
def mod_invite_username(v):
username = request.form.get("username", '').lstrip('@')
user = get_user(username)
if not user:
return jsonify({"error": "That user doesn't exist."}), 404
if not user.can_join_gms:
return jsonify({"error": f"@{user.username} already leads enough guilds."}), 409
x = g.db.query(ModRelationship).filter_by(
user_id=user.id, board_id=1).first()
if x and x.accepted:
return jsonify({"error": f"@{user.username} is already a mod."}), 409
if x and not x.invite_rescinded:
return jsonify({"error": f"@{user.username} has already been invited."}), 409
if x:
x.invite_rescinded = False
g.db.add(x)
else:
x = ModRelationship(
user_id=user.id,
board_id=1,
accepted=False,
perm_full=True,
perm_content=True,
perm_appearance=True,
perm_access=True,
perm_config=True
)
text = f"You have been invited to become an admin. You can [click here](/badmins) and accept this invitation. Or, if you weren't expecting this, you can ignore it."
send_notification(1046, user, text)
g.db.add(x)
ma=ModAction(
kind="invite_mod",
user_id=v.id,
target_user_id=user.id,
board_id=1
)
g.db.add(ma)
return "", 204
@app.route("/mod/<bid>/rescind/<username>", methods=["POST"])
@auth_required
@validate_formkey
def mod_rescind_bid_username(bid, username, board, v):
user = get_user(username)
invitation = g.db.query(ModRelationship).filter_by(board_id=board.id,
user_id=user.id,
accepted=False).first()
if not invitation:
abort(404)
invitation.invite_rescinded = True
g.db.add(invitation)
ma=ModAction(
kind="uninvite_mod",
user_id=v.id,
target_user_id=user.id,
board_id=1
)
g.db.add(ma)
return "", 204
@app.route("/mod/accept/<bid>", methods=["POST"])
@app.route("/api/v1/accept_invite/<bid>", methods=["POST"])
@auth_required
@validate_formkey
@api("guildmaster")
def mod_accept_board(bid, v):
board = g.db.query(Board).first()
x = board.has_invite(v)
if not x:
abort(404)
if not v.can_join_gms:
return jsonify({"error": f"You already lead enough guilds."}), 409
if board.has_ban(v):
return jsonify({"error": f"You are exiled from +{board.name} and can't currently become a guildmaster."}), 409
x.accepted = True
x.created_utc=int(time.time())
g.db.add(x)
ma=ModAction(
kind="accept_mod_invite",
user_id=v.id,
target_user_id=v.id,
board_id=board.id
)
g.db.add(ma)
v.admin_level = 6
return "", 204
@app.route("/mod/<bid>/step_down", methods=["POST"])
@auth_required
@validate_formkey
def mod_step_down(bid, board, v):
v_mod = board.has_mod(v)
if not v_mod:
abort(404)
g.db.delete(v_mod)
ma=ModAction(
kind="dethrone_self",
user_id=v.id,
target_user_id=v.id,
board_id=board.id
)
g.db.add(ma)
v.admin_level = 0
return "", 204
@app.route("/mod/<bid>/remove/<username>", methods=["POST"])
@auth_required
@validate_formkey
def mod_remove_username(bid, username, board, v):
user = get_user(username)
u_mod = board.has_mod(user)
v_mod = board.has_mod(v)
if not u_mod:
abort(400)
elif not v_mod:
abort(400)
if not u_mod.board_id==board.id:
abort(400)
if not v_mod.board_id==board.id:
abort(400)
if v_mod.id > u_mod.id:
abort(403)
g.db.delete(u_mod)
ma=ModAction(
kind="remove_mod",
user_id=v.id,
target_user_id=user.id,
board_id=board.id
)
g.db.add(ma)
user.admin_level = 0
return "", 204
@app.route("/badmins", methods=["GET"])
@app.route("/api/vue/mod/mods", methods=["GET"])
@app.route("/api/v1/mod/mods", methods=["GET"])
@auth_desired
@public("read")
def board_about_mods(v):
board = g.db.query(Board).first()
me = board.has_mod(v)
return {
"html":lambda:render_template("mods.html", v=v, b=board, me=me),
"api":lambda:jsonify({"data":[x.json for x in board.mods_list]})
}
@app.route("/log", methods=["GET"])
@app.route("/api/v1/mod_log", methods=["GET"])
@auth_desired
@api("read")
def board_mod_log(v):
page=int(request.args.get("page",1))
if v and v.admin_level == 6: actions = g.db.query(ModAction).order_by(ModAction.id.desc()).offset(25 * (page - 1)).limit(26).all()
else: actions=g.db.query(ModAction).filter(ModAction.kind!="shadowban", ModAction.kind!="unshadowban").order_by(ModAction.id.desc()).offset(25*(page-1)).limit(26).all()
actions=[i for i in actions]
next_exists=len(actions)==26
actions=actions[0:25]
return {
"html":lambda:render_template(
"modlog.html",
v=v,
actions=actions,
next_exists=next_exists,
page=page
),
"api":lambda:jsonify({"data":[x.json for x in actions]})
}
@app.route("/log/<aid>", methods=["GET"])
@auth_desired
def mod_log_item(aid, v):
action=g.db.query(ModAction).filter_by(id=base36decode(aid)).first()
if not action:
abort(404)
if request.path != action.permalink:
return redirect(action.permalink)
return render_template("modlog.html",
v=v,
actions=[action],
next_exists=False,
page=1,
action=action
)
@app.route("/mod/edit_perms", methods=["POST"])
@auth_required
@validate_formkey
def board_mod_perms_change(boardname, board, v):
user=get_user(request.form.get("username"))
v_mod=board.has_mod(v)
u_mod=board.has_mod_record(user)
if v_mod.id > u_mod.id:
return jsonify({"error":"You can't change perms on badmins above you."}), 403
#print({x:request.form.get(x) for x in request.form})
u_mod.perm_full = bool(request.form.get("perm_full" , False))
u_mod.perm_access = bool(request.form.get("perm_access" , False))
u_mod.perm_appearance = bool(request.form.get("perm_appearance" , False))
u_mod.perm_config = bool(request.form.get("perm_config" , False))
u_mod.perm_content = bool(request.form.get("perm_content" , False))
g.db.add(u_mod)
g.db.commit()
ma=ModAction(
kind="change_perms" if u_mod.accepted else "change_invite",
user_id=v.id,
board_id=board.id,
target_user_id=user.id,
note=u_mod.permchangelist
)
g.db.add(ma)
return redirect(f"{board.permalink}/mod/mods")

View File

@ -39,7 +39,6 @@ def banawardcomment(comment_id, v):
kind="exile_user",
user_id=v.id,
target_user_id=u.id,
board_id=1,
note=f'reason: "1 day ban award", duration: 1 day'
)
g.db.add(ma)
@ -85,16 +84,14 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None):
except: abort(404)
post = get_post(pid, v=v)
board = post.board
if post.over_18 and not (v and v.over_18) and not session_over18(comment.board):
if post.over_18 and not (v and v.over_18) and not session_over18(1):
t = int(time.time())
return {'html': lambda: render_template("errors/nsfw.html",
v=v,
t=t,
lo_formkey=make_logged_out_formkey(
t),
board=comment.board
),
'api': lambda: {'error': f'This content is not suitable for some users and situations.'}
@ -107,7 +104,7 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None):
except: context = 0
comment_info = comment
c = comment
while context > 0 and not c.is_top_level:
while context > 0 and c.level > 1:
parent = get_comment(c.parent_comment_id, v=v)
@ -312,12 +309,6 @@ def api_comment(v):
return jsonify(
{"error": "You can't reply to users who have blocked you, or users you have blocked."}), 403
# check for archive and ban state
post = get_post(parent_id)
if post.is_archived or not post.board.can_comment(v):
return jsonify({"error": "You can't comment on this."}), 403
# get bot status
is_bot = request.headers.get("X-User-Type","")=="Bot"
@ -362,7 +353,6 @@ def api_comment(v):
user_id=2317,
target_comment_id=comment.id,
kind="ban_comment",
board_id=comment.post.board_id,
note="spam"
)
g.db.add(ma)
@ -400,8 +390,6 @@ def api_comment(v):
parent_comment_id=parent_comment_id,
level=level,
over_18=post.over_18 or request.form.get("over_18","")=="true",
is_nsfl=post.is_nsfl or request.form.get("is_nsfl","")=="true",
original_board_id=parent_post.board_id,
is_bot=is_bot,
app_id=v.client.application.id if v.client else None,
shadowbanned=shadowbanned
@ -424,21 +412,6 @@ def api_comment(v):
with CustomRenderer(post_id=parent_id) as renderer:
body_md = renderer.render(mistletoe.Document(body))
body_html = sanitize(body_md, linkgen=True)
# #csam detection
# def del_function():
# delete_file(name)
# c.is_banned=True
# g.db.add(c)
# g.db.commit()
# csam_thread=threading.Thread(target=check_csam_url,
# args=(f"https://s3.eu-central-1.amazonaws.com/i.ruqqus.ga/{name}",
# v,
# del_function
# )
# )
# csam_thread.start()
c_aux = CommentAux(
id=c.id,
@ -462,7 +435,6 @@ def api_comment(v):
parent_fullname=c.fullname,
parent_comment_id=c.id,
level=level+1,
original_board_id=1,
is_bot=True,
)
@ -497,7 +469,6 @@ def api_comment(v):
parent_fullname=c.fullname,
parent_comment_id=c.id,
level=level+1,
original_board_id=1,
is_bot=True,
)
@ -530,7 +501,6 @@ def api_comment(v):
parent_fullname=c.fullname,
parent_comment_id=c.id,
level=level+1,
original_board_id=1,
is_bot=True,
)
@ -558,7 +528,6 @@ def api_comment(v):
parent_fullname=c2.fullname,
parent_comment_id=c2.id,
level=level+2,
original_board_id=1,
is_bot=True,
)
@ -586,7 +555,6 @@ def api_comment(v):
parent_fullname=c3.fullname,
parent_comment_id=c3.id,
level=level+3,
original_board_id=1,
is_bot=True,
)
@ -672,7 +640,6 @@ def api_comment(v):
# print(f"Content Event: @{v.username} comment {c.base36id}")
board = g.db.query(Board).first()
cache.delete_memoized(comment_idlist)
cache.delete_memoized(User.commentlisting, v)
@ -680,7 +647,6 @@ def api_comment(v):
v=v,
comments=[c],
render_replies=False,
is_allowed_to_comment=True
)}),
"api": lambda: c.json
}
@ -835,7 +801,6 @@ def edit_comment(cid, v):
parent_fullname=c.fullname,
parent_comment_id=c.id,
level=c.level+1,
original_board_id=1,
is_bot=True,
)
@ -961,15 +926,12 @@ def embed_comment_cid(cid, pid=None):
'api': lambda: {'error': f'Comment {cid} has been removed'}
}
if comment.board.is_banned:
abort(410)
return render_template("embeds/comment.html", c=comment)
@app.route("/comment_pin/<cid>", methods=["POST"])
@auth_required
@validate_formkey
def mod_toggle_comment_pin(cid, v):
def toggle_comment_pin(cid, v):
comment = get_comment(cid, v=v)
@ -985,7 +947,6 @@ def mod_toggle_comment_pin(cid, v):
ma=ModAction(
kind="pin_comment" if comment.is_pinned else "unpin_comment",
user_id=v.id,
board_id=1,
target_comment_id=comment.id
)
g.db.add(ma)
@ -995,7 +956,6 @@ def mod_toggle_comment_pin(cid, v):
v=v,
comments=[comment],
render_replies=False,
is_allowed_to_comment=True
)
html=str(BeautifulSoup(html, features="html.parser").find(id=f"comment-{comment.base36id}-only"))

View File

@ -154,24 +154,24 @@ def error_503(e, v):
}
@app.route("/allow_nsfw_logged_in/<bid>", methods=["POST"])
@app.route("/allow_nsfw_logged_in", methods=["POST"])
@auth_required
@validate_formkey
def allow_nsfw_logged_in(bid, v):
def allow_nsfw_logged_in(v):
cutoff = int(time.time()) + 3600
if not session.get("over_18", None):
session["over_18"] = {}
session["over_18"][bid] = cutoff
session["over_18"][1] = cutoff
return redirect(request.form.get("redir"))
@app.route("/allow_nsfw_logged_out/<bid>", methods=["POST"])
@app.route("/allow_nsfw_logged_out", methods=["POST"])
@auth_desired
def allow_nsfw_logged_out(bid, v):
def allow_nsfw_logged_out(v):
if v:
return redirect('/')
@ -187,49 +187,10 @@ def allow_nsfw_logged_out(bid, v):
session["over_18"] = {}
cutoff = int(time.time()) + 3600
session["over_18"][bid] = cutoff
session["over_18"][1] = cutoff
return redirect(request.form.get("redir"))
@app.route("/allow_nsfl_logged_in/<bid>", methods=["POST"])
@auth_required
@validate_formkey
def allow_nsfl_logged_in(bid, v):
cutoff = int(time.time()) + 3600
if not session.get("show_nsfl", None):
session["show_nsfl"] = {}
session["show_nsfl"][bid] = cutoff
return redirect(request.form.get("redir"))
@app.route("/allow_nsfl_logged_out/<bid>", methods=["POST"])
@auth_desired
def allow_nsfl_logged_out(bid, v):
if v:
return redirect('/')
t = int(request.form.get('time'))
if not validate_logged_out_formkey(t,
request.form.get("formkey")
):
abort(403)
if not session.get("show_nsfl", None):
session["show_nsfl"] = {}
cutoff = int(time.time()) + 3600
session["show_nsfl"][bid] = cutoff
return redirect(request.form.get("redir"))
@app.route("/error/<error>", methods=["GET"])
@auth_desired
def error_all_preview(error, v):

View File

@ -35,7 +35,6 @@ def feeds_user(sort='hot', t='all'):
for post in posts:
#print("POST IMAGE "+ str( post.is_image ))
board_name = f"+{post.board.name}"
with tag("entry", ("xml:base", request.url)):
with tag("title", type="text"):
text(post.title)
@ -60,8 +59,6 @@ def feeds_user(sort='hot', t='all'):
doc.stag("link", href=full_link(post.permalink))
doc.stag("category", term=board_name, label=board_name, schema=full_link("/" + board_name))
image_url = post.thumb_url or post.embed_url or post.url
doc.stag("media:thumbnail", url=image_url)

View File

@ -427,7 +427,6 @@ def all_comments(v):
idlist = idlist[0:25]
board = g.db.query(Board).first()
return {"html": lambda: render_template("home_comments.html",
v=v,
sort=sort,

View File

@ -61,7 +61,7 @@ def login_post():
if "@" in username:
account = g.db.query(User).filter(
User.email.ilike(username),
User.is_deleted == False).first()
User.deleted_utc == False).first()
else:
account = get_user(username, graceful=True)
@ -69,7 +69,7 @@ def login_post():
time.sleep(random.uniform(0, 2))
return render_template("login.html", failed=True, i=random_image())
if account.is_deleted:
if account.deleted_utc:
time.sleep(random.uniform(0, 2))
return render_template("login.html", failed=True, i=random_image())
@ -161,11 +161,9 @@ def logout(v):
@no_cors
@auth_desired
def sign_up_get(v):
board = g.db.query(Board).filter_by(id=1).first()
if board.disablesignups: return "Signups are disable for the time being.", 403
#if disablesignups: return "Signups are disable for the time being.", 403
if v:
return redirect("/")
if v: return redirect("/")
agent = request.headers.get("User-Agent", None)
if not agent:
@ -219,8 +217,7 @@ def sign_up_get(v):
@no_cors
@auth_desired
def sign_up_post(v):
board = g.db.query(Board).filter_by(id=1).first()
if board.disablesignups: return "Signups are disable for the time being.", 403
#if disablesignups: return "Signups are disable for the time being.", 403
if v:
abort(403)
@ -421,7 +418,7 @@ def post_forgot():
user = g.db.query(User).filter(
User.username.ilike(username),
User.email.ilike(email),
User.is_deleted == False).first()
User.deleted_utc == False).first()
if user:
# generate url

View File

@ -12,7 +12,6 @@ SCOPES = {
'update': 'Edit your posts and comments',
'delete': 'Delete your posts and comments',
'vote': 'Cast votes as you',
'guildmaster': 'Perform Badmin actions'
}
@ -49,9 +48,8 @@ def oauth_authorize_prompt(v):
if scope not in SCOPES:
return jsonify({"oauth_error": f"The provided scope `{scope}` is not valid."}), 400
if any(x in scopes for x in ["create", "update",
"guildmaster"]) and "identity" not in scopes:
return jsonify({"oauth_error": f"`identity` scope required when requesting `create`, `update`, or `guildmaster` scope."}), 400
if any(x in scopes for x in ["create", "update"]) and "identity" not in scopes:
return jsonify({"oauth_error": f"`identity` scope required when requesting `create` or `update` scope."}), 400
redirect_uri = request.args.get("redirect_uri")
if not redirect_uri:
@ -112,9 +110,8 @@ def oauth_authorize_post(v):
if scope not in SCOPES:
return jsonify({"oauth_error": f"The provided scope `{scope}` is not valid."}), 400
if any(x in scopes for x in ["create", "update",
"guildmaster"]) and "identity" not in scopes:
return jsonify({"oauth_error": f"`identity` scope required when requesting `create`, `update`, or `guildmaster` scope."}), 400
if any(x in scopes for x in ["create", "update"]) and "identity" not in scopes:
return jsonify({"oauth_error": f"`identity` scope required when requesting `create` or `update` scope."}), 400
if not state:
return jsonify({'oauth_error': 'state argument required'}), 400
@ -131,7 +128,6 @@ def oauth_authorize_post(v):
scope_update="update" in scopes,
scope_delete="delete" in scopes,
scope_vote="vote" in scopes,
scope_guildmaster="guildmaster" in scopes,
refresh_token=secrets.token_urlsafe(128)[0:128] if permanent else None
)

View File

@ -40,7 +40,6 @@ def postbanaward(post_id, v):
kind="exile_user",
user_id=v.id,
target_user_id=u.id,
board_id=1,
note=f'reason: "1 day ban award", duration: 1 day'
)
g.db.add(ma)
@ -69,13 +68,9 @@ def publish(pid, v):
@auth_required
def submit_get(v):
if v and v.is_banned and not v.unban_utc: return render_template("seized.html")
b = g.db.query(Board).first()
return render_template("submit.html",
v=v,
b=b
)
v=v)
@app.route("/post/<pid>", methods=["GET"])
@app.route("/post/<pid>/", methods=["GET"])
@ -237,15 +232,12 @@ def post_base36id(pid, anything=None, v=None):
g.db.add(post)
g.db.commit()
board = post.board
if post.over_18 and not (v and v.over_18) and not session_over18(board):
if post.over_18 and not (v and v.over_18) and not session_over18(1):
t = int(time.time())
return {"html":lambda:render_template("errors/nsfw.html",
v=v,
t=t,
lo_formkey=make_logged_out_formkey(t),
board=post.board
),
"api":lambda:(jsonify({"error":"Must be 18+ to view"}), 451)
@ -271,9 +263,6 @@ def edit_post(pid, v):
if p.is_banned:
abort(403)
if p.board.has_ban(v):
abort(403)
body = request.form.get("body", "")
for i in re.finditer('^(https:\/\/.*\.(png|jpg|jpeg|gif))', body, re.MULTILINE): body = body.replace(i.group(1), f'![]({i.group(1)})')
body = body.replace("\n", "\n\n")
@ -346,9 +335,6 @@ def edit_post(pid, v):
parent_fullname=p.fullname,
level=1,
over_18=False,
is_nsfl=False,
is_offensive=False,
original_board_id=1,
is_bot=True,
app_id=None,
creation_region=request.headers.get("cf-ipcountry"),
@ -600,8 +586,6 @@ def submit_post(v):
if repost:
return redirect(repost.permalink)
board = g.db.query(Board).first()
if not title:
return {"html": lambda: (render_template("submit.html",
v=v,
@ -610,22 +594,10 @@ def submit_post(v):
url=url,
body=request.form.get(
"body", ""),
b=board
), 400),
"api": lambda: ({"error": "Please enter a better title"}, 400)
}
# if len(title)<10:
# return render_template("submit.html",
# v=v,
# error="Please enter a better title.",
# title=title,
# url=url,
# body=request.form.get("body",""),
# b=board
# )
elif len(title) > 500:
return {"html": lambda: (render_template("submit.html",
v=v,
@ -634,7 +606,6 @@ def submit_post(v):
url=url,
body=request.form.get(
"body", ""),
b=board
), 400),
"api": lambda: ({"error": "500 character limit for titles"}, 400)
}
@ -649,7 +620,6 @@ def submit_post(v):
url=url,
body=request.form.get(
"body", ""),
b=board
), 400),
"api": lambda: ({"error": "`url` or `body` parameter required."}, 400)
}
@ -677,7 +647,6 @@ def submit_post(v):
Submission.author_id == v.id,
Submission.deleted_utc == 0,
Submission.board_id == board.id,
SubmissionAux.title == title,
SubmissionAux.url == url,
SubmissionAux.body == body
@ -710,7 +679,6 @@ def submit_post(v):
url=url,
body=request.form.get(
"body", ""),
b=board
), 400),
"api": lambda: ({"error": "ToS violation"}, 400)
}
@ -727,65 +695,6 @@ def submit_post(v):
embed = None
# board
board_name = request.form.get("board", "general")
board_name = board_name.lstrip("+")
board_name = board_name.strip()
board = g.db.query(Board).first()
if not board:
return {"html": lambda: (render_template("submit.html",
v=v,
error=f"Please enter a Guild to submit to.",
title=title,
url=url, body=request.form.get(
"body", ""),
b=None
), 403),
"api": lambda: (jsonify({"error": f"403 Forbidden - +{board.name} has been banned."}))
}
if board.is_banned:
return {"html": lambda: (render_template("submit.html",
v=v,
error=f"+{board.name} has been banned.",
title=title,
url=url, body=request.form.get(
"body", ""),
b=None
), 403),
"api": lambda: (jsonify({"error": f"403 Forbidden - +{board.name} has been banned."}))
}
if board.has_ban(v):
return {"html": lambda: (render_template("submit.html",
v=v,
error=f"You are exiled from +{board.name}.",
title=title,
url=url, body=request.form.get(
"body", ""),
b=None
), 403),
"api": lambda: (jsonify({"error": f"403 Not Authorized - You are exiled from +{board.name}"}), 403)
}
if (board.restricted_posting or board.is_private) and not (
board.can_submit(v)):
return {"html": lambda: (render_template("submit.html",
v=v,
error=f"You are not an approved contributor for +{board.name}.",
title=title,
url=url,
body=request.form.get(
"body", ""),
b=None
), 403),
"api": lambda: (jsonify({"error": f"403 Not Authorized - You are not an approved contributor for +{board.name}"}), 403)
}
# similarity check
now = int(time.time())
cutoff = now - 60 * 60 * 24
@ -857,7 +766,6 @@ def submit_post(v):
user_id=2317,
target_submission_id=post.id,
kind="ban_post",
board_id=post.board_id,
note="spam"
)
g.db.add(ma)
@ -874,7 +782,6 @@ def submit_post(v):
url=url,
body=request.form.get(
"body", ""),
b=board
), 400),
"api": lambda: ({"error": "10000 character limit for text body."}, 400)
}
@ -888,7 +795,6 @@ def submit_post(v):
url=url,
body=request.form.get(
"body", ""),
b=board
), 400),
"api": lambda: ({"error": "2048 character limit for URLs."}, 400)
}
@ -920,7 +826,6 @@ def submit_post(v):
url=url,
body=request.form.get(
"body", ""),
b=board
), 403),
"api": lambda: ({"error": reason}, 403)
}
@ -961,7 +866,6 @@ def submit_post(v):
url=url,
body=request.form.get(
"body", ""),
b=board
), 400),
"api": lambda: ({"error": f"The link `{badlink.link}` is not allowed. Reason: {badlink.reason}"}, 400)
}
@ -977,11 +881,7 @@ def submit_post(v):
private=bool(request.form.get("private","")),
author_id=v.id,
domain_ref=domain_obj.id if domain_obj else None,
board_id=board.id,
original_board_id=board.id,
over_18=bool(request.form.get("over_18","")),
is_nsfl=bool(request.form.get("is_nsfl","")),
post_public=not board.is_private,
repost_id=repost.id if repost else None,
is_offensive=False,
app_id=v.client.application.id if v.client else None,
@ -1037,7 +937,6 @@ def submit_post(v):
title=title,
body=request.form.get(
"body", ""),
b=board
), 400),
"api": lambda: ({"error": f"Image files only"}, 400)
}
@ -1054,7 +953,6 @@ def submit_post(v):
# spin off thumbnail generation and csam detection as new threads
if (new_post.url or request.files.get('file')) and (v.is_activated or request.headers.get('cf-ipcountry')!="T1"): thumbs(new_post)
# expire the relevant caches: front page new, board new
cache.delete_memoized(frontlist)
cache.delete_memoized(User.userpagelisting)
g.db.commit()
@ -1091,9 +989,6 @@ def submit_post(v):
parent_fullname=new_post.fullname,
level=1,
over_18=False,
is_nsfl=False,
is_offensive=False,
original_board_id=1,
is_bot=True,
app_id=None,
creation_region=request.headers.get("cf-ipcountry"),
@ -1131,9 +1026,6 @@ def submit_post(v):
parent_fullname=new_post.fullname,
level=1,
over_18=False,
is_nsfl=False,
is_offensive=False,
original_board_id=1,
is_bot=True,
app_id=None,
creation_region=request.headers.get("cf-ipcountry")
@ -1205,7 +1097,7 @@ def embed_post_pid(pid):
post = get_post(pid)
if post.is_banned or post.board.is_banned:
if post.is_banned:
abort(410)
return render_template("embeds/submission.html", p=post)
@ -1232,12 +1124,7 @@ def toggle_post_nsfw(pid, v):
post = get_post(pid)
mod=post.board.has_mod(v)
if not post.author_id == v.id and not v.admin_level >= 3 and not mod:
abort(403)
if post.board.over_18 and post.over_18:
if not post.author_id == v.id and not v.admin_level >= 3:
abort(403)
post.over_18 = not post.over_18
@ -1248,7 +1135,6 @@ def toggle_post_nsfw(pid, v):
kind="set_nsfw" if post.over_18 else "unset_nsfw",
user_id=v.id,
target_submission_id=post.id,
board_id=post.board.id,
)
g.db.add(ma)

View File

@ -55,7 +55,7 @@ def searchlisting(criteria, v=None, page=1, t="None", sort="top", b=None):
posts=posts.filter(
Submission.author_id==get_user(criteria['author']).id,
User.is_private==False,
User.is_deleted==False
User.deleted_utc==False
)
if 'domain' in criteria:

View File

@ -392,16 +392,6 @@ def settings_delete_banner(v):
msg="Banner successfully removed.")
@app.route("/settings/toggle_collapse", methods=["POST"])
@auth_required
@validate_formkey
def settings_toggle_collapse(v):
session["sidebar_collapsed"] = not session.get("sidebar_collapsed", False)
return "", 204
@app.route("/settings/read_announcement", methods=["POST"])
@auth_required
@validate_formkey

View File

@ -2,6 +2,65 @@ from drama.mail import *
from drama.__main__ import app, limiter
from drama.helpers.alerts import *
@app.route("/badmins", methods=["GET"])
@app.route("/api/vue/admin/mods", methods=["GET"])
@app.route("/api/v1/admin/mods", methods=["GET"])
@auth_desired
@public("read")
def badmins(v):
badmins = g.db.query(User).filter_by(admin_level=6).all()
return {
"html":lambda:render_template("mods.html", v=v, badmins=badmins),
"api":lambda:jsonify({"data":[x.json for x in badmins]})
}
@app.route("/log", methods=["GET"])
@app.route("/api/v1/mod_log", methods=["GET"])
@auth_desired
@api("read")
def log(v):
page=int(request.args.get("page",1))
if v and v.admin_level == 6: actions = g.db.query(ModAction).order_by(ModAction.id.desc()).offset(25 * (page - 1)).limit(26).all()
else: actions=g.db.query(ModAction).filter(ModAction.kind!="shadowban", ModAction.kind!="unshadowban").order_by(ModAction.id.desc()).offset(25*(page-1)).limit(26).all()
actions=[i for i in actions]
next_exists=len(actions)==26
actions=actions[0:25]
return {
"html":lambda:render_template(
"modlog.html",
v=v,
actions=actions,
next_exists=next_exists,
page=page
),
"api":lambda:jsonify({"data":[x.json for x in actions]})
}
@app.route("/log/<aid>", methods=["GET"])
@auth_desired
def log_item(aid, v):
action=g.db.query(ModAction).filter_by(id=base36decode(aid)).first()
if not action:
abort(404)
if request.path != action.permalink:
return redirect(action.permalink)
return render_template("modlog.html",
v=v,
actions=[action],
next_exists=False,
page=1,
action=action
)
@app.route("/sex")
def index():
return render_template("index.html", **{"greeting": "Hello from Flask!"})

View File

@ -264,7 +264,7 @@ def u_username(username, v=None):
g.db.add(view)
if u.is_deleted and (not v or v.admin_level < 3):
if u.deleted_utc > 0 and (not v or v.admin_level < 3):
return {'html': lambda: render_template("userpage_deleted.html",
u=u,
v=v),
@ -407,7 +407,6 @@ def u_username_comments(username, v=None):
is_following = (v and user.has_follower(v))
board = g.db.query(Board).first()
return {"html": lambda: render_template("userpage_comments.html",
u=user,
v=v,

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Drama</title>
<meta name="description" content="Drama Help">

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Drama</title>
<meta name="description" content="Drama Help">

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>API App Administration</title>
<meta name="description" content="Drama Help">

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Drama</title>
<meta name="description" content="Drama Help">

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>API App Administration</title>
<meta name="description" content="Drama Help">

View File

@ -6,8 +6,6 @@
{% block pagetype %}message{% endblock %}
{% block sidebarblock %}{% endblock %}
{% block content %}
{% if error %}

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Drama</title>
<meta name="description" content="Drama Help">

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Drama</title>
<meta name="description" content="Drama Help">

View File

@ -5,7 +5,6 @@
{% block banner %}{% endblock %}
{% block mobileBanner %}{% endblock %}
{% block desktopBanner %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block desktopUserBanner %}{% endblock %}
{% block mobileUserBanner %}{% endblock %}

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Image Ban</title>
<meta name="description" content="Image Ban">

View File

@ -5,7 +5,6 @@
{% block banner %}{% endblock %}
{% block mobileBanner %}{% endblock %}
{% block desktopBanner %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block desktopUserBanner %}{% endblock %}
{% block mobileUserBanner %}{% endblock %}
@ -41,22 +40,6 @@
{% endblock %}
{% block sidebarblock %}
<pre></pre>
<pre></pre>
<div class="sidebar-section sidebar-profile-basic">
<div class="body">
<pre>
</pre>
<h5 class="h6 d-inline-block mb-0" style="color:black">Image Posts</h5>
</div>
</div>
{% endblock %}
{% block content %}
<!-- Post filters bar visible only on medium devices or larger-->

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Purge Image</title>
<meta name="description" content="Purge Image">

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Drama</title>
<meta name="description" content="Drama Help">

View File

@ -6,14 +6,4 @@
{% include "user_listing.html" %}
{% endblock %}
{% block sidebarblock %}
<div class="sidebar-section sidebar-about">
<div class="title">All Users</div>
<div class="body">
</div>
</div>
{% endblock %}
{% block navbar %}{% endblock %}
{% block sidebar %}{% endblock %}
{% block navbar %}{% endblock %}

View File

@ -1,8 +1,5 @@
{% extends "default.html" %}
{% block sidebarblock %}{% endblock %}
{% block sidebarLeftblock %}{% endblock %}
{% block title %}
<title>Drama</title>
<meta name="description" content="Drama Help">

View File

@ -0,0 +1,34 @@
{% extends "settings2.html" %}
{% block pagetitle %}Badmins{% endblock %}
{% block content %}
<div class="row justify-content-around">
<div class="card mb-5">
<table class="table table-hover rounded mb-0">
<thead class="thead-dark">
<tr>
<th scope="col">User</th>
<th scope="col">Badmin Since</th>
<th scope="col"></th>
</tr>
</thead>
<tbody class="text-muted">
{% for badmin in badmins %}
<tr style="font-size:12.5px">
<td>
<a href="{{badmin.permalink}}">
<img src="{{badmin.profile_url}}" class="profile-pic-20 align-top mr-2">@{{badmin.username}}</a>
</td>
</tr>
{% else %}
<td>There are no badmins.</td>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "guild_settings.html" %}
{% extends "settings2.html" %}
{% block content %}
<table class="table table-striped mb-5">

View File

@ -1,4 +1,4 @@
{% extends "guild_settings.html" %}
{% extends "settings2.html" %}
{% block pagetitle %}Blocks{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "guild_settings.html" %}
{% extends "settings2.html" %}
{% block pagetitle %}Changelog{% endblock %}

View File

@ -85,7 +85,7 @@
{% if c.parent_submission %}
{% if c.author_id==v.id and c.child_comments and is_notification_page%}
<span class="font-weight-bold">Comment {{'Replies' if (c.child_comments | length)>1 else 'Reply'}}: <a href="{{c.post.permalink}}">{{c.post.title | safe}}</a></span>
{% elif c.post.author_id==v.id and c.is_top_level and is_notification_page%}
{% elif c.post.author_id==v.id and c.level == 1 and is_notification_page%}
<span class="font-weight-bold">Post Reply: <a href="{{c.post.permalink}}">{{c.post.title | safe}}</a></span>
{% elif is_notification_page and c.parent_submission in v.subscribed_idlist() %}
<span class="font-weight-bold">Subscribed Thread: <a href="{{c.post.permalink}}">{{c.post.title | safe}}</a></span>
@ -116,7 +116,6 @@
{% if c.banaward %} <i class="fas fa-gavel text-danger" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="Given the 1-day ban award by @{{c.banaward}}"></i>&nbsp;{% endif %}
{% if c.active_flags %}&nbsp;<a class="btn btn-primary" style="padding:1px 5px; font-size:10px;" href="javascript:void(0)" onclick="document.getElementById('flaggers-{{c.id}}').classList.toggle('d-none')">{{c.active_flags}} Reports</a>&nbsp;{% endif %}
{% if c.over_18 %}<span class="badge badge-danger text-small-extra mr-1">+18</span>&nbsp;{% endif %}
{% if c.is_nsfl %}<span class="badge text-black border-danger border-1 text-small-extra">nsfl</span>&nbsp;{% endif %}
{% if v and v.admin_level==6 and c.author.shadowbanned %}<i class="fas fa-user-times text-admin" data-toggle="tooltip" data-placement="bottom" title="Shadowbanned user"></i>&nbsp;{% endif %}
{% if c.is_pinned %}<i class="text-admin fas fa-thumbtack fa-rotate--45" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="Pinned comment"></i>&nbsp;{% endif %}
{% if c.distinguish_level %}<i class="fas fa-broom text-admin" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="Drama Badmin, speaking officially"></i>&nbsp;{% endif %}
@ -125,7 +124,7 @@
{% if c.is_blocking %}<i class="fas fa-user-minus text-warning" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="You're blocking this user, but you can see this comment because {{'it\'s an admin comment' if c.distinguish_level else 'you\'re an admin'}}."></i>&nbsp;{% endif %}
{% if c.is_blocked %}<i class="fas fa-user-minus text-danger" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="This user is blocking you, but you can see this comment because {{'it\'s an admin comment' if c.distinguish_level else 'you\'re an admin'}}."></i>&nbsp;{% endif %}
{% if c.author.is_deleted %}[deleted account]{% else %}<a {% if c.author.animatedname %}class="{% if c.author.patron %}patron{% else %}leaderboard{% endif %}"{% endif %} style="color:#{{c.author.namecolor}}; font-size:12px; font-weight:bold;" href="/@{{c.author.username}}"><img src="{{ c.author.profile_url }}" class="profile-pic-25 mr-2"/>{{c.author.username}}</a>{% if c.author.customtitle %}&nbsp;<bdi style="color: #{{c.author.titlecolor}}">&nbsp;{{c.author.customtitle | safe}}</bdi>{% endif %}{% endif %}
{% if c.author.deleted_utc > 0 %}[deleted account]{% else %}<a {% if c.author.animatedname %}class="{% if c.author.patron %}patron{% else %}leaderboard{% endif %}"{% endif %} style="color:#{{c.author.namecolor}}; font-size:12px; font-weight:bold;" href="/@{{c.author.username}}"><img src="{{ c.author.profile_url }}" class="profile-pic-25 mr-2"/>{{c.author.username}}</a>{% if c.author.customtitle %}&nbsp;<bdi style="color: #{{c.author.titlecolor}}">&nbsp;{{c.author.customtitle | safe}}</bdi>{% endif %}{% endif %}
<span id="timestamp-{{c.id}}" data-toggle="tooltip" data-placement="bottom" title="" class="time-stamp">&nbsp;{{c.age_string}}</span>
{% if c.edited_utc %}
@ -163,7 +162,7 @@
</div>
{% if c.parent_submission %}
{% if v and v.id==c.author_id and (standalone or is_allowed_to_comment) %}
{% if v and v.id==c.author_id %}
<div id="comment-edit-{{c.base36id}}" class="d-none comment-write collapsed child">
<form id="comment-edit-form-{{c.base36id}}" action="/edit_comment/{{c.base36id}}" method="post" class="input-group" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v.formkey}}">
@ -257,10 +256,8 @@
{% endif %}
{% if v %}
{% if standalone or is_allowed_to_comment %}
<li class="list-inline-item text-muted"><a href="javascript:void(0)" onclick="document.getElementById('reply-to-{{c.base36id}}').classList.remove('d-none')"><i class="fas fa-reply"
aria-hidden="true"></i><span class="d-none d-md-inline-block">Reply</span></a></li>
{% endif %}
{% endif %}
<li class="list-inline-item text-muted d-none d-md-inline-block"><a href="{{c.permalink}}?context=99#context"{% if c.author.is_private %} rel="nofollow"{% endif %}><i class="fas fa-book-open"></i>Context</a></li>
@ -280,7 +277,7 @@
<div class="dropdown-menu border-0 shadow" aria-labelledby="dropdownMoreLink">
{% if not (v and v.id==c.author_id) %}
{% if v %}
<a class="dropdown-item" href="javascript:void(0)" data-toggle="modal" data-target="#reportCommentModal" onclick="report_commentModal('{{c.base36id}}','{{c.author.username if not c.author.is_deleted else '[is_deleted]'}}',)"><i class="fas fa-flag fa-fw"></i>Report</a>
<a class="dropdown-item" href="javascript:void(0)" data-toggle="modal" data-target="#reportCommentModal" onclick="report_commentModal('{{c.base36id}}','{{c.author.username if not c.author.deleted_utc > 0 else '[deleted_utc]'}}',)"><i class="fas fa-flag fa-fw"></i>Report</a>
{% else %}
<a class="dropdown-item" href="javascript:void(0)" data-toggle="modal" data-target="#reportCommentModal" onclick=""><i class="fas fa-flag fa-fw"></i>Flag</a>
{% endif %}
@ -332,12 +329,12 @@
{% if v and c.post and (v.admin_level == 6 or v.id == c.post.author_id) %}
<div class="dropdown-divider"></div>
{% if c.is_top_level %}
{% if c.level == 1 %}
<a class="dropdown-item text-info" id="pin-comment-{{c.base36id}}" href="javascript:void(0)" data-dismiss="modal" data-target="#actionsModal-{{c.base36id}}" onclick="post('/comment_pin/{{c.base36id}}', function(){window.location.reload(true);})"><i class="fas fa-thumbtack fa-rotate--45 fa-fw text-info"></i>{{"Unpin" if c.is_pinned else "Pin"}}</a>
{% endif %}
{% endif %}
{% if v and c.post and v.admin_level == 6 %}
{% if v.id!=c.author_id and not c.author.is_deleted and c.original_board_id==c.post.board_id %}
{% if v.id!=c.author_id %}
{% if c.author.is_banned %}
<a class="dropdown-item text-danger" id="unexile-comment-{{c.base36id}}" href="javascript:void(0)" onclick="post_toast('/api/unban_user/{{c.author_id}}?toast=1')"><i class="fas fa-user-minus fa-fw text-danger"></i>Unban user</a>
{% else %}
@ -474,7 +471,7 @@
<li class="list-group-item"><a href="{{c.permalink}}?context=5#context"{% if c.author.is_private %} rel="nofollow"{% endif %}><i class="fas fa-dna"></i>Context</a></li>
{% if not (v and v.id==c.author_id) %}
<li class="list-group-item"><a href="javascript:void(0)" data-toggle="modal" data-dismiss="modal" data-target="#reportCommentModal" onclick="report_commentModal('{{c.base36id}}','{{c.author.username if not c.author.is_deleted else '[is_deleted]'}}')" class="d-block"><i class="fas fa-flag"></i>Report</a></li>
<li class="list-group-item"><a href="javascript:void(0)" data-toggle="modal" data-dismiss="modal" data-target="#reportCommentModal" onclick="report_commentModal('{{c.base36id}}','{{c.author.username if not c.author.deleted_utc > 0 else '[deleted_utc]'}}')" class="d-block"><i class="fas fa-flag"></i>Report</a></li>
{% endif %}
{% if v and c.parent_submission and c.author_id==v.id %}
@ -518,13 +515,13 @@
{% endif %}
{% if v and c.post and (v.admin_level == 6 or v.id == c.post.author_id) %}
{% if c.is_top_level %}
{% if c.level == 1 %}
<li class="list-group-item"><a class="d-block text-info" id="pin-comment-{{c.base36id}}" href="javascript:void(0)" data-dismiss="modal" data-target="#actionsModal-{{c.base36id}}" onclick="post('/comment_pin/{{c.base36id}}', function(){window.location.reload(true);})"><i class="fas fa-thumbtack fa-rotate--45 text-info"></i>{{'Unpin' if c.is_pinned else 'Pin'}}</a>
</li>
{% endif %}
{% endif %}
{% if v and c.post and v.admin_level == 6 %}
{% if c.author_id != v.id and not c.author.is_deleted and c.original_board_id==c.post.board_id %}
{% if c.author_id != v.id and not c.author.deleted_utc > 0 %}
{% if c.author.is_banned %}
<li class="list-group-item"><a class="d-block text-danger" id="unexile-comment2-{{c.base36id}}" href="javascript:void(0)" onclick="post_toast('/api/unban_user/{{c.author_id}}?toast=1')"><i class="fas fa-user-minus fa-fw text-danger"></i>Unban user</a></li>
{% else %}

View File

@ -5,8 +5,6 @@
<meta name="description" content="Contact Drama Admins">
{% endblock %}
<!-- Left Sidebar -->
{% block content %}

View File

@ -264,7 +264,7 @@
// Flag Submission
report_postModal = function(id, author, board) {
report_postModal = function(id, author) {
document.getElementById("post-author").textContent = author;
@ -461,11 +461,11 @@
}
herald_comment=function(bid,cid){
herald_comment=function(cid){
var xhr = new XMLHttpRequest();
xhr.open("post", "/mod/distinguish_comment/"+bid+'/'+cid);
xhr.open("post", "/admin/distinguish_comment/"+cid);
var form = new FormData();
@ -1005,11 +1005,6 @@
<div class="container">
<div class="row justify-content-around" id="main-content-row">
{% block leftSidebar %}
{% block leftSidebarBlock %}
{% endblock %}
{% endblock %}
<div class="col h-100 {% block customPadding %}{% if not '/message' in request.path %}custom-gutters{% endif %}{% endblock %}" id="main-content-col">
{% block desktopUserBanner %}
@ -1045,9 +1040,6 @@
{% block reportCommentModal %}
{% endblock %}
{% block guildModal %}
{% endblock %}
{% block GIFtoast %}
{% endblock %}

View File

@ -59,15 +59,6 @@
<li class="list-inline-item d-none d-md-inline-block mr-2">
<span id="comment-{{c.base36id}}-score-none"class="d-none text-black">{{score}}</span> </li>
{% if not c.board.downvotes_disabled %}
<li id="comment-{{c.base36id}}-down" class="list-inline-item arrow-down d-none d-md-inline-block">
</li>
{% endif %}
<li class="list-inline-item text-muted d-none d-md-inline-block"><a href="javascript:void(0);" role="button" class="copy-link" data-clipboard-text="{{c.permalink | full_link}}"><i class="fas fa-link"></i><span>Copy link</span></a>
</li>
<li class="list-inline-item d-none d-md-inline-block">

View File

@ -1,6 +1,3 @@
{% extends "default.html" %}
{% block sidebarLeftblock %}{% endblock %}
{% block sidebar %}{% endblock %}
{% block customPadding %}{% endblock %}

View File

@ -1,37 +0,0 @@
{% extends "home.html" %}
{% block sidebarLeftblock %}
<div class="sidebar-section sidebar-dashboard">
<div class="title">Dashboard</div>
<div class="body">
<ul class="no-bullets dashboard-list pl-0">
<li class="dashboard-item">
<a class="dashboard-link" href="/"><i class="fas fa-home-alt fa-width-rem"></i>Home</a>
</li>
<li class="dashboard-item">
<a class="dashboard-link" href="/guilds"><i class="fas fa-chess-rook fa-width-rem"></i>Guilds</a>
</li>
<li class="dashboard-item">
<a class="dashboard-link active" href="/subscriptions"><i class="fas fa-heart fa-width-rem"></i>Subscriptions</a>
</li>
<li class="dashboard-item">
<a class="dashboard-link" href="/browse"><i class="fas fa-binoculars fa-width-rem"></i>Browse</a>
</li>
</ul>
</div>
</div>
{% endblock %}
{% block sidebarblock %}
<div class="sidebar-section sidebar-about">
<div class="body text-primary text-center">
<i class="fad fa-user-friends" style="font-size: 4rem;"></i>
</div>
<div class="title">User Subscriptions</div>
<div class="body">
<p>The users you are subscribed to.</p>
</div>
</div>
{% endblock %}

View File

@ -1,7 +1,7 @@
<!-- Badmin Ban user Modal -->
<div class="modal fade" id="guildmasterBanModal" tabindex="-1" role="dialog" aria-labelledby="guildmasterBanModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<form action="/mod/exile/{{b.base36id}}" method="post">
<form action="/admin/exile/{{b.base36id}}" method="post">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Ban user from +{{b.name}}</h5>

View File

@ -1,14 +0,0 @@
<div class="modal fade show" id="gmInvitationModal" tabindex="-1" role="dialog" aria-labelledby="gmInvitationModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body text-center">
<div class="py-4">
<i class="fad fa-crown text-warning" style="font-size: 3.5rem;"></i>
</div>
<p>You've been invited to be an admin.</p>
<a class="btn btn-primary btn-block" href="javascript:void(0)" onclick="post('/mod/accept/{{b.base36id}}', callback=function(){window.location.reload(true)})">Accept Invitation</a>
<button type="button" class="btn btn-link text-muted mt-2" data-dismiss="modal">Ignore</button>
</div>
</div>
</div>
</div>

View File

@ -1,89 +0,0 @@
{% set board = b if b else p.board %}
<div class="modal fade" id="guildDetailsModal" tabindex="-1" role="dialog" aria-labelledby="guildDetailsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header position-relative border-0" style="background-image: url({{board.banner_url}}); height: 100px; overflow: hidden; object-fit: cover; background-size: cover;">
<div class="white-overlay position-absolute w-100 h-100" style="left: 0; top: 0; background-color: rgba(255, 255, 255, 0); background-image: linear-gradient(180deg, #cfcfcf00 25%, #cfcfcf 100%);"></div>
<div style="z-index: 1" class="d-flex align-items-center my-auto">
<img src="{{board.profile_url}}" class="profile-pic-65 bg-white mr-3"><h5 class="modal-title h4">+{{board.name}}
{% if board.over_18 %}
<span class="badge badge-danger text-small ml-2" data-toggle="tooltip" data-placement="bottom" title="This guild contains adult content">+18</span>
{% endif %}
</h5>
</div>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true"><i class="far fa-times"></i></span>
</button>
</div>
<div class="modal-body p-0">
<div class="d-flex justify-content-between align-items-center border-bottom p-3">
<div class="">
<div class="font-weight-bold text-small text-uppercase text-muted mb-1" style="letter-spacing: 0.025rem;">Founded</div>
<div data-toggle="tooltip" data-placement="bottom" title="{{board.created_date}}">{{board.age_string}}</div>
</div>
<div class="">
<div class="font-weight-bold text-small text-uppercase text-muted mb-1" style="letter-spacing: 0.025rem;">Badmins</div>
<div>{{board.mods_count}}</div>
</div>
<div class="">
<div class="font-weight-bold text-small text-uppercase text-muted mb-1" style="letter-spacing: 0.025rem;">Members</div>
<div>{{board.subscriber_count}}</div>
</div>
</div>
<div class="p-3">
<div class="h6">About</div>
{{board.description_html | safe}}
</div>
<!-- hide rules thingy
<div class="p-3">
<div class="h6 text-danger">Guild rules</div>
<ol class="pl-0 mb-0">
<li>
<span class="h6">Comply with US law</span>
<p style="padding: 0 0 0 22px;">Do not post copyrighted material that you are not authorized to distribute. Do not post anything not legal to publish within, or export from, the United States of America.
</p>
</li>
<li>
<span class="text-black font-weight-bold">Do not harass other users</span>
<p style="padding: 0 0 0 22px;">
Do not harass or threaten others on Drama. You are not allowed to share or publish personal information, either yours or another person.
</p>
</li>
<li>
<span class="text-black font-weight-bold">No inciting violence</span>
<p style="padding: 0 0 0 22px;">
No incitement, planning or execution of unlawful or violent activity. This does not include the exercise of human rights that may be considered unlawful in a user's home country but protected in the United States.
</p>
</li>
<li>
<span class="text-black font-weight-bold">Check for duplicates</span>
<p style="padding: 0 0 0 22px;">
Nobody likes reposts. Please make sure the content you're posting hasn't been posted on Drama before.
</p>
</li>
</ol>
</div>
-->
</div>
<div class="modal-footer" style="background-color: transparent; background-image: linear-gradient(180deg, #cfcfcf00 25%, #cfcfcf 100%);">
</div>
</div>
</div>
</div>

View File

@ -18,7 +18,7 @@
<a class="mobile-nav-icon d-block d-md-none" href="/admin"><i class="fas fa-crown align-middle text-gray-500"></i></a>
{% endif %}
{% if v %}
<a class="mobile-nav-icon d-block d-md-none" href="/submit{{'?guild='+b.name if b else ''}}"><i class="fas fa-feather-alt align-middle text-gray-500"></i></a>
<a class="mobile-nav-icon d-block d-md-none" href="/submit"><i class="fas fa-feather-alt align-middle text-gray-500"></i></a>
{% else %}
<a class="mobile-nav-icon d-block d-md-none" href="/login"><i class="fas fa-feather-alt align-middle text-gray-500"></i></a>
{% endif %}

View File

@ -4,8 +4,6 @@
<title>{{title}}</title>
{% endblock %}
{% block sidebar %}{% endblock %}
{% block pagetype %}message{% endblock %}
{% block customPadding %}{% endblock %}

View File

@ -2,15 +2,4 @@
{% block maincontent %}
{% include "user_listing.html" %}
{% endblock %}
{% block sidebarblock %}
<div class="sidebar-section sidebar-about">
<div class="title">Your Followed Users</div>
<div class="body">
<p>These are the users you follow.</p>
</div>
</div>
{% endblock %}
{% block sidebar %}{% endblock %}
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "guild_settings.html" %}
{% extends "settings2.html" %}
{% block pagetitle %}Moderation Log{% endblock %}
@ -39,7 +39,7 @@
</span>
<div class="text-muted pl-3">
<div>
{% if not ma.user.is_deleted %}
{% if not ma.user.deleted_utc > 0 %}
<a href="{{ma.user.permalink}}" class="font-weight-bold text-black" target="_self">@{{ma.user.username}}</a>
{% else %}
[deleted user]

View File

@ -1,338 +0,0 @@
{% extends "guild_settings.html" %}
{% block pagetitle %}Badmins{% endblock %}
{% block scripts %}
<script>
// Invite user to mod
function invite_mod_to_guild(boardid) {
var inviteForm = document.getElementById("invite-form");
var inviteError = document.getElementById("toast-error-message");
var usernameField = document.getElementById("invite-username");
var isValidUsername = usernameField.checkValidity();
username = usernameField.value;
if (isValidUsername) {
var xhr = new XMLHttpRequest();
xhr.open("post", "/mod/invite_mod");
xhr.withCredentials=true;
f=new FormData();
f.append("username", username);
f.append("formkey", formkey());
xhr.onload=function(){
if (xhr.status==204) {
window.location.reload(true);
}
else {
$('#toast-invite-error').toast('dispose');
$('#toast-invite-error').toast('show');
inviteError.textContent = JSON.parse(xhr.response)["error"];
}
}
xhr.send(f)
}
}
// Badmin Step Down Function
step_downModal = function(mod) {
document.getElementById("stepDownButton").onclick = function() {
this.innerHTML='<span class="spinner-border spinner-border-sm mr-2" role="status" aria-hidden="true"></span>Stepping down';
this.disabled = true;
post('/mod/{{b.base36id}}/remove/' + mod,
callback = function() {
location.reload();
}
)
}
};
</script>
{% if b.has_invite(v) %}
<script>
// Badmin Invitation Modal
$(window).on('load', function(){
$('#gmInvitationModal').modal('show');
});
</script>
{% endif %}
{% endblock %}
<!-- tabs container -->
{% block content %}
<div class="row justify-content-around">
<div class="col h-100">
{% if request.args.get('error') or error %}
<div class="alert alert-danger alert-dismissible fade show my-3" role="alert">
<i class="fas fa-exclamation-circle my-auto"></i>
<span>
{{error if error else request.args.get('error')}}
</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true"><i class="far fa-times"></i></span>
</button>
</div>
{% endif %}
{% if request.args.get('msg') or msg %}
<div class="alert alert-success alert-dismissible fade show my-3" role="alert">
<i class="fas fa-check-circle my-auto" aria-hidden="true"></i>
<span>
{{msg if msg else request.args.get('msg')}}
</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true"><i class="far fa-times"></i></span>
</button>
</div>
{% endif %}
<div class="row">
<div class="col" style="padding:0">
<div class="settings">
<div class="d-md-flex justify-content-between mb-3">
<div>
<h1>Badmins</h1>
</div>
<div class="mt-auto">
{% if me %}
<a href="javascript:void(0)" class="btn btn-outline-primary mr-2" data-toggle="modal" data-target="#stepDownModal" onclick="step_downModal('{{v.username}}')">Resign</a>
{% endif %}
{% if me and me.perm_full %}
<a href="javascript:void(0)" class="btn btn-primary" data-toggle="modal" data-target="#badminInviteModal"><i class="fas fa-plus mr-2"></i>Add badmin</a>
{% endif %}
</div>
</div>
<div class="card mb-5">
<table class="table table-hover rounded mb-0">
<thead class="thead-dark">
<tr>
<th scope="col">User</th>
<th scope="col">Badmin Since</th>
<th scope="col"></th>
</tr>
</thead>
<tbody class="text-muted">
{% for mod in b.mods_list %}
<tr style="font-size:12.5px">
<td>
<a href="{{mod.user.permalink}}">
<img src="{{mod.user.profile_url}}" class="profile-pic-20 align-top mr-2">@{{mod.user.username}}</a>
</td>
<td>{{mod.created_date}}</td>
<td>
{% if me and me.id < mod.id %}
<div class="dropdown float-right dropdown-actions">
<a href="#" role="button" id="dropdownMoreLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" style="line-height: 0;">
<i class="fas fa-ellipsis-h text-muted"></i>
</a>
<div class="dropdown-menu border-0 shadow dropdown-menu-right mt-2" aria-labelledby="dropdownMoreLink" x-placement="bottom-end" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(21px, 18px, 0px);">
<a class="dropdown-item" href="javascript:void(0)" onclick="post('/mod/{{b.base36id}}/remove/{{mod.user.username}}', callback=function(){window.location.reload(true)})"><i class="fas fa-trash-alt"></i>Remove badmin</a>
</div>
</div>
{% endif %}
</td>
</tr>
{% else %}
<td>There are no badmins.</td>
{% endfor %}
</tbody>
</table>
</div>
<h2 class="h5" name="guild_name">Pending</h2>
<p class="text-small text-muted">These users have been invited to be badmins.</p>
<div class="card mb-5">
<table class="table table-hover rounded mb-0">
<thead class="thead-dark">
<tr>
<th scope="col">User</th>
<th scope="col">Invited On</th>
<th scope="col"></th>
</tr>
</thead>
<tbody class="text-muted">
{% for m in b.mod_invites %}
<tr>
<td>
<a href="{{m.user.permalink}}">
<img src="{{m.user.profile_url}}" class="profile-pic-20 align-top mr-2">@{{m.user.username}}</a>
</td>
<td>{{m.created_date}}</td>
<td>
<div class="dropdown float-right dropdown-actions">
<a href="#" role="button" id="dropdownMoreLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" style="line-height: 0;">
<i class="fas fa-ellipsis-h text-muted"></i>
</a>
<div class="dropdown-menu border-0 shadow dropdown-menu-right mt-2" aria-labelledby="dropdownMoreLink" x-placement="bottom-end" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(21px, 18px, 0px);">
<a class="dropdown-item text-danger" href="javascript:void(0)" onclick="post('/mod/{{b.base36id}}/rescind/{{m.user.username}}', callback=function(){window.location.reload(true)})"><i class="fas fa-times text-danger fa-fw"></i>Cancel invite</a>
</div>
</div>
</td>
</tr>
{% else %}
<td>There are no badmin invitations.</td>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Badmin Invite User Modal -->
<div class="modal fade" id="badminInviteModal" tabindex="-1" role="dialog" aria-labelledby="badminInviteModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<form action="/mod/invite_mod" id="invite-form" method="post" onsubmit="return false;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Invite user to be badmin</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true"><i class="far fa-times"></i></span>
</button>
</div>
<div class="modal-body">
<p style="padding-left:15px"> Users invited will need to accept their invite. They will receive a notification shortly after you invite them.</p>
<input type="hidden" name="formkey" value="{{v.formkey}}">
<input type="text" name="username" placeholder="enter username" id="invite-username" class="form-control" maxlength=25 required>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-link text-muted" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="inviteUserButton" onclick="invite_mod_to_guild('{{b.base36id}}')">Invite user</button>
</div>
</div>
</form>
</div>
</div>
<!-- Badmin Step Down Modal -->
<div class="modal fade" id="stepDownModal" tabindex="-1" role="dialog" aria-labelledby="stepDownModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Step down as badmin</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true"><i class="far fa-times"></i></span>
</button>
</div>
<div class="modal-body">
<p>If you step down as badmin, you will lose full permissions to moderate this guild. This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-link text-muted" data-dismiss="modal">Cancel</button>
<button class="btn btn-danger" onclick="post('/mod/{{b.base36id}}/step_down', callback=function(){window.location.reload(true)})">Step down</button>
</div>
</div>
</div>
</div>
<!-- Badmin Perms Edit Modal -->
<div class="modal fade" id="editPermsModal" tabindex="-1" role="dialog" aria-labelledby="stepDownModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<form action="/+{{b.name}}/mod/edit_perms" method="post">
<div class="modal-header">
<h5 class="modal-title">Edit badmin permissions on @<span id="permedit-user"></span></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true"><i class="far fa-times"></i></span>
</button>
</div>
<div class="modal-body">
<input name="formkey" type="hidden" value="{{v.formkey}}">
<input id="edit-perm-username" name="username" type="hidden" value="">
<div class="form-group">
<label class="custom-control custom-checkbox" for="check-perm-full">
<input type="checkbox" id="check-perm-full" class="custom-control-input perm-box" onchange="permfull()" data-perm="full" name="perm_full" value="full">
<span class="custom-control-label">full</span>
</label>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox" for="check-perm-access">
<input type="checkbox" id="check-perm-access" class="custom-control-input perm-box" onchange="permother()" data-perm="access" name="perm_access" value="access">
<span class="custom-control-label">access</span>
</label>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox" for="check-perm-appearance">
<input type="checkbox" id="check-perm-appearance" class="custom-control-input perm-box" onchange="permother()" data-perm="appearance" name="perm_appearance" value="appearance">
<span class="custom-control-label">appearance</span>
</label>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox" for="check-perm-config">
<input type="checkbox" id="check-perm-config" class="custom-control-input perm-box" onchange="permother()" data-perm="config" name="perm_config" value="config">
<span class="custom-control-label">config</span>
</label>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox" for="check-perm-content">
<input type="checkbox" id="check-perm-content" class="custom-control-input perm-box" onchange="permother()" data-perm="content" name="perm_content" value="content">
<span class="custom-control-label">content</span>
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-link text-muted" data-dismiss="modal">Cancel</button>
<input type="submit" class="btn btn-danger" value="Save Changes">
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block errorToasts %}
<div class="toast error" id="toast-invite-error" role="alert" aria-live="assertive" aria-atomic="true" data-animation="true" data-autohide="true" data-delay="5000">
<div class="toast-body text-center">
<i class="fas fa-exclamation-circle text-danger mr-2"></i><span id="toast-error-message">Error. Please try again.</span>
</div>
</div>
{% endblock %}
{% block invitationModal %}
{% if b.has_invite(v) %}
{% include "gm_invitation_modal.html" %}
{% endif %}
{% endblock %}

View File

@ -34,7 +34,7 @@ Send your user to `https://rdrama.net/oauth/authorize`, with the following URL p
* `client_id` - Your application's Client ID
* `redirect_uri` - The redirect URI (or one of the URIs) specified in your application information. Must not use HTTP unless using localhost (use HTTPS instead).
* `state` - This is your own anti-cross-site-forgery token. We don't do anything with this, except give it to the user to pass back to you later. You are responsible for generating and validating the state token.
* `scope` - A comma-separated list of permission scopes that you are requesting. Valid scopes are: `identity`, `create`, `read`, `update`, `delete`, `vote`, and `guildmaster`.
* `scope` - A comma-separated list of permission scopes that you are requesting. Valid scopes are: `identity`, `create`, `read`, `update`, `delete` and `vote`.
* `permanent` - optional. Set to `true` if you are requesting indefinite access. Omit if not.
If done correctly, the user will see that your application wants to access their Drama account, and be prompted to approve or deny the request.

View File

@ -9,7 +9,6 @@
{% block listinglength %}{{users | length}}{% endblock %}
{% block pagenav %}
{% if boards %}
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm mb-0">
<li class="page-item{% if page==1 %} disabled{% endif %}">
@ -22,5 +21,4 @@
</li>
</ul>
</nav>
{% endif %}
{% endblock %}

View File

@ -20,7 +20,7 @@
{% endif %}
<div id="post-{{p.base36id}}" class="card{% if p.is_banned %} banned{% endif %}{% if p.deleted_utc > 0 %} deleted{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}{% if p.over_18 %} nsfw{% endif %}{% if p.is_nsfl %} nsfl {% endif %}">
<div id="post-{{p.base36id}}" class="card{% if p.is_banned %} banned{% endif %}{% if p.deleted_utc > 0 %} deleted{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}{% if p.over_18 %} nsfw{% endif %}">
<div class="d-flex flex-row-reverse flex-md-row flex-nowrap justify-content-end">
@ -137,7 +137,7 @@
{% if p.private %}<span class="badge border-warning border-1 text-small-extra">unlisted</span>{% endif %}
{% if p.is_repost %}&nbsp;<span class="badge border-warning border-1 text-small-extra"><a class="text-warning" href="{{p.reposts.permalink}}">repost</a></span>{% endif %}
{% if p.active_flags %}<a class="btn btn-primary" href="javascript:void(0)" style="padding:1px 5px; font-size:10px;" onclick="document.getElementById('flaggers-{{p.id}}').classList.toggle('d-none')">{{p.active_flags}} Reports</a>{% endif %}
{% if p.author.is_deleted %}[deleted account]{% else %}&nbsp;<a {% if p.author.animatedname %}class="{% if p.author.patron %}patron{% else %}leaderboard{% endif %}"{% endif %} href="{{p.author.permalink}}" style="color: #{{p.author.namecolor}}; font-weight: bold;" class="user-name"><img src="{{ p.author.profile_url }}" class="profile-pic-25 mr-2"/>{{p.author.username}}</a>{% if p.author.customtitle %}<bdi style="color: #{{p.author.titlecolor}}">&nbsp;&nbsp;{{p.author.customtitle | safe}}</bdi>{% endif %}{% endif %}
{% if p.author.deleted_utc > 0 %}[deleted account]{% else %}&nbsp;<a {% if p.author.animatedname %}class="{% if p.author.patron %}patron{% else %}leaderboard{% endif %}"{% endif %} href="{{p.author.permalink}}" style="color: #{{p.author.namecolor}}; font-weight: bold;" class="user-name"><img src="{{ p.author.profile_url }}" class="profile-pic-25 mr-2"/>{{p.author.username}}</a>{% if p.author.customtitle %}<bdi style="color: #{{p.author.titlecolor}}">&nbsp;&nbsp;{{p.author.customtitle | safe}}</bdi>{% endif %}{% endif %}
<span data-toggle="tooltip" data-placement="bottom" id="timestamp-{{p.id}}" title="">&nbsp;{{p.age_string}}</span>
&nbsp;
({% if p.realurl(v) %}<a href="/search/posts/?q=domain%3A{{p.domain}}&sort=new&t=all" target="_blank">{{p.domain}}</a>{% else %}text post{% endif %})
@ -191,12 +191,12 @@
{% endif %}
{% if not (v and v.id==p.author_id) %}
<li class="list-inline-item"><a href="javascript:void(0)" data-toggle="modal" data-dismiss="modal" data-target="#reportPostModal" onclick="report_postModal('{{p.base36id}}', '{{p.author.username}}','{{p.board.name}}')"><i class="fas fa-flag"></i>Report</a></li>
<li class="list-inline-item"><a href="javascript:void(0)" data-toggle="modal" data-dismiss="modal" data-target="#reportPostModal" onclick="report_postModal('{{p.base36id}}', '{{p.author.username}}')"><i class="fas fa-flag"></i>Report</a></li>
{% endif %}
{% if v and v.id==p.author_id %}
<li class="list-inline-item"><a href="javascript:void(0)" onclick="post('/api/pin/{{p.base36id}}', function(){window.location.reload(true);})"><i class="fas fa-thumbtack"></i>{% if p.is_pinned %}Unpin from your profile{% else %}Pin to your profile{% endif %}</a></li>
{% if p.is_deleted %}
{% if p.deleted_utc > 0 %}
<li class="list-inline-item"><a href="javascript:void(0)" onclick="post('/undelete_post/{{p.base36id}}', function(){window.location.reload(true);})"><i class="fas fa-trash-alt"></i>Undelete</a></li>
{% else %}
<li class="list-inline-item"><a href="javascript:void(0)" data-toggle="modal" data-dismiss="modal" data-target="#deletePostModal" onclick="delete_postModal('{{p.base36id}}')"><i class="fas fa-trash-alt"></i>Delete</a></li>
@ -375,11 +375,11 @@
{% endif %}
{% if not (v and v.id==p.author_id) %}
<button class="btn btn-link btn-block btn-lg text-left text-muted" data-toggle="modal" data-dismiss="modal" data-target="#reportPostModal" onclick="report_postModal('{{p.base36id}}','{{'@'+p.author.username if not p.author.is_deleted else '[deleted account]'}}','{{p.board.name}}')" class="d-block"><i class="far fa-flag text-center text-muted mr-3"></i>Report</button>
<button class="btn btn-link btn-block btn-lg text-left text-muted" data-toggle="modal" data-dismiss="modal" data-target="#reportPostModal" onclick="report_postModal('{{p.base36id}}','{{'@'+p.author.username if not p.author.deleted_utc > 0 else '[deleted account]'}}')" class="d-block"><i class="far fa-flag text-center text-muted mr-3"></i>Report</button>
{% endif %}
{% if v and v.id==p.author_id %}
<button class="btn btn-link btn-block btn-lg text-info text-left" id="pin-post-{{p.base36id}}" href="javascript:void(0)" onclick="post('/api/pin/{{p.base36id}}',callback=function(){window.location.reload(true);})"><i class="fas fa-thumbtack text-center text-muted mr-3"></i>{% if p.is_pinned %}Unpin from your profile{% else %}Pin to your profile{% endif %}</button>
{% if p.is_deleted %}
{% if p.deleted_utc > 0 %}
<button class="btn btn-link btn-block btn-lg text-left text-muted" href="javascript:void(0)" onclick="post('/undelete_post/{{p.base36id}}', function(){window.location.reload(true);})"><i class="far fa-trash-alt text-center text-muted mr-3"></i>Undelete</button>
{% else %}
<button class="btn btn-link btn-block btn-lg text-left text-muted" data-toggle="modal" data-dismiss="modal" data-target="#deletePostModal" onclick="delete_postModal('{{p.base36id}}')"><i class="far fa-trash-alt text-center text-muted mr-3"></i>Delete</button>
@ -387,9 +387,7 @@
{% endif %}
{% if v and (v.id==p.author_id or v.admin_level>=3) %}
{% if not p.board.over_18 %}
<button class="btn btn-link btn-block btn-lg text-left text-muted" onclick="post('/api/toggle_post_nsfw/{{p.base36id}}', function(){window.location.reload(true);})"><i class="far fa-eye-evil text-center text-muted mr-3"></i>Toggle +18</button>
{% endif %}
{% endif %}
{% if v %}
@ -443,7 +441,7 @@
{% else %}
{% if request.path.endswith('/mod/queue') %}
{% if request.path.endswith('/admin/queue') %}
<div class="row no-gutters">
<div class="col">
@ -460,7 +458,7 @@
<div class="text-center py-7">
<div class="h4 p-2">+{{b.name}} is barren and needs posts!</div>
{% if v and b and b.can_submit(v) %}
<div class="p-2"><a href="/submit?guild={{b.name}}" class="btn btn-primary">Be the first to post</a></div>
<div class="p-2"><a href="/submit" class="btn btn-primary">Be the first to post</a></div>
{% endif %}
</div>
</div>

View File

@ -166,21 +166,14 @@
if (nothingFocused) {
if (document.getElementById('guild-name-reference')) {
var guild = document.getElementById('guild-name-reference').innerText;
}
var clipText = event.clipboardData.getData('Text');
var url = new RegExp('^(?:[a-z]+:)?//', 'i');
if (url.test(clipText) && window.location.pathname !== '/submit' && guild == undefined) {
if (url.test(clipText) && window.location.pathname !== '/submit') {
window.location.href = '/submit?url=' + clipText;
}
else if (url.test(clipText) && window.location.pathname !== '/submit' && guild !== undefined) {
window.location.href = '/submit?url=' + clipText + '&guild=' + guild;
}
else if (url.test(clipText) && window.location.pathname == '/submit' && guild == undefined) {
else if (url.test(clipText) && window.location.pathname == '/submit') {
document.getElementById("post-URL").value = clipText;

View File

@ -31,9 +31,6 @@
<meta name="description" content="Username reserved for: {{u.reserved}}">
{% endblock %}
{% block sidebar %}
{% endblock %}
{% block adminpanel %}{% endblock %}
{% block content %}

1491
schema.sql

File diff suppressed because it is too large Load Diff

2156
snappy.txt

File diff suppressed because one or more lines are too long