From c37a90aabea2ca986219dd12b67d61df2e2d867e Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Tue, 3 Jul 2018 17:39:29 -0300 Subject: [PATCH] [IMPROVE] Setup Wizard username validation, step progress and optin/optout (#11254) Closes #11198 - [x] Show all steps on info box - [x] Properly display errors in registration process of admin user - [x] Break steps in subtemplates - [x] Redesign "Register Server" step ![Register Server](https://user-images.githubusercontent.com/2263066/42013253-cd691bc2-7a72-11e8-8832-fe9ccd9e89b4.png) - [x] Update end-to-end tests --- packages/rocketchat-i18n/i18n/en.i18n.json | 14 + .../rocketchat-lib/server/startup/settings.js | 3 + .../rocketchat-setup-wizard/client/final.html | 18 + .../rocketchat-setup-wizard/client/final.js | 51 ++ .../client/setupWizard.html | 360 +++++++------ .../client/setupWizard.js | 509 ++++++++++-------- packages/rocketchat-setup-wizard/package.js | 4 +- .../server/getSetupWizardParameters.js | 18 + .../server/lib/getWizardSettings.js | 9 - .../imports/components/setup-wizard.css | 143 ++++- tests/end-to-end/ui/00-login.js | 18 + tests/pageobjects/setup-wizard.page.js | 2 + 12 files changed, 713 insertions(+), 436 deletions(-) create mode 100644 packages/rocketchat-setup-wizard/client/final.html create mode 100644 packages/rocketchat-setup-wizard/client/final.js create mode 100644 packages/rocketchat-setup-wizard/server/getSetupWizardParameters.js delete mode 100644 packages/rocketchat-setup-wizard/server/lib/getWizardSettings.js diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 4c8582bfa328..96b4e6c56eee 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -234,6 +234,7 @@ "Allow_Invalid_SelfSigned_Certs": "Allow Invalid Self-Signed Certs", "Allow_Invalid_SelfSigned_Certs_Description": "Allow invalid and self-signed SSL certificate's for link validation and previews.", "Allow_switching_departments": "Allow Visitor to Switch Departments", + "Allow_Marketing_Emails": "Allow Marketing Emails", "Alphabetical": "Alphabetical", "Always_open_in_new_window": "Always Open in New Window", "Analytics_features_enabled": "Features Enabled", @@ -1370,6 +1371,7 @@ "InternalHubot_Username_Description": "This must be a valid username of a bot registered on your server.", "Invalid_confirm_pass": "The password confirmation does not match password", "Invalid_email": "The email entered is invalid", + "Invalid_username": "The username entered is invalid", "Invalid_Export_File": "The file uploaded isn't a valid %s export file.", "Invalid_Import_File_Type": "Invalid Import file type.", "Invalid_name": "The name must not be empty", @@ -2035,6 +2037,17 @@ "Regenerate_codes": "Regenerate codes", "Register": "Register a new account", "Register_Server": "Register Server", + "Register_Server_Info": "Use the preconfigured gateways and proxies provided by Rocket.Chat Technologies Corp.", + "Register_Server_Registered": "Register to access", + "Register_Server_Registered_Push_Notifications": "Mobile push notifications gateway", + "Register_Server_Registered_Livechat": "Livechat omnichannel proxy", + "Register_Server_Registered_OAuth": "OAuth proxy for social network", + "Register_Server_Registered_Marketplace": "Apps Marketplace", + "Register_Server_Opt_In": "Newsletter, offers and product updates", + "Register_Server_Standalone": "Keep standalone, you'll need to", + "Register_Server_Standalone_Service_Providers": "Create accounts with service providers", + "Register_Server_Standalone_Update_Settings": "Update the preconfigured settings", + "Register_Server_Standalone_Own_Certificates": "Recompile the mobile apps with your own certificates", "Registration": "Registration", "Registration_Succeeded": "Registration Succeeded", "Registration_via_Admin": "Registration via Admin", @@ -2215,6 +2228,7 @@ "Shared_Location": "Shared Location", "Should_be_a_URL_of_an_image": "Should be a URL of an image.", "Should_exists_a_user_with_this_username": "The user must already exist.", + "Show_Setup_Wizard": "Show Setup Wizard", "Show_agent_email": "Show agent email", "Show_all": "Show All", "Show_Avatars": "Show Avatars", diff --git a/packages/rocketchat-lib/server/startup/settings.js b/packages/rocketchat-lib/server/startup/settings.js index 4d8a0e329f3f..cd870f383ef4 100644 --- a/packages/rocketchat-lib/server/startup/settings.js +++ b/packages/rocketchat-lib/server/startup/settings.js @@ -2902,6 +2902,9 @@ RocketChat.settings.addGroup('Setup_Wizard', function() { order: 2 } }); + this.add('Allow_Marketing_Emails', true, { + type: 'boolean' + }); }); }); diff --git a/packages/rocketchat-setup-wizard/client/final.html b/packages/rocketchat-setup-wizard/client/final.html new file mode 100644 index 000000000000..f44289e9b1c5 --- /dev/null +++ b/packages/rocketchat-setup-wizard/client/final.html @@ -0,0 +1,18 @@ + diff --git a/packages/rocketchat-setup-wizard/client/final.js b/packages/rocketchat-setup-wizard/client/final.js new file mode 100644 index 000000000000..3fea0f68212c --- /dev/null +++ b/packages/rocketchat-setup-wizard/client/final.js @@ -0,0 +1,51 @@ +Template.setupWizardFinal.onCreated(function() { + const isSetupWizardDone = localStorage.getItem('wizardFinal'); + if (isSetupWizardDone === null) { + FlowRouter.go('setup-wizard'); + } + + this.autorun(c => { + const showSetupWizard = RocketChat.settings.get('Show_Setup_Wizard'); + if (!showSetupWizard) { + // Setup Wizard state is not defined yet + return; + } + + const userId = Meteor.userId(); + const user = userId && RocketChat.models.Users.findOne(userId, { fields: { status: true } }); + if (userId && (!user || !user.status)) { + // User and its status are not defined yet + return; + } + + c.stop(); + + const isComplete = showSetupWizard === 'completed'; + const noUserLoggedInAndIsNotPending = !userId && showSetupWizard !== 'pending'; + const userIsLoggedButIsNotAdmin = userId && !RocketChat.authz.hasRole(userId, 'admin'); + if (isComplete || noUserLoggedInAndIsNotPending || userIsLoggedButIsNotAdmin) { + FlowRouter.go('home'); + return; + } + }); +}); + +Template.setupWizardFinal.onRendered(function() { + $('#initial-page-loading').remove(); +}); + +Template.setupWizardFinal.events({ + 'click .js-finish'() { + RocketChat.settings.set('Show_Setup_Wizard', 'completed', function() { + localStorage.removeItem('wizard'); + localStorage.removeItem('wizardFinal'); + FlowRouter.go('home'); + }); + } +}); + +Template.setupWizardFinal.helpers({ + siteUrl() { + return RocketChat.settings.get('Site_Url'); + } +}); diff --git a/packages/rocketchat-setup-wizard/client/setupWizard.html b/packages/rocketchat-setup-wizard/client/setupWizard.html index 6f082939f37c..26a4613d4f3b 100644 --- a/packages/rocketchat-setup-wizard/client/setupWizard.html +++ b/packages/rocketchat-setup-wizard/client/setupWizard.html @@ -1,196 +1,214 @@ - - + + + + + + + + diff --git a/packages/rocketchat-setup-wizard/client/setupWizard.js b/packages/rocketchat-setup-wizard/client/setupWizard.js index 445cb3b504c9..715ea2e83518 100644 --- a/packages/rocketchat-setup-wizard/client/setupWizard.js +++ b/packages/rocketchat-setup-wizard/client/setupWizard.js @@ -1,90 +1,135 @@ -import s from 'underscore.string'; +const cannotSetup = () => { + const showSetupWizard = RocketChat.settings.get('Show_Setup_Wizard'); + if (!showSetupWizard) { + // Setup Wizard state is not defined yet + return; + } -const setSettingsAndGo = (settings, registerServer = true) => { - const settingsFilter = Object.entries(settings) - .filter(([key]) => !/registration-|registerServer|currentStep/.test(key)) - .map(([_id, value]) => ({_id, value})); + const userId = Meteor.userId(); + const user = userId && RocketChat.models.Users.findOne(userId, { fields: { status: true } }); + if (userId && (!user || !user.status)) { + // User and its status are not defined yet + return; + } - settingsFilter.push({ - _id: 'Statistics_reporting', - value: registerServer - }); + const isComplete = showSetupWizard === 'completed'; + const noUserLoggedInAndIsNotPending = !userId && showSetupWizard !== 'pending'; + const userIsLoggedButIsNotAdmin = userId && !RocketChat.authz.hasRole(userId, 'admin'); - RocketChat.settings.batchSet(settingsFilter, function(err) { - if (err) { - return handleError(err); + return isComplete || noUserLoggedInAndIsNotPending || userIsLoggedButIsNotAdmin; +}; + +const registerAdminUser = (state, callback) => { + const registrationData = Object.entries(state) + .filter(([ key ]) => /registration-/.test(key)) + .map(([ key, value ]) => ([ key.replace('registration-', ''), value ])) + .reduce((o, [ key, value ]) => ({ ...o, [key]: value }), {}); + + Meteor.call('registerUser', registrationData, error => { + if (error) { + return handleError(error); } - localStorage.setItem('wizardFinal', true); - FlowRouter.go('setup-wizard-final'); + RocketChat.callbacks.run('userRegistered'); + Meteor.loginWithPassword(registrationData.email, registrationData.pass, error => { + if (error) { + if (error.error === 'error-invalid-email') { + toastr.success(t('We_have_sent_registration_email')); + return false; + } else { + return handleError(error); + } + } + + Session.set('forceLogin', false); + Meteor.call('setUsername', registrationData.username, error => { + if (error) { + return handleError(error); + } + + RocketChat.callbacks.run('usernameSet'); + callback && callback(); + }); + }); }); }; -Template.setupWizard.onCreated(function() { - const userId = Meteor.userId(); - - this.autorun((c) => { - const Show_Setup_Wizard = RocketChat.settings.get('Show_Setup_Wizard'); - const user = Meteor.user(); +const persistSettings = (state, callback) => { + const settings = Object.entries(state) + .filter(([ key ]) => !/registration-|registerServer|optIn|currentStep|invalidUsername|invalidEmail/.test(key)) + .map(([ _id, value ]) => ({ _id, value })) + .concat([ + { + _id: 'Statistics_reporting', + value: state['registerServer'] + }, + { + _id: 'Allow_Marketing_Emails', + value: state['optIn'] + } + ]); - // Wait for roles and setup wizard setting - if ((userId && (!user || !user.status)) || !Show_Setup_Wizard) { - return; + RocketChat.settings.batchSet(settings, error => { + if (error) { + return handleError(error); } - c.stop(); - - if ((!userId && Show_Setup_Wizard !== 'pending') || Show_Setup_Wizard === 'completed' || (userId && !RocketChat.authz.hasRole(userId, 'admin'))) { - FlowRouter.go('home'); - } + callback && callback(); }); +}; + +Template.setupWizard.onCreated(function() { + this.state = new ReactiveDict(); + this.state.set('currentStep', 1); + this.state.set('registerServer', true); + this.state.set('optIn', true); + + this.wizardSettings = new ReactiveVar([]); + this.allowStandaloneServer = new ReactiveVar(false); if (localStorage.getItem('wizardFinal')) { FlowRouter.go('setup-wizard-final'); + return; } - this.hasAdmin = new ReactiveVar(false); - this.state = new ReactiveDict(); - this.wizardSettings = new ReactiveVar([]); - this.invalidEmail = new ReactiveVar(false); + const jsonString = localStorage.getItem('wizard'); + const state = jsonString && JSON.parse(jsonString) || {}; + Object.entries(state).forEach(entry => this.state.set(...entry)); - const storage = JSON.parse(localStorage.getItem('wizard')); - if (storage) { - Object.entries(storage).forEach(([key, value]) => { - this.state.set(key, value); - }); - } + this.autorun(c => { + const cantSetup = cannotSetup(); + if (typeof cantSetup === 'undefined') { + return; + } - this.autorun(() => { - const user = Meteor.user(); - if (user) { - if (!this.hasAdmin.get()) { - if (user.roles && user.roles.includes('admin')) { - this.state.set('currentStep', 2); - this.hasAdmin.set(true); - } else { - this.hasAdmin.set(false); - } - } + if (cantSetup) { + c.stop(); + FlowRouter.go('home'); + return; + } + + const state = this.state.all(); + state['registration-pass'] = ''; + localStorage.setItem('wizard', JSON.stringify(state)); - Meteor.call('getWizardSettings', (error, result) => { + if (Meteor.userId()) { + Meteor.call('getSetupWizardParameters', (error, { settings, allowStandaloneServer }) => { if (error) { return handleError(error); } - this.wizardSettings.set(result); + this.wizardSettings.set(settings); + this.allowStandaloneServer.set(allowStandaloneServer); }); - } else { - this.state.set('currentStep', 1); - } - if (RocketChat.settings.get('Show_Setup_Wizard') === 'completed') { - FlowRouter.go('home'); + if (this.state.get('currentStep') === 1) { + this.state.set('currentStep', 2); + } else { + this.state.set('registration-pass', ''); + } + } else if (this.state.get('currentStep') !== 1) { + this.state.set('currentStep', 1); } - - const states = this.state.all(); - states['registration-pass'] = ''; - localStorage.setItem('wizard', JSON.stringify(states)); }); }); @@ -93,67 +138,83 @@ Template.setupWizard.onRendered(function() { }); Template.setupWizard.events({ + 'submit .setup-wizard-forms__box'() { + return false; + }, 'click .setup-wizard-forms__footer-next'(e, t) { - const currentStep = t.state.get('currentStep'); - const hasAdmin = t.hasAdmin.get(); - - if (!hasAdmin && currentStep === 1) { - const emailValue = t.state.get('registration-email'); - const invalidEmail = !/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+\b/i.test(emailValue); - t.invalidEmail.set(invalidEmail); + switch (t.state.get('currentStep')) { + case 1: { + const usernameValue = t.state.get('registration-username'); + const usernameRegex = new RegExp(`^${ RocketChat.settings.get('UTF8_Names_Validation') }$`); + t.state.set('invalidUsername', !usernameRegex.test(usernameValue)); + + const emailValue = t.state.get('registration-email'); + const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+$/i; + t.state.set('invalidEmail', !emailRegex.test(emailValue)); + + if (t.state.get('invalidUsername') || t.state.get('invalidEmail')) { + return false; + } - if (invalidEmail) { + registerAdminUser(t.state.all(), () => t.state.set('currentStep', 2)); return false; } - - const state = t.state.all(); - const registration = Object.entries(state).filter(([key]) => /registration-/.test(key)); - const registrationData = Object.assign(...registration.map(d => ({[d[0].replace('registration-', '')]: d[1]}))); - - Meteor.call('registerUser', registrationData, error => { - if (error) { - return handleError(error); - } - - RocketChat.callbacks.run('userRegistered'); - - Meteor.loginWithPassword(s.trim(registrationData.email), registrationData.pass, error => { - if (error && error.error === 'error-invalid-email') { - toastr.success(t('We_have_sent_registration_email')); - return false; - } - - Session.set('forceLogin', false); - - Meteor.call('setUsername', registrationData.username, error => { - if (error) { - return handleError(error); - } - - RocketChat.callbacks.run('usernameSet'); - }); + case 2: { + t.state.set('currentStep', 3); + return false; + } + case 3: { + t.state.set('currentStep', 4); + return false; + } + case 4: { + persistSettings(t.state.all(), () => { + localStorage.removeItem('wizard'); + localStorage.setItem('wizardFinal', true); + FlowRouter.go('setup-wizard-final'); }); - }); + return false; + } } - if (hasAdmin && currentStep === 3) { - setSettingsAndGo(t.state.all()); - return false; + return false; + }, + 'click .setup-wizard-forms__footer-back'(e, t) { + switch (t.state.get('currentStep')) { + case 2: + t.state.set('currentStep', 1); + break; + case 3: + t.state.set('currentStep', 2); + break; + case 4: + t.state.set('currentStep', 3); + break; } - if (currentStep === 4) { - setSettingsAndGo(t.state.all(), JSON.parse(t.state.get('registerServer') || true)); + return false; + }, + 'input .js-setting-data'({ currentTarget: { name, value } }, t) { + t.state.set(name, value); + }, + 'click input[name="registerServer"]'({ currentTarget: { value } }, t) { + const oldValue = t.state.get('registerServer'); + const newValue = value === 'true'; + t.state.set('registerServer', newValue); - return false; + if (!oldValue && newValue) { + t.state.set('optIn', true); } - t.state.set('currentStep', currentStep + 1); - }, - 'click .setup-wizard-forms__footer-back'(e, t) { - t.state.set('currentStep', t.state.get('currentStep') - 1); + if (!newValue) { + t.state.set('optIn', false); + } + + return false; }, - 'input .js-setting-data'(e, t) { - t.state.set(e.currentTarget.name, e.currentTarget.value); + 'click input[name="optIn"]'({ currentTarget: { checked } }, t) { + t.state.set('optIn', checked); + return false; } }); @@ -161,144 +222,138 @@ Template.setupWizard.helpers({ currentStep() { return Template.instance().state.get('currentStep'); }, - itemModifier(step) { - const current = Template.instance().state.get('currentStep'); - - if (current === step) { - return 'setup-wizard-info__steps-item--active'; - } - - if (current > step) { - return 'setup-wizard-info__steps-item--past'; + currentStepTitle() { + switch (Template.instance().state.get('currentStep')) { + case 1: + return 'Admin_Info'; + case 2: + return 'Organization_Info'; + case 3: + return 'Server_Info'; + case 4: + return 'Register_Server'; } - - return ''; - }, - getValue(name) { - return Template.instance().state.get(name); - }, - selectedValue(setting, optionValue) { - return Template.instance().state.get(setting) === optionValue; }, - isDisabled() { - const user = Meteor.user(); - if (user && user.roles && !user.roles.includes('admin')) { - return 'disabled'; - } - - if (Template.instance().state.get('currentStep') === 1) { - const state = Template.instance().state.all(); - - if (Object.entries(state).filter(([key, value]) => /registration-/.test(key) && !value).length) { - return 'disabled'; - } + formLoadStateClass() { + switch (Template.instance().state.get('currentStep')) { + case 1: + return RocketChat.settings.get('Show_Setup_Wizard') === 'pending' && 'setup-wizard-forms__box--loaded'; + case 2: + case 3: + return Template.instance().wizardSettings.get().length > 0 && 'setup-wizard-forms__box--loaded'; + case 4: + return 'setup-wizard-forms__box--loaded'; } - - return ''; }, - headerTitle(step) { - if (!step) { - step = Template.instance().state.get('currentStep'); + showBackButton() { + switch (Template.instance().state.get('currentStep')) { + case 3: + return true; + case 4: + return true; } - switch (step) { - case 1: return t('Admin_Info'); - case 2: return t('Organization_Info'); - case 3: return t('Server_Info'); - case 4: return t('Register_Server'); - } + return false; }, - showStep() { - const currentStep = Template.instance().state.get('currentStep'); - if (currentStep === 2 || currentStep === 3) { - return 'setup-wizard-forms__content-step--active'; + isContinueDisabled() { + switch (Template.instance().state.get('currentStep')) { + case 1: + return Object.entries(Template.instance().state.all()) + .filter(([key, value]) => /registration-/.test(key) && !value) + .length !== 0; } - return ''; - }, - getSettings(step) { - return Template.instance().wizardSettings.get() - .filter(setting => setting.wizard.step === step) - .sort((a, b) => a.wizard.order - b.wizard.order); + return false; }, - languages() { - const languages = TAPi18n.getLanguages(); - - const result = Object.entries(languages).map(language => { - const obj = language[1]; - obj.key = language[0]; - return obj; - }).sort((a, b) => a.key - b.key); - - result.unshift({ - 'name': 'Default', - 'en': 'Default', - 'key': '' - }); + infoArgs() { + const t = Template.instance(); - return result; + return { + currentStep: t.state.get('currentStep') + }; }, - hasAdmin() { - return Template.instance().hasAdmin.get(); + adminInfoArgs() { + const t = Template.instance(); + + return { + currentStep: t.state.get('currentStep'), + name: t.state.get('registration-name'), + username: t.state.get('registration-username'), + email: t.state.get('registration-email'), + password: t.state.get('registration-pass'), + invalidUsername: t.state.get('invalidUsername'), + invalidEmail: t.state.get('invalidEmail') + }; }, - invalidEmail() { - return Template.instance().invalidEmail.get(); + registerServerArgs() { + const t = Template.instance(); + + return { + currentStep: t.state.get('currentStep'), + allowStandaloneServer: t.allowStandaloneServer.get(), + registerServer: t.allowStandaloneServer.get() ? t.state.get('registerServer') : true, + optIn: t.state.get('optIn') + }; }, - showBackButton() { - if (Template.instance().hasAdmin.get()) { - if (Template.instance().state.get('currentStep') > 2) { - return true; - } - - return false; - } - - if (Template.instance().state.get('currentStep') > 1) { - return true; - } - - return false; + customStepArgs(step) { + const t = Template.instance(); + + return { + currentStep: t.state.get('currentStep'), + step, + settings: t.wizardSettings.get() + .filter(setting => setting.wizard.step === step) + .sort((a, b) => a.wizard.order - b.wizard.order) + .map(({ type, _id, i18nLabel, values }) => ({ + type, + id: _id, + label: i18nLabel, + value: t.state.get(_id), + options: ( + type === 'select' && + values && + values.map(({ i18nLabel, key }) => ({ optionLabel: i18nLabel, optionValue: key })) + ) || ( + type === 'language' && + ([{ + optionLabel: 'Default', + optionValue: '' + }].concat( + Object.entries(TAPi18n.getLanguages()) + .map(([ key, { name } ]) => ({ optionLabel: name, optionValue: key })) + .sort((a, b) => a.key - b.key) + )) + ), + isValueSelected: (value) => value === t.state.get(_id) + })) + }; } }); -Template.setupWizardFinal.onCreated(function() { - this.autorun(() => { - const userId = Meteor.userId(); - - this.autorun((c) => { - const Show_Setup_Wizard = RocketChat.settings.get('Show_Setup_Wizard'); - const user = Meteor.user(); - - // Wait for roles and setup wizard setting - if ((userId && (!user || !user.status)) || !Show_Setup_Wizard) { - return; - } - - c.stop(); - - if ((!userId && Show_Setup_Wizard !== 'pending') || Show_Setup_Wizard === 'completed' || (userId && !RocketChat.authz.hasRole(userId, 'admin'))) { - FlowRouter.go('home'); - } - }); - }); -}); +Template.setupWizardInfo.helpers({ + stepItemModifier(step) { + const { currentStep } = Template.currentData(); -Template.setupWizardFinal.onRendered(function() { - $('#initial-page-loading').remove(); -}); + if (currentStep === step) { + return 'setup-wizard-info__steps-item--active'; + } -Template.setupWizardFinal.events({ - 'click .js-finish'() { - RocketChat.settings.set('Show_Setup_Wizard', 'completed', function() { - localStorage.removeItem('wizard'); - localStorage.removeItem('wizardFinal'); - FlowRouter.go('home'); - }); - } -}); + if (currentStep > step) { + return 'setup-wizard-info__steps-item--past'; + } -Template.setupWizardFinal.helpers({ - siteUrl() { - return RocketChat.settings.get('Site_Url'); + return ''; + }, + stepTitle(step) { + switch (step) { + case 1: + return 'Admin_Info'; + case 2: + return 'Organization_Info'; + case 3: + return 'Server_Info'; + case 4: + return 'Register_Server'; + } } }); diff --git a/packages/rocketchat-setup-wizard/package.js b/packages/rocketchat-setup-wizard/package.js index b9d07f3caeb3..ba70d31fd112 100644 --- a/packages/rocketchat-setup-wizard/package.js +++ b/packages/rocketchat-setup-wizard/package.js @@ -12,6 +12,8 @@ Package.onUse(function(api) { api.addFiles('client/setupWizard.html', 'client'); api.addFiles('client/setupWizard.js', 'client'); + api.addFiles('client/final.html', 'client'); + api.addFiles('client/final.js', 'client'); - api.addFiles('server/lib/getWizardSettings.js', 'server'); + api.addFiles('server/getSetupWizardParameters.js', 'server'); }); diff --git a/packages/rocketchat-setup-wizard/server/getSetupWizardParameters.js b/packages/rocketchat-setup-wizard/server/getSetupWizardParameters.js new file mode 100644 index 000000000000..910947c0418e --- /dev/null +++ b/packages/rocketchat-setup-wizard/server/getSetupWizardParameters.js @@ -0,0 +1,18 @@ +Meteor.methods({ + getSetupWizardParameters() { + const userId = Meteor.userId(); + const userHasAdminRole = userId && RocketChat.authz.hasRole(userId, 'admin'); + + if (!userHasAdminRole) { + throw new Meteor.Error('error-not-allowed'); + } + + const settings = RocketChat.models.Settings.findSetupWizardSettings().fetch(); + const allowStandaloneServer = process.env.DEPLOY_PLATFORM !== 'rocket-cloud'; + + return { + settings, + allowStandaloneServer + }; + } +}); diff --git a/packages/rocketchat-setup-wizard/server/lib/getWizardSettings.js b/packages/rocketchat-setup-wizard/server/lib/getWizardSettings.js deleted file mode 100644 index 918c3225bd23..000000000000 --- a/packages/rocketchat-setup-wizard/server/lib/getWizardSettings.js +++ /dev/null @@ -1,9 +0,0 @@ -Meteor.methods({ - getWizardSettings() { - if (RocketChat.authz.hasRole(Meteor.userId(), 'admin') && RocketChat.models && RocketChat.models.Settings) { - return RocketChat.models.Settings.findSetupWizardSettings().fetch(); - } - - throw new Meteor.Error('settings-are-not-ready', 'Settings are not ready'); - } -}); diff --git a/packages/rocketchat-theme/client/imports/components/setup-wizard.css b/packages/rocketchat-theme/client/imports/components/setup-wizard.css index fa2825348d28..3dea99d8cd8d 100644 --- a/packages/rocketchat-theme/client/imports/components/setup-wizard.css +++ b/packages/rocketchat-theme/client/imports/components/setup-wizard.css @@ -1,4 +1,7 @@ .setup-wizard { + --step-color: var(--rc-color-button-primary); + --highlight-color: var(--rc-color-button-primary); + display: flex; width: 100%; @@ -12,6 +15,7 @@ flex: 0 1 350px; margin: 55px 65px 30px 80px; + overflow: hidden; &__header{ display: flex; @@ -125,28 +129,30 @@ } &--active { - color: #1d74f5; + color: var(--rc-color-button-primary); &::before { - color: #1d74f5; - border-color: #1d74f5; + color: var(--rc-color-button-primary); + background-color: transparent; + border-color: var(--rc-color-button-primary); } } &--past { - color: #2f343d; + color: var(--rc-color-primary); &::before { - color: #ffffff; - background-color: #1d74f5; + color: var(--rc-color-content); + background-color: var(--rc-color-button-primary); + border-color: var(--rc-color-button-primary); } &::after { - background-color: #1d74f5 !important; + background-color: var(--rc-color-button-primary) !important; } & .setup-wizard-info__steps-item-bonding { - background-color: #1d74f5; + background-color: var(--rc-color-button-primary); } } @@ -177,8 +183,6 @@ height: calc(100% - 2rem); margin: 1rem 1rem 1rem 0; - padding: 3rem; - border-radius: 2px; background: #ffffff; box-shadow: 0 2px 4px 0 rgba(0,0,0,0.08); @@ -190,6 +194,17 @@ flex-direction: column; width: 350px; + min-height: min-content; + margin: 3rem; + + visibility: hidden; + opacity: 0; + transition: opacity 1s linear; + + &--loaded { + visibility: visible; + opacity: 1; + } } &__header { @@ -259,7 +274,7 @@ cursor: pointer; color: #2f343d; - border: 1px solid #e7ebf2; + border: 2px solid #e7ebf2; border-radius: 2px; @@ -267,6 +282,14 @@ line-height: 1.25rem; + &--selected { + border-color: var(--highlight-color); + } + + &--disabled { + opacity: 0.25; + } + &:first-child { margin-bottom: 1rem; } @@ -292,22 +315,21 @@ position: relative; - border-color: #1d74f5; + border-color: var(--highlight-color); &::before { - position: absolute; - - top: 3px; - left: 3px; + content: ""; - width: 12px; - height: 12px; + position: absolute; - content: ""; + width: 100%; + height: 100%; + background-clip: padding-box; border-radius: 50%; + border: 2px solid transparent; - background-color: #1d74f5; + background-color: var(--highlight-color); } } } @@ -319,7 +341,7 @@ height: 20px; margin: 0 0.5rem; - border: 1px solid #cfd8e6; + border: 2px solid #cfd8e6; border-radius: 50px; } @@ -328,6 +350,63 @@ } } + &-checkbox { + position: relative; + + display: flex; + + margin: 0 -0.5rem 1rem; + + cursor: inherit; + + &-element { + position: absolute; + z-index: -1; + top: 0; + left: 0; + + width: 0; + height: 0; + + &:checked + .setup-wizard-forms__content-register-checkbox-fake { + position: relative; + + border-color: var(--highlight-color); + background-color: var(--highlight-color); + color: var(--rc-color-content); + + .setup-wizard-forms__content-register-checkbox-fake-icon { + display: block; + } + } + } + + &-fake { + display: block; + + width: 16px; + height: 16px; + margin: 2px 0.5rem; + + border: 2px solid #cfd8e6; + border-radius: 2px; + + &-icon { + width: 100%; + height: 100%; + display: none; + } + } + + &-text { + color: #666666; + } + } + + &-items + * { + margin-top: 1rem; + } + &-item { display: flex; @@ -340,13 +419,20 @@ } & .setup-wizard-forms__content-register-radio-icon { - margin: 0 calc(4px + 0.5rem); + min-width: 20px; + width: 20px; + height: 20px; + margin: 0 0.5rem; + align-self: baseline; - font-size: 10px; - } + &--check { + color: var(--highlight-color); + } - & .setup-wizard-forms__content-register-radio-icon--check { - color: #1d74f5; + &--circle { + height: 6px; + margin: 7px 0.5rem; + } } } } @@ -354,8 +440,9 @@ &__footer { display: flex; + flex-direction: row; - margin: 0 -0.5rem; + margin: 0 -0.5rem 2rem -0.5rem; & .rc-button { margin: 0 0.5rem; @@ -390,7 +477,7 @@ letter-spacing: 0; - color: #1d74f5; + color: var(--highlight-color); font-size: 1rem; diff --git a/tests/end-to-end/ui/00-login.js b/tests/end-to-end/ui/00-login.js index 4cd6c69f86e6..3934bfb9da9e 100644 --- a/tests/end-to-end/ui/00-login.js +++ b/tests/end-to-end/ui/00-login.js @@ -123,6 +123,24 @@ describe('[Setup Wizard]', () => { }); }); + describe('[Render - Step 3]', () => { + it('it should have option for registered server', () => { + setupWizard.registeredServer.isExisting().should.be.true; + }); + + it('it should have option for standalone server', () => { + setupWizard.standaloneServer.isExisting().should.be.true; + }); + + it('it should check option for registered server by default', () => { + setupWizard.registeredServer.isSelected().should.be.true; + }); + + after(() => { + setupWizard.goNext(); + }); + }); + describe('[Render - Final Step]', () => { it('it should render "Go to your workspace button', () => { setupWizard.goToWorkspace.waitForVisible(15000); diff --git a/tests/pageobjects/setup-wizard.page.js b/tests/pageobjects/setup-wizard.page.js index 593b9b646e75..f6a652fee44a 100644 --- a/tests/pageobjects/setup-wizard.page.js +++ b/tests/pageobjects/setup-wizard.page.js @@ -14,6 +14,8 @@ class SetupWizard extends Page { get siteName() { return browser.element('input[name="Site_Name"]'); } get language() { return browser.element('select[name="Language"]'); } get serverType() { return browser.element('select[name="Server_Type"]'); } + get registeredServer() { return browser.element('input[name="registerServer"][value="true"]'); } + get standaloneServer() { return browser.element('input[name="registerServer"][value="false"]'); } login() { browser.execute(function(email, password) {