Преглед изворни кода

Merge branch 'support/reactify-login-page-stock' into support/reactify-login-page-back

# Conflicts:
#	src/server/views/login.html
yusuketk пре 6 година
родитељ
комит
1fc57def2b
82 измењених фајлова са 820 додато и 570 уклоњено
  1. 1 1
      config/webpack.common.js
  2. 8 0
      resource/cdn-manifests.js
  3. 3 0
      src/client/js/bootstrap.jsx
  4. 9 9
      src/client/js/components/Admin/App/AppSetting.jsx
  5. 15 15
      src/client/js/components/Admin/App/AwsSetting.jsx
  6. 1 1
      src/client/js/components/Admin/App/SiteUrlSetting.jsx
  7. 1 1
      src/client/js/components/Admin/Common/AdminUpdateButtonRow.jsx
  8. 1 1
      src/client/js/components/Admin/Customize/CustomizeFunctionSetting.jsx
  9. 1 1
      src/client/js/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.jsx
  10. 1 1
      src/client/js/components/Admin/ElasticsearchManagement/ReconnectControls.jsx
  11. 2 2
      src/client/js/components/Admin/ImportDataPage.jsx
  12. 3 0
      src/client/js/components/Admin/Notification/NotificationSetting.jsx
  13. 1 1
      src/client/js/components/Admin/Notification/SlackAppConfiguration.jsx
  14. 2 2
      src/client/js/components/Admin/Security/BasicSecuritySetting.jsx
  15. 4 4
      src/client/js/components/Admin/Security/GitHubSecuritySetting.jsx
  16. 4 4
      src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx
  17. 13 13
      src/client/js/components/Admin/Security/LdapSecuritySetting.jsx
  18. 2 2
      src/client/js/components/Admin/Security/LocalSecuritySetting.jsx
  19. 11 11
      src/client/js/components/Admin/Security/OidcSecuritySetting.jsx
  20. 19 19
      src/client/js/components/Admin/Security/SamlSecuritySetting.jsx
  21. 9 0
      src/client/js/components/Admin/Security/SecurityManagement.jsx
  22. 1 1
      src/client/js/components/Admin/Security/SecuritySetting.jsx
  23. 4 4
      src/client/js/components/Admin/Security/TwitterSecuritySetting.jsx
  24. 4 4
      src/client/js/components/Admin/UserManagement.jsx
  25. 1 1
      src/client/js/components/Admin/Users/UserTable.jsx
  26. 1 1
      src/client/js/components/BookmarkButton.jsx
  27. 1 1
      src/client/js/components/LikeButton.jsx
  28. 39 26
      src/client/js/components/LoginForm.jsx
  29. 1 1
      src/client/js/components/Me/BasicInfoSettings.jsx
  30. 1 1
      src/client/js/components/Me/PasswordSettings.jsx
  31. 0 3
      src/client/js/components/Navbar/GrowiSubNavigation.jsx
  32. 35 0
      src/client/js/components/Navbar/NavbarToggler.jsx
  33. 1 1
      src/client/js/components/Navbar/PersonalDropdown.jsx
  34. 5 10
      src/client/js/components/PageComment/CommentEditor.jsx
  35. 4 9
      src/client/js/components/PageComment/CommentEditorLazyRenderer.jsx
  36. 1 8
      src/client/js/components/PageComment/ReplayComments.jsx
  37. 1 9
      src/client/js/components/PageComments.jsx
  38. 3 3
      src/client/js/components/PageEditor/CodeMirrorEditor.jsx
  39. 1 1
      src/client/js/components/PageEditor/DrawioModal.jsx
  40. 8 9
      src/client/js/components/PageEditor/HandsontableModal.jsx
  41. 2 2
      src/client/js/components/PageEditor/OptionsSelector.jsx
  42. 1 1
      src/client/js/components/PageHistory/RevisionDiff.jsx
  43. 1 4
      src/client/js/components/Sidebar.jsx
  44. 36 8
      src/client/js/components/Sidebar/SidebarNav.jsx
  45. 0 5
      src/client/js/components/TableOfContents.jsx
  46. 5 17
      src/client/js/nologin.jsx
  47. 3 3
      src/client/js/services/PageContainer.js
  48. 1 1
      src/client/styles/scss/_admin.scss
  49. 75 1
      src/client/styles/scss/_layout.scss
  50. 3 134
      src/client/styles/scss/_layout_kibela.scss
  51. 0 22
      src/client/styles/scss/_layout_variable.scss
  52. 1 1
      src/client/styles/scss/_me.scss
  53. 1 1
      src/client/styles/scss/_mixins.scss
  54. 5 0
      src/client/styles/scss/_navbar.scss
  55. 0 4
      src/client/styles/scss/_on-edit.scss
  56. 1 1
      src/client/styles/scss/_override-bootstrap-variables.scss
  57. 8 0
      src/client/styles/scss/_override-rbt.scss
  58. 1 0
      src/client/styles/scss/_page.scss
  59. 8 5
      src/client/styles/scss/_subnav.scss
  60. 1 1
      src/client/styles/scss/_tag.scss
  61. 5 7
      src/client/styles/scss/_user.scss
  62. 2 2
      src/client/styles/scss/_variables.scss
  63. 1 32
      src/client/styles/scss/atoms/_buttons.scss
  64. 212 0
      src/client/styles/scss/theme/_apply-colors-kibela.scss
  65. 7 2
      src/client/styles/scss/theme/_apply-colors.scss
  66. 0 39
      src/client/styles/scss/theme/_layout_kibela_variable.scss
  67. 7 0
      src/client/styles/scss/theme/_reboot-bootstrap-theme-colors.scss
  68. 91 7
      src/client/styles/scss/theme/kibela.scss
  69. 26 0
      src/migrations/2020042016039-remove-crowi-layout.js
  70. 1 1
      src/server/routes/apiv3/customize-setting.js
  71. 1 1
      src/server/views/invited.html
  72. 1 1
      src/server/views/layout-growi/base/layout.html
  73. 1 1
      src/server/views/layout-growi/user_page.html
  74. 68 54
      src/server/views/layout/layout.html
  75. 3 13
      src/server/views/login.html
  76. 2 2
      src/server/views/modal/delete.html
  77. 1 1
      src/server/views/modal/put_back.html
  78. 3 3
      src/server/views/modal/rename.html
  79. 8 8
      src/server/views/modal/shortcuts.html
  80. 1 1
      src/server/views/tags.html
  81. 1 1
      src/server/views/widget/create_portal.html
  82. 2 2
      src/server/views/widget/page_tabs.html

+ 1 - 1
config/webpack.common.js

@@ -38,7 +38,7 @@ module.exports = (options) => {
       // 'styles/theme-mono-blue':       './src/client/styles/scss/theme/mono-blue.scss',
       // 'styles/theme-future':          './src/client/styles/scss/theme/future.scss',
       // 'styles/theme-blue-night':      './src/client/styles/scss/theme/blue-night.scss',
-      // 'styles/theme-kibela':          './src/client/styles/scss/theme/kibela.scss',
+      'styles/theme-kibela':          './src/client/styles/scss/theme/kibela.scss',
       // 'styles/theme-halloween':       './src/client/styles/scss/theme/halloween.scss',
       // 'styles/theme-wood':          './src/client/styles/scss/theme/wood.scss',
       // 'styles/theme-christmas':          './src/client/styles/scss/theme/christmas.scss',

+ 8 - 0
resource/cdn-manifests.js

@@ -138,6 +138,14 @@ module.exports = {
         integrity: '',
       },
     },
+    {
+      name: 'animate.css',
+      url: 'https://cdn.jsdelivr.net/npm/animate.css@3.7.2/animate.min.css',
+      groups: ['basis'],
+      args: {
+        integrity: '',
+      },
+    },
     {
       name: 'jquery-ui',
       url: 'https://cdn.jsdelivr.net/jquery.ui/1.11.4/jquery-ui.min.css',

+ 3 - 0
src/client/js/bootstrap.jsx

@@ -4,6 +4,7 @@ import loggerFactory from '@alias/logger';
 import Xss from '@commons/service/xss';
 
 import HeaderSearchBox from './components/HeaderSearchBox';
+import NavbarToggler from './components/Navbar/NavbarToggler';
 import PersonalDropdown from './components/Navbar/PersonalDropdown';
 import Sidebar from './components/Sidebar';
 import StaffCredit from './components/StaffCredit/StaffCredit';
@@ -37,6 +38,8 @@ appContainer.injectToWindow();
  *  value: React Element
  */
 const componentMappings = {
+  'grw-navbar-toggler': <NavbarToggler />,
+
   'search-top': <HeaderSearchBox />,
   'search-sidebar': <HeaderSearchBox crowi={appContainer} />,
   'personal-dropdown': <PersonalDropdown />,

+ 9 - 9
src/client/js/components/Admin/App/AppSetting.jsx

@@ -38,9 +38,9 @@ class AppSetting extends React.Component {
 
     return (
       <React.Fragment>
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.site_name')}</label>
-          <div className="col-6">
+        <div className="form-group row">
+          <label className="text-left text-md-right col-md-3 col-form-label">{t('admin:app_setting.site_name')}</label>
+          <div className="col-md-6">
             <input
               className="form-control"
               type="text"
@@ -53,8 +53,8 @@ class AppSetting extends React.Component {
         </div>
 
         <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.confidential_name')}</label>
-          <div className="col-6">
+          <label className="text-left text-md-right col-md-3 col-form-label">{t('admin:app_setting.confidential_name')}</label>
+          <div className="col-md-6">
             <input
               className="form-control"
               type="text"
@@ -67,8 +67,8 @@ class AppSetting extends React.Component {
         </div>
 
         <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.default_language')}</label>
-          <div className="col-6">
+          <label className="text-left text-md-right col-md-3 col-form-label">{t('admin:app_setting.default_language')}</label>
+          <div className="col-md-6">
             <div className="custom-control custom-radio custom-control-inline">
               <input
                 type="radio"
@@ -97,8 +97,8 @@ class AppSetting extends React.Component {
         </div>
 
         <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.file_uploading')}</label>
-          <div className="col-6">
+          <label className="text-left text-md-right col-md-3 col-form-label">{t('admin:app_setting.file_uploading')}</label>
+          <div className="col-md-6">
             <div className="custom-control custom-checkbox custom-checkbox-info">
               <input
                 type="checkbox"

+ 15 - 15
src/client/js/components/Admin/App/AwsSetting.jsx

@@ -50,11 +50,11 @@ class AwsSetting extends React.Component {
           </span>
         </p>
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             {t('admin:app_setting.region')}
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
               className="form-control"
               placeholder={`${t('eg')} ap-northeast-1`}
@@ -66,11 +66,11 @@ class AwsSetting extends React.Component {
           </div>
         </div>
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             {t('admin:app_setting.custom_endpoint')}
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
               className="form-control"
               type="text"
@@ -84,11 +84,11 @@ class AwsSetting extends React.Component {
           </div>
         </div>
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             {t('admin:app_setting.bucket_name')}
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
               className="form-control"
               type="text"
@@ -101,11 +101,11 @@ class AwsSetting extends React.Component {
           </div>
         </div>
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             Access Key ID
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
               className="form-control"
               type="text"
@@ -117,11 +117,11 @@ class AwsSetting extends React.Component {
           </div>
         </div>
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             Secret Access Key
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
               className="form-control"
               type="text"

+ 1 - 1
src/client/js/components/Admin/App/SiteUrlSetting.jsx

@@ -43,7 +43,7 @@ class SiteUrlSetting extends React.Component {
           && (<p className="alert alert-danger"><i className="icon-exclamation"></i> {t('admin:app_setting.site_url_warn')}</p>)}
 
         <div className="row form-group">
-          <div className="col-9 offset-3">
+          <div className="col-md-9 offset-md-3">
             <table className="table settings-table">
               <colgroup>
                 <col className="from-db" />

+ 1 - 1
src/client/js/components/Admin/Common/AdminUpdateButtonRow.jsx

@@ -7,7 +7,7 @@ const AdminUpdateButtonRow = (props) => {
 
   return (
     <div className="row my-3">
-      <div className="offset-4 col-5">
+      <div className="mx-auto">
         <button type="button" className="btn btn-primary" onClick={props.onClick} disabled={props.disabled}>{ t('Update') }</button>
       </div>
     </div>

+ 1 - 1
src/client/js/components/Admin/Customize/CustomizeFunctionSetting.jsx

@@ -157,7 +157,7 @@ class CustomizeBehaviorSetting extends React.Component {
                   isChecked={adminCustomizeContainer.state.isAllReplyShown || false}
                   onChecked={() => { adminCustomizeContainer.switchIsAllReplyShown() }}
                 >
-                  <p className="help-block">
+                  <p className="form-text text-muted">
                     {t('admin:customize_setting.function_options.show_all_reply_comments_desc')}
                   </p>
                 </CustomizeFunctionOption>

+ 1 - 1
src/client/js/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.jsx

@@ -22,7 +22,7 @@ class NormalizeIndicesControls extends React.PureComponent {
           { t('full_text_search_management.normalize_button') }
         </button>
 
-        <p className="help-block">
+        <p className="form-text text-muted">
           { t('full_text_search_management.normalize_description') }<br />
         </p>
       </>

+ 1 - 1
src/client/js/components/Admin/ElasticsearchManagement/ReconnectControls.jsx

@@ -22,7 +22,7 @@ class ReconnectControls extends React.PureComponent {
           { t('full_text_search_management.reconnect_button') }
         </button>
 
-        <p className="help-block">
+        <p className="form-text text-muted">
           { t('full_text_search_management.reconnect_description') }<br />
         </p>
       </>

+ 2 - 2
src/client/js/components/Admin/ImportDataPage.jsx

@@ -216,7 +216,7 @@ class ImportDataPage extends React.Component {
                     name="Esa"
                     type="button"
                     id="importFromEsa"
-                    className="btn btn-outline-secondary btn-esa"
+                    className="btn btn-secondary btn-esa"
                     onClick={this.esaHandleSubmitTest}
                     value={t('admin:importer_management.esa_settings.test_connection')}
                   />
@@ -309,7 +309,7 @@ class ImportDataPage extends React.Component {
                     name="Qiita"
                     type="button"
                     id="importFromQiita"
-                    className="btn btn-outline-secondary btn-qiita"
+                    className="btn btn-secondary btn-qiita"
                     onClick={this.qiitaHandleSubmitTest}
                     value={t('admin:importer_management.qiita_settings.test_connection')}
                   />

+ 3 - 0
src/client/js/components/Admin/Notification/NotificationSetting.jsx

@@ -63,6 +63,7 @@ class NotificationSetting extends React.Component {
             <NavLink
               className={`${activeTab === 'slack-configuration' && 'active'} `}
               onClick={() => { this.toggleActiveTab('slack-configuration') }}
+              href="#slack-configuration"
             >
               <i className="icon-settings"></i> Slack Configuration
             </NavLink>
@@ -71,6 +72,7 @@ class NotificationSetting extends React.Component {
             <NavLink
               className={`${activeTab === 'user-trigger-notification' && 'active'} `}
               onClick={() => { this.toggleActiveTab('user-trigger-notification') }}
+              href="#user-trigger-notification"
             >
               <i className="icon-settings"></i> User Trigger Notification
             </NavLink>
@@ -79,6 +81,7 @@ class NotificationSetting extends React.Component {
             <NavLink
               className={`${activeTab === 'global-notification' && 'active'} `}
               onClick={() => { this.toggleActiveTab('global-notification') }}
+              href="#global-notification"
             >
               <i className="icon-settings"></i> Global Notification
             </NavLink>

+ 1 - 1
src/client/js/components/Admin/Notification/SlackAppConfiguration.jsx

@@ -89,7 +89,7 @@ class SlackAppConfiguration extends React.Component {
                     {t('notification_setting.prioritize_webhook')}
                   </label>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   {t('notification_setting.prioritize_webhook_desc')}
                 </p>
               </div>

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

@@ -81,7 +81,7 @@ class BasicSecurityManagement extends React.Component {
                 { t('security_setting.Basic.enable_basic') }
               </label>
             </div>
-            <p className="help-block">
+            <p className="form-text text-muted">
               <small>
                 <span dangerouslySetInnerHTML={{ __html: t('security_setting.Basic.desc_1') }} /><br />
                 { t('security_setting.Basic.desc_2')}
@@ -110,7 +110,7 @@ class BasicSecurityManagement extends React.Component {
                   dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical', 'username') }}
                 />
               </div>
-              <p className="help-block">
+              <p className="form-text text-muted">
                 <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn', 'username') }} />
               </p>
             </div>

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

@@ -96,7 +96,7 @@ class GitHubSecurityManagement extends React.Component {
               value={adminGitHubSecurityContainer.state.appSiteUrl}
               readOnly
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
                 <i
@@ -125,7 +125,7 @@ class GitHubSecurityManagement extends React.Component {
                   value={adminGitHubSecurityContainer.state.githubClientId || ''}
                   onChange={e => adminGitHubSecurityContainer.changeGitHubClientId(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GITHUB_CLIENT_ID' }) }} />
                 </p>
               </div>
@@ -141,7 +141,7 @@ class GitHubSecurityManagement extends React.Component {
                   defaultValue={adminGitHubSecurityContainer.state.githubClientSecret || ''}
                   onChange={e => adminGitHubSecurityContainer.changeGitHubClientSecret(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GITHUB_CLIENT_SECRET' }) }} />
                 </p>
               </div>
@@ -163,7 +163,7 @@ class GitHubSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
               </div>

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

@@ -96,7 +96,7 @@ class GoogleSecurityManagement extends React.Component {
               value={adminGoogleSecurityContainer.state.callbackUrl}
               readOnly
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
                 <i
@@ -125,7 +125,7 @@ class GoogleSecurityManagement extends React.Component {
                   defaultValue={adminGoogleSecurityContainer.state.googleClientId || ''}
                   onChange={e => adminGoogleSecurityContainer.changeGoogleClientId(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GOOGLE_CLIENT_ID' }) }} />
                 </p>
               </div>
@@ -141,7 +141,7 @@ class GoogleSecurityManagement extends React.Component {
                   defaultValue={adminGoogleSecurityContainer.state.googleClientSecret || ''}
                   onChange={e => adminGoogleSecurityContainer.changeGoogleClientSecret(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GOOGLE_CLIENT_SECRET' }) }} />
                 </p>
               </div>
@@ -163,7 +163,7 @@ class GoogleSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
               </div>

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

@@ -112,7 +112,7 @@ class LdapSecuritySetting extends React.Component {
                 />
                 <small>
                   <p
-                    className="help-block"
+                    className="form-text text-muted"
                     // eslint-disable-next-line react/no-danger
                     dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.server_url_detail') }}
                   />
@@ -164,7 +164,7 @@ class LdapSecuritySetting extends React.Component {
                   onChange={e => adminLdapSecurityContainer.changeBindDN(e.target.value)}
                 />
                 {(adminLdapSecurityContainer.state.isUserBind === true) ? (
-                  <p className="help-block passport-ldap-userbind">
+                  <p className="form-text text-muted passport-ldap-userbind">
                     <small>
                       {t('security_setting.ldap.bind_DN_user_detail1')}<br />
                       {/* eslint-disable-next-line react/no-danger */}
@@ -175,7 +175,7 @@ class LdapSecuritySetting extends React.Component {
                   </p>
                 )
                   : (
-                    <p className="help-block passport-ldap-managerbind">
+                    <p className="form-text text-muted passport-ldap-managerbind">
                       <small>
                         {t('security_setting.ldap.bind_DN_manager_detail')}<br />
                         {t('security_setting.example')}1: <code>uid=admin,dc=domain,dc=com</code><br />
@@ -229,7 +229,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapSearchFilter || ''}
                   onChange={e => adminLdapSecurityContainer.changeSearchFilter(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                     {t('security_setting.ldap.search_filter_detail1')}<br />
                     {/* eslint-disable-next-line react/no-danger */}
@@ -238,7 +238,7 @@ class LdapSecuritySetting extends React.Component {
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.search_filter_detail3') }} />
                   </small>
                 </p>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                     {t('security_setting.example')}1 - {t('security_setting.ldap.search_filter_example1')}:
                     <code>(|(uid={'{{ username }}'})(mail={'{{ username }}'}))</code><br />
@@ -266,7 +266,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapUsername || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapUsername(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   {/* eslint-disable-next-line react/no-danger */}
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.username_detail') }} />
                 </p>
@@ -290,7 +290,7 @@ class LdapSecuritySetting extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                   />
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   {/* eslint-disable-next-line react/no-danger */}
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                 </p>
@@ -310,7 +310,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapMail || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapMail(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                     {t('security_setting.ldap.mail_detail')}
                   </small>
@@ -330,7 +330,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapName || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapName(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                     {t('security_setting.ldap.name_detail')}
                   </small>
@@ -355,7 +355,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupSearchBase || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupSearchBase(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                     {/* eslint-disable-next-line react/no-danger */}
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_base_DN_detail') }} /><br />
@@ -377,7 +377,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupSearchFilter || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupSearchFilter(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                     {/* eslint-disable react/no-danger */}
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_filter_detail1') }} /><br />
@@ -386,7 +386,7 @@ class LdapSecuritySetting extends React.Component {
                     {/* eslint-enable react/no-danger */}
                   </small>
                 </p>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                     {t('security_setting.example')}:
                     {/* eslint-disable-next-line react/no-danger */}
@@ -409,7 +409,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupDnProperty || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupDnProperty(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   {/* eslint-disable-next-line react/no-danger */}
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_user_DN_property_detail') }} />
                 </p>

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

@@ -130,7 +130,7 @@ class LocalSecuritySetting extends React.Component {
                   </div>
                 </div>
 
-                <p className="help-block small">
+                <p className="form-text text-muted small">
                   {t('security_setting.Register limitation desc')}
                 </p>
               </div>
@@ -147,7 +147,7 @@ class LocalSecuritySetting extends React.Component {
                   defaultValue={adminLocalSecurityContainer.state.registrationWhiteList.join('\n')}
                   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="form-text text-muted small">{t('security_setting.restrict_emails')}<br />{t('security_setting.for_instance')}
                   <code>@growi.org</code>{t('security_setting.only_those')}<br />
                   {t('security_setting.insert_single')}
                 </p>

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

@@ -91,7 +91,7 @@ class OidcSecurityManagement extends React.Component {
               value={adminOidcSecurityContainer.state.callbackUrl}
               readOnly
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
                 <i
@@ -132,7 +132,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcIssuerHost || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcIssuerHost(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_ISSUER_HOST' }) }} />
                 </p>
               </div>
@@ -148,7 +148,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcClientId || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientId(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_CLIENT_ID' }) }} />
                 </p>
               </div>
@@ -164,7 +164,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcClientSecret || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientSecret(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_CLIENT_SECRET' }) }} />
                 </p>
               </div>
@@ -184,7 +184,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapId || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapId(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.id_detail') }} />
                 </p>
               </div>
@@ -200,7 +200,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapUserName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapUserName(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.username_detail') }} />
                 </p>
               </div>
@@ -216,7 +216,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapName(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.name_detail') }} />
                 </p>
               </div>
@@ -232,7 +232,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapEmail || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapEmail(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.mapping_detail', { target: t('Email') }) }} />
                 </p>
               </div>
@@ -247,7 +247,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.callbackUrl || ''}
                   readOnly
                 />
-                <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+                <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
                 {!adminGeneralSecurityContainer.state.appSiteUrl && (
                   <div className="alert alert-danger">
                     <i
@@ -276,7 +276,7 @@ class OidcSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                   />
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                 </p>
               </div>
@@ -298,7 +298,7 @@ class OidcSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
               </div>

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

@@ -118,7 +118,7 @@ class SamlSecurityManagement extends React.Component {
               defaultValue={adminSamlSecurityContainer.state.callbackUrl}
               readOnly
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'SAML Identity' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'SAML Identity' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
                 <i
@@ -180,7 +180,7 @@ class SamlSecurityManagement extends React.Component {
                       value={this.state.envEntryPoint || ''}
                       readOnly
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ENTRY_POINT' }) }} />
                     </p>
                   </td>
@@ -204,7 +204,7 @@ class SamlSecurityManagement extends React.Component {
                       value={this.state.envIssuer || ''}
                       readOnly
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ISSUER' }) }} />
                     </p>
                   </td>
@@ -249,7 +249,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       readOnly
                       value={this.state.envCert || ''}
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_CERT' }) }} />
                     </p>
                   </td>
@@ -281,7 +281,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapId}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapId(e.target.value)}
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small>
                         {t('security_setting.SAML.id_detail')}
                       </small>
@@ -294,7 +294,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapId || ''}
                       readOnly
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_ID' }) }} />
                     </p>
                   </td>
@@ -309,7 +309,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapUsername}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapUserName(e.target.value)}
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.username_detail') }} />
                     </p>
                   </td>
@@ -320,7 +320,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapUsername || ''}
                       readOnly
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_USERNAME' }) }} />
                     </p>
                   </td>
@@ -335,7 +335,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapMail}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapMail(e.target.value)}
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: 'Email' }) }} />
                     </p>
                   </td>
@@ -346,7 +346,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapMail || ''}
                       readOnly
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_MAIL' }) }} />
                     </p>
                   </td>
@@ -361,7 +361,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapFirstName}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapFirstName(e.target.value)}
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       {/* eslint-disable-next-line max-len */}
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: t('security_setting.form_item_name.attrMapFirstName') }) }} />
                     </p>
@@ -373,7 +373,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapFirstName || ''}
                       readOnly
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small>
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_FIRST_NAME' }) }} />
                         <br />
@@ -392,7 +392,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapLastName}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapLastName(e.target.value)}
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       {/* eslint-disable-next-line max-len */}
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: t('security_setting.form_item_name.attrMapLastName') }) }} />
                     </p>
@@ -404,7 +404,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapLastName || ''}
                       readOnly
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small>
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_LAST_NAME' }) }} />
                         <br />
@@ -436,7 +436,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                   />
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                 </p>
               </div>
@@ -458,7 +458,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
               </div>
@@ -468,7 +468,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
               Attribute-based Login Control
             </h3>
 
-            <p className="help-block">
+            <p className="form-text text-muted">
               <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_detail') }} />
             </p>
 
@@ -494,7 +494,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       onChange={(e) => { adminSamlSecurityContainer.changeSamlABLCRule(e.target.value) }}
                       readOnly={useOnlyEnvVars}
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small>
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_rule_detail') }} />
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_rule_example') }} />
@@ -508,7 +508,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envABLCRule || ''}
                       readOnly
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ABLC_RULE' }) }} />
                     </p>
                   </td>

+ 9 - 0
src/client/js/components/Admin/Security/SecurityManagement.jsx

@@ -65,6 +65,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-local' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-local') }}
+                href="#passport-local"
               >
                 <i className="fa fa-users" /> ID/Pass
               </NavLink>
@@ -73,6 +74,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-ldap' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-ldap') }}
+                href="#passport-ldap"
               >
                 <i className="fa fa-sitemap" /> LDAP
               </NavLink>
@@ -81,6 +83,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-saml' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-saml') }}
+                href="#passport-saml"
               >
                 <i className="fa fa-key" /> SAML
               </NavLink>
@@ -89,6 +92,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-oidc' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-oidc') }}
+                href="#passport-oidc"
               >
                 <i className="fa fa-openid" /> OIDC
               </NavLink>
@@ -97,6 +101,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-basic' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-basic') }}
+                href="#passport-basic"
               >
                 <i className="fa fa-lock" /> BASIC
               </NavLink>
@@ -105,6 +110,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-google' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-google') }}
+                href="#passport-google"
               >
                 <i className="fa fa-google" /> Google
               </NavLink>
@@ -113,6 +119,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-github' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-github') }}
+                href="#passport-github"
               >
                 <i className="fa fa-github" /> GitHub
               </NavLink>
@@ -121,6 +128,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-twitter' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-twitter') }}
+                href="#passport-twitter"
               >
                 <i className="fa fa-twitter" /> Twitter
               </NavLink>
@@ -129,6 +137,7 @@ class SecurityManagement extends React.Component {
               <NavLink
                 className={`${activeTab === 'passport-facebook' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-facebook') }}
+                href="#passport-facebook"
               >
                 <i className="fa fa-facebook" /> (TBD) Facebook
               </NavLink>

+ 1 - 1
src/client/js/components/Admin/Security/SecuritySetting.jsx

@@ -167,7 +167,7 @@ class SecuritySetting extends React.Component {
                   {t('security_setting.admin_and_author')}
                 </a>
               </div>
-              <p className="help-block small">
+              <p className="form-text text-muted small">
                 {t('security_setting.complete_deletion_explain')}
               </p>
             </div>

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

@@ -96,7 +96,7 @@ class TwitterSecurityManagement extends React.Component {
               value={adminTwitterSecurityContainer.state.callbackUrl}
               readOnly
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
                 <i
@@ -125,7 +125,7 @@ class TwitterSecurityManagement extends React.Component {
                   defaultValue={adminTwitterSecurityContainer.state.twitterConsumerKey || ''}
                   onChange={e => adminTwitterSecurityContainer.changeTwitterConsumerKey(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_TWITTER_CONSUMER_KEY' }) }} />
                 </p>
               </div>
@@ -141,7 +141,7 @@ class TwitterSecurityManagement extends React.Component {
                   defaultValue={adminTwitterSecurityContainer.state.twitterConsumerSecret || ''}
                   onChange={e => adminTwitterSecurityContainer.changeTwitterConsumerSecret(e.target.value)}
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_TWITTER_CONSUMER_SECRET' }) }} />
                 </p>
               </div>
@@ -163,7 +163,7 @@ class TwitterSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
               </div>

+ 4 - 4
src/client/js/components/Admin/UserManagement.jsx

@@ -194,7 +194,7 @@ class UserManagement extends React.Component {
                 </label>
               </div>
 
-              <div className="custom-control custom-checkbox custom-checkbox-secondary mr-2">
+              <div className="custom-control custom-checkbox custom-checkbox-warning mr-2">
                 <input
                   className="custom-control-input"
                   type="checkbox"
@@ -203,7 +203,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('suspended') }}
                 />
                 <label className="custom-control-label" htmlFor="c4">
-                  <span className="badge badge-pill badge-secondary d-inline-block vt mt-1">Suspended</span>
+                  <span className="badge badge-pill badge-warning d-inline-block vt mt-1">Suspended</span>
                 </label>
               </div>
 
@@ -235,8 +235,8 @@ class UserManagement extends React.Component {
               </button>
             </div>
 
-            <div className="ml-5">
-              {this.state.isNotifyCommentShow && <span className="text-warning small">{t('admin:user_management.click_twice_same_checkbox')}</span>}
+            <div className="ml-4">
+              {this.state.isNotifyCommentShow && <span className="text-warning">{t('admin:user_management.click_twice_same_checkbox')}</span>}
             </div>
 
           </div>

+ 1 - 1
src/client/js/components/Admin/Users/UserTable.jsx

@@ -42,7 +42,7 @@ class UserTable extends React.Component {
         text = 'Active';
         break;
       case 3:
-        additionalClassName = 'badge-secondary';
+        additionalClassName = 'badge-warning';
         text = 'Suspended';
         break;
       case 4:

+ 1 - 1
src/client/js/components/BookmarkButton.jsx

@@ -64,7 +64,7 @@ class BookmarkButton extends React.Component {
         href="#"
         title="Bookmark"
         onClick={this.handleClick}
-        className={`btn btn-circle btn-outline-warning btn-bookmark border-0 ${`btn-${this.props.size}`} ${this.state.isBookmarked && 'active'}`}
+        className={`btn rounded-circle btn-outline-warning btn-bookmark border-0 ${`btn-${this.props.size}`} ${this.state.isBookmarked && 'active'}`}
       >
         <i className="icon-star"></i>
       </button>

+ 1 - 1
src/client/js/components/LikeButton.jsx

@@ -43,7 +43,7 @@ class LikeButton extends React.Component {
       <button
         type="button"
         onClick={this.handleClick}
-        className={`btn btn-circle btn-outline-info btn-like border-0 ${this.state.isLiked ? 'active' : ''}`}
+        className={`btn rounded-circle btn-outline-info btn-like border-0 ${this.state.isLiked ? 'active' : ''}`}
       >
         <i className="icon-like"></i>
       </button>

+ 39 - 26
src/client/js/components/LoginForm.jsx

@@ -8,23 +8,44 @@ class LoginForm extends React.Component {
   constructor(props) {
     super(props);
 
+    this.isRegistrationEnabled = false;
+    this.isLocalStrategySetup = false;
+    this.isLdapStrategySetup = false;
+    this.objOfIsExternalAuthEnableds = {};
+
     this.renderLocalOrLdapLoginForm = this.renderLocalOrLdapLoginForm.bind(this);
     this.renderExternalAuthLoginForm = this.renderExternalAuthLoginForm.bind(this);
     this.renderExternalAuthInput = this.renderExternalAuthInput.bind(this);
   }
 
+  componentWillMount() {
+    // [TODO][GW-1913] get params from server with axios
+    this.isRegistrationEnabled = true;
+    this.isLocalStrategySetup = true;
+    this.isLdapStrategySetup = true;
+    this.objOfIsExternalAuthEnableds = {
+      google: true,
+      github: true,
+      facebook: true,
+      twitter: true,
+      oidc: true,
+      saml: true,
+      basic: true,
+    };
+  }
+
   renderLocalOrLdapLoginForm() {
-    const { t, isLdapStrategySetup } = this.props;
+    const { t } = this.props;
 
     return (
-      <form className="col-12" role="form" action="/login" method="post">
+      <form role="form" action="/login" method="post">
 
         <div className="input-group mb-3">
           <div className="input-group-prepend">
             <span className="input-group-text"><i className="icon-user"></i></span>
           </div>
           <input type="text" className="form-control" placeholder="Username or E-mail" name="loginForm[username]" />
-          {isLdapStrategySetup && (
+          {this.isLdapStrategySetup && (
             <div className="input-group-append">
               <small className="input-group-text text-success">
                 <i className="icon-fw icon-check"></i> LDAP
@@ -56,7 +77,8 @@ class LoginForm extends React.Component {
   renderExternalAuthInput(auth) {
     const { t } = this.props;
     return (
-      <div className="input-group justify-content-center d-flex mt-5">
+      <div key={auth} className="input-group justify-content-center d-flex mt-5">
+        {/* [TODO][GW-1913] use onClick, and delete form tag */}
         <form role="form" action={`/passport/${auth}`} className="d-inline-flex flex-column">
           <input type="hidden" name="_csrf" value="{{ csrf() }}" />
           <button type="submit" className="btn btn-fill px-0 py-2" id={auth}>
@@ -71,8 +93,7 @@ class LoginForm extends React.Component {
   }
 
   renderExternalAuthLoginForm() {
-    const { isLocalStrategySetup, isLdapStrategySetup, objOfIsExternalAuthEnableds } = this.props;
-    const isExternalAuthCollapsible = isLocalStrategySetup || isLdapStrategySetup;
+    const isExternalAuthCollapsible = this.isLocalStrategySetup || this.isLdapStrategySetup;
     const collapsibleClass = isExternalAuthCollapsible ? 'collapse collapse-external-auth collapse-anchor' : '';
 
     return (
@@ -81,11 +102,11 @@ class LoginForm extends React.Component {
         <div id="external-auth" className={`external-auth ${collapsibleClass}`}>
           <div className="spacer"></div>
           <div className="d-flex flex-row justify-content-between flex-wrap">
-            {Object.keys(objOfIsExternalAuthEnableds).map((auth) => {
-              if (!objOfIsExternalAuthEnableds[auth]) {
+            {Object.keys(this.objOfIsExternalAuthEnableds).map((auth) => {
+              if (!this.objOfIsExternalAuthEnableds[auth]) {
                 return;
               }
-              return this.renderExternalAuthInput([auth]);
+              return this.renderExternalAuthInput(auth);
             })}
           </div>
           <div className="spacer"></div>
@@ -114,21 +135,15 @@ class LoginForm extends React.Component {
   }
 
   render() {
-    const {
-      t,
-      isRegistrationEnabled,
-      isLocalStrategySetup,
-      isLdapStrategySetup,
-      objOfIsExternalAuthEnableds,
-    } = this.props;
-
-    const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup;
-    const registerFormClass = isRegistrationEnabled ? 'to-flip' : '';
-    const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem);
+    const { t, isRegistering } = this.props;
+
+    const isLocalOrLdapStrategiesEnabled = this.isLocalStrategySetup || this.isLdapStrategySetup;
+    const registerFormClass = isRegistering ? 'to-flip' : '';
+    const isSomeExternalAuthEnabled = Object.values(this.objOfIsExternalAuthEnableds).some(elem => elem);
 
     return (
       <div className={`login-dialog mx-auto flipper ${registerFormClass}`} id="login-dialog">
-        <div className="row">
+        <div className="row mx-0">
           <div className="col-12">
             <div className="front">
               { isLocalOrLdapStrategiesEnabled && this.renderLocalOrLdapLoginForm() }
@@ -137,7 +152,7 @@ class LoginForm extends React.Component {
             {isRegistrationEnabled && this.renderRegisterForm()}
           </div>
         </div>
-        {isRegistrationEnabled && (
+        {this.isRegistrationEnabled && (
           <div className="row">
             <div className="col-12 text-right py-2">
               <a href="#register" id="register" className="link-switch">
@@ -155,10 +170,8 @@ class LoginForm extends React.Component {
 LoginForm.propTypes = {
   // i18next
   t: PropTypes.func.isRequired,
-  isRegistrationEnabled: PropTypes.bool,
-  isLocalStrategySetup: PropTypes.bool,
-  isLdapStrategySetup: PropTypes.bool,
-  objOfIsExternalAuthEnableds: PropTypes.object,
+  isRegistering: PropTypes.bool,
+  csrf: PropTypes.string,
 };
 
 export default withTranslation()(LoginForm);

+ 1 - 1
src/client/js/components/Me/BasicInfoSettings.jsx

@@ -71,7 +71,7 @@ class BasicInfoSettings extends React.Component {
           </div>
           {registrationWhiteList.length !== 0 && (
             <div className="col-sm-offset-2 col-sm-10">
-              <div className="help-block">
+              <div className="form-text text-muted">
                 {t('page_register.form_help.email')}
                 <ul>
                   {registrationWhiteList.map(data => <li key={data}><code>{data}</code></li>)}

+ 1 - 1
src/client/js/components/Me/PasswordSettings.jsx

@@ -111,7 +111,7 @@ class PasswordSettings extends React.Component {
               onChange={(e) => { this.onChangeNewPasswordConfirm(e.target.value) }}
             />
 
-            <p className="help-block">{t('page_register.form_help.password') }</p>
+            <p className="form-text text-muted">{t('page_register.form_help.password') }</p>
           </div>
         </div>
 

+ 0 - 3
src/client/js/components/Navbar/GrowiSubNavigation.jsx

@@ -25,9 +25,6 @@ const GrowiSubNavigation = (props) => {
   const isPageNotFound = pageId == null;
   const isPageInTrash = isTrashPage(path);
 
-  console.log({
-    isPageForbidden, isPageNotFound, isPageInTrash, pageId, path,
-  });
   // Display only the RevisionPath
   if (isPageNotFound || isPageForbidden || isPageInTrash) {
     return (

+ 35 - 0
src/client/js/components/Navbar/NavbarToggler.jsx

@@ -0,0 +1,35 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { withTranslation } from 'react-i18next';
+
+import { createSubscribedElement } from '../UnstatedUtils';
+import AppContainer from '../../services/AppContainer';
+
+const NavbarToggler = (props) => {
+
+  // eslint-disable-next-line no-unused-vars
+  const { appContainer } = props;
+
+  return (
+    <button className="navbar-toggler grw-navbar-toggler border-0" type="button" aria-expanded="false" aria-label="Toggle navigation">
+      <i className="icon-menu"></i>
+    </button>
+  );
+
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const NavbarTogglerWrapper = (props) => {
+  return createSubscribedElement(NavbarToggler, props, [AppContainer]);
+};
+
+
+NavbarToggler.propTypes = {
+  t: PropTypes.func.isRequired, //  i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+};
+
+export default withTranslation()(NavbarTogglerWrapper);

+ 1 - 1
src/client/js/components/Navbar/PersonalDropdown.jsx

@@ -59,7 +59,7 @@ const PersonalDropdown = (props) => {
       {/* remove .dropdown-toggle for hide caret */}
       {/* See https://stackoverflow.com/a/44577512/13183572 */}
       <a className="nav-link waves-effect waves-light" data-toggle="dropdown">
-        <UserPicture user={user} withoutLink />&nbsp;{user.name}
+        <UserPicture user={user} withoutLink /><span className="d-none d-sm-inline-block">&nbsp;{user.name}</span>
       </a>
 
       {/* Menu */}

+ 5 - 10
src/client/js/components/PageComment/CommentEditor.jsx

@@ -219,9 +219,6 @@ class CommentEditor extends React.Component {
     const commentPreview = this.state.isMarkdown ? this.getCommentHtml() : null;
     const emojiStrategy = appContainer.getEmojiStrategy();
 
-    const layoutType = this.props.appContainer.getConfig().layoutType;
-    const isBaloonStyle = layoutType.match(/crowi-plus|growi|kibela/);
-
     const errorMessage = <span className="text-danger text-right mr-2">{this.state.errorMessage}</span>;
     const cancelButton = (
       <Button outline color="danger" size="xs" className="btn btn-outline-danger rounded-pill" onClick={this.toggleEditor}>
@@ -242,11 +239,9 @@ class CommentEditor extends React.Component {
     return (
       <div className="form page-comment-form">
         <div className="comment-form">
-          { isBaloonStyle && (
-            <div className="comment-form-user">
-              <UserPicture user={appContainer.currentUser} />
-            </div>
-          ) }
+          <div className="comment-form-user">
+            <UserPicture user={appContainer.currentUser} />
+          </div>
           <div className="comment-form-main">
             <div className="comment-write">
               <Nav tabs>
@@ -271,7 +266,7 @@ class CommentEditor extends React.Component {
                     isGfmMode={this.state.isMarkdown}
                     lineNumbers={false}
                     isMobile={appContainer.isMobile}
-                    isUploadable={this.state.isUploadable && layoutType !== 'crowi'} // disabled upload with crowi layout
+                    isUploadable={this.state.isUploadable}
                     isUploadableFile={this.state.isUploadableFile}
                     emojiStrategy={emojiStrategy}
                     onChange={this.updateState}
@@ -289,7 +284,7 @@ class CommentEditor extends React.Component {
             <div className="comment-submit">
               <div className="d-flex">
                 <label className="mr-2">
-                  { isBaloonStyle && activeTab === 1 && (
+                  {activeTab === 1 && (
                     <span className="custom-control custom-checkbox">
                       <input
                         type="checkbox"

+ 4 - 9
src/client/js/components/PageComment/CommentEditorLazyRenderer.jsx

@@ -30,9 +30,6 @@ class CommentEditorLazyRenderer extends React.Component {
     const user = appContainer.currentUser;
     const isLoggedIn = user != null;
 
-    const layoutType = this.props.appContainer.getConfig().layoutType;
-    const isBaloonStyle = layoutType.match(/crowi-plus|growi|kibela/);
-
     if (!isLoggedIn) {
       return <React.Fragment></React.Fragment>;
     }
@@ -43,16 +40,14 @@ class CommentEditorLazyRenderer extends React.Component {
         { !this.state.isEditorShown && (
           <div className="form page-comment-form">
             <div className="comment-form">
-              { isBaloonStyle && (
-                <div className="comment-form-user">
-                  <UserPicture user={user} />
-                </div>
-              ) }
+              <div className="comment-form-user">
+                <UserPicture user={user} />
+              </div>
               <div className="comment-form-main">
                 { !this.state.isEditorShown && (
                   <button
                     type="button"
-                    className={`btn btn-lg ${isBaloonStyle ? 'btn-link' : 'btn-primary'} center-block`}
+                    className="btn btn-lg btn-link center-block"
                     onClick={this.showCommentFormBtnClickHandler}
                   >
                     <i className="icon-bubble"></i> Add Comment

+ 1 - 8
src/client/js/components/PageComment/ReplayComments.jsx

@@ -40,15 +40,8 @@ class ReplayComments extends React.PureComponent {
 
   render() {
 
-    const layoutType = this.props.appContainer.getConfig().layoutType;
-    const isBaloonStyle = layoutType.match(/crowi-plus|growi|kibela/);
-
     const isAllReplyShown = this.props.appContainer.getConfig().isAllReplyShown || false;
-
-    let replyList = this.props.replyList;
-    if (!isBaloonStyle) {
-      replyList = replyList.slice().reverse();
-    }
+    const replyList = this.props.replyList;
 
     if (isAllReplyShown) {
       return (

+ 1 - 9
src/client/js/components/PageComments.jsx

@@ -180,15 +180,7 @@ class PageComments extends React.Component {
   render() {
     const topLevelComments = [];
     const allReplies = [];
-
-    const layoutType = this.props.appContainer.getConfig().layoutType;
-    const isBaloonStyle = layoutType.match(/crowi-plus|growi|kibela/);
-
-    let comments = this.props.commentContainer.state.comments;
-    if (isBaloonStyle) {
-      // replace with asc order array
-      comments = comments.slice().reverse(); // non-destructive reverse
-    }
+    const comments = this.props.commentContainer.state.comments;
 
     comments.forEach((comment) => {
       if (comment.replyTo === undefined) {

+ 3 - 3
src/client/js/components/PageEditor/CodeMirrorEditor.jsx

@@ -552,7 +552,7 @@ export default class CodeMirrorEditor extends AbstractEditor {
 
   renderCheatsheetModalButton() {
     return (
-      <button type="button" className="btn-link gfm-cheatsheet-modal-link small p-0" onClick={() => { this.markdownHelpButtonClickedHandler() }}>
+      <button type="button" className="btn-link gfm-cheatsheet-modal-link small" onClick={() => { this.markdownHelpButtonClickedHandler() }}>
         <i className="icon-question" /> Markdown
       </button>
     );
@@ -567,13 +567,13 @@ export default class CodeMirrorEditor extends AbstractEditor {
           ? (
             <div className="text-right">
               {cheatsheetModalButton}
-              <div className="mt-2">
+              <div className="mb-2">
                 <SimpleCheatsheet />
               </div>
             </div>
           )
           : (
-            <div className="mr-4">
+            <div className="mr-4 mb-2">
               {cheatsheetModalButton}
             </div>
           )

+ 1 - 1
src/client/js/components/PageEditor/DrawioModal.jsx

@@ -134,7 +134,7 @@ class DrawioModal extends React.PureComponent {
 
   render() {
     return (
-      <Modal isOpen={this.state.show} toggle={this.cancel} className="drawio-modal" bssize="large" keyboard={false}>
+      <Modal isOpen={this.state.show} toggle={this.cancel} className="drawio-modal" size="xl" keyboard={false}>
         <ModalBody className="p-0">
           {/* Loading spinner */}
           <div className="w-100 h-100 position-absolute d-flex">

+ 8 - 9
src/client/js/components/PageEditor/HandsontableModal.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 
 import {
-  Button, ButtonGroup,
+  Button,
   Collapse,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
@@ -437,15 +437,14 @@ export default class HandsontableModal extends React.PureComponent {
         <ModalHeader tag="h4" toggle={this.cancel} close={buttons}>Edit Table</ModalHeader>
         <ModalBody className="p-0 d-flex flex-column">
           <div className="px-4 py-3 modal-navbar bg-light">
-            <Button className="mr-4 data-import-button bg-light" onClick={this.toggleDataImportArea}>
+            <button type="button" className="mr-4 data-import-button btn btn-secondary" onClick={this.toggleDataImportArea}>
               <span className="mr-3">Data Import</span><i className={this.state.isDataImportAreaExpanded ? 'fa fa-angle-up' : 'fa fa-angle-down'}></i>
-
-            </Button>
-            <ButtonGroup>
-              <Button onClick={() => { this.alignButtonHandler('l') }}><i className="ti-align-left"></i></Button>
-              <Button onClick={() => { this.alignButtonHandler('c') }}><i className="ti-align-center"></i></Button>
-              <Button onClick={() => { this.alignButtonHandler('r') }}><i className="ti-align-right"></i></Button>
-            </ButtonGroup>
+            </button>
+            <div role="group" className="btn-group">
+              <button type="button" className="btn btn-secondary" onClick={() => { this.alignButtonHandler('l') }}><i className="ti-align-left"></i></button>
+              <button type="button" className="btn btn-secondary" onClick={() => { this.alignButtonHandler('c') }}><i className="ti-align-center"></i></button>
+              <button type="button" className="btn btn-secondary" onClick={() => { this.alignButtonHandler('r') }}><i className="ti-align-right"></i></button>
+            </div>
             <Collapse isOpen={this.state.isDataImportAreaExpanded}>
               <div className="mt-4">
                 <MarkdownTableDataImportForm onCancel={this.toggleDataImportArea} onImport={this.importData} />

+ 2 - 2
src/client/js/components/PageEditor/OptionsSelector.jsx

@@ -110,7 +110,7 @@ class OptionsSelector extends React.Component {
 
     return (
       <div className="my-0 form-group">
-        <label>Theme:</label>
+        <label className="mr-2">Theme:</label>
         <div className="btn-group btn-group-sm dropup">
           <button className="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
             {selectedTheme}
@@ -137,7 +137,7 @@ class OptionsSelector extends React.Component {
 
     return (
       <div className="my-0 form-group">
-        <label>Keymap:</label>
+        <label className="mr-2">Keymap:</label>
         <div className="btn-group btn-group-sm dropup">
           <button className="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
             {selectedKeymapMode}

+ 1 - 1
src/client/js/components/PageHistory/RevisionDiff.jsx

@@ -39,7 +39,7 @@ export default class RevisionDiff extends React.Component {
 
     const diffView = { __html: diffViewHTML };
     // eslint-disable-next-line react/no-danger
-    return <div className="revision-history-diff" dangerouslySetInnerHTML={diffView} />;
+    return <div className="revision-history-diff d-table w-100" dangerouslySetInnerHTML={diffView} />;
   }
 
 }

+ 1 - 4
src/client/js/components/Sidebar.jsx

@@ -15,7 +15,6 @@ import Drawer from '@atlaskit/drawer';
 import { createSubscribedElement } from './UnstatedUtils';
 import AppContainer from '../services/AppContainer';
 
-import GrowiLogo from './GrowiLogo';
 import SidebarNav from './Sidebar/SidebarNav';
 import History from './Sidebar/History';
 import CustomSidebar from './Sidebar/CustomSidebar';
@@ -56,9 +55,6 @@ class Sidebar extends React.Component {
 
   renderGlobalNavigation = () => (
     <>
-      <div className="grw-logo">
-        <a href="/"><GrowiLogo /></a>
-      </div>
       <SidebarNav currentContentsId={this.state.currentContentsId} onItemSelected={this.itemSelectedHandler} />
       <Drawer onClose={this.closeDrawer} isOpen={this.state.isDrawerOpen} width="wide">
         <code>Drawer contents</code>
@@ -99,6 +95,7 @@ class Sidebar extends React.Component {
           // experimental_fullWidthFlyout
           shouldHideGlobalNavShadow
           showContextualNavigation
+          topOffset={50}
         >
         </LayoutManager>
       </ThemeProvider>

+ 36 - 8
src/client/js/components/Sidebar/SidebarNav.jsx

@@ -5,6 +5,7 @@ import { withTranslation } from 'react-i18next';
 
 import {
   GlobalNav,
+  GlobalItem,
 } from '@atlaskit/navigation-next';
 
 import { createSubscribedElement } from '../UnstatedUtils';
@@ -28,13 +29,37 @@ class SidebarNav extends React.Component {
     }
   }
 
-  generateSidebarItemObj(id, label, icon) {
+  generatePrimaryItemObj(id, label, icon) {
+    const isSelected = this.props.currentContentsId === id;
+
     return {
       id,
-      icon,
-      label,
-      isSelected: this.props.currentContentsId === id,
-      onClick: () => this.itemSelectedHandler(id),
+      component: ({ className }) => (
+        <div className={`${className} grw-global-item-container ${isSelected ? 'active' : ''}`}>
+          <GlobalItem
+            icon={icon}
+            label={label}
+            isSelected={isSelected}
+            onClick={() => this.itemSelectedHandler(id)}
+          />
+        </div>
+      ),
+    };
+  }
+
+  generateSecondaryItemObj(id, label, icon, href) {
+    return {
+      id,
+      component: ({ className }) => (
+        <div className={`${className} grw-global-item-container d-block d-md-none`}>
+          <a href={href}>
+            <GlobalItem
+              icon={icon}
+              label={label}
+            />
+          </a>
+        </div>
+      ),
     };
   }
 
@@ -46,10 +71,13 @@ class SidebarNav extends React.Component {
     return (
       <GlobalNav
         primaryItems={[
-          this.generateSidebarItemObj('custom', 'Custom Sidebar', this.generateIconFactory('fa fa-2x fa-code')),
-          this.generateSidebarItemObj('history', 'History', this.generateIconFactory('icon-clock fa-2x')),
+          this.generatePrimaryItemObj('custom', 'Custom Sidebar', this.generateIconFactory('fa fa-code')),
+          this.generatePrimaryItemObj('history', 'History', this.generateIconFactory('icon-clock')),
+        ]}
+        secondaryItems={[
+          this.generateSecondaryItemObj('admin', 'Admin', this.generateIconFactory('icon-settings'), '/admin'),
+          this.generateSecondaryItemObj('help', 'Help', this.generateIconFactory('icon-question'), 'https://docs.growi.org'),
         ]}
-        secondaryItems={[]}
       />
     );
   }

+ 0 - 5
src/client/js/components/TableOfContents.jsx

@@ -60,11 +60,6 @@ class TableOfContents extends React.Component {
   }
 
   init() {
-    const { layoutType } = this.props.appContainer.config;
-    if (layoutType === 'crowi') {
-      return;
-    }
-
     /*
      * set event listener
      */

+ 5 - 17
src/client/js/nologin.jsx

@@ -27,26 +27,14 @@ if (installerFormElem) {
 // render loginForm
 const loginFormElem = document.getElementById('login-form');
 if (loginFormElem) {
-  const isRegistrationEnabled = loginFormElem.dataset.isRegistrationEnabled === 'true';
-  const isLdapStrategySetup = loginFormElem.dataset.isLdapStrategySetup === 'true';
-  const isLocalStrategySetup = loginFormElem.dataset.isLocalStrategySetup === 'true';
-  const objOfIsExternalAuthEnableds = {
-    google: loginFormElem.dataset.isGoogleAuthEnabled,
-    github: loginFormElem.dataset.isGithubAuthEnabled,
-    facebook: loginFormElem.dataset.isFacebookAuthEnabled,
-    twitter: loginFormElem.dataset.isTwitterAuthEnabled,
-    oidc: loginFormElem.dataset.isOidcAuthEnabled,
-    saml: loginFormElem.dataset.isSamlAuthEnabled,
-    basic: loginFormElem.dataset.isBasicAuthEnabled,
-  };
-
+  const isRegistering = loginFormElem.dataset.isRegistering === 'true';
+  // [TODO][GW-1913] An AppContainer gets csrf data
+  const csrf = loginFormElem.dataset.csrf;
   ReactDOM.render(
     <I18nextProvider i18n={i18n}>
       <LoginForm
-        isRegistrationEnabled={isRegistrationEnabled}
-        isLdapStrategySetup={isLdapStrategySetup}
-        isLocalStrategySetup={isLocalStrategySetup}
-        objOfIsExternalAuthEnableds={objOfIsExternalAuthEnableds}
+        isRegistering={isRegistering}
+        csrf={csrf}
       />
     </I18nextProvider>,
     loginFormElem,

+ 3 - 3
src/client/js/services/PageContainer.js

@@ -6,9 +6,9 @@ import * as entities from 'entities';
 import * as toastr from 'toastr';
 
 const logger = loggerFactory('growi:services:PageContainer');
-const scrollThresForSticky = 50;
-const scrollThresForCompact = 100;
-const scrollThresForThrottling = 200;
+const scrollThresForSticky = 0;
+const scrollThresForCompact = 30;
+const scrollThresForThrottling = 100;
 
 /**
  * Service container related to Page

+ 1 - 1
src/client/styles/scss/_admin.scss

@@ -1,5 +1,5 @@
 .admin-page {
-  .grw-header.sticky-top {
+  header.grw-header {
     height: unset;
   }
 

+ 75 - 1
src/client/styles/scss/_layout.scss

@@ -1,5 +1,24 @@
 @import 'layout_variable';
 
+%fukidashi-for-active {
+  position: relative;
+
+  // speech balloon
+  &:after {
+    position: absolute;
+    top: 0.5em;
+    right: -1em;
+    display: block;
+    width: 0;
+    content: '';
+    border: 1em solid transparent;
+    border-left-width: 0;
+
+    // @include media-breakpoint-down(xs) {
+    // }
+  }
+}
+
 // FIXME: replace with mt-2 or mt-3
 .grw-mt-10px {
   margin-top: 10px !important;
@@ -32,10 +51,53 @@
 
 .grw-sidebar {
   .ak-navigation-resize-button {
-    top: 110px;
+    top: calc(50vh - 20px);
+  }
+
+  // override @atlaskit/navigation-next styles
+  div[class$='-NavigationContainer'] {
+    // Adjust to be on top of the growi subnavigation
+    z-index: $zindex-sticky + 5;
+  }
+
+  // override @atlaskit/navigation-next styles
+  div[class$='-Outer'] {
+    div[class$='-Shadow'] {
+      background: unset;
+      border-right: 1px solid $border;
+    }
+  }
+
+  .grw-global-item-container {
+    i {
+      font-size: 1.5em;
+    }
+
+    // icon opacity
+    &:not(.active) {
+      i {
+        opacity: 0.4;
+      }
+      &:hover,
+      &:focus {
+        i {
+          opacity: 0.7;
+        }
+      }
+    }
+
+    &.active {
+      button {
+        @extend %fukidashi-for-active;
+      }
+    }
   }
 }
 
+#page-wrapper {
+  margin-top: $grw-navbar-height;
+}
+
 .grw-sidebar-header-container {
   padding: 10px;
 
@@ -100,6 +162,18 @@
   }
 }
 
+.grw-fixed-controls-container {
+  position: fixed;
+  right: 1em;
+  bottom: 3em;
+
+  transition: all 200ms linear;
+
+  .grw-fixed-controls-button-container {
+    box-shadow: 0 3px 4px rgba($black, 0.3);
+  }
+}
+
 // printable style
 @media print {
   padding: 30px;

+ 3 - 134
src/client/styles/scss/_layout_kibela.scss

@@ -1,28 +1,8 @@
-@import '../scss/theme/layout_kibela_variable';
-
 body.kibela {
-  .icon-link,
-  .CodeMirror-hint-active,
-  .grw-nav-main-left-tab,
-  .tav-pane,
-  .active {
-    color: #5882fa;
-  }
-
-  .bg-white {
-    background: #fefffe !important;
-  }
-
-  .bg-primary {
-    background-color: $primary !important;
-  }
-
+  /* Logo */
   .logo {
-    background: transparent;
-
     .logo-mark {
       height: 50px;
-      background-color: white;
       box-shadow: none;
 
       svg {
@@ -32,10 +12,6 @@ body.kibela {
   }
 
   /* header */
-  .background-t {
-    background-color: transparent;
-  }
-
   .authors {
     padding-top: 10px;
 
@@ -44,28 +20,12 @@ body.kibela {
     }
   }
 
-  .search-input-group,
-  .search-typeahead {
-    .btn {
-      background-color: transparent;
-    }
-  }
-
   .panel-heading {
     border-radius: 0 !important;
   }
 
-  .btn-open-dropzone {
-    background: rgb(243, 245, 247);
-  }
-
   /* page list */
-  .page-list {
-    background: white;
-  }
-
   .page-attachments-row {
-    background-color: #e5ecf1 !important;
     border: 0px;
   }
 
@@ -75,7 +35,6 @@ body.kibela {
 
   .round-corner-top {
     z-index: absolute;
-    border-top: solid 0.4em #5584e1;
     border-radius: 0.35em;
   }
 
@@ -86,16 +45,14 @@ body.kibela {
     bottom: 0px;
     left: 0px;
     z-index: absolute;
-    height: 11em;
     max-width: 840px;
+    height: 11em;
     margin: auto;
-    border-top: solid 0.4em #5584e1;
     border-radius: 0.35em;
   }
 
   .grw-subnav {
     position: relative;
-    background: transparent;
     border: none;
 
     svg {
@@ -131,114 +88,27 @@ body.kibela {
     .list-group-item + .list-group-item.active {
       margin-top: 2px;
     }
-
-    .list-group-item.active {
-      color: #fff;
-      background: #1256a3;
-    }
-
-    .list-group-item {
-      &:hover {
-        background: #eee;
-      }
-    }
-  }
-
-  /* search page */
-  .search-result-list,
-  .page-list-li {
-    background: #f4f5f6;
   }
 
   /* Tabs */
   .nav.nav-tabs {
     > .nav-item {
-      color: #5882fa;
       cursor: pointer;
-      background: transparent;
-
-      &:hover,
-      &:focus {
-        > .nav-link {
-          color: #7a94d9;
-        }
-      }
 
       > .nav-link {
-        color: #5882fa;
         border: none;
         border-radius: 3px;
       }
-
-      > .nav-link.active {
-        background: transparent !important;
-        border-bottom: solid 2.7px #5584e1;
-      }
-    }
-
-    .wiki {
-      h1 {
-        border-bottom: solid 2px #5584e1 !important;
-      }
-
-      h2 {
-        border-color: solid 1px #5584e1;
-      }
-    }
-  }
-
-  /* Modal */
-  .modal-content {
-    background-color: $themelight;
-
-    .modal-header.bg-primary {
-      color: white;
-
-      .close {
-        color: white;
-      }
-    }
-  }
-
-  /* Inline Code */
-  :not(.hljs) > code:not(.hljs) {
-    background-color: $bgcolor-inline-code;
-    color: $color-inline-code;
-  }
-
-  /* Card */
-  .card {
-    border: 1px solid $border;
-
-    .card-header {
-      background-color: $lightthemecolor;
-      border-bottom: 1px solid $border;
-    }
-
-    .card-body {
-      background-color: $themelight;
-    }
-
-    .card-footer {
-      background: white;
-      border-top: 1px solid $border;
     }
   }
 
   /* button */
   .btn {
-    border-radius: $radius;
-  }
-
-  .btn-primary {
-    background: $primary;
-    border: 1px solid $primary;
+    // border-radius: $radius;
   }
 
   /* edit */
   .CodeMirror {
-    border: solid 1.2px #d8d8d8;
-    border-top: solid 0.3em #5584e1 !important;
     border-radius: 0.35em;
   }
 
@@ -279,7 +149,6 @@ body.kibela {
     .page-editor-preview-container {
       padding-right: 0px !important;
       padding-left: 2em;
-      background: white !important;
     }
 
     .page-editor-footer {

+ 0 - 22
src/client/styles/scss/_layout_variable.scss

@@ -1,30 +1,8 @@
 /* color variables */
 
-/* green */
-$grw-base-green: rgb(7, 146, 72);
-$grw-mos-green: rgb(5, 70, 35);
-$grw-light-green: rgb(204, 238, 220);
-$grw-white-green: #f8fffb;
-$grw-sea-green: seagreen;
-
-.bg-grw-green {
-  background: $grw-base-green;
-}
-
-.bg-grw-mos-green {
-  background: $grw-mos-green;
-}
-
-.bg-grw-light-green {
-  background: $grw-light-green;
-}
-
 /* blue */
 $grw-alice-blue: aliceblue;
 
 /* gray */
 $grw-line-gray: #dee2e6;
 $grw-line-light-gray: #ddd;
-
-/* white */
-$grw-floral-white: floralwhite;

+ 1 - 1
src/client/styles/scss/_me.scss

@@ -1,5 +1,5 @@
 .user-settings-page {
-  .grw-header.sticky-top {
+  header.grw-header {
     height: unset;
   }
 }

+ 1 - 1
src/client/styles/scss/_mixins.scss

@@ -72,7 +72,7 @@
         .hackmd-preinit,
         .hackmd-error,
         #iframe-hackmd-container > iframe {
-          width: 100vw;
+          width: 100%;
           height: calc(100vh - #{$header-plus-footer});
           min-height: calc(100vh - #{$header-plus-footer}); // for IE11
         }

+ 5 - 0
src/client/styles/scss/_navbar.scss

@@ -1,4 +1,9 @@
 .grw-navbar {
+  .grw-navbar-toggler {
+    padding: 0.5rem;
+    font-size: 1.5em;
+  }
+
   .nav-link,
   .nav-item.confidential {
     display: flex;

+ 0 - 4
src/client/styles/scss/_on-edit.scss

@@ -197,10 +197,6 @@ body.on-edit {
     }
 
     #page-editor-options-selector {
-      label {
-        margin-right: 0.5em;
-      }
-
       .grw-editor-configuration-dropdown {
         .icon-container {
           display: inline-block;

+ 1 - 1
src/client/styles/scss/_override-bootstrap-variables.scss

@@ -18,7 +18,7 @@ $dark: #3e4d6c !default;
 //## Font, line-height, and color for body text, headings, and more.
 $font-family-sans-serif:  Lato, -apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
 $font-family-serif:       Georgia, "Times New Roman", Times, serif;
-$font-family-monospace: Osaka-Mono, 'MS Gothic', Monaco, Menlo, Consolas, 'Courier New', monospace;
+$font-family-monospace: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
 $font-family-base:        $font-family-sans-serif;
 
 $font-size-root: 14px;

+ 8 - 0
src/client/styles/scss/_override-rbt.scss

@@ -9,6 +9,14 @@
     }
   }
 }
+
+.rbt-input-wrapper {
+  .close.rbt-close {
+    // default bootstrap .close has padding 0
+    padding: 3px 7px;
+  }
+}
+
 // hide loading icon
 .rbt-aux {
   display: none;

+ 1 - 0
src/client/styles/scss/_page.scss

@@ -70,6 +70,7 @@
       .revision-history-diff {
         padding-left: 40px;
         color: #333;
+        table-layout: fixed;
       }
     }
 

+ 8 - 5
src/client/styles/scss/_subnav.scss

@@ -1,6 +1,6 @@
 $easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
 
-@mixin setTransitionForCompactMode() {
+%transitionForCompactMode {
   // set transition-duration (normal -> compact)
   transition: all 300ms $easeInOutCubic;
 }
@@ -8,9 +8,12 @@ $easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
 /*
  * layout for sticky
  */
-.grw-header.sticky-top {
+header.grw-header {
+  top: $grw-navbar-height;
+
   // Adjust to be on top of the growi subnavigation
-  z-index: $zindex-sticky - 100;
+  z-index: $zindex-sticky;
+
   height: 80px;
   pointer-events: none; // disable pointer events for sticky
 
@@ -25,11 +28,11 @@ $easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
  */
 .grw-subnavbar {
   &.grw-subnavbar-compact {
-    @include setTransitionForCompactMode();
+    @extend %transitionForCompactMode;
 
     h1 {
       @include variable-font-size(18px);
-      @include setTransitionForCompactMode();
+      @extend %transitionForCompactMode;
     }
   }
 }

+ 1 - 1
src/client/styles/scss/_tag.scss

@@ -1,5 +1,5 @@
 .tags-page {
-  .grw-header.sticky-top {
+  header.grw-header {
     height: unset;
   }
 

+ 5 - 7
src/client/styles/scss/_user.scss

@@ -1,6 +1,6 @@
 $easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
 
-@mixin setTransitionForCompactMode() {
+%transitionForCompactMode {
   // set transition-duration (normal -> compact)
   transition: all 300ms $easeInOutCubic;
 }
@@ -18,24 +18,24 @@ $easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
       margin-bottom: 0;
       font-size: 14px;
 
-      @include setTransitionForCompactMode();
+      @extend %transitionForCompactMode;
     }
     .picture {
       width: 62px;
       height: 62px;
 
-      @include setTransitionForCompactMode();
+      @extend %transitionForCompactMode;
     }
     h1 {
       font-size: 1.5em;
       line-height: 30px;
 
-      @include setTransitionForCompactMode();
+      @extend %transitionForCompactMode;
     }
     .users-meta {
       margin-left: 15px;
 
-      @include setTransitionForCompactMode();
+      @extend %transitionForCompactMode;
     }
   }
 }
@@ -88,10 +88,8 @@ $easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
     }
   }
 
-  .btn.btn-like,
   .btn.btn-bookmark {
     &.btn-lg {
-      padding: 8px;
       font-size: 1.5em;
     }
   }

+ 2 - 2
src/client/styles/scss/_variables.scss

@@ -6,8 +6,8 @@ $font-family-monospace-not-strictly: Monaco, Menlo, Consolas, 'Courier New', Mei
 
 //== Layout
 $grw-navbar-height: 50px;
-$grw-logo-width: 60px;
-$grw-logomark-width: 32px;
+$grw-logo-width: 64px;
+$grw-logomark-width: 40px;
 
 // fix tab width to 95 pixels
 // see also '_on-edit.scss'

+ 1 - 32
src/client/styles/scss/atoms/_buttons.scss

@@ -1,36 +1,5 @@
-.btn.btn-circle {
-  width: 30px;
-  height: 30px;
-  padding: 6px 0;
-  font-size: 12px;
-  line-height: 1.428571429;
-  text-align: center;
-  border-radius: 15px;
-}
-
-.btn-circle.btn-lg {
-  width: 50px;
-  height: 50px;
-  padding: 10px 16px;
-  font-size: 18px;
-  line-height: 1.33;
-  border-radius: 25px;
-}
-
-.btn-circle.btn-xl {
-  width: 70px;
-  height: 70px;
-  padding: 10px 16px;
-  font-size: 24px;
-  line-height: 1.33;
-  border-radius: 35px;
-}
-
 .btn.btn-like,
 .btn.btn-bookmark {
-  font-size: 1.2em;
-  line-height: 1em;
-
   &.active,
   &:hover {
     // header buttons are always white for active
@@ -60,7 +29,7 @@
 }
 
 // fill button style
-:root button.btn-fill {
+.btn-fill {
   position: relative;
   overflow: hidden;
   color: white;

+ 212 - 0
src/client/styles/scss/theme/_apply-colors-kibela.scss

@@ -0,0 +1,212 @@
+body.kibela {
+  .icon-link,
+  .CodeMirror-hint-active,
+  .grw-nav-main-left-tab,
+  .tav-pane,
+  .active {
+    color: $subthemecolor;
+  }
+  
+  .bg-white {
+    background: #fefffe !important;
+  }
+  
+  .bg-primary {
+    background-color: $primary !important;
+  }
+  
+  /* kibela block */
+  .kibela-block {
+    border-top: solid 0.4em $thickborder;
+  }
+
+  /* page wrapper */
+  #page-wrapper {
+    background-color: $bgcolor-global;
+  }
+
+  /* Logo */
+  .logo {
+    background: transparent;
+
+    .logo-mark {
+      background-color: white;
+    }
+  }
+
+  /* header*/
+  .background-t {
+    background-color: transparent;
+  }
+
+  .search-input-group,
+  .search-typeahead {
+    .btn {
+      background-color: transparent;
+    }
+  }
+
+  .btn-open-dropzone {
+    background: $themelight;
+  }
+
+  /* page list */
+  .page-list {
+    background: white;
+  }
+
+  .page-attachments-row {
+    background-color: #e5ecf1;
+  }
+
+  /* round */
+  .round-corner-top {
+    border-top: solid 0.4em $thickborder;
+  }
+
+  /* admin navigation */
+  .admin-navigation {
+    .list-group-item {
+      background-color: transparent;
+
+      &:hover {
+        background: #eee;
+      }
+    }
+
+    .list-group-item.active {
+      color: white;
+      background: $bgcolor-navbar-active;
+    }
+  }
+
+  /* search page */
+  .search-result-list,
+  .page-list-li {
+    background: $themelight;
+  }
+
+  /* Tabs */
+  .nav.nav-tabs {
+    > .nav-item {
+      color: $color-link;
+      background: transparent;
+
+      &:hover,
+      &:focus {
+        > .nav-link {
+          color: $color-link-hover;
+        }
+      }
+
+      > .nav-link {
+        color: $color-link;
+      }
+
+      > .nav-link.active {
+        background: transparent !important;
+        border-bottom: solid 2.7px $thickborder;
+      }
+    }
+  }
+
+  /* wiki */
+  .wiki {
+    h1 {
+      border-bottom: solid 2px $thickborder !important;
+    }
+
+    h2 {
+      border-color: solid 1px $thickborder;
+    }
+
+    // change color of highlighted header in wiki (default: orange)
+    .code-line.revision-head.highlighted {
+      color: $themelight;
+      background-color: lighten($bgcolor-theme, 20%);
+
+      .icon-note,
+      .icon-link {
+        color: $themelight;
+      }
+    }
+  }
+
+  /* Modal */
+  .modal-content {
+    background-color: $themelight;
+
+    .modal-header.bg-primary {
+      color: white;
+
+      .close {
+        color: white;
+      }
+    }
+  }
+
+  /* Inline Code */
+  :not(.hljs) > code:not(.hljs) {
+    color: $color-inline-code;
+    background-color: $bgcolor-inline-code;
+  }
+
+  /* Card */
+  .card {
+    border: 1px solid $border-color-theme;
+
+    .card-header {
+      background-color: $lightthemecolor;
+      border-bottom: 1px solid $border-color-theme;
+    }
+
+    .card-body {
+      background-color: $themelight;
+    }
+
+    .card-footer {
+      background: white;
+      border-top: 1px solid $border-color-theme
+    }
+  }
+
+  /* button */
+  .btn-primary {
+    background: $primary;
+    border: 1px solid $primary;
+  }
+
+  /* edit */
+  .CodeMirror {
+    border: solid 1.2px #d8d8d8;
+    border-top: solid 0.3em $thickborder !important;
+  }
+
+  &.on-edit {
+    .page-editor-preview-container {
+      background: white !important;
+    }
+  }
+
+  /* subnav */
+  .grw-subnav {
+    background: transparent;
+  }
+
+  /* navbar */
+  .grw-navbar {
+    .nav-item > .nav-link:hover {
+      color: $color-link-nabvar-hover;
+    }
+  }
+
+  /* h */
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
+    color: $color-header;
+  }
+}

+ 7 - 2
src/client/styles/scss/theme/_apply-colors.scss

@@ -56,6 +56,8 @@ $link-hover-color: $color-link-hover;
 //
 
 .grw-logo {
+  background-color: darken($bgcolor-navbar, 10%);
+
   // set transition for fill
   svg * {
     transition: fill 0.8s ease-out;
@@ -86,8 +88,11 @@ $link-hover-color: $color-link-hover;
 }
 
 .grw-sidebar {
-  .grw-logo {
-    background-color: darken($bgcolor-navbar, 10%);
+  .grw-global-item-container.active {
+    button:after {
+      // fukidashi color
+      border-right-color: darken($bgcolor-global, 4%);
+    }
   }
 }
 

+ 0 - 39
src/client/styles/scss/theme/_layout_kibela_variable.scss

@@ -1,39 +0,0 @@
-$radius: .25em;
-
-$bgcolor-theme: rgb(18, 86, 163);
-$themelight: #f4f5f6;
-$subthemecolor: rgb(90, 149, 216);
-$lightthemecolor: rgba(181, 203, 247, 0.61);
-
-$bgcolor-navbar: $bgcolor-theme;
-$bgcolor-global: $themelight;
-$bgcolor-global: $themelight;
-
-$color-header: $bgcolor-theme;
-$color-global: #3c4a60;
-$linktext: rgb(74, 109, 204);
-$linktext-hover: lighten($linktext, 12%);
-$sidebar-text: $bgcolor-theme;
-
-$primary: $bgcolor-theme;
-$info: lighten($bgcolor-theme, 20%);
-
-$fillcolor-logo-mark: lighten($bgcolor-theme, 20%);
-$color-link-wiki: lighten($bgcolor-theme, 20%);
-$color-link-wiki-hover: lighten($color-link-wiki, 20%);
-$color-inline-code: $subthemecolor;
-$bgcolor-inline-code: lighten($subthemecolor, 70%);
-$border: $lightthemecolor;
-
-// change color of highlighted header in wiki (default: orange)
-.wiki {
-  .code-line.revision-head.highlighted {
-    color: $themelight;
-    background-color: lighten($bgcolor-theme, 20%);
-
-    .icon-note,
-    .icon-link {
-      color: $themelight;
-    }
-  }
-}

+ 7 - 0
src/client/styles/scss/theme/_reboot-bootstrap-theme-colors.scss

@@ -56,3 +56,10 @@
     }
   }
 }
+
+@each $theme-color, $color in $theme-colors {
+  .badge.badge-#{$theme-color} {
+    color: $bgcolor-global;
+    background: $color;
+  }
+}

+ 91 - 7
src/client/styles/scss/theme/kibela.scss

@@ -1,9 +1,93 @@
-// import colors
-@import '../../agile-admin/inverse/colors/kibela';
-@import 'layout_kibela_valiable';
+@import '../variables';
+@import '../override-bootstrap-variables';
 
-// apply agile-admin theme
-@import '../../agile-admin/inverse/style';
+// Light Mode
+html[light] {
+  $bgcolor-theme: rgb(18, 86, 163);
+  $themelight: #f4f5f6;
+  $subthemecolor: rgb(88, 130, 250);
+  $lightthemecolor: rgba(181, 203, 247, 0.61);
 
-// override
-@import 'override-agileadmin';
+  // Background colors
+  $bgcolor-navbar: white;
+  $bgcolor-navbar-active: $bgcolor-theme;
+  $bgcolor-global: $themelight;
+  $bgcolor-card: #e3e5e7;
+  $bgcolor-inline-code: lighten($subthemecolor, 70%);
+
+  $color-header: $bgcolor-theme;
+  $color-global: #3c4a60;
+  $color-link: rgb(74, 109, 204);
+  $color-link-hover: lighten($color-link, 12%);
+  $sidebar-text: $bgcolor-theme;
+  $color-reversal: #eee;
+
+  $primary: $bgcolor-theme;
+  $info: lighten($bgcolor-theme, 20%);
+
+  $fillcolor-logo-mark: lighten($bgcolor-theme, 20%);
+  $color-link-wiki: lighten($bgcolor-theme, 20%);
+  $color-link-wiki-hover: lighten($color-link-wiki, 20%);
+  $color-link-nabvar: $color-global;
+  $color-link-nabvar-hover: $color-global;
+  $color-inline-code: $subthemecolor;
+
+  // border colors
+  $border-color-theme: $lightthemecolor;
+  $thickborder: #5584e1;
+
+  // dropdown colors
+  $bgcolor-dropdown-link-active: $growi-blue;
+  $color-dropdown-link-active: $color-reversal;
+  $color-dropdown-link-hover: $color-global;
+
+  @import 'apply-colors';
+  @import 'apply-colors-light';
+  @import 'apply-colors-kibela';
+}
+
+
+// Dark Mode ( same as Light Mode )
+html[dark] {
+  $bgcolor-theme: rgb(18, 86, 163);
+  $themelight: #f4f5f6;
+  $subthemecolor: rgb(88, 130, 250);
+  $lightthemecolor: rgba(181, 203, 247, 0.61);
+
+  // Background colors
+  $bgcolor-navbar: white;
+  $bgcolor-navbar-active: $bgcolor-theme;
+  $bgcolor-global: $themelight;
+  $bgcolor-card: #e3e5e7;
+  $bgcolor-inline-code: lighten($subthemecolor, 70%);
+
+  $color-header: $bgcolor-theme;
+  $color-global: #3c4a60;
+  $color-link: rgb(74, 109, 204);
+  $color-link-hover: lighten($color-link, 12%);
+  $sidebar-text: $bgcolor-theme;
+  $color-reversal: #eee;
+
+  $primary: $bgcolor-theme;
+  $info: lighten($bgcolor-theme, 20%);
+
+  $fillcolor-logo-mark: lighten($bgcolor-theme, 20%);
+  $color-link-wiki: lighten($bgcolor-theme, 20%);
+  $color-link-wiki-hover: lighten($color-link-wiki, 20%);
+  $color-link-nabvar: $color-global;
+  $color-link-nabvar-hover: $color-global;
+  $color-inline-code: $subthemecolor;
+
+  // border colors
+  $border-color-theme: $lightthemecolor;
+  $thickborder: #5584e1;
+
+  // dropdown colors
+  $bgcolor-dropdown-link-active: $growi-blue;
+  $color-dropdown-link-active: $color-reversal;
+  $color-dropdown-link-hover: $color-global;
+
+  @import 'apply-colors';
+  @import 'apply-colors-light';
+  @import 'apply-colors-kibela';
+}

+ 26 - 0
src/migrations/2020042016039-remove-crowi-layout.js

@@ -0,0 +1,26 @@
+require('module-alias/register');
+const logger = require('@alias/logger')('growi:migrate:remove-crowi-lauout');
+
+const mongoose = require('mongoose');
+const config = require('@root/config/migrate');
+
+const { getModelSafely } = require('@commons/util/mongoose-utils');
+
+module.exports = {
+  async up(db) {
+    logger.info('Apply migration');
+    mongoose.connect(config.mongoUri, config.mongodb.options);
+
+    const Config = getModelSafely('Config') || require('@server/models/config')();
+
+    const query = { key: 'customize:layout', value: JSON.stringify('crowi') };
+
+    await Config.findOneAndUpdate(query, { value: JSON.stringify('growi') }); // update layout
+
+    logger.info('Migration has successfully applied');
+  },
+
+  down(db) {
+    // do not rollback
+  },
+};

+ 1 - 1
src/server/routes/apiv3/customize-setting.js

@@ -100,7 +100,7 @@ module.exports = (crowi) => {
       ]),
     ],
     layoutTheme: [
-      body('layoutType').isString().isIn(['growi', 'kibela', 'crowi']),
+      body('layoutType').isString().isIn(['growi', 'kibela']),
       body('themeType').isString().isIn([
         'default', 'nature', 'mono-blue', 'wood', 'island', 'christmas', 'antarctic', 'future', 'blue-night', 'halloween', 'spring',
       ]),

+ 1 - 1
src/server/views/invited.html

@@ -70,7 +70,7 @@
           </div>
           <input type="text" class="form-control" placeholder="{{ t('User ID') }}" name="invitedForm[username]" value="{{ req.body.invitedForm.username }}" required>
         </div>
-        <p class="help-block">
+        <p class="form-text text-muted">
           <span id="help-block-username"></span>
         </p>
 

+ 1 - 1
src/server/views/layout-growi/base/layout.html

@@ -9,7 +9,7 @@
 {% block layout_main %}
 
 {% block content_header_wrapper %}
-<header class="sticky-top py-0 grw-header">
+<header class="py-0 position-sticky grw-header">
   {% block content_header %}
   {% endblock %}
 </header>

+ 1 - 1
src/server/views/layout-growi/user_page.html

@@ -6,7 +6,7 @@
 {% endblock %}
 
 {% block content_header_wrapper %}
-  <header class="sticky-top py-0 grw-header grw-header-user-page">
+  <header class="py-0 position-sticky grw-header grw-header-user-page">
     {% if pageUser %}
       <div id="grw-subnav-for-user-page" class="grw-subnav" data-page-user="{{ pageUser|json }}"></div>
     {% else %}

+ 68 - 54
src/server/views/layout/layout.html

@@ -71,74 +71,88 @@
   data-userlang="{% if user %}{{ user.lang }}{% endif %}"
  >
 
-<div id="wrapper" class="d-flex">
+<div id="wrapper">
 
-  {# Sidebar #}
-  <nav>
-    <div id="grw-sidebar" class="grw-sidebar"></div>
-  </nav>
+  {% block layout_head_nav %}
+  <nav class="navbar grw-navbar navbar-expand-sm navbar-dark fixed-top mb-0 p-sm-0">
+    <div id="grw-navbar-toggler" class="d-sm-none mr-auto"></div>
 
-  <div class="flex-grow-1">
-
-    {% block layout_head_nav %}
-    <nav class="navbar grw-navbar navbar-expand-sm navbar-dark mb-0 p-0">
-      {# Navbar Left #}
-      <ul class="navbar-nav mr-auto pl-4">
-        <li>
-          {% if isSearchServiceConfigured() %}
-          <div class="navbar-form navbar-left search-top" role="search" id="search-top"></div>
-          {% endif %}
-        </li>
-      </ul>
-
-      {# Navbar Right #}
-      <ul class="navbar-nav">
-        {% if user and user.admin %}
-        <li class="nav-item">
-          <a class="nav-link" href="/admin">
-            <i class="icon-settings mr-2"></i>
-            <span class="d-none d-md-inline-block">{{ t('Admin') }}</span>
-          </a>
-        </li>
-        {% endif %}
+    {# Brand Logo #}
+    <div class="navbar-brand mr-sm-auto">
+      <a class="grw-logo d-block" href="/">
+        {% include '../widget/logo.html' %}
+      </a>
+    </div>
 
-        {% if user %}
-        <li class="nav-item">
-          <a class="nav-link create-page" href="#" data-target="#create-page" data-toggle="modal">
-            <i class="icon-pencil mr-2"></i>
-            <span class="d-none d-md-inline-block">{{ t('New') }}</span>
-          </a>
-        </li>
-        <li class="nav-item">
-          <a class="nav-link" href="https://docs.growi.org/" target="_blank">
-            <i class="icon-question mr-2"></i><span class="d-none d-md-inline-block mr-2">{{ t('Help') }}</span><span class="text-muted small"><i class="icon-share-alt"></i></span>
-          </a>
-        </li>
-        <li id="personal-dropdown" class="nav-item dropdown dropdown-toggle"></li>
-        {% else %}
-        <li id="login-user" class="nav-item"><a class="nav-link" href="/login">Login</a></li>
+    {# Navbar Center #}
+    <ul class="navbar-nav d-none d-md-block">
+      <li>
+        {% if isSearchServiceConfigured() %}
+        <div class="search-top" role="search" id="search-top"></div>
         {% endif %}
-        {% if getConfig('crowi', 'app:confidential') %}
-        <li class="nav-item confidential text-light">{{ getConfig('crowi', 'app:confidential') }}</li>
-        {% endif %}
-      </ul>
+      </li>
+    </ul>
+
+    {# Navbar Right #}
+    <ul class="navbar-nav ml-auto">
+      {% if user and user.admin %}
+      <li class="nav-item d-none d-md-block">
+        <a class="nav-link" href="/admin">
+          <i class="icon-settings mr-2"></i>
+          {{ t('Admin') }}
+        </a>
+      </li>
+      {% endif %}
+
+      {% if user %}
+      <li class="nav-item d-none d-md-block">
+        <a class="nav-link create-page" href="#" data-target="#create-page" data-toggle="modal">
+          <i class="icon-pencil mr-2"></i>
+          {{ t('New') }}
+        </a>
+      </li>
+      <li class="nav-item d-none d-md-block">
+        <a class="nav-link" href="https://docs.growi.org/" target="_blank">
+          <i class="icon-question mr-2"></i><span class="mr-2">{{ t('Help') }}</span><span class="small"><i class="icon-share-alt"></i></span>
+        </a>
+      </li>
+      <li id="personal-dropdown" class="nav-item dropdown dropdown-toggle"></li>
+      {% else %}
+      <li id="login-user" class="nav-item"><a class="nav-link" href="/login">Login</a></li>
+      {% endif %}
+      {% if getConfig('crowi', 'app:confidential') %}
+      <li class="nav-item confidential text-light">{{ getConfig('crowi', 'app:confidential') }}</li>
+      {% endif %}
+    </ul>
 
-    </nav>
-    {% include '../modal/create_page.html' %}
-    {% endblock  %} {# layout_head_nav #}
+  </nav>
+  {% include '../modal/create_page.html' %}
+  {% endblock  %} {# layout_head_nav #}
 
-    {% block head_warn_alert_siteurl_undefined %}{% include '../widget/alert_siteurl_undefined.html' %}{% endblock %}
-    {% block head_warn_breaking_changes %}{% include '../widget/alert_breaking_changes.html' %}{% endblock %}
+  {% block head_warn_alert_siteurl_undefined %}{% include '../widget/alert_siteurl_undefined.html' %}{% endblock %}
+  {% block head_warn_breaking_changes %}{% include '../widget/alert_breaking_changes.html' %}{% endblock %}
 
-    <div id="page-wrapper">
+  <div class="d-flex">
+    {# Sidebar #}
+    <nav class="d-none d-sm-block">
+      <div id="grw-sidebar" class="grw-sidebar"></div>
+    </nav>
+    <div id="page-wrapper" class="flex-grow-1">
       {% block layout_main %}
       {% endblock %} {# layout_main #}
     </div>
-
   </div>
 
 </div><!-- /#wrapper -->
 
+<div class="grw-fixed-controls-container d-md-none animated fadeInUp faster">
+  <div class="grw-fixed-controls-button-container rounded-circle">
+    <button class="btn btn-lg btn-primary rounded-circle waves-effect waves-light" type="button" data-target="#create-page" data-toggle="modal">
+      <i class="icon-pencil"></i>
+    </button>
+  </div>
+</div>
+
 <!-- /#staff-credit -->
 <div id="staff-credit"></div>
 

+ 3 - 13
src/server/views/login.html

@@ -104,22 +104,12 @@
           </div>
         </div>
       </div>
-        <!-- [TODO][GW-1913] react component gets params without swig  -->
-        {% set isLocalStrategySetup = passportService.isLocalStrategySetup %}
-        {% set isLdapStrategySetup = passportService.isLdapStrategySetup %}
         {% set isRegistrationEnabled = passportService.isLocalStrategySetup && getConfig('crowi', 'security:registrationMode') != 'Closed' %}
 
+      <!-- [TODO][GW-1913] An AppContainer gets csrf data -->
       <div id="login-form"
-        data-is-local-strategy-setup="{{ isLocalStrategySetup }}"
-        data-is-ldap-strategy-setup="{{ isLdapStrategySetup }}"
-        data-is-registration-enabled="{{ isRegistrationEnabled }}"
-        data-is-google-auth-enabled="{{ getConfig('crowi', 'security:passport-google:isEnabled') }}"
-        data-is-github-auth-enabled="{{ getConfig('crowi', 'security:passport-github:isEnabled') }}"
-        data-is-facebook-auth-enabled="{{ getConfig('crowi', 'security:passport-facebook:isEnabled') }}"
-        data-is-twitter-auth-enabled="{{ getConfig('crowi', 'security:passport-twitter:isEnabled') }}"
-        data-is-oidc-auth-enabled="{{ getConfig('crowi', 'security:passport-oidc:isEnabled') }}"
-        data-is-saml-auth-enabled="{{ getConfig('crowi', 'security:passport-saml:isEnabled') }}"
-        data-is-basic-auth-enabled="{{ getConfig('crowi', 'security:passport-basic:isEnabled') }}"
+        data-is-registering="{{ req.query.register or req.body.registerForm or isRegistering }}"
+        data-csrf="{{ csrf() }}"
       ></div>
 
       <div>

+ 2 - 2
src/server/views/modal/delete.html

@@ -27,7 +27,7 @@
             <input class="custom-control-input" name="recursively" id="cbDeleteRecursively" value="1" type="checkbox" checked>
             <label class="custom-control-label" for="cbDeleteRecursively">
               {{ t('modal_delete.delete_recursively') }}
-              <p class="help-block mt-0"> {{ t('modal_delete.recursively', page.path) }}</p>
+              <p class="form-text text-muted mt-0"> {{ t('modal_delete.recursively', page.path) }}</p>
             </label>
           </div>
           {% endif %}
@@ -36,7 +36,7 @@
             <input class="custom-control-input" name="completely" id="cbDeleteCompletely" {% if !user.canDeleteCompletely(page.creator._id) %} disabled="disabled" {% endif %} value="1"  type="checkbox">
             <label class="custom-control-label" for="cbDeleteCompletely" class="text-danger">
               {{ t('modal_delete.delete_completely') }}
-              <p class="help-block mt-0"> {{ t('modal_delete.completely') }}</p>
+              <p class="form-text text-muted mt-0"> {{ t('modal_delete.completely') }}</p>
             </label>
             {% if !user.canDeleteCompletely(page.creator._id) %}
               <p class="alert alert-warning p-2 my-0"><i class="icon-ban icon-fw" ></i>{{ t('modal_delete.delete_completely_restriction') }}</p>

+ 1 - 1
src/server/views/modal/put_back.html

@@ -17,7 +17,7 @@
             <input class="custom-control-input" name="recursively" id="cbPutbackRecursively" value="1" type="checkbox" checked>
             <label class="custom-control-label" for="cbPutbackRecursively">
               {{ t('modal_putback.label.recursively') }}
-              <p class="help-block mt-0">{{ t('modal_putback.help.recursively', page.path) }}</p>
+              <p class="form-text text-muted mt-0">{{ t('modal_putback.help.recursively', page.path) }}</p>
             </label>
           </div>
         </div>

+ 3 - 3
src/server/views/modal/rename.html

@@ -33,7 +33,7 @@
             <input class="custom-control-input" name="recursively" id="cbRenameRecursively" value="1" type="checkbox" checked >
             <label class="custom-control-label" for="cbRenameRecursively">
               {{ t('modal_rename.label.Recursively') }}
-              <p class="help-block mt-0">{{ t('modal_rename.help.recursive', page.path) }}</p>
+              <p class="form-text text-muted mt-0">{{ t('modal_rename.help.recursive', page.path) }}</p>
             </label>
           </div>
 
@@ -41,7 +41,7 @@
             <input class="custom-control-input" name="create_redirect" id="cbRenameRedirect" value="1" type="checkbox">
             <label class="custom-control-label" for="cbRenameRedirect">
               {{ t('modal_rename.label.Redirect') }}
-              <p class="help-block mt-0">{{ t('modal_rename.help.redirect', page.path) }}</p>
+              <p class="form-text text-muted mt-0">{{ t('modal_rename.help.redirect', page.path) }}</p>
             </label>
           </div>
 
@@ -49,7 +49,7 @@
             <input class="custom-control-input" name="remain_metadata" id="cbRenameMetadata" value="1" type="checkbox">
             <label class="custom-control-label" for="cbRenameMetadata">
               {{ t('modal_rename.label.Do not update metadata') }}
-              <p class="help-block mt-0">{{ t('modal_rename.help.metadata') }}</p>
+              <p class="form-text text-muted mt-0">{{ t('modal_rename.help.metadata') }}</p>
             </label>
           </div>
 

+ 8 - 8
src/server/views/modal/shortcuts.html

@@ -10,7 +10,7 @@
       <div class="modal-body">
 
         <div class="row">
-          <div class="col-md-6">
+          <div class="col-lg-6">
             <h3><strong>{{ t('modal_shortcuts.global.title') }}</strong></h3>
 
             <table class="table">
@@ -38,9 +38,9 @@
                 </td>
               </tr>
             </table>
-          </div><!-- /.col-md-6 -->
+          </div><!-- /.col-lg-6 -->
 
-          <div class="col-md-6">
+          <div class="col-lg-6">
             <h3><strong>{{ t('modal_shortcuts.editor.title') }}</strong></h3>
 
             <table class="table">
@@ -61,16 +61,16 @@
                 <td><span class="key cmd-key"></span> + <span class="key">D</span></td>
               </tr>
             </table>
-          </div><!-- /.col-md-6 -->
+          </div><!-- /.col-lg-6 -->
 
         </div><!-- /.row -->
 
         <div class="row">
-          <div class="col-md-6">
+          <div class="col-lg-6">
             <h3><strong></strong></h3>
-          </div><!-- /.col-md-6 -->
+          </div><!-- /.col-lg-6 -->
 
-          <div class="col-md-6">
+          <div class="col-lg-6">
             <h3><strong>{{ t('modal_shortcuts.commentform.title') }}</strong></h3>
 
             <table class="table">
@@ -83,7 +83,7 @@
                 <td><span class="key cmd-key"></span> + <span class="key">D</span></td>
               </tr>
             </table>
-          </div><!-- /.col-md-6 -->
+          </div><!-- /.col-lg-6 -->
 
         </div><!-- /.row -->
 

+ 1 - 1
src/server/views/tags.html

@@ -5,7 +5,7 @@
 {% block html_base_css %}tags-page{% endblock %}
 
 {% block layout_main %}
-<header class="sticky-top py-0 grw-header">
+<header class="py-0 position-sticky grw-header">
   <h1 class="title">{{ t('Tags') }}</h1>
 </header>
 

+ 1 - 1
src/server/views/widget/create_portal.html

@@ -1,4 +1,4 @@
 <div class="portal-form-button">
   <a class="btn btn-primary" id="create-portal-button" href="#edit" data-toggle="tab" {% if not user %}disabled{% endif %}>Create Portal</a>
-  <p class="help-block"><a href="#" data-target="#help-portal" data-toggle="modal"><i class="icon-question"></i> What is Portal?</a></p>
+  <p class="form-text text-muted"><a href="#" data-target="#help-portal" data-toggle="modal"><i class="icon-question"></i> What is Portal?</a></p>
 </div>

+ 2 - 2
src/server/views/widget/page_tabs.html

@@ -72,7 +72,7 @@
     {% if page.isPortal() %}
     <li class="nav-item dropdown">
       <a
-        {% if user %} role="button" class="nav-link dropdown-toggle dropdown-toggle-no-caret" data-toggle="dropdown" {% endif %}
+        {% if user %} role="button" class="nav-link dropdown-toggle dropdown-toggle-no-caret" href="#" data-toggle="dropdown" {% endif %}
         {% if not user %}
           class="nav-link dropdown-toggle dropdown-toggle-disabled dropdown-toggle-no-caret"
           data-toggle="tooltip" data-placement="top" data-container="body" title="{{ t('Not available for guest') }}"
@@ -94,7 +94,7 @@
     {% else %}
     <li class="nav-item dropdown">
       <a
-        {% if user %} role="button" class="nav-link dropdown-toggle dropdown-toggle-no-caret" data-toggle="dropdown" {% endif %}
+        {% if user %} role="button" class="nav-link dropdown-toggle dropdown-toggle-no-caret" href="#" data-toggle="dropdown" {% endif %}
         {% if not user %}
           class="nav-link dropdown-toggle dropdown-toggle-disabled dropdown-toggle-no-caret"
           data-toggle="tooltip" data-placement="top" data-container="body" title="{{ t('Not available for guest') }}"