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_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 v12.57 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 )
230 })?;
231 Ok(CommandSuccess::GlobalSettings(Box::new(
232 GlobalSettingsSuccess {
233 settings: document.into(),
234 },
235 )))
236 }
237 SettingsCommand::Update(args) => {
238 let request = if let Some(request) = args.source.read::<UpdateGlobalSettings>().await? {
239 request
240 } else {
241 UpdateGlobalSettings {
242 disable_registration: args.disable_registration.map(Some),
243 disable_forgot_password: args.disable_forgot_password.map(Some),
244 product_name: args.product_name.map(Some),
245 display_authentication_method: args.display_authentication_method.map(Some),
246 default_authentication_method: args.default_authentication_method,
247 spinner_name: args.spinner_name.map(Some),
248 hide_logo: args.hide_logo.map(Some),
249 hide_card_counter_list: args.hide_card_counter_list.map(Some),
250 hide_board_member_list: args.hide_board_member_list.map(Some),
251 custom_login_logo_image_url: args.custom_login_logo_image_url.map(Some),
252 custom_login_logo_link_url: args.custom_login_logo_link_url.map(Some),
253 custom_help_link_url: args.custom_help_link_url.map(Some),
254 text_below_custom_login_logo: args.text_below_custom_login_logo.map(Some),
255 automatic_linked_url_schemes: args.automatic_linked_url_schemes.map(Some),
256 custom_top_left_corner_logo_image_url: args
257 .custom_top_left_corner_logo_image_url
258 .map(Some),
259 custom_top_left_corner_logo_link_url: args
260 .custom_top_left_corner_logo_link_url
261 .map(Some),
262 custom_top_left_corner_logo_height: args
263 .custom_top_left_corner_logo_height
264 .map(Some),
265 oidc_btn_text: args.oidc_btn_text.map(Some),
266 mail_domain_name: args.mail_domain_name.map(Some),
267 legal_notice: args.legal_notice.map(Some),
268 custom_head_enabled: args.custom_head_enabled.map(Some),
269 custom_head_meta_tags: args.custom_head_meta_tags.map(Some),
270 custom_head_link_tags: args.custom_head_link_tags.map(Some),
271 custom_manifest_enabled: args.custom_manifest_enabled.map(Some),
272 custom_manifest_content: args.custom_manifest_content.map(Some),
273 custom_asset_links_enabled: args.custom_asset_links_enabled.map(Some),
274 custom_asset_links_content: args.custom_asset_links_content.map(Some),
275 accessibility_page_enabled: args.accessibility_page_enabled.map(Some),
276 accessibility_title: args.accessibility_title.map(Some),
277 accessibility_content: args.accessibility_content.map(Some),
278 support_popup_text: args.support_popup_text.map(Some),
279 support_page_enabled: args.support_page_enabled.map(Some),
280 support_page_public: args.support_page_public.map(Some),
281 support_title: args.support_title.map(Some),
282 support_page_text: args.support_page_text.map(Some),
283 }
284 };
285 let submitted_fields = submitted_fields(&request);
286 input::validate_fields(&submitted_fields)?;
287 let request = PreparedSettingsUpdate::new(&request)
288 .map_err(|error| AppError::invalid_input(error.to_string()))?;
289 let context = AuthenticatedContext::load(factory, store)?;
290 let document = context
291 .client()
292 .update_global_settings(request, context.record().token())
293 .await
294 .map_err(|e| {
295 map_client_error_with_not_found(
296 e,
297 &Redactor::with_secret(context.record().token()),
298 "admin settings update",
299 "the Wekan settings document was not found",
300 )
301 })?;
302 Ok(CommandSuccess::GlobalSettingsUpdated(Box::new(
303 GlobalSettingsUpdateSuccess {
304 settings: document.into(),
305 submitted_fields,
306 },
307 )))
308 }
309 }
310}
311
312fn submitted_fields(request: &UpdateGlobalSettings) -> Vec<GlobalSettingField> {
313 let mut fields = Vec::new();
314 if request.disable_registration.is_some() {
315 fields.push(GlobalSettingField::DisableRegistration);
316 }
317 if request.disable_forgot_password.is_some() {
318 fields.push(GlobalSettingField::DisableForgotPassword);
319 }
320 if request.product_name.is_some() {
321 fields.push(GlobalSettingField::ProductName);
322 }
323 if request.display_authentication_method.is_some() {
324 fields.push(GlobalSettingField::DisplayAuthenticationMethod);
325 }
326 if request.default_authentication_method.is_some() {
327 fields.push(GlobalSettingField::DefaultAuthenticationMethod);
328 }
329 if request.spinner_name.is_some() {
330 fields.push(GlobalSettingField::SpinnerName);
331 }
332 if request.hide_logo.is_some() {
333 fields.push(GlobalSettingField::HideLogo);
334 }
335 if request.hide_card_counter_list.is_some() {
336 fields.push(GlobalSettingField::HideCardCounterList);
337 }
338 if request.hide_board_member_list.is_some() {
339 fields.push(GlobalSettingField::HideBoardMemberList);
340 }
341 if request.custom_login_logo_image_url.is_some() {
342 fields.push(GlobalSettingField::CustomLoginLogoImageUrl);
343 }
344 if request.custom_login_logo_link_url.is_some() {
345 fields.push(GlobalSettingField::CustomLoginLogoLinkUrl);
346 }
347 if request.custom_help_link_url.is_some() {
348 fields.push(GlobalSettingField::CustomHelpLinkUrl);
349 }
350 if request.text_below_custom_login_logo.is_some() {
351 fields.push(GlobalSettingField::TextBelowCustomLoginLogo);
352 }
353 if request.automatic_linked_url_schemes.is_some() {
354 fields.push(GlobalSettingField::AutomaticLinkedUrlSchemes);
355 }
356 if request.custom_top_left_corner_logo_image_url.is_some() {
357 fields.push(GlobalSettingField::CustomTopLeftCornerLogoImageUrl);
358 }
359 if request.custom_top_left_corner_logo_link_url.is_some() {
360 fields.push(GlobalSettingField::CustomTopLeftCornerLogoLinkUrl);
361 }
362 if request.custom_top_left_corner_logo_height.is_some() {
363 fields.push(GlobalSettingField::CustomTopLeftCornerLogoHeight);
364 }
365 if request.oidc_btn_text.is_some() {
366 fields.push(GlobalSettingField::OidcBtnText);
367 }
368 if request.mail_domain_name.is_some() {
369 fields.push(GlobalSettingField::MailDomainName);
370 }
371 if request.legal_notice.is_some() {
372 fields.push(GlobalSettingField::LegalNotice);
373 }
374 if request.custom_head_enabled.is_some() {
375 fields.push(GlobalSettingField::CustomHeadEnabled);
376 }
377 if request.custom_head_meta_tags.is_some() {
378 fields.push(GlobalSettingField::CustomHeadMetaTags);
379 }
380 if request.custom_head_link_tags.is_some() {
381 fields.push(GlobalSettingField::CustomHeadLinkTags);
382 }
383 if request.custom_manifest_enabled.is_some() {
384 fields.push(GlobalSettingField::CustomManifestEnabled);
385 }
386 if request.custom_manifest_content.is_some() {
387 fields.push(GlobalSettingField::CustomManifestContent);
388 }
389 if request.custom_asset_links_enabled.is_some() {
390 fields.push(GlobalSettingField::CustomAssetLinksEnabled);
391 }
392 if request.custom_asset_links_content.is_some() {
393 fields.push(GlobalSettingField::CustomAssetLinksContent);
394 }
395 if request.accessibility_page_enabled.is_some() {
396 fields.push(GlobalSettingField::AccessibilityPageEnabled);
397 }
398 if request.accessibility_title.is_some() {
399 fields.push(GlobalSettingField::AccessibilityTitle);
400 }
401 if request.accessibility_content.is_some() {
402 fields.push(GlobalSettingField::AccessibilityContent);
403 }
404 if request.support_popup_text.is_some() {
405 fields.push(GlobalSettingField::SupportPopupText);
406 }
407 if request.support_page_enabled.is_some() {
408 fields.push(GlobalSettingField::SupportPageEnabled);
409 }
410 if request.support_page_public.is_some() {
411 fields.push(GlobalSettingField::SupportPagePublic);
412 }
413 if request.support_title.is_some() {
414 fields.push(GlobalSettingField::SupportTitle);
415 }
416 if request.support_page_text.is_some() {
417 fields.push(GlobalSettingField::SupportPageText);
418 }
419 fields
420}