Add cw to WPD #220

Closed
G-tix wants to merge 25 commits from <deleted>:master into master
23 changed files with 191 additions and 12 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -45,6 +45,7 @@ class Post(Base):
private = Column(Boolean, default=False)
comment_count = Column(Integer, default=0)
is_approved = Column(Integer, ForeignKey("users.id"))
cw = Column(Boolean, default=False)
nsfw = Column(Boolean, default=False)
is_bot = Column(Boolean, default=False)
upvotes = Column(Integer, default=1)
@ -167,6 +168,8 @@ class Post(Base):
@property
@lazy
def thumb_url(self):
if self.cw:
return f"{SITE_FULL_IMAGES}/i/cw.webp?x=6"
if self.nsfw:
return f"{SITE_FULL_IMAGES}/i/nsfw.webp?x=6"
elif not self.url:
@ -224,6 +227,7 @@ class Post(Base):
'created_utc': self.created_utc,
'id': self.id,
'title': self.title,
'is_cw': self.cw,
'is_nsfw': self.nsfw,
'is_bot': self.is_bot,
'thumb_url': self.thumb_url,

View File

@ -276,6 +276,11 @@ class User(Base):
def cursormarsey(self):
return session.get('cursormarsey', CURSORMARSEY_DEFAULT)
@property
@lazy
def cw_warnings(self):
return bool(session.get('cw_warnings', True))
@property
@lazy
def nsfw_warnings(self):

View File

@ -408,6 +408,7 @@ FEATURES = {
'MARSEYBUX': True,
'AWARDS': True,
'CHAT': True,
'CW_MARKING': False,
'PINS': True,
'PRONOUNS': False,
'BADGES': True,
@ -710,10 +711,11 @@ elif SITE == 'watchpeopledie.tv':
PIN_LIMIT = 4
WELCOME_MSG = """Hi, you! Welcome to WatchPeopleDie.tv, this really cool site where you can go to watch people die. I'm @CLiTPEELER! If you have any questions about how things work here, or suggestions on how to make them work better than they already do, definitely slide on into my DMs (no fat chicks).\n\nThere's an enormously robust suite of fun features we have here and we're always looking for more to add. Way, way too many to go over in an automated welcome message. And you're probably here for the videos of people dying more than any sort of weird, paradoxical digital community aspect anyway, so I won't bore you with a tedious overview of them. Just head on over to [your settings page](/settings/profile) and have a look at some of the basic profile stuff, at least. You can change your profile picture, username, flair, colors, banners, bio, profile anthem (autoplaying song on your page, like it's MySpace or some shit, hell yeah), CSS, all sorts of things.\n\nOr you can just go back to the main feed and carry on with watching people die. That's what the site is for, after all. Have fun!\n\nAnyway, in closing, WPD is entirely open source. We don't really need new full-time coders or anything, but if you'd like to take a look at our repo - or even submit a PR to change, fix, or add some things - go right ahead! Our codebase lives at https://fsdfsd.net/rDrama/rDrama\n\nWell, that's all. Thanks again for signing up. It's an automated message and all, but I really do mean that. Thank you, specifically. I love you. Romantically. Deeply. Passionately.\n\nHave fun!"""
FEATURES['PATRON_ICONS'] = True
FEATURES['NSFW_MARKING'] = False
FEATURES['BOTS'] = False
FEATURES['CW_MARKING'] = True
FEATURES['HAT_SUBMISSIONS'] = False
FEATURES['NSFW_MARKING'] = False
FEATURES['PATRON_ICONS'] = True
PERMS['POST_COMMENT_EDITING'] = 3
PERMS['MODS_EVERY_HOLE'] = 3

View File

@ -276,6 +276,11 @@ MODACTION_TYPES = {
"icon": 'fa-robot',
"color": 'bg-muted'
},
'set_cw': {
"str": "set {self.target_link} as Child Warning",
"icon": 'fa-eye-evil',
"color": 'bg-danger'
},
'set_flair_locked': {
"str": "set {self.target_link}'s flair (locked)",
"icon": 'fa-award',
@ -376,6 +381,11 @@ MODACTION_TYPES = {
"icon": 'fa-thumbtack fa-rotate--45',
"color": 'bg-muted'
},
'unset_cw': {
"str": "unset {self.target_link} as Child Warning",
"icon": 'fa-eye-evil',
"color": 'bg-success'
},
'unset_nsfw': {
"str": 'unset {self.target_link} as NSFW',
"icon": 'fa-eye-evil',

View File

@ -61,6 +61,14 @@ def error_500(e):
return error(e)
@app.post("/allow_cw")
@limiter.limit('1/second', scope=rpath)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
def allow_cw():
session["cw_cookies"] = int(time.time()) + 3600
redir = request.values.get("redir", "/")
return ''
@app.post("/allow_nsfw")
@limiter.limit('1/second', scope=rpath)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)

View File

@ -87,7 +87,7 @@ def front_all(v, hole=None):
return result
LIMITED_WPD_HOLES = {'aftermath', 'fights', 'gore', 'medical', 'request', 'selfharm',
LIMITED_WPD_HOLES = {'sandshit', 'slavshit', 'aftermath', 'fights', 'gore', 'medical', 'request', 'selfharm',
'discussion', 'meta', 'music', 'pets', 'social',
'countryclub', 'highrollerclub'}

View File

@ -105,6 +105,9 @@ def post_id(pid, v, anything=None, hole=None):
p = get_post(pid, v=v)
if not can_see(v, p): abort(403)
if not g.is_api_or_xhr and p.cw and not g.show_cw:
return render_template("errors/cw.html", v=v)
if not g.is_api_or_xhr and p.nsfw and not g.show_nsfw:
return render_template("errors/nsfw.html", v=v)
@ -534,11 +537,12 @@ def submit_post(v, hole=None):
body = process_files(request.files, v, body)
body = body[:POST_BODY_LENGTH_LIMIT(v)].strip() # process_files() adds content to the body, so we need to re-strip
flag_notify = (request.values.get("notify", "on") == "on")
flag_cw = FEATURES['CW_MARKING'] and request.values.get("cw", False, bool)
flag_ghost = request.values.get("ghost", False, bool) and v.can_post_in_ghost_threads
flag_new = request.values.get("new", False, bool) or 'megathread' in title.lower()
flag_notify = (request.values.get("notify", "on") == "on")
flag_nsfw = FEATURES['NSFW_MARKING'] and request.values.get("nsfw", False, bool)
flag_private = request.values.get("private", False, bool)
flag_ghost = request.values.get("ghost", False, bool) and v.can_post_in_ghost_threads
if flag_ghost: hole = None
@ -554,6 +558,7 @@ def submit_post(v, hole=None):
private=flag_private,
notify=flag_notify,
author_id=v.id,
cw=flag_cw,
nsfw=flag_nsfw,
new=flag_new,
app_id=v.client.application.id if v.client else None,
@ -646,6 +651,7 @@ def submit_post(v, hole=None):
c_jannied = Comment(author_id=AUTOJANNY_ID,
parent_post=p.id,
level=1,
cw=False,
nsfw=False,
is_bot=True,
app_id=None,
@ -760,6 +766,84 @@ def undelete_post_pid(pid, v):
return {"message": "Post undeleted!"}
@app.post("/mark_post_cw/<int:pid>")
@feature_required('CW_MARKING')
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def mark_post_cw(pid, v):
p = get_post(pid)
if p.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and not (p.hole and v.mods(p.hole)):
abort(403)
if p.cw and v.is_permabanned:
abort(403)
p.cw = True
g.db.add(p)
if p.author_id != v.id:
if v.admin_level >= PERMS['POST_COMMENT_MODERATION']:
ma = ModAction(
kind = "set_cw",
user_id = v.id,
target_post_id = p.id,
)
g.db.add(ma)
else:
ma = HoleAction(
hole = p.hole,
kind = "set_cw",
user_id = v.id,
target_post_id = p.id,
)
g.db.add(ma)
send_repeatable_notification(p.author_id, f"@{v.username} (a site admin) has marked [{p.title}](/post/{p.id}) as Child Warning")
return {"message": "Post has been marked as Child Warning!"}
@app.post("/unmark_post_cw/<int:pid>")
@feature_required('CW_MARKING')
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def unmark_post_cw(pid, v):
p = get_post(pid)
if p.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION'] and not (p.hole and v.mods(p.hole)):
abort(403)
if p.cw and v.is_permabanned:
abort(403)
p.cw = False
g.db.add(p)
if p.author_id != v.id:
if v.admin_level >= PERMS['POST_COMMENT_MODERATION']:
ma = ModAction(
kind = "unset_cw",
user_id = v.id,
target_post_id = p.id,
)
g.db.add(ma)
else:
ma = HoleAction(
hole = p.hole,
kind = "unset_cw",
user_id = v.id,
target_post_id = p.id,
)
g.db.add(ma)
send_repeatable_notification(p.author_id, f"@{v.username} (a site admin) has unmarked [{p.title}](/post/{p.id}) as Child Warning")
return {"message": "Post has been unmarked as Child Warning!"}
@app.post("/mark_post_nsfw/<int:pid>")
@feature_required('NSFW_MARKING')
@limiter.limit('1/second', scope=rpath)

View File

@ -11,17 +11,18 @@ from files.routes.wrappers import *
from files.__main__ import app
valid_params = [
'after',
'author',
'before',
'cw'
'domain',
'exact',
'hole',
'nsfw',
'post',
'before',
'after',
'exact',
'title',
'sentto',
'hole',
'subreddit',
'title',
]
def searchparse(text):
@ -110,6 +111,8 @@ def searchposts(v):
) for x in criteria['q']]
posts = posts.filter(*words)
if 'cw' in criteria: posts = posts.filter(Post.cw==True)
if 'nsfw' in criteria: posts = posts.filter(Post.nsfw==True)
if 'domain' in criteria:

View File

@ -189,6 +189,10 @@ def settings_personal_post(v):
updated = True
session["cursormarsey"] = int(request.values.get("cursormarsey") == 'true')
elif not updated and request.values.get("cw_warnings", v.cw_warnings) != v.cw_warnings:
updated = True
session["cw_warnings"] = int(request.values.get("cw_warnings") == 'true')
elif not updated and request.values.get("nsfw_warnings", v.nsfw_warnings) != v.nsfw_warnings:
updated = True
session["nsfw_warnings"] = int(request.values.get("nsfw_warnings") == 'true')

View File

@ -121,6 +121,7 @@ def get_logged_in_user():
if v and not IS_EVENT():
session.pop("event_music", None)
g.show_cw = SITE_NAME == 'WPD' or (v and not v.cw_warnings) or session.get('cw_cookies', 0) >= int(time.time())
g.show_nsfw = SITE_NAME == 'WPD' or (v and not v.nsfw_warnings) or session.get('nsfw_cookies', 0) >= int(time.time())
return v

View File

@ -0,0 +1,22 @@
{% extends "default.html" %}
{% block pagetitle %}Child Warning{% endblock %}
{% block pagetype %}error-451{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col col-md-5">
<div class="text-center px-3 mt-3">
<img alt=":#marseytwerking:" loading="lazy" src="{{SITE_FULL_IMAGES}}/e/marseytwerking.webp">
<h5>Child Warning</h5>
<p class="mb-3">This post is rated with a Child Warning (A child is either physically hurt or in close proximity to a traumatic event). Are you sure you want to proceed?</p>
<div class="btn-toolbar justify-content-center mb-4">
<form action="/allow_cw" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<input hidden name="redir" value="{{request.full_path}}">
<input type="submit" class="btn btn-danger" value="Yes">
</form>
<div class="mt-3"><a href="/" class="btn btn-secondary">No</a></div>
<div class="mt-5"><a href="/settings/advanced#cw_toggle" class="btn btn-secondary">Never show me this warning again</a></div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -74,6 +74,10 @@
<button type="button" id="unexile-{{p.id}}" class="{% if not p.author.exiler_username(p.hole) %}d-none{% endif %} dropdown-item list-inline-item text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/h/{{hole}}/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 FEATURES['CW_MARKING'] and (v.id == p.author_id or v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or (p.hole and v.mods(p.hole))) %}
<button type="button" id="mark-{{p.id}}" class="dropdown-item {% if p.cw %}d-none{% endif %} list-inline-item text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mark_post_cw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Mark Child Warning</button>
<button type="button" id="unmark-{{p.id}}" class="dropdown-item {% if not p.cw %}d-none{% endif %} list-inline-item text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmark_post_cw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Unmark Child Warning</button>
{% endif %}
{% if FEATURES['NSFW_MARKING'] and (v.id == p.author_id or v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or (p.hole and v.mods(p.hole))) %}
<button type="button" id="mark-{{p.id}}" class="dropdown-item {% if p.nsfw %}d-none{% endif %} list-inline-item text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mark_post_nsfw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Mark NSFW</button>
<button type="button" id="unmark-{{p.id}}" class="dropdown-item {% if not p.nsfw %}d-none{% endif %} list-inline-item text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmark_post_nsfw/{{p.id}}','mark-{{p.id}}','unmark-{{p.id}}','d-none')"><i class="fas fa-eye-evil"></i>Unmark NSFW</button>

View File

@ -50,6 +50,11 @@
{% endif %}
{% endif %}
{% if FEATURES['CW_MARKING'] and (v.id == p.author_id or (p.hole and v.mods(p.hole))) %}
<button type="button" id="mark3-{{p.id}}" class="{% if p.cw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mark_post_cw/{{p.id}}','mark3-{{p.id}}','unmark3-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center mr-2"></i>Mark Child Warning</button>
<button type="button" id="unmark3-{{p.id}}" class="{% if not p.cw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmark_post_cw/{{p.id}}','mark3-{{p.id}}','unmark3-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center mr-2"></i>Unmark Child Warning</button>
{% endif %}
{% if FEATURES['NSFW_MARKING'] and (v.id == p.author_id or (p.hole and v.mods(p.hole))) %}
<button type="button" id="mark3-{{p.id}}" class="{% if p.nsfw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mark_post_nsfw/{{p.id}}','mark3-{{p.id}}','unmark3-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center mr-2"></i>Mark NSFW</button>
<button type="button" id="unmark3-{{p.id}}" class="{% if not p.nsfw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmark_post_nsfw/{{p.id}}','mark3-{{p.id}}','unmark3-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center mr-2"></i>Unmark NSFW</button>

View File

@ -33,6 +33,10 @@
<button type="button" id="remove2-{{p.id}}" class="{% if p.is_banned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-danger text-left" data-nonce="{{g.nonce}}" data-onclick="removePost(this,'{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-ban text-center mr-2"></i>Remove</button>
<button type="button" id="approve2-{{p.id}}" class="{% if not p.is_banned and request.path != '/admin/reported/posts' %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-success text-left" data-nonce="{{g.nonce}}" data-onclick="approvePost(this,'{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-check text-center mr-2"></i>Approve</button>
{% endif %}
{% if FEATURES['CW_MARKING'] and v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}
<button type="button" id="mark2-{{p.id}}" class="{% if p.cw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mark_post_cw/{{p.id}}','mark2-{{p.id}}','unmark2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center text-danger mr-2"></i>Mark Child Warning</button>
<button type="button" id="unmark2-{{p.id}}" class="{% if not p.cw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmark_post_cw/{{p.id}}','mark2-{{p.id}}','unmark2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center text-success mr-2"></i>Unmark Child Warning</button>
{% endif %}
{% if FEATURES['NSFW_MARKING'] and v.admin_level >= PERMS['POST_COMMENT_MODERATION'] %}
<button type="button" id="mark2-{{p.id}}" class="{% if p.nsfw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mark_post_nsfw/{{p.id}}','mark2-{{p.id}}','unmark2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center text-danger mr-2"></i>Mark NSFW</button>
<button type="button" id="unmark2-{{p.id}}" class="{% if not p.nsfw %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmark_post_nsfw/{{p.id}}','mark2-{{p.id}}','unmark2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-eye-evil text-center text-success mr-2"></i>Unmark NSFW</button>

View File

@ -19,8 +19,10 @@
<div class="col-12">
<div id="post-{{p.id}}" class="banned {% if p.award_count('glowie', v) %}glow{% endif %} card d-flex flex-row-reverse flex-nowrap justify-content-end border-0 p-0 {% if voted == 1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}">
<div class="card-block my-md-auto">
<div class="post-meta text-left d-md-none mb-1">{% if p.cw %}<span class="badge badge-danger">Child Warning</span> {% endif %}{% if p.is_banned %}Removed by @{{p.ban_reason}} (Site Admin){% else %}[Deleted by user]{% endif %}</div>
<div class="post-meta text-left d-md-none mb-1">{% if p.nsfw %}<span class="badge badge-danger">NSFW</span> {% endif %}{% if p.is_banned %}Removed by @{{p.ban_reason}} (Site Admin){% else %}[Deleted by user]{% endif %}</div>
<h5 class="card-title post-title text-left mb-0 mb-md-1">{{p.plaintitle(v)}}</h5>
<div class="post-meta text-left d-mob-none">{% if p.cw %}<span class="badge badge-danger">Child Warning</span> {% endif %}{% if p.is_banned %}Removed by @{{p.ban_reason}} (Site Admin){% else %}[Deleted by user]{% endif %}</div>
<div class="post-meta text-left d-mob-none">{% if p.nsfw %}<span class="badge badge-danger">NSFW</span> {% endif %}{% if p.is_banned %}Removed by @{{p.ban_reason}} (Site Admin){% else %}[Deleted by user]{% endif %}</div>
</div>
<div id="voting" class="d-md-block my-auto mr-3 text-center">

View File

@ -29,7 +29,7 @@
{{macros.reports(p, 'post')}}
<div id="post-{{p.id}}" class="actual-post card {% if p.ghost %}ghost-post{% endif %} {% if p.unread %}unread{% endif %} {% if p.is_banned %} banned{% endif %}{% if p.deleted_utc %} deleted{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted == 1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}{% if p.nsfw %} nsfw{% endif %}">
<div id="post-{{p.id}}" class="actual-post card {% if p.ghost %}ghost-post{% endif %} {% if p.unread %}unread{% endif %} {% if p.is_banned %} banned{% endif %}{% if p.deleted_utc %} deleted{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted == 1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}{% if p.cw %} cw{% endif %}{% if p.nsfw %} nsfw{% endif %}">
<div class="d-flex flex-row-reverse flex-md-row flex-nowrap" style="align-items:flex-start">
<div class="voting my-2 d-none d-md-flex pr-2">
{% if v %}

View File

@ -84,6 +84,12 @@
</div>
{% endif %}
{% endif %}
{% if FEATURES['CW_MARKING'] %}
<div>
<div style="display: inline-block; width: 150px; text-align: center">Child Warning Only:</div>
<button type="button" data-nonce="{{g.nonce}}" data-onclick="addParam(this, 'bool')" class="searchparam mb-1">cw:true</button>
</div>
{% endif %}
{% if FEATURES['NSFW_MARKING'] %}
<div>
<div style="display: inline-block; width: 150px; text-align: center">NSFW Only:</div>

View File

@ -127,6 +127,9 @@
<h5>Content Filters</h5>
<div class="settings-section rounded">
{{common.toggle_section('User Signatures', 'show_sigs', 'show_sigs', v.show_sigs, 'Show user signatures.', false)}}
{% if FEATURES['CW_MARKING'] %}
{{common.toggle_section('Child Warnings', 'cw_warnings', 'cw_warnings', v.cw_warnings, "Show a warning when viewing posts containing children.", false)}}
{% endif %}
{% if FEATURES['NSFW_MARKING'] %}
{{common.toggle_section('NSFW Warnings', 'nsfw_warnings', 'nsfw_warnings', v.nsfw_warnings, "Show a warning when viewing NSFW posts and collapse NSFW comments.", false)}}
{% endif %}
@ -156,4 +159,4 @@
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -45,6 +45,10 @@
</div>
{% endif %}
<div class="sidebar-wpd--holes sidebar-wpd--holes--2">
<a href="/h/sandshit"><b>Sandshit</b></a>
<a href="/h/slavshit"><b>Slavshit</b></a>
<div class="sidebar-wpd--holes sidebar-wpd--holes--3">
<a href="/h/accident">Accident</a>
<a href="/h/animal">Animal</a>

View File

@ -73,6 +73,12 @@
<input autocomplete="off" type="checkbox" class="custom-control-input" id="post-new" name="new" data-nonce="{{g.nonce}}" data-onchange="savetext()">
<label class="custom-control-label" for="post-new">Make the default comment sorting "new"</label>
</div>
{% if FEATURES['CW_MARKING'] %}
<div class="custom-control custom-checkbox">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="post-cw" name="Child Warning" data-nonce="{{g.nonce}}" data-onchange="savetext()">
<label class="custom-control-label" for="post-cw">Child Warning</label>
</div>
{% endif %}
{% if FEATURES['NSFW_MARKING'] %}
<div class="custom-control custom-checkbox">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="post-nsfw" name="nsfw" data-nonce="{{g.nonce}}" data-onchange="savetext()">

View File

@ -55,6 +55,7 @@
{% if p.is_pinned and request.path != '/' %}
<i class="fas fa-thumbtack fa-rotate--45 pr-1 ml-1 mt-3 text-admin" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Pinned to profile"></i>
{% endif %}
{% if p.cw %}<span class="badge badge-danger text-small-extra mr-1">Child Warning</span>{% endif %}
{% if p.nsfw %}<span class="badge badge-danger text-small-extra mr-1">NSFW</span>{% endif %}
{% if p.is_bot %} <i class="fas fa-robot text-info" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bot"></i>{% endif %}
{% if p.is_blocking and not p.ghost %}<i class="fas fa-user-minus text-warning" data-bs-toggle="tooltip" data-bs-placement="bottom" title="You're blocking this user."></i>{% endif %}

View File

@ -825,6 +825,7 @@ CREATE TABLE public.posts (
author_id integer NOT NULL,
created_utc integer NOT NULL,
is_banned boolean DEFAULT false NOT NULL,
cw boolean DEFAULT false NOT NULL,
nsfw boolean DEFAULT false NOT NULL,
distinguish_level integer DEFAULT 0 NOT NULL,
deleted_utc integer DEFAULT 0 NOT NULL,