actix_admin/routes/
create_or_edit_post.rs

1use super::helpers::{add_default_context, SearchParams};
2use super::{add_auth_context, render_unauthorized, user_can_access_page};
3use super::Params;
4use crate::ActixAdminError;
5use crate::ActixAdminNotification;
6use crate::{prelude::*, ActixAdminErrorType};
7use actix_multipart::Multipart;
8use actix_multipart::MultipartError;
9use actix_session::Session;
10use actix_web::http::header;
11use actix_web::{error, web, Error, HttpRequest, HttpResponse};
12use sea_orm::DatabaseConnection;
13use std::collections::HashMap;
14use tera::Context;
15
16pub async fn create_post<E: ActixAdminViewModelTrait>(
17    session: Session,
18    req: HttpRequest,
19    data: web::Data<ActixAdmin>,
20    db: web::Data<DatabaseConnection>,
21    payload: Multipart,
22) -> Result<HttpResponse, Error> {
23    let actix_admin = data.get_ref();
24    let model = ActixAdminModel::create_from_payload(
25        None,
26        payload,
27        &format!(
28            "{}/{}",
29            actix_admin.configuration.file_upload_directory,
30            E::get_entity_name()
31        ),
32    )
33    .await;
34    create_or_edit_post::<E>(&session, req, db, model, None, actix_admin).await
35}
36
37pub async fn edit_post<E: ActixAdminViewModelTrait>(
38    session: Session,
39    req: HttpRequest,
40    data: web::Data<ActixAdmin>,
41    db: web::Data<DatabaseConnection>,
42    payload: Multipart,
43    id: web::Path<i32>,
44) -> Result<HttpResponse, Error> {
45    let actix_admin = &data.get_ref();
46    let id = id.into_inner();
47    let model = ActixAdminModel::create_from_payload(
48        Some(id), payload,
49        &format!(
50            "{}/{}",
51            actix_admin.configuration.file_upload_directory,
52            E::get_entity_name()
53        ),
54    )
55    .await;
56    create_or_edit_post::<E>(&session, req, db, model, Some(id), actix_admin).await
57}
58
59pub async fn create_or_edit_post<E: ActixAdminViewModelTrait>(
60    session: &Session,
61    req: HttpRequest,
62    db: web::Data<DatabaseConnection>,
63    model_res: Result<ActixAdminModel, MultipartError>,
64    id: Option<i32>,
65    actix_admin: &ActixAdmin,
66) -> Result<HttpResponse, Error> {
67    let entity_name = E::get_entity_name();
68
69    let view_model = actix_admin.view_models.get(&entity_name).unwrap();
70    let mut errors: Vec<ActixAdminError> = Vec::new();
71
72    if !user_can_access_page(&session, actix_admin, view_model) {
73        let mut ctx = Context::new();
74        ctx.insert("render_partial", &true);
75        return render_unauthorized(&ctx, &actix_admin);
76    }
77    let db = db.get_ref();
78
79    let mut model = model_res.unwrap();
80    let _ = E::validate_entity(&mut model, db).await;
81
82    if model.has_errors() {
83        let error = ActixAdminError {
84            ty: ActixAdminErrorType::ValidationErrors,
85            msg: "".to_owned(),
86        };
87        errors.push(error);
88        render_form::<E>(
89            session,
90            req,
91            actix_admin,
92            view_model,
93            &db,
94            entity_name,
95            &model,
96            errors,
97        )
98        .await
99    } else {
100        let tenant_ref = actix_admin
101            .configuration
102            .user_tenant_ref
103            .map_or(None, |f| f(&session));
104
105        let res = match id {
106            Some(id) => E::edit_entity(db, id, model.clone(), tenant_ref).await,
107            None => E::create_entity(db, model.clone(), tenant_ref).await,
108        };
109
110        match res {
111            Ok(model) => {
112                let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
113
114                let entity_name = E::get_entity_name();
115
116                let search_params = SearchParams::from_params(&params, view_model);
117
118                if view_model.inline_edit {
119                    let mut ctx = Context::new();
120
121                    ctx.insert("entity", &model);
122
123                    add_auth_context(&session, actix_admin, &mut ctx);
124                    add_default_context(&mut ctx, req, view_model, entity_name, actix_admin, Vec::new(), &search_params);
125
126                    let body = actix_admin
127                        .tera
128                        .render("list/row.html", &ctx)
129                        .map_err(|err| { error::ErrorInternalServerError(err) })?;
130                    Ok(HttpResponse::Ok().content_type("text/html").body(body))
131                } else {
132                    Ok(HttpResponse::SeeOther()
133                .append_header((
134                    header::LOCATION,
135                    format!("{0}/{1}/list?{2}", actix_admin.configuration.base_path, entity_name, search_params.to_query_string()),
136                ))
137                .finish())
138                }
139            }
140            Err(e) => {
141                errors.push(e);
142                render_form::<E>(
143                    session,
144                    req,
145                    actix_admin,
146                    view_model,
147                    &db,
148                    entity_name,
149                    &model,
150                    errors,
151                )
152                .await
153            }
154        }
155    }
156}
157
158async fn render_form<E: ActixAdminViewModelTrait>(
159    session: &Session,
160    req: HttpRequest,
161    actix_admin: &ActixAdmin,
162    view_model: &ActixAdminViewModel,
163    db: &&sea_orm::DatabaseConnection,
164    entity_name: String,
165    model: &ActixAdminModel,
166    errors: Vec<ActixAdminError>,
167) -> Result<HttpResponse, Error> {
168    let mut ctx = Context::new();
169
170    let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
171
172    let tenant_ref = actix_admin
173        .configuration
174        .user_tenant_ref
175        .map_or(None, |f| f(&session));
176
177    ctx.insert("select_lists", &E::get_select_lists(db, tenant_ref).await?);
178    ctx.insert("model", model);
179
180    let notifications: Vec<ActixAdminNotification> = errors
181    .into_iter()
182    .map(|err| ActixAdminNotification::from(err))
183    .collect();
184
185    add_auth_context(&session, actix_admin, &mut ctx);
186
187    let search_params = SearchParams::from_params(&params, view_model);
188    add_default_context(&mut ctx, req, view_model, entity_name, actix_admin, notifications, &search_params);
189
190    let template_path = match (view_model.inline_edit, model.primary_key.is_some()) {
191        (true, true) => "create_or_edit/inline.html",
192        (_, _) => "create_or_edit.html",
193    };
194    let body = actix_admin
195        .tera
196        .render(template_path, &ctx)
197        .map_err(|err| error::ErrorInternalServerError(err))?;
198    Ok(HttpResponse::Ok().content_type("text/html").body(body))
199}
200
201#[doc(hidden)]
202impl From<String> for ActixAdminModel {
203    fn from(string: String) -> Self {
204        let mut hashmap = HashMap::new();
205        let key_values: Vec<&str> = string.split('&').collect();
206        for key_value in key_values {
207            if !key_value.is_empty() {
208                let mut iter = key_value.splitn(2, '=');
209                hashmap.insert(
210                    iter.next().unwrap().to_string().replace("%3A", ":"),
211                    iter.next().unwrap().to_string().replace("%3A", ":"),
212                );
213            }
214        }
215
216        ActixAdminModel {
217            primary_key: None,
218            values: hashmap,
219            errors: HashMap::new(),
220            custom_errors: HashMap::new(),
221            fk_values: HashMap::new(),
222            display_name: None
223        }
224    }
225}