--- /dev/null
+-- 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;
--- /dev/null
+-- 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;
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
let post = Post::read(&conn, data.post_id)?;
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
use crate::db::password_reset_request::*;
use crate::db::post::*;
use crate::db::post_view::*;
+use crate::db::site::*;
+use crate::db::site_view::*;
use crate::db::user::*;
use crate::db::user_mention::*;
use crate::db::user_mention_view::*;
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
let post = Post::read(&conn, data.post_id)?;
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
pub struct CreateSite {
name: String,
description: Option<String>,
+ enable_downvotes: bool,
+ open_registration: bool,
+ enable_nsfw: bool,
auth: String,
}
pub struct EditSite {
name: String,
description: Option<String>,
+ enable_downvotes: bool,
+ open_registration: bool,
+ enable_nsfw: bool,
auth: String,
}
name: data.name.to_owned(),
description: data.description.to_owned(),
creator_id: user_id,
+ enable_downvotes: data.enable_downvotes,
+ open_registration: data.open_registration,
+ enable_nsfw: data.enable_nsfw,
updated: None,
};
description: data.description.to_owned(),
creator_id: found_site.creator_id,
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) {
description: read_site.description,
creator_id: data.user_id,
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) {
let data: &Register = &self.data;
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
if &data.password != &data.password_verify {
return Err(APIError::err(&self.op, "passwords_dont_match"))?;
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)]
#[table_name = "community"]
}
}
-#[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)]
mod tests {
use super::super::user::*;
}
}
-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(
Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
)]
.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)
- }
-}
pub mod password_reset_request;
pub mod post;
pub mod post_view;
+pub mod site;
+pub mod site_view;
pub mod user;
pub mod user_mention;
pub mod user_mention_view;
--- /dev/null
+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)
+ }
+}
--- /dev/null
+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)
+ }
+}
use super::*;
use crate::db::comment_view::{ReplyQueryBuilder, ReplyView};
use crate::db::community::Community;
-use crate::db::community_view::SiteView;
use crate::db::post_view::{PostQueryBuilder, PostView};
+use crate::db::site_view::SiteView;
use crate::db::user::User_;
use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView};
use crate::db::{establish_connection, ListingType, SortType};
.unwrap_or("3600".to_string())
.parse()
.unwrap(),
- email_config: email_config,
+ email_config,
}
}
fn api_endpoint(&self) -> String {
-use crate::db::community_view::SiteView;
use crate::db::establish_connection;
+use crate::db::site_view::SiteView;
use crate::version;
use crate::Settings;
use actix_web::body::Body;
creator_id -> Int4,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ enable_downvotes -> Bool,
+ open_registration -> Bool,
+ enable_nsfw -> Bool,
}
}
<div class={`font-weight-bold text-muted`}>
{node.comment.score}
</div>
- <button
- className={`btn p-0 ${
- node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'
- }`}
- onClick={linkEvent(node, this.handleCommentDisLike)}
- >
- <svg class="icon downvote">
- <use xlinkHref="#icon-arrow-down"></use>
- </svg>
- </button>
+ {WebSocketService.Instance.site.enable_downvotes && (
+ <button
+ className={`btn p-0 ${
+ node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'
+ }`}
+ onClick={linkEvent(node, this.handleCommentDisLike)}
+ >
+ <svg class="icon downvote">
+ <use xlinkHref="#icon-arrow-down"></use>
+ </svg>
+ </button>
+ )}
</div>
)}
<div
</select>
</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.communityForm.nsfw}
- onChange={linkEvent(this, this.handleCommunityNsfwChange)}
- />
- <label class="form-check-label">
- <T i18nKey="nsfw">#</T>
- </label>
+
+ {WebSocketService.Instance.site.enable_nsfw && (
+ <div class="form-group row">
+ <div class="col-12">
+ <div class="form-check">
+ <input
+ class="form-check-input"
+ type="checkbox"
+ checked={this.state.communityForm.nsfw}
+ onChange={linkEvent(this, this.handleCommunityNsfwChange)}
+ />
+ <label class="form-check-label">
+ <T i18nKey="nsfw">#</T>
+ </label>
+ </div>
</div>
</div>
- </div>
+ )}
<div class="form-group row">
<div class="col-12">
<button type="submit" class="btn btn-secondary mr-2">
/>
</div>
</div>
- <div class="form-group row">
- <div class="col-sm-10">
- <div class="form-check">
- <input
- class="form-check-input"
- type="checkbox"
- checked={this.state.registerForm.show_nsfw}
- onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
- />
- <label class="form-check-label">
- <T i18nKey="show_nsfw">#</T>
- </label>
+ {WebSocketService.Instance.site.enable_nsfw && (
+ <div class="form-group row">
+ <div class="col-sm-10">
+ <div class="form-check">
+ <input
+ class="form-check-input"
+ type="checkbox"
+ checked={this.state.registerForm.show_nsfw}
+ onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
+ />
+ <label class="form-check-label">
+ <T i18nKey="show_nsfw">#</T>
+ </label>
+ </div>
</div>
</div>
- </div>
+ )}
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary">
</div>
</div>
)}
- <div class="form-group row">
- <div class="col-sm-10">
- <div class="form-check">
- <input
- class="form-check-input"
- type="checkbox"
- checked={this.state.postForm.nsfw}
- onChange={linkEvent(this, this.handlePostNsfwChange)}
- />
- <label class="form-check-label">
- <T i18nKey="nsfw">#</T>
- </label>
+ {WebSocketService.Instance.site.enable_nsfw && (
+ <div class="form-group row">
+ <div class="col-sm-10">
+ <div class="form-check">
+ <input
+ class="form-check-input"
+ type="checkbox"
+ checked={this.state.postForm.nsfw}
+ onChange={linkEvent(this, this.handlePostNsfwChange)}
+ />
+ <label class="form-check-label">
+ <T i18nKey="nsfw">#</T>
+ </label>
+ </div>
</div>
</div>
- </div>
+ )}
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary mr-2">
</svg>
</button>
<div class={`font-weight-bold text-muted`}>{post.score}</div>
- <button
- className={`btn p-0 ${
- post.my_vote == -1 ? 'text-danger' : 'text-muted'
- }`}
- onClick={linkEvent(this, this.handlePostDisLike)}
- >
- <svg class="icon downvote">
- <use xlinkHref="#icon-arrow-down"></use>
- </svg>
- </button>
+ {WebSocketService.Instance.site.enable_downvotes && (
+ <button
+ className={`btn p-0 ${
+ post.my_vote == -1 ? 'text-danger' : 'text-muted'
+ }`}
+ onClick={linkEvent(this, this.handlePostDisLike)}
+ >
+ <svg class="icon downvote">
+ <use xlinkHref="#icon-arrow-down"></use>
+ </svg>
+ </button>
+ )}
</div>
{post.url && isImage(post.url) && !post.nsfw && !post.community_nsfw && (
<span
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
private emptyState: SiteFormState = {
siteForm: {
+ enable_downvotes: true,
+ open_registration: true,
+ enable_nsfw: true,
name: null,
},
loading: false,
this.state.siteForm = {
name: this.props.site.name,
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,
};
}
}
/>
</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="col-12">
<button type="submit" class="btn btn-secondary mr-2">
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) {
i.props.onCancel();
}
/>
</div>
</form>
- <div class="form-group">
- <div class="col-12">
- <div class="form-check">
- <input
- class="form-check-input"
- type="checkbox"
- checked={this.state.userSettingsForm.show_nsfw}
- onChange={linkEvent(
- this,
- this.handleUserSettingsShowNsfwChange
- )}
- />
- <label class="form-check-label">
- <T i18nKey="show_nsfw">#</T>
- </label>
+ {WebSocketService.Instance.site.enable_nsfw && (
+ <div class="form-group">
+ <div class="col-12">
+ <div class="form-check">
+ <input
+ class="form-check-input"
+ type="checkbox"
+ checked={this.state.userSettingsForm.show_nsfw}
+ onChange={linkEvent(
+ this,
+ this.handleUserSettingsShowNsfwChange
+ )}
+ />
+ <label class="form-check-label">
+ <T i18nKey="show_nsfw">#</T>
+ </label>
+ </div>
</div>
</div>
- </div>
+ )}
<div class="form-group">
<div class="col-12">
<button
number_of_posts: number;
number_of_comments: number;
number_of_communities: number;
+ enable_downvotes: boolean;
+ open_registration: boolean;
+ enable_nsfw: boolean;
}
export enum BanType {
export interface SiteForm {
name: string;
description?: string;
- removed?: boolean;
- reason?: string;
- expires?: number;
+ enable_downvotes: boolean;
+ open_registration: boolean;
+ enable_nsfw: boolean;
auth?: string;
}
expires: 'Expires',
language: 'Language',
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',
body: 'Body',
copy_suggested_title: 'copy suggested title: {{title}}',