diff --git a/src/duplicates.rs b/src/duplicates.rs
index eb032e4..12bce84 100644
--- a/src/duplicates.rs
+++ b/src/duplicates.rs
@@ -3,7 +3,7 @@
use crate::client::json;
use crate::server::RequestExt;
use crate::subreddit::{can_access_quarantine, quarantine};
-use crate::utils::{error, filter_posts, get_filters, parse_post, template, Post, Preferences};
+use crate::utils::{error, filter_posts, get_filters, nsfw_landing, parse_post, setting, template, Post, Preferences};
use askama::Template;
use hyper::{Body, Request, Response};
@@ -65,8 +65,15 @@ pub async fn item(req: Request
) -> Result, String> {
match json(path, quarantined).await {
// Process response JSON.
Ok(response) => {
- let filters = get_filters(&req);
let post = parse_post(&response[0]["data"]["children"][0]).await;
+
+ // Return landing page if this post if this Reddit deems this post
+ // NSFW, but we have also disabled the display of NSFW content.
+ if setting(&req, "show_nsfw") != "on" && post.nsfw {
+ return Ok(nsfw_landing(req).await.unwrap_or_default());
+ }
+
+ let filters = get_filters(&req);
let (duplicates, num_posts_filtered, all_posts_filtered) = parse_duplicates(&response[1], &filters).await;
// These are the values for the "before=", "after=", and "sort="
diff --git a/src/post.rs b/src/post.rs
index 7e1add2..a0da095 100644
--- a/src/post.rs
+++ b/src/post.rs
@@ -3,7 +3,7 @@ use crate::client::json;
use crate::server::RequestExt;
use crate::subreddit::{can_access_quarantine, quarantine};
use crate::utils::{
- error, format_num, get_filters, param, parse_post, rewrite_urls, setting, template, time, val, Author, Awards, Comment, Flair, FlairPart, Post, Preferences,
+ error, format_num, get_filters, nsfw_landing, param, parse_post, rewrite_urls, setting, template, time, val, Author, Awards, Comment, Flair, FlairPart, Post, Preferences,
};
use hyper::{Body, Request, Response};
@@ -55,6 +55,13 @@ pub async fn item(req: Request) -> Result, String> {
Ok(response) => {
// Parse the JSON into Post and Comment structs
let post = parse_post(&response[0]["data"]["children"][0]).await;
+
+ // Return landing page if this post if this Reddit deems this post
+ // NSFW, but we have also disabled the display of NSFW content.
+ if setting(&req, "show_nsfw") != "on" && post.nsfw {
+ return Ok(nsfw_landing(req).await.unwrap_or_default());
+ }
+
let comments = parse_comments(&response[1], &post.permalink, &post.author.name, highlighted_comment, &get_filters(&req));
let url = req.uri().to_string();
diff --git a/src/subreddit.rs b/src/subreddit.rs
index 5d4600f..242a508 100644
--- a/src/subreddit.rs
+++ b/src/subreddit.rs
@@ -1,6 +1,6 @@
// CRATES
use crate::utils::{
- catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit,
+ catch_random, error, filter_posts, format_num, format_url, get_filters, nsfw_landing, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit,
};
use crate::{client::json, server::ResponseExt, RequestExt};
use askama::Template;
@@ -96,6 +96,12 @@ pub async fn community(req: Request) -> Result, String> {
}
};
+ // Return landing page if this post if this is NSFW community but the user
+ // has disabled the display of NSFW content.
+ if setting(&req, "show_nsfw") != "on" && sub.nsfw {
+ return Ok(nsfw_landing(req).await.unwrap_or_default());
+ }
+
let path = format!("/r/{}/{}.json?{}&raw_json=1", sub_name.clone(), sort, req.uri().query().unwrap_or_default());
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
let redirect_url = url[1..].replace('?', "%3F").replace('&', "%26").replace('+', "%2B");
@@ -416,5 +422,6 @@ async fn subreddit(sub: &str, quarantined: bool) -> Result {
members: format_num(members),
active: format_num(active),
wiki: res["data"]["wiki_enabled"].as_bool().unwrap_or_default(),
+ nsfw: res["data"]["over18"].as_bool().unwrap_or_default(),
})
}
diff --git a/src/user.rs b/src/user.rs
index 8d70e86..181c36a 100644
--- a/src/user.rs
+++ b/src/user.rs
@@ -1,7 +1,7 @@
// CRATES
use crate::client::json;
use crate::server::RequestExt;
-use crate::utils::{error, filter_posts, format_url, get_filters, param, setting, template, Post, Preferences, User};
+use crate::utils::{error, filter_posts, format_url, get_filters, nsfw_landing, param, setting, template, Post, Preferences, User};
use askama::Template;
use hyper::{Body, Request, Response};
use time::{macros::format_description, OffsetDateTime};
@@ -45,8 +45,16 @@ pub async fn profile(req: Request) -> Result, String> {
// Retrieve other variables from libbacon request
let sort = param(&path, "sort").unwrap_or_default();
let username = req.param("name").unwrap_or_default();
+
+ // Retrieve info from user about page.
let user = user(&username).await.unwrap_or_default();
+ // Return landing page if this post if this Reddit deems this user NSFW,
+ // but we have also disabled the display of NSFW content.
+ if setting(&req, "show_nsfw") != "on" && user.nsfw {
+ return Ok(nsfw_landing(req).await.unwrap_or_default());
+ }
+
let filters = get_filters(&req);
if filters.contains(&["u_", &username].concat()) {
template(UserTemplate {
@@ -111,6 +119,7 @@ async fn user(name: &str) -> Result {
created: created.format(format_description!("[month repr:short] [day] '[year repr:last_two]")).unwrap_or_default(),
banner: about("banner_img"),
description: about("public_description"),
+ nsfw: res["data"]["subreddit"]["over_18"].as_bool().unwrap_or_default(),
}
})
}
diff --git a/src/utils.rs b/src/utils.rs
index c0fcfc3..2c7e010 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -13,6 +13,16 @@ use std::str::FromStr;
use time::{macros::format_description, Duration, OffsetDateTime};
use url::Url;
+/// Identifies whether or not the page is a subreddit, a user page, or a post.
+/// This is used by the NSFW landing template to determine the mesage to convey
+/// to the user.
+#[derive(PartialEq, Eq)]
+pub enum ResourceType {
+ Subreddit,
+ User,
+ Post,
+}
+
// Post flair with content, background color and foreground color
pub struct Flair {
pub flair_parts: Vec,
@@ -214,6 +224,7 @@ pub struct Post {
pub comments: (String, String),
pub gallery: Vec,
pub awards: Awards,
+ pub nsfw: bool,
}
impl Post {
@@ -314,6 +325,7 @@ impl Post {
comments: format_num(data["num_comments"].as_i64().unwrap_or_default()),
gallery,
awards,
+ nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
});
}
@@ -405,6 +417,27 @@ pub struct ErrorTemplate {
pub url: String,
}
+/// Template for NSFW landing page. The landing page is displayed when a page's
+/// content is wholly NSFW, but a user has not enabled the option to view NSFW
+/// posts.
+#[derive(Template)]
+#[template(path = "nsfwlanding.html")]
+pub struct NSFWLandingTemplate {
+ /// Identifier for the resource. This is either a subreddit name or a
+ /// username. (In the case of the latter, set is_user to true.)
+ pub res: String,
+
+ /// Identifies whether or not the resource is a subreddit, a user page,
+ /// or a post.
+ pub res_type: ResourceType,
+
+ /// User preferences.
+ pub prefs: Preferences,
+
+ /// Request URL.
+ pub url: String,
+}
+
#[derive(Default)]
// User struct containing metadata about user
pub struct User {
@@ -415,6 +448,7 @@ pub struct User {
pub created: String,
pub banner: String,
pub description: String,
+ pub nsfw: bool,
}
#[derive(Default)]
@@ -429,6 +463,7 @@ pub struct Subreddit {
pub members: (String, String),
pub active: (String, String),
pub wiki: bool,
+ pub nsfw: bool,
}
// Parser for query params, used in sorting (eg. /r/rust/?sort=hot)
@@ -602,6 +637,7 @@ pub async fn parse_post(post: &serde_json::Value) -> Post {
comments: format_num(post["data"]["num_comments"].as_i64().unwrap_or_default()),
gallery,
awards,
+ nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
}
}
@@ -813,6 +849,37 @@ pub async fn error(req: Request, msg: String) -> Result, St
Ok(Response::builder().status(404).header("content-type", "text/html").body(body.into()).unwrap_or_default())
}
+/// Render the landing page for NSFW content when the user has not enabled
+/// "show NSFW posts" in settings.
+pub async fn nsfw_landing(req: Request) -> Result, String> {
+ let res_type: ResourceType;
+ let url = req.uri().to_string();
+
+ // Determine from the request URL if the resource is a subreddit, a user
+ // page, or a post.
+ let res: String = if !req.param("name").unwrap_or_default().is_empty() {
+ res_type = ResourceType::User;
+ req.param("name").unwrap_or_default()
+ } else if !req.param("id").unwrap_or_default().is_empty() {
+ res_type = ResourceType::Post;
+ req.param("id").unwrap_or_default()
+ } else {
+ res_type = ResourceType::Subreddit;
+ req.param("sub").unwrap_or_default()
+ };
+
+ let body = NSFWLandingTemplate {
+ res,
+ res_type,
+ prefs: Preferences::new(req),
+ url,
+ }
+ .render()
+ .unwrap_or_default();
+
+ Ok(Response::builder().status(403).header("content-type", "text/html").body(body.into()).unwrap_or_default())
+}
+
#[cfg(test)]
mod tests {
use super::format_num;
diff --git a/static/style.css b/static/style.css
index de26bc9..0d26302 100644
--- a/static/style.css
+++ b/static/style.css
@@ -1286,6 +1286,31 @@ td, th {
color: var(--accent);
};
+/* NSFW Landing Page */
+
+#nsfw_landing {
+ display: inline-block;
+ text-align: center;
+ width: 100%;
+}
+
+#nsfw_landing h1 {
+ display: inline-block;
+ margin-bottom: 20px;
+ text-align: center;
+ width: 100%;
+}
+
+#nsfw_landing p {
+ display: inline-block;
+ text-align: center;
+ width: 100%;
+}
+
+#nsfw_landing a {
+ color: var(--accent);
+}
+
/* Mobile */
@media screen and (max-width: 800px) {
diff --git a/templates/nsfwlanding.html b/templates/nsfwlanding.html
new file mode 100644
index 0000000..aff229d
--- /dev/null
+++ b/templates/nsfwlanding.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+{% block title %}NSFW content gated{% endblock %}
+{% block sortstyle %}{% endblock %}
+{% block content %}
+
+ {% if res_type == crate::utils::ResourceType::Subreddit %}
+
😱 r/{{ res }} is a NSFW community!
+ {% else if res_type == crate::utils::ResourceType::User %}
+
😱 u/{{ res }}'s content is NSFW!
+ {% else if res_type == crate::utils::ResourceType::Post %}
+
😱 This post is NSFW!
+ {% endif %}
+
+
Enable "Show NSFW posts" in settings to view this {% if res_type == crate::utils::ResourceType::Subreddit %}subreddit{% else if res_type == crate::utils::ResourceType::User %}user's posts or comments{% else if res_type == crate::utils::ResourceType::Post %}post{% endif %}.
+
+{% endblock %}