Merge branch 'master' into push-notifs

pull/37/head
Snakes 2022-12-02 17:20:46 -05:00
commit 64bff9dd24
Signed by: Snakes
GPG Key ID: E745A82778055C7E
38 changed files with 225 additions and 215 deletions

4
env
View File

@ -2,8 +2,8 @@ export FLASK_APP="/rDrama/files/cli:app"
export SITE="localhost"
export SITE_NAME="rDrama"
export SECRET_KEY="blahblahblah"
export DATABASE_URL="postgresql://postgres@localhost:5432"
export REDIS_URL="redis://localhost:6379"
export DATABASE_URL="postgresql://postgres@postgres:5432"
export REDIS_URL="redis://redis:6379"
export PROXY_URL="http://localhost:18080"
export LOG_DIRECTORY="/var/log/rdrama"
export SETTINGS_FILENAME="/site_settings.json"

View File

@ -3925,7 +3925,7 @@ ul.comment-section {
border-left: none;
top: -0.1px;
left: -3px;
font-size: 10px;
font-size: 14px;
}
.comment.collapsed .comment-collapse-icon:hover::before {
color: var(--primary);
@ -4774,7 +4774,7 @@ video {
width: 10px;
content: "\f056";
position: static;
font-size: 10px;
font-size: 14px;
font-weight: 900;
border: none;
margin-left: 0.25rem;

View File

@ -66,5 +66,5 @@ h5.post-title a:visited {
}
.modal-content {
border: 3px var(--gray-500) solid;
border: 1px var(--gray-500) solid;
}

View File

@ -16,6 +16,6 @@
background-color: #21262d !important;
}
.App-side, .flaggers {
.App-side, .flaggers, .comment-section {
background: transparent !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -1,12 +1,12 @@
import random
from operator import *
from typing import Union
from typing import Any, Union
import pyotp
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import aliased, deferred
from sqlalchemy.sql import func
from sqlalchemy.sql.expression import not_, and_, or_
from sqlalchemy.sql.expression import not_, and_, or_, ColumnOperators
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
@ -129,6 +129,7 @@ class User(Base):
total_lottery_winnings = Column(Integer, default=0)
last_viewed_post_notifs = Column(Integer, default=0)
last_viewed_log_notifs = Column(Integer, default=0)
last_viewed_reddit_notifs = Column(Integer, default=0)
pronouns = Column(String, default='they/them')
bite = Column(Integer)
earlylife = Column(Integer)
@ -413,6 +414,13 @@ class User(Base):
def can_view_offsitementions(self):
return self.offsitementions or self.admin_level >= PERMS['NOTIFICATIONS_REDDIT']
@lazy
def can_edit(self, target:Union[Submission, Comment]) -> bool:
if isinstance(target, Comment) and not target.post: return False
if self.id == target.author_id: return True
if not isinstance(target, Submission): return False
return bool(self.admin_level >= PERMS['POST_EDITING'])
@property
@lazy
def user_awards(self):
@ -632,7 +640,7 @@ class User(Base):
if not self.can_see_shadowbanned:
notifs = notifs.filter(User.shadowbanned == None)
return notifs.count() + self.post_notifications_count + self.modaction_notifications_count
return notifs.count() + self.post_notifications_count + self.modaction_notifications_count + self.reddit_notifications_count
@property
@lazy
@ -701,9 +709,9 @@ class User(Base):
@property
@lazy
def reddit_notifications_count(self):
if not self.can_view_offsitementions: return 0
return g.db.query(Notification).join(Comment).filter(
Notification.user_id == self.id, Notification.read == False,
if not self.can_view_offsitementions or self.id == AEVANN_ID: return 0
return g.db.query(Comment).filter(
Comment.created_utc > self.last_viewed_reddit_notifs,
Comment.is_banned == False, Comment.deleted_utc == 0,
Comment.body_html.like('%<p>New site mention%<a href="https://old.reddit.com/r/%'),
Comment.parent_submission == None, Comment.author_id == AUTOJANNY_ID).count()

View File

@ -467,7 +467,7 @@ def execute_lawlz_actions(v:User, p:Submission):
if SITE_NAME != 'rDrama': return
if not FEATURES['PINS']: return
p.stickied_utc = int(time.time()) + 86400
p.stickied = AUTOJANNY_ID
p.stickied = "AutoJanny"
p.distinguish_level = 6
p.flair = filter_emojis_only(":ben10: Required Reading")
pin_time = 'for 1 day'

View File

@ -207,7 +207,7 @@ BACKGROUND_CATEGORIES = ["glitter", "anime", "fantasy", "solarpunk", "pixelart"]
COMMENT_SORTS = ["hot", "new", "old", "top", "bottom", "controversial"]
SORTS = COMMENT_SORTS + ["bump", "comments"]
TIME_FILTERS = ["hour", "day", "week", "month", "year", "all"]
PAGE_SIZES = {10, 25, 50, 100}
PAGE_SIZES = (10, 25, 50, 100)
################################################################################
### SITE SPECIFIC CONSTANTS
@ -491,7 +491,8 @@ TIERS_ID_TO_NAME = {
}
BADGE_BLACKLIST = { # only grantable by AEVANN_ID and SNAKES_ID except on PCM
16, 17, 21, 22, 23, 24, 25, 26, 27, # Marsey Artist x2 / Patron Tiers
1, 2, 6, 10, 11, 12, # Alpha, Verified Email, Beta, Recruiter x3
16, 17, 143, 21, 22, 23, 24, 25, 26, 27, # Marsey Artist x3 / Patron Tiers
94, 95, 96, 97, 98, 109, 67, 68, 83, 84, 87, 90, 140, 179, 185, # Award Status
137, # Lottery Winner
}
@ -568,6 +569,7 @@ if SITE == 'rdrama.net':
29, #QuadNarca
JOAN_ID,
4389, #WorldAroundEwe
5526, #Dramarama
}
GIFT_NOTIF_ID = CARP_ID
@ -579,6 +581,12 @@ elif SITE == 'pcmemes.net':
PIN_LIMIT = 10
FEATURES['REPOST_DETECTION'] = False
FEATURES['STREAMERS'] = True
PERMS['SITE_SETTINGS'] = 2
PERMS['SITE_SETTINGS_UNDER_ATTACK'] = 2
PERMS['SITE_CACHE_PURGE_CDN'] = 2
PERMS['SITE_CACHE_DUMP_INTERNAL'] = 2
ERROR_MSGS[500] = "Hiiiii it's <b>nigger</b>! I think this error means that there's a <b>nigger</b> error. And I think that means something took too long to load so it decided to be a <b>nigger</b>. If you keep seeing this on the same page but not other pages, then something its probably a <b>niggerfaggot</b>. It may not be called a <b>nigger</b>, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily &lt;3"
ERROR_MARSEYS[500] = "wholesome"
POST_RATE_LIMIT = '1/second;4/minute;20/hour;100/day'
@ -678,7 +686,7 @@ elif SITE == 'watchpeopledie.tv':
}
BADGE_WHITELIST = {
85, 99, 101, # Sigma, Artist Badges x2
7, 74, 85, 99, 101, # Bug, Grass, Sigma, SidebarArt, BannerArt
59, 60, 66, 104, 108, # Classic Accolades, Nword
117, 124, 144, 145, 146, 147, 148, 149, # Census Reused for Fun
}
@ -1729,6 +1737,7 @@ if SITE_NAME == 'rDrama':
'us.forums.blizzard.com',
'eu.forums.blizzard.com',
'bungie.net',
'soyjak.party',
#fediverse
'rdrama.cc',

View File

@ -131,6 +131,7 @@ def _give_monthly_marseybux_task():
g.db.add(u)
if u.admin_level or u.id in GUMROAD_MESSY:
give_marseybux(u)
badge_grant(badge_id=20+u.patron, user=u, notify=False)
elif u.email and u.is_activated and u.email.lower() in emails:
data = {'access_token': GUMROAD_TOKEN, 'email': u.email}
try:

View File

@ -10,7 +10,6 @@ from sqlalchemy import or_
import files.helpers.const as const
from files.classes.badges import Badge
from files.classes.comment import Comment
from files.classes.notifications import Notification
from files.classes.user import User
from files.helpers.sanitize import sanitize
@ -21,23 +20,15 @@ from files.helpers.sanitize import sanitize
# value from /meta (or just guessing) and doing a random selection of keywords.
def offsite_mentions_task(cache:Cache):
if const.REDDIT_NOTIFS_SITE:
row_send_to = g.db.query(Badge.user_id).filter_by(badge_id=140).all()
row_send_to += g.db.query(User.id).filter(
User.admin_level >= const.PERMS['NOTIFICATIONS_REDDIT'],
User.id != const.AEVANN_ID,
).all()
send_to = [x[0] for x in row_send_to]
send_to = set(send_to)
site_mentions = get_mentions(cache, const.REDDIT_NOTIFS_SITE)
notify_mentions(send_to, site_mentions)
site_mentions = get_mentions(cache, const.REDDIT_NOTIFS_SITE)
notify_mentions(site_mentions)
if const.REDDIT_NOTIFS_USERS:
for query, send_user in const.REDDIT_NOTIFS_USERS.items():
user_mentions = get_mentions(cache, [query], reddit_notifs_users=True)
notify_mentions([send_user], user_mentions, mention_str='mention of you')
notify_mentions(user_mentions, send_to=send_user, mention_str='mention of you')
g.db.commit() # commit early otherwise localhost testing fails to commit
def get_mentions(cache:Cache, queries:Iterable[str], reddit_notifs_users=False):
kinds = ['submission', 'comment']
@ -90,7 +81,7 @@ def get_mentions(cache:Cache, queries:Iterable[str], reddit_notifs_users=False):
print("Failed to set cache value; there may be duplication of reddit notifications")
return mentions
def notify_mentions(send_to, mentions, mention_str='site mention'):
def notify_mentions(mentions, send_to=None, mention_str='site mention'):
for m in mentions:
author = m['author']
permalink = m['permalink']
@ -119,6 +110,6 @@ def notify_mentions(send_to, mentions, mention_str='site mention'):
g.db.flush()
new_comment.top_comment_id = new_comment.id
for user_id in send_to:
notif = Notification(comment_id=new_comment.id, user_id=user_id)
if send_to:
notif = Notification(comment_id=new_comment.id, user_id=send_to)
g.db.add(notif)

View File

@ -38,7 +38,7 @@ TLDS = ( # Original gTLDs and ccTLDs
# New gTLDs
'app','cleaning','club','dev','farm','florist','fun','gay','lgbt','life','lol',
'moe','mom','monster','new','news','online','pics','press','pub','site',
'vip','win','world','wtf','xyz','video','host','art',
'vip','win','world','wtf','xyz','video','host','art','media'
)
allowed_tags = ('b','blockquote','br','code','del','em','h1','h2','h3','h4','h5','h6','hr','i',

View File

@ -32,13 +32,6 @@ def before_request():
g.webview = '; wv) ' in ua
if ' firefox/' in ua:
g.type = 'firefox'
elif 'iphone' in ua or 'ipad' in ua or 'ipod' in ua or 'mac os' in ua:
g.type = 'apple'
else:
g.type = 'chromium'
g.is_tor = request.headers.get("cf-ipcountry") == "T1"
request.path = request.path.rstrip('/')

View File

@ -224,28 +224,20 @@ def notifications_reddit(v:User):
if not v.can_view_offsitementions: abort(403)
notifications = g.db.query(Notification, Comment).join(Notification.comment).filter(
Notification.user_id == v.id,
listing = g.db.query(Comment).filter(
Comment.body_html.like('%<p>New site mention%<a href="https://old.reddit.com/r/%'),
Comment.parent_submission == None,
Comment.author_id == AUTOJANNY_ID
).order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
).order_by(Comment.created_utc.desc()).offset(PAGE_SIZE*(page-1)).limit(PAGE_SIZE+1).all()
listing = []
next_exists = len(listing) > PAGE_SIZE
listing = listing[:PAGE_SIZE]
for index, x in enumerate(notifications[:100]):
n, c = x
if n.read and index > 24: break
elif not n.read:
n.read = True
c.unread = True
g.db.add(n)
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c)
for ma in listing:
ma.unread = ma.created_utc > v.last_viewed_reddit_notifs
next_exists = (len(notifications) > len(listing))
g.db.commit()
v.last_viewed_reddit_notifs = int(time.time())
g.db.add(v)
if v.client: return {"data":[x.json(g.db) for x in listing]}
@ -271,7 +263,6 @@ def notifications(v:User):
Notification.user_id == v.id,
Comment.is_banned == False,
Comment.deleted_utc == 0,
Comment.body_html.notlike('%<p>New site mention%<a href="https://old.reddit.com/r/%'),
or_(Comment.sentto == None, Comment.sentto == MODMAIL_ID),
not_(and_(Comment.sentto != None, Comment.sentto == MODMAIL_ID, User.is_muted)),
)

View File

@ -145,7 +145,7 @@ def post_id(pid, anything=None, v=None, sub=None):
if g.is_api_or_xhr: return {"error":"Must be 18+ to view"}, 451
return render_template("errors/nsfw.html", v=v)
if post.new or 'megathread' in post.title.lower(): defaultsortingcomments = 'new'
if post.new: defaultsortingcomments = 'new'
elif v: defaultsortingcomments = v.defaultsortingcomments
else: defaultsortingcomments = "hot"
sort = request.values.get("sort", defaultsortingcomments)
@ -314,8 +314,7 @@ def morecomments(v, cid):
@ratelimit_user("1/second;10/minute;100/hour;200/day")
def edit_post(pid, v):
p = get_post(pid)
if v.id != p.author_id and v.admin_level < PERMS['POST_EDITING']:
abort(403)
if not v.can_edit(p): abort(403)
# Disable edits on things older than 1wk unless it's a draft or editor is a jannie
if (time.time() - p.created_utc > 7*24*60*60 and not p.private
@ -924,9 +923,11 @@ def submit_post(v:User, sub=None):
execute_lawlz_actions(v, post)
if SITE == 'rdrama.net' and v.id in (IMPASSIONATA_ID, PIZZASHILL_ID, 2008):
if (SITE == 'rdrama.net'
and v.id in (IMPASSIONATA_ID, PIZZASHILL_ID, 2008)
and not (post.sub and post.sub.stealth)):
post.stickied_utc = int(time.time()) + 3600
post.stickied = AUTOJANNY_ID
post.stickied = "AutoJanny"
cache.delete_memoized(frontlist)
cache.delete_memoized(userpagelisting)
@ -941,7 +942,7 @@ def submit_post(v:User, sub=None):
if v.client: return post.json(g.db)
else:
post.voted = 1
if post.new or 'megathread' in post.title.lower(): sort = 'new'
if post.new: sort = 'new'
else: sort = v.defaultsortingcomments
return render_template('submission.html', v=v, p=post, sort=sort, render_replies=True, offset=0, success=True, sub=post.subr)
@ -1073,6 +1074,16 @@ def pin_post(post_id, v):
else: return {"message": "Post unpinned!"}
return abort(404, "Post not found!")
@app.route("/post/<post_id>/new", methods=["PUT", "DELETE"])
@limiter.limit(DEFAULT_RATELIMIT_SLOWER)
@auth_required
def toggle_new_sort(post_id:int, v:User):
post = get_post(post_id)
if not v.can_edit(post): abort(403, "Only the post author can do that!")
post.new = request.method == "PUT"
g.db.add(post)
return {"message": f"Turned {'on' if post.new else 'off'} sort by new"}
extensions = IMAGE_FORMATS + VIDEO_FORMATS + AUDIO_FORMATS

View File

@ -1135,27 +1135,33 @@ kofi_tiers={
@auth_required
def settings_kofi(v:User):
if not KOFI_TOKEN or KOFI_TOKEN == DEFAULT_CONFIG_VALUE: abort(404)
if not (v.email and v.is_activated):
abort(400, f"You must have a verified email to verify {patron} status and claim your rewards!")
transaction = g.db.query(Transaction).filter_by(email=v.email).order_by(Transaction.created_utc.desc()).first()
if not transaction:
abort(404, "Email not found")
if transaction.claimed:
transactions = g.db.query(Transaction).filter_by(email=v.email, claimed=None).all()
if not transactions:
abort(400, f"{patron} rewards already claimed")
tier = kofi_tiers[transaction.amount]
highest_tier = 0
marseybux = 0
for transaction in transactions:
tier = kofi_tiers[transaction.amount]
marseybux += marseybux_li[tier]
if tier > highest_tier: highest_tier = tier
transaction.claimed = True
g.db.add(transaction)
marseybux = marseybux_li[tier]
v.pay_account('marseybux', marseybux)
send_repeatable_notification(v.id, f"You have received {marseybux} Marseybux! You can use them to buy awards in the [shop](/shop).")
g.db.add(v)
if tier > v.patron:
v.patron = tier
if highest_tier > v.patron:
v.patron = highest_tier
for badge in g.db.query(Badge).filter(Badge.user_id == v.id, Badge.badge_id > 20, Badge.badge_id < 28).all():
g.db.delete(badge)
badge_grant(badge_id=20+tier, user=v)
badge_grant(badge_id=20+highest_tier, user=v)
transaction.claimed = True
g.db.add(transaction)
return {"message": f"{patron} rewards claimed!"}

View File

@ -17,20 +17,40 @@ def vote_info_get(v, link):
if thing.ghost and v.id != AEVANN_ID: abort(403)
if isinstance(thing, Submission):
if thing.author.shadowbanned and not (v and v.admin_level >= PERMS['USER_SHADOWBAN']):
thing_id = g.db.query(Submission.id).filter_by(upvotes=thing.upvotes, downvotes=thing.downvotes).order_by(Submission.id).first()[0]
if (thing.author.shadowbanned
and not (v and v.admin_level >= PERMS['USER_SHADOWBAN'])):
thing_id = g.db.query(Submission.id) \
.filter_by(upvotes=thing.upvotes, downvotes=thing.downvotes) \
.order_by(Submission.id).first()[0]
else: thing_id = thing.id
ups = g.db.query(Vote).filter_by(submission_id=thing_id, vote_type=1).order_by(Vote.created_utc).all()
downs = g.db.query(Vote).filter_by(submission_id=thing_id, vote_type=-1).order_by(Vote.created_utc).all()
query = g.db.query(Vote).join(Vote.user).filter(
Vote.submission_id == thing_id,
).order_by(Vote.created_utc)
if not v.can_see_shadowbanned:
query = query.filter(User.shadowbanned == None)
ups = query.filter(Vote.vote_type == 1).all()
downs = query.filter(Vote.vote_type == -1).all()
elif isinstance(thing, Comment):
if thing.author.shadowbanned and not (v and v.admin_level >= PERMS['USER_SHADOWBAN']):
thing_id = g.db.query(Comment.id).filter_by(upvotes=thing.upvotes, downvotes=thing.downvotes).order_by(Comment.id).first()[0]
if (thing.author.shadowbanned
and not (v and v.admin_level >= PERMS['USER_SHADOWBAN'])):
thing_id = g.db.query(Comment.id) \
.filter_by(upvotes=thing.upvotes, downvotes=thing.downvotes) \
.order_by(Comment.id).first()[0]
else: thing_id = thing.id
ups = g.db.query(CommentVote).filter_by(comment_id=thing_id, vote_type=1).order_by(CommentVote.created_utc).all()
downs = g.db.query(CommentVote).filter_by(comment_id=thing_id, vote_type=-1 ).order_by(CommentVote.created_utc).all()
query = g.db.query(CommentVote).join(CommentVote.user).filter(
CommentVote.comment_id == thing_id,
).order_by(CommentVote.created_utc)
if not v.can_see_shadowbanned:
query = query.filter(User.shadowbanned == None)
ups = query.filter(CommentVote.vote_type == 1).all()
downs = query.filter(CommentVote.vote_type == -1).all()
else: abort(400)
@ -40,6 +60,7 @@ def vote_info_get(v, link):
ups=ups,
downs=downs)
def vote_post_comment(target_id, new, v, cls, vote_cls):
if new == "-1" and DISABLE_DOWNVOTES: abort(403)
if new not in {"-1", "0", "1"}: abort(400)
@ -91,7 +112,8 @@ def vote_post_comment(target_id, new, v, cls, vote_cls):
existing.coins = coin_value
g.db.add(existing)
elif existing.vote_type != 0 and new == 0:
target.author.charge_account('coins', existing.coins, should_check_balance=False)
target.author.charge_account('coins', existing.coins,
should_check_balance=False)
target.author.truescore -= coin_delta
g.db.add(target.author)
g.db.delete(existing)
@ -126,16 +148,17 @@ def vote_post_comment(target_id, new, v, cls, vote_cls):
# this is hacky but it works, we should probably do better later
def get_vote_count(dir, real_instead_of_dir):
votes = g.db.query(vote_cls)
votes = g.db.query(vote_cls).join(vote_cls.user) \
.filter(User.shadowbanned == None)
if real_instead_of_dir:
votes = votes.filter_by(real=True)
votes = votes.filter(vote_cls.real == True)
else:
votes = votes.filter_by(vote_type=dir)
votes = votes.filter(vote_cls.vote_type == dir)
if vote_cls == Vote:
votes = votes.filter_by(submission_id=target.id)
votes = votes.filter(vote_cls.submission_id == target.id)
elif vote_cls == CommentVote:
votes = votes.filter_by(comment_id=target.id)
votes = votes.filter(vote_cls.comment_id == target.id)
else:
return 0
return votes.count()
@ -150,9 +173,13 @@ def vote_post_comment(target_id, new, v, cls, vote_cls):
if target.author.progressivestack or target.author.id in BOOSTED_USERS:
mul = 2
elif cls == Submission:
if target.domain.endswith('.win') or (target.domain in BOOSTED_SITES and not target.url.startswith('/')) or target.sub in BOOSTED_HOLES:
if (target.domain.endswith('.win')
or (target.domain in BOOSTED_SITES and not target.url.startswith('/'))
or target.sub in BOOSTED_HOLES):
mul = 2
elif not target.sub and target.body_html and target.author.id not in {8768,3402,5214,12719}:
elif (not target.sub
and target.body_html
and target.author.id not in {8768,3402,5214,12719}):
x = target.body_html.count('" target="_blank" rel="nofollow noopener">')
x += target.body_html.count('<a href="/images/')
target.realupvotes += min(x*2, 20)
@ -164,6 +191,7 @@ def vote_post_comment(target_id, new, v, cls, vote_cls):
g.db.add(target)
return "", 204
@app.post("/vote/post/<post_id>/<new>")
@limiter.limit("5/second;60/minute;1000/hour;2000/day")
@is_not_permabanned

View File

@ -61,7 +61,8 @@ def get_logged_in_user():
if AEVANN_ID and request.headers.get("Cf-Ipcountry") == 'EG':
if v and not v.username.startswith('Aev') and v.truescore > 0:
with open(f"{LOG_DIRECTORY}/eg.log", "r+", encoding="utf-8") as f:
with open(f"{LOG_DIRECTORY}/eg.log", "a+", encoding="utf-8") as f:
f.seek(0)
ip = request.headers.get('CF-Connecting-IP')
if f'@{v.username}, ' not in f.read():
t = time.strftime("%d/%B/%Y %H:%M:%S UTC", time.gmtime(time.time()))

View File

@ -4,10 +4,21 @@
<div class="ml-3 mt-4">
<b>How to install {{SITE_NAME}}'s mobile app</b>
{% set ua = g.agent.lower() %}
{% if ' firefox/' in ua %}
{% set type = 'firefox' %}
{% elif 'iphone' in ua or 'ipad' in ua or 'ipod' in ua or 'mac os' in ua %}
{% set type = 'apple' %}
{% elif v %}
{% set type = 'chromium' %}
{% else %}
{% set type = 'chromium-lo' %}
{% endif %}
<div class="font-weight-bold mt-4 mb-2">First step:</div>
<img onclick="expandDesktopImage()" alt="First step" style="height:30vh" src="/i/{{SITE_NAME}}/app-{{g.type}}-1.webp">
<img onclick="expandDesktopImage()" alt="First step" style="height:30vh" src="/i/{{SITE_NAME}}/app-{{type}}-1.webp">
<div class="font-weight-bold mt-4 mb-2">Second step:</div>
<img onclick="expandDesktopImage()" alt="Second step" style="height:30vh" src="/i/{{SITE_NAME}}/app-{{g.type}}-2.webp">
<img onclick="expandDesktopImage()" alt="Second step" style="height:30vh" src="/i/{{SITE_NAME}}/app-{{type}}-2.webp">
</div>
{% endblock %}

View File

@ -3,18 +3,21 @@
{% block body %}
{% block banner %}
{% include "modals/expanded_image.html" %}
{% if '@' not in request.path %}
{% if err or '@' not in request.path %}
{% if EVENT_BANNER and not sub %}
{% include 'event/' + EVENT_BANNER %}
{% else %}
{% if sub and SITE_NAME != WPD %}
{% if err and SITE_NAME == 'rDrama' %}
{% set src = "banner_error.webp" | asset_siteimg %}
{% elif sub %}
{% set src = sub.banner_url %}
{% set alt = ['/h/', sub, 'banner']|join %}
{% set class = 'site-banner-hole' %}
{% elif SITE_NAME == "rDrama" %}
{% set href = "https://secure.transequality.org/site/Donation2?df_id=1480" %}
{% set expand = false %}
{% elif SITE_NAME == 'PCM' %}{# special case: christmas #}
{% set src = "/i/PCM/banners_event/2022christmas1.webp" %}
{% endif %}
{{macros.banner(src, href, alt, expand, class)}}
{% endif %}

View File

@ -63,7 +63,7 @@
</a>
{% if sub %}
<a id="sub-name" href="/h/{{sub}}" class="font-weight-bold ml-2 flex-grow-1 mt-1" {% if sub.name|length >= 17 %}style="font-size:max(10px,1.2vw)"{% else %}style="font-size:max(10px,1.2vw)"{% endif %}>{% if not HOLE_STYLE_FLAIR %}/h/{% endif %}{{sub}}</a>
<a id="sub-name" href="/h/{{sub}}" class="font-weight-bold ml-2 flex-grow-1 mt-1" {% if sub.name|length >= 17 %}style="font-size:max(10px,1.2vw)"{% else %}style="font-size:max(14px,1.2vw)"{% endif %}>{% if not HOLE_STYLE_FLAIR %}/h/{% endif %}{{sub}}</a>
{% elif has_logo %}
<style>
{% if SITE_NAME == 'WPD' %}

View File

@ -185,8 +185,7 @@
color: #919191;
float: left;
font-size: 14px;
padding-top: 0.25rem;
padding-right: 0.7rem;
padding: 0.25rem 0.7rem 2rem 0.7rem;
}
</style>

View File

@ -26,35 +26,6 @@
{% block content %}
<div id="register-form">
{% if not ref_user and SITE_NAME == 'rDrama' and site_settings['login_required'] %}
<p class="text-muted text-justify mb-3" style="line-height: 1.2em">
Hiiiiii its your favorite user Carp! Were currently on <span style="font-variant-caps: small-caps;">Login Required</span> mode for one of a few reasons:
</p>
<ol style="padding-left: 1.5em">
<li>The site is running really slowly and we think it might be miscreants up to no good, or</li>
<li>Its our monthly 24 hours of login-required-mode to encourage lovely lurkers to become lovely new friends of ours, or</li>
<li>Someone clicked the toggle by mistake lol oops sorry!</li>
</ol>
<p class="text-muted text-justify mb-1" style="line-height: 1.2em">
But thats all fine. Signing up is easy. It takes literally like 4 seconds if youre slow. You dont even need an email! Just pick a username, make up some neat new password where you replace all the es with 3s or whatever and bam, done, youre in.<br>
Remember to click “Follow” on my profile!
</p>
<p class="text-muted mb-md-2">
I love you.<br>
<em>xoxo carp</em> 💋
</p>
<hr>
{% elif not ref_user and SITE_NAME == 'WPD' and site_settings['login_required'] %}
<p class="text-muted text-justify mb-1" style="line-height: 1.2em">
<strong>Hi you!</strong> &mdash; <span style="font-variant-caps: small-caps;">watchpeopledie.tv</span> is currently doing our monthly 24(ish) hours of "everyone needs to have an account" &mdash; sorry about that! But making an account is actually super easy. <strong>You don't even need an email address!</strong> Literally just pick a username, set a password, and that's that. Bam, done, like 8 seconds, tops.<br>
</p>
<p class="text-muted mb-md-2">
We WANT you with us &#128150;<br>
Please join!
</p>
<hr>
{% endif %}
<form action="/signup" method="post" class="mt-md-3" id="signup">
{% if error %}<div class="text-danger mb-2">{{error}}</div>{% endif %}

View File

@ -17,9 +17,7 @@
{% if v %}
<button type="button" id="subscribe-{{p.id}}" class="{% if p.id in v.subscribed_idlist %}d-none{% endif %} list-inline-item" data-click2="postToastSwitch(this,'/subscribe/{{p.id}}','subscribe-{{p.id}}','unsubscribe-{{p.id}}','d-none')" onclick="areyousure(this)"><i class="fas fa-eye"></i>Subscribe</button>
<button type="button" id="unsubscribe-{{p.id}}" class="{% if p.id not in v.subscribed_idlist %}d-none{% endif %} list-inline-item" onclick="postToastSwitch(this,'/unsubscribe/{{p.id}}','subscribe-{{p.id}}','unsubscribe-{{p.id}}','d-none')"><i class="fas fa-eye-slash"></i>Unsubscribe</button>
{% endif %}
{% if v %}
<button type="button" id="save-{{p.id}}" class="{% if p.id in v.saved_idlist %}d-none{% endif %} list-inline-item" onclick="postToastSwitch(this,'/save_post/{{p.id}}','save-{{p.id}}','unsave-{{p.id}}','d-none')"><i class="fas fa-save"></i>Save</button>
<button type="button" id="unsave-{{p.id}}" class="{% if not p.id in v.saved_idlist %}d-none{% endif %} list-inline-item" onclick="postToastSwitch(this,'/unsave_post/{{p.id}}','save-{{p.id}}','unsave-{{p.id}}','d-none')"><i class="fas fa-save"></i>Unsave</button>
@ -35,72 +33,60 @@
<button type="button" id="undelete2-{{p.id}}" class="{% if not p.deleted_utc %}d-none{% endif %} list-inline-item" onclick="postToastSwitch(this,'/undelete_post/{{p.id}}', 'delete2-{{p.id}}', 'undelete2-{{p.id}}','d-none');document.getElementById('post-{{p.id}}').classList.remove('deleted')"><i class="fas fa-trash-alt"></i>Undelete</button>
<button type="button" id="delete2-{{p.id}}" class="{% if p.deleted_utc %}d-none{% endif %} list-inline-item" data-bs-toggle="modal" data-bs-dismiss="modal" data-bs-target="#deletePostModal" onclick="delete_postModal(this, '{{p.id}}')"><i class="fas fa-trash-alt"></i>Delete</button>
{% endif %}
{% endif %}
{% if v %}
<button type="button" class="list-inline-item" data-bs-toggle="dropdown" aria-expanded="false"><i class="fas fa-ellipsis-h fa-fw"></i></button>
<ul class="dropdown-menu">
{% if v.admin_level >= PERMS['POST_COMMENT_MODERATION'] and p.oauth_app %}
<a class="dropdown-item list-inline-item text-info" href="{{p.oauth_app.permalink}}/posts"><i class="fas fa-code"></i>API App</a>
{% if v %}
<button type="button" class="list-inline-item" data-bs-toggle="dropdown" aria-expanded="false"><i class="fas fa-ellipsis-h fa-fw"></i></button>
<ul class="dropdown-menu">
{% if v.admin_level >= PERMS['POST_COMMENT_MODERATION'] and p.oauth_app %}
<a class="dropdown-item list-inline-item text-info" href="{{p.oauth_app.permalink}}/posts"><i class="fas fa-code"></i>API App</a>
{% endif %}
{% if v.can_edit(p) %}
<button type="button" class="dropdown-item {% if p.new %} d-none{% endif %} list-inline-item text-info" id="{{p.id}}-sort-new" onclick="postToastSwitch(this, '/post/{{p.id}}/new', this.id, '{{p.id}}-unsort-new', 'd-none', null, 'PUT')"><i class="fas fa-sparkles text-center text-primary mr-2"></i>Set Default Sort New</button>
<button type="button" class="dropdown-item {% if not p.new %} d-none{% endif %} list-inline-item text-info" id="{{p.id}}-unsort-new" onclick="postToastSwitch(this, '/post/{{p.id}}/new', this.id, '{{p.id}}-sort-new', 'd-none', null, 'DELETE')"><i class="fas fa-sparkles text-center text-primary mr-2"></i>Unset Default Sort New</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] %}
<button type="button" id="distinguish-{{p.id}}" class="dropdown-item {% if p.distinguish_level %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish-{{p.id}}','undistinguish-{{p.id}}','d-none')"><i class="fas fa-crown"></i>Distinguish</button>
<button type="button" id="undistinguish-{{p.id}}" class="dropdown-item {% if not p.distinguish_level %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish-{{p.id}}','undistinguish-{{p.id}}','d-none')"><i class="fas fa-crown"></i>Undistinguish</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}
<button type="button" id="pin-{{p.id}}" class="dropdown-item {% if p.stickied and not p.stickied_utc %}d-none{% endif %} list-inline-item text-info" onclick="pinPost(this, '{{p.id}}')"><i class="fas fa-thumbtack fa-rotate--45"></i>Pin {% if p.stickied_utc %}permanently{% else %}for 1 hour{% endif %}</button>
<button type="button" id="unpin-{{p.id}}" class="dropdown-item {% if not p.stickied %}d-none{% endif %} list-inline-item text-info" onclick="unpinPost(this, '{{p.id}}')"><i class="fas fa-thumbtack fa-rotate--45"></i>Unpin</button>
{% endif %}
{% if p.sub and v.mods(p.sub) %}
<button type="button" id="hole-pin-{{p.id}}" class="dropdown-item {% if p.hole_pinned %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/hole_pin/{{p.id}}','hole-pin-{{p.id}}','hole-unpin-{{p.id}}','d-none')"><i class="fas fa-thumbtack fa-rotate--45"></i>Pin to /h/{{p.sub}}</button>
<button type="button" id="hole-unpin-{{p.id}}" class="dropdown-item {% if not p.hole_pinned %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/hole_unpin/{{p.id}}','hole-pin-{{p.id}}','hole-unpin-{{p.id}}','d-none')"><i class="fas fa-thumbtack fa-rotate--45"></i>Unpin from /h/{{p.sub}}</button>
{% endif %}
{% if FEATURES['COUNTRY_CLUB'] and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or v.id == p.author_id) %}
<button type="button" id="club-{{p.id}}" class="dropdown-item {% if p.club %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/club_post/{{p.id}}','club-{{p.id}}','unclub-{{p.id}}','d-none')"><i class="fas fa-eye-slash"></i>Mark club</button>
<button type="button" id="unclub-{{p.id}}" class="dropdown-item {% if not p.club %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/unclub_post/{{p.id}}','club-{{p.id}}','unclub-{{p.id}}','d-none')"><i class="fas fa-eye"></i>Unmark club</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}
<button type="button" id="remove-{{p.id}}" class="dropdown-item {% if p.is_banned %}d-none{% endif %} list-inline-item text-danger" onclick="removePost(this,'{{p.id}}','remove-{{p.id}}','approve-{{p.id}}','d-none')"><i class="fas fa-ban"></i>Remove</button>
<button type="button" id="approve-{{p.id}}" class="dropdown-item {% if not p.is_banned and request.path != '/admin/reported/posts' %}d-none{% endif %} list-inline-item text-success" onclick="approvePost(this,'{{p.id}}','remove-{{p.id}}','approve-{{p.id}}','d-none')"><i class="fas fa-check"></i>Approve</button>
{% endif %}
{% if v.id != p.author_id and not p.ghost %}
<button type="button" id="unblock-{{p.id}}" class="dropdown-item text-success list-inline-item {% if not p.is_blocking %}d-none{% endif %}" onclick="postToastSwitch(this,'/settings/unblock?username={{p.author.username}}','block-{{p.id}}','unblock-{{p.id}}','d-none')"><i class="fas fa-eye text-success"></i>Unblock user</button>
<button type="button" id="block-{{p.id}}" class="dropdown-item list-inline-item text-danger {% if p.is_blocking %}d-none{% endif %}" onclick="postToastSwitch(this,'/settings/block?username={{p.author.username}}','block-{{p.id}}','unblock-{{p.id}}','d-none')"><i class="fas fa-eye-slash text-danger"></i>Block user</button>
{% endif %}
{% if p.sub and v.mods(p.sub) %}
<button type="button" class="dropdown-item list-inline-item text-danger" onclick="postToastSwitch(this,'/kick/{{p.id}}')"><i class="fas fa-sign-out text-danger"></i>Kick</button>
{% if not p.author.mods(p.sub) %}
<button type="button" id="exile-{{p.id}}" class="{% if p.author.exiled_from(p.sub) %}d-none{% endif %} dropdown-item list-inline-item text-danger" onclick="postToastSwitch(this,'/exile/post/{{p.id}}','exile-{{p.id}}','unexile-{{p.id}}','d-none')"><i class="fas fa-campfire text-danger"></i>Exile user</button>
<button type="button" id="unexile-{{p.id}}" class="{% if not p.author.exiled_from(p.sub) %}d-none{% endif %} dropdown-item list-inline-item text-success" onclick="postToastSwitch(this,'/h/{{sub}}/unexile/{{p.author_id}}','exile-{{p.id}}','unexile-{{p.id}}','d-none')"><i class="fas fa-campfire text-success"></i>Unexile user</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] %}
<button type="button" id="distinguish-{{p.id}}" class="dropdown-item {% if p.distinguish_level %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish-{{p.id}}','undistinguish-{{p.id}}','d-none')"><i class="fas fa-crown"></i>Distinguish</button>
<button type="button" id="undistinguish-{{p.id}}" class="dropdown-item {% if not p.distinguish_level %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish-{{p.id}}','undistinguish-{{p.id}}','d-none')"><i class="fas fa-crown"></i>Undistinguish</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}
<button type="button" id="pin-{{p.id}}" class="dropdown-item {% if p.stickied and not p.stickied_utc %}d-none{% endif %} list-inline-item text-info" onclick="pinPost(this, '{{p.id}}')"><i class="fas fa-thumbtack fa-rotate--45"></i>Pin {% if p.stickied_utc %}permanently{% else %}for 1 hour{% endif %}</button>
<button type="button" id="unpin-{{p.id}}" class="dropdown-item {% if not p.stickied %}d-none{% endif %} list-inline-item text-info" onclick="unpinPost(this, '{{p.id}}')"><i class="fas fa-thumbtack fa-rotate--45"></i>Unpin</button>
{% endif %}
{% if p.sub and v.mods(p.sub) %}
<button type="button" id="hole-pin-{{p.id}}" class="dropdown-item {% if p.hole_pinned %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/hole_pin/{{p.id}}','hole-pin-{{p.id}}','hole-unpin-{{p.id}}','d-none')"><i class="fas fa-thumbtack fa-rotate--45"></i>Pin to /h/{{p.sub}}</button>
<button type="button" id="hole-unpin-{{p.id}}" class="dropdown-item {% if not p.hole_pinned %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/hole_unpin/{{p.id}}','hole-pin-{{p.id}}','hole-unpin-{{p.id}}','d-none')"><i class="fas fa-thumbtack fa-rotate--45"></i>Unpin from /h/{{p.sub}}</button>
{% endif %}
{% if FEATURES['COUNTRY_CLUB'] and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or v.id == p.author_id) %}
<button type="button" id="club-{{p.id}}" class="dropdown-item {% if p.club %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/club_post/{{p.id}}','club-{{p.id}}','unclub-{{p.id}}','d-none')"><i class="fas fa-eye-slash"></i>Mark club</button>
<button type="button" id="unclub-{{p.id}}" class="dropdown-item {% if not p.club %}d-none{% endif %} list-inline-item text-info" onclick="postToastSwitch(this,'/unclub_post/{{p.id}}','club-{{p.id}}','unclub-{{p.id}}','d-none')"><i class="fas fa-eye"></i>Unmark club</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}
<button type="button" id="remove-{{p.id}}" class="dropdown-item {% if p.is_banned %}d-none{% endif %} list-inline-item text-danger" onclick="removePost(this,'{{p.id}}','remove-{{p.id}}','approve-{{p.id}}','d-none')"><i class="fas fa-ban"></i>Remove</button>
<button type="button" id="approve-{{p.id}}" class="dropdown-item {% if not p.is_banned and request.path != '/admin/reported/posts' %}d-none{% endif %} list-inline-item text-success" onclick="approvePost(this,'{{p.id}}','remove-{{p.id}}','approve-{{p.id}}','d-none')"><i class="fas fa-check"></i>Approve</button>
{% endif %}
{% if v.id != p.author_id and not p.ghost %}
<button type="button" id="unblock-{{p.id}}" class="dropdown-item text-success list-inline-item {% if not p.is_blocking %}d-none{% endif %}" onclick="postToastSwitch(this,'/settings/unblock?username={{p.author.username}}','block-{{p.id}}','unblock-{{p.id}}','d-none')"><i class="fas fa-eye text-success"></i>Unblock user</button>
<button type="button" id="block-{{p.id}}" class="dropdown-item list-inline-item text-danger {% if p.is_blocking %}d-none{% endif %}" onclick="postToastSwitch(this,'/settings/block?username={{p.author.username}}','block-{{p.id}}','unblock-{{p.id}}','d-none')"><i class="fas fa-eye-slash text-danger"></i>Block user</button>
{% endif %}
{% if p.sub and v.mods(p.sub) %}
<button type="button" class="dropdown-item list-inline-item text-danger" onclick="postToastSwitch(this,'/kick/{{p.id}}')"><i class="fas fa-sign-out text-danger"></i>Kick</button>
{% if not p.author.mods(p.sub) %}
<button type="button" id="exile-{{p.id}}" class="{% if p.author.exiled_from(p.sub) %}d-none{% endif %} dropdown-item list-inline-item text-danger" onclick="postToastSwitch(this,'/exile/post/{{p.id}}','exile-{{p.id}}','unexile-{{p.id}}','d-none')"><i class="fas fa-campfire text-danger"></i>Exile user</button>
<button type="button" id="unexile-{{p.id}}" class="{% if not p.author.exiled_from(p.sub) %}d-none{% endif %} dropdown-item list-inline-item text-success" onclick="postToastSwitch(this,'/h/{{sub}}/unexile/{{p.author_id}}','exile-{{p.id}}','unexile-{{p.id}}','d-none')"><i class="fas fa-campfire text-success"></i>Unexile user</button>
{% endif %}
{% endif %}
{% if v.id==p.author_id or v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or (p.sub and v.mods(p.sub)) %}
<button type="button" id="mark-{{p.id}}" class="dropdown-item {% if p.over_18 %}d-none{% endif %} list-inline-item text-danger" onclick="postToastSwitch(this,'/toggle_post_nsfw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Mark +18</button>
<button type="button" id="unmark-{{p.id}}" class="dropdown-item {% if not p.over_18 %}d-none{% endif %} list-inline-item text-success" onclick="postToastSwitch(this,'/toggle_post_nsfw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Unmark +18</button>
{% endif %}
{% if v.admin_level >= PERMS['USER_BAN'] and v.id != p.author_id %}
<button type="button" id="ban-{{p.fullname}}" class="dropdown-item {% if p.author.is_suspended %}d-none{% endif %} list-inline-item text-danger" data-bs-toggle="modal" data-bs-target="#banModal" onclick="banModal('/post/{{p.id}}', '{{p.author.id}}', '{{p.author_name}}', '{{p.fullname}}','d-none')"><i class="fas fa-user-slash text-danger fa-fw"></i>Ban user</button>
<button type="button" id="unban-{{p.fullname}}" class="dropdown-item {% if not p.author.is_suspended %}d-none{% endif %} list-inline-item text-success" id="unexile2-user-{{p.id}}" onclick="postToastSwitch(this,'/unban_user/{{p.author_id}}','ban-{{p.fullname}}','unban-{{p.fullname}}','d-none')"><i class="fas fa-user-slash"></i>Unban user</button>
{% endif %}
{% if v.admin_level >= PERMS['USER_AGENDAPOSTER'] and v.id != p.author_id %}
<button type="button" id="chud-{{p.fullname}}" class="dropdown-item {% if p.author.agendaposter %}d-none{% endif %} list-inline-item text-danger" data-bs-toggle="modal" data-bs-target="#chudModal" onclick="chudModal('/post/{{p.id}}', '{{p.author.id}}', '{{p.author_name}}', '{{p.fullname}}','d-none')"><i class="fas fa-face-sleeping text-danger fa-fw"></i>Chud user</button>
<button type="button" id="unchud-{{p.fullname}}" class="dropdown-item {% if not p.author.agendaposter %}d-none{% endif %} list-inline-item text-success" id="unexile2-user-{{p.id}}" onclick="postToastSwitch(this,'/unagendaposter/{{p.author_id}}','chud-{{p.fullname}}','unchud-{{p.fullname}}','d-none')"><i class="fas fa-face-sleeping"></i>Unchud user</button>
{% endif %}
</ul>
{% endif %}
{% endif %}
{% if v.id==p.author_id or v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or (p.sub and v.mods(p.sub)) %}
<button type="button" id="mark-{{p.id}}" class="dropdown-item {% if p.over_18 %}d-none{% endif %} list-inline-item text-danger" onclick="postToastSwitch(this,'/toggle_post_nsfw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Mark +18</button>
<button type="button" id="unmark-{{p.id}}" class="dropdown-item {% if not p.over_18 %}d-none{% endif %} list-inline-item text-success" onclick="postToastSwitch(this,'/toggle_post_nsfw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Unmark +18</button>
{% endif %}
{% if v.admin_level >= PERMS['USER_BAN'] and v.id != p.author_id %}
<button type="button" id="ban-{{p.fullname}}" class="dropdown-item {% if p.author.is_suspended %}d-none{% endif %} list-inline-item text-danger" data-bs-toggle="modal" data-bs-target="#banModal" onclick="banModal('/post/{{p.id}}', '{{p.author.id}}', '{{p.author_name}}', '{{p.fullname}}','d-none')"><i class="fas fa-user-slash text-danger fa-fw"></i>Ban user</button>
<button type="button" id="unban-{{p.fullname}}" class="dropdown-item {% if not p.author.is_suspended %}d-none{% endif %} list-inline-item text-success" id="unexile2-user-{{p.id}}" onclick="postToastSwitch(this,'/unban_user/{{p.author_id}}','ban-{{p.fullname}}','unban-{{p.fullname}}','d-none')"><i class="fas fa-user-slash"></i>Unban user</button>
{% endif %}
{% if v.admin_level >= PERMS['USER_AGENDAPOSTER'] and v.id != p.author_id %}
<button type="button" id="chud-{{p.fullname}}" class="dropdown-item {% if p.author.agendaposter %}d-none{% endif %} list-inline-item text-danger" data-bs-toggle="modal" data-bs-target="#chudModal" onclick="chudModal('/post/{{p.id}}', '{{p.author.id}}', '{{p.author_name}}', '{{p.fullname}}','d-none')"><i class="fas fa-face-sleeping text-danger fa-fw"></i>Chud user</button>
<button type="button" id="unchud-{{p.fullname}}" class="dropdown-item {% if not p.author.agendaposter %}d-none{% endif %} list-inline-item text-success" id="unexile2-user-{{p.id}}" onclick="postToastSwitch(this,'/unagendaposter/{{p.author_id}}','chud-{{p.fullname}}','unchud-{{p.fullname}}','d-none')"><i class="fas fa-face-sleeping"></i>Unchud user</button>
{% endif %}
</ul>
{% endif %}

View File

@ -21,6 +21,10 @@
<button type="button" id="club2-{{p.id}}" class="{% if p.club %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-info text-left" onclick="postToastSwitch(this,'/club_post/{{p.id}}','club2-{{p.id}}','unclub2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-slash mr-2"></i>Mark club</button>
<button type="button" id="unclub2-{{p.id}}" class="{% if not p.club %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-info text-left" onclick="postToastSwitch(this,'/unclub_post/{{p.id}}','club2-{{p.id}}','unclub2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye mr-2"></i>Unmark club</button>
{%- endif %}
{% if v.can_edit(p) %}
<button type="button" class="dropdown-item {% if p.new %} d-none{% endif %} list-inline-item text-info" id="{{p.id}}-sort-new-mobile" onclick="postToastSwitch(this, '/post/{{p.id}}/new', this.id, '{{p.id}}-unsort-new-mobile', 'd-none', null, 'PUT')"><i class="fas fa-sparkles text-center text-primary mr-2"></i>Set Default Sort New</button>
<button type="button" class="dropdown-item {% if not p.new %} d-none{% endif %} list-inline-item text-info" id="{{p.id}}-unsort-new-mobile" onclick="postToastSwitch(this, '/post/{{p.id}}/new', this.id, '{{p.id}}-sort-new-mobile', 'd-none', null, 'DELETE')"><i class="fas fa-sparkles text-center text-primary mr-2"></i>Unset Default Sort New</button>
{% endif %}
{% if v.admin_level >= PERMS['POST_COMMENT_DISTINGUISH'] and (v.id == p.author.id or v.admin_level >= PERMS['POST_COMMENT_MODERATION']) %}
<button type="button" id="distinguish2-{{p.id}}" class="{% if p.distinguish_level %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" onclick="postToastSwitch(this,'/distinguish/{{p.id}}','distinguish2-{{p.id}}','undistinguish2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-crown text-center text-primary mr-2"></i>Distinguish</button>

View File

@ -178,7 +178,7 @@
</div>
{% endif %}
{% if v and (v.id==p.author_id or v.admin_level >= PERMS['POST_EDITING']) and not v.is_suspended %}
{% if v and v.can_edit(p) and not v.is_suspended %}
<div id="edit-post-body-{{p.id}}" class="d-none comment-write collapsed child">
<form id="post-edit-form-{{p.id}}" action="/edit_post/{{p.id}}" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v|formkey}}">
@ -227,7 +227,7 @@
<span class="text-info d-none {{p.id}}-new-comments"></span>
</a>
{% if v and (v.id==p.author_id or v.admin_level >= PERMS['POST_EDITING']) %}
{% if v and v.can_edit(p) %}
<button type="button" class="list-inline-item" onclick="togglePostEdit('{{p.id}}')"><i class="fas fa-edit"></i>Edit</button>
{% endif %}
@ -321,9 +321,7 @@
</li>
</ul>
</div>
</div>
</div>
{% if v and v.id != p.author_id and p.body and not v_forbid_deleted %}
@ -334,7 +332,7 @@
<div class="col border-top">
<div class="comments-count py-3">
<div class="dropdown dropdown-actions">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" style="margin-right:0.5em;">
{% if sort=="hot" %}<i class="fas fa-fire mr-1"></i>{% endif %}
{% if sort=="top" %}<i class="fas fa-arrow-alt-circle-up mr-1"></i>{% endif %}
{% if sort=="bottom" %}<i class="fas fa-arrow-alt-circle-down mr-1"></i>{% endif %}
@ -474,7 +472,7 @@
{% include "modals/report_post.html" %}
{% endif %}
{% if v and (v.id == p.author_id or v.admin_level >= PERMS['POST_EDITING']) %}
{% if v and v.can_edit(p) %}
<script defer src="{{'js/togglePostEdit.js' | asset}}"></script>
{% endif %}

View File

@ -1018,7 +1018,8 @@ CREATE TABLE public.users (
coins_spent_on_hats integer DEFAULT 0 NOT NULL,
rainbow integer,
spider integer,
profanityreplacer integer DEFAULT 1 NOT NULL
profanityreplacer integer DEFAULT 1 NOT NULL,
last_viewed_reddit_notifs integer NOT NULL
);

View File

@ -227,6 +227,7 @@ SET row_security = off;
INSERT INTO public.hat_defs VALUES (716, 'Baked Beans Bucket Hat', 'This neighbor is wearing beans!', 2, 500, NULL, 1663647585);
INSERT INTO public.hat_defs VALUES (1002, 'Hearty Hands', 'I dont think hands should do that...', 2, 500, NULL, 1667084251);
INSERT INTO public.hat_defs VALUES (1003, 'Dragged', 'HELLLLLLLLLLPPPPPPPPPPPPPPPPP', 2, 500, NULL, 1667084272);
INSERT INTO public.hat_defs VALUES (718, 'Literally Me', 'Sving music, jazz? PTOOEY!', 2, 500, NULL, 1663801724);
@ -1170,7 +1171,7 @@ INSERT INTO public.hat_defs VALUES (911, 'Racecar Hat', 'I sleep in a hat with m
-- Name: hat_defs_id_seq; Type: SEQUENCE SET; Schema: public; Owner: -
--
SELECT pg_catalog.setval('public.hat_defs_id_seq', 1103, true);
SELECT pg_catalog.setval('public.hat_defs_id_seq', 1104, true);
--
@ -1375,7 +1376,6 @@ INSERT INTO public.marseys (name, author_id, tags, created_utc) VALUES
('marseybiting',2,'reaction datass lip bite dat ass',NULL),
('marseyblack',2,'basketball black african american bipoc coon fade jersey sports kang gang jogger',NULL),
('marseyblack2',2,'nigger african coon ape monkey bipoc excited happy',NULL),
('marseyblackandwhite',2,'black mixed white shredded manlet cat cute',1669229303),
('marseyblackcat',2,'cat black halloween homoween',1666656250),
('marseyblackcock',2,'cock chicken poultry bbc black ride mount cowgirl',NULL),
('marseyblackcop',2,'police officer acab thin blue line pig bipoc',NULL),
@ -1589,7 +1589,6 @@ INSERT INTO public.marseys (name, author_id, tags, created_utc) VALUES
('marseychonkerbutch',2,'fat obese brap bitch dyke dangerhair lesbian',NULL),
('marseychonkerfoid',2,'female woman fds fat obese hambeast landwhale porker pig bleeder birthing person bangs',NULL),
('marseychonkerfoidpuke',2,'fat huge sick yuck eat barf',1664574968),
('marseychonkernoticeme',2,'attention fat foid annoying',1669407871),
('marseychristmas',2,'candy cane santa christmas holiday',NULL),
('marseychristmasbulb',2,'ornament christmas happy holiday china',NULL),
('marseychristmasbulb2',2,'ornament holiday christmas',NULL),
@ -2677,7 +2676,6 @@ INSERT INTO public.marseys (name, author_id, tags, created_utc) VALUES
('marseyschizochadseethecapylove',2,'animated schizo schizocel rare love chad seethe capy aevann',NULL),
('marseyschizochadthankscapylove',2,'animated schizo schizocel rare love chad thanks queen capy aevann',NULL),
('marseyschizochadyescapylove',2,'animated schizo schizocel rare love chad yes capy aevann',NULL),
('marseyschizochiobulove',2,'chink asian tangerine feline singapore',1669161511),
('marseyschizodoggilove',2,'animated schizo schizocel rare love dussy doggi',NULL),
('marseyschizodongerlove',2,'animated schizo schizocel rare love kaiser uraniumdonger donger',NULL),
('marseyschizoducklove',2,'animated schizo schizocel rare love duck',NULL),

View File

@ -2484,7 +2484,7 @@ THESE are the times that try dramautists' souls. The summer shitposter and the s
{[para]}
That I am trapped in this particular irrelevancy is never more apparent to me than when I am banned from meta posts. Paralyzed by the neurotic lassitude engendered by meeting one's old meta posts at every turn, around every page refresh, inside my browser history, I go aimlessly from thread to thread. I decide to meet it head-on and sort by Old, and I view the contents on the page. A carp poll about Vampires and Furries. A Masterlawlz post, a photo of an asshole. My own posts begging to scam people. There is no final photo of an asshole for me to find. Nor is there any new polls from Carp, begging people for attention. I refresh the frontpage and look into its face, and do and do not see anything new. I close the webpage, and have another cup of coffee with Aevann. We get along very well, veterans of a guerilla war we never understood.
{[para]}
https://youtube.com/watch?v=5I884gOUONg
https://youtube.com/watch?v=2YYNPnql9YI
{[para]}
Fuck you. Dog walkings great. Excellent way to make side cash and get exercise, and being able to immediately make any dog fall in love with you is a skill I use on a monthly basis.
Clearly you've never held true power. When the dogs at the dog park won't leave your side, no matter how many times their owners call them. When the girl you have a crush on's dog breaks free and makes a break for it, and in seconds its sitting at your feet refusing to budge.
@ -3115,10 +3115,7 @@ Mashallah, a few more russian chimpouts and we will finally get the mandate by p
{[para]}
![](/images/16661820983697305.webp)
{[para]}
OP is streaming on YouTube lmao look at this loser
https://youtu.be/0alzveKoS2I
OP is streaming on YouTube lmao look at [this loser](https://youtu.be/0alzveKoS2I)
{[para]}
**Hi,**
<span style="color:#33b8f3">**I was going through your website & I personally see a lot of potential in your website & business**</span>
@ -3182,4 +3179,7 @@ just want to know. Who hurt you so badly that you have to act like a grade schoo
{[para]}
I want to fuck the conservative out of you. I can't help myself. Every time you come into my office with your low cut shirt and cross necklace subtly splayed across your immaculate breasts I think about taking you, right there, as a man (with your consent, of course, rape culture is not okay). I want to pull off your panties and eat your pussy until your juices trickle down to the lower classes. I want you to moan so hard and so loud that you can't form coherent words, let alone talk about what Rush Limbaugh said about immigrants the other day. Each time 'those people' crosses your lips I think about your mouth wrapped around my prodigious cock as my little people spill out of it. I want to make an anchor baby with you. I want to throw all the papers off my desk and ride you until gay marriage is legal in a majority of States or until you've come enough times to admit that maybe universal health care makes sense. And I mean all this respectfully, of course. I'm a feminist. Why do you do this to me. Why. Do you know what you're doing? Every time you come into my office and sit across from me and cross and recross your legs and talk about the weather and then (somehow) about how unemployment insurance is actually bad for poor people do you know that I'm wondering if your panties are equally as conservative? That I'm curious what you'd look like on top of me, my hands tweaking your nipples like doing so would be tweaking taxes on the top one percent? That I'm thinking about you looking back at me as I fuck you from behind, your Jesus necklace swaying back and forth as you scream "Drill, baby, drill!" You're not crazy, just politically hypocritical. Social conservatism is selfish and untenable. Your adherence to laws written when people owned slaves and the largest city was 1/10th of what it is now is ruining this god damn country. And I want you so bad. I want you so so bad. Ugh. Be my Monica Lewinsky. I'll be your Bill Clinton. Let's reach across the aisle... and into each other's pants.
{[para]}
![](/images/16697743092332416.webp)
![](/images/16697743092332416.webp)
{[para]}
![](/images/16699834958292103.webp)