add nsfw warning pages to posts when nsfw content is disabled

This commit is contained in:
teddit 2020-12-27 23:25:39 +01:00
parent 357c6402cd
commit 5f8e8eb7a3
8 changed files with 224 additions and 154 deletions

6
app.js
View File

@ -110,6 +110,12 @@ const preferencesMiddleware = (req, res, next) => {
res.cookie('flairs', flairsOverride, { maxAge: 31536000, httpOnly: true }) res.cookie('flairs', flairsOverride, { maxAge: 31536000, httpOnly: true })
} }
let nsfwEnabledOverride = req.query.nsfw_enabled
if(nsfwEnabledOverride) {
req.cookies.nsfw_enabled = nsfwEnabledOverride
res.cookie('nsfw_enabled', nsfwEnabledOverride, { maxAge: 31536000, httpOnly: true })
}
next() next()
} }

View File

@ -1,6 +1,6 @@
const config = { const config = {
domain: process.env.DOMAIN || '127.0.0.1', // Or for example 'teddit.net' domain: process.env.DOMAIN || '127.0.0.1', // Or for example 'teddit.net'
reddit_app_id: process.env.REDDIT_APP_ID || 'H6-HjZ5pUPjaFQ', // You should obtain your own Reddit app ID. For testing purposes it's okay to use this project's default app ID. Create your Reddit app here: https://old.reddit.com/prefs/apps/. Make sure to create an "installed app" type of app. reddit_app_id: process.env.REDDIT_APP_ID || 'ABfYqdDc9qPh1w', // You should obtain your own Reddit app ID. For testing purposes it's okay to use this project's default app ID. Create your Reddit app here: https://old.reddit.com/prefs/apps/. Make sure to create an "installed app" type of app.
cert_dir: process.env.CERT_DIR || '', // For example '/home/teddit/letsencrypt/live/teddit.net', if you are using https. No trailing slash. cert_dir: process.env.CERT_DIR || '', // For example '/home/teddit/letsencrypt/live/teddit.net', if you are using https. No trailing slash.
flairs_enabled: process.env.FLAIRS_ENABLED !== "true" || true, // Enables the rendering of user and link flairs on teddit flairs_enabled: process.env.FLAIRS_ENABLED !== "true" || true, // Enables the rendering of user and link flairs on teddit
api_enabled: process.env.API_ENABLED !== "true" || true, // Teddit API feature. Might increase loads significantly on your instance. api_enabled: process.env.API_ENABLED !== "true" || true, // Teddit API feature. Might increase loads significantly on your instance.
@ -20,6 +20,8 @@ const config = {
use_helmet_hsts: process.env.USE_HELMET_HSTS === "true" || false, // Recommended to be true when using https use_helmet_hsts: process.env.USE_HELMET_HSTS === "true" || false, // Recommended to be true when using https
trust_proxy: process.env.TRUST_PROXY === "true" || false, // Enable trust_proxy if you are using reverse proxy like nginx trust_proxy: process.env.TRUST_PROXY === "true" || false, // Enable trust_proxy if you are using reverse proxy like nginx
trust_proxy_address: process.env.TRUST_PROXY_ADDRESS || '127.0.0.1', trust_proxy_address: process.env.TRUST_PROXY_ADDRESS || '127.0.0.1',
nsfw_enabled: process.env.NSFW_ENABLED !== "true" || true, // Enable NSFW (over 18) content. If false, a warning is shown to the user before opening any NSFW post. When the NFSW content is disabled, NSFW posts are hidden from subreddits and from user page feeds. Note: Users can set this to true or false from their preferences.
post_comments_sort: process.env.POST_COMMENTS_SORT || 'confidence', // "confidence" is the default sorting in Reddit. Must be one of: confidence, top, new, controversial, old, random, qa, live.
setexs: { setexs: {
/**, /**,
* Redis cache expiration values (in seconds). * Redis cache expiration values (in seconds).
@ -34,7 +36,6 @@ const config = {
sidebar: 60 * 60 * 24 * 7, // 7 days sidebar: 60 * 60 * 24 * 7, // 7 days
shorts: 60 * 60 * 24 * 31 shorts: 60 * 60 * 24 * 31
}, },
post_comments_sort: 'confidence', // "confidence" is the default sorting in Reddit. Must be one of: confidence, top, new, controversial, old, random, qa, live.
valid_media_domains: ['preview.redd.it', 'external-preview.redd.it', 'i.redd.it', 'v.redd.it', 'a.thumbs.redditmedia.com', 'b.thumbs.redditmedia.com', 'emoji.redditmedia.com', 'thumbs.gfycat.com', 'i.ytimg.com'], valid_media_domains: ['preview.redd.it', 'external-preview.redd.it', 'i.redd.it', 'v.redd.it', 'a.thumbs.redditmedia.com', 'b.thumbs.redditmedia.com', 'emoji.redditmedia.com', 'thumbs.gfycat.com', 'i.ytimg.com'],
reddit_api_error_text: `Seems like your instance is either blocked (e.g. due to API rate limiting), reddit is currently down, or your API key is expired and not renewd properly. This can also happen for other reasons.` reddit_api_error_text: `Seems like your instance is either blocked (e.g. due to API rate limiting), reddit is currently down, or your API key is expired and not renewd properly. This can also happen for other reasons.`
}; };

View File

@ -27,6 +27,11 @@ module.exports = function() {
let images = null let images = null
let is_self_link = false let is_self_link = false
let valid_reddit_self_domains = ['reddit.com'] let valid_reddit_self_domains = ['reddit.com']
if(data.over_18)
if((config.nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false')
continue
if(data.domain) { if(data.domain) {
let tld = data.domain.split('self.') let tld = data.domain.split('self.')

View File

@ -1,4 +1,5 @@
module.exports = function() { module.exports = function() {
const config = require('../config');
this.processJsonUser = function(json, parsed, after, before, user_preferences) { this.processJsonUser = function(json, parsed, after, before, user_preferences) {
return new Promise(resolve => { return new Promise(resolve => {
(async () => { (async () => {
@ -39,6 +40,10 @@ module.exports = function() {
let post_id = post.permalink.split('/').slice(-2)[0] + '/' let post_id = post.permalink.split('/').slice(-2)[0] + '/'
let url = post.permalink.replace(post_id, '') let url = post.permalink.replace(post_id, '')
if(post.over_18)
if((config.nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false')
continue
if(type === 't3') { if(type === 't3') {
let duration = null let duration = null

View File

@ -15,11 +15,13 @@ module.exports = (app, redis, fetch, RedditAPI) => {
}) })
app.get('/preferences', (req, res, next) => { app.get('/preferences', (req, res, next) => {
return res.render('preferences', { user_preferences: req.cookies }) return res.render('preferences', { user_preferences: req.cookies, instance_config: config })
}) })
app.get('/resetprefs', (req, res, next) => { app.get('/resetprefs', (req, res, next) => {
res.clearCookie('theme') res.clearCookie('theme')
res.clearCookie('flairs')
res.clearCookie('nsfw_enabled')
return res.redirect('/preferences') return res.redirect('/preferences')
}) })
@ -501,7 +503,8 @@ module.exports = (app, redis, fetch, RedditAPI) => {
post_url: post_url, post_url: post_url,
subreddit: subreddit, subreddit: subreddit,
sortby: sortby, sortby: sortby,
user_preferences: req.cookies user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled
}) })
} else { } else {
let key = `morechildren:${post_url};1` let key = `morechildren:${post_url};1`
@ -534,7 +537,8 @@ module.exports = (app, redis, fetch, RedditAPI) => {
subreddit: req.params.subreddit, subreddit: req.params.subreddit,
sortby: sortby, sortby: sortby,
more_comments_page: 1, more_comments_page: 1,
user_preferences: req.cookies user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled
}) })
})() })()
}) })
@ -566,7 +570,8 @@ module.exports = (app, redis, fetch, RedditAPI) => {
post_url: post_url, post_url: post_url,
subreddit: subreddit, subreddit: subreddit,
sortby: sortby, sortby: sortby,
user_preferences: req.cookies user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled
}) })
})() })()
} }
@ -736,6 +741,7 @@ module.exports = (app, redis, fetch, RedditAPI) => {
app.post('/saveprefs', (req, res, next) => { app.post('/saveprefs', (req, res, next) => {
let theme = req.body.theme let theme = req.body.theme
let flairs = req.body.flairs let flairs = req.body.flairs
let nsfw_enabled = req.body.nsfw_enabled
res.cookie('theme', theme, { maxAge: 365 * 24 * 60 * 60 * 1000, httpOnly: true }) res.cookie('theme', theme, { maxAge: 365 * 24 * 60 * 60 * 1000, httpOnly: true })
@ -744,6 +750,13 @@ module.exports = (app, redis, fetch, RedditAPI) => {
else else
flairs = 'false' flairs = 'false'
res.cookie('flairs', flairs, { maxAge: 365 * 24 * 60 * 60 * 1000, httpOnly: true }) res.cookie('flairs', flairs, { maxAge: 365 * 24 * 60 * 60 * 1000, httpOnly: true })
if(nsfw_enabled === 'on')
nsfw_enabled = 'true'
else
nsfw_enabled = 'false'
res.cookie('nsfw_enabled', nsfw_enabled, { maxAge: 365 * 24 * 60 * 60 * 1000, httpOnly: true })
return res.redirect('/preferences') return res.redirect('/preferences')
}) })

View File

@ -297,12 +297,13 @@ form legend {
padding-bottom: 10px; padding-bottom: 10px;
} }
form .setting { form .setting {
margin: 10px 0px; margin: 10px 0px;
width: 100%; width: 100%;
} }
.container .content p.notice { .container .content small.notice {
padding-top: 20px; padding-top: 20px;
padding-bottom: 20px; padding-bottom: 5px;
display: inline-block;
} }
.container .content p.version { .container .content p.version {
text-align: right; text-align: right;
@ -851,6 +852,30 @@ input[type="submit"]:hover,
#post .source-url { #post .source-url {
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
#post .nsfw-warning {
text-align: center;
float: left;
width: 100%;
margin: 40px 0px;
}
#post .nsfw-warning span {
font-size: 3rem;
background: #ff575b;
border-radius: 130px;
display: inline-block;
padding: 39px 20px 39px 20px;
color: white;
}
#post .nsfw-warning h2 {
margin: 20px 0px;
}
#post .nsfw-warning a {
margin: 20px;
display: inline-block;
background: #4f86b5;
color: white;
padding: 16px;
}
/* USER */ /* USER */
#user .entries { #user .entries {
float: left; float: left;

View File

@ -10,166 +10,175 @@ html
p #{JSON.stringify(error_data)} p #{JSON.stringify(error_data)}
else else
#post #post
header if post.over_18 && (instance_nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false'
div .nsfw-warning
p subreddit: span 18+
a(href="/r/" + subreddit + "") h2 You must be 18+ to view this community
p /r/#{subreddit} p You must be at least eighteen years old to view this content. Are you over eighteen and willing to see adult content?
.info a(href="/") No thank you
.score a(href="?nsfw_enabled=true") Continue
div.arrow p If you continue, <code>nsfw_enabled</code> cookie preference will be automatically set to <code>true</code>.
span #{kFormatter(post.ups)} else
div.arrow.down header
.title div
a(href="" + post.url + "") p subreddit:
h2 #{cleanTitle(post.title)} a(href="/r/" + subreddit + "")
!= post.link_flair p /r/#{subreddit}
span(class="domain") (#{post.domain}) .info
p.submitted
span(title="" + toUTCString(post.created) + "") submitted #{timeDifference(post.created)} by
if post.author === '[deleted]'
span [deleted]
else
a(href="/u/" + post.author + "")
| #{post.author}
!= post.user_flair
.links
if post.over_18
span.tag.nsfw NSFW
if post.crosspost.is_crosspost === true
.crosspost
.title
a(href="" + post.crosspost.permalink + "")
h2 #{cleanTitle(post.crosspost.title)}
span(class="domain") (#{post.domain})
.num_comments
| #{post.crosspost.num_comments} comments
.score .score
div.arrow div.arrow
span #{kFormatter(post.crosspost.ups)} span #{kFormatter(post.ups)}
div.arrow.down div.arrow.down
.title
a(href="" + post.url + "")
h2 #{cleanTitle(post.title)}
!= post.link_flair
span(class="domain") (#{post.domain})
p.submitted p.submitted
span(title="" + toUTCString(post.crosspost.created) + "") submitted #{timeDifference(post.crosspost.created)} by span(title="" + toUTCString(post.created) + "") submitted #{timeDifference(post.created)} by
if post.crosspost.author === '[deleted]' if post.author === '[deleted]'
span [deleted] span [deleted]
else else
a(href="/u/" + post.crosspost.author + "") a(href="/u/" + post.author + "")
| #{post.crosspost.author} | #{post.author}
!= post.user_flair != post.user_flair
p.to to .links
a(href="/r/" + post.crosspost.subreddit + "") if post.over_18
| #{post.crosspost.subreddit} span.tag.nsfw NSFW
if post.crosspost.is_crosspost === true
.crosspost
.title
a(href="" + post.crosspost.permalink + "")
h2 #{cleanTitle(post.crosspost.title)}
span(class="domain") (#{post.domain})
.num_comments
| #{post.crosspost.num_comments} comments
.score
div.arrow
span #{kFormatter(post.crosspost.ups)}
div.arrow.down
p.submitted
span(title="" + toUTCString(post.crosspost.created) + "") submitted #{timeDifference(post.crosspost.created)} by
if post.crosspost.author === '[deleted]'
span [deleted]
else
a(href="/u/" + post.crosspost.author + "")
| #{post.crosspost.author}
!= post.user_flair
p.to to
a(href="/r/" + post.crosspost.subreddit + "")
| #{post.crosspost.subreddit}
if !post.has_media
if post.images
.image
a(href="" + post.images.source + "")
img(src="" + post.images.source + "", alt="")
else
if post.media.not_hosted_in_reddit
.video
a(href="" + post.media.source + "")
img(src=""+ post.media.source +"")
p Embed URL:
a(href="" + post.media.embed_src + "", target="_blank")
p #{post.media.embed_src}
else
.video
video(controls="controls", autoplay="autoplay", loop="loop")
source(src="" + post.media.source + "", type="video/mp4")
| Your browser does not support the video element.
a(href="" + post.media.source + "") [media]
else
if !post.has_media if !post.has_media
if post.gallery
.gallery
each item in post.gallery_items
.item
div
a(href="" + item.large + "", target="_blank")
img(src=""+ item.thumbnail +"", alt="")
a(href="" + item.source + "", target="_blank", class="source-link")
small source
if post.images if post.images
.image .image
a(href="" + post.images.source + "") a(href="" + post.images.source + "")
img(src="" + post.images.source + "", alt="") img(src="" + post.images.source + "", alt="")
else else
if post.media.not_hosted_in_reddit if post.media.not_hosted_in_reddit
.video if post.media.source === 'YouTube'
a(href="" + post.media.source + "") .video
img(src=""+ post.media.source +"") .title
p Embed URL: a(href="" + post.media.embed_src + "", target="_blank")
a(href="" + post.media.embed_src + "", target="_blank") p #{cleanTitle(post.media.title)}
p #{post.media.embed_src} span(class="domain") (#{post.domain})
.video-holder
a(href="" + post.media.embed_src + "", target="_blank")
img(src="" + post.media.thumbnail + "")
div(class="youtube-info")
p YouTube video info:
p #{cleanTitle(post.media.title)} <a href="#{post.media.embed_src}">#{post.media.embed_src}</a>
p #{post.media.author_name} <a href="#{post.media.author_url}">#{post.media.author_url}</a>
else
if post.media.source === 'external'
if post.images
.image
a(href="" + post.media.embed_src + "", target="_blank")
img(src="" + post.images.source + "", alt="")
if !post.media.embed_src.startsWith("https://twitter.com")
p
| source:
a(href="" + post.media.embed_src + "", target="_blank")
p(class="source-url") #{post.media.embed_src}
else
.video
a(href="" + post.media.source + "")
img(src="" + post.media.source + "")
p Embed URL:
a(href="" + post.media.embed_src + "", target="_blank")
p #{post.media.embed_src}
else else
.video .video
video(controls="controls", autoplay="autoplay", loop="loop") video(controls="controls", autoplay="autoplay", loop="loop")
source(src="" + post.media.source + "", type="video/mp4") source(src="" + post.media.source + "", type="video/mp4")
| Your browser does not support the video element. | Your browser does not support the video element.
a(href="" + post.media.source + "") [media] a(href="" + post.media.source + "") [media]
else if post.selftext
if !post.has_media div.usertext-body !{post.selftext}
if post.gallery if post.contest_mode
.gallery .infobar.blue
each item in post.gallery_items p this thread is in contest mode - contest mode randomizes comment sorting and hides scores.
.item if viewing_comment
div .infobar
a(href="" + item.large + "", target="_blank") p you are viewing a single comment's thread.
img(src=""+ item.thumbnail +"", alt="") a(href="" + post_url + "") view the rest of the comments →
a(href="" + item.source + "", target="_blank", class="source-link") .comments-info
small source p all #{post.num_comments} comments
if post.images .comments-sort
.image details
a(href="" + post.images.source + "") summary
img(src="" + post.images.source + "", alt="") if sortby === 'confidence'
else span sorted by: <strong>best</strong>
if post.media.not_hosted_in_reddit if sortby === 'top'
if post.media.source === 'YouTube' span sorted by: <strong>top</strong>
.video if sortby === 'new'
.title span sorted by: <strong>new</strong>
a(href="" + post.media.embed_src + "", target="_blank") if sortby === 'controversial'
p #{cleanTitle(post.media.title)} span sorted by: <strong>controversial</strong>
span(class="domain") (#{post.domain}) if sortby === 'old'
.video-holder span sorted by: <strong>old</strong>
a(href="" + post.media.embed_src + "", target="_blank") if sortby === 'qa'
img(src="" + post.media.thumbnail + "") span sorted by: <strong>q&a</strong>
div(class="youtube-info") ul
p YouTube video info: li(class=sortby === 'confidence' ? 'active' : '')
p #{cleanTitle(post.media.title)} <a href="#{post.media.embed_src}">#{post.media.embed_src}</a> a(href="?sort=confidence") best
p #{post.media.author_name} <a href="#{post.media.author_url}">#{post.media.author_url}</a> li(class=sortby === 'top' ? 'active' : '')
else a(href="?sort=top") top
if post.media.source === 'external' li(class=sortby === 'new' ? 'active' : '')
if post.images a(href="?sort=new") new
.image li(class=sortby === 'controversial' ? 'active' : '')
a(href="" + post.media.embed_src + "", target="_blank") a(href="?sort=controversial") controversial
img(src="" + post.images.source + "", alt="") li(class=sortby === 'old' ? 'active' : '')
if !post.media.embed_src.startsWith("https://twitter.com") a(href="?sort=old") old
p li(class=sortby === 'qa' ? 'active' : '')
| source: a(href="?sort=qa") Q&A
a(href="" + post.media.embed_src + "", target="_blank") != comments
p(class="source-url") #{post.media.embed_src}
else
.video
a(href="" + post.media.source + "")
img(src="" + post.media.source + "")
p Embed URL:
a(href="" + post.media.embed_src + "", target="_blank")
p #{post.media.embed_src}
else
.video
video(controls="controls", autoplay="autoplay", loop="loop")
source(src="" + post.media.source + "", type="video/mp4")
| Your browser does not support the video element.
a(href="" + post.media.source + "") [media]
if post.selftext
div.usertext-body !{post.selftext}
if post.contest_mode
.infobar.blue
p this thread is in contest mode - contest mode randomizes comment sorting and hides scores.
if viewing_comment
.infobar
p you are viewing a single comment's thread.
a(href="" + post_url + "") view the rest of the comments →
.comments-info
p all #{post.num_comments} comments
.comments-sort
details
summary
if sortby === 'confidence'
span sorted by: <strong>best</strong>
if sortby === 'top'
span sorted by: <strong>top</strong>
if sortby === 'new'
span sorted by: <strong>new</strong>
if sortby === 'controversial'
span sorted by: <strong>controversial</strong>
if sortby === 'old'
span sorted by: <strong>old</strong>
if sortby === 'qa'
span sorted by: <strong>q&a</strong>
ul
li(class=sortby === 'confidence' ? 'active' : '')
a(href="?sort=confidence") best
li(class=sortby === 'top' ? 'active' : '')
a(href="?sort=top") top
li(class=sortby === 'new' ? 'active' : '')
a(href="?sort=new") new
li(class=sortby === 'controversial' ? 'active' : '')
a(href="?sort=controversial") controversial
li(class=sortby === 'old' ? 'active' : '')
a(href="?sort=old") old
li(class=sortby === 'qa' ? 'active' : '')
a(href="?sort=qa") Q&A
!= comments

View File

@ -25,6 +25,12 @@ html
input(type="checkbox", name="flairs", id="flairs", checked="checked") input(type="checkbox", name="flairs", id="flairs", checked="checked")
else else
input(type="checkbox", name="flairs", id="flairs") input(type="checkbox", name="flairs", id="flairs")
p(class="notice") Preferences are stored client-side using cookies without any personal information. .setting
label(for="nsfw_enabled") Show NSFW content:
if (instance_config.nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false'
input(type="checkbox", name="nsfw_enabled", id="nsfw_enabled")
else
input(type="checkbox", name="nsfw_enabled", id="nsfw_enabled", checked="checked")
small(class="notice") Preferences are stored client-side using cookies without any personal information.
input(type="submit", value="Save preferences") input(type="submit", value="Save preferences")
a(href="/resetprefs", class="btn") Reset preferences a(href="/resetprefs", class="btn") Reset preferences