Explorar el Código

Merge pull request #382 from weseek/master

release v3.0.11
Yuki Takei hace 8 años
padre
commit
001cc3aa65

+ 10 - 1
CHANGES.md

@@ -1,7 +1,16 @@
 CHANGES
 ========
 
-## 3.0.10-RC
+## 3.0.11-RC
+
+* Fix: login.html is broken in iOS
+* Support: Optimize development build
+* Support: Upgrade libs
+    * env-cmd
+    * googleapis
+    * sinon
+
+## 3.0.10
 
 * Improvement: Add 'nature' theme
 * Fix: Page list and Timeline layout for layout-growi

+ 6 - 28
config/webpack.common.js

@@ -10,15 +10,13 @@ const helpers = require('./helpers');
  */
 const AssetsPlugin = require('assets-webpack-plugin');
 const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
 
 /*
  * Webpack configuration
  *
  * See: http://webpack.github.io/docs/configuration.html#cli
  */
-module.exports = function (options) {
-  isProd = options.env === 'production';
+module.exports = function(options) {
   return {
     entry: {
       'app':                  './resource/js/app',
@@ -36,9 +34,9 @@ module.exports = function (options) {
     externals: {
       // require("jquery") is external and available
       //  on the global var jQuery
-      "jquery": "jQuery",
-      "emojione": "emojione",
-      "hljs": "hljs",
+      'jquery': 'jQuery',
+      'emojione': 'emojione',
+      'hljs': 'hljs',
     },
     resolve: {
       extensions: ['.js', '.json'],
@@ -56,26 +54,6 @@ module.exports = function (options) {
             }
           }]
         },
-        {
-          test: /\.scss$/,
-          use: ExtractTextPlugin.extract({
-            fallback: 'style-loader',
-            use: [
-              { loader: 'css-loader', options: {
-                sourceMap: !isProd,
-                minimize: isProd
-              } },
-              { loader: 'postcss-loader', options: {
-                sourceMap: !isProd,
-                plugins: (loader) => [
-                  require('autoprefixer')()
-                ]
-              } },
-              { loader: 'sass-loader', options: { sourceMap: !isProd } }
-            ]
-          }),
-          include: [helpers.root('resource/styles/scss')]
-        },
         {
           test: /\.css$/,
           use: ['style-loader', 'css-loader'],
@@ -127,8 +105,8 @@ module.exports = function (options) {
       new webpack.IgnorePlugin(/^\.\/lib\/deflate\.js/, /markdown-it-plantuml/),
 
       new webpack.ProvidePlugin({ // refs externals
-        jQuery: "jquery",
-        $: "jquery",
+        jQuery: 'jquery',
+        $: 'jquery',
       }),
 
     ]

+ 33 - 10
config/webpack.dev.js

@@ -12,8 +12,6 @@ const commonConfig = require('./webpack.common.js');
 /*
  * Webpack Plugins
  */
-const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
 const DllBundlesPlugin = require('webpack-dll-bundles-plugin').DllBundlesPlugin;
 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
 
@@ -22,17 +20,15 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
  */
 const ANALYZE = process.env.ANALYZE;
 const ENV = process.env.ENV = process.env.NODE_ENV = 'development';
-const HOST = process.env.HOST || '0.0.0.0';
-const PORT = process.env.PORT || 3000;
 
 /*
  * Webpack configuration
  *
  * See: http://webpack.github.io/docs/configuration.html#cli
  */
-module.exports = function (options) {
+module.exports = function(options) {
   return webpackMerge(commonConfig({ env: ENV }), {
-    devtool: 'cheap-module-source-map',
+    devtool: 'cheap-module-eval-source-map',
     entry: {
       dev: './resource/js/dev',
     },
@@ -40,7 +36,6 @@ module.exports = function (options) {
       path: helpers.root('public/js'),
       publicPath: '/js/',
       filename: '[name].bundle.js',
-      sourceMapFilename: '[file].map',
     },
     resolve: {
       extensions: ['.js', '.json'],
@@ -48,17 +43,45 @@ module.exports = function (options) {
     },
     module: {
       rules: [
+        {
+          test: /\.css$/,
+          use: [
+            'style-loader',
+            { loader: 'css-loader', options: { sourceMap: true } },
+          ],
+          include: [helpers.root('resource/styles/scss')]
+        },
+        {
+          test: /\.scss$/,
+          use: [
+            'style-loader',
+            { loader: 'css-loader', options: { sourceMap: true } },
+            { loader: 'sass-loader', options: { sourceMap: true } },
+          ],
+          include: [helpers.root('resource/styles/scss')]
+        },
       ],
     },
     plugins: [
 
-      new ExtractTextPlugin('[name].bundle.css'),
-
       new DllBundlesPlugin({
         bundles: {
           vendor: [
+            'axios',
+            'codemirror',
+            'date-fns',
+            'diff',
+            'diff2html',
+            'jquery-ui',
+            'markdown-it',
+            'metismenu',
             'react',
-            'react-dom'
+            'react-dom',
+            'react-bootstrap',
+            'react-bootstrap-typeahead',
+            'react-dropzone',
+            'socket.io-client',
+            'toastr',
           ],
         },
         dllDir: helpers.root('public/dll'),

+ 21 - 4
config/webpack.prod.js

@@ -10,7 +10,6 @@ const commonConfig = require('./webpack.common.js'); // the settings that are co
 /**
  * Webpack Plugins
  */
-const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
 const ExtractTextPlugin = require('extract-text-webpack-plugin');
 const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
 const OptimizeJsPlugin = require('optimize-js-plugin');
@@ -21,10 +20,8 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
  */
 const ANALYZE = process.env.ANALYZE;
 const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
-const HOST = process.env.HOST || 'localhost';
-const PORT = process.env.PORT || 3000;
 
-module.exports = function (env) {
+module.exports = function(env) {
   return webpackMerge(commonConfig({ env: ENV }), {
     devtool: undefined,
     output: {
@@ -36,6 +33,26 @@ module.exports = function (env) {
     },
     module: {
       rules: [
+        {
+          test: /\.scss$/,
+          use: ExtractTextPlugin.extract({
+            fallback: 'style-loader',
+            use: [
+              { loader: 'css-loader', options: {
+                sourceMap: false,
+                minimize: true
+              } },
+              { loader: 'postcss-loader', options: {
+                sourceMap: false,
+                plugins: (loader) => [
+                  require('autoprefixer')()
+                ]
+              } },
+              { loader: 'sass-loader', options: { sourceMap: false } }
+            ]
+          }),
+          include: [helpers.root('resource/styles/scss')]
+        }
       ]
     },
     plugins: [

+ 4 - 4
lib/crowi/dev.js

@@ -1,11 +1,9 @@
 const debug = require('debug')('crowi:crowi:dev');
 const fs = require('fs');
 const path = require('path');
-const webpack = require('webpack');
-const helpers = require('./helpers');
 
 const swig = require('swig-templates');
-const onHeaders = require('on-headers')
+const onHeaders = require('on-headers');
 
 
 class CrowiDev {
@@ -29,7 +27,9 @@ class CrowiDev {
 
   initPromiseRejectionWarningHandler() {
     // https://qiita.com/syuilo/items/0800d7e44e93203c7285
+    /* eslint-disable no-console */
     process.on('unhandledRejection', console.dir);
+    /* eslint-enable */
   }
 
   initSwig() {
@@ -103,4 +103,4 @@ class CrowiDev {
   }
 }
 
-module.exports = CrowiDev
+module.exports = CrowiDev;

+ 1 - 2
lib/form/admin/securityGeneral.js

@@ -11,6 +11,5 @@ module.exports = form(
   field('settingForm[security:basicSecret]'),
   field('settingForm[security:restrictGuestMode]').required(),
   field('settingForm[security:registrationMode]').required(),
-  field('settingForm[security:registrationWhiteList]').custom(normalizeCRLF).custom(stringToArray),
+  field('settingForm[security:registrationWhiteList]').custom(normalizeCRLF).custom(stringToArray)
 );
-

+ 1 - 2
lib/form/admin/slackSetting.js

@@ -4,6 +4,5 @@ var form = require('express-form')
   , field = form.field;
 
 module.exports = form(
-  field('slackSetting[slack:token]', 'token'),
+  field('slackSetting[slack:token]', 'token')
 );
-

+ 41 - 17
lib/locales/en-US/translation.json

@@ -262,23 +262,47 @@
 
   },
   "security_setting": {
-    "Basic authentication": "Basic authentication",
-    "Security settings": "Security settings",
-    "Guest users access": "Guest users access",
-    "Register limitation": "Register limitation",
-    "The whitelist of registration permission E-mail address": "The whitelist of registration permission E-mail address",
-    "Selecting authentication mechanism": "Selecting authentication mechanism",
-    "common_authentication": "If you set the basic authentication, common authentication is applied on the whole page." ,
-    "without_encryption": "Please be noted that your ID and Password will be sent wihtout encryption.",
-    "users_without_account": "Users without account is not accessible",
-    "restrict_emails": "You can restrict registerable e-mail address.",
-    "for_instance":" For instance, if you use growi within a company, you can write ",
-    "only_those":" Only those whose e-mail address including the company address can register.",
-    "insert_single":"Please insert single e-mail address per line.",
-    "Authentication mechanism settings":"Authentication mechanism settings",
-    "Treat username matching as identical": "Automatically bind external accounts newly logged in to local accounts when <code>username</code> match",
-    "Treat username matching as identical_warn": "WARNING: Be aware of security because the system treats the same user as a match of <code>username</code>."
-  },
+		"Basic authentication": "Basic authentication",
+		"Security settings": "Security settings",
+		"Guest users access": "Guest users access",
+		"Register limitation": "Register limitation",
+		"The whitelist of registration permission E-mail address": "The whitelist of registration permission E-mail address",
+		"Selecting authentication mechanism": "Selecting authentication mechanism",
+		"common_authentication": "If you set the basic authentication, common authentication is applied on the whole page.",
+		"without_encryption": "Please be noted that your ID and Password will be sent wihtout encryption.",
+		"users_without_account": "Users without account is not accessible",
+		"restrict_emails": "You can restrict registerable e-mail address.",
+		"for_instance": " For instance, if you use growi within a company, you can write ",
+		"only_those": " Only those whose e-mail address including the company address can register.",
+		"insert_single": "Please insert single e-mail address per line.",
+		"Authentication mechanism settings": "Authentication mechanism settings",
+    "note": "Note",
+    "require_server_restart_change_auth": "Restarting the server is required if you switch the auth mechanism.",
+    "passport": "Passport",
+    "auth_mechanism": "authentication mechanism",
+    "recommended": "Recommended",
+    "username_email_password": "Username, Email and Password authentication",
+    "ldap_auth": "LDAP authentication",
+    "google_auth2": "Google OAuth2 authentication",
+    "facebook_auth2": "Facebook OAuth2 authentication",
+    "twitter_auth2": "Twitter OAuth authentication",
+    "github_auth2": "Github OAuth2 authentication",
+    "crowi_auth": "Crowi classic authentication mechanism",
+		"require_server_restart": "Restarting the server is required.",
+		"server_on_passport_auth": "The server is running with Passport authentication mechanism.",
+		"server_on_crowi_auth": "The server is running with Official Crowi authentication mechanism.",
+		"google_setting": "Google Setting",
+    "api_manager": "You can use your Google account to sign up and login after creating OAuth2 ClientId at <a href=\"https://console.cloud.google.com/apis/credentials\">API Manager on Google Cloud Platform</a>",
+		"access_api_manager": "Access <a href=\"https://console.cloud.google.com/apis/credentials\">API Manager</a>",
+		"create_project": "Create Project if no projects have been created.",
+		"create_auth_to_oauth": "\"Create credentials\" -> \"OAuth clientID\"",
+		"select_webapp": "Select \"Web Application\"",
+    "change_redirect_url": "Enter <code>https://${crowi.host}/google/callback</code> <br>(where < code > $ {crowi.host} < /code> is your host name) for \"Authorized redirect URIs\".",
+		"updated": "Updated",
+		"error": "Error",
+		"Treat username matching as identical": "Automatically bind external accounts newly logged in to local accounts when <code>username</code> match",
+		"Treat username matching as identical_warn": "WARNING: Be aware of security because the system treats the same user as a match of <code>username</code>."
+	},
 
   "markdown_setting": {
     "markdown_rendering": "You can change Markdown rendering settings.",

+ 24 - 1
lib/locales/ja/translation.json

@@ -296,6 +296,30 @@
     "only_those":"その会社のメールアドレスを持っている人のみ登録可能になります。",
     "insert_single":"1行に1メールアドレス入力してください。",
     "Authentication mechanism settings":"認証機構設定",
+    "note": "メモ",
+    "require_server_restart_change_auth": "認証機構の変更後はサーバーを再起動してください。",
+    "passport": "パスポート",
+    "auth_mechanism": "認証機構",
+    "recommended": "推奨",
+    "username_email_password": "ユーサー名、Eメール、パスワードでの認証",
+    "ldap_auth": "LDAP認証",
+    "google_auth2": "Google OAuth2認証",
+    "facebook_auth2": "Facebook OAuth2認証",
+    "twitter_auth2": "Twitter OAuth認証",
+    "github_auth2": "Github OAuth2認証",
+    "crowi_auth": "Crowiクラシック認証機構",
+    "require_server_restart": "サーバーを再起動してください。",
+    "server_on_passport_auth": "パスポート認証でサーバーが稼働しています。",
+    "server_on_crowi_auth": "公式crowi認証でサーバーが稼働しています。",
+    "google_setting": "Google 設定",
+    "api_manager": "Google Cloud Platform の <a href=\"https://console.cloud.google.com/apis/credentials\">API Manager</a>から OAuth2 Client ID を作成すると、Google アカウントにコネクトして登録やログインが可能になります。",
+    "access_api_manager": "<a href=\"https://console.cloud.google.com/apis/credentials\">API Manager</a> へアクセス",
+    "create_project": "プロジェクトを作成していない場合は作成してください",
+    "create_auth_to_oauth": "「認証情報を作成」-> OAuthクライアントID",
+    "select_webapp": "「ウェブアプリケーション」を選択",
+    "change_redirect_url": "承認済みのリダイレクトURLに、 <code>https://${crowi.host}/google/callback</code> を入力<br>(<code>${crowi.host}</code>は環境に合わせて変更してください)",
+    "updated": "更新しました",
+    "error": "エラーが発生しました",
     "Treat username matching as identical": "新規ログイン時、<code>username</code> が一致したローカルアカウントが存在した場合は自動的に紐付ける",
     "Treat username matching as identical_warn": "WARNING: <code>username</code> の一致を以て同一ユーザーであるとみなすので、セキュリティに注意してください"
   },
@@ -351,4 +375,3 @@
   }
 
 }
-

+ 15 - 4
lib/views/admin/customize.html

@@ -3,8 +3,13 @@
 {% block html_title %}{{ customTitle(t('Customize')) }} {% endblock %}
 
 {% block style_css_block %}
-  <link rel="stylesheet" href="{{ webpack_asset('style').css }}">
-  <link rel="stylesheet" id="jssDefault" {# append id for theme selector #} href="{{ webpack_asset('style-theme-' + theme()).css }}">
+  {% if env === 'development' %}
+    <script src="{{ webpack_asset('style').js }}"></script>
+    <script src="{{ webpack_asset('style-theme-' + theme()).js }}"></script>
+  {% else %}
+    <link rel="stylesheet" href="{{ webpack_asset('style').css }}">
+    <link rel="stylesheet" id="jssDefault" {# append id for theme selector #} href="{{ webpack_asset('style-theme-' + theme()).css }}">
+  {% endif %}
 {% endblock %}
 
 {% block html_additional_headers %}
@@ -52,6 +57,12 @@
         <fieldset>
           <legend>{{ t('customize_page.Theme') }}</legend>
 
+          {% if env === 'development' %}
+            <div class="alert alert-warning">
+              <strong>DEBUG MESSAGE:</strong> development build では、リアルタイムプレビューが無効になります
+            </div>
+          {% endif %}
+
           <div id="themeOptions">
             {# Light Themes #}
             <div class="d-flex">
@@ -501,12 +512,12 @@ window.addEventListener('load', (event) => {
       $(this).submit(function()
       {
         function showMessage(formId, msg, status) {
-          $('#' + formId + ' .alert').remove();
+          $('#' + formId + ' #alert-results').remove();
 
           if (!status) {
             status = 'success';
           }
-          var $message = $('<p class="alert"></p>');
+          var $message = $('<p id="alert-results" class="alert"></p>');
           $message.addClass('alert-' + status);
           $message.html(msg.replace('\n', '<br>'));
           $message.insertAfter('#' + formId + ' legend');

+ 1 - 1
lib/views/admin/security.html

@@ -103,7 +103,7 @@
       <form action="/_api/admin/security/mechanism" method="post" class="form-horizontal m-t-30" id="mechanismSetting" role="form">
         <fieldset>
           <legend class="alert-anchor">{{ t('Selecting authentication mechanism') }}</legend>
-          <p class="alert alert-info"><b>NOTE: </b>Restarting the server is needed if you switch the auth mechanism.</p>
+          <p class="alert alert-info"><b>{{ t("security_setting.note") }}: </b>{{ t("security_setting.require_server_restart_change_auth") }}</p>
           <div class="form-group">
             <div class="col-xs-6">
               <h4>

+ 2 - 2
lib/views/layout-crowi/page_list.html

@@ -59,11 +59,11 @@
   {% endif %}
   {% endif %}
 
-  <div class="{% if isPortal %}m-b-30{% endif %}">
+  <div>
     {% include '../widget/page_content.html' %}
   </div>
 
-  <div class="row page-list">
+  <div class="row page-list {% if isPortal %}m-t-30{% endif %}">
     <div class="col-md-12">
       {% include '../widget/page_list_and_timeline.html' %}
     </div>

+ 2 - 2
lib/views/layout-growi/page.html

@@ -11,7 +11,7 @@
 
 
 {% block content_main %}
-  <div class="row m-b-30">
+  <div class="row">
 
     <div class="col-lg-10 col-md-9">
 
@@ -34,7 +34,7 @@
   </div>
 
   {% if 'growi' === behaviorType() || 'crowi-plus' === behaviorType() %}
-  <div class="row page-list">
+  <div class="row page-list m-t-30">
     <div class="col-md-10">
       {% include '../widget/page_list_and_timeline.html' %}
     </div>

+ 2 - 2
lib/views/layout-growi/page_list.html

@@ -11,7 +11,7 @@
 
 
 {% block content_main %}
-  <div class="row m-b-30">
+  <div class="row">
 
     <div class="col-lg-10 col-md-9">
 
@@ -33,7 +33,7 @@
 
   </div>
 
-  <div class="row page-list">
+  <div class="row page-list {% if isPortal %}m-t-30{% endif %}">
     <div class="col-md-10">
       {% include '../widget/page_list_and_timeline.html' %}
     </div>

+ 2 - 2
lib/views/layout-growi/user_page.html

@@ -15,7 +15,7 @@
 
 
 {% block content_main %}
-  <div class="row m-b-30">
+  <div class="row">
 
     <div class="col-lg-10 col-md-9">
 
@@ -52,7 +52,7 @@
   </div>
 
   {% if 'growi' === behaviorType() || 'crowi-plus' === behaviorType() %}
-  <div class="row page-list">
+  <div class="row page-list m-t-30">
     <div class="col-md-10">
       {% include '../widget/page_list_and_timeline.html' %}
     </div>

+ 7 - 2
lib/views/layout/layout.html

@@ -83,8 +83,13 @@ gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js
 
   <!-- styles -->
   {% block style_css_block %}
-  <link rel="stylesheet" href="{{ webpack_asset('style').css }}">
-  <link rel="stylesheet" href="{{ webpack_asset('style-theme-' + theme()).css }}">
+    {% if env === 'development' %}
+      <script src="{{ webpack_asset('style').js }}"></script>
+      <script src="{{ webpack_asset('style-theme-' + theme()).js }}"></script>
+    {% else %}
+      <link rel="stylesheet" href="{{ webpack_asset('style').css }}">
+      <link rel="stylesheet" href="{{ webpack_asset('style-theme-' + theme()).css }}">
+    {% endif %}
   {% endblock %}
 
   <!-- Google Fonts -->

+ 4 - 4
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "3.0.10-RC",
+  "version": "3.0.11-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -78,7 +78,7 @@
     "eazy-logger": "^3.0.2",
     "elasticsearch": "^14.0.0",
     "entities": "^1.1.1",
-    "env-cmd": "^7.0.0",
+    "env-cmd": "^8.0.1",
     "escape-string-regexp": "^1.0.5",
     "express": "^4.16.1",
     "express-form": "~0.12.0",
@@ -87,7 +87,7 @@
     "express-webpack-assets": "^0.1.0",
     "extract-text-webpack-plugin": "^3.0.2",
     "file-loader": "^1.1.0",
-    "googleapis": "^28.1.0",
+    "googleapis": "^29.0.0",
     "graceful-fs": "^4.1.11",
     "growi-pluginkit": "^1.1.0",
     "i18next": "^11.1.1",
@@ -164,7 +164,7 @@
     "morgan": "^1.8.2",
     "node-dev": "^3.1.3",
     "on-headers": "^1.0.1",
-    "sinon": "^4.0.0",
+    "sinon": "^5.0.2",
     "sinon-chai": "^3.0.0",
     "webpack-dll-bundles-plugin": "^1.0.0-beta.5"
   },

+ 25 - 25
resource/js/components/PageAttachment.js

@@ -27,19 +27,19 @@ export default class PageAttachment extends React.Component {
     }
 
     this.props.crowi.apiGet('/attachments.list', {page_id: pageId })
-    .then(res => {
-      const attachments = res.attachments;
-      let inUse = {};
-
-      for (const attachment of attachments) {
-        inUse[attachment._id] = this.checkIfFileInUse(attachment);
-      }
-
-      this.setState({
-        attachments: attachments,
-        inUse: inUse,
+      .then(res => {
+        const attachments = res.attachments;
+        let inUse = {};
+
+        for (const attachment of attachments) {
+          inUse[attachment._id] = this.checkIfFileInUse(attachment);
+        }
+
+        this.setState({
+          attachments: attachments,
+          inUse: inUse,
+        });
       });
-    });
   }
 
   checkIfFileInUse(attachment) {
@@ -62,20 +62,20 @@ export default class PageAttachment extends React.Component {
     });
 
     this.props.crowi.apiPost('/attachments.remove', {attachment_id: attachmentId})
-    .then(res => {
-      this.setState({
-        attachments: this.state.attachments.filter((at) => {
-          return at._id != attachmentId;
-        }),
-        attachmentToDelete: null,
-        deleting: false,
-      });
-    }).catch(err => {
-      this.setState({
-        deleteError: 'Something went wrong.',
-        deleting: false,
+      .then(res => {
+        this.setState({
+          attachments: this.state.attachments.filter((at) => {
+            return at._id != attachmentId;
+          }),
+          attachmentToDelete: null,
+          deleting: false,
+        });
+      }).catch(err => {
+        this.setState({
+          deleteError: 'Something went wrong.',
+          deleting: false,
+        });
       });
-    });
   }
 
   isUserLoggedIn() {

+ 12 - 11
resource/js/components/PageAttachment/Attachment.js

@@ -12,10 +12,10 @@ export default class Attachment extends React.Component {
 
   iconNameByFormat(format) {
     if (format.match(/image\/.+/i)) {
-      return 'fa fa-file-image-o';
+      return 'icon-picture';
     }
 
-    return 'fa fa-file-o';
+    return 'icon-doc';
   }
 
   _onAttachmentDeleteClicked(event) {
@@ -24,7 +24,6 @@ export default class Attachment extends React.Component {
 
   render() {
     const attachment = this.props.attachment;
-    const attachmentId = attachment._id
     const formatIcon = this.iconNameByFormat(attachment.fileFormat);
 
     let fileInUse = '';
@@ -35,16 +34,18 @@ export default class Attachment extends React.Component {
     const fileType = <span className="attachment-filetype label label-default">{attachment.fileFormat}</span>;
 
     const btnDownload = (this.props.isUserLoggedIn)
-        ? <a className="attachment-download" href={`/download/${attachment._id}`}>
-            <i className="icon-cloud-download"></i>
-          </a>
-        : '';
+      ? (
+        <a className="attachment-download" href={`/download/${attachment._id}`}>
+          <i className="icon-cloud-download"></i>
+        </a>)
+      : '';
 
     const btnTrash = (this.props.isUserLoggedIn)
-        ? <a className="text-danger attachment-delete" onClick={this._onAttachmentDeleteClicked}>
-            <i className="icon-trash"></i>
-          </a>
-        : '';
+      ? (
+        <a className="text-danger attachment-delete" onClick={this._onAttachmentDeleteClicked}>
+          <i className="icon-trash"></i>
+        </a>)
+      : '';
 
     return (
       <li>

+ 26 - 20
resource/js/components/PageAttachment/DeleteAttachmentModal.js

@@ -15,22 +15,30 @@ export default class DeleteAttachmentModal extends React.Component {
     this.props.onAttachmentDeleteClickedConfirm(this.props.attachmentToDelete);
   }
 
-  renderByFileFormat(attachment) {
-    if (attachment.fileFormat.match(/image\/.+/i)) {
-      return (
-        <div className="attachment-delete-image">
-          <p>
-            {attachment.originalName} uploaded by <User user={attachment.creator} username />
-          </p>
-          <img src={attachment.url} />
-        </div>
-      );
+  iconNameByFormat(format) {
+    if (format.match(/image\/.+/i)) {
+      return 'icon-picture';
     }
 
+    return 'icon-doc';
+  }
+
+  renderByFileFormat(attachment) {
+    const content = (attachment.fileFormat.match(/image\/.+/i))
+      ? <img src={attachment.url} />
+      : '';
+
+
     return (
-        <p className="attachment-delete-file">
-          <i className="fa fa-file-o"></i>
+      <div className="attachment-delete-image">
+        <p>
+          <i className={this.iconNameByFormat(attachment.fileFormat)}></i> {attachment.originalName}
+        </p>
+        <p>
+          uploaded by <User user={attachment.creator} username />
         </p>
+        {content}
+      </div>
     );
   }
 
@@ -40,9 +48,6 @@ export default class DeleteAttachmentModal extends React.Component {
       return null;
     }
 
-
-    const inUse = this.props.inUse;
-
     const props = Object.assign({}, this.props);
     delete props.onAttachmentDeleteClickedConfirm;
     delete props.attachmentToDelete;
@@ -52,10 +57,10 @@ export default class DeleteAttachmentModal extends React.Component {
 
     let deletingIndicator = '';
     if (this.props.deleting) {
-      deletingIndicator = <Icon name="spinner" spin />;
+      deletingIndicator = <div class="speeding-wheel-sm"></div>;
     }
     if (this.props.deleteError) {
-      deletingIndicator = <p>{this.props.deleteError}</p>;
+      deletingIndicator = <span>{this.props.deleteError}</span>;
     }
 
     let renderAttachment = this.renderByFileFormat(attachment);
@@ -69,10 +74,11 @@ export default class DeleteAttachmentModal extends React.Component {
           {renderAttachment}
         </Modal.Body>
         <Modal.Footer>
-          {deletingIndicator}
+          <div className="mr-3 d-inline-block">
+            {deletingIndicator}
+          </div>
           <Button onClick={this._onDeleteConfirm} bsStyle="danger"
-            disabled={this.props.deleting}
-            >Delete!</Button>
+            disabled={this.props.deleting}>Delete!</Button>
         </Modal.Footer>
       </Modal>
     );

+ 26 - 28
resource/js/components/PageEditor.js

@@ -100,7 +100,7 @@ export default class PageEditor extends React.Component {
    */
   onMarkdownChanged(value) {
     this.renderWithDebounce(value);
-    this.saveDraftWithDebounce()
+    this.saveDraftWithDebounce();
   }
 
   /**
@@ -135,15 +135,15 @@ export default class PageEditor extends React.Component {
           closeButton: true,
           progressBar: true,
           newestOnTop: false,
-          showDuration: "100",
-          hideDuration: "100",
-          timeOut: "1200",
-          extendedTimeOut: "150",
+          showDuration: '100',
+          hideDuration: '100',
+          timeOut: '1200',
+          extendedTimeOut: '150',
         });
 
         this.pageSavedHandler(res.page);
       })
-      .catch(this.apiErrorHandler)
+      .catch(this.apiErrorHandler);
   }
 
   /**
@@ -158,7 +158,7 @@ export default class PageEditor extends React.Component {
     formData.append('_csrf', this.props.crowi.csrfToken);
     formData.append('file', file);
     formData.append('path', this.props.pagePath);
-    formData.append('page_id', this.props.pageId || 0);
+    formData.append('page_id', this.state.pageId || 0);
 
     // post
     this.props.crowi.apiPost(endpoint, formData)
@@ -233,7 +233,7 @@ export default class PageEditor extends React.Component {
     // turn on the flag
     this.isOriginOfScrollSyncEditor = true;
     scrollSyncHelper.scrollPreview(this.previewElement, line);
-  };
+  }
 
   /**
    * scroll Preview element by cursor moving
@@ -253,7 +253,7 @@ export default class PageEditor extends React.Component {
     // turn on the flag
     this.isOriginOfScrollSyncEditor = true;
     scrollSyncHelper.scrollPreviewToRevealOverflowing(this.previewElement, line);
-  };
+  }
 
   /**
    * the scroll event handler from Preview component
@@ -316,15 +316,13 @@ export default class PageEditor extends React.Component {
       closeButton: true,
       progressBar: true,
       newestOnTop: false,
-      showDuration: "100",
-      hideDuration: "100",
-      timeOut: "3000",
+      showDuration: '100',
+      hideDuration: '100',
+      timeOut: '3000',
     });
   }
 
   renderPreview(value) {
-    const config = this.props.crowi.config;
-
     this.setState({ markdown: value });
 
     // render html
@@ -367,27 +365,27 @@ export default class PageEditor extends React.Component {
       <div className="row">
         <div className="col-md-6 col-sm-12 page-editor-editor-container">
           <Editor ref="editor" value={this.state.markdown}
-              editorOptions={this.state.editorOptions}
-              isUploadable={this.state.isUploadable}
-              isUploadableFile={this.state.isUploadableFile}
-              onScroll={this.onEditorScroll}
-              onScrollCursorIntoView={this.onEditorScrollCursorIntoView}
-              onChange={this.onMarkdownChanged}
-              onSave={this.onSave}
-              onUpload={this.onUpload}
+            editorOptions={this.state.editorOptions}
+            isUploadable={this.state.isUploadable}
+            isUploadableFile={this.state.isUploadableFile}
+            onScroll={this.onEditorScroll}
+            onScrollCursorIntoView={this.onEditorScrollCursorIntoView}
+            onChange={this.onMarkdownChanged}
+            onSave={this.onSave}
+            onUpload={this.onUpload}
           />
         </div>
         <div className="col-md-6 hidden-sm hidden-xs page-editor-preview-container">
           <Preview html={this.state.html}
-              inputRef={el => this.previewElement = el}
-              isMathJaxEnabled={this.state.isMathJaxEnabled}
-              renderMathJaxOnInit={false}
-              previewOptions={this.state.previewOptions}
-              onScroll={this.onPreviewScroll}
+            inputRef={el => this.previewElement = el}
+            isMathJaxEnabled={this.state.isMathJaxEnabled}
+            renderMathJaxOnInit={false}
+            previewOptions={this.state.previewOptions}
+            onScroll={this.onPreviewScroll}
           />
         </div>
       </div>
-    )
+    );
   }
 }
 

+ 0 - 2
resource/js/legacy/crowi.js

@@ -502,8 +502,6 @@ $(function() {
     if ($affixContent.length > 0) {
       var $affixContentContainer = $('.row.bg-title');
       var containerHeight = $affixContentContainer.outerHeight(true);
-      // fix height(固定)
-      $affixContentContainer.css({height: containerHeight + 'px'});
       $affixContent.affix({
         offset: {
           top: function() {

+ 1 - 1
resource/styles/agile-admin/inverse/colors/_apply-colors.scss

@@ -281,7 +281,7 @@ body.on-edit {
     background-color: darken($bodycolor, 2%);
 
     .page-editor-editor-container {
-      border-right-color: $border;
+      border-right-color: $navbar-border;
     }
     .page-editor-preview-container {
       background-color: $bodycolor;

+ 5 - 1
resource/styles/scss/_login.scss

@@ -202,9 +202,12 @@
     }
 
     .front, .back {
-      backface-visibility: hidden;
       transition: 0.4s;
+      backface-visibility: hidden;
       transform-style: preserve-3d;
+      // fix https://github.com/weseek/growi/issues/330
+      -webkit-backface-visibility: hidden;
+      -webkit-transform-style: preserve-3d;
     }
     .front {
       z-index: 2;
@@ -223,6 +226,7 @@
       // 'backface-visibility: hidden' and 'z-index: -1' breaks layout in iOS
       .fcbtn:after {
         z-index: 0;
+        opacity: 0.3;
       }
     }
     &.to-flip .back {

+ 1 - 1
resource/styles/scss/_search.scss

@@ -75,7 +75,7 @@
     border-radius: 40px;
     border-top-right-radius: 40px;
     border-bottom-right-radius: 40px;
-    padding-top: 4px;
+    padding-top: 6px;
     height: 30px;
 
     .rbt-input-wrapper {

+ 45 - 21
yarn.lock

@@ -2,6 +2,12 @@
 # yarn lockfile v1
 
 
+"@sinonjs/formatio@^2.0.0":
+  version "2.0.0"
+  resolved "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2"
+  dependencies:
+    samsam "1.3.0"
+
 "@types/body-parser@*":
   version "1.16.8"
   resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.16.8.tgz#687ec34140624a3bec2b1a8ea9268478ae8f3be3"
@@ -1875,6 +1881,16 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0:
     shebang-command "^1.2.0"
     which "^1.2.9"
 
+cross-spawn@^6.0.5:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+  dependencies:
+    nice-try "^1.0.4"
+    path-key "^2.0.1"
+    semver "^5.5.0"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
 crypt@~0.0.1:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
@@ -2373,11 +2389,11 @@ entities@^1.1.1, entities@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
 
-env-cmd@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/env-cmd/-/env-cmd-7.0.0.tgz#d1fcfea6e0cbe6bf50b7130221d568907b6349bd"
+env-cmd@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/env-cmd/-/env-cmd-8.0.1.tgz#0070518b29e78b1102f2e753551eaae3f971a335"
   dependencies:
-    cross-spawn "^5.0.1"
+    cross-spawn "^6.0.5"
 
 errno@^0.1.3:
   version "0.1.6"
@@ -2970,7 +2986,7 @@ form-data@~2.3.1:
     combined-stream "^1.0.5"
     mime-types "^2.1.12"
 
-formatio@1.2.0, formatio@^1.2.0:
+formatio@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb"
   dependencies:
@@ -3181,14 +3197,14 @@ google-p12-pem@^1.0.0:
     node-forge "^0.7.1"
     pify "^3.0.0"
 
-googleapis@^28.1.0:
-  version "28.1.0"
-  resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-28.1.0.tgz#f78ce5751581387274f8eb22eee947a13c7c4285"
+googleapis@^29.0.0:
+  version "29.0.0"
+  resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-29.0.0.tgz#b1b2c080b7c5722621671f732a278a879758f4dd"
   dependencies:
     google-auth-library "^1.3.1"
     pify "^3.0.0"
     qs "^6.5.1"
-    string-template "1.0.0"
+    url-template "^2.0.8"
     uuid "^3.2.1"
 
 graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
@@ -4760,6 +4776,10 @@ neo-async@^2.5.0:
   version "2.5.1"
   resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee"
 
+nice-try@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
+
 nise@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/nise/-/nise-1.2.0.tgz#079d6cadbbcb12ba30e38f1c999f36ad4d6baa53"
@@ -5279,7 +5299,7 @@ path-is-inside@^1.0.1, path-is-inside@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
 
-path-key@^2.0.0:
+path-key@^2.0.0, path-key@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
 
@@ -6470,7 +6490,7 @@ safe-json-stringify@~1:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz#81a098f447e4bbc3ff3312a243521bc060ef5911"
 
-samsam@1.x:
+samsam@1.3.0, samsam@1.x:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50"
 
@@ -6529,6 +6549,10 @@ select@^1.1.2:
   version "5.4.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
 
+semver@^5.5.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
+
 semver@~5.3.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@@ -6673,16 +6697,16 @@ sinon-chai@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.0.0.tgz#d5cbd70fa71031edd96b528e0eed4038fcc99f29"
 
-sinon@^4.0.0:
-  version "4.1.4"
-  resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.4.tgz#36bb237bae38ddf9cc92dcc1b16c51e7785bbc9c"
+sinon@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-5.0.2.tgz#1d54bd6fa3d736053333f02e6e11def4fe7a2a9c"
   dependencies:
+    "@sinonjs/formatio" "^2.0.0"
     diff "^3.1.0"
-    formatio "1.2.0"
     lodash.get "^4.4.2"
     lolex "^2.2.0"
     nise "^1.2.0"
-    supports-color "^4.4.0"
+    supports-color "^5.1.0"
     type-detect "^4.0.5"
 
 slack-node@^0.1.8:
@@ -6896,10 +6920,6 @@ strict-uri-encode@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
 
-string-template@1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
-
 string-width@^1.0.1, string-width@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -7000,7 +7020,7 @@ supports-color@^3.2.3:
   dependencies:
     has-flag "^1.0.0"
 
-supports-color@^4.0.0, supports-color@^4.2.1, supports-color@^4.4.0:
+supports-color@^4.0.0, supports-color@^4.2.1:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
   dependencies:
@@ -7310,6 +7330,10 @@ url-join@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.0.tgz#4d3340e807d3773bda9991f8305acdcc2a665d2a"
 
+url-template@^2.0.8:
+  version "2.0.8"
+  resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"
+
 url@0.10.3:
   version "0.10.3"
   resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"