Repository: LemmyNet/lemmy (language: rust)

Issue:
user waiting for registration approval is confusing
### Requirements

- [x] Is this a feature request? For questions or discussions use https://lemmy.ml/c/lemmy_support
- [x] Did you check to see if this issue already exists?
- [x] Is this only a feature request? Do not put multiple feature requests in one issue.
- [x] Is this a backend issue? Use the [lemmy-ui](https://github.com/LemmyNet/lemmy-ui) repo for UI / frontend issues.
- [x] Do you agree to follow the rules in our [Code of Conduct](https://join-lemmy.org/docs/code_of_conduct.html)?

### Is your proposal related to a problem?

User received an email to verify their email address but was not told that they also need to wait for an admin to manually approve them.

### Describe the solution you'd like.

The emails should tell you that you will also need manual approval from an admin.

### Describe alternatives you've considered.

The website should also tell you if you need to verify your email, and/or are waiting to be approved, or any other reason why your account is still locked.

### Additional context

Confused new user: https://old.reddit.com/r/technology/comments/1ihwo9g/reddit_temporarily_bans_rwhitepeopletwitter_after/mb2ds0s/?context=3


Discussion / hints:
Adding a "still waiting on admin approval" in emails for servers that have that turned on would be a great idea, thx.

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index d2324ad7ae..2b1e01770f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2591,7 +2591,6 @@ dependencies = [
  "regex",
  "reqwest 0.12.12",
  "reqwest-middleware",
- "rosetta-i18n",
  "serde",
  "serde_with",
  "serial_test",
diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs
index 8122d31cca..289012c251 100644
--- a/crates/api/src/local_user/save_settings.rs
+++ b/crates/api/src/local_user/save_settings.rs
@@ -54,7 +54,9 @@ pub async fn save_user_settings(
     if previous_email.deref() != email {
       LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
       send_verification_email(
-        &local_user_view,
+        &site_view.local_site,
+        &local_user_view.local_user,
+        &local_user_view.person,
         email,
         &mut context.pool(),
         context.settings(),
diff --git a/crates/api_common/Cargo.toml b/crates/api_common/Cargo.toml
index 616b306670..366c647c48 100644
--- a/crates/api_common/Cargo.toml
+++ b/crates/api_common/Cargo.toml
@@ -19,7 +19,6 @@ workspace = true
 [features]
 full = [
   "tracing",
-  "rosetta-i18n",
   "lemmy_db_views/full",
   "lemmy_utils/full",
   "activitypub_federation",
@@ -51,7 +50,6 @@ chrono = { workspace = true }
 tracing = { workspace = true, optional = true }
 reqwest-middleware = { workspace = true, optional = true }
 regex = { workspace = true }
-rosetta-i18n = { workspace = true, optional = true }
 futures = { workspace = true, optional = true }
 uuid = { workspace = true, optional = true }
 tokio = { workspace = true, optional = true }
diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs
index ac8e299beb..a7c8b7afab 100644
--- a/crates/api_common/src/build_response.rs
+++ b/crates/api_common/src/build_response.rs
@@ -3,12 +3,7 @@ use crate::{
   community::CommunityResponse,
   context::LemmyContext,
   post::PostResponse,
-  utils::{
-    check_person_instance_community_block,
-    get_interface_language,
-    is_mod_or_admin,
-    send_email_to_user,
-  },
+  utils::{check_person_instance_community_block, is_mod_or_admin, send_email_to_user},
 };
 use actix_web::web::Json;
 use lemmy_db_schema::{
@@ -192,7 +187,7 @@ pub async fn send_local_notifs(
 
       // Send an email to those local users that have notifications on
       if do_send_email {
-        let lang = get_interface_language(&mention_user_view);
+        let lang = &mention_user_view.local_user.interface_i18n_language();
         let content = markdown_to_html(&comment_content_or_post_body);
         send_email_to_user(
           &mention_user_view,
@@ -245,7 +240,7 @@ pub async fn send_local_notifs(
               .ok();
 
             if do_send_email {
-              let lang = get_interface_language(&parent_user_view);
+              let lang = &parent_user_view.local_user.interface_i18n_language();
               let content = markdown_to_html(&comment.content);
               send_email_to_user(
                 &parent_user_view,
@@ -298,7 +293,7 @@ pub async fn send_local_notifs(
               .ok();
 
             if do_send_email {
-              let lang = get_interface_language(&parent_user_view);
+              let lang = &parent_user_view.local_user.interface_i18n_language();
               let content = markdown_to_html(&comment.content);
               send_email_to_user(
                 &parent_user_view,
diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs
index 944f8e4fbf..5113ab5b37 100644
--- a/crates/api_common/src/utils.rs
+++ b/crates/api_common/src/utils.rs
@@ -26,6 +26,7 @@ use lemmy_db_schema::{
     local_site::LocalSite,
     local_site_rate_limit::LocalSiteRateLimit,
     local_site_url_blocklist::LocalSiteUrlBlocklist,
+    local_user::LocalUser,
     mod_log::moderator::{
       ModRemoveComment,
       ModRemoveCommentForm,
@@ -59,7 +60,7 @@ use lemmy_db_views::{
   },
 };
 use lemmy_utils::{
-  email::{send_email, translations::Lang},
+  email::send_email,
   error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
   rate_limit::{ActionType, BucketConfig},
   settings::{
@@ -77,7 +78,6 @@ use lemmy_utils::{
 };
 use moka::future::Cache;
 use regex::{escape, Regex, RegexSet};
-use rosetta_i18n::{Language, LanguageId};
 use std::sync::LazyLock;
 use tracing::{warn, Instrument};
 use url::{ParseError, Url};
@@ -445,7 +445,7 @@ pub async fn send_password_reset_email(
     .email
     .clone()
     .ok_or(LemmyErrorType::EmailRequired)?;
-  let lang = get_interface_language(user);
+  let lang = &user.local_user.interface_i18n_language();
   let subject = &lang.password_reset_subject(&user.person.name);
   let protocol_and_hostname = settings.get_protocol_and_hostname();
   let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token);
@@ -461,13 +461,15 @@ pub async fn send_password_reset_email(
 
 /// Send a verification email
 pub async fn send_verification_email(
-  user: &LocalUserView,
+  local_site: &LocalSite,
+  local_user: &LocalUser,
+  person: &Person,
   new_email: &str,
   pool: &mut DbPool<'_>,
   settings: &Settings,
 ) -> LemmyResult<()> {
   let form = EmailVerificationForm {
-    local_user_id: user.local_user.id,
+    local_user_id: local_user.id,
     email: new_email.to_string(),
     verification_token: uuid::Uuid::new_v4().to_string(),
   };
@@ -478,29 +480,17 @@ pub async fn send_verification_email(
   );
   EmailVerification::create(pool, &form).await?;
 
-  let lang = get_interface_language(user);
+  let lang = local_user.interface_i18n_language();
   let subject = lang.verify_email_subject(&settings.hostname);
-  let body = lang.verify_email_body(&settings.hostname, &user.person.name, verify_link);
-  send_email(&subject, new_email, &user.person.name, &body, settings).await?;
-
-  Ok(())
-}
 
-pub fn get_interface_language(user: &LocalUserView) -> Lang {
-  lang_str_to_lang(&user.local_user.interface_language)
-}
-
-pub fn get_interface_language_from_settings(user: &LocalUserView) -> Lang {
-  lang_str_to_lang(&user.local_user.interface_language)
-}
+  // If an application is required, use a translation that includes that warning.
+  let body = if local_site.registration_mode == RegistrationMode::RequireApplication {
+    lang.verify_email_body_with_application(&settings.hostname, &person.name, verify_link)
+  } else {
+    lang.verify_email_body(&settings.hostname, &person.name, verify_link)
+  };
 
-#[allow(clippy::expect_used)]
-fn lang_str_to_lang(lang: &str) -> Lang {
-  let lang_id = LanguageId::new(lang);
-  Lang::from_language_id(&lang_id).unwrap_or_else(|| {
-    let en = LanguageId::new("en");
-    Lang::from_language_id(&en).expect("default language")
-  })
+  send_email(&subject, new_email, &person.name, &body, settings).await
 }
 
 pub fn local_site_rate_limit_to_rate_limit_config(
@@ -567,7 +557,7 @@ pub async fn send_application_approved_email(
     .email
     .clone()
     .ok_or(LemmyErrorType::EmailRequired)?;
-  let lang = get_interface_language(user);
+  let lang = &user.local_user.interface_i18n_language();
   let subject = lang.registration_approved_subject(&user.person.ap_id);
   let body = lang.registration_approved_body(&settings.hostname);
   send_email(&subject, email, &user.person.name, &body, settings).await
@@ -593,7 +583,7 @@ pub async fn send_new_applicant_email_to_admins(
       .email
       .clone()
       .ok_or(LemmyErrorType::EmailRequired)?;
-    let lang = get_interface_language_from_settings(admin);
+    let lang = &admin.local_user.interface_i18n_language();
     let subject = lang.new_application_subject(&settings.hostname, applicant_username);
     let body = lang.new_application_body(applications_link);
     send_email(&subject, email, &admin.person.name, &body, settings).await?;
@@ -615,7 +605,7 @@ pub async fn send_new_report_email_to_admins(
 
   for admin in &admins {
     if let Some(email) = &admin.local_user.email {
-      let lang = get_interface_language_from_settings(admin);
+      let lang = &admin.local_user.interface_i18n_language();
       let subject =
         lang.new_report_subject(&settings.hostname, reported_username, reporter_username);
       let body = lang.new_report_body(reports_link);
diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs
index fba8aedd85..ff89e52339 100644
--- a/crates/api_crud/src/private_message/create.rs
+++ b/crates/api_crud/src/private_message/create.rs
@@ -6,7 +6,6 @@ use lemmy_api_common::{
   send_activity::{ActivityChannel, SendActivityData},
   utils::{
     check_private_messages_enabled,
-    get_interface_language,
     get_url_blocklist,
     local_site_to_slur_regex,
     process_markdown,
@@ -72,7 +71,7 @@ pub async fn create_private_message(
   if view.recipient.local {
     let recipient_id = data.recipient_id;
     let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?;
-    let lang = get_interface_language(&local_recipient);
+    let lang = &local_recipient.local_user.interface_i18n_language();
     let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
     let sender_name = &local_user_view.person.name;
     let content = markdown_to_html(&content);
diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs
index 43a854e62c..a4d3c334b3 100644
--- a/crates/api_crud/src/user/create.rs
+++ b/crates/api_crud/src/user/create.rs
@@ -18,7 +18,6 @@ use lemmy_api_common::{
   },
 };
 use lemmy_db_schema::{
-  aggregates::structs::PersonAggregates,
   newtypes::{InstanceId, OAuthProviderId},
   source::{
     actor_language::SiteLanguage,
@@ -26,7 +25,6 @@ use lemmy_db_schema::{
     language::Language,
     local_site::LocalSite,
     local_user::{LocalUser, LocalUserInsertForm},
-    local_user_vote_display_mode::LocalUserVoteDisplayMode,
     oauth_account::{OAuthAccount, OAuthAccountInsertForm},
     oauth_provider::OAuthProvider,
     person::{Person, PersonInsertForm},
@@ -476,35 +474,32 @@ async fn create_local_user(
   Ok(inserted_local_user)
 }
 
+/// Returns true if email was sent.
 async fn send_verification_email_if_required(
   context: &Data<LemmyContext>,
   local_site: &LocalSite,
   local_user: &LocalUser,
   person: &Person,
 ) -> LemmyResult<bool> {
-  let mut sent = false;
-  if !local_user.admin && local_site.require_email_verification && !local_user.email_verified {
-    let local_user_view = LocalUserView {
-      local_user: local_user.clone(),
-      local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
-      person: person.clone(),
-      counts: PersonAggregates::default(),
-    };
+  let email = &local_user
+    .email
+    .clone()
+    .ok_or(LemmyErrorType::EmailRequired)?;
 
+  if !local_user.admin && local_site.require_email_verification && !local_user.email_verified {
     send_verification_email(
-      &local_user_view,
-      &local_user
-        .email
-        .clone()
-        .ok_or(LemmyErrorType::EmailRequired)?,
+      local_site,
+      local_user,
+      person,
+      email,
       &mut context.pool(),
       context.settings(),
     )
     .await?;
-
-    sent = true;
+    Ok(true)
+  } else {
+    Ok(false)
   }
-  Ok(sent)
 }
 
 fn validate_registration_answer(
diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs
index 4309f8d52b..44978b1903 100644
--- a/crates/db_schema/src/impls/local_user.rs
+++ b/crates/db_schema/src/impls/local_user.rs
@@ -29,7 +29,10 @@ use diesel::{
   QueryDsl,
 };
 use diesel_async::RunQueryDsl;
-use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
+use lemmy_utils::{
+  email::{lang_str_to_lang, translations::Lang},
+  error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
+};
 
 impl LocalUser {
   pub async fn create(
@@ -297,6 +300,10 @@ impl LocalUser {
       Err(LemmyErrorType::NotHigherMod)?
     }
   }
+
+  pub fn interface_i18n_language(&self) -> Lang {
+    lang_str_to_lang(&self.interface_language)
+  }
 }
 
 // TODO
diff --git a/crates/utils/src/email.rs b/crates/utils/src/email.rs
index aa06ba1b99..0806f2df28 100644
--- a/crates/utils/src/email.rs
+++ b/crates/utils/src/email.rs
@@ -10,7 +10,9 @@ use lettre::{
   AsyncTransport,
   Message,
 };
+use rosetta_i18n::{Language, LanguageId};
 use std::str::FromStr;
+use translations::Lang;
 use uuid::Uuid;
 
 pub mod translations {
@@ -93,3 +95,12 @@ pub async fn send_email(
 
   Ok(())
 }
+
+#[allow(clippy::expect_used)]
+pub fn lang_str_to_lang(lang: &str) -> Lang {
+  let lang_id = LanguageId::new(lang);
+  Lang::from_language_id(&lang_id).unwrap_or_else(|| {
+    let en = LanguageId::new("en");
+    Lang::from_language_id(&en).expect("default language")
+  })
+}
diff --git a/crates/utils/translations b/crates/utils/translations
index 0955855d96..9bf0a79cf8 160000
--- a/crates/utils/translations
+++ b/crates/utils/translations
@@ -1,1 +1,1 @@
-Subproject commit 0955855d9635f88f30d7c2aeaa864278c08c985b
+Subproject commit 9bf0a79cf87912d401534525dc9030f225e4cd4a


Test patch: