use super::session::{create_user_request, request}; use crate::model::{classroom_user::ClassroomPersonCreate, person::PersonLink}; use scraper::{Html, Selector}; const CREATION_ERR: &str = "Creation successful, but linking failed"; /// Creates an online classroom user pub async fn create(data: &ClassroomPersonCreate) -> Result<(), String> { let sec_token = get_form_sec_token().await?; let body = get_request_body( &data.person_surnames, &data.person_names, &data.person_email, &data.person_username, &data.person_password, &data.person_expiration_date, &sec_token, ); // Do create let response = create_user_request("/main/admin/user_add.php".into(), body).await?; if response.is_empty() { // User created successfully. // Have to find its user_id, and link it to the person let users = super::user::get_users_impl(data.person_username.clone()).await; match users { Ok(user) if user.len() == 1 => { let user_id: i32 = user[0].user_id.parse().or_else(|err| { Err(format!( "{}: Error parsing user_id: {:?}", CREATION_ERR, err )) })?; let result = PersonLink { person_id: data.person_id, person_classroom_id: user_id, } .insert() .await; match result { Ok(_) => return Ok(()), Err(reason) => return Err(format!("{}: {}", CREATION_ERR, reason)), } } Ok(u) if u.is_empty() => { return Err(format!( "{}: No users with username {} found", CREATION_ERR, data.person_username )) } Ok(_) => return Err(format!("{}: More than 1 user found", CREATION_ERR)), Err(reason) => return Err(format!("{}: {}", CREATION_ERR, reason)), } } Err("Expected empty body, found something else".into()) } async fn get_form_sec_token() -> Result { let creation_form = request("/main/admin/user_add.php".into()).await?; let sec_token_selector = Selector::parse("#user_add_sec_token") .or_else(|err| Err(format!("Error creating sec_token selector: {:?}", err)))?; let fragment = Html::parse_document(&creation_form); let input_element = match fragment.select(&sec_token_selector).next() { Some(el) => el, None => return Err(format!("Error selecting sec_token element. Not found")), }; let sec_token_value = match input_element.value().attr("value") { Some(val) => val, None => { return Err(format!( "Error getting sec_token value from input. Not found" )) } }; Ok(sec_token_value.into()) } fn get_request_body( surnames: &String, names: &String, email: &String, username: &String, password: &String, expiration_date: &String, sec_token: &String, ) -> String { format!( r#" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="lastname" {surnames} -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="firstname" {names} -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="official_code" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="email" {email} -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="phone" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="picture"; filename="" Content-Type: application/octet-stream -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="username" {username} -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="password[password_auto]" 0 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="password[password]" {password} -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="status" 5 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="admin[platform_admin]" 0 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="language" spanish -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="mail[send_mail]" 1 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="radio_expiration_date" 1 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="expiration_date" {expiration_date} 23:59 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="active" 1 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_legal_accept" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_already_logged_in" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_update_type" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_rssfeeds" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_dashboard" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_timezone" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_mail_notify_invitation" 1 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_mail_notify_message" 1 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_mail_notify_group_message" 1 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_user_chat_status" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_google_calendar_url" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_captcha_blocked_until_date" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_skype" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_linkedin_url" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_request_for_legal_agreement_consent_removal_justification" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_request_for_delete_account_justification" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_request_for_legal_agreement_consent_removal" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="extra_request_for_delete_account" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="submit" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="_qf__user_add" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="MAX_FILE_SIZE" 536870912 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="picture_crop_result" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="picture_crop_image_base_64" -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="item_id" 0 -----------------------------83919643214156711801978607619 Content-Disposition: form-data; name="sec_token" {sec_token} -----------------------------83919643214156711801978607619-- "# ) }