summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2020-01-22 16:35:29 -0500
committerDessalines <tyhou13@gmx.com>2020-01-22 16:38:16 -0500
commit253bc3e0afb6adf64b79f334a8bc1f972aa45eba (patch)
treebdbbb36ee59ea4331c7bf7cf89e8ef554eb2d63e
parenta964b4ce21cc19eb42ae4da1a1aef8bfc0a1df5c (diff)
Adding private messaging, and matrix user ids.
- Fixes #244
-rw-r--r--README.md18
-rw-r--r--server/migrations/2020-01-21-001001_create_private_message/down.sql34
-rw-r--r--server/migrations/2020-01-21-001001_create_private_message/up.sql90
-rw-r--r--server/src/api/comment.rs4
-rw-r--r--server/src/api/mod.rs5
-rw-r--r--server/src/api/user.rs214
-rw-r--r--server/src/apub/mod.rs1
-rw-r--r--server/src/db/comment.rs1
-rw-r--r--server/src/db/comment_view.rs1
-rw-r--r--server/src/db/community.rs1
-rw-r--r--server/src/db/mod.rs2
-rw-r--r--server/src/db/moderator.rs2
-rw-r--r--server/src/db/password_reset_request.rs1
-rw-r--r--server/src/db/post.rs1
-rw-r--r--server/src/db/post_view.rs1
-rw-r--r--server/src/db/private_message.rs144
-rw-r--r--server/src/db/private_message_view.rs140
-rw-r--r--server/src/db/user.rs4
-rw-r--r--server/src/db/user_mention.rs2
-rw-r--r--server/src/db/user_view.rs3
-rw-r--r--server/src/routes/index.rs1
-rw-r--r--server/src/schema.rs15
-rw-r--r--server/src/websocket/server.rs16
-rw-r--r--ui/src/components/create-private-message.tsx52
-rw-r--r--ui/src/components/inbox.tsx117
-rw-r--r--ui/src/components/navbar.tsx41
-rw-r--r--ui/src/components/private-message-form.tsx291
-rw-r--r--ui/src/components/private-message.tsx249
-rw-r--r--ui/src/components/user.tsx51
-rw-r--r--ui/src/index.tsx5
-rw-r--r--ui/src/interfaces.ts55
-rw-r--r--ui/src/services/WebSocketService.ts26
-rw-r--r--ui/src/translations/en.ts13
-rw-r--r--ui/src/utils.ts5
34 files changed, 1560 insertions, 46 deletions
diff --git a/README.md b/README.md
index 51bd0476..752e7d98 100644
--- a/README.md
+++ b/README.md
@@ -157,15 +157,15 @@ If you'd like to add translations, take a look a look at the [English translatio
lang | done | missing
--- | --- | ---
-de | 93% | avatar,upload_avatar,show_avatars,docs,old_password,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,email_already_exists
-eo | 80% | number_of_communities,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,are_you_sure,yes,no,email_already_exists
-es | 89% | avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,email_already_exists
-fr | 89% | avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,email_already_exists
-it | 89% | avatar,upload_avatar,show_avatars,archive_link,docs,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,email_already_exists
-nl | 99% | donate_to_lemmy,donate,email_already_exists
-ru | 77% | cross_posts,cross_post,number_of_communities,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no,email_already_exists
-sv | 89% | avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,email_already_exists
-zh | 75% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no,email_already_exists
+de | 88% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
+eo | 76% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
+es | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
+fr | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
+it | 85% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
+nl | 93% | create_private_message,send_secure_message,send_message,message,message_sent,messages,matrix_user_id,private_message_disclaimer,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
+ru | 72% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
+sv | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
+zh | 71% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
<!-- translationsstop -->
diff --git a/server/migrations/2020-01-21-001001_create_private_message/down.sql b/server/migrations/2020-01-21-001001_create_private_message/down.sql
new file mode 100644
index 00000000..0d951e3e
--- /dev/null
+++ b/server/migrations/2020-01-21-001001_create_private_message/down.sql
@@ -0,0 +1,34 @@
+-- Drop the triggers
+drop trigger refresh_private_message on private_message;
+drop function refresh_private_message();
+
+-- Drop the view and table
+drop view private_message_view cascade;
+drop table private_message;
+
+-- Rebuild the old views
+drop view user_view cascade;
+create view user_view as
+select
+u.id,
+u.name,
+u.avatar,
+u.email,
+u.fedi_name,
+u.admin,
+u.banned,
+u.show_avatars,
+u.send_notifications_to_email,
+u.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;
+
+create materialized view user_mview as select * from user_view;
+
+create unique index idx_user_mview_id on user_mview (id);
+
+-- Drop the columns
+alter table user_ drop column matrix_user_id;
diff --git a/server/migrations/2020-01-21-001001_create_private_message/up.sql b/server/migrations/2020-01-21-001001_create_private_message/up.sql
new file mode 100644
index 00000000..48e16dd8
--- /dev/null
+++ b/server/migrations/2020-01-21-001001_create_private_message/up.sql
@@ -0,0 +1,90 @@
+-- Creating private message
+create table private_message (
+ id serial primary key,
+ creator_id int references user_ on update cascade on delete cascade not null,
+ recipient_id int references user_ on update cascade on delete cascade not null,
+ content text not null,
+ deleted boolean default false not null,
+ read boolean default false not null,
+ published timestamp not null default now(),
+ updated timestamp
+);
+
+-- Create the view and materialized view which has the avatar and creator name
+create view private_message_view as
+select
+pm.*,
+u.name as creator_name,
+u.avatar as creator_avatar,
+u2.name as recipient_name,
+u2.avatar as recipient_avatar
+from private_message pm
+inner join user_ u on u.id = pm.creator_id
+inner join user_ u2 on u2.id = pm.recipient_id;
+
+create materialized view private_message_mview as select * from private_message_view;
+
+create unique index idx_private_message_mview_id on private_message_mview (id);
+
+-- Create the triggers
+create or replace function refresh_private_message()
+returns trigger language plpgsql
+as $$
+begin
+ refresh materialized view concurrently private_message_mview;
+ return null;
+end $$;
+
+create trigger refresh_private_message
+after insert or update or delete or truncate
+on private_message
+for each statement
+execute procedure refresh_private_message();
+
+-- Update user to include matrix id
+alter table user_ add column matrix_user_id text unique;
+
+drop view user_view cascade;
+create view user_view as
+select
+u.id,
+u.name,
+u.avatar,
+u.email,
+u.matrix_user_id,
+u.fedi_name,
+u.admin,
+u.banned,
+u.show_avatars,
+u.send_notifications_to_email,
+u.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;
+
+create materialized view user_mview as select * from user_view;
+
+create unique index idx_user_mview_id on user_mview (id);
+
+-- This is what a group pm table would look like
+-- Not going to do it now because of the complications
+--
+-- create table private_message (
+-- id serial primary key,
+-- creator_id int references user_ on update cascade on delete cascade not null,
+-- content text not null,
+-- deleted boolean default false not null,
+-- published timestamp not null default now(),
+-- updated timestamp
+-- );
+--
+-- create table private_message_recipient (
+-- id serial primary key,
+-- private_message_id int references private_message on update cascade on delete cascade not null,
+-- recipient_id int references user_ on update cascade on delete cascade not null,
+-- read boolean default false not null,
+-- published timestamp not null default now(),
+-- unique(private_message_id, recipient_id)
+-- )
diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs
index 61cc9506..382afb5b 100644
--- a/server/src/api/comment.rs
+++ b/server/src/api/comment.rs
@@ -7,7 +7,7 @@ use diesel::PgConnection;
pub struct CreateComment {
content: String,
parent_id: Option<i32>,
- edit_id: Option<i32>,
+ edit_id: Option<i32>, // TODO this isn't used
pub post_id: i32,
auth: String,
}
@@ -15,7 +15,7 @@ pub struct CreateComment {
#[derive(Serialize, Deserialize)]
pub struct EditComment {
content: String,
- parent_id: Option<i32>,
+ parent_id: Option<i32>, // TODO why are the parent_id, creator_id, post_id, etc fields required? They aren't going to change
edit_id: i32,
creator_id: i32,
pub post_id: i32,
diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs
index e3580447..3b2466ac 100644
--- a/server/src/api/mod.rs
+++ b/server/src/api/mod.rs
@@ -8,6 +8,8 @@ use crate::db::moderator_views::*;
use crate::db::password_reset_request::*;
use crate::db::post::*;
use crate::db::post_view::*;
+use crate::db::private_message::*;
+use crate::db::private_message_view::*;
use crate::db::site::*;
use crate::db::site_view::*;
use crate::db::user::*;
@@ -67,6 +69,9 @@ pub enum UserOperation {
DeleteAccount,
PasswordReset,
PasswordChange,
+ CreatePrivateMessage,
+ EditPrivateMessage,
+ GetPrivateMessages,
}
#[derive(Fail, Debug)]
diff --git a/server/src/api/user.rs b/server/src/api/user.rs
index ac700aca..046da6fb 100644
--- a/server/src/api/user.rs
+++ b/server/src/api/user.rs
@@ -30,6 +30,7 @@ pub struct SaveUserSettings {
lang: String,
avatar: Option<String>,
email: Option<String>,
+ matrix_user_id: Option<String>,
new_password: Option<String>,
new_password_verify: Option<String>,
old_password: Option<String>,
@@ -167,6 +168,42 @@ pub struct PasswordChange {
password_verify: String,
}
+#[derive(Serialize, Deserialize)]
+pub struct CreatePrivateMessage {
+ content: String,
+ recipient_id: i32,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditPrivateMessage {
+ edit_id: i32,
+ content: Option<String>,
+ deleted: Option<bool>,
+ read: Option<bool>,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetPrivateMessages {
+ unread_only: bool,
+ page: Option<i64>,
+ limit: Option<i64>,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PrivateMessagesResponse {
+ op: String,
+ messages: Vec<PrivateMessageView>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PrivateMessageResponse {
+ op: String,
+ message: PrivateMessageView,
+}
+
impl Perform<LoginResponse> for Oper<Login> {
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
let data: &Login = &self.data;
@@ -221,6 +258,7 @@ impl Perform<LoginResponse> for Oper<Register> {
name: data.username.to_owned(),
fedi_name: Settings::get().hostname.to_owned(),
email: data.email.to_owned(),
+ matrix_user_id: None,
avatar: None,
password_encrypted: data.password.to_owned(),
preferred_username: None,
@@ -357,6 +395,7 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
name: read_user.name,
fedi_name: read_user.fedi_name,
email,
+ matrix_user_id: data.matrix_user_id.to_owned(),
avatar: data.avatar.to_owned(),
password_encrypted,
preferred_username: read_user.preferred_username,
@@ -504,10 +543,12 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
let read_user = User_::read(&conn, data.user_id)?;
+ // TODO make addadmin easier
let user_form = UserForm {
name: read_user.name,
fedi_name: read_user.fedi_name,
email: read_user.email,
+ matrix_user_id: read_user.matrix_user_id,
avatar: read_user.avatar,
password_encrypted: read_user.password_encrypted,
preferred_username: read_user.preferred_username,
@@ -568,10 +609,12 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
let read_user = User_::read(&conn, data.user_id)?;
+ // TODO make bans and addadmins easier
let user_form = UserForm {
name: read_user.name,
fedi_name: read_user.fedi_name,
email: read_user.email,
+ matrix_user_id: read_user.matrix_user_id,
avatar: read_user.avatar,
password_encrypted: read_user.password_encrypted,
preferred_username: read_user.preferred_username,
@@ -762,6 +805,30 @@ impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
};
}
+ // messages
+ let messages = PrivateMessageQueryBuilder::create(&conn, user_id)
+ .page(1)
+ .limit(999)
+ .unread_only(true)
+ .list()?;
+
+ for message in &messages {
+ let private_message_form = PrivateMessageForm {
+ content: None,
+ creator_id: message.to_owned().creator_id,
+ recipient_id: message.to_owned().recipient_id,
+ deleted: None,
+ read: Some(true),
+ updated: None,
+ };
+
+ let _updated_message = match PrivateMessage::update(&conn, message.id, &private_message_form)
+ {
+ Ok(message) => message,
+ Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_private_message").into()),
+ };
+ }
+
Ok(GetRepliesResponse {
op: self.op.to_string(),
replies: vec![],
@@ -905,3 +972,150 @@ impl Perform<LoginResponse> for Oper<PasswordChange> {
})
}
}
+
+impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
+ fn perform(&self, conn: &PgConnection) -> Result<PrivateMessageResponse, Error> {
+ let data: &CreatePrivateMessage = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let hostname = &format!("https://{}", Settings::get().hostname);
+
+ // Check for a site ban
+ if UserView::read(&conn, user_id)?.banned {
+ return Err(APIError::err(&self.op, "site_ban").into());
+ }
+
+ let content_slurs_removed = remove_slurs(&data.content.to_owned());
+
+ let private_message_form = PrivateMessageForm {
+ content: Some(content_slurs_removed.to_owned()),
+ creator_id: user_id,
+ recipient_id: data.recipient_id,
+ deleted: None,
+ read: None,
+ updated: None,
+ };
+
+ let inserted_private_message = match PrivateMessage::create(&conn, &private_message_form) {
+ Ok(private_message) => private_message,
+ Err(_e) => {
+ return Err(APIError::err(&self.op, "couldnt_create_private_message").into());
+ }
+ };
+
+ // Send notifications to the recipient
+ let recipient_user = User_::read(&conn, data.recipient_id)?;
+ if recipient_user.send_notifications_to_email {
+ if let Some(email) = recipient_user.email {
+ let subject = &format!(
+ "{} - Private Message from {}",
+ Settings::get().hostname,
+ claims.username
+ );
+ let html = &format!(
+ "<h1>Private Message</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+ claims.username, &content_slurs_removed, hostname
+ );
+ match send_email(subject, &email, &recipient_user.name, html) {
+ Ok(_o) => _o,
+ Err(e) => eprintln!("{}", e),
+ };
+ }
+ }
+
+ let private_message_view = PrivateMessageView::read(&conn, inserted_private_message.id)?;
+
+ Ok(PrivateMessageResponse {
+ op: self.op.to_string(),
+ message: private_message_view,
+ })
+ }
+}
+
+impl Perform<PrivateMessageResponse> for Oper<EditPrivateMessage> {
+ fn perform(&self, conn: &PgConnection) -> Result<PrivateMessageResponse, Error> {
+ let data: &EditPrivateMessage = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let orig_private_message = PrivateMessage::read(&conn, data.edit_id)?;
+
+ // Check for a site ban
+ if UserView::read(&conn, user_id)?.banned {
+ return Err(APIError::err(&self.op, "site_ban").into());
+ }
+
+ // Check to make sure they are the creator (or the recipient marking as read
+ if !(data.read.is_some() && orig_private_message.recipient_id.eq(&user_id)
+ || orig_private_message.creator_id.eq(&user_id))
+ {
+ return Err(APIError::err(&self.op, "no_private_message_edit_allowed").into());
+ }
+
+ let content_slurs_removed = match &data.content {
+ Some(content) => Some(remove_slurs(content)),
+ None => None,
+ };
+
+ let private_message_form = PrivateMessageForm {
+ content: content_slurs_removed,
+ creator_id: orig_private_message.creator_id,
+ recipient_id: orig_private_message.recipient_id,
+ deleted: data.deleted.to_owned(),
+ read: data.read.to_owned(),
+ updated: if data.read.is_some() {
+ orig_private_message.updated
+ } else {
+ Some(naive_now())
+ },
+ };
+
+ let _updated_private_message =
+ match PrivateMessage::update(&conn, data.edit_id, &private_message_form) {
+ Ok(private_message) => private_message,
+ Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_private_message").into()),
+ };
+
+ let private_message_view = PrivateMessageView::read(&conn, data.edit_id)?;
+
+ Ok(PrivateMessageResponse {
+ op: self.op.to_string(),
+ message: private_message_view,
+ })
+ }
+}
+
+impl Perform<PrivateMessagesResponse> for Oper<GetPrivateMessages> {
+ fn perform(&self, conn: &PgConnection) -> Result<PrivateMessagesResponse, Error> {
+ let data: &GetPrivateMessages = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let messages = PrivateMessageQueryBuilder::create(&conn, user_id)
+ .page(data.page)
+ .limit(data.limit)
+ .unread_only(data.unread_only)
+ .list()?;
+
+ Ok(PrivateMessagesResponse {
+ op: self.op.to_string(),
+ messages,
+ })
+ }
+}
diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs
index 2d2e5ad3..c5a0b2f0 100644
--- a/server/src/apub/mod.rs
+++ b/server/src/apub/mod.rs
@@ -22,6 +22,7 @@ mod tests {
preferred_username: None,
password_encrypted: "here".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
published: naive_now(),
admin: false,
diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs
index a9c7d81d..efba07a5 100644
--- a/server/src/db/comment.rs
+++ b/server/src/db/comment.rs
@@ -174,6 +174,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs
index 3b06e8e3..d4a65c9a 100644
--- a/server/src/db/comment_view.rs
+++ b/server/src/db/comment_view.rs
@@ -398,6 +398,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/community.rs b/server/src/db/community.rs
index b482ca4a..63500963 100644
--- a/server/src/db/community.rs
+++ b/server/src/db/community.rs
@@ -220,6 +220,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/mod.rs b/server/src/db/mod.rs
index fef3ffce..dacdb6f6 100644
--- a/server/src/db/mod.rs
+++ b/server/src/db/mod.rs
@@ -15,6 +15,8 @@ pub mod moderator_views;
pub mod password_reset_request;
pub mod post;
pub mod post_view;
+pub mod private_message;
+pub mod private_message_view;
pub mod site;
pub mod site_view;
pub mod user;
diff --git a/server/src/db/moderator.rs b/server/src/db/moderator.rs
index 3c6233cb..4fd532af 100644
--- a/server/src/db/moderator.rs
+++ b/server/src/db/moderator.rs
@@ -442,6 +442,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
@@ -463,6 +464,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/password_reset_request.rs b/server/src/db/password_reset_request.rs
index fa060a59..6951fd39 100644
--- a/server/src/db/password_reset_request.rs
+++ b/server/src/db/password_reset_request.rs
@@ -92,6 +92,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/post.rs b/server/src/db/post.rs
index d3fba4da..9e7a4341 100644
--- a/server/src/db/post.rs
+++ b/server/src/db/post.rs
@@ -187,6 +187,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/post_view.rs b/server/src/db/post_view.rs
index f6cc274f..c80d1696 100644
--- a/server/src/db/post_view.rs
+++ b/server/src/db/post_view.rs
@@ -339,6 +339,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
updated: None,
admin: false,
diff --git a/server/src/db/private_message.rs b/server/src/db/private_message.rs
new file mode 100644
index 00000000..cc073b59
--- /dev/null
+++ b/server/src/db/private_message.rs
@@ -0,0 +1,144 @@
+use super::*;
+use crate::schema::private_message;
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name = "private_message"]
+pub struct PrivateMessage {
+ pub id: i32,
+ pub creator_id: i32,
+ pub recipient_id: i32,
+ pub content: String,
+ pub deleted: bool,
+ pub read: bool,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
+}
+
+#[derive(Insertable, AsChangeset, Clone)]
+#[table_name = "private_message"]
+pub struct PrivateMessageForm {
+ pub creator_id: i32,
+ pub recipient_id: i32,
+ pub content: Option<String>,
+ pub deleted: Option<bool>,
+ pub read: Option<bool>,
+ pub updated: Option<chrono::NaiveDateTime>,
+}
+
+impl Crud<PrivateMessageForm> for PrivateMessage {
+ fn read(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> {