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

Merge pull request #3653 from weseek/master

release v4.2.15
Yuki Takei 5 лет назад
Родитель
Сommit
d5c4a94385

+ 7 - 0
.github/ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE/bug-report.md

@@ -1,3 +1,10 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: 'Bug:' 
+labels: bug
+---
+
 Environment
 ------------
 

+ 5 - 0
.github/ISSUE_TEMPLATE/config.yml

@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+  - name: Question or Suggestions
+    url: https://growi-slackin.weseek.co.jp/
+    about: If you have questions or suggestions, you can join our Slack team and talk about anything, anytime.

+ 25 - 0
.github/ISSUE_TEMPLATE/user-request.md

@@ -0,0 +1,25 @@
+---
+name: User request
+about: Suggest an idea for this project
+title: 'Request:'
+labels: user requests
+---
+
+
+## Informations
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. 
+
+e.g. I'm having trouble getting immediate access to information
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to realization.
+
+e.g. It's good if there is a space where everyone can access information in common.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+- [ ] Custom Page In Sidebar
+  - [ ] Can be edited in `/Sidebar` path
+  - [ ] Can be described with md like a page

+ 1 - 1
.github/workflows/release.yml

@@ -125,7 +125,7 @@ jobs:
     - name: Slack Notification
       uses: weseek/ghaction-release-slack-notification@master
       with:
-        channel: '#general'
+        channel: '#release'
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
         created_tag: 'v${{ needs.github-release.outputs.RELEASE_VERSION }}${{ env.SUFFIX }}'
 

+ 13 - 1
CHANGES.md

@@ -1,6 +1,18 @@
 # CHANGES
 
-## v4.2.14-RC
+## v4.2.15-EC
+
+* Improvement: toastr location for editing
+* Improvement: Handsontable with static backdrop to prevent from closing when backdrop is clicked
+* Fix: Accept invalid page path like `..%2f`
+* Fix: Pages updated date is corrupted after recursive operation
+    * Introduced by v4.2.8
+* Support: Upgrade libs
+    * reactstrap
+
+
+
+## v4.2.14
 
 * Feature: Add an option to restrict publishing email property for new users
 * Improvement: Invite modal in admin page without email server settings

+ 54 - 74
README.md

@@ -1,3 +1,5 @@
+- [日本語 🇯🇵](./README_JP.md)
+
 <p align="center">
   <a href="https://growi.org">
     <img src="https://user-images.githubusercontent.com/1638767/38254268-d4476bbe-3793-11e8-964c-8865d690baff.png" width="240px">
@@ -12,52 +14,46 @@
   <a href="https://docs.growi.org">Documentation</a> / <a href="https://demo.growi.org">Demo</a>
 </p>
 
-
-GROWI
-===========
+# GROWI
 
 [![Actions Status](https://github.com/weseek/growi/workflows/Node%20CI/badge.svg)](https://github.com/weseek/growi/actions)
 [![dependencies status](https://david-dm.org/weseek/growi.svg)](https://david-dm.org/weseek/growi)
 [![devDependencies Status](https://david-dm.org/weseek/growi/dev-status.svg)](https://david-dm.org/weseek/growi?type=dev)
 [![docker pulls](https://img.shields.io/docker/pulls/weseek/growi.svg)](https://hub.docker.com/r/weseek/growi/)
 
-| demonstration |
-| :-: |
-|![sample image](https://user-images.githubusercontent.com/42988650/70600974-6b29cc80-1c34-11ea-94ef-33c39c6a00dc.gif)|
+|                                                     demonstration                                                     |
+| :-------------------------------------------------------------------------------------------------------------------: |
+| ![sample image](https://user-images.githubusercontent.com/42988650/70600974-6b29cc80-1c34-11ea-94ef-33c39c6a00dc.gif) |
 
-Table Of Contents
----------------
+## Table Of Contents
 
 - [Features](#features)
 - [Quick Start for Production](#quick-start-for-production)
-    - [docker-compose](#docker-compose)
-    - [Helm (Experimental)](#helm-experimental)
-    - [On-premise](#on-premise)
+  - [docker-compose](#docker-compose)
+  - [Helm (Experimental)](#helm-experimental)
+  - [On-premise](#on-premise)
 - [Environment Variables](#environment-variables)
 - [Documentation](#documentation)
 - [License](#license)
 
-Features
-========
-
-* **Features**
-    * Create hierarchical pages with markdown -> [HERE](https://docs.growi.org/en/guide/getting-started/five_minutes.html) is 5 minutes tutorial
-    * Simultaneously edit with multiple people by [HackMD(CodiMD)](https://hackmd.io/) integration
-        * [GROWI Docs: HackMD(CodiMD) Integration](https://docs.growi.org/en/admin-guide/admin-cookbook/integrate-with-hackmd.html)
-    * Support Authentication with LDAP / Active Directory, OAuth
-    * SSO(Single Sign On) with SAML
-    * Slack/Mattermost, IFTTT Integration
-    * [GROWI Docs: Features](https://docs.growi.org/en/guide/features/page_layout.html)
-* **Pluggable**
-    * You can find plugins from [npm](https://www.npmjs.com/browse/keyword/growi-plugin) or [github](https://github.com/search?q=topic%3Agrowi-plugin)!
-* **[Docker Ready][dockerhub]**
-* **[Docker Compose Ready][docker-compose]**
-    * [GROWI Docs: Multiple sites](https://docs.growi.org/en/admin-guide/admin-cookbook/multi-app.html)
-    * [GROWI Docs: HTTPS(with Let's Encrypt) proxy integration](https://docs.growi.org/en/admin-guide/admin-cookbook/lets-encrypt.html)
-
-Quick Start for Production
-===========================
-
+# Features
+
+- **Features**
+  - Create hierarchical pages with markdown -> [HERE](https://docs.growi.org/en/guide/getting-started/five_minutes.html) is 5 minutes tutorial
+  - Simultaneously edit with multiple people by [HackMD(CodiMD)](https://hackmd.io/) integration
+    - [GROWI Docs: HackMD(CodiMD) Integration](https://docs.growi.org/en/admin-guide/admin-cookbook/integrate-with-hackmd.html)
+  - Support Authentication with LDAP / Active Directory, OAuth
+  - SSO(Single Sign On) with SAML
+  - Slack/Mattermost, IFTTT Integration
+  - [GROWI Docs: Features](https://docs.growi.org/en/guide/features/page_layout.html)
+- **Pluggable**
+  - You can find plugins from [npm](https://www.npmjs.com/browse/keyword/growi-plugin) or [github](https://github.com/search?q=topic%3Agrowi-plugin)!
+- **[Docker Ready][dockerhub]**
+- **[Docker Compose Ready][docker-compose]**
+  - [GROWI Docs: Multiple sites](https://docs.growi.org/en/admin-guide/admin-cookbook/multi-app.html)
+  - [GROWI Docs: HTTPS(with Let's Encrypt) proxy integration](https://docs.growi.org/en/admin-guide/admin-cookbook/lets-encrypt.html)
+
+# Quick Start for Production
 
 ### docker-compose
 
@@ -74,9 +70,7 @@ Quick Start for Production
 - [GROWI Docs: Install on Ubuntu Server](https://docs.growi.org/en/admin-guide/getting-started/ubuntu-server.html)
 - [GROWI Docs: Install on CentOS](https://docs.growi.org/en/admin-guide/getting-started/centos.html)
 
-
-Configuration
-------------
+## Configuration
 
 See [GROWI Docs: Admin Guide](https://docs.growi.org/en/admin-guide/) ([en](https://docs.growi.org/en/admin-guide/)/[ja](https://docs.growi.org/ja/admin-guide/)).
 
@@ -84,9 +78,7 @@ See [GROWI Docs: Admin Guide](https://docs.growi.org/en/admin-guide/) ([en](http
 
 See [GROWI Docs: Environment Variables](https://docs.growi.org/en/admin-guide/admin-cookbook/env-vars.html) ([en](https://docs.growi.org/en/admin-guide/admin-cookbook/env-vars.html)/[ja](https://docs.growi.org/ja/admin-guide/admin-cookbook/env-vars.html)).
 
-
-Development
-==========
+# Development
 
 ## Dependencies
 
@@ -95,72 +87,60 @@ Development
 - yarn
 - MongoDB 4.x
 
-See [confirmed versions](https://docs.growi.org/en/dev/startup/dev-env.html#set-up-node-js-environment).
-
 ### Optional Dependencies
 
 - Redis 3.x
 - ElasticSearch 6.x (needed when using Full-text search)
-    - **CAUTION: Following plugins are required**
-        - [Japanese (kuromoji) Analysis plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html)
-        - [ICU Analysis Plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-icu.html)
+  - **CAUTION: Following plugins are required**
+    - [Japanese (kuromoji) Analysis plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html)
+    - [ICU Analysis Plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-icu.html)
 
 ## Command details
 
-|command|desc|
-|--|--|
-|`yarn run build:prod`|Build the client|
-|`yarn run server:prod`|Launch the server|
-|`yarn start`|Invoke `yarn run build:prod` and `yarn run server:prod`|
+| command                | desc                                                    |
+| ---------------------- | ------------------------------------------------------- |
+| `yarn run build:prod`  | Build the client                                        |
+| `yarn run server:prod` | Launch the server                                       |
+| `yarn start`           | Invoke `yarn run build:prod` and `yarn run server:prod` |
 
-For more info, see [GROWI Docs: List of npm Commands](https://docs.growi.org/en/dev/startup-v2/launch.html#list-of-npm-commands).
+<!-- The following links do not exist -->
 
+For more info, see [GROWI Docs: List of npm Commands](https://docs.growi.org/en/dev/startup-v2/launch-system.html#list-of-npm-commands).
 
-Documentation
-==============
+# Documentation
 
 - [GROWI Docs](https://docs.growi.org/)
 - [GROWI Developers Wiki (ja)](https://dev.growi.org/)
 
+# Contribution
 
-Contribution
-============
-
-Found a Bug?
--------------
+## Found a Bug?
 
 If you found a bug in the source code, you can help us by
 [submitting an issue][issues] to our [GitHub Repository][growi]. Even better, you can
 [submit a Pull Request][pulls] with a fix.
 
-Missing a Feature?
--------------------
+## Missing a Feature?
 
-You can *request* a new feature by [submitting an issue][issues] to our GitHub
-Repository. If you would like to *implement* a new feature, firstly please submit the issue with your proposal to make sure we can confirm it. Please clarify what kind of change you would like to propose.
+You can _request_ a new feature by [submitting an issue][issues] to our GitHub
+Repository. If you would like to _implement_ a new feature, firstly please submit the issue with your proposal to make sure we can confirm it. Please clarify what kind of change you would like to propose.
 
-* For a **Major Feature**, firstly open an issue and outline your proposal so it can be discussed.  
-It also allows us to coordinate better, prevent duplication of work and help you to create the change so it can be successfully accepted into the project.
-* **Small Features** can be created and directly [submitted as a Pull Request][pulls].
+- For a **Major Feature**, firstly open an issue and outline your proposal so it can be discussed.  
+  It also allows us to coordinate better, prevent duplication of work and help you to create the change so it can be successfully accepted into the project.
+- **Small Features** can be created and directly [submitted as a Pull Request][pulls].
 
-
-Language on GitHub
-------------------
+## Language on GitHub
 
 You can write issues and PRs in English or Japanese.
 
-Discussion
------------
+## Discussion
 
 If you have questions or suggestions, you can [join our Slack team](https://growi-slackin.weseek.co.jp/) and talk about anything, anytime.
 
+# License
 
-License
-=======
-
-* The MIT License (MIT)
-* See [LICENSE](https://github.com/weseek/growi/blob/master/LICENSE) and [THIRD-PARTY-NOTICES.md](https://github.com/weseek/growi/blob/master/THIRD-PARTY-NOTICES.md).
-
+- The MIT License (MIT)
+- See [LICENSE](https://github.com/weseek/growi/blob/master/LICENSE) and [THIRD-PARTY-NOTICES.md](https://github.com/weseek/growi/blob/master/THIRD-PARTY-NOTICES.md).
 
 [crowi]: https://github.com/crowi/crowi
 [growi]: https://github.com/weseek/growi

+ 148 - 0
README_JP.md

@@ -0,0 +1,148 @@
+- [English 🇺🇸](./README.md)
+  <p align="center">
+    <a href="https://growi.org">
+      <img src="https://user-images.githubusercontent.com/1638767/38254268-d4476bbe-3793-11e8-964c-8865d690baff.png" width="240px">
+    </a>
+  </p>
+  <p align="center">
+    <a href="https://github.com/weseek/growi/releases/latest"><img src="https://img.shields.io/github/release/weseek/growi.svg"></a>
+    <a href="https://growi-slackin.weseek.co.jp/"><img src="https://growi-slackin.weseek.co.jp/badge.svg"></a>
+  </p>
+
+<p align="center">
+  <a href="https://docs.growi.org">ドキュメント</a> / <a href="https://demo.growi.org">デモ</a>
+</p>
+
+# GROWI
+
+[![Actions Status](https://github.com/weseek/growi/workflows/Node%20CI/badge.svg)](https://github.com/weseek/growi/actions)
+[![dependencies status](https://david-dm.org/weseek/growi.svg)](https://david-dm.org/weseek/growi)
+[![devDependencies Status](https://david-dm.org/weseek/growi/dev-status.svg)](https://david-dm.org/weseek/growi?type=dev)
+[![docker pulls](https://img.shields.io/docker/pulls/weseek/growi.svg)](https://hub.docker.com/r/weseek/growi/)
+
+|                                                 デモンストレーション                                                 |
+| :-------------------------------------------------------------------------------------------------------------------: |
+| ![sample image](https://user-images.githubusercontent.com/42988650/70600974-6b29cc80-1c34-11ea-94ef-33c39c6a00dc.gif) |
+
+## 目次
+
+- [機能紹介](#機能紹介)
+- [クイックスタート](#クイックスタート)
+  - [docker-compose を使ってはじめる](#docker-compose-を使ってはじめる)
+  - [Helm (Experimental) でデプロイする](#Helm-Experimental-でデプロイする)
+  - [オンプレミス](#オンプレミスではじめる)
+- [環境変数](#環境変数)
+- [ドキュメント](#ドキュメント)
+- [ライセンス](#ライセンス)
+
+# 機能紹介
+
+- **主な機能**
+  - マークダウンを使用してページを階層構造で作成することが可能です。 -> 5 分間チュートリアルは[こちら](https://docs.growi.org/ja/guide/getting-started/five_minutes.html))。
+  - HackMD(CodiMd)[https://hackmd.io/] と連携することで同時多人数編集が可能です。
+    - [GROWI Docs: HackMD(CodiMD) 連携](https://docs.growi.org/ja/admin-guide/admin-cookbook/integrate-with-hackmd.html)
+  - LDAP / Active Direcotry , OAuth 認証をサポートしています。
+  - SAML を用いた Single Sign On が可能です。
+  - Slack / Mattermost, IFTTT と連携することが可能です。
+  - [GROWI Docs: 機能紹介](https://docs.growi.org/ja/guide/features/page_layout.html)
+- **プラグイン**
+  - [npm](https://www.npmjs.com/browse/keyword/growi-plugin) または [github](https://github.com/search?q=topic%3Agrowi-plugin) から 便利なプラグインを見つけることができます。
+- **[Docker の準備][dockerhub]**
+- **[Docker Compose の準備][docker-compose]**
+  - [GROWI Docs: 複数の GROWI を起動](https://docs.growi.org/ja/admin-guide/admin-cookbook/multi-app.html)
+  - [GROWI Docs: Let's Encrypt による HTTPS 運用](https://docs.growi.org/ja/admin-guide/admin-cookbook/lets-encrypt.html)
+
+# クイックスタート
+
+### docker-compose を使ってはじめる
+
+- [GROWI Docs: docker-compose](https://docs.growi.org/ja/admin-guide/getting-started/docker-compose.html) ([en](https://docs.growi.org/en/admin-guide/getting-started/docker-compose.html)/[ja](https://docs.growi.org/ja/admin-guide/getting-started/docker-compose.html))
+
+### Helm (Experimental) でデプロイする
+
+- [GROWI Helm Chart](https://github.com/weseek/helm-charts/tree/master/charts/growi)
+
+### オンプレミスではじめる
+
+Crowi からの移行は **[こちら](https://docs.growi.org/en/admin-guide/migration-guide/from-crowi-onpremise.html) ([en](https://docs.growi.org/en/admin-guide/migration-guide/from-crowi-onpremise.html)/[ja](https://docs.growi.org/ja/admin-guide/migration-guide/from-crowi-onpremise.html))**。
+
+- [GROWI Docs: Ubuntu Server 上でインストール](https://docs.growi.org/ja/admin-guide/getting-started/ubuntu-server.html)
+- [GROWI Docs: CentOS 上でインストール](https://docs.growi.org/ja/admin-guide/getting-started/centos.html)
+
+## 設定
+
+[GROWI Docs: 管理者ガイド](https://docs.growi.org/ja/admin-guide/) ([en](https://docs.growi.org/en/admin-guide/)/[ja](https://docs.growi.org/ja/admin-guide/))をご覧ください。
+
+### 環境変数
+
+[GROWI Docs: 環境変数](https://docs.growi.org/ja/admin-guide/admin-cookbook/env-vars.html) ([en](https://docs.growi.org/en/admin-guide/admin-cookbook/env-vars.html)/[ja](https://docs.growi.org/ja/admin-guide/admin-cookbook/env-vars.html)) をご覧ください。
+
+# 開発環境
+
+## 依存関係
+
+- Node.js v12.x or v14.x
+- npm 6.x
+- yarn
+- MongoDB 4.x
+
+### オプションの依存関係
+
+- Redis 3.x
+- ElasticSearch 6.x (needed when using Full-text search)
+  - **注意: 次のプラグインが必要です**
+    - [Japanese (kuromoji) Analysis plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html)
+    - [ICU Analysis Plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-icu.html)
+
+## コマンド詳細
+
+| コマンド               | 説明                                                             |
+| ---------------------- | ---------------------------------------------------------------- |
+| `yarn run build:prod`  | クライアントをビルドします。                                     |
+| `yarn run server:prod` | サーバーを起動します。                                           |
+| `yarn start`           | `yarn run build:prod` と `yarn run server:prod` を呼び出します。 |
+
+  <!-- 以下のリンクは存在しない (ja と en 両方) -->
+
+詳しくは [GROWI Docs: List of npm Commands](https://docs.growi.org/ja/dev/startup-v2/launch-system.html#npm-コマンドリスト)をご覧ください。
+
+# ドキュメント
+
+- [GROWI Docs](https://docs.growi.org/)
+- [GROWI Developers Wiki](https://dev.growi.org/)
+
+# コントリビューション
+
+## バグがありましたか?
+
+ソースコード上でバグを発見されたら、私たちの GitHub 上の Repository にて Issue を作成していただけると助かります。バグを修正して Pull requests を提出していただけるとさらに助かります。
+
+## 欲しい機能が見あたりませんか?
+
+私たちの GitHub 上の リポジトリに Issue を出して、新しい機能をリクエストすることができます。新機能を実装したい場合も同様に、まずは Issue を提出してください。どのような新機能や変更を提案されるのかを明確にしていただきます。
+
+- **大規模な機能追加につきましては**、Issue を open にした上で、提案された概要を説明していただき、議論できる状態にします。
+  議論を積み重ねることで、提案内容を双方向的に実装したい機能を整理することができ、実装の重複を防ぐことにもなります。これによって、GROWI への導入がスムーズになります。
+
+- **小規模な機能追加につきましては**、 Issue を作成し、直接 [Pull requests][pulls] を提出してください。
+
+## GitHub 上での言語について
+
+Issue と Pull requests の作成は英語・日本語どちらでも受け付けています。
+
+## GROWI について話し合いましょう!
+
+質問や提案があれば、私たちの [Slack team](https://growi-slackin.weseek.co.jp/) にぜひご参加ください。
+いつでも、どこでも GROWI について議論しましょう!
+
+# ライセンス
+
+- The MIT License (MIT)
+- [ライセンス](https://github.com/weseek/growi/blob/master/LICENSE) と [THIRD-PARTY-NOTICES.md](https://github.com/weseek/growi/blob/master/THIRD-PARTY-NOTICES.md) をご覧ください。
+
+  [crowi]: https://github.com/crowi/crowi
+  [growi]: https://github.com/weseek/growi
+  [issues]: https://github.com/weseek/growi/issues
+  [pulls]: https://github.com/weseek/growi/pulls
+  [dockerhub]: https://hub.docker.com/r/weseek/growi
+  [docker-compose]: https://github.com/weseek/growi-docker-compose

+ 3 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "4.2.14-RC",
+  "version": "4.2.15-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -207,6 +207,7 @@
     "i18next-browser-languagedetector": "^4.0.1",
     "imports-loader": "^0.8.0",
     "jest": "^25.1.0",
+    "jest-date-mock": "^1.0.8",
     "jquery-slimscroll": "^1.3.8",
     "jquery-ui": "^1.12.1",
     "jquery.cookie": "~1.4.1",
@@ -245,7 +246,7 @@
     "react-hotkeys": "^2.0.0",
     "react-i18next": "^11.1.0",
     "react-waypoint": "^9.0.0",
-    "reactstrap": "^8.0.1",
+    "reactstrap": "^8.9.0",
     "replacestream": "^4.0.3",
     "reveal.js": "^3.5.0",
     "rs-i18n": "^0.0.9",

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

@@ -428,7 +428,14 @@ export default class HandsontableModal extends React.PureComponent {
     );
 
     return (
-      <Modal isOpen={this.state.show} toggle={this.cancel} size="lg" className={`handsontable-modal ${this.state.isWindowExpanded && 'grw-modal-expanded'}`}>
+      <Modal
+        isOpen={this.state.show}
+        toggle={this.cancel}
+        backdrop="static"
+        keyboard={false}
+        size="lg"
+        className={`handsontable-modal ${this.state.isWindowExpanded && 'grw-modal-expanded'}`}
+      >
         <ModalHeader tag="h4" toggle={this.cancel} close={buttons} className="bg-primary text-light">
           Edit Table
         </ModalHeader>

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

@@ -76,6 +76,10 @@ body.on-edit {
     display: none;
   }
 
+  .toast-top-right {
+    top: 64px;
+  }
+
   /*****************
    * Expand Editor
    *****************/

+ 13 - 0
src/client/styles/scss/_override-bootstrap.scss

@@ -133,6 +133,19 @@
     border-top: 1px solid #e5e5e5;
   }
 
+  // When fading in the modal, animate it to slide down
+  .modal.fade .modal-dialog {
+    @include transition($modal-transition);
+    transform: $modal-fade-transform;
+  }
+  .modal.show .modal-dialog {
+    transform: $modal-show-transform;
+  }
+  // When trying to close, animate focus to scale
+  .modal.modal-static .modal-dialog {
+    transform: $modal-scale-transform;
+  }
+
   // col-form-label (substitute for control-label of bootstrap3)
   .col-form-label {
     text-align: right;

+ 40 - 0
src/migrations/2021042016038-convert-double-to-date.js

@@ -0,0 +1,40 @@
+require('module-alias/register');
+const logger = require('@alias/logger')('growi:migrate:convert-double-to-date');
+
+const mongoose = require('mongoose');
+const config = require('@root/config/migrate');
+
+const { getModelSafely } = require('@commons/util/mongoose-utils');
+
+module.exports = {
+  async up(db) {
+    logger.info('Apply migration');
+    mongoose.connect(config.mongoUri, config.mongodb.options);
+
+    const Page = getModelSafely('Page') || require('@server/models/page')();
+
+    const pages = await Page.find({ updatedAt: { $type: 'double' } });
+
+    if (pages.length === 0) {
+      return logger.info('The target page did not exist.');
+    }
+
+    const operations = pages.map((page) => {
+      return {
+        updateMany: {
+          filter: { _id: page._id },
+          update: { updatedAt: new Date(page.updatedAt) },
+        },
+      };
+    });
+
+    await Page.bulkWrite(operations);
+
+    logger.info('Migration has successfully applied');
+
+  },
+
+  down(db) {
+    // do not rollback
+  },
+};

+ 2 - 0
src/server/models/page.js

@@ -533,6 +533,8 @@ module.exports = function(crowi) {
       /\s+\/\s+/, // avoid miss in renaming
       /.+\/edit$/,
       /.+\.md$/,
+      /^(\.\.)$/, // see: https://github.com/weseek/growi/issues/3582
+      /(\/\.\.)\/?/, // see: https://github.com/weseek/growi/issues/3582
       /^\/(installer|register|login|logout|admin|me|files|trash|paste|comments|tags|share)(\/.*|$)/,
     ];
 

+ 3 - 1
src/server/service/page.js

@@ -103,7 +103,9 @@ class PageService {
       const revisionId = new mongoose.Types.ObjectId();
 
       if (updateMetadata) {
-        unorderedBulkOp.find({ _id: page._id }).update({ $set: { path: newPagePath, lastUpdateUser: user._id, updatedAt:  Date.now() } });
+        unorderedBulkOp
+          .find({ _id: page._id })
+          .update({ $set: { path: newPagePath, lastUpdateUser: user._id, updatedAt: new Date() } });
       }
       else {
         unorderedBulkOp.find({ _id: page._id }).update({ $set: { path: newPagePath } });

+ 6 - 0
src/test/models/page.test.js

@@ -193,6 +193,12 @@ describe('Page', () => {
 
       expect(Page.isCreatableName('/hoge/xx.md')).toBeFalsy();
 
+      // relative path
+      expect(Page.isCreatableName('/..')).toBeFalsy();
+      expect(Page.isCreatableName('/../page')).toBeFalsy();
+      expect(Page.isCreatableName('/page/..')).toBeFalsy();
+      expect(Page.isCreatableName('/page/../page')).toBeFalsy();
+
       // start with https?
       expect(Page.isCreatableName('/http://demo.growi.org/hoge')).toBeFalsy();
       expect(Page.isCreatableName('/https://demo.growi.org/hoge')).toBeFalsy();

+ 5 - 2
src/test/service/page.test.js

@@ -1,4 +1,6 @@
 /* eslint-disable no-unused-vars */
+import { advanceTo } from 'jest-date-mock';
+
 const mongoose = require('mongoose');
 
 const { getInstance } = require('../setup-crowi');
@@ -236,11 +238,12 @@ describe('PageService', () => {
   describe('rename page', () => {
     let pageEventSpy;
     let renameDescendantsWithStreamSpy;
-    const dateToUse = new Date('2000-01-01');
+    // mock new Date() and Date.now()
+    advanceTo(new Date(2000, 1, 1, 0, 0, 0));
+    const dateToUse = new Date();
     const socketClientId = null;
 
     beforeEach(async(done) => {
-      jest.spyOn(global.Date, 'now').mockImplementation(() => dateToUse);
       pageEventSpy = jest.spyOn(crowi.pageService.pageEvent, 'emit').mockImplementation();
       renameDescendantsWithStreamSpy = jest.spyOn(crowi.pageService, 'renameDescendantsWithStream').mockImplementation();
       done();

+ 87 - 35
yarn.lock

@@ -1123,12 +1123,12 @@
   dependencies:
     regenerator-runtime "^0.12.0"
 
-"@babel/runtime@^7.2.0":
-  version "7.6.2"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.2.tgz#c3d6e41b304ef10dcf13777a33e7694ec4a9a6dd"
-  integrity sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==
+"@babel/runtime@^7.12.5":
+  version "7.13.16"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.16.tgz#673e7b84c1f6287d96d483fa65cd3db792d53e22"
+  integrity sha512-7VsWJsI5USRhBLE/3of+VU2DDNWtYHQlq2IHu2iL15+Yx4qVqP8KllR6JMHQlTKWRyDk9Tw6unkqSusaHXt//A==
   dependencies:
-    regenerator-runtime "^0.13.2"
+    regenerator-runtime "^0.13.4"
 
 "@babel/runtime@^7.3.1":
   version "7.4.3"
@@ -1499,6 +1499,14 @@
   resolved "https://registry.yarnpkg.com/@handsontable/react/-/react-2.1.0.tgz#3b87ebfc0d5d47e1b0d07856bd473017a0a7179f"
   integrity sha512-Du73MFU2y1Bfe9m7mvxY70lB2R/VigFSpOwWZjDnUt/HwNPbNr+UQcY40w6u7acllQeee45H7jRdEExzsrvDKw==
 
+"@hypnosphi/create-react-context@^0.3.1":
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz#f8bfebdc7665f5d426cba3753e0e9c7d3154d7c6"
+  integrity sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==
+  dependencies:
+    gud "^1.0.0"
+    warning "^4.0.3"
+
 "@istanbuljs/load-nyc-config@^1.0.0":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b"
@@ -3571,6 +3579,14 @@ cacheable-request@^2.1.1:
     normalize-url "2.0.1"
     responselike "1.0.2"
 
+call-bind@^1.0.0, call-bind@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
+  integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
+  dependencies:
+    function-bind "^1.1.1"
+    get-intrinsic "^1.0.2"
+
 call-me-maybe@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
@@ -4867,6 +4883,18 @@ decompress-response@^3.3.0:
   dependencies:
     mimic-response "^1.0.0"
 
+deep-equal@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
+  integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
+  dependencies:
+    is-arguments "^1.0.4"
+    is-date-object "^1.0.1"
+    is-regex "^1.0.4"
+    object-is "^1.0.1"
+    object-keys "^1.1.1"
+    regexp.prototype.flags "^1.2.0"
+
 deep-extend@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
@@ -6689,6 +6717,15 @@ get-caller-file@^2.0.1:
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
   integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
 
+get-intrinsic@^1.0.2:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
+  integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
+  dependencies:
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.1"
+
 get-stdin@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
@@ -7073,6 +7110,11 @@ has-symbols@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
 
+has-symbols@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
+  integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
+
 has-to-string-tag-x@^1.2.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d"
@@ -7678,6 +7720,13 @@ is-alphanumerical@^1.0.0:
     is-alphabetical "^1.0.0"
     is-decimal "^1.0.0"
 
+is-arguments@^1.0.4:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9"
+  integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==
+  dependencies:
+    call-bind "^1.0.0"
+
 is-arrayish@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -8161,6 +8210,11 @@ jest-config@^25.1.0:
     pretty-format "^25.1.0"
     realpath-native "^1.1.0"
 
+jest-date-mock@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/jest-date-mock/-/jest-date-mock-1.0.8.tgz#13468c0352c5a3614c6b356dbc6b88eb37d9e0b3"
+  integrity sha512-0Lyp+z9xvuNmLbK+5N6FOhSiBeux05Lp5bbveFBmYo40Aggl2wwxFoIrZ+rOWC8nDNcLeBoDd2miQdEDSf3iQw==
+
 jest-diff@^25.1.0:
   version "25.1.0"
   resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.1.0.tgz#58b827e63edea1bc80c1de952b80cec9ac50e1ad"
@@ -9017,16 +9071,6 @@ lodash.isfinite@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3"
 
-lodash.isfunction@^3.0.9:
-  version "3.0.9"
-  resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
-  integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==
-
-lodash.isobject@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
-  integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=
-
 lodash.isplainobject@^4.0.6:
   version "4.0.6"
   resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
@@ -9050,11 +9094,6 @@ lodash.sortby@^4.7.0:
   resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
   integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
 
-lodash.tonumber@^4.0.3:
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/lodash.tonumber/-/lodash.tonumber-4.0.3.tgz#0b96b31b35672793eb7f5a63ee791f1b9e9025d9"
-  integrity sha1-C5azGzVnJ5Prf1pj7nkfG56QJdk=
-
 lodash.unescape@4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
@@ -10536,6 +10575,14 @@ object-inspect@^1.6.0:
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
   integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==
 
+object-is@^1.0.1:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
+  integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+
 object-keys@^1.0.11, object-keys@^1.0.8:
   version "1.0.11"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
@@ -12333,13 +12380,14 @@ react-popper@^1.0.0:
     typed-styles "^0.0.7"
     warning "^4.0.2"
 
-react-popper@^1.3.3:
-  version "1.3.4"
-  resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.4.tgz#f0cd3b0d30378e1f663b0d79bcc8614221652ced"
-  integrity sha512-9AcQB29V+WrBKk6X7p0eojd1f25/oJajVdMZkywIoAV6Ag7hzE1Mhyeup2Q1QnvFRtGQFQvtqfhlEoDAPfKAVA==
+react-popper@^1.3.6:
+  version "1.3.11"
+  resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.11.tgz#a2cc3f0a67b75b66cfa62d2c409f9dd1fcc71ffd"
+  integrity sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==
   dependencies:
     "@babel/runtime" "^7.1.2"
-    create-react-context "^0.3.0"
+    "@hypnosphi/create-react-context" "^0.3.1"
+    deep-equal "^1.1.1"
     popper.js "^1.14.4"
     prop-types "^15.6.1"
     typed-styles "^0.0.7"
@@ -12408,19 +12456,15 @@ react@^16.8.3:
     prop-types "^15.6.2"
     scheduler "^0.13.3"
 
-reactstrap@^8.0.1:
-  version "8.0.1"
-  resolved "https://registry.yarnpkg.com/reactstrap/-/reactstrap-8.0.1.tgz#0b663c8195f540bc1d6d5dbcbcf73cab56fe7c79"
-  integrity sha512-GvUWEL+a2+3npK1OxTXcNBMHXX4x6uc1KQRzK7yAOl+8sAHTRWqjunvMUfny3oDh8yKVzgqpqQlWWvs1B2HR9A==
+reactstrap@^8.9.0:
+  version "8.9.0"
+  resolved "https://registry.yarnpkg.com/reactstrap/-/reactstrap-8.9.0.tgz#bca4afa3f5cd18899ef9b33d877a141886d5abae"
+  integrity sha512-pmf33YjpNZk1IfrjqpWCUMq9hk6GzSnMWBAofTBNIRJQB1zQ0Au2kzv3lPUAFsBYgWEuI9iYa/xKXHaboSiMkQ==
   dependencies:
-    "@babel/runtime" "^7.2.0"
+    "@babel/runtime" "^7.12.5"
     classnames "^2.2.3"
-    lodash.isfunction "^3.0.9"
-    lodash.isobject "^3.0.2"
-    lodash.tonumber "^4.0.3"
     prop-types "^15.5.8"
-    react-lifecycles-compat "^3.0.4"
-    react-popper "^1.3.3"
+    react-popper "^1.3.6"
     react-transition-group "^2.3.1"
 
 read-pkg-up@^1.0.1:
@@ -12702,6 +12746,14 @@ regexp-tree@^0.1.6:
   resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.10.tgz#d837816a039c7af8a8d64d7a7c3cf6a1d93450bc"
   integrity sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==
 
+regexp.prototype.flags@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26"
+  integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+
 regexpp@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"