diff --git a/README.md b/README.md index 9812dbf2..c5a30598 100644 --- a/README.md +++ b/README.md @@ -257,16 +257,15 @@ If you'd like to add translations, take a look a look at the [English translatio lang | done | missing --- | --- | --- -de | 97% | avatar,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw -eo | 84% | number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,are_you_sure,yes,no -es | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw -fr | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw -it | 93% | avatar,archive_link,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw -nl | 86% | preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme -ru | 80% | cross_posts,cross_post,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no -sv | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw -zh | 78% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no - +de | 97% | avatar,old_password,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +eo | 83% | number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,are_you_sure,yes,no +es | 92% | avatar,archive_link,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +fr | 92% | avatar,archive_link,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +it | 93% | avatar,archive_link,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +nl | 85% | preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme +ru | 79% | cross_posts,cross_post,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no +sv | 92% | avatar,archive_link,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +zh | 77% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no If you'd like to update this report, run: diff --git a/server/migrations/2020-01-01-200418_add_email_to_user_view/down.sql b/server/migrations/2020-01-01-200418_add_email_to_user_view/down.sql new file mode 100644 index 00000000..92f771f8 --- /dev/null +++ b/server/migrations/2020-01-01-200418_add_email_to_user_view/down.sql @@ -0,0 +1,15 @@ +-- user +drop view user_view; +create view user_view as +select id, +name, +avatar, +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; diff --git a/server/migrations/2020-01-01-200418_add_email_to_user_view/up.sql b/server/migrations/2020-01-01-200418_add_email_to_user_view/up.sql new file mode 100644 index 00000000..59972dfb --- /dev/null +++ b/server/migrations/2020-01-01-200418_add_email_to_user_view/up.sql @@ -0,0 +1,16 @@ +-- user +drop view user_view; +create view user_view as +select id, +name, +avatar, +email, +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; diff --git a/server/src/api/user.rs b/server/src/api/user.rs index e8ad20aa..c074228f 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -28,6 +28,10 @@ pub struct SaveUserSettings { default_listing_type: i16, lang: String, avatar: Option, + email: Option, + new_password: Option, + new_password_verify: Option, + old_password: Option, auth: String, } @@ -312,12 +316,45 @@ impl Perform for Oper { let read_user = User_::read(&conn, user_id)?; + let email = match &data.email { + Some(email) => Some(email.to_owned()), + None => read_user.email, + }; + + let password_encrypted = match &data.new_password { + Some(new_password) => { + match &data.new_password_verify { + Some(new_password_verify) => { + // Make sure passwords match + if new_password != new_password_verify { + return Err(APIError::err(&self.op, "passwords_dont_match"))?; + } + + // Check the old password + match &data.old_password { + Some(old_password) => { + let valid: bool = + verify(old_password, &read_user.password_encrypted).unwrap_or(false); + if !valid { + return Err(APIError::err(&self.op, "password_incorrect"))?; + } + User_::update_password(&conn, user_id, &new_password)?.password_encrypted + } + None => return Err(APIError::err(&self.op, "password_incorrect"))?, + } + } + None => return Err(APIError::err(&self.op, "passwords_dont_match"))?, + } + } + None => read_user.password_encrypted, + }; + let user_form = UserForm { name: read_user.name, fedi_name: read_user.fedi_name, - email: read_user.email, + email, avatar: data.avatar.to_owned(), - password_encrypted: read_user.password_encrypted, + password_encrypted, preferred_username: read_user.preferred_username, updated: Some(naive_now()), admin: read_user.admin, @@ -850,28 +887,8 @@ impl Perform for Oper { return Err(APIError::err(&self.op, "passwords_dont_match"))?; } - // Fetch the user - let read_user = User_::read(&conn, user_id)?; - // Update the user with the new password - let user_form = UserForm { - name: read_user.name, - fedi_name: read_user.fedi_name, - email: read_user.email, - avatar: read_user.avatar, - password_encrypted: data.password.to_owned(), - preferred_username: read_user.preferred_username, - updated: Some(naive_now()), - admin: read_user.admin, - banned: read_user.banned, - show_nsfw: read_user.show_nsfw, - theme: read_user.theme, - default_sort_type: read_user.default_sort_type, - default_listing_type: read_user.default_listing_type, - lang: read_user.lang, - }; - - let updated_user = match User_::update_password(&conn, user_id, &user_form) { + let updated_user = match User_::update_password(&conn, user_id, &data.password) { Ok(user) => user, Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user"))?, }; diff --git a/server/src/db/user.rs b/server/src/db/user.rs index db4aa453..82736f8e 100644 --- a/server/src/db/user.rs +++ b/server/src/db/user.rs @@ -75,14 +75,13 @@ impl User_ { pub fn update_password( conn: &PgConnection, user_id: i32, - form: &UserForm, + new_password: &str, ) -> Result { - let mut edited_user = form.clone(); - let password_hash = - hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password"); - edited_user.password_encrypted = password_hash; + let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password"); - Self::update(&conn, user_id, &edited_user) + diesel::update(user_.find(user_id)) + .set(password_encrypted.eq(password_hash)) + .get_result::(conn) } pub fn read_from_name(conn: &PgConnection, from_user_name: String) -> Result { diff --git a/server/src/db/user_view.rs b/server/src/db/user_view.rs index 616159de..2d50b40c 100644 --- a/server/src/db/user_view.rs +++ b/server/src/db/user_view.rs @@ -7,6 +7,7 @@ table! { id -> Int4, name -> Varchar, avatar -> Nullable, + email -> Nullable, fedi_name -> Varchar, admin -> Bool, banned -> Bool, @@ -26,6 +27,7 @@ pub struct UserView { pub id: i32, pub name: String, pub avatar: Option, + pub email: Option, pub fedi_name: String, pub admin: bool, pub banned: bool, diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index e97b26f9..99c340c5 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -99,7 +99,6 @@ export class User extends Component { default_sort_type: null, default_listing_type: null, lang: null, - avatar: null, auth: null, }, userSettingsLoading: null, @@ -437,199 +436,240 @@ export class User extends Component {
-
- - - - + # + + +
+ + +
-
-
+ + + + {languages.map(lang => ( + + ))} +
-
-
+ + {themes.map(theme => ( + + ))} +
-
- - -
+ +
-
- - + + # + + + + +
+ +
+
- +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
{WebSocketService.Instance.site.enable_nsfw && (
-
-
- - -
+
+ +
)}
-
- -
+

-
- - {this.state.deleteAccountShowConfirm && ( - <> - - - - - + + {this.state.deleteAccountShowConfirm && ( + <> + + + + + + )}
@@ -786,6 +826,38 @@ export class User extends Component { this.setState(this.state); } + handleUserSettingsEmailChange(i: User, event: any) { + i.state.userSettingsForm.email = event.target.value; + if (i.state.userSettingsForm.email == '' && !i.state.user.email) { + i.state.userSettingsForm.email = undefined; + } + i.setState(i.state); + } + + handleUserSettingsNewPasswordChange(i: User, event: any) { + i.state.userSettingsForm.new_password = event.target.value; + if (i.state.userSettingsForm.new_password == '') { + i.state.userSettingsForm.new_password = undefined; + } + i.setState(i.state); + } + + handleUserSettingsNewPasswordVerifyChange(i: User, event: any) { + i.state.userSettingsForm.new_password_verify = event.target.value; + if (i.state.userSettingsForm.new_password_verify == '') { + i.state.userSettingsForm.new_password_verify = undefined; + } + i.setState(i.state); + } + + handleUserSettingsOldPasswordChange(i: User, event: any) { + i.state.userSettingsForm.old_password = event.target.value; + if (i.state.userSettingsForm.old_password == '') { + i.state.userSettingsForm.old_password = undefined; + } + i.setState(i.state); + } + handleImageUpload(i: User, event: any) { event.preventDefault(); let file = event.target.files[0]; @@ -856,6 +928,8 @@ export class User extends Component { if (msg.error) { alert(i18n.t(msg.error)); this.state.deleteAccountLoading = false; + this.state.avatarLoading = false; + this.state.userSettingsLoading = false; if (msg.error == 'couldnt_find_that_username_or_email') { this.context.router.history.push('/'); } @@ -882,6 +956,7 @@ export class User extends Component { UserService.Instance.user.default_listing_type; this.state.userSettingsForm.lang = UserService.Instance.user.lang; this.state.userSettingsForm.avatar = UserService.Instance.user.avatar; + this.state.userSettingsForm.email = this.state.user.email; } document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`; window.scrollTo(0, 0); diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 7020ea49..1762bd60 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -87,6 +87,7 @@ export interface UserView { id: number; name: string; avatar?: string; + email?: string; fedi_name: string; published: string; number_of_posts: number; @@ -481,6 +482,10 @@ export interface UserSettingsForm { default_listing_type: ListingType; lang: string; avatar?: string; + email?: string; + new_password?: string; + new_password_verify?: string; + old_password?: string; auth: string; } diff --git a/ui/src/translations/en.ts b/ui/src/translations/en.ts index 10862081..73726808 100644 --- a/ui/src/translations/en.ts +++ b/ui/src/translations/en.ts @@ -118,6 +118,7 @@ export const en = { unread_messages: 'Unread Messages', password: 'Password', verify_password: 'Verify Password', + old_password: 'Old Password', forgot_password: 'forgot password', reset_password_mail_sent: 'Sent an Email to reset your password.', password_change: 'Password Change',