Adding some site oriented settings.
- Adding option to close registration. Fixes #350 - Adding option to disable showing NSFW buttons. Fixes #364 - Adding option to disable downvotes. Fixes #239
This commit is contained in:
parent
e9f4765663
commit
fca8e6a0a9
25 changed files with 349 additions and 167 deletions
16
server/migrations/2019-12-11-181820_add_site_fields/down.sql
vendored
Normal file
16
server/migrations/2019-12-11-181820_add_site_fields/down.sql
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
-- Drop the columns
|
||||||
|
drop view site_view;
|
||||||
|
alter table site drop column enable_downvotes;
|
||||||
|
alter table site drop column open_registration;
|
||||||
|
alter table site drop column enable_nsfw;
|
||||||
|
|
||||||
|
-- Rebuild the views
|
||||||
|
|
||||||
|
create view site_view as
|
||||||
|
select *,
|
||||||
|
(select name from user_ u where s.creator_id = u.id) as creator_name,
|
||||||
|
(select count(*) from user_) as number_of_users,
|
||||||
|
(select count(*) from post) as number_of_posts,
|
||||||
|
(select count(*) from comment) as number_of_comments,
|
||||||
|
(select count(*) from community) as number_of_communities
|
||||||
|
from site s;
|
16
server/migrations/2019-12-11-181820_add_site_fields/up.sql
vendored
Normal file
16
server/migrations/2019-12-11-181820_add_site_fields/up.sql
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
-- Add the column
|
||||||
|
alter table site add column enable_downvotes boolean default true not null;
|
||||||
|
alter table site add column open_registration boolean default true not null;
|
||||||
|
alter table site add column enable_nsfw boolean default true not null;
|
||||||
|
|
||||||
|
-- Reload the view
|
||||||
|
drop view site_view;
|
||||||
|
|
||||||
|
create view site_view as
|
||||||
|
select *,
|
||||||
|
(select name from user_ u where s.creator_id = u.id) as creator_name,
|
||||||
|
(select count(*) from user_) as number_of_users,
|
||||||
|
(select count(*) from post) as number_of_posts,
|
||||||
|
(select count(*) from comment) as number_of_comments,
|
||||||
|
(select count(*) from community) as number_of_communities
|
||||||
|
from site s;
|
|
@ -298,6 +298,14 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
// Don't do a downvote if site has downvotes disabled
|
||||||
|
if data.score == -1 {
|
||||||
|
let site = SiteView::read(&conn)?;
|
||||||
|
if site.enable_downvotes == false {
|
||||||
|
return Err(APIError::err(&self.op, "downvotes_disabled"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for a community ban
|
// Check for a community ban
|
||||||
let post = Post::read(&conn, data.post_id)?;
|
let post = Post::read(&conn, data.post_id)?;
|
||||||
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
||||||
|
|
|
@ -8,6 +8,8 @@ use crate::db::moderator_views::*;
|
||||||
use crate::db::password_reset_request::*;
|
use crate::db::password_reset_request::*;
|
||||||
use crate::db::post::*;
|
use crate::db::post::*;
|
||||||
use crate::db::post_view::*;
|
use crate::db::post_view::*;
|
||||||
|
use crate::db::site::*;
|
||||||
|
use crate::db::site_view::*;
|
||||||
use crate::db::user::*;
|
use crate::db::user::*;
|
||||||
use crate::db::user_mention::*;
|
use crate::db::user_mention::*;
|
||||||
use crate::db::user_mention_view::*;
|
use crate::db::user_mention_view::*;
|
||||||
|
|
|
@ -265,6 +265,14 @@ impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
// Don't do a downvote if site has downvotes disabled
|
||||||
|
if data.score == -1 {
|
||||||
|
let site = SiteView::read(&conn)?;
|
||||||
|
if site.enable_downvotes == false {
|
||||||
|
return Err(APIError::err(&self.op, "downvotes_disabled"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for a community ban
|
// Check for a community ban
|
||||||
let post = Post::read(&conn, data.post_id)?;
|
let post = Post::read(&conn, data.post_id)?;
|
||||||
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
||||||
|
|
|
@ -56,6 +56,9 @@ pub struct GetModlogResponse {
|
||||||
pub struct CreateSite {
|
pub struct CreateSite {
|
||||||
name: String,
|
name: String,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
|
enable_downvotes: bool,
|
||||||
|
open_registration: bool,
|
||||||
|
enable_nsfw: bool,
|
||||||
auth: String,
|
auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +66,9 @@ pub struct CreateSite {
|
||||||
pub struct EditSite {
|
pub struct EditSite {
|
||||||
name: String,
|
name: String,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
|
enable_downvotes: bool,
|
||||||
|
open_registration: bool,
|
||||||
|
enable_nsfw: bool,
|
||||||
auth: String,
|
auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +214,9 @@ impl Perform<SiteResponse> for Oper<CreateSite> {
|
||||||
name: data.name.to_owned(),
|
name: data.name.to_owned(),
|
||||||
description: data.description.to_owned(),
|
description: data.description.to_owned(),
|
||||||
creator_id: user_id,
|
creator_id: user_id,
|
||||||
|
enable_downvotes: data.enable_downvotes,
|
||||||
|
open_registration: data.open_registration,
|
||||||
|
enable_nsfw: data.enable_nsfw,
|
||||||
updated: None,
|
updated: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -255,6 +264,9 @@ impl Perform<SiteResponse> for Oper<EditSite> {
|
||||||
description: data.description.to_owned(),
|
description: data.description.to_owned(),
|
||||||
creator_id: found_site.creator_id,
|
creator_id: found_site.creator_id,
|
||||||
updated: Some(naive_now()),
|
updated: Some(naive_now()),
|
||||||
|
enable_downvotes: data.enable_downvotes,
|
||||||
|
open_registration: data.open_registration,
|
||||||
|
enable_nsfw: data.enable_nsfw,
|
||||||
};
|
};
|
||||||
|
|
||||||
match Site::update(&conn, 1, &site_form) {
|
match Site::update(&conn, 1, &site_form) {
|
||||||
|
@ -431,6 +443,9 @@ impl Perform<GetSiteResponse> for Oper<TransferSite> {
|
||||||
description: read_site.description,
|
description: read_site.description,
|
||||||
creator_id: data.user_id,
|
creator_id: data.user_id,
|
||||||
updated: Some(naive_now()),
|
updated: Some(naive_now()),
|
||||||
|
enable_downvotes: read_site.enable_downvotes,
|
||||||
|
open_registration: read_site.open_registration,
|
||||||
|
enable_nsfw: read_site.enable_nsfw,
|
||||||
};
|
};
|
||||||
|
|
||||||
match Site::update(&conn, 1, &site_form) {
|
match Site::update(&conn, 1, &site_form) {
|
||||||
|
|
|
@ -193,6 +193,13 @@ impl Perform<LoginResponse> for Oper<Register> {
|
||||||
let data: &Register = &self.data;
|
let data: &Register = &self.data;
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
|
|
||||||
|
// Make sure site has open registration
|
||||||
|
if let Ok(site) = SiteView::read(&conn) {
|
||||||
|
if !site.open_registration {
|
||||||
|
return Err(APIError::err(&self.op, "registration_closed"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure passwords match
|
// Make sure passwords match
|
||||||
if &data.password != &data.password_verify {
|
if &data.password != &data.password_verify {
|
||||||
return Err(APIError::err(&self.op, "passwords_dont_match"))?;
|
return Err(APIError::err(&self.op, "passwords_dont_match"))?;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::schema::{community, community_follower, community_moderator, community_user_ban, site};
|
use crate::schema::{community, community_follower, community_moderator, community_user_ban};
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
|
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
#[table_name = "community"]
|
#[table_name = "community"]
|
||||||
|
@ -202,50 +202,6 @@ impl Followable<CommunityFollowerForm> for CommunityFollower {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
|
|
||||||
#[table_name = "site"]
|
|
||||||
pub struct Site {
|
|
||||||
pub id: i32,
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub creator_id: i32,
|
|
||||||
pub published: chrono::NaiveDateTime,
|
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
|
|
||||||
#[table_name = "site"]
|
|
||||||
pub struct SiteForm {
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub creator_id: i32,
|
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Crud<SiteForm> for Site {
|
|
||||||
fn read(conn: &PgConnection, _site_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::site::dsl::*;
|
|
||||||
site.first::<Self>(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete(conn: &PgConnection, site_id: i32) -> Result<usize, Error> {
|
|
||||||
use crate::schema::site::dsl::*;
|
|
||||||
diesel::delete(site.find(site_id)).execute(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create(conn: &PgConnection, new_site: &SiteForm) -> Result<Self, Error> {
|
|
||||||
use crate::schema::site::dsl::*;
|
|
||||||
insert_into(site).values(new_site).get_result::<Self>(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(conn: &PgConnection, site_id: i32, new_site: &SiteForm) -> Result<Self, Error> {
|
|
||||||
use crate::schema::site::dsl::*;
|
|
||||||
diesel::update(site.find(site_id))
|
|
||||||
.set(new_site)
|
|
||||||
.get_result::<Self>(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::user::*;
|
use super::super::user::*;
|
||||||
|
|
|
@ -59,22 +59,6 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
|
||||||
site_view (id) {
|
|
||||||
id -> Int4,
|
|
||||||
name -> Varchar,
|
|
||||||
description -> Nullable<Text>,
|
|
||||||
creator_id -> Int4,
|
|
||||||
published -> Timestamp,
|
|
||||||
updated -> Nullable<Timestamp>,
|
|
||||||
creator_name -> Varchar,
|
|
||||||
number_of_users -> BigInt,
|
|
||||||
number_of_posts -> BigInt,
|
|
||||||
number_of_comments -> BigInt,
|
|
||||||
number_of_communities -> BigInt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
|
Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
|
||||||
)]
|
)]
|
||||||
|
@ -328,28 +312,3 @@ impl CommunityUserBanView {
|
||||||
.first::<Self>(conn)
|
.first::<Self>(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
|
|
||||||
)]
|
|
||||||
#[table_name = "site_view"]
|
|
||||||
pub struct SiteView {
|
|
||||||
pub id: i32,
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub creator_id: i32,
|
|
||||||
pub published: chrono::NaiveDateTime,
|
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
|
||||||
pub creator_name: String,
|
|
||||||
pub number_of_users: i64,
|
|
||||||
pub number_of_posts: i64,
|
|
||||||
pub number_of_comments: i64,
|
|
||||||
pub number_of_communities: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SiteView {
|
|
||||||
pub fn read(conn: &PgConnection) -> Result<Self, Error> {
|
|
||||||
use super::community_view::site_view::dsl::*;
|
|
||||||
site_view.first::<Self>(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ pub mod moderator_views;
|
||||||
pub mod password_reset_request;
|
pub mod password_reset_request;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod post_view;
|
pub mod post_view;
|
||||||
|
pub mod site;
|
||||||
|
pub mod site_view;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod user_mention;
|
pub mod user_mention;
|
||||||
pub mod user_mention_view;
|
pub mod user_mention_view;
|
||||||
|
|
52
server/src/db/site.rs
Normal file
52
server/src/db/site.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use super::*;
|
||||||
|
use crate::schema::site;
|
||||||
|
|
||||||
|
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
#[table_name = "site"]
|
||||||
|
pub struct Site {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub creator_id: i32,
|
||||||
|
pub published: chrono::NaiveDateTime,
|
||||||
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
|
pub enable_downvotes: bool,
|
||||||
|
pub open_registration: bool,
|
||||||
|
pub enable_nsfw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
|
||||||
|
#[table_name = "site"]
|
||||||
|
pub struct SiteForm {
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub creator_id: i32,
|
||||||
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
|
pub enable_downvotes: bool,
|
||||||
|
pub open_registration: bool,
|
||||||
|
pub enable_nsfw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Crud<SiteForm> for Site {
|
||||||
|
fn read(conn: &PgConnection, _site_id: i32) -> Result<Self, Error> {
|
||||||
|
use crate::schema::site::dsl::*;
|
||||||
|
site.first::<Self>(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete(conn: &PgConnection, site_id: i32) -> Result<usize, Error> {
|
||||||
|
use crate::schema::site::dsl::*;
|
||||||
|
diesel::delete(site.find(site_id)).execute(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(conn: &PgConnection, new_site: &SiteForm) -> Result<Self, Error> {
|
||||||
|
use crate::schema::site::dsl::*;
|
||||||
|
insert_into(site).values(new_site).get_result::<Self>(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(conn: &PgConnection, site_id: i32, new_site: &SiteForm) -> Result<Self, Error> {
|
||||||
|
use crate::schema::site::dsl::*;
|
||||||
|
diesel::update(site.find(site_id))
|
||||||
|
.set(new_site)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
}
|
||||||
|
}
|
48
server/src/db/site_view.rs
Normal file
48
server/src/db/site_view.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
table! {
|
||||||
|
site_view (id) {
|
||||||
|
id -> Int4,
|
||||||
|
name -> Varchar,
|
||||||
|
description -> Nullable<Text>,
|
||||||
|
creator_id -> Int4,
|
||||||
|
published -> Timestamp,
|
||||||
|
updated -> Nullable<Timestamp>,
|
||||||
|
enable_downvotes -> Bool,
|
||||||
|
open_registration -> Bool,
|
||||||
|
enable_nsfw -> Bool,
|
||||||
|
creator_name -> Varchar,
|
||||||
|
number_of_users -> BigInt,
|
||||||
|
number_of_posts -> BigInt,
|
||||||
|
number_of_comments -> BigInt,
|
||||||
|
number_of_communities -> BigInt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
|
||||||
|
)]
|
||||||
|
#[table_name = "site_view"]
|
||||||
|
pub struct SiteView {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub creator_id: i32,
|
||||||
|
pub published: chrono::NaiveDateTime,
|
||||||
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
|
pub enable_downvotes: bool,
|
||||||
|
pub open_registration: bool,
|
||||||
|
pub enable_nsfw: bool,
|
||||||
|
pub creator_name: String,
|
||||||
|
pub number_of_users: i64,
|
||||||
|
pub number_of_posts: i64,
|
||||||
|
pub number_of_comments: i64,
|
||||||
|
pub number_of_communities: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SiteView {
|
||||||
|
pub fn read(conn: &PgConnection) -> Result<Self, Error> {
|
||||||
|
use super::site_view::site_view::dsl::*;
|
||||||
|
site_view.first::<Self>(conn)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,8 @@ extern crate rss;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::db::comment_view::{ReplyQueryBuilder, ReplyView};
|
use crate::db::comment_view::{ReplyQueryBuilder, ReplyView};
|
||||||
use crate::db::community::Community;
|
use crate::db::community::Community;
|
||||||
use crate::db::community_view::SiteView;
|
|
||||||
use crate::db::post_view::{PostQueryBuilder, PostView};
|
use crate::db::post_view::{PostQueryBuilder, PostView};
|
||||||
|
use crate::db::site_view::SiteView;
|
||||||
use crate::db::user::User_;
|
use crate::db::user::User_;
|
||||||
use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView};
|
use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView};
|
||||||
use crate::db::{establish_connection, ListingType, SortType};
|
use crate::db::{establish_connection, ListingType, SortType};
|
||||||
|
|
|
@ -118,7 +118,7 @@ impl Settings {
|
||||||
.unwrap_or("3600".to_string())
|
.unwrap_or("3600".to_string())
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
email_config: email_config,
|
email_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn api_endpoint(&self) -> String {
|
fn api_endpoint(&self) -> String {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::db::community_view::SiteView;
|
|
||||||
use crate::db::establish_connection;
|
use crate::db::establish_connection;
|
||||||
|
use crate::db::site_view::SiteView;
|
||||||
use crate::version;
|
use crate::version;
|
||||||
use crate::Settings;
|
use crate::Settings;
|
||||||
use actix_web::body::Body;
|
use actix_web::body::Body;
|
||||||
|
|
|
@ -246,6 +246,9 @@ table! {
|
||||||
creator_id -> Int4,
|
creator_id -> Int4,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
|
enable_downvotes -> Bool,
|
||||||
|
open_registration -> Bool,
|
||||||
|
enable_nsfw -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
ui/src/components/comment-node.tsx
vendored
22
ui/src/components/comment-node.tsx
vendored
|
@ -102,16 +102,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<div class={`font-weight-bold text-muted`}>
|
<div class={`font-weight-bold text-muted`}>
|
||||||
{node.comment.score}
|
{node.comment.score}
|
||||||
</div>
|
</div>
|
||||||
<button
|
{WebSocketService.Instance.site.enable_downvotes && (
|
||||||
className={`btn p-0 ${
|
<button
|
||||||
node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'
|
className={`btn p-0 ${
|
||||||
}`}
|
node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'
|
||||||
onClick={linkEvent(node, this.handleCommentDisLike)}
|
}`}
|
||||||
>
|
onClick={linkEvent(node, this.handleCommentDisLike)}
|
||||||
<svg class="icon downvote">
|
>
|
||||||
<use xlinkHref="#icon-arrow-down"></use>
|
<svg class="icon downvote">
|
||||||
</svg>
|
<use xlinkHref="#icon-arrow-down"></use>
|
||||||
</button>
|
</svg>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
|
|
29
ui/src/components/community-form.tsx
vendored
29
ui/src/components/community-form.tsx
vendored
|
@ -156,21 +156,24 @@ export class CommunityForm extends Component<
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-12">
|
{WebSocketService.Instance.site.enable_nsfw && (
|
||||||
<div class="form-check">
|
<div class="form-group row">
|
||||||
<input
|
<div class="col-12">
|
||||||
class="form-check-input"
|
<div class="form-check">
|
||||||
type="checkbox"
|
<input
|
||||||
checked={this.state.communityForm.nsfw}
|
class="form-check-input"
|
||||||
onChange={linkEvent(this, this.handleCommunityNsfwChange)}
|
type="checkbox"
|
||||||
/>
|
checked={this.state.communityForm.nsfw}
|
||||||
<label class="form-check-label">
|
onChange={linkEvent(this, this.handleCommunityNsfwChange)}
|
||||||
<T i18nKey="nsfw">#</T>
|
/>
|
||||||
</label>
|
<label class="form-check-label">
|
||||||
|
<T i18nKey="nsfw">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button type="submit" class="btn btn-secondary mr-2">
|
<button type="submit" class="btn btn-secondary mr-2">
|
||||||
|
|
28
ui/src/components/login.tsx
vendored
28
ui/src/components/login.tsx
vendored
|
@ -205,21 +205,23 @@ export class Login extends Component<any, State> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
{WebSocketService.Instance.site.enable_nsfw && (
|
||||||
<div class="col-sm-10">
|
<div class="form-group row">
|
||||||
<div class="form-check">
|
<div class="col-sm-10">
|
||||||
<input
|
<div class="form-check">
|
||||||
class="form-check-input"
|
<input
|
||||||
type="checkbox"
|
class="form-check-input"
|
||||||
checked={this.state.registerForm.show_nsfw}
|
type="checkbox"
|
||||||
onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
|
checked={this.state.registerForm.show_nsfw}
|
||||||
/>
|
onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
|
||||||
<label class="form-check-label">
|
/>
|
||||||
<T i18nKey="show_nsfw">#</T>
|
<label class="form-check-label">
|
||||||
</label>
|
<T i18nKey="show_nsfw">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<button type="submit" class="btn btn-secondary">
|
<button type="submit" class="btn btn-secondary">
|
||||||
|
|
28
ui/src/components/post-form.tsx
vendored
28
ui/src/components/post-form.tsx
vendored
|
@ -280,21 +280,23 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class="form-group row">
|
{WebSocketService.Instance.site.enable_nsfw && (
|
||||||
<div class="col-sm-10">
|
<div class="form-group row">
|
||||||
<div class="form-check">
|
<div class="col-sm-10">
|
||||||
<input
|
<div class="form-check">
|
||||||
class="form-check-input"
|
<input
|
||||||
type="checkbox"
|
class="form-check-input"
|
||||||
checked={this.state.postForm.nsfw}
|
type="checkbox"
|
||||||
onChange={linkEvent(this, this.handlePostNsfwChange)}
|
checked={this.state.postForm.nsfw}
|
||||||
/>
|
onChange={linkEvent(this, this.handlePostNsfwChange)}
|
||||||
<label class="form-check-label">
|
/>
|
||||||
<T i18nKey="nsfw">#</T>
|
<label class="form-check-label">
|
||||||
</label>
|
<T i18nKey="nsfw">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<button type="submit" class="btn btn-secondary mr-2">
|
<button type="submit" class="btn btn-secondary mr-2">
|
||||||
|
|
22
ui/src/components/post-listing.tsx
vendored
22
ui/src/components/post-listing.tsx
vendored
|
@ -114,16 +114,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div class={`font-weight-bold text-muted`}>{post.score}</div>
|
<div class={`font-weight-bold text-muted`}>{post.score}</div>
|
||||||
<button
|
{WebSocketService.Instance.site.enable_downvotes && (
|
||||||
className={`btn p-0 ${
|
<button
|
||||||
post.my_vote == -1 ? 'text-danger' : 'text-muted'
|
className={`btn p-0 ${
|
||||||
}`}
|
post.my_vote == -1 ? 'text-danger' : 'text-muted'
|
||||||
onClick={linkEvent(this, this.handlePostDisLike)}
|
}`}
|
||||||
>
|
onClick={linkEvent(this, this.handlePostDisLike)}
|
||||||
<svg class="icon downvote">
|
>
|
||||||
<use xlinkHref="#icon-arrow-down"></use>
|
<svg class="icon downvote">
|
||||||
</svg>
|
<use xlinkHref="#icon-arrow-down"></use>
|
||||||
</button>
|
</svg>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{post.url && isImage(post.url) && !post.nsfw && !post.community_nsfw && (
|
{post.url && isImage(post.url) && !post.nsfw && !post.community_nsfw && (
|
||||||
<span
|
<span
|
||||||
|
|
69
ui/src/components/site-form.tsx
vendored
69
ui/src/components/site-form.tsx
vendored
|
@ -19,6 +19,9 @@ interface SiteFormState {
|
||||||
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
private emptyState: SiteFormState = {
|
private emptyState: SiteFormState = {
|
||||||
siteForm: {
|
siteForm: {
|
||||||
|
enable_downvotes: true,
|
||||||
|
open_registration: true,
|
||||||
|
enable_nsfw: true,
|
||||||
name: null,
|
name: null,
|
||||||
},
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -31,6 +34,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
this.state.siteForm = {
|
this.state.siteForm = {
|
||||||
name: this.props.site.name,
|
name: this.props.site.name,
|
||||||
description: this.props.site.description,
|
description: this.props.site.description,
|
||||||
|
enable_downvotes: this.props.site.enable_downvotes,
|
||||||
|
open_registration: this.props.site.open_registration,
|
||||||
|
enable_nsfw: this.props.site.enable_nsfw,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,6 +83,54 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.siteForm.enable_downvotes}
|
||||||
|
onChange={linkEvent(this, this.handleSiteEnableDownvotesChange)}
|
||||||
|
/>
|
||||||
|
<label class="form-check-label">
|
||||||
|
<T i18nKey="enable_downvotes">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.siteForm.enable_nsfw}
|
||||||
|
onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
|
||||||
|
/>
|
||||||
|
<label class="form-check-label">
|
||||||
|
<T i18nKey="enable_nsfw">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.siteForm.open_registration}
|
||||||
|
onChange={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleSiteOpenRegistrationChange
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<label class="form-check-label">
|
||||||
|
<T i18nKey="open_registration">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button type="submit" class="btn btn-secondary mr-2">
|
<button type="submit" class="btn btn-secondary mr-2">
|
||||||
|
@ -126,6 +180,21 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSiteEnableNsfwChange(i: SiteForm, event: any) {
|
||||||
|
i.state.siteForm.enable_nsfw = event.target.checked;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
|
||||||
|
i.state.siteForm.open_registration = event.target.checked;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
|
||||||
|
i.state.siteForm.enable_downvotes = event.target.checked;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleCancel(i: SiteForm) {
|
handleCancel(i: SiteForm) {
|
||||||
i.props.onCancel();
|
i.props.onCancel();
|
||||||
}
|
}
|
||||||
|
|
34
ui/src/components/user.tsx
vendored
34
ui/src/components/user.tsx
vendored
|
@ -496,24 +496,26 @@ export class User extends Component<any, UserState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="form-group">
|
{WebSocketService.Instance.site.enable_nsfw && (
|
||||||
<div class="col-12">
|
<div class="form-group">
|
||||||
<div class="form-check">
|
<div class="col-12">
|
||||||
<input
|
<div class="form-check">
|
||||||
class="form-check-input"
|
<input
|
||||||
type="checkbox"
|
class="form-check-input"
|
||||||
checked={this.state.userSettingsForm.show_nsfw}
|
type="checkbox"
|
||||||
onChange={linkEvent(
|
checked={this.state.userSettingsForm.show_nsfw}
|
||||||
this,
|
onChange={linkEvent(
|
||||||
this.handleUserSettingsShowNsfwChange
|
this,
|
||||||
)}
|
this.handleUserSettingsShowNsfwChange
|
||||||
/>
|
)}
|
||||||
<label class="form-check-label">
|
/>
|
||||||
<T i18nKey="show_nsfw">#</T>
|
<label class="form-check-label">
|
||||||
</label>
|
<T i18nKey="show_nsfw">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button
|
<button
|
||||||
|
|
9
ui/src/interfaces.ts
vendored
9
ui/src/interfaces.ts
vendored
|
@ -199,6 +199,9 @@ export interface Site {
|
||||||
number_of_posts: number;
|
number_of_posts: number;
|
||||||
number_of_comments: number;
|
number_of_comments: number;
|
||||||
number_of_communities: number;
|
number_of_communities: number;
|
||||||
|
enable_downvotes: boolean;
|
||||||
|
open_registration: boolean;
|
||||||
|
enable_nsfw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BanType {
|
export enum BanType {
|
||||||
|
@ -625,9 +628,9 @@ export interface CreatePostLikeResponse {
|
||||||
export interface SiteForm {
|
export interface SiteForm {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
removed?: boolean;
|
enable_downvotes: boolean;
|
||||||
reason?: string;
|
open_registration: boolean;
|
||||||
expires?: number;
|
enable_nsfw: boolean;
|
||||||
auth?: string;
|
auth?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
ui/src/translations/en.ts
vendored
5
ui/src/translations/en.ts
vendored
|
@ -127,6 +127,11 @@ export const en = {
|
||||||
expires: 'Expires',
|
expires: 'Expires',
|
||||||
language: 'Language',
|
language: 'Language',
|
||||||
browser_default: 'Browser Default',
|
browser_default: 'Browser Default',
|
||||||
|
downvotes_disabled: 'Downvotes disabled',
|
||||||
|
enable_downvotes: 'Enable Downvotes',
|
||||||
|
open_registration: 'Open Registration',
|
||||||
|
registration_closed: 'Registration closed',
|
||||||
|
enable_nsfw: 'Enable NSFW',
|
||||||
url: 'URL',
|
url: 'URL',
|
||||||
body: 'Body',
|
body: 'Body',
|
||||||
copy_suggested_title: 'copy suggested title: {{title}}',
|
copy_suggested_title: 'copy suggested title: {{title}}',
|
||||||
|
|
Loading…
Reference in a new issue