diff --git a/server/core/src/https/views/profile.rs b/server/core/src/https/views/profile.rs index a3678010f..770ead2b2 100644 --- a/server/core/src/https/views/profile.rs +++ b/server/core/src/https/views/profile.rs @@ -10,7 +10,7 @@ use askama::Template; use askama_axum::IntoResponse; use axum::extract::{Query, State}; use axum::http::Uri; -use axum::response::Response; +use axum::response::{Redirect, Response}; use axum::Extension; use axum_extra::extract::cookie::CookieJar; use axum_extra::extract::Form; @@ -60,6 +60,18 @@ pub(crate) struct SaveProfileQuery { primary_email_index: u16, } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub(crate) struct CommitSaveProfileQuery { + #[serde(rename = "account_name")] + account_name: String, + #[serde(rename = "display_name")] + display_name: String, + #[serde(rename = "emails[]")] + emails: Vec<String>, + #[serde(rename = "new_primary_mail")] + new_primary_mail: Option<String> +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub(crate) struct ProfileAttributes { account_name: String, @@ -73,7 +85,9 @@ struct ProfileChangesPartialView { menu_active_item: ProfileMenuItems, can_rw: bool, person: ScimPerson, + primary_mail: Option<String>, new_attrs: ProfileAttributes, + new_primary_mail: Option<String>, } #[derive(Template, Clone)] @@ -109,7 +123,7 @@ pub(crate) async fn view_profile_get( &kopid, client_auth_info.clone(), ) - .await?; + .await?; let time = time::OffsetDateTime::now_utc() + time::Duration::new(60, 0); @@ -149,8 +163,7 @@ pub(crate) async fn view_profile_diff_start_save_post( state, &kopid, client_auth_info.clone(), - ) - .await?; + ).await?; let primary_index = query .emails_indexes @@ -166,23 +179,28 @@ pub(crate) async fn view_profile_diff_start_save_post( value: email.to_string(), }) .collect(); + let old_primary_mail = scim_person.mails.iter() + .find(|sm| sm.primary) + .map(|sm| sm.value.clone()); + let profile_view = ProfileChangesPartialView { menu_active_item: ProfileMenuItems::UserProfile, can_rw, person: scim_person, + primary_mail: old_primary_mail, new_attrs: ProfileAttributes { account_name: query.account_name, display_name: query.display_name, emails: new_mails, }, + new_primary_mail: query.emails.get(primary_index).cloned(), }; Ok(( HxPushUrl(Uri::from_static("/ui/profile/diff")), profile_view, - ) - .into_response()) + ).into_response()) } pub(crate) async fn view_profile_diff_confirm_save_post( @@ -191,14 +209,14 @@ pub(crate) async fn view_profile_diff_confirm_save_post( VerifiedClientInformation(client_auth_info): VerifiedClientInformation, DomainInfo(domain_info): DomainInfo, // Form must be the last parameter because it consumes the request body - Form(mut new_attrs): Form<ProfileAttributes>, + Form(query): Form<CommitSaveProfileQuery>, ) -> axum::response::Result<Response> { let uat: UserAuthToken = state .qe_r_ref .handle_whoami_uat(client_auth_info.clone(), kopid.eventid) .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone())) .await?; - dbg!(&new_attrs); + dbg!(&query); let filter = filter_all!(f_and!([f_id(uat.uuid.to_string().as_str())])); @@ -208,24 +226,26 @@ pub(crate) async fn view_profile_diff_confirm_save_post( client_auth_info.clone(), uat.uuid.to_string(), ATTR_DISPLAYNAME.to_string(), - vec![new_attrs.display_name], + vec![query.display_name], filter.clone(), kopid.eventid, ) .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone())) .await?; - new_attrs - .emails - .sort_by_key(|sm| if sm.primary { 0 } else { 1 }); - let email_addresses = new_attrs.emails.into_iter().map(|sm| sm.value).collect(); + + let mut emails = query.emails; + if let Some(primary) = query.new_primary_mail { + emails.insert(0, primary); + } + state .qe_w_ref .handle_setattribute( client_auth_info.clone(), uat.uuid.to_string(), ATTR_MAIL.to_string(), - email_addresses, + emails, filter.clone(), kopid.eventid, ) @@ -266,9 +286,9 @@ pub(crate) async fn view_profile_diff_confirm_save_post( VerifiedClientInformation(client_auth_info), DomainInfo(domain_info), ) - .await + .await { - Ok(pv) => Ok(pv.into_response()), + Ok(_) => Ok(Redirect::to(Urls::Profile.as_ref()).into_response()), Err(e) => Ok(e.into_response()), } } @@ -331,5 +351,5 @@ pub(crate) async fn view_profile_unlock_get( Urls::Profile.as_ref(), display_ctx, ) - .await) + .await) } diff --git a/server/core/templates/user_settings/profile_changes_partial.html b/server/core/templates/user_settings/profile_changes_partial.html index 078c6f134..43229b24e 100644 --- a/server/core/templates/user_settings/profile_changes_partial.html +++ b/server/core/templates/user_settings/profile_changes_partial.html @@ -13,10 +13,15 @@ Profile Difference <input type="hidden" name="display_name" value="(( new_attrs.display_name ))"/> <!-- <input type="hidden" name="legal_name" value=" new_attrs.legal_name "/>--> (% for email in new_attrs.emails %) - <input type="hidden" name="emails[]" value="(( email.value ))"/> + (% if !email.primary %) + <input type="hidden" name="emails[]" value="(( email.value ))"/> + (% endif %) (% endfor %) + (% if let Some(new_primary_mail) = new_primary_mail %) + <input type="hidden" name="new_primary_mail" value="(( new_primary_mail ))"/> + (% endif %) - <table class="table table-bordered table-responsive"> + <table class="table table-bordered overflow-x-scroll"> <thead> <tr> <th scope="col">Attribute</th> @@ -39,32 +44,44 @@ Profile Difference <td class="text-break">(( new_attrs.display_name ))</td> </tr> (% endif %) - - - <!-- TODO: List new items with +, same items with . --> - <tr> - <th scope="row">Emails</th> - <td class="text-break"> - <ul> + <!-- TODO: List new items with +, same items with . --> + <tr> + <th scope="row">Primary Emails</th> + <td class="text-nowrap"> + (( primary_mail.clone().unwrap_or("none".to_string()) )) + </td> + <td class="text-nowrap"> + (( new_primary_mail.clone().unwrap_or("none".to_string()) )) + </td> + </tr> + <tr> + <th scope="row">Secondary Emails</th> + <td class="text-break"> + <ul class="ps-3"> (% for email in person.mails %) - <li>(( email.value ))</li> + (% if !email.primary %) + <li class="text-nowrap">(( email.value ))</li> + (% endif %) (% endfor %) - </ul> - </td> - <td class="text-break"> - <ul> + </ul> + </td> + <td class="text-break"> + <ul class="ps-3"> (% for email in new_attrs.emails %) - <li>(( email.value ))</li> + (% if !email.primary %) + <li class="text-nowrap">(( email.value ))</li> + (% endif %) (% endfor %) - </ul> - </td> - </tr> + </ul> + </td> + </tr> + </table> <!-- Edit button --> <div class="pt-4" hx-target="#user_settings_container" hx-swap="outerHTML"> (% if can_rw %) - <button class="btn btn-danger" type="button" hx-get="/ui/profile" hx-target="#user_settings_container" hx-swap="outerHTML">Discard Changes</button> - <button class="btn btn-primary" type="button" hx-post="/ui/api/user_settings/confirm_profile" hx-target="#user_settings_container" hx-swap="outerHTML">Confirm Changes</button> + <button class="btn btn-danger" type="button" hx-get="/ui/profile" hx-target="body" hx-swap="outerHTML">Discard Changes</button> + <button class="btn btn-primary" type="button" hx-post="/ui/api/user_settings/confirm_profile" hx-target="body" hx-swap="outerHTML">Confirm Changes</button> (% else %) <a href="/ui/profile/unlock" hx-boost="false"> <!-- TODO: at the moment, session expiring here means progress is lost. Do we just show an error screen ? We can't pass the update state through the reauth session, and we don't have profile update sessions like cred update. --> diff --git a/server/core/templates/user_settings_profile_partial.html b/server/core/templates/user_settings_profile_partial.html index 0f170280b..24a788b2d 100644 --- a/server/core/templates/user_settings_profile_partial.html +++ b/server/core/templates/user_settings_profile_partial.html @@ -23,7 +23,7 @@ Profile <form id="user_settings_container" class="needs-validation" hx-post="/ui/api/user_settings/edit_profile" - hx-target="#user_settings_container" + hx-target="#main" hx-swap="outerHTML" hx-validate="true" hx-ext="bs-validation"