-drop table user_
+drop table user_ban;
+drop table user_;
password_encrypted text not null,
email text unique,
icon bytea,
+ admin boolean default false,
+ banned boolean default false,
published timestamp not null default now(),
updated timestamp,
unique(name, fedi_name)
);
+create table user_ban (
+ id serial primary key,
+ user_id int references user_ on update cascade on delete cascade not null,
+ published timestamp not null default now(),
+ unique (user_id)
+);
+
insert into user_ (name, fedi_name, password_encrypted) values ('admin', 'TBD', 'TBD');
+drop table community_user_ban;;
drop table community_moderator;
drop table community_follower;
drop table community;
description text,
category_id int references category on update cascade on delete cascade not null,
creator_id int references user_ on update cascade on delete cascade not null,
+ removed boolean default false,
published timestamp not null default now(),
updated timestamp
);
id serial primary key,
community_id int references community on update cascade on delete cascade not null,
user_id int references user_ on update cascade on delete cascade not null,
- published timestamp not null default now()
+ published timestamp not null default now(),
+ unique (community_id, user_id)
);
create table community_follower (
id serial primary key,
community_id int references community on update cascade on delete cascade not null,
user_id int references user_ on update cascade on delete cascade not null,
- published timestamp not null default now()
+ published timestamp not null default now(),
+ unique (community_id, user_id)
+);
+
+create table community_user_ban (
+ id serial primary key,
+ community_id int references community on update cascade on delete cascade not null,
+ user_id int references user_ on update cascade on delete cascade not null,
+ published timestamp not null default now(),
+ unique (community_id, user_id)
);
insert into community (name, title, category_id, creator_id) values ('main', 'The Default Community', 1, 1);
body text,
creator_id int references user_ on update cascade on delete cascade not null,
community_id int references community on update cascade on delete cascade not null,
+ removed boolean default false,
+ locked boolean default false,
published timestamp not null default now(),
updated timestamp
);
post_id int references post on update cascade on delete cascade not null,
parent_id int references comment on update cascade on delete cascade,
content text not null,
+ removed boolean default false,
published timestamp not null default now(),
updated timestamp
);
ap.*,
u.id as user_id,
coalesce(pl.score, 0) as my_vote,
-(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed
+(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
+u.admin or (select cm.id::bool from community_moderator cm where u.id = cm.user_id and cm.community_id = ap.community_id) as am_mod
from user_ u
cross join all_post ap
left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
ap.*,
null as user_id,
null as my_vote,
-null as subscribed
+null as subscribed,
+null as am_mod
from all_post ap
;
-
-/* The old post view */
-/* create view post_view as */
-/* select */
-/* u.id as user_id, */
-/* pl.score as my_vote, */
-/* p.id as id, */
-/* p.name as name, */
-/* p.url, */
-/* p.body, */
-/* p.creator_id, */
-/* (select name from user_ where p.creator_id = user_.id) creator_name, */
-/* p.community_id, */
-/* (select name from community where p.community_id = community.id) as community_name, */
-/* (select count(*) from comment where comment.post_id = p.id) as number_of_comments, */
-/* coalesce(sum(pl.score) over (partition by p.id), 0) as score, */
-/* count (case when pl.score = 1 then 1 else null end) over (partition by p.id) as upvotes, */
-/* count (case when pl.score = -1 then 1 else null end) over (partition by p.id) as downvotes, */
-/* hot_rank(coalesce(sum(pl.score) over (partition by p.id) , 0), p.published) as hot_rank, */
-/* p.published, */
-/* p.updated */
-/* from user_ u */
-/* cross join post p */
-/* left join post_like pl on u.id = pl.user_id and p.id = pl.post_id; */
-
-
-
drop view community_view;
drop view community_moderator_view;
drop view community_follower_view;
+drop view community_user_ban_view;
select
ac.*,
u.id as user_id,
-cf.id::boolean as subscribed
+cf.id::boolean as subscribed,
+u.admin or (select cm.id::bool from community_moderator cm where u.id = cm.user_id and cm.community_id = ac.id) as am_mod
from user_ u
cross join all_community ac
left join community_follower cf on u.id = cf.user_id and ac.id = cf.community_id
select
ac.*,
null as user_id,
-null as subscribed
+null as subscribed,
+null as am_mod
from all_community ac
;
(select name from user_ u where cf.user_id = u.id) as user_name,
(select name from community c where cf.community_id = c.id) as community_name
from community_follower cf;
+
+create view community_user_ban_view as
+select *,
+(select name from user_ u where cm.user_id = u.id) as user_name,
+(select name from community c where cm.community_id = c.id) as community_name
+from community_user_ban cm;
(
select
c.*,
- (select name from user_ where c.creator_id = user_.id) creator_name,
+ (select community_id from post p where p.id = c.post_id),
+ (select cb.id::bool from community_user_ban cb where c.creator_id = cb.user_id) as banned,
+ (select name from user_ where c.creator_id = user_.id) as creator_name,
coalesce(sum(cl.score), 0) as score,
count (case when cl.score = 1 then 1 else null end) as upvotes,
count (case when cl.score = -1 then 1 else null end) as downvotes
select
ac.*,
u.id as user_id,
-coalesce(cl.score, 0) as my_vote
+coalesce(cl.score, 0) as my_vote,
+u.admin or (select cm.id::bool from community_moderator cm, post p where u.id = cm.user_id and ac.post_id = p.id and p.community_id = cm.community_id) as am_mod
from user_ u
cross join all_comment ac
left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id
select
ac.*,
null as user_id,
- null as my_vote
+ null as my_vote,
+ null as am_mod
from all_comment ac
;
drop table mod_remove_comment;
drop table mod_remove_community;
drop table mod_ban;
-drop table mod_add_mod;
+drop table mod_ban_from_community;
+drop table mod_add;
+drop table mod_add_community;
-
create table mod_remove_post (
id serial primary key,
mod_user_id int references user_ on update cascade on delete cascade not null,
id serial primary key,
mod_user_id int references user_ on update cascade on delete cascade not null,
post_id int references post on update cascade on delete cascade not null,
+ locked boolean default true,
when_ timestamp not null default now()
);
community_id int references community on update cascade on delete cascade not null,
reason text,
removed boolean default true,
+ expires timestamp,
when_ timestamp not null default now()
);
-- TODO make sure you can't ban other mods
+create table mod_ban_from_community (
+ id serial primary key,
+ mod_user_id int references user_ on update cascade on delete cascade not null,
+ other_user_id int references user_ on update cascade on delete cascade not null,
+ community_id int references community on update cascade on delete cascade not null,
+ reason text,
+ banned boolean default true,
+ expires timestamp,
+ when_ timestamp not null default now()
+);
+
create table mod_ban (
id serial primary key,
mod_user_id int references user_ on update cascade on delete cascade not null,
other_user_id int references user_ on update cascade on delete cascade not null,
reason text,
- removed boolean default true,
+ banned boolean default true,
expires timestamp,
when_ timestamp not null default now()
);
+create table mod_add_community (
+ id serial primary key,
+ mod_user_id int references user_ on update cascade on delete cascade not null,
+ other_user_id int references user_ on update cascade on delete cascade not null,
+ community_id int references community on update cascade on delete cascade not null,
+ removed boolean default false,
+ when_ timestamp not null default now()
+);
+
-- When removed is false that means kicked
-create table mod_add_mod (
+create table mod_add (
id serial primary key,
mod_user_id int references user_ on update cascade on delete cascade not null,
other_user_id int references user_ on update cascade on delete cascade not null,
removed boolean default false,
when_ timestamp not null default now()
-)
+);
+
select id,
name,
fedi_name,
+admin,
+banned,
published,
(select count(*) from post p where p.creator_id = u.id) as number_of_posts,
(select coalesce(sum(score), 0) from post p, post_like pl where u.id = p.creator_id and p.id = pl.post_id) as post_score,
(select count(*) from comment c where c.creator_id = u.id) as number_of_comments,
(select coalesce(sum(score), 0) from comment c, comment_like cl where u.id = c.creator_id and c.id = cl.comment_id) as comment_score
from user_ u;
-
--- /dev/null
+drop view mod_remove_post_view;
+drop view mod_lock_post_view;
+drop view mod_remove_comment_view;
+drop view mod_remove_community_view;
+drop view mod_ban_from_community_view;
+drop view mod_ban_view;
+drop view mod_add_community_view;
+drop view mod_add_view;
--- /dev/null
+create view mod_remove_post_view as
+select mrp.*,
+(select name from user_ u where mrp.mod_user_id = u.id) as mod_user_name,
+(select name from post p where mrp.post_id = p.id) as post_name,
+(select c.id from post p, community c where mrp.post_id = p.id and p.community_id = c.id) as community_id,
+(select c.name from post p, community c where mrp.post_id = p.id and p.community_id = c.id) as community_name
+from mod_remove_post mrp;
+
+create view mod_lock_post_view as
+select mlp.*,
+(select name from user_ u where mlp.mod_user_id = u.id) as mod_user_name,
+(select name from post p where mlp.post_id = p.id) as post_name,
+(select c.id from post p, community c where mlp.post_id = p.id and p.community_id = c.id) as community_id,
+(select c.name from post p, community c where mlp.post_id = p.id and p.community_id = c.id) as community_name
+from mod_lock_post mlp;
+
+create view mod_remove_comment_view as
+select mrc.*,
+(select name from user_ u where mrc.mod_user_id = u.id) as mod_user_name,
+(select c.id from comment c where mrc.comment_id = c.id) as comment_user_id,
+(select name from user_ u, comment c where mrc.comment_id = c.id and u.id = c.creator_id) as comment_user_name,
+(select content from comment c where mrc.comment_id = c.id) as comment_content,
+(select p.id from post p, comment c where mrc.comment_id = c.id and c.post_id = p.id) as post_id,
+(select p.name from post p, comment c where mrc.comment_id = c.id and c.post_id = p.id) as post_name,
+(select co.id from comment c, post p, community co where mrc.comment_id = c.id and c.post_id = p.id and p.community_id = co.id) as community_id,
+(select co.name from comment c, post p, community co where mrc.comment_id = c.id and c.post_id = p.id and p.community_id = co.id) as community_name
+from mod_remove_comment mrc;
+
+create view mod_remove_community_view as
+select mrc.*,
+(select name from user_ u where mrc.mod_user_id = u.id) as mod_user_name,
+(select c.name from community c where mrc.community_id = c.id) as community_name
+from mod_remove_community mrc;
+
+create view mod_ban_from_community_view as
+select mb.*,
+(select name from user_ u where mb.mod_user_id = u.id) as mod_user_name,
+(select name from user_ u where mb.other_user_id = u.id) as other_user_name,
+(select name from community c where mb.community_id = c.id) as community_name
+from mod_ban_from_community mb;
+
+create view mod_ban_view as
+select mb.*,
+(select name from user_ u where mb.mod_user_id = u.id) as mod_user_name,
+(select name from user_ u where mb.other_user_id = u.id) as other_user_name
+from mod_ban_from_community mb;
+
+
+create view mod_add_community_view as
+select ma.*,
+(select name from user_ u where ma.mod_user_id = u.id) as mod_user_name,
+(select name from user_ u where ma.other_user_id = u.id) as other_user_name,
+(select name from community c where ma.community_id = c.id) as community_name
+from mod_add_community ma;
+
+
+create view mod_add_view as
+select ma.*,
+(select name from user_ u where ma.mod_user_id = u.id) as mod_user_name,
+(select name from user_ u where ma.other_user_id = u.id) as other_user_name
+from mod_add ma;
pub post_id: i32,
pub parent_id: Option<i32>,
pub content: String,
+ pub removed: Option<bool>,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>
}
pub post_id: i32,
pub parent_id: Option<i32>,
pub content: String,
+ pub removed: Option<bool>,
pub updated: Option<chrono::NaiveDateTime>
}
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ admin: None,
+ banned: None,
updated: None
};
description: None,
category_id: 1,
creator_id: inserted_user.id,
+ removed: None,
updated: None
};
url: None,
body: None,
community_id: inserted_community.id,
+ removed: None,
+ locked: None,
updated: None
};
content: "A test comment".into(),
creator_id: inserted_user.id,
post_id: inserted_post.id,
+ removed: None,
parent_id: None,
updated: None
};
content: "A test comment".into(),
creator_id: inserted_user.id,
post_id: inserted_post.id,
+ removed: Some(false),
parent_id: None,
published: inserted_comment.published,
updated: None
creator_id: inserted_user.id,
post_id: inserted_post.id,
parent_id: Some(inserted_comment.id),
+ removed: None,
updated: None
};
post_id -> Int4,
parent_id -> Nullable<Int4>,
content -> Text,
+ removed -> Nullable<Bool>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ community_id -> Int4,
+ banned -> Nullable<Bool>,
creator_name -> Varchar,
score -> BigInt,
upvotes -> BigInt,
downvotes -> BigInt,
user_id -> Nullable<Int4>,
my_vote -> Nullable<Int4>,
+ am_mod -> Nullable<Bool>,
}
}
pub post_id: i32,
pub parent_id: Option<i32>,
pub content: String,
+ pub removed: Option<bool>,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
+ pub community_id: i32,
+ pub banned: Option<bool>,
pub creator_name: String,
pub score: i64,
pub upvotes: i64,
pub downvotes: i64,
pub user_id: Option<i32>,
pub my_vote: Option<i32>,
+ pub am_mod: Option<bool>,
}
impl CommentView {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ admin: None,
+ banned: None,
updated: None
};
description: None,
category_id: 1,
creator_id: inserted_user.id,
+ removed: None,
updated: None
};
url: None,
body: None,
community_id: inserted_community.id,
+ removed: None,
+ locked: None,
updated: None
};
creator_id: inserted_user.id,
post_id: inserted_post.id,
parent_id: None,
+ removed: None,
updated: None
};
content: "A test comment 32".into(),
creator_id: inserted_user.id,
post_id: inserted_post.id,
+ community_id: inserted_community.id,
parent_id: None,
+ removed: Some(false),
+ banned: None,
published: inserted_comment.published,
updated: None,
creator_name: inserted_user.name.to_owned(),
downvotes: 0,
upvotes: 1,
user_id: None,
- my_vote: None
+ my_vote: None,
+ am_mod: None,
};
let expected_comment_view_with_user = CommentView {
content: "A test comment 32".into(),
creator_id: inserted_user.id,
post_id: inserted_post.id,
+ community_id: inserted_community.id,
parent_id: None,
+ removed: Some(false),
+ banned: None,
published: inserted_comment.published,
updated: None,
creator_name: inserted_user.name.to_owned(),
upvotes: 1,
user_id: Some(inserted_user.id),
my_vote: Some(1),
+ am_mod: None,
};
let read_comment_views_no_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, None, 999).unwrap();
extern crate diesel;
-use schema::{community, community_moderator, community_follower};
+use schema::{community, community_moderator, community_follower, community_user_ban};
use diesel::*;
use diesel::result::Error;
use serde::{Deserialize, Serialize};
-use {Crud, Followable, Joinable};
+use {Crud, Followable, Joinable, Bannable};
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
#[table_name="community"]
pub description: Option<String>,
pub category_id: i32,
pub creator_id: i32,
+ pub removed: Option<bool>,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>
}
pub description: Option<String>,
pub category_id: i32,
pub creator_id: i32,
+ pub removed: Option<bool>,
pub updated: Option<chrono::NaiveDateTime>
}
pub user_id: i32,
}
+#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
+#[belongs_to(Community)]
+#[table_name = "community_user_ban"]
+pub struct CommunityUserBan {
+ pub id: i32,
+ pub community_id: i32,
+ pub user_id: i32,
+ pub published: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone)]
+#[table_name="community_user_ban"]
+pub struct CommunityUserBanForm {
+ pub community_id: i32,
+ pub user_id: i32,
+}
+
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
#[belongs_to(Community)]
#[table_name = "community_follower"]
}
}
+impl Bannable<CommunityUserBanForm> for CommunityUserBan {
+ fn ban(conn: &PgConnection, community_user_ban_form: &CommunityUserBanForm) -> Result<Self, Error> {
+ use schema::community_user_ban::dsl::*;
+ insert_into(community_user_ban)
+ .values(community_user_ban_form)
+ .get_result::<Self>(conn)
+ }
+
+ fn unban(conn: &PgConnection, community_user_ban_form: &CommunityUserBanForm) -> Result<usize, Error> {
+ use schema::community_user_ban::dsl::*;
+ diesel::delete(community_user_ban
+ .filter(community_id.eq(community_user_ban_form.community_id))
+ .filter(user_id.eq(community_user_ban_form.user_id)))
+ .execute(conn)
+ }
+}
+
#[cfg(test)]
mod tests {
use establish_connection;
let conn = establish_connection();
let new_user = UserForm {
- name: "bob".into(),
+ name: "bobbee".into(),
fedi_name: "rrf".into(),
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ admin: None,
+ banned: None,
updated: None
};
title: "nada".to_owned(),
description: None,
category_id: 1,
+ removed: None,
updated: None,
};
title: "nada".to_owned(),
description: None,
category_id: 1,
+ removed: Some(false),
published: inserted_community.published,
updated: None
};
-
let community_follower_form = CommunityFollowerForm {
community_id: inserted_community.id,
user_id: inserted_user.id
let inserted_community_follower = CommunityFollower::follow(&conn, &community_follower_form).unwrap();
+
let expected_community_follower = CommunityFollower {
id: inserted_community_follower.id,
community_id: inserted_community.id,
published: inserted_community_user.published
};
+ let community_user_ban_form = CommunityUserBanForm {
+ community_id: inserted_community.id,
+ user_id: inserted_user.id
+ };
+
+ let inserted_community_user_ban = CommunityUserBan::ban(&conn, &community_user_ban_form).unwrap();
+
+ let expected_community_user_ban = CommunityUserBan {
+ id: inserted_community_user_ban.id,
+ community_id: inserted_community.id,
+ user_id: inserted_user.id,
+ published: inserted_community_user_ban.published
+ };
+
let read_community = Community::read(&conn, inserted_community.id).unwrap();
let updated_community = Community::update(&conn, inserted_community.id, &new_community).unwrap();
let ignored_community = CommunityFollower::ignore(&conn, &community_follower_form).unwrap();
let left_community = CommunityModerator::leave(&conn, &community_user_form).unwrap();
+ let unban = CommunityUserBan::unban(&conn, &community_user_ban_form).unwrap();
let num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
User_::delete(&conn, inserted_user.id).unwrap();
assert_eq!(expected_community, updated_community);
assert_eq!(expected_community_follower, inserted_community_follower);
assert_eq!(expected_community_user, inserted_community_user);
+ assert_eq!(expected_community_user_ban, inserted_community_user_ban);
assert_eq!(1, ignored_community);
assert_eq!(1, left_community);
+ assert_eq!(1, unban);
// assert_eq!(2, loaded_count);
assert_eq!(1, num_deleted);
description -> Nullable<Text>,
category_id -> Int4,
creator_id -> Int4,
+ removed -> Nullable<Bool>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
creator_name -> Varchar,
number_of_comments -> BigInt,
user_id -> Nullable<Int4>,
subscribed -> Nullable<Bool>,
+ am_mod -> Nullable<Bool>,
}
}
}
}
+table! {
+ community_user_ban_view (id) {
+ id -> Int4,
+ community_id -> Int4,
+ user_id -> Int4,
+ published -> Timestamp,
+ user_name -> Varchar,
+ community_name -> Varchar,
+ }
+}
+
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
#[table_name="community_view"]
pub struct CommunityView {
pub description: Option<String>,
pub category_id: i32,
pub creator_id: i32,
+ pub removed: Option<bool>,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub creator_name: String,
pub number_of_comments: i64,
pub user_id: Option<i32>,
pub subscribed: Option<bool>,
+ pub am_mod: Option<bool>,
}
impl CommunityView {
query = query.limit(limit);
};
- query.load::<Self>(conn)
+ query.filter(removed.eq(false)).load::<Self>(conn)
}
}
community_follower_view.filter(user_id.eq(from_user_id)).load::<Self>(conn)
}
}
+
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="community_user_ban_view"]
+pub struct CommunityUserBanView {
+ pub id: i32,
+ pub community_id: i32,
+ pub user_id: i32,
+ pub published: chrono::NaiveDateTime,
+ pub user_name : String,
+ pub community_name: String,
+}
+
+impl CommunityUserBanView {
+ pub fn for_community(conn: &PgConnection, from_community_id: i32) -> Result<Vec<Self>, Error> {
+ use actions::community_view::community_user_ban_view::dsl::*;
+ community_user_ban_view.filter(community_id.eq(from_community_id)).load::<Self>(conn)
+ }
+
+ pub fn for_user(conn: &PgConnection, from_user_id: i32) -> Result<Vec<Self>, Error> {
+ use actions::community_view::community_user_ban_view::dsl::*;
+ community_user_ban_view.filter(user_id.eq(from_user_id)).load::<Self>(conn)
+ }
+
+ pub fn get(conn: &PgConnection, from_user_id: i32, from_community_id: i32) -> Result<Self, Error> {
+ use actions::community_view::community_user_ban_view::dsl::*;
+ community_user_ban_view
+ .filter(user_id.eq(from_user_id))
+ .filter(community_id.eq(from_community_id))
+ .first::<Self>(conn)
+ }
+}
pub mod category;
pub mod community_view;
pub mod user_view;
+pub mod moderator;
+pub mod moderator_views;
--- /dev/null
+extern crate diesel;
+use schema::{mod_remove_post, mod_lock_post, mod_remove_comment, mod_remove_community, mod_ban_from_community, mod_ban, mod_add_community, mod_add};
+use diesel::*;
+use diesel::result::Error;
+use serde::{Deserialize, Serialize};
+use {Crud};
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name="mod_remove_post"]
+pub struct ModRemovePost {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub post_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name="mod_remove_post"]
+pub struct ModRemovePostForm {
+ pub mod_user_id: i32,
+ pub post_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+}
+
+impl Crud<ModRemovePostForm> for ModRemovePost {
+ fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+ use schema::mod_remove_post::dsl::*;
+ mod_remove_post.find(from_id)
+ .first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+ use schema::mod_remove_post::dsl::*;
+ diesel::delete(mod_remove_post.find(from_id))
+ .execute(conn)
+ }
+
+ fn create(conn: &PgConnection, form: &ModRemovePostForm) -> Result<Self, Error> {
+ use schema::mod_remove_post::dsl::*;
+ insert_into(mod_remove_post)
+ .values(form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, from_id: i32, form: &ModRemovePostForm) -> Result<Self, Error> {
+ use schema::mod_remove_post::dsl::*;
+ diesel::update(mod_remove_post.find(from_id))
+ .set(form)
+ .get_result::<Self>(conn)
+ }
+}
+
+
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name="mod_lock_post"]
+pub struct ModLockPost {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub post_id: i32,
+ pub locked: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name="mod_lock_post"]
+pub struct ModLockPostForm {
+ pub mod_user_id: i32,
+ pub post_id: i32,
+ pub locked: Option<bool>,
+}
+
+impl Crud<ModLockPostForm> for ModLockPost {
+ fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+ use schema::mod_lock_post::dsl::*;
+ mod_lock_post.find(from_id)
+ .first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+ use schema::mod_lock_post::dsl::*;
+ diesel::delete(mod_lock_post.find(from_id))
+ .execute(conn)
+ }
+
+ fn create(conn: &PgConnection, form: &ModLockPostForm) -> Result<Self, Error> {
+ use schema::mod_lock_post::dsl::*;
+ insert_into(mod_lock_post)
+ .values(form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, from_id: i32, form: &ModLockPostForm) -> Result<Self, Error> {
+ use schema::mod_lock_post::dsl::*;
+ diesel::update(mod_lock_post.find(from_id))
+ .set(form)
+ .get_result::<Self>(conn)
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name="mod_remove_comment"]
+pub struct ModRemoveComment {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub comment_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name="mod_remove_comment"]
+pub struct ModRemoveCommentForm {
+ pub mod_user_id: i32,
+ pub comment_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+}
+
+impl Crud<ModRemoveCommentForm> for ModRemoveComment {
+ fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+ use schema::mod_remove_comment::dsl::*;
+ mod_remove_comment.find(from_id)
+ .first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+ use schema::mod_remove_comment::dsl::*;
+ diesel::delete(mod_remove_comment.find(from_id))
+ .execute(conn)
+ }
+
+ fn create(conn: &PgConnection, form: &ModRemoveCommentForm) -> Result<Self, Error> {
+ use schema::mod_remove_comment::dsl::*;
+ insert_into(mod_remove_comment)
+ .values(form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, from_id: i32, form: &ModRemoveCommentForm) -> Result<Self, Error> {
+ use schema::mod_remove_comment::dsl::*;
+ diesel::update(mod_remove_comment.find(from_id))
+ .set(form)
+ .get_result::<Self>(conn)
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name="mod_remove_community"]
+pub struct ModRemoveCommunity {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub community_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+ pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name="mod_remove_community"]
+pub struct ModRemoveCommunityForm {
+ pub mod_user_id: i32,
+ pub community_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+}
+
+impl Crud<ModRemoveCommunityForm> for ModRemoveCommunity {
+ fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+ use schema::mod_remove_community::dsl::*;
+ mod_remove_community.find(from_id)
+ .first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+ use schema::mod_remove_community::dsl::*;
+ diesel::delete(mod_remove_community.find(from_id))
+ .execute(conn)
+ }
+
+ fn create(conn: &PgConnection, form: &ModRemoveCommunityForm) -> Result<Self, Error> {
+ use schema::mod_remove_community::dsl::*;
+ insert_into(mod_remove_community)
+ .values(form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, from_id: i32, form: &ModRemoveCommunityForm) -> Result<Self, Error> {
+ use schema::mod_remove_community::dsl::*;
+ diesel::update(mod_remove_community.find(from_id))
+ .set(form)
+ .get_result::<Self>(conn)
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name="mod_ban_from_community"]
+pub struct ModBanFromCommunity {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub community_id: i32,
+ pub reason: Option<String>,
+ pub banned: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+ pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name="mod_ban_from_community"]
+pub struct ModBanFromCommunityForm {
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub community_id: i32,
+ pub reason: Option<String>,
+ pub banned: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+}
+
+impl Crud<ModBanFromCommunityForm> for ModBanFromCommunity {
+ fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+ use schema::mod_ban_from_community::dsl::*;
+ mod_ban_from_community.find(from_id)
+ .first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+ use schema::mod_ban_from_community::dsl::*;
+ diesel::delete(mod_ban_from_community.find(from_id))
+ .execute(conn)
+ }
+
+ fn create(conn: &PgConnection, form: &ModBanFromCommunityForm) -> Result<Self, Error> {
+ use schema::mod_ban_from_community::dsl::*;
+ insert_into(mod_ban_from_community)
+ .values(form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, from_id: i32, form: &ModBanFromCommunityForm) -> Result<Self, Error> {
+ use schema::mod_ban_from_community::dsl::*;
+ diesel::update(mod_ban_from_community.find(from_id))
+ .set(form)
+ .get_result::<Self>(conn)
+ }
+}
+
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name="mod_ban"]
+pub struct ModBan {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub reason: Option<String>,
+ pub banned: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+ pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name="mod_ban"]
+pub struct ModBanForm {
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub reason: Option<String>,
+ pub banned: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+}
+
+impl Crud<ModBanForm> for ModBan {
+ fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+ use schema::mod_ban::dsl::*;
+ mod_ban.find(from_id)
+ .first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+ use schema::mod_ban::dsl::*;
+ diesel::delete(mod_ban.find(from_id))
+ .execute(conn)
+ }
+
+ fn create(conn: &PgConnection, form: &ModBanForm) -> Result<Self, Error> {
+ use schema::mod_ban::dsl::*;
+ insert_into(mod_ban)
+ .values(form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, from_id: i32, form: &ModBanForm) -> Result<Self, Error> {
+ use schema::mod_ban::dsl::*;
+ diesel::update(mod_ban.find(from_id))
+ .set(form)
+ .get_result::<Self>(conn)
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name="mod_add_community"]
+pub struct ModAddCommunity {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub community_id: i32,
+ pub removed: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name="mod_add_community"]
+pub struct ModAddCommunityForm {
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub community_id: i32,
+ pub removed: Option<bool>,
+}
+
+impl Crud<ModAddCommunityForm> for ModAddCommunity {
+ fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+ use schema::mod_add_community::dsl::*;
+ mod_add_community.find(from_id)
+ .first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+ use schema::mod_add_community::dsl::*;
+ diesel::delete(mod_add_community.find(from_id))
+ .execute(conn)
+ }
+
+ fn create(conn: &PgConnection, form: &ModAddCommunityForm) -> Result<Self, Error> {
+ use schema::mod_add_community::dsl::*;
+ insert_into(mod_add_community)
+ .values(form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, from_id: i32, form: &ModAddCommunityForm) -> Result<Self, Error> {
+ use schema::mod_add_community::dsl::*;
+ diesel::update(mod_add_community.find(from_id))
+ .set(form)
+ .get_result::<Self>(conn)
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name="mod_add"]
+pub struct ModAdd {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub removed: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name="mod_add"]
+pub struct ModAddForm {
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub removed: Option<bool>,
+}
+
+impl Crud<ModAddForm> for ModAdd {
+ fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+ use schema::mod_add::dsl::*;
+ mod_add.find(from_id)
+ .first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+ use schema::mod_add::dsl::*;
+ diesel::delete(mod_add.find(from_id))
+ .execute(conn)
+ }
+
+ fn create(conn: &PgConnection, form: &ModAddForm) -> Result<Self, Error> {
+ use schema::mod_add::dsl::*;
+ insert_into(mod_add)
+ .values(form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, from_id: i32, form: &ModAddForm) -> Result<Self, Error> {
+ use schema::mod_add::dsl::*;
+ diesel::update(mod_add.find(from_id))
+ .set(form)
+ .get_result::<Self>(conn)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use establish_connection;
+ use super::*;
+ use actions::user::*;
+ use actions::post::*;
+ use actions::community::*;
+ use actions::comment::*;
+ // use Crud;
+ #[test]
+ fn test_crud() {
+ let conn = establish_connection();
+
+ let new_mod = UserForm {
+ name: "the mod".into(),
+ fedi_name: "rrf".into(),
+ preferred_username: None,
+ password_encrypted: "nope".into(),
+ email: None,
+ admin: None,
+ banned: None,
+ updated: None
+ };
+
+ let inserted_mod = User_::create(&conn, &new_mod).unwrap();
+
+ let new_user = UserForm {
+ name: "jim2".into(),
+ fedi_name: "rrf".into(),
+ preferred_username: None,
+ password_encrypted: "nope".into(),
+ email: None,
+ admin: None,
+ banned: None,
+ updated: None
+ };
+
+ let inserted_user = User_::create(&conn, &new_user).unwrap();
+
+ let new_community = CommunityForm {
+ name: "mod_community".to_string(),
+ title: "nada".to_owned(),
+ description: None,
+ category_id: 1,
+ creator_id: inserted_user.id,
+ removed: None,
+ updated: None
+ };
+
+ let inserted_community = Community::create(&conn, &new_community).unwrap();
+
+ let new_post = PostForm {
+ name: "A test post thweep".into(),
+ url: None,
+ body: None,
+ creator_id: inserted_user.id,
+ community_id: inserted_community.id,
+ removed: None,
+ locked: None,
+ updated: None
+ };
+
+ let inserted_post = Post::create(&conn, &new_post).unwrap();
+
+ let comment_form = CommentForm {
+ content: "A test comment".into(),
+ creator_id: inserted_user.id,
+ post_id: inserted_post.id,
+ removed: None,
+ parent_id: None,
+ updated: None
+ };
+
+ let inserted_comment = Comment::create(&conn, &comment_form).unwrap();
+
+ // Now the actual tests
+
+ // remove post
+ let mod_remove_post_form = ModRemovePostForm {
+ mod_user_id: inserted_mod.id,
+ post_id: inserted_post.id,
+ reason: None,
+ removed: None,
+ };
+ let inserted_mod_remove_post = ModRemovePost::create(&conn, &mod_remove_post_form).unwrap();
+ let read_moderator_remove_post = ModRemovePost::read(&conn, inserted_mod_remove_post.id).unwrap();
+ let expected_moderator_remove_post = ModRemovePost {
+ id: inserted_mod_remove_post.id,
+ post_id: inserted_post.id,
+ mod_user_id: inserted_mod.id,
+ reason: None,
+ removed: Some(true),
+ when_: inserted_mod_remove_post.when_,
+ };
+
+ // lock post
+
+ let mod_lock_post_form = ModLockPostForm {
+ mod_user_id: inserted_mod.id,
+ post_id: inserted_post.id,
+ locked: None,
+ };
+ let inserted_mod_lock_post = ModLockPost::create(&conn, &mod_lock_post_form).unwrap();
+ let read_moderator_lock_post = ModLockPost::read(&conn, inserted_mod_lock_post.id).unwrap();
+ let expected_moderator_lock_post = ModLockPost {
+ id: inserted_mod_lock_post.id,
+ post_id: inserted_post.id,
+ mod_user_id: inserted_mod.id,
+ locked: Some(true),
+ when_: inserted_mod_lock_post.when_,
+ };
+
+ // comment
+
+ let mod_remove_comment_form = ModRemoveCommentForm {
+ mod_user_id: inserted_mod.id,
+ comment_id: inserted_comment.id,
+ reason: None,
+ removed: None,
+ };
+ let inserted_mod_remove_comment = ModRemoveComment::create(&conn, &mod_remove_comment_form).unwrap();
+ let read_moderator_remove_comment = ModRemoveComment::read(&conn, inserted_mod_remove_comment.id).unwrap();
+ let expected_moderator_remove_comment = ModRemoveComment {
+ id: inserted_mod_remove_comment.id,
+ comment_id: inserted_comment.id,
+ mod_user_id: inserted_mod.id,
+ reason: None,
+ removed: Some(true),
+ when_: inserted_mod_remove_comment.when_,
+ };
+
+ // community
+
+ let mod_remove_community_form = ModRemoveCommunityForm {
+ mod_user_id: inserted_mod.id,
+ community_id: inserted_community.id,
+ reason: None,
+ removed: None,
+ expires: None,
+ };
+ let inserted_mod_remove_community = ModRemoveCommunity::create(&conn, &mod_remove_community_form).unwrap();
+ let read_moderator_remove_community = ModRemoveCommunity::read(&conn, inserted_mod_remove_community.id).unwrap();
+ let expected_moderator_remove_community = ModRemoveCommunity {
+ id: inserted_mod_remove_community.id,
+ community_id: inserted_community.id,
+ mod_user_id: inserted_mod.id,
+ reason: None,
+ removed: Some(true),
+ expires: None,
+ when_: inserted_mod_remove_community.when_,
+ };
+
+ // ban from community
+
+ let mod_ban_from_community_form = ModBanFromCommunityForm {
+ mod_user_id: inserted_mod.id,
+ other_user_id: inserted_user.id,
+ community_id: inserted_community.id,
+ reason: None,
+ banned: None,
+ expires: None,
+ };
+ let inserted_mod_ban_from_community = ModBanFromCommunity::create(&conn, &mod_ban_from_community_form).unwrap();
+ let read_moderator_ban_from_community = ModBanFromCommunity::read(&conn, inserted_mod_ban_from_community.id).unwrap();
+ let expected_moderator_ban_from_community = ModBanFromCommunity {
+ id: inserted_mod_ban_from_community.id,
+ community_id: inserted_community.id,
+ mod_user_id: inserted_mod.id,
+ other_user_id: inserted_user.id,
+ reason: None,
+ banned: Some(true),
+ expires: None,
+ when_: inserted_mod_ban_from_community.when_,
+ };
+
+ // ban
+
+ let mod_ban_form = ModBanForm {
+ mod_user_id: inserted_mod.id,
+ other_user_id: inserted_user.id,
+ reason: None,
+ banned: None,
+ expires: None,
+ };
+ let inserted_mod_ban = ModBan::create(&conn, &mod_ban_form).unwrap();
+ let read_moderator_ban = ModBan::read(&conn, inserted_mod_ban.id).unwrap();
+ let expected_moderator_ban = ModBan {
+ id: inserted_mod_ban.id,
+ mod_user_id: inserted_mod.id,
+ other_user_id: inserted_user.id,
+ reason: None,
+ banned: Some(true),
+ expires: None,
+ when_: inserted_mod_ban.when_,
+ };
+
+ // mod add community
+
+ let mod_add_community_form = ModAddCommunityForm {
+ mod_user_id: inserted_mod.id,
+ other_user_id: inserted_user.id,
+ community_id: inserted_community.id,
+ removed: None,
+ };
+ let inserted_mod_add_community = ModAddCommunity::create(&conn, &mod_add_community_form).unwrap();
+ let read_moderator_add_community = ModAddCommunity::read(&conn, inserted_mod_add_community.id).unwrap();
+ let expected_moderator_add_community = ModAddCommunity {
+ id: inserted_mod_add_community.id,
+ community_id: inserted_community.id,
+ mod_user_id: inserted_mod.id,
+ other_user_id: inserted_user.id,
+ removed: Some(false),
+ when_: inserted_mod_add_community.when_,
+ };
+
+ // mod add
+
+ let mod_add_form = ModAddForm {
+ mod_user_id: inserted_mod.id,
+ other_user_id: inserted_user.id,
+ removed: None,
+ };
+ let inserted_mod_add = ModAdd::create(&conn, &mod_add_form).unwrap();
+ let read_moderator_add = ModAdd::read(&conn, inserted_mod_add.id).unwrap();
+ let expected_moderator_add = ModAdd {
+ id: inserted_mod_add.id,
+ mod_user_id: inserted_mod.id,
+ other_user_id: inserted_user.id,
+ removed: Some(false),
+ when_: inserted_mod_add.when_,
+ };
+
+ ModRemovePost::delete(&conn, inserted_mod_remove_post.id).unwrap();
+ ModLockPost::delete(&conn, inserted_mod_lock_post.id).unwrap();
+ ModRemoveComment::delete(&conn, inserted_mod_remove_comment.id).unwrap();
+ ModRemoveCommunity::delete(&conn, inserted_mod_remove_community.id).unwrap();
+ ModBanFromCommunity::delete(&conn, inserted_mod_ban_from_community.id).unwrap();
+ ModBan::delete(&conn, inserted_mod_ban.id).unwrap();
+ ModAddCommunity::delete(&conn, inserted_mod_add_community.id).unwrap();
+ ModAdd::delete(&conn, inserted_mod_add.id).unwrap();
+
+ Comment::delete(&conn, inserted_comment.id).unwrap();
+ Post::delete(&conn, inserted_post.id).unwrap();
+ Community::delete(&conn, inserted_community.id).unwrap();
+ User_::delete(&conn, inserted_user.id).unwrap();
+ User_::delete(&conn, inserted_mod.id).unwrap();
+
+ assert_eq!(expected_moderator_remove_post, read_moderator_remove_post);
+ assert_eq!(expected_moderator_lock_post, read_moderator_lock_post);
+ assert_eq!(expected_moderator_remove_comment, read_moderator_remove_comment);
+ assert_eq!(expected_moderator_remove_community, read_moderator_remove_community);
+ assert_eq!(expected_moderator_ban_from_community, read_moderator_ban_from_community);
+ assert_eq!(expected_moderator_ban, read_moderator_ban);
+ assert_eq!(expected_moderator_add_community, read_moderator_add_community);
+ assert_eq!(expected_moderator_add, read_moderator_add);
+ }
+}
--- /dev/null
+extern crate diesel;
+use diesel::*;
+use diesel::result::Error;
+use serde::{Deserialize, Serialize};
+
+table! {
+ mod_remove_post_view (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ post_id -> Int4,
+ reason -> Nullable<Text>,
+ removed -> Nullable<Bool>,
+ when_ -> Timestamp,
+ mod_user_name -> Varchar,
+ post_name -> Varchar,
+ community_id -> Int4,
+ community_name -> Varchar,
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="mod_remove_post_view"]
+pub struct ModRemovePostView {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub post_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+ pub mod_user_name: String,
+ pub post_name: String,
+ pub community_id: i32,
+ pub community_name: String,
+}
+
+impl ModRemovePostView {
+ pub fn list(conn: &PgConnection,
+ from_community_id: Option<i32>,
+ from_mod_user_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>) -> Result<Vec<Self>, Error> {
+ use actions::moderator_views::mod_remove_post_view::dsl::*;
+ let mut query = mod_remove_post_view.into_boxed();
+
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+
+ if let Some(from_community_id) = from_community_id {
+ query = query.filter(community_id.eq(from_community_id));
+ };
+
+ if let Some(from_mod_user_id) = from_mod_user_id {
+ query = query.filter(mod_user_id.eq(from_mod_user_id));
+ };
+
+ query.limit(limit).offset(offset).order_by(when_.desc()).load::<Self>(conn)
+ }
+}
+
+table! {
+ mod_lock_post_view (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ post_id -> Int4,
+ locked -> Nullable<Bool>,
+ when_ -> Timestamp,
+ mod_user_name -> Varchar,
+ post_name -> Varchar,
+ community_id -> Int4,
+ community_name -> Varchar,
+ }
+}
+
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="mod_lock_post_view"]
+pub struct ModLockPostView {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub post_id: i32,
+ pub locked: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+ pub mod_user_name: String,
+ pub post_name: String,
+ pub community_id: i32,
+ pub community_name: String,
+}
+
+impl ModLockPostView {
+ pub fn list(conn: &PgConnection,
+ from_community_id: Option<i32>,
+ from_mod_user_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>) -> Result<Vec<Self>, Error> {
+ use actions::moderator_views::mod_lock_post_view::dsl::*;
+ let mut query = mod_lock_post_view.into_boxed();
+
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+
+ if let Some(from_community_id) = from_community_id {
+ query = query.filter(community_id.eq(from_community_id));
+ };
+
+ if let Some(from_mod_user_id) = from_mod_user_id {
+ query = query.filter(mod_user_id.eq(from_mod_user_id));
+ };
+
+ query.limit(limit).offset(offset).order_by(when_.desc()).load::<Self>(conn)
+ }
+}
+
+table! {
+ mod_remove_comment_view (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ comment_id -> Int4,
+ reason -> Nullable<Text>,
+ removed -> Nullable<Bool>,
+ when_ -> Timestamp,
+ mod_user_name -> Varchar,
+ comment_user_id -> Int4,
+ comment_user_name -> Varchar,
+ comment_content -> Text,
+ post_id -> Int4,
+ post_name -> Varchar,
+ community_id -> Int4,
+ community_name -> Varchar,
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="mod_remove_comment_view"]
+pub struct ModRemoveCommentView {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub comment_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+ pub mod_user_name: String,
+ pub comment_user_id: i32,
+ pub comment_user_name: String,
+ pub comment_content: String,
+ pub post_id: i32,
+ pub post_name: String,
+ pub community_id: i32,
+ pub community_name: String,
+}
+
+impl ModRemoveCommentView {
+ pub fn list(conn: &PgConnection,
+ from_community_id: Option<i32>,
+ from_mod_user_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>) -> Result<Vec<Self>, Error> {
+ use actions::moderator_views::mod_remove_comment_view::dsl::*;
+ let mut query = mod_remove_comment_view.into_boxed();
+
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+
+ if let Some(from_community_id) = from_community_id {
+ query = query.filter(community_id.eq(from_community_id));
+ };
+
+ if let Some(from_mod_user_id) = from_mod_user_id {
+ query = query.filter(mod_user_id.eq(from_mod_user_id));
+ };
+
+ query.limit(limit).offset(offset).order_by(when_.desc()).load::<Self>(conn)
+ }
+}
+
+table! {
+ mod_remove_community_view (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ community_id -> Int4,
+ reason -> Nullable<Text>,
+ removed -> Nullable<Bool>,
+ expires -> Nullable<Timestamp>,
+ when_ -> Timestamp,
+ mod_user_name -> Varchar,
+ community_name -> Varchar,
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="mod_remove_community_view"]
+pub struct ModRemoveCommunityView {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub community_id: i32,
+ pub reason: Option<String>,
+ pub removed: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+ pub when_: chrono::NaiveDateTime,
+ pub mod_user_name: String,
+ pub community_name: String,
+}
+
+impl ModRemoveCommunityView {
+ pub fn list(conn: &PgConnection,
+ from_mod_user_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>) -> Result<Vec<Self>, Error> {
+ use actions::moderator_views::mod_remove_community_view::dsl::*;
+ let mut query = mod_remove_community_view.into_boxed();
+
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+
+ if let Some(from_mod_user_id) = from_mod_user_id {
+ query = query.filter(mod_user_id.eq(from_mod_user_id));
+ };
+
+ query.limit(limit).offset(offset).order_by(when_.desc()).load::<Self>(conn)
+ }
+}
+
+
+table! {
+ mod_ban_from_community_view (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ other_user_id -> Int4,
+ community_id -> Int4,
+ reason -> Nullable<Text>,
+ banned -> Nullable<Bool>,
+ expires -> Nullable<Timestamp>,
+ when_ -> Timestamp,
+ mod_user_name -> Varchar,
+ other_user_name -> Varchar,
+ community_name -> Varchar,
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="mod_ban_from_community_view"]
+pub struct ModBanFromCommunityView {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub community_id: i32,
+ pub reason: Option<String>,
+ pub banned: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+ pub when_: chrono::NaiveDateTime,
+ pub mod_user_name: String,
+ pub other_user_name: String,
+ pub community_name: String,
+}
+
+impl ModBanFromCommunityView {
+ pub fn list(conn: &PgConnection,
+ from_community_id: Option<i32>,
+ from_mod_user_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>) -> Result<Vec<Self>, Error> {
+ use actions::moderator_views::mod_ban_from_community_view::dsl::*;
+ let mut query = mod_ban_from_community_view.into_boxed();
+
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+
+ if let Some(from_community_id) = from_community_id {
+ query = query.filter(community_id.eq(from_community_id));
+ };
+
+ if let Some(from_mod_user_id) = from_mod_user_id {
+ query = query.filter(mod_user_id.eq(from_mod_user_id));
+ };
+
+ query.limit(limit).offset(offset).order_by(when_.desc()).load::<Self>(conn)
+ }
+}
+
+table! {
+ mod_ban_view (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ other_user_id -> Int4,
+ reason -> Nullable<Text>,
+ banned -> Nullable<Bool>,
+ expires -> Nullable<Timestamp>,
+ when_ -> Timestamp,
+ mod_user_name -> Varchar,
+ other_user_name -> Varchar,
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="mod_ban_view"]
+pub struct ModBanView {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub reason: Option<String>,
+ pub banned: Option<bool>,
+ pub expires: Option<chrono::NaiveDateTime>,
+ pub when_: chrono::NaiveDateTime,
+ pub mod_user_name: String,
+ pub other_user_name: String,
+}
+
+impl ModBanView {
+ pub fn list(conn: &PgConnection,
+ from_mod_user_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>) -> Result<Vec<Self>, Error> {
+ use actions::moderator_views::mod_ban_view::dsl::*;
+ let mut query = mod_ban_view.into_boxed();
+
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+
+ if let Some(from_mod_user_id) = from_mod_user_id {
+ query = query.filter(mod_user_id.eq(from_mod_user_id));
+ };
+
+ query.limit(limit).offset(offset).order_by(when_.desc()).load::<Self>(conn)
+ }
+}
+
+table! {
+ mod_add_community_view (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ other_user_id -> Int4,
+ community_id -> Int4,
+ removed -> Nullable<Bool>,
+ when_ -> Timestamp,
+ mod_user_name -> Varchar,
+ other_user_name -> Varchar,
+ community_name -> Varchar,
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="mod_add_community_view"]
+pub struct ModAddCommunityView {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub community_id: i32,
+ pub removed: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+ pub mod_user_name: String,
+ pub other_user_name: String,
+ pub community_name: String,
+}
+
+impl ModAddCommunityView {
+ pub fn list(conn: &PgConnection,
+ from_community_id: Option<i32>,
+ from_mod_user_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>) -> Result<Vec<Self>, Error> {
+ use actions::moderator_views::mod_add_community_view::dsl::*;
+ let mut query = mod_add_community_view.into_boxed();
+
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+
+ if let Some(from_community_id) = from_community_id {
+ query = query.filter(community_id.eq(from_community_id));
+ };
+
+ if let Some(from_mod_user_id) = from_mod_user_id {
+ query = query.filter(mod_user_id.eq(from_mod_user_id));
+ };
+
+ query.limit(limit).offset(offset).order_by(when_.desc()).load::<Self>(conn)
+ }
+}
+
+table! {
+ mod_add_view (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ other_user_id -> Int4,
+ removed -> Nullable<Bool>,
+ when_ -> Timestamp,
+ mod_user_name -> Varchar,
+ other_user_name -> Varchar,
+ }
+}
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
+#[table_name="mod_add_view"]
+pub struct ModAddView {
+ pub id: i32,
+ pub mod_user_id: i32,
+ pub other_user_id: i32,
+ pub removed: Option<bool>,
+ pub when_: chrono::NaiveDateTime,
+ pub mod_user_name: String,
+ pub other_user_name: String,
+}
+
+impl ModAddView {
+ pub fn list(conn: &PgConnection,
+ from_mod_user_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>) -> Result<Vec<Self>, Error> {
+ use actions::moderator_views::mod_add_view::dsl::*;
+ let mut query = mod_add_view.into_boxed();
+
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+
+ if let Some(from_mod_user_id) = from_mod_user_id {
+ query = query.filter(mod_user_id.eq(from_mod_user_id));
+ };
+
+ query.limit(limit).offset(offset).order_by(when_.desc()).load::<Self>(conn)
+ }
+}
pub body: Option<String>,
pub creator_id: i32,
pub community_id: i32,
+ pub removed: Option<bool>,
+ pub locked: Option<bool>,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>
}
pub body: Option<String>,
pub creator_id: i32,
pub community_id: i32,
+ pub removed: Option<bool>,
+ pub locked: Option<bool>,
pub updated: Option<chrono::NaiveDateTime>
}
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ admin: None,
+ banned: None,
updated: None
};
let inserted_user = User_::create(&conn, &new_user).unwrap();
let new_community = CommunityForm {
- name: "test community_2".to_string(),
+ name: "test community_3".to_string(),
title: "nada".to_owned(),
description: None,
category_id: 1,
creator_id: inserted_user.id,
+ removed: None,
updated: None
};
body: None,
creator_id: inserted_user.id,
community_id: inserted_community.id,
+ removed: None,
+ locked: None,
updated: None
};
creator_id: inserted_user.id,
community_id: inserted_community.id,
published: inserted_post.published,
+ removed: Some(false),
+ locked: Some(false),
updated: None
};
body -> Nullable<Text>,
creator_id -> Int4,
community_id -> Int4,
+ removed -> Nullable<Bool>,
+ locked -> Nullable<Bool>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
creator_name -> Varchar,
user_id -> Nullable<Int4>,
my_vote -> Nullable<Int4>,
subscribed -> Nullable<Bool>,
+ am_mod -> Nullable<Bool>,
}
}
pub body: Option<String>,
pub creator_id: i32,
pub community_id: i32,
+ pub removed: Option<bool>,
+ pub locked: Option<bool>,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub creator_name: String,
pub user_id: Option<i32>,
pub my_vote: Option<i32>,
pub subscribed: Option<bool>,
+ pub am_mod: Option<bool>,
}
impl PostView {
.order_by(score.desc())
};
+ query = query.filter(removed.eq(false));
+
query.load::<Self>(conn)
}
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
- updated: None
+ updated: None,
+ admin: None,
+ banned: None,
};
let inserted_user = User_::create(&conn, &new_user).unwrap();
description: None,
creator_id: inserted_user.id,
category_id: 1,
+ removed: None,
updated: None
};
body: None,
creator_id: inserted_user.id,
community_id: inserted_community.id,
+ removed: None,
+ locked: None,
updated: None
};
creator_id: inserted_user.id,
creator_name: user_name.to_owned(),
community_id: inserted_community.id,
+ removed: Some(false),
+ locked: Some(false),
community_name: community_name.to_owned(),
number_of_comments: 0,
score: 1,
hot_rank: 864,
published: inserted_post.published,
updated: None,
- subscribed: None
+ subscribed: None,
+ am_mod: None,
};
let expected_post_listing_with_user = PostView {
name: post_name.to_owned(),
url: None,
body: None,
+ removed: Some(false),
+ locked: Some(false),
creator_id: inserted_user.id,
creator_name: user_name.to_owned(),
community_id: inserted_community.id,
hot_rank: 864,
published: inserted_post.published,
updated: None,
- subscribed: None
+ subscribed: None,
+ am_mod: None,
};
assert_eq!(expected_post_like, inserted_post_like);
assert_eq!(1, like_removed);
assert_eq!(1, num_deleted);
-
}
}
pub password_encrypted: String,
pub email: Option<String>,
pub icon: Option<Vec<u8>>,
+ pub admin: Option<bool>,
+ pub banned: Option<bool>,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>
}
pub fedi_name: String,
pub preferred_username: Option<String>,
pub password_encrypted: String,
+ pub admin: Option<bool>,
+ pub banned: Option<bool>,
pub email: Option<String>,
pub updated: Option<chrono::NaiveDateTime>
}
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ admin: None,
+ banned: None,
updated: None
};
password_encrypted: "$2y$12$YXpNpYsdfjmed.QlYLvw4OfTCgyKUnKHc/V8Dgcf9YcVKHPaYXYYy".into(),
email: None,
icon: None,
+ admin: Some(false),
+ banned: Some(false),
published: inserted_user.published,
updated: None
};
id -> Int4,
name -> Varchar,
fedi_name -> Varchar,
+ admin -> Nullable<Bool>,
+ banned -> Nullable<Bool>,
published -> Timestamp,
number_of_posts -> BigInt,
post_score -> BigInt,
pub id: i32,
pub name: String,
pub fedi_name: String,
+ pub admin: Option<bool>,
+ pub banned: Option<bool>,
pub published: chrono::NaiveDateTime,
pub number_of_posts: i64,
pub post_score: i64,
email: None,
icon: None,
published: naive_now(),
+ admin: None,
+ banned: None,
updated: None
};
fn remove(conn: &PgConnection, form: &T) -> Result<usize, Error> where Self: Sized;
}
+pub trait Bannable<T> {
+ fn ban(conn: &PgConnection, form: &T) -> Result<Self, Error> where Self: Sized;
+ fn unban(conn: &PgConnection, form: &T) -> Result<usize, Error> where Self: Sized;
+}
+
pub fn establish_connection() -> PgConnection {
let db_url = Settings::get().db_url;
PgConnection::establish(&db_url)
chrono::prelude::Utc::now().naive_utc()
}
+pub fn naive_from_unix(time: i64) -> NaiveDateTime {
+ NaiveDateTime::from_timestamp(time, 0)
+}
+
pub fn is_email_regex(test: &str) -> bool {
EMAIL_REGEX.is_match(test)
}
post_id -> Int4,
parent_id -> Nullable<Int4>,
content -> Text,
+ removed -> Nullable<Bool>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
}
description -> Nullable<Text>,
category_id -> Int4,
creator_id -> Int4,
+ removed -> Nullable<Bool>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
}
}
}
+table! {
+ community_user_ban (id) {
+ id -> Int4,
+ community_id -> Int4,
+ user_id -> Int4,
+ published -> Timestamp,
+ }
+}
+
+table! {
+ mod_add (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ other_user_id -> Int4,
+ removed -> Nullable<Bool>,
+ when_ -> Timestamp,
+ }
+}
+
+table! {
+ mod_add_community (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ other_user_id -> Int4,
+ community_id -> Int4,
+ removed -> Nullable<Bool>,
+ when_ -> Timestamp,
+ }
+}
+
+table! {
+ mod_ban (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ other_user_id -> Int4,
+ reason -> Nullable<Text>,
+ banned -> Nullable<Bool>,
+ expires -> Nullable<Timestamp>,
+ when_ -> Timestamp,
+ }
+}
+
+table! {
+ mod_ban_from_community (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ other_user_id -> Int4,
+ community_id -> Int4,
+ reason -> Nullable<Text>,
+ banned -> Nullable<Bool>,
+ expires -> Nullable<Timestamp>,
+ when_ -> Timestamp,
+ }
+}
+
+table! {
+ mod_lock_post (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ post_id -> Int4,
+ locked -> Nullable<Bool>,
+ when_ -> Timestamp,
+ }
+}
+
+table! {
+ mod_remove_comment (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ comment_id -> Int4,
+ reason -> Nullable<Text>,
+ removed -> Nullable<Bool>,
+ when_ -> Timestamp,
+ }
+}
+
+table! {
+ mod_remove_community (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ community_id -> Int4,
+ reason -> Nullable<Text>,
+ removed -> Nullable<Bool>,
+ expires -> Nullable<Timestamp>,
+ when_ -> Timestamp,
+ }
+}
+
+table! {
+ mod_remove_post (id) {
+ id -> Int4,
+ mod_user_id -> Int4,
+ post_id -> Int4,
+ reason -> Nullable<Text>,
+ removed -> Nullable<Bool>,
+ when_ -> Timestamp,
+ }
+}
+
table! {
post (id) {
id -> Int4,
body -> Nullable<Text>,
creator_id -> Int4,
community_id -> Int4,
+ removed -> Nullable<Bool>,
+ locked -> Nullable<Bool>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
}
password_encrypted -> Text,
email -> Nullable<Text>,
icon -> Nullable<Bytea>,
+ admin -> Nullable<Bool>,
+ banned -> Nullable<Bool>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
}
}
+table! {
+ user_ban (id) {
+ id -> Int4,
+ user_id -> Int4,
+ published -> Timestamp,
+ }
+}
+
joinable!(comment -> post (post_id));
joinable!(comment -> user_ (creator_id));
joinable!(comment_like -> comment (comment_id));
joinable!(community_follower -> user_ (user_id));
joinable!(community_moderator -> community (community_id));
joinable!(community_moderator -> user_ (user_id));
+joinable!(community_user_ban -> community (community_id));
+joinable!(community_user_ban -> user_ (user_id));
+joinable!(mod_add_community -> community (community_id));
+joinable!(mod_ban_from_community -> community (community_id));
+joinable!(mod_lock_post -> post (post_id));
+joinable!(mod_lock_post -> user_ (mod_user_id));
+joinable!(mod_remove_comment -> comment (comment_id));
+joinable!(mod_remove_comment -> user_ (mod_user_id));
+joinable!(mod_remove_community -> community (community_id));
+joinable!(mod_remove_community -> user_ (mod_user_id));
+joinable!(mod_remove_post -> post (post_id));
+joinable!(mod_remove_post -> user_ (mod_user_id));
joinable!(post -> community (community_id));
joinable!(post -> user_ (creator_id));
joinable!(post_like -> post (post_id));
joinable!(post_like -> user_ (user_id));
+joinable!(user_ban -> user_ (user_id));
allow_tables_to_appear_in_same_query!(
category,
community,
community_follower,
community_moderator,
+ community_user_ban,
+ mod_add,
+ mod_add_community,
+ mod_ban,
+ mod_ban_from_community,
+ mod_lock_post,
+ mod_remove_comment,
+ mod_remove_community,
+ mod_remove_post,
post,
post_like,
user_,
+ user_ban,
);
use serde_json::{Value};
use bcrypt::{verify};
use std::str::FromStr;
+use diesel::PgConnection;
-use {Crud, Joinable, Likeable, Followable, establish_connection, naive_now, SortType, has_slurs, remove_slurs};
+use {Crud, Joinable, Likeable, Followable, Bannable, establish_connection, naive_now, naive_from_unix, SortType, has_slurs, remove_slurs};
use actions::community::*;
use actions::user::*;
use actions::post::*;
use actions::category::*;
use actions::community_view::*;
use actions::user_view::*;
+use actions::moderator_views::*;
+use actions::moderator::*;
#[derive(EnumString,ToString,Debug)]
pub enum UserOperation {
- Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails
+ Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity,
}
#[derive(Serialize, Deserialize)]
content: String,
parent_id: Option<i32>,
edit_id: i32,
+ creator_id: i32,
post_id: i32,
+ removed: Option<bool>,
+ reason: Option<String>,
auth: String
}
auth: String
}
-
#[derive(Serialize, Deserialize)]
pub struct CreatePostLike {
post_id: i32,
#[derive(Serialize, Deserialize)]
pub struct EditPost {
edit_id: i32,
+ creator_id: i32,
community_id: i32,
name: String,
url: Option<String>,
body: Option<String>,
+ removed: Option<bool>,
+ reason: Option<String>,
+ locked: Option<bool>,
auth: String
}
title: String,
description: Option<String>,
category_id: i32,
+ removed: Option<bool>,
+ reason: Option<String>,
+ expires: Option<i64>,
auth: String
}
saved_comments: Vec<CommentView>,
}
+#[derive(Serialize, Deserialize)]
+pub struct GetModlog {
+ mod_user_id: Option<i32>,
+ community_id: Option<i32>,
+ limit: Option<i64>,
+ page: Option<i64>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetModlogResponse {
+ op: String,
+ removed_posts: Vec<ModRemovePostView>,
+ locked_posts: Vec<ModLockPostView>,
+ removed_comments: Vec<ModRemoveCommentView>,
+ removed_communities: Vec<ModRemoveCommunityView>,
+ banned_from_community: Vec<ModBanFromCommunityView>,
+ banned: Vec<ModBanView>,
+ added_to_community: Vec<ModAddCommunityView>,
+ added: Vec<ModAddView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct BanFromCommunity {
+ community_id: i32,
+ user_id: i32,
+ ban: bool,
+ reason: Option<String>,
+ expires: Option<i64>,
+ auth: String
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct BanFromCommunityResponse {
+ op: String,
+ user: UserView,
+ banned: bool,
+}
+
+
+#[derive(Serialize, Deserialize)]
+pub struct AddModToCommunity {
+ community_id: i32,
+ user_id: i32,
+ added: bool,
+ auth: String
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct AddModToCommunityResponse {
+ op: String,
+ moderators: Vec<CommunityModeratorView>,
+}
+
/// `ChatServer` manages chat rooms and responsible for coordinating chat
/// session. implementation is super primitive
pub struct ChatServer {
}
}
}
+
+ fn send_community_message(&self, conn: &PgConnection, community_id: i32, message: &str, skip_id: usize) {
+ let posts = PostView::list(conn,
+ PostListingType::Community,
+ &SortType::New,
+ Some(community_id),
+ None,
+ None,
+ 999).unwrap();
+ for post in posts {
+ self.send_room_message(post.id, message, skip_id);
+ }
+ }
}
/// Make actor from `ChatServer`
let op = &json["op"].as_str().unwrap();
let user_operation: UserOperation = UserOperation::from_str(&op).unwrap();
+
+ // TODO figure out how to do proper error handling here, instead of just returning
+ // error strings
let res: String = match user_operation {
UserOperation::Login => {
let login: Login = serde_json::from_str(data).unwrap();
let get_user_details: GetUserDetails = serde_json::from_str(data).unwrap();
get_user_details.perform(self, msg.id)
},
- // _ => {
- // let e = ErrorMessage {
- // op: "Unknown".to_string(),
- // error: "Unknown User Operation".to_string()
- // };
- // serde_json::to_string(&e).unwrap()
- // }
+ UserOperation::GetModlog => {
+ let get_modlog: GetModlog = serde_json::from_str(data).unwrap();
+ get_modlog.perform(self, msg.id)
+ },
+ UserOperation::BanFromCommunity => {
+ let ban_from_community: BanFromCommunity = serde_json::from_str(data).unwrap();
+ ban_from_community.perform(self, msg.id)
+ },
+ UserOperation::AddModToCommunity => {
+ let mod_add_to_community: AddModToCommunity = serde_json::from_str(data).unwrap();
+ mod_add_to_community.perform(self, msg.id)
+ },
};
MessageResult(res)
email: self.email.to_owned(),
password_encrypted: self.password.to_owned(),
preferred_username: None,
- updated: None
+ updated: None,
+ admin: None,
+ banned: None,
};
// Create the user
description: self.description.to_owned(),
category_id: self.category_id,
creator_id: user_id,
- updated: None
+ removed: None,
+ updated: None,
};
let inserted_community = match Community::create(&conn, &community_form) {
let user_id = claims.id;
+ // Check for a ban
+ if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() {
+ return self.error("You have been banned from this community");
+ }
+
let post_form = PostForm {
name: self.name.to_owned(),
url: self.url.to_owned(),
body: self.body.to_owned(),
community_id: self.community_id,
creator_id: user_id,
+ removed: None,
+ locked: None,
updated: None
};
let user_id = claims.id;
+ // Check for a ban
+ let post = Post::read(&conn, self.post_id).unwrap();
+ if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
+ return self.error("You have been banned from this community");
+ }
+
let content_slurs_removed = remove_slurs(&self.content.to_owned());
let comment_form = CommentForm {
parent_id: self.parent_id.to_owned(),
post_id: self.post_id,
creator_id: user_id,
+ removed: None,
updated: None
};
let user_id = claims.id;
- // Verify its the creator
- let orig_comment = Comment::read(&conn, self.edit_id).unwrap();
- if user_id != orig_comment.creator_id {
- return self.error("Incorrect creator.");
+
+ // Verify its the creator or a mod
+ let orig_comment = CommentView::read(&conn, self.edit_id, None).unwrap();
+ let mut editors: Vec<i32> = CommunityModeratorView::for_community(&conn, orig_comment.community_id)
+ .unwrap()
+ .into_iter()
+ .map(|m| m.user_id)
+ .collect();
+ editors.push(self.creator_id);
+ if !editors.contains(&user_id) {
+ return self.error("Not allowed to edit comment.");
+ }
+
+ // Check for a ban
+ if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() {
+ return self.error("You have been banned from this community");
}
let content_slurs_removed = remove_slurs(&self.content.to_owned());
content: content_slurs_removed,
parent_id: self.parent_id,
post_id: self.post_id,
- creator_id: user_id,
+ creator_id: self.creator_id,
+ removed: self.removed.to_owned(),
updated: Some(naive_now())
};
}
};
+ // Mod tables
+ if let Some(removed) = self.removed.to_owned() {
+ let form = ModRemoveCommentForm {
+ mod_user_id: user_id,
+ comment_id: self.edit_id,
+ removed: Some(removed),
+ reason: self.reason.to_owned(),
+ };
+ ModRemoveComment::create(&conn, &form).unwrap();
+ }
+
let comment_view = CommentView::read(&conn, self.edit_id, Some(user_id)).unwrap();
let user_id = claims.id;
+ // Check for a ban
+ let post = Post::read(&conn, self.post_id).unwrap();
+ if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
+ return self.error("You have been banned from this community");
+ }
+
let like_form = CommentLikeForm {
comment_id: self.comment_id,
post_id: self.post_id,
let user_id = claims.id;
+ // Check for a ban
+ let post = Post::read(&conn, self.post_id).unwrap();
+ if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
+ return self.error("You have been banned from this community");
+ }
+
let like_form = PostLikeForm {
post_id: self.post_id,
user_id: user_id,
let user_id = claims.id;
- // Verify its the creator
- let orig_post = Post::read(&conn, self.edit_id).unwrap();
- if user_id != orig_post.creator_id {
- return self.error("Incorrect creator.");
+ // Verify its the creator or a mod
+ let mut editors: Vec<i32> = CommunityModeratorView::for_community(&conn, self.community_id)
+ .unwrap()
+ .into_iter()
+ .map(|m| m.user_id)
+ .collect();
+ editors.push(self.creator_id);
+ if !editors.contains(&user_id) {
+ return self.error("Not allowed to edit comment.");
+ }
+
+ // Check for a ban
+ if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() {
+ return self.error("You have been banned from this community");
}
let post_form = PostForm {
name: self.name.to_owned(),
url: self.url.to_owned(),
body: self.body.to_owned(),
- creator_id: user_id,
+ creator_id: self.creator_id.to_owned(),
community_id: self.community_id,
+ removed: self.removed.to_owned(),
+ locked: self.locked.to_owned(),
updated: Some(naive_now())
};
}
};
+ // Mod tables
+ if let Some(removed) = self.removed.to_owned() {
+ let form = ModRemovePostForm {
+ mod_user_id: user_id,
+ post_id: self.edit_id,
+ removed: Some(removed),
+ reason: self.reason.to_owned(),
+ };
+ ModRemovePost::create(&conn, &form).unwrap();
+ }
+
+ if let Some(locked) = self.locked.to_owned() {
+ let form = ModLockPostForm {
+ mod_user_id: user_id,
+ post_id: self.edit_id,
+ locked: Some(locked),
+ };
+ ModLockPost::create(&conn, &form).unwrap();
+ }
+
let post_view = PostView::read(&conn, self.edit_id, Some(user_id)).unwrap();
let mut post_sent = post_view.clone();
let user_id = claims.id;
-
// Verify its a mod
let moderator_view = CommunityModeratorView::for_community(&conn, self.edit_id).unwrap();
let mod_ids: Vec<i32> = moderator_view.into_iter().map(|m| m.user_id).collect();
description: self.description.to_owned(),
category_id: self.category_id.to_owned(),
creator_id: user_id,
+ removed: self.removed.to_owned(),
updated: Some(naive_now())
};
}
};
- let community_view = CommunityView::read(&conn, self.edit_id, Some(user_id)).unwrap();
+ // Mod tables
+ if let Some(removed) = self.removed.to_owned() {
+ let expires = match self.expires {
+ Some(time) => Some(naive_from_unix(time)),
+ None => None
+ };
+ let form = ModRemoveCommunityForm {
+ mod_user_id: user_id,
+ community_id: self.edit_id,
+ removed: Some(removed),
+ reason: self.reason.to_owned(),
+ expires: expires
+ };
+ ModRemoveCommunity::create(&conn, &form).unwrap();
+ }
- // Do the subscriber stuff here
- // let mut community_sent = post_view.clone();
- // community_sent.my_vote = None;
+ let community_view = CommunityView::read(&conn, self.edit_id, Some(user_id)).unwrap();
let community_out = serde_json::to_string(
&CommunityResponse {
)
.unwrap();
- // let post_sent_out = serde_json::to_string(
- // &PostResponse {
- // op: self.op_type().to_string(),
- // post: post_sent
- // }
- // )
- // .unwrap();
+ let community_view_sent = CommunityView::read(&conn, self.edit_id, None).unwrap();
+
+ let community_sent = serde_json::to_string(
+ &CommunityResponse {
+ op: self.op_type().to_string(),
+ community: community_view_sent
+ }
+ )
+ .unwrap();
- chat.send_room_message(self.edit_id, &community_out, addr);
+ chat.send_community_message(&conn, self.edit_id, &community_sent, addr);
community_out
}
}
}
+impl Perform for GetModlog {
+ fn op_type(&self) -> UserOperation {
+ UserOperation::GetModlog
+ }
+
+ fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String {
+
+ let conn = establish_connection();
+
+ let removed_posts = ModRemovePostView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
+ let locked_posts = ModLockPostView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
+ let removed_comments = ModRemoveCommentView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
+ let removed_communities = ModRemoveCommunityView::list(&conn, self.mod_user_id, self.limit, self.page).unwrap();
+ let banned_from_community = ModBanFromCommunityView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
+ let banned = ModBanView::list(&conn, self.mod_user_id, self.limit, self.page).unwrap();
+ let added_to_community = ModAddCommunityView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
+ let added = ModAddView::list(&conn, self.mod_user_id, self.limit, self.page).unwrap();
+
+ // Return the jwt
+ serde_json::to_string(
+ &GetModlogResponse {
+ op: self.op_type().to_string(),
+ removed_posts: removed_posts,
+ locked_posts: locked_posts,
+ removed_comments: removed_comments,
+ removed_communities: removed_communities,
+ banned_from_community: banned_from_community,
+ banned: banned,
+ added_to_community: added_to_community,
+ added: added,
+ }
+ )
+ .unwrap()
+ }
+}
+
+impl Perform for BanFromCommunity {
+ fn op_type(&self) -> UserOperation {
+ UserOperation::BanFromCommunity
+ }
+
+ fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+
+ let conn = establish_connection();
+
+ let claims = match Claims::decode(&self.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => {
+ return self.error("Not logged in.");
+ }
+ };
+
+ let user_id = claims.id;
+
+ let community_user_ban_form = CommunityUserBanForm {
+ community_id: self.community_id,
+ user_id: self.user_id,
+ };
+
+ if self.ban {
+ match CommunityUserBan::ban(&conn, &community_user_ban_form) {
+ Ok(user) => user,
+ Err(_e) => {
+ return self.error("Community user ban already exists");
+ }
+ };
+ } else {
+ match CommunityUserBan::unban(&conn, &community_user_ban_form) {
+ Ok(user) => user,
+ Err(_e) => {
+ return self.error("Community user ban already exists");
+ }
+ };
+ }
+
+ // Mod tables
+ let expires = match self.expires {
+ Some(time) => Some(naive_from_unix(time)),
+ None => None
+ };
+
+ let form = ModBanFromCommunityForm {
+ mod_user_id: user_id,
+ other_user_id: self.user_id,
+ community_id: self.community_id,
+ reason: self.reason.to_owned(),
+ banned: Some(self.ban),
+ expires: expires,
+ };
+ ModBanFromCommunity::create(&conn, &form).unwrap();
+
+ let user_view = UserView::read(&conn, self.user_id).unwrap();
+
+ let res = serde_json::to_string(
+ &BanFromCommunityResponse {
+ op: self.op_type().to_string(),
+ user: user_view,
+ banned: self.ban
+ }
+ )
+ .unwrap();
+
+
+ chat.send_community_message(&conn, self.community_id, &res, addr);
+
+ res
+ }
+}
+
+impl Perform for AddModToCommunity {
+ fn op_type(&self) -> UserOperation {
+ UserOperation::AddModToCommunity
+ }
+
+ fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
+
+ let conn = establish_connection();
+
+ let claims = match Claims::decode(&self.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => {
+ return self.error("Not logged in.");
+ }
+ };
+
+ let user_id = claims.id;
+
+ let community_moderator_form = CommunityModeratorForm {
+ community_id: self.community_id,
+ user_id: self.user_id
+ };
+
+ if self.added {
+ match CommunityModerator::join(&conn, &community_moderator_form) {
+ Ok(user) => user,
+ Err(_e) => {
+ return self.error("Community moderator already exists.");
+ }
+ };
+ } else {
+ match CommunityModerator::leave(&conn, &community_moderator_form) {
+ Ok(user) => user,
+ Err(_e) => {
+ return self.error("Community moderator already exists.");
+ }
+ };
+ }
+
+ // Mod tables
+ let form = ModAddCommunityForm {
+ mod_user_id: user_id,
+ other_user_id: self.user_id,
+ community_id: self.community_id,
+ removed: Some(!self.added),
+ };
+ ModAddCommunity::create(&conn, &form).unwrap();
+
+ let moderators = CommunityModeratorView::for_community(&conn, self.community_id).unwrap();
+
+ let res = serde_json::to_string(
+ &AddModToCommunityResponse {
+ op: self.op_type().to_string(),
+ moderators: moderators,
+ }
+ )
+ .unwrap();
+
+
+ chat.send_community_message(&conn, self.community_id, &res, addr);
+
+ res
+
+ }
+}
+
import { Component, linkEvent } from 'inferno';
import { CommentNode as CommentNodeI, CommentForm as CommentFormI } from '../interfaces';
-import { WebSocketService } from '../services';
+import { WebSocketService, UserService } from '../services';
import * as autosize from 'autosize';
interface CommentFormProps {
node?: CommentNodeI;
onReplyCancel?(): any;
edit?: boolean;
+ disabled?: boolean;
}
interface CommentFormState {
commentForm: {
auth: null,
content: null,
- post_id: this.props.node ? this.props.node.comment.post_id : this.props.postId
+ post_id: this.props.node ? this.props.node.comment.post_id : this.props.postId,
+ creator_id: UserService.Instance.loggedIn ? UserService.Instance.user.id : null,
},
- buttonTitle: !this.props.node ? "Post" : this.props.edit ? "Edit" : "Reply"
+ buttonTitle: !this.props.node ? "Post" : this.props.edit ? "Edit" : "Reply",
}
constructor(props: any, context: any) {
this.state.commentForm.edit_id = this.props.node.comment.id;
this.state.commentForm.parent_id = this.props.node.comment.parent_id;
this.state.commentForm.content = this.props.node.comment.content;
+ this.state.commentForm.creator_id = this.props.node.comment.creator_id;
} else {
// A reply gets a new parent id
this.state.commentForm.parent_id = this.props.node.comment.id;
<form onSubmit={linkEvent(this, this.handleCommentSubmit)}>
<div class="form-group row">
<div class="col-sm-12">
- <textarea class="form-control" value={this.state.commentForm.content} onInput={linkEvent(this, this.handleCommentContentChange)} placeholder="Comment here" required />
+ <textarea class="form-control" value={this.state.commentForm.content} onInput={linkEvent(this, this.handleCommentContentChange)} placeholder="Comment here" required disabled={this.props.disabled}/>
</div>
</div>
<div class="row">
<div class="col-sm-12">
- <button type="submit" class="btn btn-sm btn-secondary mr-2">{this.state.buttonTitle}</button>
+ <button type="submit" class="btn btn-sm btn-secondary mr-2" disabled={this.props.disabled}>{this.state.buttonTitle}</button>
{this.props.node && <button type="button" class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.handleReplyCancel)}>Cancel</button>}
</div>
</div>
import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
-import { CommentNode as CommentNodeI, CommentLikeForm, CommentForm as CommentFormI } from '../interfaces';
+import { CommentNode as CommentNodeI, CommentLikeForm, CommentForm as CommentFormI, BanFromCommunityForm, CommunityUser, AddModToCommunityForm } from '../interfaces';
import { WebSocketService, UserService } from '../services';
-import { mdToHtml } from '../utils';
+import { mdToHtml, getUnixTime } from '../utils';
import { MomentTime } from './moment-time';
import { CommentForm } from './comment-form';
import { CommentNodes } from './comment-nodes';
interface CommentNodeState {
showReply: boolean;
showEdit: boolean;
+ showRemoveDialog: boolean;
+ removeReason: string;
+ showBanDialog: boolean;
+ banReason: string;
+ banExpires: string;
}
interface CommentNodeProps {
node: CommentNodeI;
noIndent?: boolean;
viewOnly?: boolean;
+ locked?: boolean;
+ moderators: Array<CommunityUser>;
}
export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
private emptyState: CommentNodeState = {
showReply: false,
- showEdit: false
+ showEdit: false,
+ showRemoveDialog: false,
+ removeReason: null,
+ showBanDialog: false,
+ banReason: null,
+ banExpires: null,
}
constructor(props: any, context: any) {
<span><MomentTime data={node.comment} /></span>
</li>
</ul>
- {this.state.showEdit && <CommentForm node={node} edit onReplyCancel={this.handleReplyCancel} />}
+ {this.state.showEdit && <CommentForm node={node} edit onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} />}
{!this.state.showEdit &&
<div>
- <div className="md-div" dangerouslySetInnerHTML={mdToHtml(node.comment.content)} />
+ <div className="md-div" dangerouslySetInnerHTML={mdToHtml(node.comment.removed ? '*removed*' : node.comment.content)} />
<ul class="list-inline mb-1 text-muted small font-weight-bold">
{!this.props.viewOnly &&
<span class="mr-2">
<span class="pointer" onClick={linkEvent(this, this.handleReplyClick)}>reply</span>
</li>
{this.myComment &&
+ <>
<li className="list-inline-item">
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
</li>
- }
- {this.myComment &&
<li className="list-inline-item">
<span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
</li>
+ </>
+ }
+ {this.canMod &&
+ <>
+ <li className="list-inline-item">
+ {!this.props.node.comment.removed ?
+ <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
+ <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
+ }
+ </li>
+ {!this.isMod &&
+ <>
+ <li className="list-inline-item">
+ {!this.props.node.comment.banned ?
+ <span class="pointer" onClick={linkEvent(this, this.handleModBanShow)}>ban</span> :
+ <span class="pointer" onClick={linkEvent(this, this.handleModBanSubmit)}>unban</span>
+ }
+ </li>
+ </>
+ }
+ {!this.props.node.comment.banned &&
+ <li className="list-inline-item">
+ <span class="pointer" onClick={linkEvent(this, this.handleAddModToCommunity)}>{`${this.isMod ? 'remove' : 'appoint'} as mod`}</span>
+ </li>
+ }
+ </>
}
</span>
}
</div>
}
</div>
- {this.state.showReply && <CommentForm node={node} onReplyCancel={this.handleReplyCancel} />}
- {this.props.node.children && <CommentNodes nodes={this.props.node.children} />}
+ {this.state.showRemoveDialog &&
+ <form class="form-inline" onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
+ <input type="text" class="form-control mr-2" placeholder="Reason" value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
+ <button type="submit" class="btn btn-secondary">Remove Comment</button>
+ </form>
+ }
+ {this.state.showBanDialog &&
+ <form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
+ <div class="form-group row">
+ <label class="col-form-label">Reason</label>
+ <input type="text" class="form-control mr-2" placeholder="Optional" value={this.state.banReason} onInput={linkEvent(this, this.handleModBanReasonChange)} />
+ </div>
+ <div class="form-group row">
+ <label class="col-form-label">Expires</label>
+ <input type="date" class="form-control mr-2" placeholder="Expires" value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} />
+ </div>
+ <div class="form-group row">
+ <button type="submit" class="btn btn-secondary">Ban {this.props.node.comment.creator_name}</button>
+ </div>
+ </form>
+ }
+ {this.state.showReply && <CommentForm node={node} onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} />}
+ {this.props.node.children && <CommentNodes nodes={this.props.node.children} locked={this.props.locked} moderators={this.props.moderators}/>}
</div>
)
}
- private get myComment(): boolean {
+ get myComment(): boolean {
return UserService.Instance.loggedIn && this.props.node.comment.creator_id == UserService.Instance.user.id;
}
+ get canMod(): boolean {
+
+ // You can do moderator actions only on the mods added after you.
+ if (UserService.Instance.loggedIn) {
+ let modIds = this.props.moderators.map(m => m.user_id);
+ let yourIndex = modIds.findIndex(id => id == UserService.Instance.user.id);
+ if (yourIndex == -1) {
+ return false;
+ } else {
+ console.log(modIds);
+ modIds = modIds.slice(0, yourIndex+1); // +1 cause you cant mod yourself
+ console.log(modIds);
+ return !modIds.includes(this.props.node.comment.creator_id);
+ }
+ } else {
+ return false;
+ }
+
+ }
+
+ get isMod(): boolean {
+ return this.props.moderators.map(m => m.user_id).includes(this.props.node.comment.creator_id);
+ }
+
handleReplyClick(i: CommentNode) {
i.state.showReply = true;
i.setState(i.state);
let deleteForm: CommentFormI = {
content: "*deleted*",
edit_id: i.props.node.comment.id,
+ creator_id: i.props.node.comment.creator_id,
post_id: i.props.node.comment.post_id,
parent_id: i.props.node.comment.parent_id,
auth: null
};
WebSocketService.Instance.likeComment(form);
}
+
+ handleModRemoveShow(i: CommentNode) {
+ i.state.showRemoveDialog = true;
+ i.setState(i.state);
+ }
+
+ handleModRemoveReasonChange(i: CommentNode, event: any) {
+ i.state.removeReason = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleModRemoveSubmit(i: CommentNode) {
+ let form: CommentFormI = {
+ content: i.props.node.comment.content,
+ edit_id: i.props.node.comment.id,
+ creator_id: i.props.node.comment.creator_id,
+ post_id: i.props.node.comment.post_id,
+ parent_id: i.props.node.comment.parent_id,
+ removed: !i.props.node.comment.removed,
+ reason: i.state.removeReason,
+ auth: null
+ };
+ WebSocketService.Instance.editComment(form);
+
+ i.state.showRemoveDialog = false;
+ i.setState(i.state);
+ }
+
+ handleModBanShow(i: CommentNode) {
+ i.state.showBanDialog = true;
+ i.setState(i.state);
+ }
+
+ handleModBanReasonChange(i: CommentNode, event: any) {
+ i.state.banReason = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleModBanExpiresChange(i: CommentNode, event: any) {
+ i.state.banExpires = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleModBanSubmit(i: CommentNode) {
+ let form: BanFromCommunityForm = {
+ user_id: i.props.node.comment.creator_id,
+ community_id: i.props.node.comment.community_id,
+ ban: !i.props.node.comment.banned,
+ reason: i.state.banReason,
+ expires: getUnixTime(i.state.banExpires),
+ };
+ WebSocketService.Instance.banFromCommunity(form);
+
+ i.state.showBanDialog = false;
+ i.setState(i.state);
+ }
+
+ handleAddModToCommunity(i: CommentNode) {
+ let form: AddModToCommunityForm = {
+ user_id: i.props.node.comment.creator_id,
+ community_id: i.props.node.comment.community_id,
+ added: !i.isMod,
+ };
+ WebSocketService.Instance.addModToCommunity(form);
+ i.setState(i.state);
+ }
}
import { Component } from 'inferno';
-import { CommentNode as CommentNodeI } from '../interfaces';
+import { CommentNode as CommentNodeI, CommunityUser } from '../interfaces';
import { CommentNode } from './comment-node';
interface CommentNodesState {
interface CommentNodesProps {
nodes: Array<CommentNodeI>;
+ moderators: Array<CommunityUser>;
noIndent?: boolean;
viewOnly?: boolean;
+ locked?: boolean;
}
export class CommentNodes extends Component<CommentNodesProps, CommentNodesState> {
return (
<div className="comments">
{this.props.nodes.map(node =>
- <CommentNode node={node} noIndent={this.props.noIndent} viewOnly={this.props.viewOnly}/>
+ <CommentNode node={node}
+ noIndent={this.props.noIndent}
+ viewOnly={this.props.viewOnly}
+ locked={this.props.locked}
+ moderators={this.props.moderators}/>
)}
</div>
)
}
+
}
WebSocketService.Instance.followCommunity(form);
}
-
handleSubscribe(communityId: number) {
let form: FollowCommunityForm = {
community_id: communityId,
found.subscribed = res.community.subscribed;
found.number_of_subscribers = res.community.number_of_subscribers;
this.setState(this.state);
- }
+ }
}
}
<h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
<div class="row">
<div class="col-12 col-md-9">
- <h4>{this.state.community.title}</h4>
+ <h4>{this.state.community.title}
+ {this.state.community.removed &&
+ <small className="ml-2 text-muted font-italic">removed</small>
+ }
+ </h4>
<PostListings communityId={this.state.communityId} />
</div>
<div class="col-12 col-md-3">
--- /dev/null
+import { Component } from 'inferno';
+import { Link } from 'inferno-router';
+import { Subscription } from "rxjs";
+import { retryWhen, delay, take } from 'rxjs/operators';
+import { UserOperation, GetModlogForm, GetModlogResponse, ModRemovePost, ModLockPost, ModRemoveComment, ModRemoveCommunity, ModBanFromCommunity, ModBan, ModAddCommunity, ModAdd } from '../interfaces';
+import { WebSocketService } from '../services';
+import { msgOp, addTypeInfo } from '../utils';
+import { MomentTime } from './moment-time';
+import * as moment from 'moment';
+
+interface ModlogState {
+ removed_posts: Array<ModRemovePost>,
+ locked_posts: Array<ModLockPost>,
+ removed_comments: Array<ModRemoveComment>,
+ removed_communities: Array<ModRemoveCommunity>,
+ banned_from_community: Array<ModBanFromCommunity>,
+ banned: Array<ModBan>,
+ added_to_community: Array<ModAddCommunity>,
+ added: Array<ModAdd>,
+ loading: boolean;
+}
+
+export class Modlog extends Component<any, ModlogState> {
+ private subscription: Subscription;
+ private emptyState: ModlogState = {
+ removed_posts: [],
+ locked_posts: [],
+ removed_comments: [],
+ removed_communities: [],
+ banned_from_community: [],
+ banned: [],
+ added_to_community: [],
+ added: [],
+ loading: true
+ }
+
+ constructor(props: any, context: any) {
+ super(props, context);
+ this.state = this.emptyState;
+ this.subscription = WebSocketService.Instance.subject
+ .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .subscribe(
+ (msg) => this.parseMessage(msg),
+ (err) => console.error(err),
+ () => console.log('complete')
+ );
+
+ let modlogForm: GetModlogForm = {
+
+ };
+ WebSocketService.Instance.getModlog(modlogForm);
+ }
+
+ componentWillUnmount() {
+ this.subscription.unsubscribe();
+ }
+
+ combined() {
+ let combined: Array<{type_: string, data: ModRemovePost | ModLockPost | ModRemoveCommunity}> = [];
+ let removed_posts = addTypeInfo(this.state.removed_posts, "removed_posts");
+ let locked_posts = addTypeInfo(this.state.locked_posts, "locked_posts");
+ let removed_comments = addTypeInfo(this.state.removed_comments, "removed_comments");
+ let removed_communities = addTypeInfo(this.state.removed_communities, "removed_communities");
+ let banned_from_community = addTypeInfo(this.state.banned_from_community, "banned_from_community");
+ let added_to_community = addTypeInfo(this.state.added_to_community, "added_to_community");
+
+ combined.push(...removed_posts);
+ combined.push(...locked_posts);
+ combined.push(...removed_comments);
+ combined.push(...removed_communities);
+ combined.push(...banned_from_community);
+ combined.push(...added_to_community);
+
+ // Sort them by time
+ combined.sort((a, b) => b.data.when_.localeCompare(a.data.when_));
+
+ console.log(combined);
+
+ return (
+ <tbody>
+ {combined.map(i =>
+ <tr>
+ <td><MomentTime data={i.data} /></td>
+ <td><Link to={`/user/${i.data.mod_user_id}`}>{i.data.mod_user_name}</Link></td>
+ <td>
+ {i.type_ == 'removed_posts' &&
+ <>
+ {(i.data as ModRemovePost).removed? 'Removed' : 'Restored'}
+ <span> Post <Link to={`/post/${(i.data as ModRemovePost).post_id}`}>{(i.data as ModRemovePost).post_name}</Link></span>
+ <div>{(i.data as ModRemovePost).reason && ` reason: ${(i.data as ModRemovePost).reason}`}</div>
+ </>
+ }
+ {i.type_ == 'locked_posts' &&
+ <>
+ {(i.data as ModLockPost).locked? 'Locked' : 'Unlocked'}
+ <span> Post <Link to={`/post/${(i.data as ModLockPost).post_id}`}>{(i.data as ModLockPost).post_name}</Link></span>
+ </>
+ }
+ {i.type_ == 'removed_comments' &&
+ <>
+ {(i.data as ModRemoveComment).removed? 'Removed' : 'Restored'}
+ <span> Comment <Link to={`/post/${(i.data as ModRemoveComment).post_id}/comment/${(i.data as ModRemoveComment).comment_id}`}>{(i.data as ModRemoveComment).comment_content}</Link></span>
+ <div>{(i.data as ModRemoveComment).reason && ` reason: ${(i.data as ModRemoveComment).reason}`}</div>
+ </>
+ }
+ {i.type_ == 'removed_communities' &&
+ <>
+ {(i.data as ModRemoveCommunity).removed ? 'Removed' : 'Restored'}
+ <span> Community <Link to={`/community/${i.data.community_id}`}>{i.data.community_name}</Link></span>
+ <div>{(i.data as ModRemoveCommunity).reason && ` reason: ${(i.data as ModRemoveCommunity).reason}`}</div>
+ <div>{(i.data as ModRemoveCommunity).expires && ` expires: ${moment.utc((i.data as ModRemoveCommunity).expires).fromNow()}`}</div>
+ </>
+ }
+ {i.type_ == 'banned_from_community' &&
+ <>
+ <span>{(i.data as ModBanFromCommunity).banned ? 'Banned ' : 'Unbanned '} </span>
+ <span><Link to={`/user/${(i.data as ModBanFromCommunity).other_user_id}`}>{(i.data as ModBanFromCommunity).other_user_name}</Link></span>
+ <div>{(i.data as ModBanFromCommunity).reason && ` reason: ${(i.data as ModBanFromCommunity).reason}`}</div>
+ <div>{(i.data as ModBanFromCommunity).expires && ` expires: ${moment.utc((i.data as ModBanFromCommunity).expires).fromNow()}`}</div>
+ </>
+ }
+ {i.type_ == 'added_to_community' &&
+ <>
+ <span>{(i.data as ModAddCommunity).removed ? 'Removed ' : 'Appointed '} </span>
+ <span><Link to={`/user/${(i.data as ModAddCommunity).other_user_id}`}>{(i.data as ModAddCommunity).other_user_name}</Link></span>
+ <span> as a mod to the community </span>
+ <span><Link to={`/community/${i.data.community_id}`}>{i.data.community_name}</Link></span>
+ </>
+ }
+ </td>
+ </tr>
+ )
+ }
+
+ </tbody>
+ );
+
+ }
+
+ render() {
+ return (
+ <div class="container">
+ {this.state.loading ?
+ <h4 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
+ <div>
+ <h4>Modlog</h4>
+ <div class="table-responsive">
+ <table id="modlog_table" class="table table-sm table-hover">
+ <thead class="pointer">
+ <tr>
+ <th>Time</th>
+ <th>Mod</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ {this.combined()}
+ </table>
+ </div>
+ </div>
+ }
+ </div>
+ );
+ }
+
+ parseMessage(msg: any) {
+ console.log(msg);
+ let op: UserOperation = msgOp(msg);
+ if (msg.error) {
+ alert(msg.error);
+ return;
+ } else if (op == UserOperation.GetModlog) {
+ let res: GetModlogResponse = msg;
+ this.state.loading = false;
+ this.state.removed_posts = res.removed_posts;
+ this.state.locked_posts = res.locked_posts;
+ this.state.removed_comments = res.removed_comments;
+ this.state.removed_communities = res.removed_communities;
+ this.state.banned_from_community = res.banned_from_community;
+ this.state.added_to_community = res.added_to_community;
+
+ this.setState(this.state);
+ }
+ }
+}
interface MomentTimeProps {
data: {
- published: string;
+ published?: string;
+ when_?: string;
updated?: string;
}
}
<span title={this.props.data.updated} className="font-italics">modified {moment.utc(this.props.data.updated).fromNow()}</span>
)
} else {
+ let str = this.props.data.published || this.props.data.when_;
return (
- <span title={this.props.data.published}>{moment.utc(this.props.data.published).fromNow()}</span>
+ <span title={str}>{moment.utc(str).fromNow()}</span>
)
}
}
<li class="nav-item">
<Link class="nav-link" to="/communities">Forums</Link>
</li>
+ <li class="nav-item">
+ <Link class="nav-link" to="/modlog">Modlog</Link>
+ </li>
<li class="nav-item">
<Link class="nav-link" to="/create_post">Create Post</Link>
</li>
import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
import { PostForm as PostFormI, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse, ListCommunitiesForm, SortType } from '../interfaces';
-import { WebSocketService } from '../services';
+import { WebSocketService, UserService } from '../services';
import { msgOp } from '../utils';
import * as autosize from 'autosize';
postForm: {
name: null,
auth: null,
- community_id: null
+ community_id: null,
+ creator_id: UserService.Instance.loggedIn ? UserService.Instance.user.id : null
},
communities: [],
loading: false
name: this.props.post.name,
community_id: this.props.post.community_id,
edit_id: this.props.post.id,
+ creator_id: this.props.post.creator_id,
url: this.props.post.url,
auth: null
}
interface PostListingState {
showEdit: boolean;
+ showRemoveDialog: boolean;
+ removeReason: string;
iframeExpanded: boolean;
}
private emptyState: PostListingState = {
showEdit: false,
+ showRemoveDialog: false,
+ removeReason: null,
iframeExpanded: false
}
<div className="ml-4">
{post.url
? <div className="mb-0">
- <h4 className="d-inline"><a className="text-white" href={post.url}>{post.name}</a></h4>
- <small><a className="ml-2 text-muted font-italic" href={post.url}>{(new URL(post.url)).hostname}</a></small>
- { !this.state.iframeExpanded
- ? <span class="pointer ml-2 text-muted small" title="Expand here" onClick={linkEvent(this, this.handleIframeExpandClick)}>+</span>
- :
- <span>
- <span class="pointer ml-2 text-muted small" onClick={linkEvent(this, this.handleIframeExpandClick)}>-</span>
- <div class="embed-responsive embed-responsive-1by1">
- <iframe scrolling="yes" class="embed-responsive-item" src={post.url}></iframe>
- </div>
- </span>
+ <h4 className="d-inline"><a className="text-white" href={post.url}>{post.name}</a>
+ {post.removed &&
+ <small className="ml-2 text-muted font-italic">removed</small>
+ }
+ {post.locked &&
+ <small className="ml-2 text-muted font-italic">locked</small>
}
- </div>
- : <h4 className="mb-0"><Link className="text-white" to={`/post/${post.id}`}>{post.name}</Link></h4>
+ </h4>
+ <small><a className="ml-2 text-muted font-italic" href={post.url}>{(new URL(post.url)).hostname}</a></small>
+ { !this.state.iframeExpanded
+ ? <span class="pointer ml-2 text-muted small" title="Expand here" onClick={linkEvent(this, this.handleIframeExpandClick)}>+</span>
+ :
+ <span>
+ <span class="pointer ml-2 text-muted small" onClick={linkEvent(this, this.handleIframeExpandClick)}>-</span>
+ <div class="embed-responsive embed-responsive-1by1">
+ <iframe scrolling="yes" class="embed-responsive-item" src={post.url}></iframe>
+ </div>
+ </span>
+ }
+ </div>
+ : <h4 className="mb-0"><Link className="text-white" to={`/post/${post.id}`}>{post.name}</Link>
+ {post.removed &&
+ <small className="ml-2 text-muted font-italic">removed</small>
+ }
+ {post.locked &&
+ <small className="ml-2 text-muted font-italic">locked</small>
+ }
+ </h4>
}
</div>
<div className="details ml-4 mb-1">
<Link className="text-muted" to={`/post/${post.id}`}>{post.number_of_comments} Comments</Link>
</li>
</ul>
- {this.myPost &&
+ {this.props.editable &&
<ul class="list-inline mb-1 text-muted small font-weight-bold">
- <li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
- </li>
- <li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
- </li>
+ {this.myPost &&
+ <span>
+ <li className="list-inline-item">
+ <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
+ </li>
+ <li className="list-inline-item mr-2">
+ <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span>
+ </li>
+ </span>
+ }
+ {this.props.post.am_mod &&
+ <span>
+ <li className="list-inline-item">
+ {!this.props.post.removed ?
+ <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
+ <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
+ }
+ </li>
+ <li className="list-inline-item">
+ <span class="pointer" onClick={linkEvent(this, this.handleModLock)}>{this.props.post.locked ? 'unlock' : 'lock'}</span>
+ </li>
+ </span>
+ }
</ul>
}
+ {this.state.showRemoveDialog &&
+ <form class="form-inline" onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
+ <input type="text" class="form-control mr-2" placeholder="Reason" value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
+ <button type="submit" class="btn btn-secondary">Remove Post</button>
+ </form>
+ }
{this.props.showBody && this.props.post.body && <div className="md-div" dangerouslySetInnerHTML={mdToHtml(post.body)} />}
</div>
</div>
}
private get myPost(): boolean {
- return this.props.editable && UserService.Instance.loggedIn && this.props.post.creator_id == UserService.Instance.user.id;
+ return UserService.Instance.loggedIn && this.props.post.creator_id == UserService.Instance.user.id;
}
handlePostLike(i: PostListing) {
name: "deleted",
url: '',
edit_id: i.props.post.id,
+ creator_id: i.props.post.creator_id,
auth: null
};
WebSocketService.Instance.editPost(deleteForm);
}
+ handleModRemoveShow(i: PostListing) {
+ i.state.showRemoveDialog = true;
+ i.setState(i.state);
+ }
+
+ handleModRemoveReasonChange(i: PostListing, event: any) {
+ i.state.removeReason = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleModRemoveSubmit(i: PostListing) {
+ let form: PostFormI = {
+ name: i.props.post.name,
+ community_id: i.props.post.community_id,
+ edit_id: i.props.post.id,
+ creator_id: i.props.post.creator_id,
+ removed: !i.props.post.removed,
+ reason: i.state.removeReason,
+ auth: null,
+ };
+ WebSocketService.Instance.editPost(form);
+
+ i.state.showRemoveDialog = false;
+ i.setState(i.state);
+ }
+
+ handleModLock(i: PostListing) {
+ let form: PostFormI = {
+ name: i.props.post.name,
+ community_id: i.props.post.community_id,
+ edit_id: i.props.post.id,
+ creator_id: i.props.post.creator_id,
+ locked: !i.props.post.locked,
+ auth: null,
+ };
+ WebSocketService.Instance.editPost(form);
+ }
+
handleIframeExpandClick(i: PostListing) {
i.state.iframeExpanded = !i.state.iframeExpanded;
i.setState(i.state);
import { Component, linkEvent } from 'inferno';
import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentResponse, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse, CommentNode as CommentNodeI } from '../interfaces';
+import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentResponse, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse, CommentNode as CommentNodeI, BanFromCommunityResponse, AddModToCommunityResponse } from '../interfaces';
import { WebSocketService } from '../services';
import { msgOp, hotRank } from '../utils';
import { PostListing } from './post-listing';
<div class="col-12 col-sm-8 col-lg-7 mb-3">
<PostListing post={this.state.post} showBody showCommunity editable />
<div className="mb-2" />
- <CommentForm postId={this.state.post.id} />
+ <CommentForm postId={this.state.post.id} disabled={this.state.post.locked} />
{this.sortRadios()}
{this.commentsTree()}
</div>
<div class="sticky-top">
<h4>New Comments</h4>
{this.state.comments.map(comment =>
- <CommentNodes nodes={[{comment: comment}]} noIndent />
+ <CommentNodes nodes={[{comment: comment}]} noIndent locked={this.state.post.locked} moderators={this.state.moderators} />
)}
</div>
)
let nodes = this.buildCommentsTree();
return (
<div className="">
- <CommentNodes nodes={nodes} />
+ <CommentNodes nodes={nodes} locked={this.state.post.locked} moderators={this.state.moderators} />
</div>
);
}
let found = this.state.comments.find(c => c.id == res.comment.id);
found.content = res.comment.content;
found.updated = res.comment.updated;
+ found.removed = res.comment.removed;
+ found.upvotes = res.comment.upvotes;
+ found.downvotes = res.comment.downvotes;
+ found.score = res.comment.score;
+
this.setState(this.state);
}
else if (op == UserOperation.CreateCommentLike) {
this.state.community.subscribed = res.community.subscribed;
this.state.community.number_of_subscribers = res.community.number_of_subscribers;
this.setState(this.state);
+ } else if (op == UserOperation.BanFromCommunity) {
+ let res: BanFromCommunityResponse = msg;
+ this.state.comments.filter(c => c.creator_id == res.user.id)
+ .forEach(c => c.banned = res.banned);
+ this.setState(this.state);
+ } else if (op == UserOperation.AddModToCommunity) {
+ let res: AddModToCommunityResponse = msg;
+ this.state.moderators = res.moderators;
+ this.setState(this.state);
}
}
import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
-import { Community, CommunityUser, FollowCommunityForm } from '../interfaces';
+import { Community, CommunityUser, FollowCommunityForm, CommunityForm as CommunityFormI } from '../interfaces';
import { WebSocketService, UserService } from '../services';
-import { mdToHtml } from '../utils';
+import { mdToHtml, getUnixTime } from '../utils';
import { CommunityForm } from './community-form';
interface SidebarProps {
interface SidebarState {
showEdit: boolean;
+ showRemoveDialog: boolean;
+ removeReason: string;
+ removeExpires: string;
}
export class Sidebar extends Component<SidebarProps, SidebarState> {
private emptyState: SidebarState = {
- showEdit: false
+ showEdit: false,
+ showRemoveDialog: false,
+ removeReason: null,
+ removeExpires: null
}
constructor(props: any, context: any) {
let community = this.props.community;
return (
<div>
- <h4 className="mb-0">{community.title}</h4>
- <Link className="text-muted" to={`/community/${community.id}`}>/f/{community.name}</Link>
- {this.amMod &&
- <ul class="list-inline mb-1 text-muted small font-weight-bold">
- <li className="list-inline-item">
- <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
- </li>
- {this.amCreator &&
- <li className="list-inline-item">
- {/* <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span> */}
- </li>
- }
- </ul>
+ <h4 className="mb-0">{community.title}
+ {community.removed &&
+ <small className="ml-2 text-muted font-italic">removed</small>
+ }
+ </h4>
+ <Link className="text-muted" to={`/community/${community.id}`}>/f/{community.name}</Link>
+ {community.am_mod &&
+ <ul class="list-inline mb-1 text-muted small font-weight-bold">
+ <li className="list-inline-item">
+ <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
+ </li>
+ {this.amCreator &&
+ <li className="list-inline-item">
+ {/* <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span> */}
+ </li>
}
- <ul class="mt-1 list-inline">
- <li className="list-inline-item"><Link className="badge badge-light" to="/communities">{community.category_name}</Link></li>
- <li className="list-inline-item badge badge-light">{community.number_of_subscribers} Subscribers</li>
- <li className="list-inline-item badge badge-light">{community.number_of_posts} Posts</li>
- <li className="list-inline-item badge badge-light">{community.number_of_comments} Comments</li>
+ <li className="list-inline-item">
+ {!this.props.community.removed ?
+ <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
+ <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
+ }
+ </li>
</ul>
- <div>
- {community.subscribed
- ? <button class="btn btn-sm btn-secondary" onClick={linkEvent(community.id, this.handleUnsubscribe)}>Unsubscribe</button>
- : <button class="btn btn-sm btn-secondary" onClick={linkEvent(community.id, this.handleSubscribe)}>Subscribe</button>
- }
- </div>
- {community.description &&
- <div>
- <hr />
- <div className="md-div" dangerouslySetInnerHTML={mdToHtml(community.description)} />
+ }
+ {this.state.showRemoveDialog &&
+ <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
+ <div class="form-group row">
+ <label class="col-form-label">Reason</label>
+ <input type="text" class="form-control mr-2" placeholder="Optional" value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
</div>
+ <div class="form-group row">
+ <label class="col-form-label">Expires</label>
+ <input type="date" class="form-control mr-2" placeholder="Expires" value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} />
+ </div>
+ <div class="form-group row">
+ <button type="submit" class="btn btn-secondary">Remove Community</button>
+ </div>
+ </form>
+ }
+ <ul class="mt-1 list-inline">
+ <li className="list-inline-item"><Link className="badge badge-light" to="/communities">{community.category_name}</Link></li>
+ <li className="list-inline-item badge badge-light">{community.number_of_subscribers} Subscribers</li>
+ <li className="list-inline-item badge badge-light">{community.number_of_posts} Posts</li>
+ <li className="list-inline-item badge badge-light">{community.number_of_comments} Comments</li>
+ </ul>
+ <div>
+ {community.subscribed
+ ? <button class="btn btn-sm btn-secondary" onClick={linkEvent(community.id, this.handleUnsubscribe)}>Unsubscribe</button>
+ : <button class="btn btn-sm btn-secondary" onClick={linkEvent(community.id, this.handleSubscribe)}>Subscribe</button>
}
- <hr />
- <h4>Moderators</h4>
+ </div>
+ {community.description &&
+ <div>
+ <hr />
+ <div className="md-div" dangerouslySetInnerHTML={mdToHtml(community.description)} />
+ </div>
+ }
+ <hr />
+ <h4>Moderators</h4>
+ <ul class="list-inline">
{this.props.moderators.map(mod =>
- <Link to={`/user/${mod.user_id}`}>{mod.user_name}</Link>
+ <li class="list-inline-item"><Link to={`/user/${mod.user_id}`}>{mod.user_name}</Link></li>
)}
- </div>
+ </ul>
+ </div>
);
}
return UserService.Instance.loggedIn && this.props.community.creator_id == UserService.Instance.user.id;
}
- private get amMod(): boolean {
- console.log(this.props.moderators);
- console.log(this.props);
- return UserService.Instance.loggedIn &&
- this.props.moderators.map(m => m.user_id).includes(UserService.Instance.user.id);
+ // private get amMod(): boolean {
+ // return UserService.Instance.loggedIn &&
+ // this.props.moderators.map(m => m.user_id).includes(UserService.Instance.user.id);
+ // }
+
+ handleDeleteClick() {
+ }
+
+ handleModRemoveShow(i: Sidebar) {
+ i.state.showRemoveDialog = true;
+ i.setState(i.state);
}
+
+ handleModRemoveReasonChange(i: Sidebar, event: any) {
+ i.state.removeReason = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleModRemoveExpiresChange(i: Sidebar, event: any) {
+ console.log(event.target.value);
+ i.state.removeExpires = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleModRemoveSubmit(i: Sidebar) {
+
+ let deleteForm: CommunityFormI = {
+ name: i.props.community.name,
+ title: i.props.community.title,
+ category_id: i.props.community.category_id,
+ edit_id: i.props.community.id,
+ removed: !i.props.community.removed,
+ reason: i.state.removeReason,
+ expires: getUnixTime(i.state.removeExpires),
+ auth: null,
+ };
+ WebSocketService.Instance.editCommunity(deleteForm);
+
+ i.state.showRemoveDialog = false;
+ i.setState(i.state);
+ }
+
+
+
}
}
overview() {
- let combined: Array<any> = [];
- combined.push(...this.state.comments);
- combined.push(...this.state.posts);
+ let combined: Array<{type_: string, data: Comment | Post}> = [];
+ let comments = this.state.comments.map(e => {return {type_: "comments", data: e}});
+ let posts = this.state.posts.map(e => {return {type_: "posts", data: e}});
+
+ combined.push(...comments);
+ combined.push(...posts);
// Sort it
if (this.state.sort == SortType.New) {
- combined.sort((a, b) => b.published.localeCompare(a.published));
+ combined.sort((a, b) => b.data.published.localeCompare(a.data.published));
} else {
- combined.sort((a, b) => b.score - a.score);
+ combined.sort((a, b) => b.data.score - a.data.score);
}
return (
<div>
{combined.map(i =>
<div>
- {i.community_id
- ? <PostListing post={i} showCommunity viewOnly />
- : <CommentNodes nodes={[{comment: i}]} noIndent viewOnly />
+ {i.type_ == "posts"
+ ? <PostListing post={i.data as Post} showCommunity viewOnly />
+ : <CommentNodes nodes={[{comment: i.data as Comment}]} noIndent viewOnly />
}
</div>
)
import { Community } from './components/community';
import { Communities } from './components/communities';
import { User } from './components/user';
+import { Modlog } from './components/modlog';
import { Symbols } from './components/symbols';
import './main.css';
<Route path={`/community/:id`} component={Community} />
<Route path={`/user/:id/:heading`} component={User} />
<Route path={`/user/:id`} component={User} />
+ <Route path={`/modlog`} component={Modlog} />
</Switch>
<Symbols />
</div>
export enum UserOperation {
- Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails
+ Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity
+}
+
+export enum CommentSortType {
+ Hot, Top, New
+}
+
+export enum ListingType {
+ All, Subscribed, Community
+}
+
+export enum SortType {
+ Hot, New, TopDay, TopWeek, TopMonth, TopYear, TopAll
}
export interface User {
export interface Community {
user_id?: number;
subscribed?: boolean;
+ am_mod?: boolean;
+ removed?: boolean;
id: number;
name: string;
title: string;
updated?: string;
}
+export interface Post {
+ user_id?: number;
+ my_vote?: number;
+ am_mod?: boolean;
+ removed?: boolean;
+ locked?: boolean;
+ id: number;
+ name: string;
+ url?: string;
+ body?: string;
+ creator_id: number;
+ creator_name: string;
+ community_id: number;
+ community_name: string;
+ number_of_comments: number;
+ score: number;
+ upvotes: number;
+ downvotes: number;
+ hot_rank: number;
+ published: string;
+ updated?: string;
+}
+
+export interface Comment {
+ id: number;
+ content: string;
+ creator_id: number;
+ creator_name: string;
+ post_id: number,
+ community_id: number,
+ parent_id?: number;
+ published: string;
+ updated?: string;
+ score: number;
+ upvotes: number;
+ downvotes: number;
+ my_vote?: number;
+ am_mod?: boolean;
+ removed?: boolean;
+ banned?: boolean;
+}
+
+export interface Category {
+ id: number;
+ name: string;
+}
+
+export interface FollowCommunityForm {
+ community_id: number;
+ follow: boolean;
+ auth?: string;
+}
+
+export interface GetFollowedCommunitiesResponse {
+ op: string;
+ communities: Array<CommunityUser>;
+}
+
+export interface GetUserDetailsForm {
+ user_id: number;
+ sort: string; // TODO figure this one out
+ limit: number;
+ community_id?: number;
+ auth?: string;
+}
+
+export interface UserDetailsResponse {
+ op: string;
+ user: UserView;
+ follows: Array<CommunityUser>;
+ moderates: Array<CommunityUser>;
+ comments: Array<Comment>;
+ posts: Array<Post>;
+ saved?: Array<Post>;
+}
+
+export interface BanFromCommunityForm {
+ community_id: number;
+ user_id: number;
+ ban: boolean;
+ reason?: string,
+ expires?: number,
+ auth?: string;
+}
+
+export interface BanFromCommunityResponse {
+ op: string;
+ user: UserView,
+ banned: boolean,
+}
+
+export interface AddModToCommunityForm {
+ community_id: number;
+ user_id: number;
+ added: boolean;
+ auth?: string;
+}
+
+export interface AddModToCommunityResponse {
+ op: string;
+ moderators: Array<CommunityUser>;
+}
+
+export interface GetModlogForm {
+ mod_user_id?: number;
+ community_id?: number;
+ limit?: number;
+ page?: number;
+}
+
+export interface GetModlogResponse {
+ op: string;
+ removed_posts: Array<ModRemovePost>,
+ locked_posts: Array<ModLockPost>,
+ removed_comments: Array<ModRemoveComment>,
+ removed_communities: Array<ModRemoveCommunity>,
+ banned_from_community: Array<ModBanFromCommunity>,
+ banned: Array<ModBan>,
+ added_to_community: Array<ModAddCommunity>,
+ added: Array<ModAdd>,
+}
+
+export interface ModRemovePost {
+ id: number;
+ mod_user_id: number;
+ post_id: number;
+ reason?: string;
+ removed?: boolean;
+ when_: string
+ mod_user_name: string;
+ post_name: string;
+ community_id: number;
+ community_name: string;
+}
+
+export interface ModLockPost {
+ id: number,
+ mod_user_id: number,
+ post_id: number,
+ locked?: boolean,
+ when_: string,
+ mod_user_name: string,
+ post_name: string,
+ community_id: number,
+ community_name: string,
+}
+
+export interface ModRemoveComment {
+ id: number,
+ mod_user_id: number,
+ comment_id: number,
+ reason?: string,
+ removed?: boolean,
+ when_: string,
+ mod_user_name: string,
+ comment_user_id: number,
+ comment_user_name: string,
+ comment_content: string,
+ post_id: number,
+ post_name: string,
+ community_id: number,
+ community_name: string,
+}
+
+export interface ModRemoveCommunity {
+ id: number,
+ mod_user_id: number,
+ community_id: number,
+ reason?: string,
+ removed?: boolean,
+ expires?: number,
+ when_: string,
+ mod_user_name: string,
+ community_name: string,
+}
+
+export interface ModBanFromCommunity {
+ id: number,
+ mod_user_id: number,
+ other_user_id: number,
+ community_id: number,
+ reason?: string,
+ banned?: boolean,
+ expires?: number,
+ when_: string,
+ mod_user_name: string,
+ other_user_name: string,
+ community_name: string,
+}
+
+export interface ModBan {
+ id: number,
+ mod_user_id: number,
+ other_user_id: number,
+ reason?: string,
+ banned?: boolean,
+ expires?: number,
+ when_: string,
+ mod_user_name: string,
+ other_user_name: string,
+}
+
+export interface ModAddCommunity {
+ id: number,
+ mod_user_id: number,
+ other_user_id: number,
+ community_id: number,
+ removed?: boolean,
+ when_: string,
+ mod_user_name: string,
+ other_user_name: string,
+ community_name: string,
+}
+
+export interface ModAdd {
+ id: number,
+ mod_user_id: number,
+ other_user_id: number,
+ removed?: boolean,
+ when_: string,
+ mod_user_name: string,
+ other_user_name: string,
+}
+
+export interface LoginForm {
+ username_or_email: string;
+ password: string;
+}
+
+export interface RegisterForm {
+ username: string;
+ email?: string;
+ password: string;
+ password_verify: string;
+}
+
+export interface LoginResponse {
+ op: string;
+ jwt: string;
+}
+
+
+
export interface CommunityForm {
name: string;
title: string;
description?: string,
category_id: number,
edit_id?: number;
+ removed?: boolean;
+ reason?: string;
+ expires?: number;
auth?: string;
}
op: string;
categories: Array<Category>;
}
-
-export interface Post {
- user_id?: number;
- my_vote?: number;
- id: number;
- name: string;
- url?: string;
- body?: string;
- creator_id: number;
- creator_name: string;
- community_id: number;
- community_name: string;
- number_of_comments: number;
- score: number;
- upvotes: number;
- downvotes: number;
- hot_rank: number;
- published: string;
- updated?: string;
-}
-
export interface PostForm {
name: string;
url?: string;
community_id: number;
updated?: number;
edit_id?: number;
+ creator_id: number;
+ removed?: boolean;
+ reason?: string;
+ locked?: boolean;
auth: string;
}
post: Post;
}
-export interface Comment {
- id: number;
- content: string;
- creator_id: number;
- creator_name: string;
- post_id: number,
- parent_id?: number;
- published: string;
- updated?: string;
- score: number;
- upvotes: number;
- downvotes: number;
- my_vote?: number;
-}
-
export interface CommentForm {
content: string;
post_id: number;
parent_id?: number;
edit_id?: number;
+ creator_id: number;
+ removed?: boolean;
+ reason?: string;
auth: string;
}
post: Post;
}
-export interface Category {
- id: number;
- name: string;
-}
-
-export interface FollowCommunityForm {
- community_id: number;
- follow: boolean;
- auth?: string;
-}
-
-export interface GetFollowedCommunitiesResponse {
- op: string;
- communities: Array<CommunityUser>;
-}
-
-export interface GetUserDetailsForm {
- user_id: number;
- sort: string; // TODO figure this one out
- limit: number;
- community_id?: number;
- auth?: string;
-}
-
-
-
-export interface UserDetailsResponse {
- op: string;
- user: UserView;
- follows: Array<CommunityUser>;
- moderates: Array<CommunityUser>;
- comments: Array<Comment>;
- posts: Array<Post>;
- saved?: Array<Post>;
-}
-
-
-export interface LoginForm {
- username_or_email: string;
- password: string;
-}
-
-export interface RegisterForm {
- username: string;
- email?: string;
- password: string;
- password_verify: string;
-}
-
-
-export interface LoginResponse {
- op: string;
- jwt: string;
-}
-
-export enum CommentSortType {
- Hot, Top, New
-}
-
-export enum ListingType {
- All, Subscribed, Community
-}
-
-export enum SortType {
- Hot, New, TopDay, TopWeek, TopMonth, TopYear, TopAll
-}
color: #fff;
}
+.form-control:disabled {
+ background-color: var(--secondary);
+ opacity: .5;
+}
+
.custom-select {
color: #fff;
background-color: var(--secondary);
import { wsUri } from '../env';
-import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm } from '../interfaces';
+import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm } from '../interfaces';
import { webSocket } from 'rxjs/webSocket';
import { Subject } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
this.subject.next(this.wsSendWrapper(UserOperation.EditPost, postForm));
}
+ public banFromCommunity(form: BanFromCommunityForm) {
+ this.setAuth(form);
+ this.subject.next(this.wsSendWrapper(UserOperation.BanFromCommunity, form));
+ }
+
+ public addModToCommunity(form: AddModToCommunityForm) {
+ this.setAuth(form);
+ this.subject.next(this.wsSendWrapper(UserOperation.AddModToCommunity, form));
+ }
+
public getUserDetails(form: GetUserDetailsForm) {
this.setAuth(form, false);
this.subject.next(this.wsSendWrapper(UserOperation.GetUserDetails, form));
}
+ public getModlog(form: GetModlogForm) {
+ this.subject.next(this.wsSendWrapper(UserOperation.GetModlog, form));
+ }
+
private wsSendWrapper(op: UserOperation, data: any) {
let send = { op: UserOperation[op], data: data };
console.log(send);
export function mdToHtml(text: string) {
return {__html: md.render(text)};
}
+
+export function getUnixTime(text: string): number {
+ return text ? new Date(text).getTime()/1000 : undefined;
+}
+
+export function addTypeInfo<T>(arr: Array<T>, name: string): Array<{type_: string, data: T}> {
+ return arr.map(e => {return {type_: name, data: e}});
+}
-export let version: string = "v0.0.2-9-g8e5a5d1";
\ No newline at end of file
+export let version: string = "v0.0.2-13-g1bf0dfd";
\ No newline at end of file