Przeglądaj źródła

Merge branch 'support/reactify-login-page-stock' into support/reactify-login-page-accessable-ex-auth-url

# Conflicts:
#	src/client/js/components/LoginForm.jsx
#	src/client/js/nologin.jsx
yusuketk 6 lat temu
rodzic
commit
cad7835581

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

@@ -159,8 +159,8 @@ class UserManagement extends React.Component {
         <h2>{t('User_Management')}</h2>
         <div className="border-top border-bottom">
 
-          <div className="d-flex justify-content-start align-items-center my-2">
-            <div className="d-flex align-items-baseline">
+          <div className="row d-flex justify-content-start align-items-center my-2">
+            <div className="col-md-4 d-flex align-items-center my-2">
               <i className="icon-magnifier mr-1"></i>
               <span className="search-typeahead">
                 <input
@@ -172,7 +172,7 @@ class UserManagement extends React.Component {
               </span>
             </div>
 
-            <div className="mx-5">
+            <div className="col-md-6 my-2">
               <div className="form-inline">
                 {this.renderCheckbox('all', 'All', 'primary')}
                 {this.renderCheckbox('registered', 'Approval Pending', 'info')}
@@ -188,7 +188,7 @@ class UserManagement extends React.Component {
               </div>
             </div>
 
-            <div>
+            <div className="col-md-2 my-2">
               <button
                 type="button"
                 className="btn btn-outline-secondary btn-sm"

+ 47 - 42
src/client/js/components/LoginForm.jsx

@@ -1,47 +1,35 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import loggerFactory from '@alias/logger';
+
 import { withTranslation } from 'react-i18next';
 
-import LoginContainer from '../services/LoginContainer';
-import AppContainer from '../services/AppContainer';
 import { createSubscribedElement } from './UnstatedUtils';
-
-const logger = loggerFactory('growi:loginForm');
+import AppContainer from '../services/AppContainer';
 
 class LoginForm extends React.Component {
 
   constructor(props) {
     super(props);
 
+    this.state = {
+      isRegistering: false,
+    };
+
     this.switchForm = this.switchForm.bind(this);
     this.handleLoginWithExternalAuth = this.handleLoginWithExternalAuth.bind(this);
     this.renderLocalOrLdapLoginForm = this.renderLocalOrLdapLoginForm.bind(this);
     this.renderExternalAuthLoginForm = this.renderExternalAuthLoginForm.bind(this);
     this.renderExternalAuthInput = this.renderExternalAuthInput.bind(this);
     this.renderRegisterForm = this.renderRegisterForm.bind(this);
-  }
 
-  async componentDidMount() {
-    const { loginContainer } = this.props;
-
-    try {
-      await loginContainer.retrieveData();
-    }
-    catch (err) {
-      loginContainer.setState({ retrieveError: err.message });
-      logger.error(err);
+    const { hash } = window.location;
+    if (hash === '#register') {
+      this.state.isRegistering = true;
     }
   }
 
-  // for flip [TODO][GW-1865] use state or react component for flip
-  switchForm(e) {
-    if (e.target.id === 'register') {
-      $('#login-dialog').addClass('to-flip');
-    }
-    else {
-      $('#login-dialog').removeClass('to-flip');
-    }
+  switchForm() {
+    this.setState({ isRegistering: !this.state.isRegistering });
   }
 
   handleLoginWithExternalAuth(e) {
@@ -51,7 +39,7 @@ class LoginForm extends React.Component {
   }
 
   renderLocalOrLdapLoginForm() {
-    const { t, appContainer } = this.props;
+    const { t, appContainer, isLdapStrategySetup } = this.props;
 
     return (
       <form role="form" action="/login" method="post">
@@ -62,7 +50,7 @@ class LoginForm extends React.Component {
             </span>
           </div>
           <input type="text" className="form-control" placeholder="Username or E-mail" name="loginForm[username]" />
-          {this.isLdapStrategySetup && (
+          {isLdapStrategySetup && (
             <div className="input-group-append">
               <small className="input-group-text text-success">
                 <i className="icon-fw icon-check"></i> LDAP
@@ -111,8 +99,8 @@ class LoginForm extends React.Component {
   }
 
   renderExternalAuthLoginForm() {
-    const { loginContainer } = this.props;
-    const isExternalAuthCollapsible = loginContainer.state.isLocalStrategySetup || loginContainer.state.isLdapStrategySetup;
+    const { isLocalStrategySetup, isLdapStrategySetup, objOfIsExternalAuthEnableds } = this.props;
+    const isExternalAuthCollapsible = isLocalStrategySetup || isLdapStrategySetup;
     const collapsibleClass = isExternalAuthCollapsible ? 'collapse collapse-external-auth collapse-anchor' : '';
 
     return (
@@ -121,8 +109,8 @@ 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(loginContainer.state.objOfIsExternalAuthEnableds).map((auth) => {
-              if (!loginContainer.state.objOfIsExternalAuthEnableds[auth]) {
+            {Object.keys(objOfIsExternalAuthEnableds).map((auth) => {
+              if (!objOfIsExternalAuthEnableds[auth]) {
                 return;
               }
               return this.renderExternalAuthInput(auth);
@@ -148,10 +136,16 @@ class LoginForm extends React.Component {
   }
 
   renderRegisterForm() {
-    const { t, appContainer, loginContainer } = this.props;
+    const {
+      t,
+      appContainer,
+      registrationMode,
+      registrationWhiteList,
+    } = this.props;
+
     return (
       <div className="back">
-        {this.registrationMode === 'Restricted' && (
+        {registrationMode === 'Restricted' && (
           <p className="alert alert-warning">
             {t('page_register.notice.restricted')}
             <br />
@@ -189,11 +183,11 @@ class LoginForm extends React.Component {
             <input type="email" className="form-control" placeholder={t('Email')} name="registerForm[email]" defaultValue={this.props.email} required />
           </div>
 
-          {loginContainer.state.registrationWhiteList.length > 0 && (
+          {registrationWhiteList.length > 0 && (
             <>
               <p className="form-text">{t('page_register.form_help.email')}</p>
               <ul>
-                {loginContainer.state.registrationWhiteList.map((elem) => {
+                {registrationWhiteList.map((elem) => {
                   return (
                     <li key={elem}>
                       <code>{elem}</code>
@@ -229,7 +223,7 @@ class LoginForm extends React.Component {
 
         <div className="row">
           <div className="text-right col-12 py-1">
-            <a href="#login" id="login" className="link-switch" onClick={this.handleClick}>
+            <a href="#login" id="login" className="link-switch" onClick={this.switchForm}>
               <i className="icon-fw icon-login"></i>
               {t('Sign in is here')}
             </a>
@@ -240,11 +234,17 @@ class LoginForm extends React.Component {
   }
 
   render() {
-    const { t, loginContainer, isRegistering } = this.props;
+    const {
+      t,
+      isLocalStrategySetup,
+      isLdapStrategySetup,
+      isRegistrationEnabled,
+      objOfIsExternalAuthEnableds,
+    } = this.props;
 
-    const isLocalOrLdapStrategiesEnabled = loginContainer.state.isLocalStrategySetup || loginContainer.state.isLdapStrategySetup;
-    const registerFormClass = isRegistering ? 'to-flip' : '';
-    const isSomeExternalAuthEnabled = Object.values(loginContainer.state.objOfIsExternalAuthEnableds).some(elem => elem);
+    const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup;
+    const registerFormClass = this.state.isRegistering ? 'to-flip' : '';
+    const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem);
 
     return (
       <div className={`login-dialog mx-auto flipper ${registerFormClass}`} id="login-dialog">
@@ -253,7 +253,7 @@ class LoginForm extends React.Component {
             <div className="front">
               {isLocalOrLdapStrategiesEnabled && this.renderLocalOrLdapLoginForm()}
               {isSomeExternalAuthEnabled && this.renderExternalAuthLoginForm()}
-              {loginContainer.state.isRegistrationEnabled && (
+              {isRegistrationEnabled && (
                 <div className="row">
                   <div className="col-12 text-right py-2">
                     <a href="#register" id="register" className="link-switch" onClick={this.switchForm}>
@@ -263,7 +263,7 @@ class LoginForm extends React.Component {
                 </div>
               )}
             </div>
-            {loginContainer.state.isRegistrationEnabled && this.renderRegisterForm()}
+            {isRegistrationEnabled && this.renderRegisterForm()}
             <a href="https://growi.org" className="link-growi-org pl-3">
               <span className="growi">GROWI</span>.<span className="org">ORG</span>
             </a>
@@ -279,18 +279,23 @@ class LoginForm extends React.Component {
  * Wrapper component for using unstated
  */
 const LoginFormWrapper = (props) => {
-  return createSubscribedElement(LoginForm, props, [AppContainer, LoginContainer]);
+  return createSubscribedElement(LoginForm, props, [AppContainer]);
 };
 
 LoginForm.propTypes = {
   // i18next
   t: PropTypes.func.isRequired,
-  loginContainer: PropTypes.instanceOf(LoginContainer).isRequired,
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   isRegistering: PropTypes.bool,
   username: PropTypes.string,
   name: PropTypes.string,
   email: PropTypes.string,
+  isRegistrationEnabled: PropTypes.bool,
+  registrationMode: PropTypes.string,
+  registrationWhiteList: PropTypes.array,
+  isLocalStrategySetup: PropTypes.bool,
+  isLdapStrategySetup: PropTypes.bool,
+  objOfIsExternalAuthEnableds: PropTypes.object,
 };
 
 export default withTranslation()(LoginFormWrapper);

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

@@ -567,7 +567,7 @@ export default class CodeMirrorEditor extends AbstractEditor {
           ? (
             <div className="text-right">
               {cheatsheetModalButton}
-              <div className="mb-2">
+              <div className="mb-2 d-none d-md-block">
                 <SimpleCheatsheet />
               </div>
             </div>

+ 9 - 2
src/client/js/components/StaffCredit/StaffCredit.jsx

@@ -49,6 +49,13 @@ export default class StaffCredit extends React.Component {
         const scrollTargetHeight = target.children().innerHeight();
         const duration = scrollTargetHeight / scrollSpeed;
         target.animate({ scrollTop: scrollTargetHeight }, duration, 'linear');
+
+        target.slimScroll({
+          height: target.innerHeight(),
+          // Able to scroll after automatic schooling is complete so set "bottom" to allow scrolling from the bottom.
+          start: 'bottom',
+          color: '#FFFFFF',
+        });
       }
       else {
         // add UserCommand
@@ -97,7 +104,7 @@ export default class StaffCredit extends React.Component {
         });
         return (
           <React.Fragment key={`${contributor.sectionName}-fragment`}>
-            <div className={`row staff-credit-my-10 ${contributor.additionalClass}`} key={`${contributor.sectionName}-row`}>
+            <div className={`row ${contributor.additionalClass}`} key={`${contributor.sectionName}-row`}>
               <h2 className="col-md-12 dev-team mt-5 staff-credit-mb-10" key={contributor.sectionName}>{contributor.sectionName}</h2>
               {memberGroups}
             </div>
@@ -106,7 +113,7 @@ export default class StaffCredit extends React.Component {
         );
       });
       return (
-        <div className="text-center" onClick={this.deleteCredit}>
+        <div className="text-center staff-credit-pb-10" onClick={this.deleteCredit}>
           <h1 className="staff-credit-mb-10">GROWI Contributors</h1>
           <div className="clearfix"></div>
           {credit}

+ 29 - 9
src/client/js/nologin.jsx

@@ -5,13 +5,10 @@ import { I18nextProvider } from 'react-i18next';
 
 import i18nFactory from './util/i18n';
 
-import InstallerForm from './components/InstallerForm';
-import LoginForm from './components/LoginForm';
-
 import AppContainer from './services/AppContainer';
-import LoginContainer from './services/LoginContainer';
 
-const appContainer = new AppContainer();
+import InstallerForm from './components/InstallerForm';
+import LoginForm from './components/LoginForm';
 
 const i18n = i18nFactory();
 
@@ -33,17 +30,40 @@ if (installerFormElem) {
 // render loginForm
 const loginFormElem = document.getElementById('login-form');
 if (loginFormElem) {
-  const loginContainer = new LoginContainer(appContainer);
+  const appContainer = new AppContainer();
 
-  const isRegistering = loginFormElem.dataset.isRegistering === 'true';
   const username = loginFormElem.dataset.username;
   const name = loginFormElem.dataset.name;
   const email = loginFormElem.dataset.email;
+  const isRegistrationEnabled = loginFormElem.dataset.isRegistrationEnabled === 'true';
+  const registrationMode = loginFormElem.dataset.registrationMode;
+  const registrationWhiteList = loginFormElem.dataset.registrationWhiteList.split(',');
+  const isLocalStrategySetup = loginFormElem.dataset.isLocalStrategySetup === 'true';
+  const isLdapStrategySetup = loginFormElem.dataset.isLdapStrategySetup === 'true';
+  const objOfIsExternalAuthEnableds = {
+    google: loginFormElem.dataset.isGoogleAuthEnabled === 'true',
+    github: loginFormElem.dataset.isGithubAuthEnabled === 'true',
+    facebook: loginFormElem.dataset.isFacebookAuthEnabled === 'true',
+    twitter: loginFormElem.dataset.isTwitterAuthEnabled === 'true',
+    saml: loginFormElem.dataset.isSamlAuthEnabled === 'true',
+    oidc: loginFormElem.dataset.isOidcAuthEnabled === 'true',
+    basic: loginFormElem.dataset.isBasicAuthEnabled === 'true',
+  };
 
   ReactDOM.render(
     <I18nextProvider i18n={i18n}>
-      <Provider inject={[appContainer, loginContainer]}>
-        <LoginForm isRegistering={isRegistering} username={username} name={name} email={email} />
+      <Provider inject={[appContainer]}>
+        <LoginForm
+          username={username}
+          name={name}
+          email={email}
+          isRegistrationEnabled={isRegistrationEnabled}
+          registrationMode={registrationMode}
+          registrationWhiteList={registrationWhiteList}
+          isLocalStrategySetup={isLocalStrategySetup}
+          isLdapStrategySetup={isLdapStrategySetup}
+          objOfIsExternalAuthEnableds={objOfIsExternalAuthEnableds}
+        />
       </Provider>
     </I18nextProvider>,
     loginFormElem,

+ 0 - 63
src/client/js/services/LoginContainer.js

@@ -1,63 +0,0 @@
-import { Container } from 'unstated';
-
-import loggerFactory from '@alias/logger';
-
-import { toastError } from '../util/apiNotification';
-
-const logger = loggerFactory('growi:loginForm');
-
-/**
- * Service container for login form (LoginForm.jsx)
- * @extends {Container} unstated Container
- */
-export default class LoginForm extends Container {
-
-  constructor(appContainer) {
-    super();
-
-    this.appContainer = appContainer;
-
-    this.state = {
-      retrieveError: null,
-      isRegistrationEnabled: false,
-      registrationMode: 'Closed',
-      registrationWhiteList: [],
-      isLocalStrategySetup: false,
-      isLdapStrategySetup: false,
-      objOfIsExternalAuthEnableds: {},
-    };
-
-    this.retrieveData = this.retrieveData.bind(this);
-  }
-
-  /**
-   * Workaround for the mangling in production build to break constructor.name
-   */
-  static getClassName() {
-    return 'LoginContainer';
-  }
-
-  /**
-   * retrieve app sttings data
-   */
-  async retrieveData() {
-    try {
-      const response = await this.appContainer.apiv3.get('/login/');
-      const { data } = response.data;
-
-      this.setState({
-        isRegistrationEnabled: data.isRegistrationEnabled,
-        registrationMode: data.registrationMode,
-        registrationWhiteList: data.registrationWhiteList,
-        isLocalStrategySetup: data.isLocalStrategySetup,
-        isLdapStrategySetup: data.isLdapStrategySetup,
-        objOfIsExternalAuthEnableds: data.objOfIsExternalAuthEnableds,
-      });
-    }
-    catch (err) {
-      logger.error(err);
-      toastError(new Error('Failed to fetch data'));
-    }
-  }
-
-}

+ 18 - 10
src/client/styles/scss/_layout_kibela.scss

@@ -1,4 +1,11 @@
+$navbar-height-adjustment: 10px;
+
 body.kibela {
+  /* navbar for kibela */
+  #page-wrapper {
+    margin-top: 60px;
+  }
+
   /* Logo */
   .logo {
     .logo-mark {
@@ -40,13 +47,13 @@ body.kibela {
 
   .kibela-block {
     position: relative;
-    top: 20px;
+    top: 10px;
     right: 100px;
     bottom: 0px;
     left: 0px;
     z-index: absolute;
-    max-width: 840px;
-    height: 8em;
+    max-width: 1024px;
+    min-height: 8em;
     margin: auto;
     border-radius: 0.35em;
   }
@@ -59,6 +66,10 @@ body.kibela {
       display: none;
     }
 
+    &.grw-subnav-user-page {
+      min-height: 128px;
+    }
+
     @media screen and (max-width: 765px) {
       padding-top: 30px;
     }
@@ -119,10 +130,13 @@ body.kibela {
       + 1px //                    .page-editor-footer border-top
       + 60px; //                  .page-editor-footer min-height
 
-    @include expand-editor($header-plus-footer);
+    @include expand-editor($header-plus-footer, $navbar-height-adjustment);
 
     .kibela-block {
+      top: 0px;
       max-width: unset;
+      padding-top: 0px;
+      border: 0px;
     }
 
     .tab-content {
@@ -153,9 +167,3 @@ body.kibela {
     }
   }
 }
-
-.on-edit {
-  .kibela-block {
-    border: 0px;
-  }
-}

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

@@ -17,7 +17,8 @@
   }
 }
 
-@mixin expand-editor($editor-header-plus-footer) {
+@mixin expand-editor($editor-header-plus-footer, $navbar-height-adjustment: 0px) {
+  $grw-navbar-height: $grw-navbar-height + $navbar-height-adjustment;
   $header-plus-footer: $grw-navbar-height + $editor-header-plus-footer + 2px; // add .main padding-top
 
   $editor-margin: $header-plus-footer //

+ 4 - 0
src/client/styles/scss/_staff_credit.scss

@@ -60,4 +60,8 @@
     @extend .staff-credit-mt-10;
     @extend .staff-credit-mb-10;
   }
+
+  .staff-credit-pb-10 {
+    padding-bottom: 6rem;
+  }
 }

+ 5 - 1
src/client/styles/scss/_wiki.scss

@@ -11,7 +11,11 @@ div.body {
 
 .wiki {
   font-size: 15px;
-  line-height: 1.8em;
+
+  // override line-height except hljs and child of it.
+  :not(pre*) {
+    line-height: 1.8em;
+  }
 
   h1,
   h2,

+ 8 - 3
src/client/styles/scss/theme/_apply-colors-kibela.scss

@@ -20,7 +20,7 @@ body.kibela {
   }
 
   /* kibela block */
-  .kibela-block {
+  .kibela-border-top {
     border-top: solid 0.4em $thickborder;
   }
 
@@ -161,8 +161,13 @@ body.kibela {
 
   /* navbar */
   .grw-navbar {
-    .nav-item > .nav-link:hover {
-      color: $color-link-nabvar-hover;
+    .nav-item > .nav-link {
+      &:hover {
+        color: $color-link-nabvar-hover;
+      }
+      &:focus {
+        color: $color-link-nabvar;
+      }
     }
   }
 

+ 0 - 2
src/server/routes/apiv3/index.js

@@ -13,8 +13,6 @@ module.exports = (crowi) => {
 
   router.use('/healthcheck', require('./healthcheck')(crowi));
 
-  router.use('/login', require('./login')(crowi));
-
   // admin
   router.use('/admin-home', require('./admin-home')(crowi));
   router.use('/markdown-setting', require('./markdown-setting')(crowi));

+ 0 - 63
src/server/routes/apiv3/login.js

@@ -1,63 +0,0 @@
-const loggerFactory = require('@alias/logger');
-
-const logger = loggerFactory('growi:routes:apiv3:login'); // eslint-disable-line no-unused-vars
-
-const express = require('express');
-
-const router = express.Router();
-
-/**
- * @swagger
- *  tags:
- *    name: logins
- */
-
-module.exports = (crowi) => {
-  const accessTokenParser = require('../../middleware/access-token-parser')(crowi);
-  const { passportService, configManager } = crowi;
-  /**
-   * @swagger
-   *
-   *    /login:
-   *      get:
-   *        summary: /login
-   *        description: Get data for rendering login form
-   *        responses:
-   *          200:
-   *            description: Succeeded to get data for rendering login form.
-   *            content:
-   *              application/json:
-   *                schema:
-   *                  properties:
-   *                    data:
-   *                      type: object
-   *                      description: data for rendering login form
-   */
-  router.get('/', accessTokenParser, async(req, res) => {
-    const data = {};
-    try {
-      data.isRegistrationEnabled = passportService.isLocalStrategySetup
-        && await configManager.getConfig('crowi', 'security:registrationMode') !== 'Closed';
-      data.registrationMode = await configManager.getConfig('crowi', 'security:registrationMode');
-      data.registrationWhiteList = await configManager.getConfig('crowi', 'security:registrationWhiteList');
-      data.isLocalStrategySetup = passportService.isLocalStrategySetup;
-      data.isLdapStrategySetup = passportService.isLdapStrategySetup;
-      data.objOfIsExternalAuthEnableds = {
-        google: configManager.getConfig('crowi', 'security:passport-google:isEnabled'),
-        github: configManager.getConfig('crowi', 'security:passport-github:isEnabled'),
-        facebook: configManager.getConfig('crowi', 'security:passport-facebook:isEnabled'),
-        twitter: configManager.getConfig('crowi', 'security:passport-twitter:isEnabled'),
-        oidc: configManager.getConfig('crowi', 'security:passport-oidc:isEnabled'),
-        saml: configManager.getConfig('crowi', 'security:passport-saml:isEnabled'),
-        basic: configManager.getConfig('crowi', 'security:passport-basic:isEnabled'),
-      };
-      return res.apiv3({ data });
-    }
-    catch (err) {
-      logger.error('get-auth-setting-data-failed', err);
-      return res.apiv3Err(err, 500);
-    }
-  });
-
-  return router;
-};

+ 3 - 2
src/server/views/layout-kibela/base/layout.html

@@ -11,11 +11,12 @@
 
   <div class="row body m-0 p-0">
 
-    <div id="main" class="main col-12 kibela-block round-corner border-0 {% if page %}{{ css.grant(page) }}{% endif %}{% block main_css_class %}{% endblock %}">
+    <div id="main" class="main col-12 kibela-block round-corner {% if page %}{{ css.grant(page) }}{% endif %}{% block main_css_class %}{% endblock %}">
       <div class="row grw-subnav d-edit-none">
-        <div class="col-9 px-0 mx-0 kibela-block">
+        <div class="col px-0 mx-0 bg-white kibela-border-top round-corner">
           {% block content_header %} {% endblock %}
         </div>
+        <div class="col-xl-3 col-lg-4"></div>
       </div>
       <!-- /.grw-subnav -->
 

+ 2 - 2
src/server/views/layout-kibela/user_page.html

@@ -7,7 +7,7 @@
 
 {% block content_header %}
   {% if pageUser %}
-    <header id="grw-subnav-for-user-page" class="grw- subnav grw-subnav-user-page" data-page-user="{{ pageUser|json }}"></header>
+    <header id="grw-subnav-for-user-page" class="grw-subnav grw-subnav-user-page" data-page-user="{{ pageUser|json }}"></header>
   {% else %}
     {% parent %}
   {% endif %}
@@ -53,7 +53,7 @@
 
   {% if 'growi' === getConfig('crowi', 'customize:behavior') || 'crowi-plus' === getConfig('crowi', 'customize:behavior') %}
   <div class="row page-list mt-5 d-edit-none">
-    <div class="col-xs-12">
+    <div class="col-12">
       {% include '../widget/page_list_and_timeline_kibela.html' %}
     </div>
   </div>

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

@@ -110,7 +110,7 @@
       <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') }}
+          <span>{{ t('New') }}</span>
         </a>
       </li>
       <li class="nav-item d-none d-md-block">

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

@@ -104,15 +104,30 @@
           </div>
         </div>
       </div>
-        {% set isRegistrationEnabled = passportService.isLocalStrategySetup && getConfig('crowi', 'security:registrationMode') != 'Closed' %}
 
-      <!-- [TODO][GW-1913] An AppContainer gets csrf data -->
-      <div id="login-form"
+      {% set registrationMode = getConfig('crowi', 'security:registrationMode') %}
+      {% set isRegistrationEnabled = passportService.isLocalStrategySetup && registrationMode != 'Closed' %}
+
+      <!-- [TODO][GW-2112] An AppContainer gets csrf data -->
+      <div
+        id="login-form"
         data-is-registering="{{ req.query.register or req.body.registerForm or isRegistering }}"
         data-username ="{{ req.body.registerForm.username }}"
         data-name ="{{ req.body.registerForm.name }}"
         data-email ="{{ req.body.registerForm.email }}"
         data-csrf="{{ csrf() }}"
+        data-is-registration-enabled="{{ isRegistrationEnabled }}"
+        data-registration-mode = "{{ registrationMode }}"
+        data-registration-white-list = "{{ getConfig('crowi', 'security:registrationWhiteList') }}"
+        data-is-local-strategy-setup = "{{ passportService.isLocalStrategySetup }}"
+        data-is-ldap-strategy-setup = "{{ passportService.isLdapStrategySetup}}"
+        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-saml-auth-enabled = "{{ getConfig('crowi', 'security:passport-saml:isEnabled') }}"
+        data-is-oidc-auth-enabled = "{{ getConfig('crowi', 'security:passport-oidc:isEnabled') }}"
+        data-is-basic-auth-enabled = "{{ getConfig('crowi', 'security:passport-basic:isEnabled') }}"
       ></div>
     </div>
   </div>

+ 10 - 11
src/server/views/widget/page_tabs.html

@@ -47,25 +47,24 @@
     Right Tabs
   #}
 
+  {# to place right side #}
+  <div class="mr-auto"></div>
+
   <!-- presentation -->
   {% if not page.isPortal() %}
-    <li class="nav-item ml-md-auto">
+    <li class="nav-item">
       <a href="?presentation=1" class="nav-link toggle-presentation">
-        <i class="icon-film"></i><span class="hidden-xs"> {{ t('Presentation Mode') }}</span>
+        <i class="icon-film"></i><span class="d-none d-md-inline"> {{ t('Presentation Mode') }}</span>
       </a>
     </li>
   {% endif %}
 
   <!-- revision-history -->
-  {% if page.isPortal() %}
-    <li class="nav-item ml-md-auto">
-    {% else %}
-    <li class="nav-item">
-  {% endif %}
-        <a class="nav-link" href="#revision-history" role="tab" data-toggle="tab">
-          <i class="icon-layers"></i><span class="hidden-xs"> {{ t('History') }}</span>
-        </a>
-      </li>
+  <li class="nav-item">
+    <a class="nav-link" href="#revision-history" role="tab" data-toggle="tab">
+      <i class="icon-layers"></i><span class="d-none d-md-inline"> {{ t('History') }}</span>
+    </a>
+  </li>
 
   <!-- icon-options-vertical -->
   {% if !isTrashPage() %}

+ 1 - 1
src/server/views/widget/system-version.html

@@ -1,4 +1,4 @@
-<div class="system-version hidden-xs">
+<div class="system-version d-none d-md-block">
   <span>
     <a href="https://growi.org">GROWI</a> {{ growiVersion() }}
   </span>