From 5f8e8eb7a33eb44cded1efa78d0c7a0091268707 Mon Sep 17 00:00:00 2001 From: teddit Date: Sun, 27 Dec 2020 23:25:39 +0100 Subject: [PATCH] add nsfw warning pages to posts when nsfw content is disabled --- app.js | 6 + config.js.template | 5 +- inc/processJsonSubreddit.js | 5 + inc/processJsonUser.js | 5 + routes.js | 21 ++- static/css/styles.css | 35 ++++- views/post.pug | 293 +++++++++++++++++++----------------- views/preferences.pug | 8 +- 8 files changed, 224 insertions(+), 154 deletions(-) diff --git a/app.js b/app.js index 848fbdd..ad1d719 100644 --- a/app.js +++ b/app.js @@ -110,6 +110,12 @@ const preferencesMiddleware = (req, res, next) => { 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() } diff --git a/config.js.template b/config.js.template index dcab1cf..46b5491 100644 --- a/config.js.template +++ b/config.js.template @@ -1,6 +1,6 @@ const config = { 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. 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. @@ -20,6 +20,8 @@ const config = { 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_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: { /**, * Redis cache expiration values (in seconds). @@ -34,7 +36,6 @@ const config = { sidebar: 60 * 60 * 24 * 7, // 7 days 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'], 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.` }; diff --git a/inc/processJsonSubreddit.js b/inc/processJsonSubreddit.js index 6b05760..4597ded 100644 --- a/inc/processJsonSubreddit.js +++ b/inc/processJsonSubreddit.js @@ -27,6 +27,11 @@ module.exports = function() { let images = null let is_self_link = false 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) { let tld = data.domain.split('self.') diff --git a/inc/processJsonUser.js b/inc/processJsonUser.js index cc101f4..05ae464 100644 --- a/inc/processJsonUser.js +++ b/inc/processJsonUser.js @@ -1,4 +1,5 @@ module.exports = function() { + const config = require('../config'); this.processJsonUser = function(json, parsed, after, before, user_preferences) { return new Promise(resolve => { (async () => { @@ -39,6 +40,10 @@ module.exports = function() { let post_id = post.permalink.split('/').slice(-2)[0] + '/' 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') { let duration = null diff --git a/routes.js b/routes.js index 4c6a6d1..9d50095 100644 --- a/routes.js +++ b/routes.js @@ -15,11 +15,13 @@ module.exports = (app, redis, fetch, RedditAPI) => { }) 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) => { res.clearCookie('theme') + res.clearCookie('flairs') + res.clearCookie('nsfw_enabled') return res.redirect('/preferences') }) @@ -501,7 +503,8 @@ module.exports = (app, redis, fetch, RedditAPI) => { post_url: post_url, subreddit: subreddit, sortby: sortby, - user_preferences: req.cookies + user_preferences: req.cookies, + instance_nsfw_enabled: config.nsfw_enabled }) } else { let key = `morechildren:${post_url};1` @@ -534,7 +537,8 @@ module.exports = (app, redis, fetch, RedditAPI) => { subreddit: req.params.subreddit, sortby: sortby, 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, subreddit: subreddit, 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) => { let theme = req.body.theme let flairs = req.body.flairs + let nsfw_enabled = req.body.nsfw_enabled res.cookie('theme', theme, { maxAge: 365 * 24 * 60 * 60 * 1000, httpOnly: true }) @@ -744,6 +750,13 @@ module.exports = (app, redis, fetch, RedditAPI) => { else flairs = 'false' 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') }) diff --git a/static/css/styles.css b/static/css/styles.css index 60db2c3..c1dcfa2 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -297,12 +297,13 @@ form legend { padding-bottom: 10px; } form .setting { - margin: 10px 0px; - width: 100%; + margin: 10px 0px; + width: 100%; } -.container .content p.notice { - padding-top: 20px; - padding-bottom: 20px; +.container .content small.notice { + padding-top: 20px; + padding-bottom: 5px; + display: inline-block; } .container .content p.version { text-align: right; @@ -851,6 +852,30 @@ input[type="submit"]:hover, #post .source-url { 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 .entries { float: left; diff --git a/views/post.pug b/views/post.pug index 602a904..71c4ac1 100644 --- a/views/post.pug +++ b/views/post.pug @@ -10,166 +10,175 @@ html p #{JSON.stringify(error_data)} else #post - header - div - p subreddit: - a(href="/r/" + subreddit + "") - p /r/#{subreddit} - .info - .score - div.arrow - span #{kFormatter(post.ups)} - div.arrow.down - .title - a(href="" + post.url + "") - h2 #{cleanTitle(post.title)} - != post.link_flair - span(class="domain") (#{post.domain}) - 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 + if post.over_18 && (instance_nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false' + .nsfw-warning + span 18+ + h2 You must be 18+ to view this community + p You must be at least eighteen years old to view this content. Are you over eighteen and willing to see adult content? + a(href="/") No thank you + a(href="?nsfw_enabled=true") Continue + p If you continue, nsfw_enabled cookie preference will be automatically set to true. + else + header + div + p subreddit: + a(href="/r/" + subreddit + "") + p /r/#{subreddit} + .info .score div.arrow - span #{kFormatter(post.crosspost.ups)} + span #{kFormatter(post.ups)} div.arrow.down + .title + a(href="" + post.url + "") + h2 #{cleanTitle(post.title)} + != post.link_flair + span(class="domain") (#{post.domain}) p.submitted - span(title="" + toUTCString(post.crosspost.created) + "") submitted #{timeDifference(post.crosspost.created)} by - if post.crosspost.author === '[deleted]' + span(title="" + toUTCString(post.created) + "") submitted #{timeDifference(post.created)} by + if post.author === '[deleted]' span [deleted] else - a(href="/u/" + post.crosspost.author + "") - | #{post.crosspost.author} + a(href="/u/" + post.author + "") + | #{post.author} != post.user_flair - p.to to - a(href="/r/" + post.crosspost.subreddit + "") - | #{post.crosspost.subreddit} + .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 + 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.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 .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} + if post.media.source === 'YouTube' + .video + .title + a(href="" + post.media.embed_src + "", target="_blank") + p #{cleanTitle(post.media.title)} + 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)} #{post.media.embed_src} + p #{post.media.author_name} #{post.media.author_url} + 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 .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.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 - .image - a(href="" + post.images.source + "") - img(src="" + post.images.source + "", alt="") - else - if post.media.not_hosted_in_reddit - if post.media.source === 'YouTube' - .video - .title - a(href="" + post.media.embed_src + "", target="_blank") - p #{cleanTitle(post.media.title)} - 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)} #{post.media.embed_src} - p #{post.media.author_name} #{post.media.author_url} - 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 - .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: best - if sortby === 'top' - span sorted by: top - if sortby === 'new' - span sorted by: new - if sortby === 'controversial' - span sorted by: controversial - if sortby === 'old' - span sorted by: old - if sortby === 'qa' - span sorted by: q&a - 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 + 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: best + if sortby === 'top' + span sorted by: top + if sortby === 'new' + span sorted by: new + if sortby === 'controversial' + span sorted by: controversial + if sortby === 'old' + span sorted by: old + if sortby === 'qa' + span sorted by: q&a + 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 diff --git a/views/preferences.pug b/views/preferences.pug index 054dfc5..1f758cb 100644 --- a/views/preferences.pug +++ b/views/preferences.pug @@ -25,6 +25,12 @@ html input(type="checkbox", name="flairs", id="flairs", checked="checked") else 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") a(href="/resetprefs", class="btn") Reset preferences