From b95fd455cde4d11df0777bfa9c3aee8398602909 Mon Sep 17 00:00:00 2001
From: Rodrigo Nascimento <rodrigoknascimento@gmail.com>
Date: Mon, 21 Sep 2020 17:35:10 -0300
Subject: [PATCH] [NEW] Option to require settings on wizard UI via ENV
 variables (#18974)

Co-authored-by: Martin <martin.schoeler@rocket.chat>
---
 app/models/server/models/Settings.js                |  4 ++--
 app/settings/server/functions/settings.ts           | 10 ++++++++++
 client/views/setupWizard/steps/SettingsBasedStep.js | 10 +++++++---
 server/publications/settings/index.js               |  1 +
 4 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/app/models/server/models/Settings.js b/app/models/server/models/Settings.js
index 2c766c24ca5..83169ac126f 100644
--- a/app/models/server/models/Settings.js
+++ b/app/models/server/models/Settings.js
@@ -58,7 +58,7 @@ export class Settings extends Base {
 			filter._id = { $in: ids };
 		}
 
-		return this.find(filter, { fields: { _id: 1, value: 1, editor: 1, enterprise: 1, invalidValue: 1, modules: 1 } });
+		return this.find(filter, { fields: { _id: 1, value: 1, editor: 1, enterprise: 1, invalidValue: 1, modules: 1, requiredOnWizard: 1 } });
 	}
 
 	findNotHiddenPublicUpdatedAfter(updatedAt) {
@@ -70,7 +70,7 @@ export class Settings extends Base {
 			},
 		};
 
-		return this.find(filter, { fields: { _id: 1, value: 1, editor: 1, enterprise: 1, invalidValue: 1, modules: 1 } });
+		return this.find(filter, { fields: { _id: 1, value: 1, editor: 1, enterprise: 1, invalidValue: 1, modules: 1, requiredOnWizard: 1 } });
 	}
 
 	findNotHiddenPrivate() {
diff --git a/app/settings/server/functions/settings.ts b/app/settings/server/functions/settings.ts
index 480c2f93683..c0b239e7be5 100644
--- a/app/settings/server/functions/settings.ts
+++ b/app/settings/server/functions/settings.ts
@@ -8,6 +8,7 @@ import SettingsModel from '../../../models/server/models/Settings';
 
 const blockedSettings = new Set<string>();
 const hiddenSettings = new Set<string>();
+const wizardRequiredSettings = new Set<string>();
 
 if (process.env.SETTINGS_BLOCKED) {
 	process.env.SETTINGS_BLOCKED.split(',').forEach((settingId) => blockedSettings.add(settingId.trim()));
@@ -17,6 +18,10 @@ if (process.env.SETTINGS_HIDDEN) {
 	process.env.SETTINGS_HIDDEN.split(',').forEach((settingId) => hiddenSettings.add(settingId.trim()));
 }
 
+if (process.env.SETTINGS_REQUIRED_ON_WIZARD) {
+	process.env.SETTINGS_REQUIRED_ON_WIZARD.split(',').forEach((settingId) => wizardRequiredSettings.add(settingId.trim()));
+}
+
 export const SettingsEvents = new EventEmitter();
 
 const overrideSetting = (_id: string, value: SettingValue, options: ISettingAddOptions): SettingValue => {
@@ -67,6 +72,7 @@ export interface ISettingAddOptions {
 	valueSource?: string;
 	hidden?: boolean;
 	blocked?: boolean;
+	requiredOnWizard?: boolean;
 	secret?: boolean;
 	sorter?: number;
 	i18nLabel?: string;
@@ -154,6 +160,7 @@ class Settings extends SettingsBase {
 		options.valueSource = 'packageValue';
 		options.hidden = options.hidden || false;
 		options.blocked = options.blocked || false;
+		options.requiredOnWizard = options.requiredOnWizard || false;
 		options.secret = options.secret || false;
 		options.enterprise = options.enterprise || false;
 
@@ -180,6 +187,9 @@ class Settings extends SettingsBase {
 		if (hiddenSettings.has(_id)) {
 			options.hidden = true;
 		}
+		if (wizardRequiredSettings.has(_id)) {
+			options.requiredOnWizard = true;
+		}
 		if (options.autocomplete == null) {
 			options.autocomplete = true;
 		}
diff --git a/client/views/setupWizard/steps/SettingsBasedStep.js b/client/views/setupWizard/steps/SettingsBasedStep.js
index a38e2d604ac..323c4a7dcab 100644
--- a/client/views/setupWizard/steps/SettingsBasedStep.js
+++ b/client/views/setupWizard/steps/SettingsBasedStep.js
@@ -9,7 +9,7 @@ import {
 	TextInput,
 } from '@rocket.chat/fuselage';
 import { useAutoFocus } from '@rocket.chat/fuselage-hooks';
-import React, { useEffect, useReducer, useState, useCallback } from 'react';
+import React, { useEffect, useReducer, useState, useCallback, useMemo } from 'react';
 
 import { useSettingsDispatch } from '../../../contexts/SettingsContext';
 import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
@@ -86,6 +86,9 @@ function SettingsBasedStep({ step, title, active }) {
 		}
 	};
 
+	const hasEmptyRequiredFields = useMemo(() => !!fields.find((field) => field.requiredOnWizard && String(field.value).trim() === ''), [fields]);
+
+
 	if (fields.length === 0) {
 		return <Step active={active} working={commiting} onSubmit={handleSubmit}>
 			<StepHeader number={step} title={title} />
@@ -110,9 +113,9 @@ function SettingsBasedStep({ step, title, active }) {
 
 		<Margins blockEnd='x32'>
 			<FieldGroup>
-				{fields.map(({ _id, type, i18nLabel, value, values }, i) =>
+				{fields.map(({ _id, type, i18nLabel, value, values, requiredOnWizard }, i) =>
 					<Field key={i}>
-						<Field.Label htmlFor={_id}>{t(i18nLabel)}</Field.Label>
+						<Field.Label htmlFor={_id} required={requiredOnWizard}>{t(i18nLabel)}</Field.Label>
 						<Field.Row>
 							{type === 'string' && <TextInput
 								type='text'
@@ -166,6 +169,7 @@ function SettingsBasedStep({ step, title, active }) {
 
 		<Pager
 			disabled={commiting}
+			isContinueEnabled={!hasEmptyRequiredFields}
 			onBackClick={currentStep > 2 && handleBackClick}
 		/>
 	</Step>;
diff --git a/server/publications/settings/index.js b/server/publications/settings/index.js
index 7b379059b50..c06948fb22a 100644
--- a/server/publications/settings/index.js
+++ b/server/publications/settings/index.js
@@ -92,6 +92,7 @@ Settings.on('change', ({ clientAction, id, data, diff }) => {
 				editor: setting.editor,
 				properties: setting.properties,
 				enterprise: setting.enterprise,
+				requiredOnWizard: setting.requiredOnWizard,
 			};
 
 			SettingsEvents.emit('change-setting', setting, value);
-- 
GitLab