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

Merge remote-tracking branch 'origin/master' into feat/grouping-users

# Conflicts:
#	lib/locales/en-US/translation.json
#	lib/locales/ja/translation.json
Tatsuya Ise 8 лет назад
Родитель
Сommit
c1e11387e2

+ 20 - 1
CHANGES.md

@@ -1,9 +1,28 @@
 CHANGES
 ========
 
-## 2.4.2-RC
+## 2.4.4-RC
+
+* 
+
+## 2.4.3
+
+* Improvement: i18n in `/admin`
+* Improvement: Add `SESSION_NAME` environment variable
+* Fix: All Elements are cleared when the Check All button in DeletionMode
+* Support: Upgrade libs
+    * uglifycss
+    * sinon-chai
+    
+## 2.4.2
 
 * Improvement: Ensure to set absolute url from root when attaching files when `FILE_UPLOAD=local`
+* Fix: Inline code blocks that includes doller sign are broken
+* Fix: Comment count is not updated when a comment of the page is deleted
+* Improvement: i18n in `/admin` (WIP)
+* Support: Upgrade libs
+    * googleapis
+    * markdown-it-plantuml
 
 ## 2.4.1
 

+ 16 - 0
README.md

@@ -152,6 +152,22 @@ npm start
 For more info, see [Developers Guide](https://github.com/weseek/crowi-plus/wiki/Developers-Guide) on Wiki.
 
 
+Environment Variables
+======================
+
+* **Required**
+    * MONGO_URI: URI to connect to MongoDB.
+* **Option**
+    * NODE_ENV: `production` OR `development`.
+    * PORT: Server port. default: `3000`
+    * REDIS_URL: URI to connect to Redis (to session store).
+    * SESSION_NAME: The name of the session ID cookie to set in the response by Express. default: `connect.sid`
+    * ELASTICSEARCH_URI: URI to connect to Elasticearch.
+    * PASSWORD_SEED: A password seed used by password hash generator.
+    * SECRET_TOKEN: A secret key for verifying the integrity of signed cookies.
+    * FILE_UPLOAD: `aws` (default), `local`, `none`
+
+
 Documentation
 ==============
 

+ 4 - 0
lib/crowi/index.js

@@ -172,6 +172,10 @@ Crowi.prototype.setupSessionConfig = function() {
       },
     };
 
+    if (self.env.SESSION_NAME) {
+        sessionConfig.name = self.env.SESSION_NAME;
+    }
+
     if (redisUrl) {
       var ru   = require('url').parse(redisUrl);
       var redis = require('redis');

+ 73 - 7
lib/locales/en-US/translation.json

@@ -233,7 +233,7 @@
     "ex): internal use only":"ex): internal use only",
     "enable_files_except_image": "Enable file upload other than image files.",
     "attach_enable": "You can attach files other than image files if you enable this option.",
-    "Reload": "Reload",
+    "Update": "Update",
     "Mail settings": "Mail settings",
     "SMTP_used": "If you have SMTP settings, it will be used.",
     "SMTP_but_AWS": "If you do not have SMTP settings but AWS settings,  e-mails will be sent by SES.",
@@ -247,15 +247,81 @@
     "AWS_access": "This is for AWS settings. If you complete AWS settings, file upload function, progile picture function etc will be enabled.",
     "No_SMTP_setting": "If you do not have SMTP settings, e-mails will be sent via SES. You need to verify from e-mail address and production settings.",
     "change_setting": "Caution:if you change this setting not completed, you will not be able to access files you have uploaded so far.",
-    "region": "region",
-    "packet name": "packet name",
+    "region": "Region",
+    "Packet name": "Packet name",
     "Plugin settings": "Plugin settings",
     "Enable plugin loading": "Enable plugin loading",
-    "valid": "valid",
-    "invalid": "invalid"
+    "Load plugins": "Load plugins",
+    "valid": "Valid",
+    "invalid": "Invalid"
 
+  },
+ "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 Crowi-plus 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"
+
+ },
+
+  "markdown_setting": {
+    "markdown_rendering": "You can change Markdown rendering settings.",
+    "validate Line Break": "Validate Line Break",
+    "treat_text": "Treat line breaking in the text page as <code>&lt;br&gt;</code> in HTML",
+    "validate_comment": "Validate Line Break in the comment section",
+    "treat_comment": "Treat line breaking in the comment section as <code>&lt;br&gt;</code> in HTML",
+    "TBD": "(TBD: Markdown function in the comment section has not been implemented yet)"
+
+     },
+
+     "customize_page": {
+       "Behavior": "Behavior",
+       "Layout": "Layout",
+        "Function": "Function",
+        "function_choose": "You can choose Valid/Invalid of the function",
+        "Timeline function": "Timeline function",
+        "subpage_display": "You can show the timeline of the subpages.",
+        "performance_decrease": "If there are many subpages, performance decreases while page loading.",
+        "list_page_display": "You can speed up list page display by invalidating.",
+        "tab_switch": "Save tab-switching in the browser",
+        "save_edit": "Save edit tab and history tab switching in the browser and make it object for forward/back command of the browser.",
+        "by_invalidating": "By invalidating, you can make page transition as the only object for forward/back command of the browser.",
+        "Custom CSS": "Custom CSS",
+        "write_CSS": "You can write CSS that is applied to whole system.",
+        "reflect_change": "You need to reload the page to reflect the change.",
+        "ctrl_space": "Ctrl+Space to Autocomplete",
+        "Custom script": "Custom script",
+        "write_java": "You can write Javascript that is applied to whole system."
+
+
+ },
+
+  "user_management": {
+        "User management": "User management",
+        "invite_users": "Invite new users",
+        "external_account": "External account management",
+        "user_list": "List of users",
+        "Date created": "Date created",
+        "Last login": "Last login",
+        "Manage": "Manage",
+        "Edit menu": "Edit menu",
+        "Reissue password": "Reissue password",
+        "Status":"Status",
+        "Deactivate account":"Deactivate account",
+        "your_own":"You cannot deactivate your own account",
+        "Administrator menu":"Administrator menu",
+        "cannot_remove":"You cannot remove yourself from administrator"
 
-  }
 
+      }
 }
-

+ 77 - 4
lib/locales/ja/translation.json

@@ -247,10 +247,10 @@
     "wiki_change": "ヘッダーやHTMLタイトルに使用されるWikiの名前を変更できます。",
     "header_content": "ここに入力した内容は、ヘッダー等に表示されます。",
     "Confidential name": "コンフィデンシャル表示",
-    "コンフィデンシャル表示": "Confidential name",
+    "ex): internal use only": "例: 社外秘",
     "enable_files_except_image": "画像以外のファイルアップロードを許可",
     "attach_enable": "許可をしている場合、画像以外のファイルをページに添付可能になります。",
-    "Reload": "更新",
+    "Update": "更新",
     "Mail settings": "メールの設定",
     "SMTP_used": "SMTPの設定がされている場合、それが利用されます。",
     "SMTP_but_AWS": "SMTP設定がなく、AWSの設定がある場合、SESでの送信を試みます。",
@@ -265,11 +265,84 @@
     "No_SMTP_setting": "また、SMTP の設定が無い場合、SES を利用したメール送信が行われます。FromメールアドレスのVerify、プロダクション利用設定をする必要があります。",
     "change_setting": "この設定を途中で変更すると、これまでにアップロードしたファイル等へのアクセスができなくなりますのでご注意下さい。",
     "region": "リージョン",
-    "packet name": "パケット名",
+    "Packet name": "パケット名",
     "Plugin settings": "プラグイン設定",
     "Enable plugin loading": "プラグインの読み込みを有効にします。",
+    "Load plugins": "プラグインを読み込む",
     "valid": "有効",
     "invalid": "無効"
 
-   }
+   },
+
+  "security_setting": {
+    "Basic authentication": "Basic認証",
+    "Security settings": "セキュリティ設定",
+    "Guest users access": "ゲストユーザーのアクセス",
+    "Register limitation": "登録の制限",
+    "The whitelist of registration permission E-mail address": "登録許可メールアドレスの<br>ホワイトリスト",
+    "Selecting authentication mechanism": "認証機構選択",
+    "common_authentication": "Basic認証を設定すると、ページ全体に共通の認証がかかります。",
+    "without_encryption": "IDとパスワードは暗号化されずに送信されるのでご注意下さい。",
+    "users_without_account": "アカウントを持たないユーザーはアクセス不可",
+    "restrict_emails": "登録可能なメールアドレスを制限することができます。",
+    "for_instance":"例えば、会社で使う場合 などと記載すると、",
+    "only_those":"その会社のメールアドレスを持っている人のみ登録可能になります。",
+    "insert_single":"1行に1メールアドレス入力してください。",
+    "Authentication mechanism settings":"認証機構設定"
+  },
+
+  "markdown_setting": {
+    "markdown_rendering": "Markdownレンダリングの設定を変更できます。",
+    "validate Line Break": "Line Break を有効にする",
+    "treat_text": "ページテキスト中の改行を、HTML内で<code>&lt;br&gt;</code>として扱います",
+    "validate_comment": "コメント欄で Line Break を有効にする",
+    "treat_comment": "コメント中の改行を、HTML内で<code>&lt;br&gt;</code>として扱います",
+    "TBD": "(TBD: コメント欄の Markdown 化は未だ実装されていません)"
+
+  },
+
+     "customize_page": {
+       "Behavior": "挙動",
+       "Layout": "レイアウト",
+       "Function": "機能",
+       "function_choose": "機能の有効/無効を選択できます。",
+       "Timeline function": "タイムライン機能",
+       "subpage_display": "配下ページのタイムラインを表示できます。",
+       "performance_decrease": "配下ページが多い場合はページロード時のパフォーマンスが落ちます。",
+       "list_page_display": "無効化することでリストページの表示を高速化できます。",
+       "tab_switch":"タブ変更をブラウザ履歴に保存",
+       "save_edit": "編集タブやヒストリータブ等の切り替えをブラウザ履歴に保存し、ブラウザの戻る/進む操作の対象にします。",
+       "by_invalidating": "無効化することで、ページ遷移のみを戻る/進む操作の対象にすることができます。",
+       "Custom CSS": "カスタム CSS",
+        "write_CSS": " システム全体に適用されるCSSを記述できます。",
+        "reflect_change": "変更の反映はページの更新が必要です。",
+        "ctrl_space": "Ctrl+Space でコード補完",
+        "Custom script": "カスタムスクリプト",
+        "write_java": "システム全体に適用されるJavaScriptを記述できます。"
+
+ },
+
+     "user_management": {
+        "User management": "ユーザー管理",
+        "invite_users": "新規ユーザーの招待",
+        "external_account": "外部アカウントの管理",
+        "user_list": "ユーザー一覧",
+        "Date created": "作成日",
+        "Last login": "最終ログイン",
+        "Manage": "操作",
+        "Edit menu": "編集メニュー",
+        "Reissue password": "パスワードの再発行",
+        "Status":"ステータス",
+        "Deactivate account":"アカウント停止",
+        "your_own":"自分自身のアカウントを停止することはできません",
+        "Administrator menu":"管理者メニュー",
+        "cannot_remove":"自分自身を管理者から外すことはできません"
+
+
+
+
+
+
+      }
 }
+

+ 2 - 4
lib/models/comment.js

@@ -122,10 +122,8 @@ module.exports = function(crowi) {
       , Comment = crowi.model('Comment')
     ;
 
-    Comment.countCommentByPageId(savedComment.page)
-    .then(function(count) {
-      return Page.updateCommentCount(savedComment.page, count);
-    }).then(function(page) {
+    Page.updateCommentCount(savedComment.page)
+    .then(function(page) {
       debug('CommentCount Updated', page);
     }).catch(function() {
     });

+ 9 - 7
lib/models/page.js

@@ -17,7 +17,8 @@ module.exports = function(crowi) {
 
     , pageEvent = crowi.event('page')
 
-    , pageSchema;
+    , pageSchema
+    , Comment = crowi.model('Comment');
 
   function isPortalPath(path) {
     if (path.match(/.*\/$/)) {
@@ -306,18 +307,19 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.updateCommentCount = function (page, num)
+  pageSchema.statics.updateCommentCount = function (pageId)
   {
     var self = this;
-
-    return new Promise(function(resolve, reject) {
-      self.update({_id: page}, {commentCount: num}, {}, function(err, data) {
+    var Comment = crowi.model("Comment");
+    return Comment.countCommentByPageId(pageId)
+    .then(function(count) {
+      self.update({_id: pageId}, {commentCount: count}, {}, function(err, data) {
         if (err) {
           debug('Update commentCount Error', err);
-          return reject(err);
+          throw err;
         }
 
-        return resolve(data);
+        return data;
       });
     });
   };

+ 14 - 6
lib/routes/comment.js

@@ -82,15 +82,23 @@ module.exports = function(crowi, app) {
   api.remove = function(req, res){
     var commentId = req.body.comment_id;
     if (!commentId) {
-      return res.json(ApiResponse.error(`'comment_id' is undefined`));
+      return Promise.resolve(res.json(ApiResponse.error(`'comment_id' is undefined`)));
     }
 
-    return Comment.remove({_id: commentId})
-      .then(function() {
-        return res.json(ApiResponse.success({}));
-      }).catch(function(err) {
-        return res.json(ApiResponse.error(err));
+    return Comment.findById(commentId).exec()
+      .then(function(comment) {
+        return comment.remove()
+        .then(function() {
+           return Page.updateCommentCount(comment.page);
+        })
+        .then(function() {
+           return res.json(ApiResponse.success({})); 
+        });
+      })
+      .catch(function(err) {
+        return res.json(ApiResponse.error(err)); 
       });
+
   };
 
   return actions;

+ 1 - 1
lib/routes/page.js

@@ -168,9 +168,9 @@ module.exports = function(crowi, app) {
     Page.hasPortalPage(path, req.user, req.query.revision)
     .then(function(portalPage) {
       renderVars.page = portalPage;
-      renderVars.revision = portalPage.revision;
 
       if (portalPage) {
+        renderVars.revision = portalPage.revision;
         return Revision.findRevisionList(portalPage.path, {});
       } else {
         return Promise.resolve([]);

+ 14 - 14
lib/views/admin/app.html

@@ -75,7 +75,7 @@
         <div class="form-group">
           <div class="col-xs-offset-3 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">{{ t('app_setting.Reload') }}</button>
+            <button type="submit" class="btn btn-primary">{{ t('app_setting.Update') }}</button>
           </div>
         </div>
       </fieldset>
@@ -119,7 +119,7 @@
         <div class="form-group">
           <div class="col-xs-offset-3 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">{{ t('app_setting.Reload') }}</button>
+            <button type="submit" class="btn btn-primary">{{ t('app_setting.Update') }}</button>
           </div>
         </div>
 
@@ -129,22 +129,22 @@
       <form action="/_api/admin/settings/aws" method="post" class="form-horizontal" id="awsSettingForm" role="form">
       <fieldset>
       <legend>{{ t('app_setting.AWS settings') }}</legend>
-        <p class="well">AWS にアクセスするための設定を行います。AWS の設定を完了させると、ファイルアップロード機能、プロフィール写真機能などが有効になります。<br>
-        また、SMTP の設定が無い場合、SES を利用したメール送信が行われます。FromメールアドレスのVerify、プロダクション利用設定をする必要があります。<br>
+        <p class="well">{{ t("app_setting.AWS_access") }}<br>
+        {{ t("app_setting.No_SMTP_setting") }}<br>
           <br>
 
-          <span class="text-danger"><i class="fa fa-warning"></i> この設定を途中で変更すると、これまでにアップロードしたファイル等へのアクセスができなくなりますのでご注意下さい。</span>
+          <span class="text-danger"><i class="fa fa-warning"></i> {{ t("app_setting.change_setting") }}</span>
         </p>
 
         <div class="form-group">
-          <label for="settingForm[app.region]" class="col-xs-3 control-label">リージョン</label>
+          <label for="settingForm[app.region]" class="col-xs-3 control-label">{{ t('app_setting.region') }}</label>
           <div class="col-xs-6">
             <input class="form-control" type="text" name="settingForm[aws:region]" placeholder="例: ap-northeast-1" value="{{ settingForm['aws:region'] }}">
           </div>
         </div>
 
         <div class="form-group">
-          <label for="settingForm[aws:bucket]" class="col-xs-3 control-label">バケット名</label>
+          <label for="settingForm[aws:bucket]" class="col-xs-3 control-label">{{ t('app_setting.Packet name') }}</label>
           <div class="col-xs-6">
             <input class="form-control" type="text" name="settingForm[aws:bucket]" placeholder="例: crowi"  value="{{ settingForm['aws:bucket'] }}">
           </div>
@@ -168,7 +168,7 @@
         <div class="form-group">
           <div class="col-xs-offset-3 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">更新</button>
+            <button type="submit" class="btn btn-primary">{{ t('app_setting.Update') }}</button>
           </div>
         </div>
 
@@ -177,20 +177,20 @@
 
       <form action="/_api/admin/settings/plugin" method="post" class="form-horizontal" id="pluginSettingForm" role="form">
       <fieldset>
-      <legend>プラグイン設定</legend>
-        <p class="well">プラグインの読み込みを有効にします。</p>
+      <legend>{{ t('app_setting.Plugin settings') }}</legend>
+        <p class="well">{{ t('app_setting.Enable plugin loading') }}</p>
 
         <div class="form-group">
-          <label for="settingForm[plugin:isEnabledPlugins]" class="col-xs-3 control-label">プラグインを読み込む</label>
+          <label for="settingForm[plugin:isEnabledPlugins]" class="col-xs-3 control-label">{{ t('app_setting.Load plugins') }}</label>
           <div class="col-xs-6">
             <div class="btn-group btn-toggle" data-toggle="buttons">
               <label class="btn btn-default {% if settingForm['plugin:isEnabledPlugins'] %}active{% endif %}" data-active-class="primary">
                 <input name="settingForm[plugin:isEnabledPlugins]" value="true" type="radio"
-                    {% if true === settingForm['plugin:isEnabledPlugins'] %}checked{% endif %}> 有効
+                    {% if true === settingForm['plugin:isEnabledPlugins'] %}checked{% endif %}> {{ t('app_setting.valid') }}
               </label>
               <label class="btn btn-default {% if !settingForm['plugin:isEnabledPlugins'] %}active{% endif %}" data-active-class="primary">
                 <input name="settingForm[plugin:isEnabledPlugins]" value="false" type="radio"
-                    {% if !settingForm['plugin:isEnabledPlugins'] %}checked{% endif %}> 無効
+                    {% if !settingForm['plugin:isEnabledPlugins'] %}checked{% endif %}> {{ t('app_setting.invalid') }}
               </label>
             </div>
           </div>
@@ -199,7 +199,7 @@
         <div class="form-group">
           <div class="col-xs-offset-3 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">更新</button>
+            <button type="submit" class="btn btn-primary">{{ t('app_setting.Update') }}</button>
           </div>
         </div>
 

+ 30 - 30
lib/views/admin/customize.html

@@ -1,6 +1,6 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}カスタマイズ · {% endblock %}
+{% block html_title %}{{ t('Customize') }} {% endblock %}
 
 {% block html_additional_headers %}
   <!-- CodeMirror -->
@@ -18,7 +18,7 @@
 {% block content_head %}
 <div class="header-wrap">
   <header id="page-header">
-    <h1 class="title" id="">カスタマイズ</h1>
+    <h1 class="title" id="">{{ t('Customize') }} </h1>
   </header>
 </div>
 {% endblock %}
@@ -47,7 +47,7 @@
 
       <form action="/_api/admin/customize/behavior" method="post" class="form-horizontal" id="cutombehaviorSettingForm" role="form">
       <fieldset>
-        <legend>挙動</legend>
+        <legend>{{ t('customize_page.Behavior') }}</legend>
 
         <div class="form-group">
           <div class="col-xs-6">
@@ -83,7 +83,7 @@
         <div class="form-group">
           <div class="col-xs-offset-5 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">更新</button>
+            <button type="submit" class="btn btn-primary">{{ t('Update') }}</button>
           </div>
         </div>
 
@@ -93,7 +93,7 @@
 
       <form action="/_api/admin/customize/layout" method="post" class="form-horizontal" id="cutomlayoutSettingForm" role="form">
       <fieldset>
-        <legend>レイアウト</legend>
+        <legend>{{ t('customize_page.Layout') }}</legend>
 
         <div class="form-group">
           <div class="col-xs-6">
@@ -136,7 +136,7 @@
         <div class="form-group">
           <div class="col-xs-offset-5 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">更新</button>
+            <button type="submit" class="btn btn-primary">{{ t('Update') }}</button>
           </div>
         </div>
 
@@ -146,50 +146,50 @@
 
       <form action="/_api/admin/customize/features" method="post" class="form-horizontal" id="customfeaturesSettingForm" role="form">
       <fieldset>
-      <legend>機能</legend>
-        <p class="well">機能の有効/無効を選択できます。</p>
+      <legend>{{ t('customize_page.Function') }}</legend>
+        <p class="well">{{ t("customize_page.function_choose") }}</p>
 
         <div class="form-group">
-          <label for="settingForm[customize:isEnabledTimeline]" class="col-xs-3 control-label">タイムライン機能</label>
+          <label for="settingForm[customize:isEnabledTimeline]" class="col-xs-3 control-label">{{ t('customize_page.Timeline function') }}</label>
           <div class="col-xs-9">
             <div class="btn-group btn-toggle" data-toggle="buttons">
               <label class="btn btn-default {% if settingForm['customize:isEnabledTimeline'] %}active{% endif %}" data-active-class="primary">
                 <input name="settingForm[customize:isEnabledTimeline]" value="true" type="radio"
-                    {% if true === settingForm['customize:isEnabledTimeline'] %}checked{% endif %}> 有効
+                    {% if true === settingForm['customize:isEnabledTimeline'] %}checked{% endif %}> {{ t('Valid') }}
               </label>
               <label class="btn btn-default {% if !settingForm['customize:isEnabledTimeline'] %}active{% endif %}" data-active-class="primary">
                 <input name="settingForm[customize:isEnabledTimeline]" value="false" type="radio"
-                    {% if !settingForm['customize:isEnabledTimeline'] %}checked{% endif %}> 無効
+                    {% if !settingForm['customize:isEnabledTimeline'] %}checked{% endif %}> {{ t('Invalid') }}
               </label>
             </div>
 
             <p class="help-block">
-              配下ページのタイムラインを表示できます。
+              {{ t("customize_page.subpage_display") }}
             </p>
             <p class="help-block">
-              配下ページが多い場合はページロード時のパフォーマンスが落ちます。<br>
-              無効化することでリストページの表示を高速化できます。
+              {{ t("customize_page.performance_decrease") }}<br>
+              {{ t("customize_page.list_page_display") }}
             </p>
           </div>
         </div>
 
         <div class="form-group">
-          <label for="settingForm[customize:isSavedStatesOfTabChanges]" class="col-xs-3 control-label">タブ変更をブラウザ履歴に保存</label>
+          <label for="settingForm[customize:isSavedStatesOfTabChanges]" class="col-xs-3 control-label">{{ t("customize_page.tab_switch") }}</label>
           <div class="col-xs-9">
             <div class="btn-group btn-toggle" data-toggle="buttons">
               <label class="btn btn-default {% if settingForm['customize:isSavedStatesOfTabChanges'] %}active{% endif %}" data-active-class="primary">
                 <input name="settingForm[customize:isSavedStatesOfTabChanges]" value="true" type="radio"
-                    {% if true === settingForm['customize:isSavedStatesOfTabChanges'] %}checked{% endif %}> 有効
+                    {% if true === settingForm['customize:isSavedStatesOfTabChanges'] %}checked{% endif %}> {{ t('Valid') }}
               </label>
               <label class="btn btn-default {% if !settingForm['customize:isSavedStatesOfTabChanges'] %}active{% endif %}" data-active-class="primary">
                 <input name="settingForm[customize:isSavedStatesOfTabChanges]" value="false" type="radio"
-                    {% if !settingForm['customize:isSavedStatesOfTabChanges'] %}checked{% endif %}> 無効
+                    {% if !settingForm['customize:isSavedStatesOfTabChanges'] %}checked{% endif %}> {{ t('Invalid') }}
               </label>
             </div>
 
             <p class="help-block">
-              編集タブやヒストリータブ等の切り替えをブラウザ履歴に保存し、ブラウザの戻る/進む操作の対象にします。<br>
-              無効化することで、ページ遷移のみを戻る/進む操作の対象にすることができます。
+              {{ t("customize_page.save_edit") }}<br>
+              {{ t("customize_page.by_invalidating") }}
             </p>
           </div>
         </div>
@@ -197,7 +197,7 @@
         <div class="form-group">
           <div class="col-xs-offset-3 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">更新</button>
+            <button type="submit" class="btn btn-primary">{{ t('Update') }}</button>
           </div>
         </div>
 
@@ -243,11 +243,11 @@
 
       <form action="/_api/admin/customize/css" method="post" class="form-horizontal" id="cutomcssSettingForm" role="form">
       <fieldset>
-        <legend>カスタムヘッダーCSS</legend>
+        <legend>{{ t('customize_page.Custom CSS') }}</legend>
 
         <p class="well">
-          システム全体に適用されるCSSを記述できます。<code>&lt;header&gt;</code> タグ内に展開されます。<br>
-          変更の反映はページの更新が必要です。
+          {{ t("customize_page.write_CSS") }}<br>
+          {{ t("customize_page.reflect_change") }}
         </p>
 
         <div class="form-group">
@@ -258,7 +258,7 @@
           <div class="col-xs-12">
             <p class="help-block text-right">
               <i class="fa fa-fw fa-keyboard-o" aria-hidden="true"></i>
-              Ctrl+Space でコード補完
+              {{ t("customize_page.ctrl_space") }}
             </p>
           </div>
         </div>
@@ -266,7 +266,7 @@
         <div class="form-group">
           <div class="col-xs-offset-5 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">更新</button>
+            <button type="submit" class="btn btn-primary">{{ t('Update') }}</button>
           </div>
         </div>
 
@@ -276,11 +276,11 @@
 
       <form action="/_api/admin/customize/script" method="post" class="form-horizontal" id="cutomscriptSettingForm" role="form">
       <fieldset>
-        <legend>カスタムスクリプト</legend>
+        <legend>{{ t('customize_page.Custom script') }}</legend>
 
         <p class="well">
-          システム全体に適用されるJavaScriptを記述できます。<code>&lt;body&gt;</code> タグ内の最後に展開されます。<br>
-          変更の反映はページの更新が必要です。
+          {{ t("customize_page.write_java") }}<br>
+          {{ t("customize_page.reflect_change") }}
         </p>
 
         <p class="help-block">
@@ -316,7 +316,7 @@ window.addEventListener('load', (event) => {
           <div class="col-xs-12">
             <p class="help-block text-right">
               <i class="fa fa-fw fa-keyboard-o" aria-hidden="true"></i>
-              Ctrl+Space でコード補完
+              {{ t("customize_page.ctrl_space") }}
             </p>
           </div>
         </div>
@@ -324,7 +324,7 @@ window.addEventListener('load', (event) => {
         <div class="form-group">
           <div class="col-xs-offset-5 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">更新</button>
+            <button type="submit" class="btn btn-primary">{{ t('Update') }}</button>
           </div>
         </div>
 

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

@@ -1,6 +1,6 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}Wiki管理· {{ path }}{% endblock %}
+{% block html_title %}{{ t('admin_top.Management Wiki') }}· {{ path }}{% endblock %}
 
 {% block content_head %}
 <div class="header-wrap">

+ 15 - 13
lib/views/admin/markdown.html

@@ -1,11 +1,12 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}Markdown設定 · {{ path }}{% endblock %}
+{% block html_title %}{{ t('Markdown settings') }}
+ · {{ path }}{% endblock %}
 
 {% block content_head %}
 <div class="header-wrap">
   <header id="page-header">
-    <h1 class="title" id="">Markdown設定</h1>
+    <h1 class="title" id="">{{ t('Markdown settings') }}</h1>
   </header>
 </div>
 {% endblock %}
@@ -38,51 +39,52 @@
 
       <form action="/admin/markdown/lineBreaksSetting" method="post" class="form-horizontal" id="markdownSettingForm" role="form">
       <fieldset>
-        <legend>Markdown設定</legend>
-        <p class="well">Markdownレンダリングの設定を変更できます。</p>
+        <legend>{{ t('Markdown settings') }}</legend>
+        <p class="well">{{ t("markdown_setting.markdown_rendering") }}</p>
 
         <div class="form-group">
           <label for="markdownSetting[markdown:isEnabledLinebreaks]" class="col-xs-4 control-label">
-            Line Break を有効にする
+            {{ t('markdown_setting.validate Line Break') }}
           </label>
           <div class="col-xs-5">
             <div class="btn-group btn-toggle" data-toggle="buttons">
               <label class="btn btn-default {% if markdownSetting['markdown:isEnabledLinebreaks'] %}active{% endif %}" data-active-class="primary">
                 <input name="markdownSetting[markdown:isEnabledLinebreaks]" value="true" type="radio"
-                    {% if true === markdownSetting['markdown:isEnabledLinebreaks'] %}checked{% endif %}> 有効
+                    {% if true === markdownSetting['markdown:isEnabledLinebreaks'] %}checked{% endif %}> {{ t('valid') }}
               </label>
               <label class="btn btn-default {% if !markdownSetting['markdown:isEnabledLinebreaks'] %}active{% endif %}" data-active-class="primary">
                 <input name="markdownSetting[markdown:isEnabledLinebreaks]" value="false" type="radio"
-                    {% if !markdownSetting['markdown:isEnabledLinebreaks'] %}checked{% endif %}> 無効
+                    {% if !markdownSetting['markdown:isEnabledLinebreaks'] %}checked{% endif %}> {{ t('invalid') }}
               </label>
             </div>
-            <p class="help-block">ページテキスト中の改行を、HTML内で&lt;br&gt;として扱います</p>
+            <p class="help-block">{{ t("markdown_setting.treat_text") }}
+</p>
           </div>
         </div>
 
         <div class="form-group">
           <label for="markdownSetting[markdown:isEnabledLinebreaksInComments]" class="col-xs-4 control-label">
-            (TBD)<br>コメント欄で Line Break を有効にする
+            (TBD)<br>{{ t("markdown_setting.validate_comment") }}
           </label>
           <div class="col-xs-5">
             <div class="btn-group btn-toggle" data-toggle="buttons">
               <label class="btn btn-default {% if markdownSetting['markdown:isEnabledLinebreaksInComments'] %}active{% endif %}" data-active-class="primary">
                 <input name="markdownSetting[markdown:isEnabledLinebreaksInComments]" value="true" type="radio"
-                    {% if true === markdownSetting['markdown:isEnabledLinebreaksInComments'] %}checked{% endif %}> 有効
+                    {% if true === markdownSetting['markdown:isEnabledLinebreaksInComments'] %}checked{% endif %}> {{ t('valid') }}
               </label>
               <label class="btn btn-default {% if !markdownSetting['markdown:isEnabledLinebreaksInComments'] %}active{% endif %}" data-active-class="primary">
                 <input name="markdownSetting[markdown:isEnabledLinebreaksInComments]" value="false" type="radio"
-                    {% if !markdownSetting['markdown:isEnabledLinebreaksInComments'] %}checked{% endif %}> 無効
+                    {% if !markdownSetting['markdown:isEnabledLinebreaksInComments'] %}checked{% endif %}> {{ t('invalid') }}
               </label>
             </div>
-            <p class="help-block">コメント中の改行を、HTML内で&lt;br&gt;として扱います<br>(TBD: コメント欄の Markdown 化は未だ実装されていません)</p>
+            <p class="help-block">{{ t("markdown_setting.treat_comment") }}<br>{{ t("markdown_setting.TBD") }}</p>
           </div>
         </div>
 
         <div class="form-group">
           <div class="col-xs-offset-4 col-xs-5">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">更新</button>
+            <button type="submit" class="btn btn-primary">{{ t("Update") }}</button>
           </div>
         </div>
       </fieldset>

+ 2 - 2
lib/views/admin/notification.html

@@ -1,11 +1,11 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}通知設定 · {{ path }}{% endblock %}
+{% block html_title %}{{ t('Notification settings') }} · {{ path }}{% endblock %}
 
 {% block content_head %}
 <div class="header-wrap">
   <header id="page-header">
-    <h1 class="title" id="">通知設定</h1>
+    <h1 class="title" id="">{{ t('Notification settings') }}</h1>
   </header>
 </div>
 {% endblock %}

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

@@ -1,6 +1,6 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}セキュリティ · {% endblock %}
+{% block html_title %}{{ t('Security settings') }} · {% endblock %}
 
 {% block content_head %}
 <div class="header-wrap">
@@ -38,7 +38,7 @@
 
       <form action="/_api/admin/security/general" method="post" class="form-horizontal" id="generalSetting" role="form">
         <fieldset>
-        <legend>{{ t('Security settings') }}</legend>
+        <legend>{{ t('security_setting.Security settings') }}</legend>
 
           <div class="form-group">
             <label for="settingForm[security:registrationMode]" class="col-xs-3 control-label">{{ t('Basic authentication') }}</label>
@@ -52,8 +52,8 @@
             </div>
             <div class="col-xs-offset-3 col-xs-9">
               <p class="help-block">
-                Basic認証を設定すると、ページ全体に共通の認証がかかります。<br>
-                IDとパスワードは暗号化されずに送信されるのでご注意下さい。<br>
+                {{ t("security_setting.common_authentication") }}<br>
+                {{ t("security_setting.without_encryption") }}<br>
               </p>
             </div>
           </div>
@@ -85,8 +85,8 @@
             <label for="settingForm[security:registrationWhiteList]" class="col-xs-3 control-label">{{ t('The whitelist of registration permission E-mail address') }}</label>
             <div class="col-xs-8">
               <textarea class="form-control" type="textarea" name="settingForm[security:registrationWhiteList]" placeholder="例: @crowi.wiki">{{ settingForm['security:registrationWhiteList']|join('&#13')|raw }}</textarea>
-              <p class="help-block">登録可能なメールアドレスを制限することができます。例えば、会社で使う場合、<code>@crowi.wiki</code> などと記載すると、その会社のメールアドレスを持っている人のみ登録可能になります。<br>
-              1行に1メールアドレス入力してください。</p>
+              <p class="help-block">{{ t("security_setting.restrict_emails") }}{{ t("security_setting.for_instance") }}<code>@crowi.wiki</code>{{ t("security_setting.only_those") }}<br>
+              {{ t("security_setting.insert_single") }}</p>
             </div>
           </div>
 
@@ -147,7 +147,7 @@
 
       <div class="auth-mechanism-configurations">
 
-        <legend>認証機構設定</legend>
+        <legend>{{ t('security_setting.Authentication mechanism settings') }}</legend>
 
         {% set isOfficialConfigurationVisible = !isEnabledPassport() %}
         <div class="official-crowi-auth-settings" {% if !isOfficialConfigurationVisible %}style="display: none;"{% endif %}>

+ 20 - 20
lib/views/admin/users.html

@@ -1,11 +1,11 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}ユーザー管理 · {% endblock %}
+{% block html_title %}{{ t('user_management.User management') }}· {% endblock %}
 
 {% block content_head %}
 <div class="header-wrap">
   <header id="page-header">
-    <h1 class="title" id="">ユーザー管理</h1>
+    <h1 class="title" id="">{{ t('user_management.User management') }}</h1>
   </header>
 </div>
 {% endblock %}
@@ -33,10 +33,10 @@
 
     <div class="col-md-9">
       <p>
-        <button data-toggle="collapse" class="btn btn-default" href="#inviteUserForm">新規ユーザーの招待</button>
+        <button data-toggle="collapse" class="btn btn-default" href="#inviteUserForm">{{ t("user_management.invite_users") }}</button>
         <a class="btn btn-default" href="/admin/users/external-accounts">
           <i class="fa fa-user-plus" aria-hidden="true"></i>
-          外部アカウントの管理
+          {{ t("user_management.external_account") }}
         </a>
       </p>
       <form role="form" action="/admin/user/invite" method="post">
@@ -136,7 +136,7 @@
         </div><!-- /.modal-dialog -->
       </div>
 
-      <h2>ユーザー一覧</h2>
+      <h2>{{ t("user_management.user_list") }}</h2>
 
       <table class="table table-hover table-striped table-bordered table-user-list">
         <thead>
@@ -144,11 +144,11 @@
             <th width="100px">#</th>
             <th>Status</th>
             <th><code>username</code></th>
-            <th>名前</th>
-            <th>メールアドレス</th>
-            <th width="100px">作成日</th>
-            <th width="150px">最終ログイン</th>
-            <th width="90px">操作</th>
+            <th>{{ t('Name') }}</th>
+            <th>{{ t('Email') }}</th>
+            <th width="100px">{{ t('user_management.Date created') }}</th>
+            <th width="150px">{{ t('user_management.Last login') }}</th>
+            <th width="90px">{{ t('user_management.Manage') }}</th>
           </tr>
         </thead>
         <tbody>
@@ -181,13 +181,13 @@
             <td>
               <div class="btn-group admin-user-menu">
                 <button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
-                  編集
+                  {{ t('Edit') }}
                   <span class="caret"></span>
                 </button>
                 <ul class="dropdown-menu" role="menu">
-                  <li class="dropdown-header">編集メニュー</li>
+                  <li class="dropdown-header">{{ t('user_management.Edit menu') }}</li>
                   <li>
-                    <a href="">編集</a>
+                    <a href="">{{ t('Edit') }}</a>
 
                   </li>
                   <li class="dropdown-button">
@@ -196,11 +196,11 @@
                       data-user-email="{{ sUser.email }}"
                       data-target="#admin-password-reset-modal"
                       data-toggle="modal" class="btn btn-block btn-default">
-                      パスワードの再発行
+                      {{ t('user_management.Reissue password') }}
                     </a>
                   </li>
                   <li class="divider"></li>
-                  <li class="dropdown-header">ステータス</li>
+                  <li class="dropdown-header">{{ t('user_management.Status') }}</li>
 
                   {% if sUser.status == 1 %}
                   <li class="dropdown-button">
@@ -216,12 +216,12 @@
                     {% if sUser.username != user.username %}
                     <form action="/admin/user/{{ sUser._id.toString() }}/suspend" method="post">
                       <input type="hidden" name="_csrf" value="{{ csrf() }}">
-                      <button type="submit" class="btn btn-block btn-warning">アカウント停止</button>
+                      <button type="submit" class="btn btn-block btn-warning">{{ t('user_management.Deactivate account') }}</button>
                     </form>
                     {% else %}
-                    <button class="btn btn-block btn-warning" disabled>アカウント停止</button>
+                    <button class="btn btn-block btn-warning" disabled>{{ t('user_management.Deactivate account') }}</button>
                     <br>
-                    <p class="alert alert-danger">自分自身のアカウントを停止することはできません</p>
+                    <p class="alert alert-danger">{{ t("user_management.your_own") }}</p>
                     {% endif %}
                   </li>
                   {% endif %}
@@ -254,7 +254,7 @@
 
                   {% if sUser.status == 2 %} {# activated な人だけこのメニューを表示 #}
                   <li class="divider"></li>
-                  <li class="dropdown-header">管理者メニュー</li>
+                  <li class="dropdown-header">{{ t('user_management.Administrator menu') }}</li>
 
                   <li class="dropdown-button">
                     {% if sUser.admin %}
@@ -264,7 +264,7 @@
                         <button type="submit" class="btn btn-block btn-danger">管理者からはずす</button>
                       </form>
                       {% else %}
-                      <p class="alert alert-danger">自分自身を管理者から外すことはできません</p>
+                      <p class="alert alert-danger">{{ t("user_management.cannot_remove") }}</p>
                       {% endif %}
                     {% else %}
                       <form action="/admin/user/{{ sUser._id.toString() }}/makeAdmin" method="post">

+ 6 - 6
package.json

@@ -1,6 +1,6 @@
 {
   "name": "crowi-plus",
-  "version": "2.4.2-RC",
+  "version": "2.4.4-RC",
   "description": "Enhanced Crowi",
   "tags": [
     "wiki",
@@ -84,7 +84,7 @@
     "express-session": "~1.15.0",
     "express-webpack-assets": "^0.1.0",
     "file-loader": "^1.1.0",
-    "googleapis": "^26.0.0",
+    "googleapis": "^27.0.0",
     "graceful-fs": "^4.1.11",
     "i18next": "^10.0.1",
     "i18next-express-middleware": "^1.0.5",
@@ -98,7 +98,7 @@
     "markdown-it-footnote": "^3.0.1",
     "markdown-it-mathjax": "^2.0.0",
     "markdown-it-named-headers": "^0.0.4",
-    "markdown-it-plantuml": "^0.3.1",
+    "markdown-it-plantuml": "^1.0.0",
     "markdown-it-task-lists": "^2.1.0",
     "markdown-it-toc-and-anchor-with-slugid": "^1.1.2",
     "md5": "^2.2.1",
@@ -135,10 +135,10 @@
     "swig-templates": "^2.0.2",
     "throttle-debounce": "^1.0.1",
     "toastr": "^2.1.2",
-    "uglifycss": "^0.0.27",
+    "uglifycss": "^0.0.28",
     "url-join": "^4.0.0",
     "uslug": "^1.0.4",
-    "webpack": "^3.1.0",
+    "webpack": "3.11.0",
     "webpack-bundle-analyzer": "^2.9.0",
     "webpack-merge": "~4.1.0",
     "xss": "^0.3.5"
@@ -154,7 +154,7 @@
     "node-dev": "^3.1.3",
     "on-headers": "^1.0.1",
     "sinon": "^4.0.0",
-    "sinon-chai": "^2.13.0",
+    "sinon-chai": "^3.0.0",
     "webpack-dll-bundles-plugin": "^1.0.0-beta.5"
   },
   "engines": {

+ 3 - 3
resource/js/components/SearchPage/DeletePageListModal.js

@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import { Button } from 'react-bootstrap/es/Button';
-import { Modal } from 'react-bootstrap/es/Modal';
-import { Checkbox } from 'react-bootstrap/es/Checkbox';
+import Button from 'react-bootstrap/es/Button';
+import Modal from 'react-bootstrap/es/Modal';
+import Checkbox from 'react-bootstrap/es/Checkbox';
 
 import ReactUtils from '../ReactUtils';
 

+ 3 - 4
resource/js/util/interceptor/detach-code-blocks.js

@@ -1,6 +1,3 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-
 import { BasicInterceptor } from 'crowi-pluginkit';
 
 
@@ -107,7 +104,9 @@ export class RestoreCodeBlockInterceptor extends BasicInterceptor {
       // get context object from context
       let dcbContext = context.dcbContextMap[replaceId];
 
-      context.markdown = context.markdown.replace(dcbContext.substituteContent, dcbContext.content);
+      // replace it with content by using getter function so that the doller sign does not work
+      // see: https://github.com/weseek/crowi-plus/issues/285
+      context.markdown = context.markdown.replace(dcbContext.substituteContent, () => { return dcbContext.content; });
     });
 
     // resolve

+ 1 - 1
resource/js/util/markdown-it/plantuml.js

@@ -13,7 +13,7 @@ export default class PlantUMLConfigurer {
   }
 
   configure(md) {
-    md.use(require('markdown-it-plantuml'), 'name', {
+    md.use(require('markdown-it-plantuml'), {
       generateSource: this.generateSource,
     });
   }

+ 61 - 26
yarn.lock

@@ -122,10 +122,14 @@ agentkeepalive@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef"
 
-ajv-keywords@^2.0.0, ajv-keywords@^2.1.0:
+ajv-keywords@^2.1.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
 
+ajv-keywords@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be"
+
 ajv@^4.9.1:
   version "4.11.8"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
@@ -133,7 +137,7 @@ ajv@^4.9.1:
     co "^4.6.0"
     json-stable-stringify "^1.0.1"
 
-ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5:
+ajv@^5.0.0, ajv@^5.1.0:
   version "5.5.2"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
   dependencies:
@@ -142,6 +146,14 @@ ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5:
     fast-json-stable-stringify "^2.0.0"
     json-schema-traverse "^0.3.0"
 
+ajv@^6.1.0:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.2.1.tgz#28a6abc493a2abe0fb4c8507acaedb43fa550671"
+  dependencies:
+    fast-deep-equal "^1.0.0"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.3.0"
+
 align-text@^0.1.1, align-text@^0.1.3:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -2348,7 +2360,7 @@ express@^4.15.2, express@^4.16.1:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
-extend@3, extend@~3.0.0, extend@~3.0.1:
+extend@3, extend@^3.0.1, extend@~3.0.0, extend@~3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
 
@@ -2603,6 +2615,14 @@ gaze@^1.0.0:
   dependencies:
     globule "^1.0.0"
 
+gcp-metadata@^0.6.0:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-0.6.1.tgz#62d54871fc6aeeac6a688e094abc886cb7aaacd0"
+  dependencies:
+    axios "^0.17.1"
+    extend "^3.0.1"
+    retry-axios "0.3.0"
+
 generate-function@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74"
@@ -2687,15 +2707,17 @@ good-listener@^1.2.2:
   dependencies:
     delegate "^3.1.2"
 
-google-auth-library@^1.1.0:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-1.2.1.tgz#20eb9d585b1837a703712abdb787da4984982b64"
+google-auth-library@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-1.3.1.tgz#a0456166c9538ee062c51b8e70765a5a0e13e757"
   dependencies:
-    axios "^0.17.1"
+    axios "^0.18.0"
+    gcp-metadata "^0.6.0"
     gtoken "^2.1.0"
     jws "^3.1.4"
     lodash.isstring "^4.0.1"
     lru-cache "^4.1.1"
+    retry-axios "^0.3.2"
 
 google-p12-pem@^1.0.0:
   version "1.0.0"
@@ -2704,14 +2726,15 @@ google-p12-pem@^1.0.0:
     node-forge "^0.7.1"
     pify "^3.0.0"
 
-googleapis@^26.0.0:
-  version "26.0.1"
-  resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-26.0.1.tgz#e1efb43b00546b1ad8c055a83cf210d5422b7f42"
+googleapis@^27.0.0:
+  version "27.0.0"
+  resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-27.0.0.tgz#c210633b43e7047b65d33da40c489b6d8f9c02b8"
   dependencies:
-    google-auth-library "^1.1.0"
+    google-auth-library "^1.3.1"
+    pify "^3.0.0"
     qs "^6.5.1"
     string-template "1.0.0"
-    uuid "^3.1.0"
+    uuid "^3.2.1"
 
 graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
   version "4.1.11"
@@ -3732,9 +3755,9 @@ markdown-it-named-headers@^0.0.4:
   dependencies:
     string "^3.0.1"
 
-markdown-it-plantuml@^0.3.1:
-  version "0.3.1"
-  resolved "https://registry.yarnpkg.com/markdown-it-plantuml/-/markdown-it-plantuml-0.3.1.tgz#f338df4d691a5561364e65809b6812bcb3d8b047"
+markdown-it-plantuml@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/markdown-it-plantuml/-/markdown-it-plantuml-1.0.0.tgz#7b6a351a1d9275705c09626b02d873301e5899c2"
 
 markdown-it-task-lists@^2.1.0:
   version "2.1.0"
@@ -5501,6 +5524,14 @@ resolve@^1.0.0:
   dependencies:
     path-parse "^1.0.5"
 
+retry-axios@0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-0.3.0.tgz#7858ad369872d6acaf05fd97b0490969c9c35ee2"
+
+retry-axios@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-0.3.2.tgz#5757c80f585b4cc4c4986aa2ffd47a60c6d35e13"
+
 retry@^0.9.0:
   version "0.9.0"
   resolved "https://registry.yarnpkg.com/retry/-/retry-0.9.0.tgz#6f697e50a0e4ddc8c8f7fb547a9b60dead43678d"
@@ -5698,9 +5729,9 @@ signal-exit@^3.0.0:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
 
-sinon-chai@^2.13.0:
-  version "2.14.0"
-  resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-2.14.0.tgz#da7dd4cc83cd6a260b67cca0f7a9fdae26a1205d"
+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"
@@ -6184,9 +6215,9 @@ uglify-to-browserify@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
 
-uglifycss@^0.0.27:
-  version "0.0.27"
-  resolved "https://registry.yarnpkg.com/uglifycss/-/uglifycss-0.0.27.tgz#53553b8115de26dce57b42ba0b34e88dd80d0cde"
+uglifycss@^0.0.28:
+  version "0.0.28"
+  resolved "https://registry.yarnpkg.com/uglifycss/-/uglifycss-0.0.28.tgz#b865ae5f66e4fcec6ea1d979e150dc5bf37ceb1c"
 
 uglifyjs-webpack-plugin@^0.4.6:
   version "0.4.6"
@@ -6298,6 +6329,10 @@ uuid@3.1.0, uuid@^3.0.0, uuid@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
 
+uuid@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
+
 uws@~0.14.4:
   version "0.14.5"
   resolved "https://registry.yarnpkg.com/uws/-/uws-0.14.5.tgz#67aaf33c46b2a587a5f6666d00f7691328f149dc"
@@ -6408,14 +6443,14 @@ webpack-sources@^1.0.1:
     source-list-map "^2.0.0"
     source-map "~0.6.1"
 
-webpack@^3.1.0:
-  version "3.10.0"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.10.0.tgz#5291b875078cf2abf42bdd23afe3f8f96c17d725"
+webpack@3.11.0:
+  version "3.11.0"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.11.0.tgz#77da451b1d7b4b117adaf41a1a93b5742f24d894"
   dependencies:
     acorn "^5.0.0"
     acorn-dynamic-import "^2.0.0"
-    ajv "^5.1.5"
-    ajv-keywords "^2.0.0"
+    ajv "^6.1.0"
+    ajv-keywords "^3.1.0"
     async "^2.1.2"
     enhanced-resolve "^3.4.0"
     escope "^3.6.0"