1use super::input;
2use crate::client::admin::{PreparedSettingsUpdate, settings::UpdateGlobalSettings};
3use crate::{
4 client::WekanClientFactory,
5 command_result::*,
6 commands::{
7 authenticated::AuthenticatedContext,
8 client_error::{map_client_error, map_partial_mutation_client_error_with_not_found},
9 },
10 credentials::CredentialStore,
11 error::AppError,
12 redaction::Redactor,
13};
14use clap::{ArgGroup, Args, Subcommand};
15#[derive(Debug, Args)]
16pub struct SettingsArgs {
17 #[command(subcommand)]
18 pub command: SettingsCommand,
19}
20#[derive(Debug, Subcommand)]
21pub enum SettingsCommand {
22 Get,
24 Update(Box<UpdateArgs>),
26}
27#[derive(Debug, Args)]
28#[command(group(ArgGroup::new("setting_flags").args([
29 "disable_registration",
30 "disable_forgot_password",
31 "product_name",
32 "display_authentication_method",
33 "default_authentication_method",
34 "spinner_name",
35 "hide_logo",
36 "hide_card_counter_list",
37 "hide_board_member_list",
38 "custom_login_logo_image_url",
39 "custom_login_logo_link_url",
40 "custom_help_link_url",
41 "text_below_custom_login_logo",
42 "automatic_linked_url_schemes",
43 "custom_top_left_corner_logo_image_url",
44 "custom_top_left_corner_logo_link_url",
45 "custom_top_left_corner_logo_height",
46 "oidc_btn_text",
47 "mail_domain_name",
48 "legal_notice",
49 "custom_head_enabled",
50 "custom_head_meta_tags",
51 "custom_head_link_tags",
52 "custom_manifest_enabled",
53 "custom_manifest_content",
54 "custom_asset_links_enabled",
55 "custom_asset_links_content",
56 "accessibility_page_enabled",
57 "accessibility_title",
58 "accessibility_content",
59 "support_popup_text",
60 "support_page_enabled",
61 "support_page_public",
62 "support_title",
63 "support_page_text",
64]).multiple(true).conflicts_with("settings_source")), group(ArgGroup::new("update_input").args([
65 "settings_file",
66 "settings_stdin",
67 "disable_registration",
68 "disable_forgot_password",
69 "product_name",
70 "display_authentication_method",
71 "default_authentication_method",
72 "spinner_name",
73 "hide_logo",
74 "hide_card_counter_list",
75 "hide_board_member_list",
76 "custom_login_logo_image_url",
77 "custom_login_logo_link_url",
78 "custom_help_link_url",
79 "text_below_custom_login_logo",
80 "automatic_linked_url_schemes",
81 "custom_top_left_corner_logo_image_url",
82 "custom_top_left_corner_logo_link_url",
83 "custom_top_left_corner_logo_height",
84 "oidc_btn_text",
85 "mail_domain_name",
86 "legal_notice",
87 "custom_head_enabled",
88 "custom_head_meta_tags",
89 "custom_head_link_tags",
90 "custom_manifest_enabled",
91 "custom_manifest_content",
92 "custom_asset_links_enabled",
93 "custom_asset_links_content",
94 "accessibility_page_enabled",
95 "accessibility_title",
96 "accessibility_content",
97 "support_popup_text",
98 "support_page_enabled",
99 "support_page_public",
100 "support_title",
101 "support_page_text",
102]).required(true).multiple(true)), after_help = "Only the v11.06 REST allowlist is supported; SMTP/mailServer is excluded. Omitted fields remain unchanged. JSON source options conflict with all setting flags.")]
103pub struct UpdateArgs {
104 #[command(flatten)]
105 source: input::SourceArgs,
106 #[arg(long = "disable-registration")]
108 disable_registration: Option<bool>,
109 #[arg(long = "disable-forgot-password")]
111 disable_forgot_password: Option<bool>,
112 #[arg(long = "product-name")]
114 product_name: Option<String>,
115 #[arg(long = "display-authentication-method")]
117 display_authentication_method: Option<bool>,
118 #[arg(long = "default-authentication-method")]
120 default_authentication_method: Option<String>,
121 #[arg(long = "spinner-name")]
123 spinner_name: Option<String>,
124 #[arg(long = "hide-logo")]
126 hide_logo: Option<bool>,
127 #[arg(long = "hide-card-counter-list")]
129 hide_card_counter_list: Option<bool>,
130 #[arg(long = "hide-board-member-list")]
132 hide_board_member_list: Option<bool>,
133 #[arg(long = "custom-login-logo-image-url")]
135 custom_login_logo_image_url: Option<String>,
136 #[arg(long = "custom-login-logo-link-url")]
138 custom_login_logo_link_url: Option<String>,
139 #[arg(long = "custom-help-link-url")]
141 custom_help_link_url: Option<String>,
142 #[arg(long = "text-below-custom-login-logo")]
144 text_below_custom_login_logo: Option<String>,
145 #[arg(long = "automatic-linked-url-schemes")]
147 automatic_linked_url_schemes: Option<String>,
148 #[arg(long = "custom-top-left-corner-logo-image-url")]
150 custom_top_left_corner_logo_image_url: Option<String>,
151 #[arg(long = "custom-top-left-corner-logo-link-url")]
153 custom_top_left_corner_logo_link_url: Option<String>,
154 #[arg(long = "custom-top-left-corner-logo-height")]
156 custom_top_left_corner_logo_height: Option<String>,
157 #[arg(long = "oidc-btn-text")]
159 oidc_btn_text: Option<String>,
160 #[arg(long = "mail-domain-name")]
162 mail_domain_name: Option<String>,
163 #[arg(long = "legal-notice")]
165 legal_notice: Option<String>,
166 #[arg(long = "custom-head-enabled")]
168 custom_head_enabled: Option<bool>,
169 #[arg(long = "custom-head-meta-tags")]
171 custom_head_meta_tags: Option<String>,
172 #[arg(long = "custom-head-link-tags")]
174 custom_head_link_tags: Option<String>,
175 #[arg(long = "custom-manifest-enabled")]
177 custom_manifest_enabled: Option<bool>,
178 #[arg(long = "custom-manifest-content")]
180 custom_manifest_content: Option<String>,
181 #[arg(long = "custom-asset-links-enabled")]
183 custom_asset_links_enabled: Option<bool>,
184 #[arg(long = "custom-asset-links-content")]
186 custom_asset_links_content: Option<String>,
187 #[arg(long = "accessibility-page-enabled")]
189 accessibility_page_enabled: Option<bool>,
190 #[arg(long = "accessibility-title")]
192 accessibility_title: Option<String>,
193 #[arg(long = "accessibility-content")]
195 accessibility_content: Option<String>,
196 #[arg(long = "support-popup-text")]
198 support_popup_text: Option<String>,
199 #[arg(long = "support-page-enabled")]
201 support_page_enabled: Option<bool>,
202 #[arg(long = "support-page-public")]
204 support_page_public: Option<bool>,
205 #[arg(long = "support-title")]
207 support_title: Option<String>,
208 #[arg(long = "support-page-text")]
210 support_page_text: Option<String>,
211}
212pub(crate) async fn execute(
213 command: SettingsCommand,
214 factory: &WekanClientFactory,
215 store: &dyn CredentialStore,
216) -> Result<CommandSuccess, AppError> {
217 match command {
218 SettingsCommand::Get => {
219 let context = AuthenticatedContext::load(factory, store)?;
220 let document = context
221 .client()
222 .global_settings(context.record().token())
223 .await
224 .map_err(|e| {
225 map_client_error(
226 e,
227 &Redactor::with_secret(context.record().token()),
228 "admin settings get",
229 false,
230 )
231 })?;
232 Ok(CommandSuccess::GlobalSettings(Box::new(
233 GlobalSettingsSuccess {
234 settings: document.into(),
235 },
236 )))
237 }
238 SettingsCommand::Update(args) => {
239 let request = if let Some(request) = args.source.read::<UpdateGlobalSettings>().await? {
240 request
241 } else {
242 UpdateGlobalSettings {
243 disable_registration: args.disable_registration,
244 disable_forgot_password: args.disable_forgot_password,
245 product_name: args.product_name,
246 display_authentication_method: args.display_authentication_method,
247 default_authentication_method: args.default_authentication_method,
248 spinner_name: args.spinner_name,
249 hide_logo: args.hide_logo,
250 hide_card_counter_list: args.hide_card_counter_list,
251 hide_board_member_list: args.hide_board_member_list,
252 custom_login_logo_image_url: args.custom_login_logo_image_url,
253 custom_login_logo_link_url: args.custom_login_logo_link_url,
254 custom_help_link_url: args.custom_help_link_url,
255 text_below_custom_login_logo: args.text_below_custom_login_logo,
256 automatic_linked_url_schemes: args.automatic_linked_url_schemes,
257 custom_top_left_corner_logo_image_url: args
258 .custom_top_left_corner_logo_image_url,
259 custom_top_left_corner_logo_link_url: args.custom_top_left_corner_logo_link_url,
260 custom_top_left_corner_logo_height: args.custom_top_left_corner_logo_height,
261 oidc_btn_text: args.oidc_btn_text,
262 mail_domain_name: args.mail_domain_name,
263 legal_notice: args.legal_notice,
264 custom_head_enabled: args.custom_head_enabled,
265 custom_head_meta_tags: args.custom_head_meta_tags,
266 custom_head_link_tags: args.custom_head_link_tags,
267 custom_manifest_enabled: args.custom_manifest_enabled,
268 custom_manifest_content: args.custom_manifest_content,
269 custom_asset_links_enabled: args.custom_asset_links_enabled,
270 custom_asset_links_content: args.custom_asset_links_content,
271 accessibility_page_enabled: args.accessibility_page_enabled,
272 accessibility_title: args.accessibility_title,
273 accessibility_content: args.accessibility_content,
274 support_popup_text: args.support_popup_text,
275 support_page_enabled: args.support_page_enabled,
276 support_page_public: args.support_page_public,
277 support_title: args.support_title,
278 support_page_text: args.support_page_text,
279 }
280 };
281 let submitted_fields = submitted_fields(&request);
282 input::validate_fields(&submitted_fields)?;
283 let request = PreparedSettingsUpdate::new(&request)
284 .map_err(|error| AppError::invalid_input(error.to_string()))?;
285 let context = AuthenticatedContext::load(factory, store)?;
286 let document = context
287 .client()
288 .update_global_settings(request, context.record().token())
289 .await
290 .map_err(|e| {
291 map_partial_mutation_client_error_with_not_found(
292 e,
293 &Redactor::with_secret(context.record().token()),
294 "admin settings update",
295 "the Wekan settings document was not found",
296 )
297 })?;
298 Ok(CommandSuccess::GlobalSettingsUpdated(Box::new(
299 GlobalSettingsUpdateSuccess {
300 settings: document.into(),
301 submitted_fields,
302 },
303 )))
304 }
305 }
306}
307
308fn submitted_fields(request: &UpdateGlobalSettings) -> Vec<GlobalSettingField> {
309 let mut fields = Vec::new();
310 if request.disable_registration.is_some() {
311 fields.push(GlobalSettingField::DisableRegistration);
312 }
313 if request.disable_forgot_password.is_some() {
314 fields.push(GlobalSettingField::DisableForgotPassword);
315 }
316 if request.product_name.is_some() {
317 fields.push(GlobalSettingField::ProductName);
318 }
319 if request.display_authentication_method.is_some() {
320 fields.push(GlobalSettingField::DisplayAuthenticationMethod);
321 }
322 if request.default_authentication_method.is_some() {
323 fields.push(GlobalSettingField::DefaultAuthenticationMethod);
324 }
325 if request.spinner_name.is_some() {
326 fields.push(GlobalSettingField::SpinnerName);
327 }
328 if request.hide_logo.is_some() {
329 fields.push(GlobalSettingField::HideLogo);
330 }
331 if request.hide_card_counter_list.is_some() {
332 fields.push(GlobalSettingField::HideCardCounterList);
333 }
334 if request.hide_board_member_list.is_some() {
335 fields.push(GlobalSettingField::HideBoardMemberList);
336 }
337 if request.custom_login_logo_image_url.is_some() {
338 fields.push(GlobalSettingField::CustomLoginLogoImageUrl);
339 }
340 if request.custom_login_logo_link_url.is_some() {
341 fields.push(GlobalSettingField::CustomLoginLogoLinkUrl);
342 }
343 if request.custom_help_link_url.is_some() {
344 fields.push(GlobalSettingField::CustomHelpLinkUrl);
345 }
346 if request.text_below_custom_login_logo.is_some() {
347 fields.push(GlobalSettingField::TextBelowCustomLoginLogo);
348 }
349 if request.automatic_linked_url_schemes.is_some() {
350 fields.push(GlobalSettingField::AutomaticLinkedUrlSchemes);
351 }
352 if request.custom_top_left_corner_logo_image_url.is_some() {
353 fields.push(GlobalSettingField::CustomTopLeftCornerLogoImageUrl);
354 }
355 if request.custom_top_left_corner_logo_link_url.is_some() {
356 fields.push(GlobalSettingField::CustomTopLeftCornerLogoLinkUrl);
357 }
358 if request.custom_top_left_corner_logo_height.is_some() {
359 fields.push(GlobalSettingField::CustomTopLeftCornerLogoHeight);
360 }
361 if request.oidc_btn_text.is_some() {
362 fields.push(GlobalSettingField::OidcBtnText);
363 }
364 if request.mail_domain_name.is_some() {
365 fields.push(GlobalSettingField::MailDomainName);
366 }
367 if request.legal_notice.is_some() {
368 fields.push(GlobalSettingField::LegalNotice);
369 }
370 if request.custom_head_enabled.is_some() {
371 fields.push(GlobalSettingField::CustomHeadEnabled);
372 }
373 if request.custom_head_meta_tags.is_some() {
374 fields.push(GlobalSettingField::CustomHeadMetaTags);
375 }
376 if request.custom_head_link_tags.is_some() {
377 fields.push(GlobalSettingField::CustomHeadLinkTags);
378 }
379 if request.custom_manifest_enabled.is_some() {
380 fields.push(GlobalSettingField::CustomManifestEnabled);
381 }
382 if request.custom_manifest_content.is_some() {
383 fields.push(GlobalSettingField::CustomManifestContent);
384 }
385 if request.custom_asset_links_enabled.is_some() {
386 fields.push(GlobalSettingField::CustomAssetLinksEnabled);
387 }
388 if request.custom_asset_links_content.is_some() {
389 fields.push(GlobalSettingField::CustomAssetLinksContent);
390 }
391 if request.accessibility_page_enabled.is_some() {
392 fields.push(GlobalSettingField::AccessibilityPageEnabled);
393 }
394 if request.accessibility_title.is_some() {
395 fields.push(GlobalSettingField::AccessibilityTitle);
396 }
397 if request.accessibility_content.is_some() {
398 fields.push(GlobalSettingField::AccessibilityContent);
399 }
400 if request.support_popup_text.is_some() {
401 fields.push(GlobalSettingField::SupportPopupText);
402 }
403 if request.support_page_enabled.is_some() {
404 fields.push(GlobalSettingField::SupportPageEnabled);
405 }
406 if request.support_page_public.is_some() {
407 fields.push(GlobalSettingField::SupportPagePublic);
408 }
409 if request.support_title.is_some() {
410 fields.push(GlobalSettingField::SupportTitle);
411 }
412 if request.support_page_text.is_some() {
413 fields.push(GlobalSettingField::SupportPageText);
414 }
415 fields
416}