diff --git a/app/constants/settings.js b/app/constants/settings.js index 4b54b400e2df7eeeef6281c744c89db00da166b0..4f2c1f444912991d97de41644cd0fd0a5e919e25 100644 --- a/app/constants/settings.js +++ b/app/constants/settings.js @@ -35,6 +35,15 @@ export default { Accounts_PasswordReset: { type: 'valueAsBoolean' }, + Accounts_RegistrationForm: { + type: 'valueAsString' + }, + Accounts_RegistrationForm_LinkReplacementText: { + type: 'valueAsString' + }, + Accounts_ShowFormLogin: { + type: 'valueAsBoolean' + }, CROWD_Enable: { type: 'valueAsBoolean' }, diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.js index 997e3c53373ffd0a123b04a4438186df2411f981..5ab7aa59af98232c502ab260080190a4ee467a7c 100644 --- a/app/lib/methods/getSettings.js +++ b/app/lib/methods/getSettings.js @@ -52,6 +52,23 @@ const serverInfoUpdate = async(serverInfo, iconSetting) => { }); }; +export function getSetting({ server, setting }) { + return new Promise(async(resolve, reject) => { + try { + const result = await fetch(`${ server }/api/v1/settings.public?query={"_id":{"$in":["${ setting }"]}}`).then(response => response.json()); + + if (result.success && result.settings.length) { + const [{ value }] = result.settings; + return resolve(value); + } + } catch (e) { + log(e); + } + + return reject(); + }); +} + export async function setSettings() { const db = database.active; const settingsCollection = db.collections.get('settings'); diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 4d5de613cafd0edccda3526a731e142fa093d9ed..0805ed58c96c6f4a82ae77371b5a5ca4baff18c0 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -24,7 +24,7 @@ import subscribeRooms from './methods/subscriptions/rooms'; import protectedFunction from './methods/helpers/protectedFunction'; import readMessages from './methods/readMessages'; -import getSettings, { setSettings } from './methods/getSettings'; +import getSettings, { getSetting, setSettings } from './methods/getSettings'; import getRooms from './methods/getRooms'; import getPermissions from './methods/getPermissions'; @@ -620,6 +620,7 @@ const RocketChat = { cancelUpload, isUploadActive, getSettings, + getSetting, setSettings, getPermissions, getCustomEmojis, @@ -911,7 +912,7 @@ const RocketChat = { let loginServices = []; const loginServicesResult = await fetch(`${ server }/api/v1/settings.oauth`).then(response => response.json()); - if (loginServicesResult.success && loginServicesResult.services.length > 0) { + if (loginServicesResult.success && loginServicesResult.services) { const { services } = loginServicesResult; loginServices = services; diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js index 5cfa51ad2a4afc5da2e1d44831f39862fb6178c3..c939868f59935785d9f8333758c84eebb30cb09e 100644 --- a/app/sagas/selectServer.js +++ b/app/sagas/selectServer.js @@ -129,8 +129,9 @@ const handleServerRequest = function* handleServerRequest({ server, certificate const serverInfo = yield getServerInfo({ server }); if (serverInfo) { + const showFormLogin = yield RocketChat.getSetting({ server, setting: 'Accounts_ShowFormLogin' }); const loginServicesLength = yield RocketChat.getLoginServices(server); - if (loginServicesLength === 0) { + if (loginServicesLength === 0 && showFormLogin) { Navigation.navigate('LoginView'); } else { Navigation.navigate('LoginSignupView'); diff --git a/app/views/LoginSignupView.js b/app/views/LoginSignupView.js index c2711775afbb93763af27777bb176ba5643263a8..d3c5bb2888d5a4711fe20f62cff41e75ec7303e4 100644 --- a/app/views/LoginSignupView.js +++ b/app/views/LoginSignupView.js @@ -58,6 +58,11 @@ const styles = StyleSheet.create({ serviceName: { ...sharedStyles.textBold }, + registerDisabled: { + ...sharedStyles.textRegular, + ...sharedStyles.textAlignCenter, + fontSize: 16 + }, servicesTogglerContainer: { flexDirection: 'row', alignItems: 'center', @@ -108,6 +113,9 @@ class LoginSignupView extends React.Component { Gitlab_URL: PropTypes.string, CAS_enabled: PropTypes.bool, CAS_login_url: PropTypes.string, + Accounts_ShowFormLogin: PropTypes.bool, + Accounts_RegistrationForm: PropTypes.string, + Accounts_RegistrationForm_LinkReplacementText: PropTypes.string, theme: PropTypes.string } @@ -124,7 +132,7 @@ class LoginSignupView extends React.Component { shouldComponentUpdate(nextProps, nextState) { const { collapsed, servicesHeight } = this.state; const { - server, Site_Name, services, theme + server, Site_Name, services, Accounts_ShowFormLogin, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme } = this.props; if (nextState.collapsed !== collapsed) { return true; @@ -141,6 +149,15 @@ class LoginSignupView extends React.Component { if (nextProps.theme !== theme) { return true; } + if (nextProps.Accounts_ShowFormLogin !== Accounts_ShowFormLogin) { + return true; + } + if (nextProps.Accounts_RegistrationForm !== Accounts_RegistrationForm) { + return true; + } + if (nextProps.Accounts_RegistrationForm_LinkReplacementText !== Accounts_RegistrationForm_LinkReplacementText) { + return true; + } if (!equal(nextProps.services, services)) { return true; } @@ -333,10 +350,12 @@ class LoginSignupView extends React.Component { renderServicesSeparator = () => { const { collapsed } = this.state; - const { services, theme } = this.props; + const { + services, theme, Accounts_ShowFormLogin, Accounts_RegistrationForm + } = this.props; const { length } = Object.values(services); - if (length > 3) { + if (length > 3 && Accounts_ShowFormLogin && Accounts_RegistrationForm) { return ( <View style={styles.servicesTogglerContainer}> <View style={[styles.separatorLine, styles.separatorLineLeft, { backgroundColor: themes[theme].auxiliaryText }]} /> @@ -358,6 +377,7 @@ class LoginSignupView extends React.Component { let { name } = service; name = name === 'meteor-developer' ? 'meteor' : name; const icon = `icon_${ name }`; + const isSaml = service.service === 'saml'; let onPress = () => {}; switch (service.authType) { @@ -383,8 +403,8 @@ class LoginSignupView extends React.Component { name = name.charAt(0).toUpperCase() + name.slice(1); const { CAS_enabled, theme } = this.props; let buttonText; - if (service.service === 'saml' || (service.service === 'cas' && CAS_enabled)) { - buttonText = <Text style={styles.serviceName}>{name}</Text>; + if (isSaml || (service.service === 'cas' && CAS_enabled)) { + buttonText = <Text style={[styles.serviceName, isSaml && { color: service.buttonLabelColor }]}>{name}</Text>; } else { buttonText = ( <> @@ -396,7 +416,7 @@ class LoginSignupView extends React.Component { <Touch key={service.name} onPress={onPress} - style={styles.serviceButton} + style={[styles.serviceButton, isSaml && { backgroundColor: service.buttonColor }]} theme={theme} > <View style={[styles.serviceButtonContainer, { borderColor: themes[theme].borderColor }]}> @@ -409,15 +429,14 @@ class LoginSignupView extends React.Component { renderServices = () => { const { servicesHeight } = this.state; - const { services } = this.props; + const { services, Accounts_ShowFormLogin, Accounts_RegistrationForm } = this.props; const { length } = Object.values(services); const style = { overflow: 'hidden', height: servicesHeight }; - - if (length > 3) { + if (length > 3 && Accounts_ShowFormLogin && Accounts_RegistrationForm) { return ( <Animated.View style={style}> {Object.values(services).map(service => this.renderItem(service))} @@ -431,6 +450,38 @@ class LoginSignupView extends React.Component { ); } + renderLogin = () => { + const { Accounts_ShowFormLogin, theme } = this.props; + if (!Accounts_ShowFormLogin) { + return null; + } + return ( + <Button + title={<Text>{I18n.t('Login_with')} <Text style={{ ...sharedStyles.textBold }}>{I18n.t('email')}</Text></Text>} + type='primary' + onPress={() => this.login()} + theme={theme} + testID='welcome-view-login' + /> + ); + } + + renderRegister = () => { + const { Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme } = this.props; + if (Accounts_RegistrationForm !== 'Public') { + return <Text style={[styles.registerDisabled, { color: themes[theme].auxiliaryText }]}>{Accounts_RegistrationForm_LinkReplacementText}</Text>; + } + return ( + <Button + title={I18n.t('Create_account')} + type='secondary' + onPress={() => this.register()} + theme={theme} + testID='welcome-view-register' + /> + ); + } + render() { const { theme } = this.props; return ( @@ -452,20 +503,8 @@ class LoginSignupView extends React.Component { <StatusBar theme={theme} /> {this.renderServices()} {this.renderServicesSeparator()} - <Button - title={<Text>{I18n.t('Login_with')} <Text style={{ ...sharedStyles.textBold }}>{I18n.t('email')}</Text></Text>} - type='primary' - onPress={() => this.login()} - theme={theme} - testID='welcome-view-login' - /> - <Button - title={I18n.t('Create_account')} - type='secondary' - onPress={() => this.register()} - theme={theme} - testID='welcome-view-register' - /> + {this.renderLogin()} + {this.renderRegister()} </ScrollView> </SafeAreaView> ); @@ -478,6 +517,9 @@ const mapStateToProps = state => ({ Gitlab_URL: state.settings.API_Gitlab_URL, CAS_enabled: state.settings.CAS_enabled, CAS_login_url: state.settings.CAS_login_url, + Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin, + Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm, + Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText, services: state.login.services }); diff --git a/app/views/LoginView.js b/app/views/LoginView.js index a958d44e90f4f5f09a9e66ea740083e742908200..76ff777674240d59e0d169188da1a24030aee93e 100644 --- a/app/views/LoginView.js +++ b/app/views/LoginView.js @@ -29,6 +29,11 @@ const styles = StyleSheet.create({ alignItems: 'center', marginTop: 10 }, + registerDisabled: { + ...sharedStyles.textRegular, + ...sharedStyles.textAlignCenter, + fontSize: 13 + }, dontHaveAccount: { ...sharedStyles.textRegular, fontSize: 13 @@ -61,6 +66,8 @@ class LoginView extends React.Component { Accounts_EmailOrUsernamePlaceholder: PropTypes.string, Accounts_PasswordPlaceholder: PropTypes.string, Accounts_PasswordReset: PropTypes.bool, + Accounts_RegistrationForm: PropTypes.string, + Accounts_RegistrationForm_LinkReplacementText: PropTypes.string, isFetching: PropTypes.bool, failure: PropTypes.bool, theme: PropTypes.string @@ -101,7 +108,7 @@ class LoginView extends React.Component { user, password, code, showTOTP } = this.state; const { - isFetching, failure, error, Site_Name, Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, theme + isFetching, failure, error, Site_Name, Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme } = this.props; if (nextState.user !== user) { return true; @@ -133,6 +140,12 @@ class LoginView extends React.Component { if (nextProps.Accounts_PasswordPlaceholder !== Accounts_PasswordPlaceholder) { return true; } + if (nextProps.Accounts_RegistrationForm !== Accounts_RegistrationForm) { + return true; + } + if (nextProps.Accounts_RegistrationForm_LinkReplacementText !== Accounts_RegistrationForm_LinkReplacementText) { + return true; + } if (!equal(nextProps.error, error)) { return true; } @@ -225,7 +238,7 @@ class LoginView extends React.Component { renderUserForm = () => { const { - Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_PasswordReset, isFetching, theme + Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_PasswordReset, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, isFetching, theme } = this.props; return ( <SafeAreaView @@ -283,15 +296,17 @@ class LoginView extends React.Component { theme={theme} /> )} - <View style={styles.bottomContainer}> - <Text style={[styles.dontHaveAccount, { color: themes[theme].auxiliaryText }]}>{I18n.t('Dont_Have_An_Account')}</Text> - <Text - style={[styles.createAccount, { color: themes[theme].actionTintColor }]} - onPress={this.register} - testID='login-view-register' - >{I18n.t('Create_account')} - </Text> - </View> + {Accounts_RegistrationForm === 'Public' ? ( + <View style={styles.bottomContainer}> + <Text style={[styles.dontHaveAccount, { color: themes[theme].auxiliaryText }]}>{I18n.t('Dont_Have_An_Account')}</Text> + <Text + style={[styles.createAccount, { color: themes[theme].actionTintColor }]} + onPress={this.register} + testID='login-view-register' + >{I18n.t('Create_account')} + </Text> + </View> + ) : (<Text style={[styles.registerDisabled, { color: themes[theme].auxiliaryText }]}>{Accounts_RegistrationForm_LinkReplacementText}</Text>)} </SafeAreaView> ); } @@ -323,6 +338,8 @@ const mapStateToProps = state => ({ Site_Name: state.settings.Site_Name, Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder, + Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm, + Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText, Accounts_PasswordReset: state.settings.Accounts_PasswordReset });