Просмотр исходного кода

Merge pull request #1628 from weseek/reactify-admin/fetch-config-before-rendering

Reactify admin/fetch config before rendering
itizawa 6 лет назад
Родитель
Сommit
9545938cd9
34 измененных файлов с 279 добавлено и 334 удалено
  1. 35 0
      src/client/js/admin.jsx
  2. 8 8
      src/client/js/components/Admin/Security/BasicSecuritySetting.jsx
  3. 9 8
      src/client/js/components/Admin/Security/GitHubSecuritySetting.jsx
  4. 9 8
      src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx
  5. 8 7
      src/client/js/components/Admin/Security/LdapSecuritySetting.jsx
  6. 10 4
      src/client/js/components/Admin/Security/LocalSecuritySetting.jsx
  7. 18 23
      src/client/js/components/Admin/Security/OidcSecuritySetting.jsx
  8. 9 15
      src/client/js/components/Admin/Security/SamlSecuritySetting.jsx
  9. 8 7
      src/client/js/components/Admin/Security/TwitterSecuritySetting.jsx
  10. 16 5
      src/client/js/services/AdminBasicSecurityContainer.js
  11. 17 7
      src/client/js/services/AdminGitHubSecurityContainer.js
  12. 18 7
      src/client/js/services/AdminGoogleSecurityContainer.js
  13. 27 16
      src/client/js/services/AdminLdapSecurityContainer.js
  14. 18 6
      src/client/js/services/AdminLocalSecurityContainer.js
  15. 25 14
      src/client/js/services/AdminOidcSecurityContainer.js
  16. 28 16
      src/client/js/services/AdminSamlSecurityContainer.js
  17. 16 7
      src/client/js/services/AdminTwitterSecurityContainer.js
  18. 0 7
      src/server/form/admin/custombehavior.js
  19. 0 7
      src/server/form/admin/customcss.js
  20. 0 12
      src/server/form/admin/customfeatures.js
  21. 0 7
      src/server/form/admin/customheader.js
  22. 0 8
      src/server/form/admin/customhighlightJsStyle.js
  23. 0 8
      src/server/form/admin/customlayout.js
  24. 0 7
      src/server/form/admin/customscript.js
  25. 0 7
      src/server/form/admin/customtheme.js
  26. 0 7
      src/server/form/admin/customtitle.js
  27. 0 8
      src/server/form/admin/securityPassportBasic.js
  28. 0 10
      src/server/form/admin/securityPassportGitHub.js
  29. 0 10
      src/server/form/admin/securityPassportGoogle.js
  30. 0 23
      src/server/form/admin/securityPassportLdap.js
  31. 0 11
      src/server/form/admin/securityPassportLocal.js
  32. 0 17
      src/server/form/admin/securityPassportOidc.js
  33. 0 17
      src/server/form/admin/securityPassportSaml.js
  34. 0 10
      src/server/form/admin/securityPassportTwitter.js

+ 35 - 0
src/client/js/admin.jsx

@@ -12,6 +12,7 @@ import ManageGlobalNotification from './components/Admin/Notification/ManageGlob
 import MarkdownSetting from './components/Admin/MarkdownSetting/MarkDownSetting';
 import MarkdownSetting from './components/Admin/MarkdownSetting/MarkDownSetting';
 import UserManagement from './components/Admin/UserManagement';
 import UserManagement from './components/Admin/UserManagement';
 import AppSettingsPage from './components/Admin/App/AppSettingsPage';
 import AppSettingsPage from './components/Admin/App/AppSettingsPage';
+import SecurityManagement from './components/Admin/Security/SecurityManagement';
 import ManageExternalAccount from './components/Admin/ManageExternalAccount';
 import ManageExternalAccount from './components/Admin/ManageExternalAccount';
 import UserGroupPage from './components/Admin/UserGroup/UserGroupPage';
 import UserGroupPage from './components/Admin/UserGroup/UserGroupPage';
 import Customize from './components/Admin/Customize/Customize';
 import Customize from './components/Admin/Customize/Customize';
@@ -26,6 +27,15 @@ import AdminUsersContainer from './services/AdminUsersContainer';
 import AdminAppContainer from './services/AdminAppContainer';
 import AdminAppContainer from './services/AdminAppContainer';
 import AdminMarkDownContainer from './services/AdminMarkDownContainer';
 import AdminMarkDownContainer from './services/AdminMarkDownContainer';
 import AdminExternalAccountsContainer from './services/AdminExternalAccountsContainer';
 import AdminExternalAccountsContainer from './services/AdminExternalAccountsContainer';
+import AdminGeneralSecurityContainer from './services/AdminGeneralSecurityContainer';
+import AdminLdapSecurityContainer from './services/AdminLdapSecurityContainer';
+import AdminLocalSecurityContainer from './services/AdminLocalSecurityContainer';
+import AdminSamlSecurityContainer from './services/AdminSamlSecurityContainer';
+import AdminOidcSecurityContainer from './services/AdminOidcSecurityContainer';
+import AdminBasicSecurityContainer from './services/AdminBasicSecurityContainer';
+import AdminGoogleSecurityContainer from './services/AdminGoogleSecurityContainer';
+import AdminGitHubSecurityContainer from './services/AdminGitHubSecurityContainer';
+import AdminTwitterSecurityContainer from './services/AdminTwitterSecurityContainer';
 import AdminNotificationContainer from './services/AdminNotificationContainer';
 import AdminNotificationContainer from './services/AdminNotificationContainer';
 
 
 import { appContainer, componentMappings } from './bootstrap';
 import { appContainer, componentMappings } from './bootstrap';
@@ -95,3 +105,28 @@ Object.keys(componentMappings).forEach((key) => {
     );
     );
   }
   }
 });
 });
+
+const adminSecuritySettingElem = document.getElementById('admin-security-setting');
+if (adminSecuritySettingElem != null) {
+  const adminGeneralSecurityContainer = new AdminGeneralSecurityContainer(appContainer);
+  const adminLocalSecurityContainer = new AdminLocalSecurityContainer(appContainer);
+  const adminLdapSecurityContainer = new AdminLdapSecurityContainer(appContainer);
+  const adminSamlSecurityContainer = new AdminSamlSecurityContainer(appContainer);
+  const adminOidcSecurityContainer = new AdminOidcSecurityContainer(appContainer);
+  const adminBasicSecurityContainer = new AdminBasicSecurityContainer(appContainer);
+  const adminGoogleSecurityContainer = new AdminGoogleSecurityContainer(appContainer);
+  const adminGitHubSecurityContainer = new AdminGitHubSecurityContainer(appContainer);
+  const adminTwitterSecurityContainer = new AdminTwitterSecurityContainer(appContainer);
+  const adminSecurityContainers = [
+    adminGeneralSecurityContainer, adminLocalSecurityContainer, adminLdapSecurityContainer, adminSamlSecurityContainer,
+    adminOidcSecurityContainer, adminBasicSecurityContainer, adminGoogleSecurityContainer, adminGitHubSecurityContainer, adminTwitterSecurityContainer,
+  ];
+  ReactDOM.render(
+    <Provider inject={[...injectableContainers, ...adminSecurityContainers]}>
+      <I18nextProvider i18n={i18n}>
+        <SecurityManagement />
+      </I18nextProvider>
+    </Provider>,
+    adminSecuritySettingElem,
+  );
+}

+ 8 - 8
src/client/js/components/Admin/Security/BasicSecuritySetting.jsx

@@ -2,7 +2,6 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -11,15 +10,13 @@ import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminBasicSecurityContainer from '../../../services/AdminBasicSecurityContainer';
 import AdminBasicSecurityContainer from '../../../services/AdminBasicSecurityContainer';
 
 
-const logger = loggerFactory('growi:security:AdminTwitterSecurityContainer');
-
 class BasicSecurityManagement extends React.Component {
 class BasicSecurityManagement extends React.Component {
 
 
   constructor(props) {
   constructor(props) {
     super(props);
     super(props);
 
 
     this.state = {
     this.state = {
-      retrieveError: null,
+      isRetrieving: true,
     };
     };
 
 
     this.onClickSubmit = this.onClickSubmit.bind(this);
     this.onClickSubmit = this.onClickSubmit.bind(this);
@@ -33,9 +30,8 @@ class BasicSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      this.setState({ retrieveError: err.message });
-      logger.error(err);
     }
     }
+    this.setState({ isRetrieving: false });
   }
   }
 
 
   async onClickSubmit() {
   async onClickSubmit() {
@@ -47,13 +43,15 @@ class BasicSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      logger.error(err);
     }
     }
   }
   }
 
 
   render() {
   render() {
     const { t, adminGeneralSecurityContainer, adminBasicSecurityContainer } = this.props;
     const { t, adminGeneralSecurityContainer, adminBasicSecurityContainer } = this.props;
 
 
+    if (this.state.isRetrieving) {
+      return null;
+    }
     return (
     return (
       <React.Fragment>
       <React.Fragment>
 
 
@@ -116,7 +114,9 @@ class BasicSecurityManagement extends React.Component {
 
 
         <div className="row my-3">
         <div className="row my-3">
           <div className="col-xs-offset-4 col-xs-5">
           <div className="col-xs-offset-4 col-xs-5">
-            <button type="button" className="btn btn-primary" disabled={this.state.retrieveError != null} onClick={this.onClickSubmit}>{ t('Update') }</button>
+            <button type="button" className="btn btn-primary" disabled={adminBasicSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
+              { t('Update') }
+            </button>
           </div>
           </div>
         </div>
         </div>
 
 

+ 9 - 8
src/client/js/components/Admin/Security/GitHubSecuritySetting.jsx

@@ -2,7 +2,6 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -11,15 +10,13 @@ import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGitHubSecurityContainer from '../../../services/AdminGitHubSecurityContainer';
 import AdminGitHubSecurityContainer from '../../../services/AdminGitHubSecurityContainer';
 
 
-const logger = loggerFactory('growi:security:AdminGitHubSecurityContainer');
-
 class GitHubSecurityManagement extends React.Component {
 class GitHubSecurityManagement extends React.Component {
 
 
   constructor(props) {
   constructor(props) {
     super(props);
     super(props);
 
 
     this.state = {
     this.state = {
-      retrieveError: null,
+      isRetrieving: true,
     };
     };
 
 
     this.onClickSubmit = this.onClickSubmit.bind(this);
     this.onClickSubmit = this.onClickSubmit.bind(this);
@@ -33,9 +30,8 @@ class GitHubSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      this.setState({ retrieveError: err.message });
-      logger.error(err);
     }
     }
+    this.setState({ isRetrieving: false });
   }
   }
 
 
   async onClickSubmit() {
   async onClickSubmit() {
@@ -47,12 +43,15 @@ class GitHubSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      logger.error(err);
     }
     }
   }
   }
 
 
   render() {
   render() {
     const { t, adminGeneralSecurityContainer, adminGitHubSecurityContainer } = this.props;
     const { t, adminGeneralSecurityContainer, adminGitHubSecurityContainer } = this.props;
+
+    if (this.state.isRetrieving) {
+      return null;
+    }
     return (
     return (
 
 
       <React.Fragment>
       <React.Fragment>
@@ -167,7 +166,9 @@ class GitHubSecurityManagement extends React.Component {
 
 
         <div className="row my-3">
         <div className="row my-3">
           <div className="col-xs-offset-3 col-xs-5">
           <div className="col-xs-offset-3 col-xs-5">
-            <div className="btn btn-primary" disabled={this.state.retrieveError != null} onClick={this.onClickSubmit}>{t('Update')}</div>
+            <div className="btn btn-primary" disabled={adminGitHubSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
+              {t('Update')}
+            </div>
           </div>
           </div>
         </div>
         </div>
 
 

+ 9 - 8
src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx

@@ -2,7 +2,6 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -11,15 +10,13 @@ import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGoogleSecurityContainer from '../../../services/AdminGoogleSecurityContainer';
 import AdminGoogleSecurityContainer from '../../../services/AdminGoogleSecurityContainer';
 
 
-const logger = loggerFactory('growi:security:AdminGoogleSecurityContainer');
-
 class GoogleSecurityManagement extends React.Component {
 class GoogleSecurityManagement extends React.Component {
 
 
   constructor(props) {
   constructor(props) {
     super(props);
     super(props);
 
 
     this.state = {
     this.state = {
-      retrieveError: null,
+      isRetrieving: true,
     };
     };
 
 
     this.onClickSubmit = this.onClickSubmit.bind(this);
     this.onClickSubmit = this.onClickSubmit.bind(this);
@@ -33,9 +30,8 @@ class GoogleSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      this.setState({ retrieveError: err.message });
-      logger.error(err);
     }
     }
+    this.setState({ isRetrieving: false });
   }
   }
 
 
   async onClickSubmit() {
   async onClickSubmit() {
@@ -47,12 +43,15 @@ class GoogleSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      logger.error(err);
     }
     }
   }
   }
 
 
   render() {
   render() {
     const { t, adminGeneralSecurityContainer, adminGoogleSecurityContainer } = this.props;
     const { t, adminGeneralSecurityContainer, adminGoogleSecurityContainer } = this.props;
+
+    if (this.state.isRetrieving) {
+      return null;
+    }
     return (
     return (
 
 
       <React.Fragment>
       <React.Fragment>
@@ -167,7 +166,9 @@ class GoogleSecurityManagement extends React.Component {
 
 
         <div className="row my-3">
         <div className="row my-3">
           <div className="col-xs-offset-3 col-xs-5">
           <div className="col-xs-offset-3 col-xs-5">
-            <button type="button" className="btn btn-primary" disabled={this.state.retrieveError != null} onClick={this.onClickSubmit}>{t('Update')}</button>
+            <button type="button" className="btn btn-primary" disabled={adminGoogleSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
+              {t('Update')}
+            </button>
           </div>
           </div>
         </div>
         </div>
 
 

+ 8 - 7
src/client/js/components/Admin/Security/LdapSecuritySetting.jsx

@@ -1,7 +1,6 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -11,7 +10,6 @@ import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurit
 import AdminLdapSecurityContainer from '../../../services/AdminLdapSecurityContainer';
 import AdminLdapSecurityContainer from '../../../services/AdminLdapSecurityContainer';
 import LdapAuthTestModal from './LdapAuthTestModal';
 import LdapAuthTestModal from './LdapAuthTestModal';
 
 
-const logger = loggerFactory('growi:security:AdminLdapSecurityContainer');
 
 
 class LdapSecuritySetting extends React.Component {
 class LdapSecuritySetting extends React.Component {
 
 
@@ -19,7 +17,7 @@ class LdapSecuritySetting extends React.Component {
     super(props);
     super(props);
 
 
     this.state = {
     this.state = {
-      retrieveError: null,
+      isRetrieving: true,
       isLdapAuthTestModalShown: false,
       isLdapAuthTestModalShown: false,
     };
     };
 
 
@@ -36,9 +34,8 @@ class LdapSecuritySetting extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      this.setState({ retrieveError: err.message });
-      logger.error(err);
     }
     }
+    this.setState({ isRetrieving: false });
   }
   }
 
 
   async onClickSubmit() {
   async onClickSubmit() {
@@ -50,7 +47,6 @@ class LdapSecuritySetting extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      logger.error(err);
     }
     }
   }
   }
 
 
@@ -66,6 +62,9 @@ class LdapSecuritySetting extends React.Component {
     const { t, adminGeneralSecurityContainer, adminLdapSecurityContainer } = this.props;
     const { t, adminGeneralSecurityContainer, adminLdapSecurityContainer } = this.props;
     const { isLdapEnabled } = adminGeneralSecurityContainer.state;
     const { isLdapEnabled } = adminGeneralSecurityContainer.state;
 
 
+    if (this.state.isRetrieving) {
+      return null;
+    }
     return (
     return (
       <React.Fragment>
       <React.Fragment>
 
 
@@ -389,7 +388,9 @@ class LdapSecuritySetting extends React.Component {
 
 
         <div className="row my-3">
         <div className="row my-3">
           <div className="col-xs-offset-3 col-xs-5">
           <div className="col-xs-offset-3 col-xs-5">
-            <button type="button" className="btn btn-primary" disabled={this.state.retrieveError != null} onClick={this.onClickSubmit}>{t('Update')}</button>
+            <button type="button" className="btn btn-primary" disabled={adminLdapSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
+              {t('Update')}
+            </button>
             {adminGeneralSecurityContainer.state.isLdapEnabled
             {adminGeneralSecurityContainer.state.isLdapEnabled
               && <button type="button" className="btn btn-default ml-2" onClick={this.openLdapAuthTestModal}>{t('security_setting.ldap.test_config')}</button>
               && <button type="button" className="btn btn-default ml-2" onClick={this.openLdapAuthTestModal}>{t('security_setting.ldap.test_config')}</button>
             }
             }

+ 10 - 4
src/client/js/components/Admin/Security/LocalSecuritySetting.jsx

@@ -16,7 +16,7 @@ class LocalSecuritySetting extends React.Component {
     super(props);
     super(props);
 
 
     this.state = {
     this.state = {
-      retrieveError: null,
+      isRetrieving: true,
     };
     };
     this.onClickSubmit = this.onClickSubmit.bind(this);
     this.onClickSubmit = this.onClickSubmit.bind(this);
   }
   }
@@ -29,8 +29,8 @@ class LocalSecuritySetting extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      this.setState({ retrieveError: err.message });
     }
     }
+    this.setState({ isRetrieving: false });
   }
   }
 
 
 
 
@@ -49,6 +49,10 @@ class LocalSecuritySetting extends React.Component {
     const { t, adminGeneralSecurityContainer, adminLocalSecurityContainer } = this.props;
     const { t, adminGeneralSecurityContainer, adminLocalSecurityContainer } = this.props;
     const { registrationMode } = adminLocalSecurityContainer.state;
     const { registrationMode } = adminLocalSecurityContainer.state;
 
 
+    if (this.state.isRetrieving) {
+      return null;
+    }
+
     return (
     return (
       <React.Fragment>
       <React.Fragment>
         {this.state.retrieveError != null && (
         {this.state.retrieveError != null && (
@@ -142,7 +146,7 @@ class LocalSecuritySetting extends React.Component {
                     className="form-control"
                     className="form-control"
                     type="textarea"
                     type="textarea"
                     name="registrationWhiteList"
                     name="registrationWhiteList"
-                    value={adminLocalSecurityContainer.state.registrationWhiteList.join('\n')}
+                    defaultValue={adminLocalSecurityContainer.state.registrationWhiteList.join('\n')}
                     onChange={e => adminLocalSecurityContainer.changeRegistrationWhiteList(e.target.value)}
                     onChange={e => adminLocalSecurityContainer.changeRegistrationWhiteList(e.target.value)}
                   />
                   />
                   <p className="help-block small">{t('security_setting.restrict_emails')}<br />{t('security_setting.for_instance')}
                   <p className="help-block small">{t('security_setting.restrict_emails')}<br />{t('security_setting.for_instance')}
@@ -157,7 +161,9 @@ class LocalSecuritySetting extends React.Component {
 
 
         <div className="row my-3">
         <div className="row my-3">
           <div className="col-xs-offset-3 col-xs-5">
           <div className="col-xs-offset-3 col-xs-5">
-            <button type="button" className="btn btn-primary" disabled={this.state.retrieveError != null} onClick={this.onClickSubmit}>{t('Update')}</button>
+            <button type="button" className="btn btn-primary" disabled={adminLocalSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
+              {t('Update')}
+            </button>
           </div>
           </div>
         </div>
         </div>
 
 

+ 18 - 23
src/client/js/components/Admin/Security/OidcSecuritySetting.jsx

@@ -2,7 +2,6 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -11,15 +10,13 @@ import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminOidcSecurityContainer from '../../../services/AdminOidcSecurityContainer';
 import AdminOidcSecurityContainer from '../../../services/AdminOidcSecurityContainer';
 
 
-const logger = loggerFactory('growi:security:AdminGoogleSecurityContainer');
-
 class OidcSecurityManagement extends React.Component {
 class OidcSecurityManagement extends React.Component {
 
 
   constructor(props) {
   constructor(props) {
     super(props);
     super(props);
 
 
     this.state = {
     this.state = {
-      retrieveError: null,
+      isRetrieving: true,
     };
     };
 
 
     this.onClickSubmit = this.onClickSubmit.bind(this);
     this.onClickSubmit = this.onClickSubmit.bind(this);
@@ -33,9 +30,8 @@ class OidcSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      this.setState({ retrieveError: err.message });
-      logger.error(err);
     }
     }
+    this.setState({ isRetrieving: false });
   }
   }
 
 
   async onClickSubmit() {
   async onClickSubmit() {
@@ -47,13 +43,16 @@ class OidcSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      logger.error(err);
     }
     }
   }
   }
 
 
   render() {
   render() {
     const { t, adminGeneralSecurityContainer, adminOidcSecurityContainer } = this.props;
     const { t, adminGeneralSecurityContainer, adminOidcSecurityContainer } = this.props;
 
 
+    if (this.state.isRetrieving) {
+      return null;
+    }
+
     return (
     return (
 
 
       <React.Fragment>
       <React.Fragment>
@@ -62,12 +61,6 @@ class OidcSecurityManagement extends React.Component {
           {t('security_setting.OAuth.OIDC.name')} {t('security_setting.configuration')}
           {t('security_setting.OAuth.OIDC.name')} {t('security_setting.configuration')}
         </h2>
         </h2>
 
 
-        {this.state.retrieveError != null && (
-          <div className="alert alert-danger">
-            <p>{t('Error occurred')} : {this.state.err}</p>
-          </div>
-        )}
-
         <div className="row mb-5">
         <div className="row mb-5">
           <strong className="col-xs-3 text-right">{t('security_setting.OAuth.OIDC.name')}</strong>
           <strong className="col-xs-3 text-right">{t('security_setting.OAuth.OIDC.name')}</strong>
           <div className="col-xs-6 text-left">
           <div className="col-xs-6 text-left">
@@ -117,7 +110,7 @@ class OidcSecurityManagement extends React.Component {
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
                   name="oidcProviderName"
                   name="oidcProviderName"
-                  value={adminOidcSecurityContainer.state.oidcProviderName || ''}
+                  defaultValue={adminOidcSecurityContainer.state.oidcProviderName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcProviderName(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcProviderName(e.target.value)}
                 />
                 />
               </div>
               </div>
@@ -130,7 +123,7 @@ class OidcSecurityManagement extends React.Component {
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
                   name="oidcIssuerHost"
                   name="oidcIssuerHost"
-                  value={adminOidcSecurityContainer.state.oidcIssuerHost || ''}
+                  defaultValue={adminOidcSecurityContainer.state.oidcIssuerHost || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcIssuerHost(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcIssuerHost(e.target.value)}
                 />
                 />
                 <p className="help-block">
                 <p className="help-block">
@@ -146,7 +139,7 @@ class OidcSecurityManagement extends React.Component {
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
                   name="oidcClientId"
                   name="oidcClientId"
-                  value={adminOidcSecurityContainer.state.oidcClientId || ''}
+                  defaultValue={adminOidcSecurityContainer.state.oidcClientId || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientId(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientId(e.target.value)}
                 />
                 />
                 <p className="help-block">
                 <p className="help-block">
@@ -162,7 +155,7 @@ class OidcSecurityManagement extends React.Component {
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
                   name="oidcClientSecret"
                   name="oidcClientSecret"
-                  value={adminOidcSecurityContainer.state.oidcClientSecret || ''}
+                  defaultValue={adminOidcSecurityContainer.state.oidcClientSecret || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientSecret(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientSecret(e.target.value)}
                 />
                 />
                 <p className="help-block">
                 <p className="help-block">
@@ -182,7 +175,7 @@ class OidcSecurityManagement extends React.Component {
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
                   name="oidcAttrMapId"
                   name="oidcAttrMapId"
-                  value={adminOidcSecurityContainer.state.oidcAttrMapId || ''}
+                  defaultValue={adminOidcSecurityContainer.state.oidcAttrMapId || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapId(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapId(e.target.value)}
                 />
                 />
                 <p className="help-block">
                 <p className="help-block">
@@ -198,7 +191,7 @@ class OidcSecurityManagement extends React.Component {
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
                   name="oidcAttrMapUserName"
                   name="oidcAttrMapUserName"
-                  value={adminOidcSecurityContainer.state.oidcAttrMapUserName || ''}
+                  defaultValue={adminOidcSecurityContainer.state.oidcAttrMapUserName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapUserName(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapUserName(e.target.value)}
                 />
                 />
                 <p className="help-block">
                 <p className="help-block">
@@ -214,7 +207,7 @@ class OidcSecurityManagement extends React.Component {
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
                   name="oidcAttrMapName"
                   name="oidcAttrMapName"
-                  value={adminOidcSecurityContainer.state.oidcAttrMapName || ''}
+                  defaultValue={adminOidcSecurityContainer.state.oidcAttrMapName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapName(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapName(e.target.value)}
                 />
                 />
                 <p className="help-block">
                 <p className="help-block">
@@ -230,7 +223,7 @@ class OidcSecurityManagement extends React.Component {
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
                   name="oidcAttrMapEmail"
                   name="oidcAttrMapEmail"
-                  value={adminOidcSecurityContainer.state.oidcAttrMapEmail || ''}
+                  defaultValue={adminOidcSecurityContainer.state.oidcAttrMapEmail || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapEmail(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapEmail(e.target.value)}
                 />
                 />
                 <p className="help-block">
                 <p className="help-block">
@@ -245,7 +238,7 @@ class OidcSecurityManagement extends React.Component {
                 <input
                 <input
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
-                  value={adminOidcSecurityContainer.state.callbackUrl || ''}
+                  defaultValue={adminOidcSecurityContainer.state.callbackUrl || ''}
                   readOnly
                   readOnly
                 />
                 />
                 <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
                 <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
@@ -306,7 +299,9 @@ class OidcSecurityManagement extends React.Component {
 
 
         <div className="row my-3">
         <div className="row my-3">
           <div className="col-xs-offset-3 col-xs-5">
           <div className="col-xs-offset-3 col-xs-5">
-            <button type="button" className="btn btn-primary" disabled={this.state.retrieveError != null} onClick={this.onClickSubmit}>{t('Update')}</button>
+            <button type="button" className="btn btn-primary" disabled={adminOidcSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
+              {t('Update')}
+            </button>
           </div>
           </div>
         </div>
         </div>
 
 

+ 9 - 15
src/client/js/components/Admin/Security/SamlSecuritySetting.jsx

@@ -2,7 +2,6 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -11,15 +10,13 @@ import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminSamlSecurityContainer from '../../../services/AdminSamlSecurityContainer';
 import AdminSamlSecurityContainer from '../../../services/AdminSamlSecurityContainer';
 
 
-const logger = loggerFactory('growi:security:AdminSamlSecurityContainer');
-
 class SamlSecurityManagement extends React.Component {
 class SamlSecurityManagement extends React.Component {
 
 
   constructor(props) {
   constructor(props) {
     super(props);
     super(props);
 
 
     this.state = {
     this.state = {
-      retrieveError: null,
+      isRetrieving: true,
       envEntryPoint: '',
       envEntryPoint: '',
       envIssuer: '',
       envIssuer: '',
       envCert: '',
       envCert: '',
@@ -51,9 +48,8 @@ class SamlSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      this.setState({ retrieveError: err.message });
-      logger.error(err);
     }
     }
+    this.setState({ isRetrieving: false });
   }
   }
 
 
   async onClickSubmit() {
   async onClickSubmit() {
@@ -65,7 +61,6 @@ class SamlSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      logger.error(err);
     }
     }
   }
   }
 
 
@@ -73,6 +68,9 @@ class SamlSecurityManagement extends React.Component {
     const { t, adminGeneralSecurityContainer, adminSamlSecurityContainer } = this.props;
     const { t, adminGeneralSecurityContainer, adminSamlSecurityContainer } = this.props;
     const { useOnlyEnvVars } = adminSamlSecurityContainer.state;
     const { useOnlyEnvVars } = adminSamlSecurityContainer.state;
 
 
+    if (this.state.isRetrieving) {
+      return null;
+    }
     return (
     return (
       <React.Fragment>
       <React.Fragment>
 
 
@@ -80,12 +78,6 @@ class SamlSecurityManagement extends React.Component {
           {t('security_setting.SAML.name')} {t('security_setting.configuration')}
           {t('security_setting.SAML.name')} {t('security_setting.configuration')}
         </h2>
         </h2>
 
 
-        {this.state.retrieveError != null && (
-          <div className="alert alert-danger">
-            <p>{t('Error occurred')} : {this.state.err}</p>
-          </div>
-        )}
-
         {useOnlyEnvVars && (
         {useOnlyEnvVars && (
           <p
           <p
             className="alert alert-info"
             className="alert alert-info"
@@ -219,7 +211,7 @@ class SamlSecurityManagement extends React.Component {
                       rows="5"
                       rows="5"
                       name="samlCert"
                       name="samlCert"
                       readOnly={useOnlyEnvVars}
                       readOnly={useOnlyEnvVars}
-                      value={adminSamlSecurityContainer.state.samlCert}
+                      defaultValue={adminSamlSecurityContainer.state.samlCert}
                       onChange={e => adminSamlSecurityContainer.changeSamlCert(e.target.value)}
                       onChange={e => adminSamlSecurityContainer.changeSamlCert(e.target.value)}
                     />
                     />
                     <p className="help-block">
                     <p className="help-block">
@@ -467,7 +459,9 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
 
 
         <div className="row my-3">
         <div className="row my-3">
           <div className="col-xs-offset-3 col-xs-5">
           <div className="col-xs-offset-3 col-xs-5">
-            <button type="button" className="btn btn-primary" disabled={this.state.retrieveError != null} onClick={this.onClickSubmit}>{t('Update')}</button>
+            <button type="button" className="btn btn-primary" disabled={adminSamlSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
+              {t('Update')}
+            </button>
           </div>
           </div>
         </div>
         </div>
 
 

+ 8 - 7
src/client/js/components/Admin/Security/TwitterSecuritySetting.jsx

@@ -2,7 +2,6 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '@alias/logger';
 
 
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -11,8 +10,6 @@ import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminTwitterSecurityContainer from '../../../services/AdminTwitterSecurityContainer';
 import AdminTwitterSecurityContainer from '../../../services/AdminTwitterSecurityContainer';
 
 
-const logger = loggerFactory('growi:security:AdminTwitterSecurityContainer');
-
 class TwitterSecurityManagement extends React.Component {
 class TwitterSecurityManagement extends React.Component {
 
 
   constructor(props) {
   constructor(props) {
@@ -33,9 +30,8 @@ class TwitterSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      this.setState({ retrieveError: err.message });
-      logger.error(err);
     }
     }
+    this.setState({ isRetrieving: false });
   }
   }
 
 
   async onClickSubmit() {
   async onClickSubmit() {
@@ -47,12 +43,15 @@ class TwitterSecurityManagement extends React.Component {
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
-      logger.error(err);
     }
     }
   }
   }
 
 
   render() {
   render() {
     const { t, adminGeneralSecurityContainer, adminTwitterSecurityContainer } = this.props;
     const { t, adminGeneralSecurityContainer, adminTwitterSecurityContainer } = this.props;
+
+    if (this.state.isRetrieving) {
+      return null;
+    }
     return (
     return (
 
 
       <React.Fragment>
       <React.Fragment>
@@ -167,7 +166,9 @@ class TwitterSecurityManagement extends React.Component {
 
 
         <div className="row my-3">
         <div className="row my-3">
           <div className="col-xs-offset-3 col-xs-5">
           <div className="col-xs-offset-3 col-xs-5">
-            <button type="button" className="btn btn-primary" disabled={this.state.retrieveError != null} onClick={this.onClickSubmit}>{t('Update')}</button>
+            <button type="button" className="btn btn-primary" disabled={adminTwitterSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
+              {t('Update')}
+            </button>
           </div>
           </div>
         </div>
         </div>
 
 

+ 16 - 5
src/client/js/services/AdminBasicSecurityContainer.js

@@ -1,7 +1,10 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
+import loggerFactory from '@alias/logger';
 
 
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 
 
+const logger = loggerFactory('growi:security:AdminTwitterSecurityContainer');
+
 /**
 /**
  * Service container for admin security page (BasicSecuritySetting.jsx)
  * Service container for admin security page (BasicSecuritySetting.jsx)
  * @extends {Container} unstated Container
  * @extends {Container} unstated Container
@@ -14,6 +17,7 @@ export default class AdminBasicSecurityContainer extends Container {
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {
+      retrieveError: null,
       isSameUsernameTreatedAsIdenticalUser: false,
       isSameUsernameTreatedAsIdenticalUser: false,
     };
     };
 
 
@@ -23,11 +27,18 @@ export default class AdminBasicSecurityContainer extends Container {
    * retrieve security data
    * retrieve security data
    */
    */
   async retrieveSecurityData() {
   async retrieveSecurityData() {
-    const response = await this.appContainer.apiv3.get('/security-setting/');
-    const { basicAuth } = response.data.securityParams;
-    this.setState({
-      isSameUsernameTreatedAsIdenticalUser: basicAuth.isSameUsernameTreatedAsIdenticalUser,
-    });
+    try {
+      const response = await this.appContainer.apiv3.get('/security-setting/');
+      const { basicAuth } = response.data.securityParams;
+      this.setState({
+        isSameUsernameTreatedAsIdenticalUser: basicAuth.isSameUsernameTreatedAsIdenticalUser,
+      });
+    }
+    catch (err) {
+      this.setState({ retrieveError: err });
+      logger.error(err);
+      throw new Error('Failed to fetch data');
+    }
   }
   }
 
 
   /**
   /**

+ 17 - 7
src/client/js/services/AdminGitHubSecurityContainer.js

@@ -1,9 +1,11 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
+import loggerFactory from '@alias/logger';
 
 
 import { pathUtils } from 'growi-commons';
 import { pathUtils } from 'growi-commons';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 
 
+const logger = loggerFactory('growi:security:AdminGitHubSecurityContainer');
 
 
 /**
 /**
  * Service container for admin security page (GitHubSecurityManagement.jsx)
  * Service container for admin security page (GitHubSecurityManagement.jsx)
@@ -17,6 +19,7 @@ export default class AdminGitHubSecurityContainer extends Container {
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {
+      retrieveError: null,
       appSiteUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/github/callback'),
       appSiteUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/github/callback'),
       githubClientId: '',
       githubClientId: '',
       githubClientSecret: '',
       githubClientSecret: '',
@@ -29,13 +32,20 @@ export default class AdminGitHubSecurityContainer extends Container {
    * retrieve security data
    * retrieve security data
    */
    */
   async retrieveSecurityData() {
   async retrieveSecurityData() {
-    const response = await this.appContainer.apiv3.get('/security-setting/');
-    const { githubOAuth } = response.data.securityParams;
-    this.setState({
-      githubClientId: githubOAuth.githubClientId,
-      githubClientSecret: githubOAuth.githubClientSecret,
-      isSameUsernameTreatedAsIdenticalUser: githubOAuth.isSameUsernameTreatedAsIdenticalUser,
-    });
+    try {
+      const response = await this.appContainer.apiv3.get('/security-setting/');
+      const { githubOAuth } = response.data.securityParams;
+      this.setState({
+        githubClientId: githubOAuth.githubClientId,
+        githubClientSecret: githubOAuth.githubClientSecret,
+        isSameUsernameTreatedAsIdenticalUser: githubOAuth.isSameUsernameTreatedAsIdenticalUser,
+      });
+    }
+    catch (err) {
+      this.setState({ retrieveError: err });
+      logger.error(err);
+      throw new Error('Failed to fetch data');
+    }
   }
   }
 
 
   /**
   /**

+ 18 - 7
src/client/js/services/AdminGoogleSecurityContainer.js

@@ -1,9 +1,12 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
+import loggerFactory from '@alias/logger';
 
 
 import { pathUtils } from 'growi-commons';
 import { pathUtils } from 'growi-commons';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 
 
+const logger = loggerFactory('growi:security:AdminGoogleSecurityContainer');
+
 /**
 /**
  * Service container for admin security page (GoogleSecurityManagement.jsx)
  * Service container for admin security page (GoogleSecurityManagement.jsx)
  * @extends {Container} unstated Container
  * @extends {Container} unstated Container
@@ -16,6 +19,7 @@ export default class AdminGoogleSecurityContainer extends Container {
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {
+      retrieveError: null,
       callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/google/callback'),
       callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/google/callback'),
       googleClientId: '',
       googleClientId: '',
       googleClientSecret: '',
       googleClientSecret: '',
@@ -29,13 +33,20 @@ export default class AdminGoogleSecurityContainer extends Container {
    * retrieve security data
    * retrieve security data
    */
    */
   async retrieveSecurityData() {
   async retrieveSecurityData() {
-    const response = await this.appContainer.apiv3.get('/security-setting/');
-    const { googleOAuth } = response.data.securityParams;
-    this.setState({
-      googleClientId: googleOAuth.googleClientId,
-      googleClientSecret: googleOAuth.googleClientSecret,
-      isSameUsernameTreatedAsIdenticalUser: googleOAuth.isSameUsernameTreatedAsIdenticalUser,
-    });
+    try {
+      const response = await this.appContainer.apiv3.get('/security-setting/');
+      const { googleOAuth } = response.data.securityParams;
+      this.setState({
+        googleClientId: googleOAuth.googleClientId,
+        googleClientSecret: googleOAuth.googleClientSecret,
+        isSameUsernameTreatedAsIdenticalUser: googleOAuth.isSameUsernameTreatedAsIdenticalUser,
+      });
+    }
+    catch (err) {
+      this.setState({ retrieveError: err });
+      logger.error(err);
+      throw new Error('Failed to fetch data');
+    }
   }
   }
 
 
   /**
   /**

+ 27 - 16
src/client/js/services/AdminLdapSecurityContainer.js

@@ -1,7 +1,10 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
+import loggerFactory from '@alias/logger';
 
 
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 
 
+const logger = loggerFactory('growi:services:AdminLdapSecurityContainer');
+
 /**
 /**
  * Service container for admin security page (SecurityLdapSetting.jsx)
  * Service container for admin security page (SecurityLdapSetting.jsx)
  * @extends {Container} unstated Container
  * @extends {Container} unstated Container
@@ -14,6 +17,7 @@ export default class AdminLdapSecurityContainer extends Container {
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {
+      retrieveError: null,
       serverUrl: '',
       serverUrl: '',
       isUserBind: false,
       isUserBind: false,
       ldapBindDN: '',
       ldapBindDN: '',
@@ -34,22 +38,29 @@ export default class AdminLdapSecurityContainer extends Container {
    * retrieve security data
    * retrieve security data
    */
    */
   async retrieveSecurityData() {
   async retrieveSecurityData() {
-    const response = await this.appContainer.apiv3.get('/security-setting/');
-    const { ldapAuth } = response.data.securityParams;
-    this.setState({
-      serverUrl: ldapAuth.serverUrl,
-      isUserBind: ldapAuth.isUserBind,
-      ldapBindDN: ldapAuth.ldapBindDN,
-      ldapBindDNPassword: ldapAuth.ldapBindDNPassword,
-      ldapSearchFilter: ldapAuth.ldapSearchFilter,
-      ldapAttrMapUsername: ldapAuth.ldapAttrMapUsername,
-      isSameUsernameTreatedAsIdenticalUser: ldapAuth.isSameUsernameTreatedAsIdenticalUser,
-      ldapAttrMapMail: ldapAuth.ldapAttrMapMail,
-      ldapAttrMapName: ldapAuth.ldapAttrMapName,
-      ldapGroupSearchBase: ldapAuth.ldapGroupSearchBase,
-      ldapGroupSearchFilter: ldapAuth.ldapGroupSearchFilter,
-      ldapGroupDnProperty: ldapAuth.ldapGroupDnProperty,
-    });
+    try {
+      const response = await this.appContainer.apiv3.get('/security-setting/');
+      const { ldapAuth } = response.data.securityParams;
+      this.setState({
+        serverUrl: ldapAuth.serverUrl,
+        isUserBind: ldapAuth.isUserBind,
+        ldapBindDN: ldapAuth.ldapBindDN,
+        ldapBindDNPassword: ldapAuth.ldapBindDNPassword,
+        ldapSearchFilter: ldapAuth.ldapSearchFilter,
+        ldapAttrMapUsername: ldapAuth.ldapAttrMapUsername,
+        isSameUsernameTreatedAsIdenticalUser: ldapAuth.isSameUsernameTreatedAsIdenticalUser,
+        ldapAttrMapMail: ldapAuth.ldapAttrMapMail,
+        ldapAttrMapName: ldapAuth.ldapAttrMapName,
+        ldapGroupSearchBase: ldapAuth.ldapGroupSearchBase,
+        ldapGroupSearchFilter: ldapAuth.ldapGroupSearchFilter,
+        ldapGroupDnProperty: ldapAuth.ldapGroupDnProperty,
+      });
+    }
+    catch (err) {
+      this.setState({ retrieveError: err });
+      logger.error(err);
+      throw new Error('Failed to fetch data');
+    }
   }
   }
 
 
 
 

+ 18 - 6
src/client/js/services/AdminLocalSecurityContainer.js

@@ -1,5 +1,8 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
+import loggerFactory from '@alias/logger';
 
 
+// eslint-disable-next-line no-unused-vars
+const logger = loggerFactory('growi:services:AdminLocalSecurityContainer');
 /**
 /**
  * Service container for admin security page (LocalSecuritySetting.jsx)
  * Service container for admin security page (LocalSecuritySetting.jsx)
  * @extends {Container} unstated Container
  * @extends {Container} unstated Container
@@ -12,6 +15,7 @@ export default class AdminLocalSecurityContainer extends Container {
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {
+      retrieveError: null,
       registrationMode: 'Open',
       registrationMode: 'Open',
       registrationWhiteList: [],
       registrationWhiteList: [],
     };
     };
@@ -19,12 +23,20 @@ export default class AdminLocalSecurityContainer extends Container {
   }
   }
 
 
   async retrieveSecurityData() {
   async retrieveSecurityData() {
-    const response = await this.appContainer.apiv3.get('/security-setting/');
-    const { localSetting } = response.data.securityParams;
-    this.setState({
-      registrationMode: localSetting.registrationMode,
-      registrationWhiteList: localSetting.registrationWhiteList,
-    });
+    try {
+      const response = await this.appContainer.apiv3.get('/security-setting/');
+      const { localSetting } = response.data.securityParams;
+      this.setState({
+        registrationMode: localSetting.registrationMode,
+        registrationWhiteList: localSetting.registrationWhiteList,
+      });
+    }
+    catch (err) {
+      this.setState({ retrieveError: err });
+      logger.error(err);
+      throw new Error('Failed to fetch data');
+    }
+
   }
   }
 
 
   /**
   /**

+ 25 - 14
src/client/js/services/AdminOidcSecurityContainer.js

@@ -1,9 +1,12 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
+import loggerFactory from '@alias/logger';
 
 
 import { pathUtils } from 'growi-commons';
 import { pathUtils } from 'growi-commons';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 
 
+const logger = loggerFactory('growi:services:AdminLdapSecurityContainer');
+
 /**
 /**
  * Service container for admin security page (OidcSecurityManagement.jsx)
  * Service container for admin security page (OidcSecurityManagement.jsx)
  * @extends {Container} unstated Container
  * @extends {Container} unstated Container
@@ -16,6 +19,7 @@ export default class AdminOidcSecurityContainer extends Container {
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {
+      retrieveError: null,
       callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/oidc/callback'),
       callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/oidc/callback'),
       oidcProviderName: '',
       oidcProviderName: '',
       oidcIssuerHost: '',
       oidcIssuerHost: '',
@@ -35,20 +39,27 @@ export default class AdminOidcSecurityContainer extends Container {
    * retrieve security data
    * retrieve security data
    */
    */
   async retrieveSecurityData() {
   async retrieveSecurityData() {
-    const response = await this.appContainer.apiv3.get('/security-setting/');
-    const { oidcAuth } = response.data.securityParams;
-    this.setState({
-      oidcProviderName: oidcAuth.oidcProviderName,
-      oidcIssuerHost: oidcAuth.oidcIssuerHost,
-      oidcClientId: oidcAuth.oidcClientId,
-      oidcClientSecret: oidcAuth.oidcClientSecret,
-      oidcAttrMapId: oidcAuth.oidcAttrMapId,
-      oidcAttrMapUserName: oidcAuth.oidcAttrMapUserName,
-      oidcAttrMapName: oidcAuth.oidcAttrMapName,
-      oidcAttrMapEmail: oidcAuth.oidcAttrMapEmail,
-      isSameUsernameTreatedAsIdenticalUser: oidcAuth.isSameUsernameTreatedAsIdenticalUser,
-      isSameEmailTreatedAsIdenticalUser: oidcAuth.isSameEmailTreatedAsIdenticalUser,
-    });
+    try {
+      const response = await this.appContainer.apiv3.get('/security-setting/');
+      const { oidcAuth } = response.data.securityParams;
+      this.setState({
+        oidcProviderName: oidcAuth.oidcProviderName,
+        oidcIssuerHost: oidcAuth.oidcIssuerHost,
+        oidcClientId: oidcAuth.oidcClientId,
+        oidcClientSecret: oidcAuth.oidcClientSecret,
+        oidcAttrMapId: oidcAuth.oidcAttrMapId,
+        oidcAttrMapUserName: oidcAuth.oidcAttrMapUserName,
+        oidcAttrMapName: oidcAuth.oidcAttrMapName,
+        oidcAttrMapEmail: oidcAuth.oidcAttrMapEmail,
+        isSameUsernameTreatedAsIdenticalUser: oidcAuth.isSameUsernameTreatedAsIdenticalUser,
+        isSameEmailTreatedAsIdenticalUser: oidcAuth.isSameEmailTreatedAsIdenticalUser,
+      });
+    }
+    catch (err) {
+      this.setState({ retrieveError: err });
+      logger.error(err);
+      throw new Error('Failed to fetch data');
+    }
   }
   }
 
 
   /**
   /**

+ 28 - 16
src/client/js/services/AdminSamlSecurityContainer.js

@@ -1,9 +1,13 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
+import loggerFactory from '@alias/logger';
+
 import { pathUtils } from 'growi-commons';
 import { pathUtils } from 'growi-commons';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 
 
+const logger = loggerFactory('growi:security:AdminSamlSecurityContainer');
+
 /**
 /**
  * Service container for admin security page (SecuritySamlSetting.jsx)
  * Service container for admin security page (SecuritySamlSetting.jsx)
  * @extends {Container} unstated Container
  * @extends {Container} unstated Container
@@ -16,6 +20,7 @@ export default class AdminSamlSecurityContainer extends Container {
     this.appContainer = appContainer;
     this.appContainer = appContainer;
 
 
     this.state = {
     this.state = {
+      retrieveError: null,
       useOnlyEnvVars: false,
       useOnlyEnvVars: false,
       callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/saml/callback'),
       callbackUrl: urljoin(pathUtils.removeTrailingSlash(appContainer.config.crowi.url), '/passport/saml/callback'),
       missingMandatoryConfigKeys: [],
       missingMandatoryConfigKeys: [],
@@ -37,22 +42,29 @@ export default class AdminSamlSecurityContainer extends Container {
    * retrieve security data
    * retrieve security data
    */
    */
   async retrieveSecurityData() {
   async retrieveSecurityData() {
-    const response = await this.appContainer.apiv3.get('/security-setting/');
-    const { samlAuth } = response.data.securityParams;
-    this.setState({
-      missingMandatoryConfigKeys: samlAuth.missingMandatoryConfigKeys,
-      samlEntryPoint: samlAuth.samlEntryPoint,
-      samlIssuer: samlAuth.samlIssuer,
-      samlCert: samlAuth.samlCert,
-      samlAttrMapId: samlAuth.samlAttrMapId,
-      samlAttrMapUserName: samlAuth.samlAttrMapUserName,
-      samlAttrMapMail: samlAuth.samlAttrMapMail,
-      samlAttrMapFirstName: samlAuth.samlAttrMapFirstName,
-      samlAttrMapLastName: samlAuth.samlAttrMapLastName,
-      isSameUsernameTreatedAsIdenticalUser: samlAuth.isSameUsernameTreatedAsIdenticalUser,
-      isSameEmailTreatedAsIdenticalUser: samlAuth.isSameEmailTreatedAsIdenticalUser,
-    });
-    return samlAuth;
+    try {
+      const response = await this.appContainer.apiv3.get('/security-setting/');
+      const { samlAuth } = response.data.securityParams;
+      this.setState({
+        missingMandatoryConfigKeys: samlAuth.missingMandatoryConfigKeys,
+        samlEntryPoint: samlAuth.samlEntryPoint,
+        samlIssuer: samlAuth.samlIssuer,
+        samlCert: samlAuth.samlCert,
+        samlAttrMapId: samlAuth.samlAttrMapId,
+        samlAttrMapUserName: samlAuth.samlAttrMapUserName,
+        samlAttrMapMail: samlAuth.samlAttrMapMail,
+        samlAttrMapFirstName: samlAuth.samlAttrMapFirstName,
+        samlAttrMapLastName: samlAuth.samlAttrMapLastName,
+        isSameUsernameTreatedAsIdenticalUser: samlAuth.isSameUsernameTreatedAsIdenticalUser,
+        isSameEmailTreatedAsIdenticalUser: samlAuth.isSameEmailTreatedAsIdenticalUser,
+      });
+      return samlAuth;
+    }
+    catch (err) {
+      this.setState({ retrieveError: err });
+      logger.error(err);
+      throw new Error('Failed to fetch data');
+    }
   }
   }
 
 
   /**
   /**

+ 16 - 7
src/client/js/services/AdminTwitterSecurityContainer.js

@@ -1,9 +1,11 @@
 import { Container } from 'unstated';
 import { Container } from 'unstated';
+import loggerFactory from '@alias/logger';
 
 
 import { pathUtils } from 'growi-commons';
 import { pathUtils } from 'growi-commons';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 import removeNullPropertyFromObject from '../../../lib/util/removeNullPropertyFromObject';
 
 
+const logger = loggerFactory('growi:security:AdminTwitterSecurityContainer');
 
 
 /**
 /**
  * Service container for admin security page (TwitterSecurityManagement.jsx)
  * Service container for admin security page (TwitterSecurityManagement.jsx)
@@ -30,13 +32,20 @@ export default class AdminTwitterSecurityContainer extends Container {
    * retrieve security data
    * retrieve security data
    */
    */
   async retrieveSecurityData() {
   async retrieveSecurityData() {
-    const response = await this.appContainer.apiv3.get('/security-setting/');
-    const { twitterOAuth } = response.data.securityParams;
-    this.setState({
-      twitterConsumerKey: twitterOAuth.twitterConsumerKey,
-      twitterConsumerSecret: twitterOAuth.twitterConsumerSecret,
-      isSameUsernameTreatedAsIdenticalUser: twitterOAuth.isSameUsernameTreatedAsIdenticalUser,
-    });
+    try {
+      const response = await this.appContainer.apiv3.get('/security-setting/');
+      const { twitterOAuth } = response.data.securityParams;
+      this.setState({
+        twitterConsumerKey: twitterOAuth.twitterConsumerKey,
+        twitterConsumerSecret: twitterOAuth.twitterConsumerSecret,
+        isSameUsernameTreatedAsIdenticalUser: twitterOAuth.isSameUsernameTreatedAsIdenticalUser,
+      });
+    }
+    catch (err) {
+      this.setState({ retrieveError: err });
+      logger.error(err);
+      throw new Error('Failed to fetch data');
+    }
   }
   }
 
 
   /**
   /**

+ 0 - 7
src/server/form/admin/custombehavior.js

@@ -1,7 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:behavior]'),
-);

+ 0 - 7
src/server/form/admin/customcss.js

@@ -1,7 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:css]'),
-);

+ 0 - 12
src/server/form/admin/customfeatures.js

@@ -1,12 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:isEnabledTimeline]').trim().toBooleanStrict(),
-  field('settingForm[customize:isEnabledDeleteCompletely]').trim().toBooleanStrict(),
-  field('settingForm[customize:isSavedStatesOfTabChanges]').trim().toBooleanStrict(),
-  field('settingForm[customize:isEnabledAttachTitleHeader]').trim().toBooleanStrict(),
-  field('settingForm[customize:showRecentCreatedNumber]').trim().toInt(),
-  field('settingForm[customize:isEnabledStaleNotification]').trim().toBooleanStrict(),
-);

+ 0 - 7
src/server/form/admin/customheader.js

@@ -1,7 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:header]'),
-);

+ 0 - 8
src/server/form/admin/customhighlightJsStyle.js

@@ -1,8 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:highlightJsStyle]'),
-  field('settingForm[customize:highlightJsStyleBorder]').trim().toBooleanStrict(),
-);

+ 0 - 8
src/server/form/admin/customlayout.js

@@ -1,8 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:layout]'),
-  field('settingForm[customize:theme]'),
-);

+ 0 - 7
src/server/form/admin/customscript.js

@@ -1,7 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:script]'),
-);

+ 0 - 7
src/server/form/admin/customtheme.js

@@ -1,7 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:theme]'),
-);

+ 0 - 7
src/server/form/admin/customtitle.js

@@ -1,7 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[customize:title]'),
-);

+ 0 - 8
src/server/form/admin/securityPassportBasic.js

@@ -1,8 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[security:passport-basic:isEnabled]').trim().toBooleanStrict().required(),
-  field('settingForm[security:passport-basic:isSameUsernameTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-);

+ 0 - 10
src/server/form/admin/securityPassportGitHub.js

@@ -1,10 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[security:passport-github:isEnabled]').trim().toBooleanStrict().required(),
-  field('settingForm[security:passport-github:clientId]').trim(),
-  field('settingForm[security:passport-github:clientSecret]').trim(),
-  field('settingForm[security:passport-github:isSameUsernameTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-);

+ 0 - 10
src/server/form/admin/securityPassportGoogle.js

@@ -1,10 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[security:passport-google:isEnabled]').trim().toBooleanStrict().required(),
-  field('settingForm[security:passport-google:clientId]').trim(),
-  field('settingForm[security:passport-google:clientSecret]').trim(),
-  field('settingForm[security:passport-google:isSameUsernameTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-);

+ 0 - 23
src/server/form/admin/securityPassportLdap.js

@@ -1,23 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[security:passport-ldap:isEnabled]').trim().toBooleanStrict().required(),
-  field('settingForm[security:passport-ldap:serverUrl]').trim()
-  // https://regex101.com/r/E0UL6D/1
-    .is(/^ldaps?:\/\/([^/\s]+)\/([^/\s]+)$/, 'Server URL is invalid. <small><a href="https://regex101.com/r/E0UL6D/1">&gt;&gt; Regex</a></small>'),
-  field('settingForm[security:passport-ldap:isUserBind]').trim().toBooleanStrict(),
-  field('settingForm[security:passport-ldap:bindDN]').trim()
-  // https://regex101.com/r/jK8lpO/1
-    .is(/^(,?[^,=\s]+=[^,=\s]+){1,}$|^[^@\s]+@[^@\s]+$/, 'Bind DN is invalid. <small><a href="https://regex101.com/r/jK8lpO/3">&gt;&gt; Regex</a></small>'),
-  field('settingForm[security:passport-ldap:bindDNPassword]'),
-  field('settingForm[security:passport-ldap:searchFilter]'),
-  field('settingForm[security:passport-ldap:attrMapUsername]'),
-  field('settingForm[security:passport-ldap:attrMapName]'),
-  field('settingForm[security:passport-ldap:attrMapMail]'),
-  field('settingForm[security:passport-ldap:isSameUsernameTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-  field('settingForm[security:passport-ldap:groupSearchBase]'),
-  field('settingForm[security:passport-ldap:groupSearchFilter]'),
-  field('settingForm[security:passport-ldap:groupDnProperty]'),
-);

+ 0 - 11
src/server/form/admin/securityPassportLocal.js

@@ -1,11 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-const stringToArray = require('../../util/formUtil').stringToArrayFilter;
-const normalizeCRLF = require('../../util/formUtil').normalizeCRLFFilter;
-
-module.exports = form(
-  field('settingForm[security:passport-local:isEnabled]').trim().toBooleanStrict().required(),
-  field('settingForm[security:registrationMode]').required(),
-  field('settingForm[security:registrationWhiteList]').custom(normalizeCRLF).custom(stringToArray),
-);

+ 0 - 17
src/server/form/admin/securityPassportOidc.js

@@ -1,17 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[security:passport-oidc:isEnabled]').trim().toBooleanStrict().required(),
-  field('settingForm[security:passport-oidc:providerName]').trim(),
-  field('settingForm[security:passport-oidc:issuerHost]').trim(),
-  field('settingForm[security:passport-oidc:clientId]').trim(),
-  field('settingForm[security:passport-oidc:clientSecret]').trim(),
-  field('settingForm[security:passport-oidc:attrMapId]').trim(),
-  field('settingForm[security:passport-oidc:attrMapUserName]').trim(),
-  field('settingForm[security:passport-oidc:attrMapName]').trim(),
-  field('settingForm[security:passport-oidc:attrMapMail]').trim(),
-  field('settingForm[security:passport-oidc:isSameEmailTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-  field('settingForm[security:passport-oidc:isSameUsernameTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-);

+ 0 - 17
src/server/form/admin/securityPassportSaml.js

@@ -1,17 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[security:passport-saml:isEnabled]').trim().toBooleanStrict(),
-  field('settingForm[security:passport-saml:entryPoint]').trim().isUrl(),
-  field('settingForm[security:passport-saml:issuer]').trim(),
-  field('settingForm[security:passport-saml:attrMapId]').trim(),
-  field('settingForm[security:passport-saml:attrMapUsername]').trim(),
-  field('settingForm[security:passport-saml:attrMapMail]').trim(),
-  field('settingForm[security:passport-saml:attrMapFirstName]').trim(),
-  field('settingForm[security:passport-saml:attrMapLastName]').trim(),
-  field('settingForm[security:passport-saml:cert]').trim(),
-  field('settingForm[security:passport-saml:isSameUsernameTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-  field('settingForm[security:passport-saml:isSameEmailTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-);

+ 0 - 10
src/server/form/admin/securityPassportTwitter.js

@@ -1,10 +0,0 @@
-const form = require('express-form');
-
-const field = form.field;
-
-module.exports = form(
-  field('settingForm[security:passport-twitter:isEnabled]').trim().toBooleanStrict().required(),
-  field('settingForm[security:passport-twitter:consumerKey]').trim(),
-  field('settingForm[security:passport-twitter:consumerSecret]').trim(),
-  field('settingForm[security:passport-twitter:isSameUsernameTreatedAsIdenticalUser]').trim().toBooleanStrict(),
-);