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(¶ms, 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(¶ms, 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}