Browse Source

Merge pull request #152 from weseek/master

release v2.0.7
Yuki Takei 8 years ago
parent
commit
6a2983d9bc

+ 6 - 1
CHANGES.md

@@ -1,6 +1,11 @@
 CHANGES
 ========
 
+## 2.0.7
+
+* Improvement: Add recursively option for Delete/Move/Putback operation
+* Improvement: Comment layout and sort order (crowi-plus Enhanced Layout)
+
 ## 2.0.6
 
 * Fix: check whether `$APP_DIR/public/uploads` exists before creating symlink
@@ -9,7 +14,7 @@ CHANGES
 ## 2.0.5
 
 * Improvement: Adjust styles for CodeMirror
-* Fix: File upload does not work when `FILE_UPLOAD=local` is set  
+* Fix: File upload does not work when using crowi-plus-docker-compose and `FILE_UPLOAD=local` is set  
     * Fixed in weseek/crowi-plus-docker
 
 ## 2.0.2 - 2.0.4 (Missing number)

+ 16 - 44
README.md

@@ -79,6 +79,8 @@ On-premise
 - yarn
 - MongoDB 3.x
 
+See [confirmed versions](https://github.com/weseek/crowi-plus/wiki/Developers-Guide#versions-confirmed-to-work).
+
 #### Optional Dependencies
 
 - Redix 3.x
@@ -88,6 +90,8 @@ On-premise
 
 ### Start
 
+## Build and run the app
+
 ```bash
 git clone https://github.com/weseek/crowi-plus.git
 cd crowi-plus
@@ -95,7 +99,7 @@ yarn
 MONGO_URI=mongodb://MONGO_HOST:MONGO_PORT/crowi npm start
 ```
 
-`npm start` lauches the server after building the client.  
+**DON'T USE `npm install`**, use `yarn` instead.
 
 If you launch crowi-plus with Redis and ElasticSearch, add environment variables before `npm start` like following:
 
@@ -108,12 +112,13 @@ npm start
 
 For more info, check [the official documents](https://github.com/crowi/crowi/wiki/Install-and-Configuration#env-parameters).
 
-#### Other commands
+#### Command details
 
 |command|desc|
 |--|--|
 |`npm run build:prod`|Build the client|
 |`npm run server:prod`|Launch the server|
+|`npm start`|Invoke `npm run build:prod` and `npm run server:prod`|
 
 ### Upgrade
 
@@ -141,51 +146,18 @@ npm start
 Getting Started to Develop
 ==========================
 
-## Dependencies
-
-- node 6.x (DON'T USE 7.x)
-- npm 4.x
-- yarn
-
-* following environment is confirmed to work
-
-    ```bash
-    $ node -v
-    v6.10.0
-    
-    $ npm -v
-    4.6.1
-    
-    $ yarn --version
-    0.24.5
-    ```
-
 ## Build and Running the app
-* `clone` this repository
-* `npm install -g npm@4` to install required global dependencies
-* `npm install` to install all dependencies or `yarn`
-* `npm run build` to build client app
-* `npm run server` to start the dev server in another tab
-
-After you have installed all dependencies and build client you can now run the app. Run `npm run server` to start a local server using `node-dev` which will watch server-side codes and reload for you. The port will be displayed to you as `http://0.0.0.0:3000`.
-
 
-List of npm commands
-=====================
+1. `clone` this repository
+1. `yarn global add npm@4` to install required global dependencies
+1. `yarn` to install all dependencies
+    * DON'T USE `npm install`
+1. `npm run build` to build client app
+1. `npm run server` to start the dev server in another tab
 
-e.g. `npm run build`
+After you have installed all dependencies and build client you can now run the app. Run `npm run server` to start a local server using `node-dev` which will watch server-side codes and reload for you. The port will be displayed to you as `http://0.0.0.0:3000`.
 
-|command|desc|
-|--|--|
-|`build`|Same to `build:dev`|
-|`build:dev`|Build the client|
-|`build:dev:watch`|Watch and Re-build the client|
-|`build:prod`|Build the client for production|
-|`server`|Same to `server:dev:watch`|
-|`server:dev`|Launch the server|
-|`server:dev:watch`|Watch and Re-start the server|
-|`server:prod`|Launch the server for production|
-|`start`|run `build:prod` and `server:prod`|
+For more info, read [Developers Guide](https://github.com/weseek/crowi-plus/wiki/Developers-Guide) on Wiki.
 
 
 Documentation
@@ -193,7 +165,7 @@ Documentation
 
 * [github wiki pages](https://github.com/weseek/crowi-plus/wiki)
   * [Questions and Answers](https://github.com/weseek/crowi-plus/wiki/Questions-and-Answers)
-
+* [Developers Guide](https://github.com/weseek/crowi-plus/wiki/Developers-Guide)
 
 Contributing
 ============

+ 1 - 1
lib/crowi/dev.js

@@ -48,7 +48,7 @@ class CrowiDev {
       orgCreateConnection.call(this, socket);
 
       socket.on('error', (err) => {
-        console.warn(`[WARN] Worthless error in client socket: '${err}'`);
+        console.warn(`[WARN] An insignificant error occured in client socket: '${err}'`);
       });
     }
   }

+ 27 - 5
lib/locales/en-US/translation.json

@@ -147,13 +147,35 @@
       }
   },
 
-  "Rename page": "Rename page",
-  "New page name": "New page name",
-  "Current page name": "Current page name",
-  "Redirect": "Redirect",
   "modal_rename": {
+    "label": {
+      "Rename page": "Rename page",
+      "New page name": "New page name",
+      "Current page name": "Current page name",
+      "Move recursively": "Move recursively",
+      "Redirect": "Redirect"
+    },
     "help": {
-      "redirect": "Redirect to new page if someone accesses <code>%s</code>"
+      "redirect": "Redirect to new page if someone accesses <code>%s</code>",
+      "recursive": "Rename children of under <code>%s</code> recursively"
+    }
+  },
+
+  "Put Back": "Put Back",
+  "Delete Completely": "Delete Completely",
+
+  "modal_delete": {
+    "label": {
+      "Delete Page": "Delete Page",
+      "recursively": "Process recursively",
+      "completely": "Delete completely"
+    }
+  },
+
+  "modal_putback": {
+    "label": {
+      "Put Back Page": "Put Back Page",
+      "recursively": "Process recursively"
     }
   },
 

+ 27 - 5
lib/locales/ja/translation.json

@@ -146,13 +146,35 @@
       }
   },
 
-  "Rename page": "ページを移動する",
-  "New page name": "移動先のページ名",
-  "Current page name": "現在のページ名",
-  "Redirect": "リダイレクトする",
   "modal_rename": {
+    "label": {
+      "Rename page": "ページを移動する",
+      "New page name": "移動先のページ名",
+      "Current page name": "現在のページ名",
+      "Move recursively": "再帰的に移動",
+      "Redirect": "リダイレクトする"
+    },
     "help": {
-      "redirect": "チェックを入れると、<code>%s</code>にアクセスされた際に自動的に新しいページにジャンプします。"
+      "redirect": "<code>%s</code> にアクセスされた際に自動的に新しいページにジャンプします",
+      "recursive": "<code>%s</code> 配下のページも移動します"
+    }
+  },
+
+  "Put Back": "元に戻す",
+  "Delete Completely": "完全削除",
+
+  "modal_delete": {
+    "label": {
+      "Delete Page": "ページを削除する",
+      "recursively": "全ての子ページも処理",
+      "completely": "完全削除"
+    }
+  },
+
+  "modal_putBack": {
+    "label": {
+      "Put Back Page": "ページを元に戻す",
+      "recursively": "全ての子ページも処理"
     }
   },
 

+ 83 - 0
lib/models/page.js

@@ -881,6 +881,27 @@ module.exports = function(crowi) {
     }
   };
 
+  pageSchema.statics.deletePageRecursively = function (pageData, user, options) {
+    var Page = this
+      , path = pageData.path
+      , options = options || {}
+      ;
+
+    return new Promise(function (resolve, reject) {
+      Page
+      .generateQueryToListByStartWith(path, user, options)
+      .then(function (pages) {
+        Promise.all(pages.map(function (page) {
+          return Page.deletePage(page, user, options);
+        }))
+        .then(function (data) {
+          return resolve(pageData);
+        });
+      });
+    });
+
+  };
+
   pageSchema.statics.revertDeletedPage = function(pageData, user, options) {
     var Page = this
       , newPath = Page.getRevertDeletedPageName(pageData.path)
@@ -911,6 +932,26 @@ module.exports = function(crowi) {
     });
   };
 
+  pageSchema.statics.revertDeletedPageRecursively = function (pageData, user, options) {
+    var Page = this
+      , path = pageData.path
+      , options = options || { includeDeletedPage: true}
+      ;
+
+      return new Promise(function (resolve, reject) {
+        Page
+        .generateQueryToListByStartWith(path, user, options)
+        .then(function (pages) {
+          Promise.all(pages.map(function (page) {
+            return Page.revertDeletedPage(page, user, options);
+          }))
+          .then(function (data) {
+            return resolve(data[0]);
+          });
+        });
+      });
+    };
+
   /**
    * This is danger.
    */
@@ -946,6 +987,27 @@ module.exports = function(crowi) {
     });
   };
 
+  pageSchema.statics.completelyDeletePageRecursively = function (pageData, user, options) {
+    // Delete Bookmarks, Attachments, Revisions, Pages and emit delete
+    var Page = this
+      , path = pageData.path
+      , options = options || { includeDeletedPage: true }
+      ;
+
+    return new Promise(function (resolve, reject) {
+      Page
+      .generateQueryToListByStartWith(path, user, options)
+      .then(function (pages) {
+        Promise.all(pages.map(function (page) {
+          return Page.completelyDeletePage(page, user, options);
+        }))
+        .then(function (data) {
+          return resolve(data[0]);
+        });
+      });
+    });
+  };
+
   pageSchema.statics.removePageById = function(pageId) {
     var Page = this;
 
@@ -1025,6 +1087,27 @@ module.exports = function(crowi) {
     });
   };
 
+  pageSchema.statics.renameRecursively = function(pageData, newPagePathPrefix, user, options) {
+    var Page = this
+      , path = pageData.path
+      , pathRegExp = new RegExp('^' + path, 'i');
+
+    return new Promise(function(resolve, reject) {
+      Page
+      .generateQueryToListByStartWith(path, user, options)
+      .then(function(pages) {
+        Promise.all(pages.map(function(page) {
+          newPagePath = page.path.replace(pathRegExp, newPagePathPrefix);
+          return Page.rename(page, newPagePath, user, options);
+        }))
+        .then(function() {
+          pageData.path = newPagePathPrefix;
+          return resolve();
+        });
+      });
+    });
+  };
+
   pageSchema.statics.getHistories = function() {
     // TODO
     return;

+ 32 - 5
lib/routes/page.js

@@ -1001,13 +1001,20 @@ module.exports = function(crowi, app) {
 
     // get completely flag
     const isCompletely = (req.body.completely !== undefined);
+    // get recursively flag
+    const isRecursively = (req.body.recursively !== undefined);
 
     Page.findPageByIdAndGrantedUser(pageId, req.user)
     .then(function(pageData) {
       debug('Delete page', pageData._id, pageData.path);
 
       if (isCompletely) {
-        return Page.completelyDeletePage(pageData, req.user);
+        if (isRecursively) {
+          return Page.completelyDeletePageRecursively(pageData, req.user);
+        }
+        else {
+          return Page.completelyDeletePage(pageData, req.user);
+        }
       }
 
       // else
@@ -1015,7 +1022,13 @@ module.exports = function(crowi, app) {
       if (!pageData.isUpdatable(previousRevision)) {
         throw new Error('Someone could update this page, so couldn\'t delete.');
       }
-      return Page.deletePage(pageData, req.user);
+
+      if (isRecursively) {
+        return Page.deletePageRecursively(pageData, req.user);
+      }
+      else {
+        return Page.deletePage(pageData, req.user);
+      }
     }).then(function(data) {
       debug('Page deleted', data.path);
       var result = {};
@@ -1038,11 +1051,18 @@ module.exports = function(crowi, app) {
   api.revertRemove = function(req, res){
     var pageId = req.body.page_id;
 
+    // get recursively flag
+    const isRecursively = (req.body.recursively !== undefined);
+
     Page.findPageByIdAndGrantedUser(pageId, req.user)
     .then(function(pageData) {
 
-      // TODO: これでいいんだっけ
-      return Page.revertDeletedPage(pageData, req.user);
+      if (isRecursively) {
+        return Page.revertDeletedPageRecursively(pageData, req.user);
+      }
+      else {
+        return Page.revertDeletedPage(pageData, req.user);
+      }
     }).then(function(data) {
       debug('Complete to revert deleted page', data.path);
       var result = {};
@@ -1074,6 +1094,7 @@ module.exports = function(crowi, app) {
       createRedirectPage: req.body.create_redirect || 0,
       moveUnderTrees: req.body.move_trees || 0,
     };
+    var isRecursiveMove = req.body.move_recursively || 0;
     var page = {};
 
     if (!Page.isCreatableName(newPagePath)) {
@@ -1093,7 +1114,13 @@ module.exports = function(crowi, app) {
           throw new Error('Someone could update this page, so couldn\'t delete.');
         }
 
-        return Page.rename(pageData, newPagePath, req.user, options);
+        if (isRecursiveMove) {
+          return Page.renameRecursively(pageData, newPagePath, req.user, options);
+        }
+        else {
+          return Page.rename(pageData, newPagePath, req.user, options);
+        }
+
       }).then(function() {
         var result = {};
         result.page = page;

+ 11 - 3
lib/views/modal/delete.html

@@ -6,7 +6,7 @@
 
         <div class="modal-header">
           <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-          <h4 class="modal-title"><i class="fa fa-trash-o"></i> Delete Page</h4>
+          <h4 class="modal-title"><i class="fa fa-trash-o"></i> {{ t('modal_delete.label.Delete Page') }}</h4>
         </div>
         <div class="modal-body">
           <div class="form-group">
@@ -21,9 +21,17 @@
           <input type="hidden" name="page_id" value="{{ page._id.toString() }}">
           <input type="hidden" name="revision_id" value="{{ page.revision._id.toString() }}">
           <label class="checkbox-inline text-danger">
-            <input type="checkbox" name="completely">completely
+            <input type="checkbox" name="recursively">{{ t('modal_delete.label.recursively') }}
           </label>
-          <button type="submit" class="btn btn-danger delete-button">Delete</button>
+          {% if page.isDeleted() %}
+            <input type="hidden" name="completely" value="true">
+            <button type="submit" class="btn btn-danger delete-button"><i class="fa fa-times-circle" aria-hidden="true"></i> {{ t('Delete Completely') }}</button>
+          {% else %}
+            <label class="checkbox-inline text-danger">
+              <input type="checkbox" name="completely">{{ t('modal_delete.label.completely') }}
+            </label>
+            <button type="submit" class="btn btn-danger delete-button">Delete</button>
+          {% endif %}
         </div>
 
       </form>

+ 37 - 0
lib/views/modal/put_back.html

@@ -0,0 +1,37 @@
+<div class="modal" id="putBackPage">
+  <div class="modal-dialog">
+    <div class="modal-content">
+
+      <form role="form" id="revert-delete-page-form" onsubmit="return false;">
+
+        <div class="modal-header">
+          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+          <h4 class="modal-title"><i class="fa fa-trash-o"></i> {{ t('modal_putBack.label.Put Back Page') }}</h4>
+        </div>
+        <div class="modal-body">
+          <div class="form-group">
+            <label for="">Put back page:</label><br>
+            <code>{{ page.path }}</code>
+          </div>
+        </div>
+        <div class="modal-footer">
+          <p><small class="pull-left" id="put_back-errors"></small></p>
+          <input type="hidden" name="_csrf" value="{{ csrf() }}">
+          <input type="hidden" name="path" value="{{ page.path }}">
+          <input type="hidden" name="page_id" value="{{ page._id.toString() }}">
+          <label class="checkbox-inline text-danger">
+            <input type="checkbox" name="recursively" checked> {{ t('modal_putBack.label.recursively') }}
+          </label>
+          <button type="submit" class="btn btn-default btn-sm putBack-button">
+            <i class="fa fa-undo" aria-hidden="true"></i>
+            {{ t('Put Back') }}
+          </button>
+        </div>
+
+      </form>
+    </div>
+    <!-- /.modal-content -->
+  </div>
+  <!-- /.mod<div class="modal" id="deletePage">al-dialog -->
+</div>
+<!-- /.modal -->

+ 8 - 1
lib/views/modal/rename.html

@@ -20,9 +20,16 @@
                 <input type="text" class="form-control" name="new_path" id="newPageName" value="{{ page.path }}">
               </div>
             </div>
+            <div class="checkbox">
+              <label>
+                <input name="move_recursively" value="1" type="checkbox" checked> {{ t('modal_rename.label.Move recursively') }}
+              </label>
+              <p class="help-block"> {{ t('modal_rename.help.recursive', page.path) }}
+              </p>
+            </div>
             <div class="checkbox">
                <label>
-                 <input name="create_redirect" value="1"  type="checkbox"> {{ t('Redirect') }}
+                 <input name="create_redirect" value="1"  type="checkbox"> {{ t('modal_rename.label.Redirect') }}
                </label>
                <p class="help-block"> {{ t('modal_rename.help.redirect', page.path) }}
                </p>

+ 3 - 19
lib/views/page.html

@@ -83,27 +83,10 @@
       {% if user %}
       <ul class="list-inline pull-right">
         <li>
-          <form role="form" id="revert-delete-page-form" onsubmit="return false;">
-            <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <input type="hidden" name="path" value="{{ page.path }}">
-            <input type="hidden" name="page_id" value="{{ page._id.toString() }}">
-            <button type="submit" class="btn btn-default btn-sm">
-              <i class="fa fa-undo" aria-hidden="true"></i>
-              Put Back
-            </button>
-          </form>
+          <a href="#" class="btn btn-default btn-sm" data-target="#putBackPage" data-toggle="modal"><i class="fa fa-undo" aria-hidden="true"></i> {{ t('Put Back') }}</a>
         </li>
         <li>
-          <form role="form" id="delete-page-form" onsubmit="return false;">
-            <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <input type="hidden" name="path" value="{{ page.path }}">
-            <input type="hidden" name="page_id" value="{{ page._id.toString() }}">
-            <input type="hidden" name="completely" value="true">
-            <button type="submit" class="btn btn-danger btn-sm">
-              <i class="fa fa-times-circle" aria-hidden="true"></i>
-              Delete Completely
-            </button>
-          </form>
+          <a href="#" class="btn btn-danger btn-sm" data-target="#deletePage" data-toggle="modal"><i class="fa fa-times-circle" aria-hidden="true"></i> {{ t('Delete Completely') }}</a>
         </li>
       </ul>{# /.pull-right #}
       {% endif %}
@@ -284,6 +267,7 @@
 <div id="crowi-modals">
   {% include 'modal/rename.html' %}
   {% include 'modal/delete.html' %}
+  {% include 'modal/put_back.html' %}
   {% include 'modal/page_name_warning.html' %}
 </div>
 {% endblock %}

+ 6 - 6
package.json

@@ -1,6 +1,6 @@
 {
   "name": "crowi-plus",
-  "version": "2.0.6-RC",
+  "version": "2.0.7-RC",
   "description": "Enhanced Crowi",
   "tags": [
     "wiki",
@@ -63,7 +63,7 @@
     "crowi-pluginkit": "^1.1.0",
     "csrf": "~3.0.3",
     "css-loader": "^0.28.0",
-    "debug": "~2.6.0",
+    "debug": "^3.0.0",
     "diff": "^3.2.0",
     "diff2html": "^2.3.0",
     "elasticsearch": "^13.2.0",
@@ -119,16 +119,16 @@
     "webpack-merge": "~4.1.0"
   },
   "devDependencies": {
-    "chai": "^4.0.2",
+    "chai": "^4.1.0",
     "cli": "~1.0.1",
     "colors": "^1.1.2",
     "commander": "^2.11.0",
     "easy-livereload": "^1.2.0",
-    "mocha": "^3.2.0",
+    "mocha": "^3.5.0",
     "morgan": "^1.8.2",
     "node-dev": "^3.1.3",
-    "sinon": "^2.1.0",
-    "sinon-chai": "^2.9.0"
+    "sinon": "^3.0.0",
+    "sinon-chai": "^2.12.0"
   },
   "engines": {
     "node": "6.x",

+ 25 - 11
resource/css/_comment.scss

@@ -1,3 +1,6 @@
+// import crowi variable
+@import 'utilities';
+
 .crowi.main-container {
   .page-comment-delete-modal .modal-content {
     .modal-body {
@@ -40,14 +43,6 @@
   }
 
   .page-comments-list {
-    .page-comments-list-toggle-newer,
-    .page-comments-list-toggle-older {
-      text-align: center;
-      display: block;
-      margin: 8px;
-      font-size: .9em;
-      color: #999;
-    }
     .page-comment {
       margin-top: 8px;
       padding-top: 8px;
@@ -110,7 +105,28 @@
 
     }
 
-    .page-comment.page-comment-old {
+  }
+}
+
+} // .crowi.main-container aside.sidebar .side-content
+
+.crowi.main-container {
+  .page-comments {
+
+    .page-comments-list-toggle-newer,
+    .page-comments-list-toggle-older {
+      text-align: center;
+      display: block;
+      margin: 8px;
+      font-size: .9em;
+      color: #999;
+    }
+
+    // older comments
+    .page-comments-list-older .page-comment {
+    }
+    // newer comments
+    .page-comments-list-newer .page-comment {
       opacity: .7;
 
       &:hover {
@@ -119,5 +135,3 @@
     }
   }
 }
-
-} // .crowi.main-container aside.sidebar .side-content

+ 0 - 8
resource/css/_comment_crowi-plus.scss

@@ -87,14 +87,6 @@
     display: block;
   }
 
-  .page-comment-old {
-    opacity: .7;
-
-    &:hover {
-      opacity: 1;
-    }
-  }
-
   .comment-form {
     position: relative;
     margin-top: 2em;

+ 6 - 0
resource/css/_delete.scss

@@ -3,3 +3,9 @@
     margin-left: .5em;
   }
 }
+
+#putBackPage {
+  .putBack-button {
+    margin-left: .5em;
+  }
+}

+ 1 - 2
resource/js/components/PageComment/Comment.js

@@ -36,8 +36,7 @@ export default class Comment extends React.Component {
 
   getRootClassName() {
     return "page-comment "
-        + (this.isCurrentUserEqualsToAuthor() ? 'page-comment-me' : '')
-        + (this.isCurrentRevision() ? '': 'page-comment-old');
+        + (this.isCurrentUserEqualsToAuthor() ? 'page-comment-me ' : '');
   }
 
   getRevisionLabelClassName() {

+ 67 - 20
resource/js/components/PageComments.js

@@ -19,8 +19,11 @@ export default class PageComments extends React.Component {
     super(props);
 
     this.state = {
+      // desc order array
       comments: [],
 
+      isLayoutTypeCrowiPlus: false,
+
       // for deleting comment
       commentToDelete: undefined,
       isDeleteConfirmModalShown: false,
@@ -49,6 +52,10 @@ export default class PageComments extends React.Component {
 
     const pageId = this.props.pageId;
 
+    const layoutType = this.props.crowi.getConfig()['layoutType'];
+    this.setState({isLayoutTypeCrowiPlus: 'crowi-plus' === layoutType});
+
+    // get data (desc order array)
     this.props.crowi.apiGet('/comments.get', {page_id: pageId})
     .then(res => {
       if (res.ok) {
@@ -126,10 +133,16 @@ export default class PageComments extends React.Component {
     let newerComments = [];
     let olderComments = [];
 
+    let comments = this.state.comments;
+    if (this.state.isLayoutTypeCrowiPlus) {
+      // replace with asc order array
+      comments = comments.slice().reverse();  // non-destructive reverse
+    }
+
     // divide by revisionId and createdAt
     const revisionId = this.props.revisionId;
     const revisionCreatedAt = this.props.revisionCreatedAt;
-    this.state.comments.forEach((comment) => {
+    comments.forEach((comment) => {
       if (comment.revision == revisionId) {
         currentComments.push(comment);
       }
@@ -142,38 +155,72 @@ export default class PageComments extends React.Component {
     });
 
     // generate elements
-    let currentElements = this.generateCommentElements(currentComments);
-    let newerElements = this.generateCommentElements(newerComments);
-    let olderElements = this.generateCommentElements(olderComments);
+    const currentElements = this.generateCommentElements(currentComments);
+    const newerElements = this.generateCommentElements(newerComments);
+    const olderElements = this.generateCommentElements(olderComments);
+    // generate blocks
+    const currentBlock = (
+      <div className="page-comments-list-current" id="page-comments-list-current">
+        {currentElements}
+      </div>
+    );
+    const newerBlock = (
+      <div className="page-comments-list-newer collapse" id="page-comments-list-newer">
+        {newerElements}
+      </div>
+    );
+    const olderBlock = (
+      <div className="page-comments-list-older collapse in" id="page-comments-list-older">
+        {olderElements}
+      </div>
+    );
 
-    let toggleNewer = (newerElements.length === 0)
+    // generate toggle elements
+    const iconForNewer = (this.state.isLayoutTypeCrowiPlus)
+      ? <i className="fa fa-angle-double-down"></i>
+      : <i className="fa fa-angle-double-up"></i>;
+    const toggleNewer = (newerElements.length === 0)
       ? <div></div>
       : (
         <a className="page-comments-list-toggle-newer text-center" data-toggle="collapse" href="#page-comments-list-newer">
-          <i className="fa fa-angle-double-up"></i> Comments for Newer Revision <i className="fa fa-angle-double-up"></i>
+          {iconForNewer} Comments for Newer Revision {iconForNewer}
         </a>
-      )
-    let toggleOlder = (olderElements.length === 0)
+      );
+    const iconForOlder = (this.state.isLayoutTypeCrowiPlus)
+      ? <i className="fa fa-angle-double-up"></i>
+      : <i className="fa fa-angle-double-down"></i>;
+    const toggleOlder = (olderElements.length === 0)
       ? <div></div>
       : (
         <a className="page-comments-list-toggle-older text-center" data-toggle="collapse" href="#page-comments-list-older">
-          <i className="fa fa-angle-double-down"></i> Comments for Older Revision <i className="fa fa-angle-double-down"></i>
+          {iconForOlder} Comments for Older Revision {iconForOlder}
         </a>
+      );
+
+    // layout blocks
+    const commentsElements = (this.state.isLayoutTypeCrowiPlus)
+      ? (
+        <div>
+          {olderBlock}
+          {toggleOlder}
+          {currentBlock}
+          {toggleNewer}
+          {newerBlock}
+        </div>
       )
+      : (
+        <div>
+          {newerBlock}
+          {toggleNewer}
+          {currentBlock}
+          {toggleOlder}
+          {olderBlock}
+        </div>
+      );
 
     return (
       <div>
-        <div className="page-comments-list-newer collapse" id="page-comments-list-newer">
-          {newerElements}
-        </div>
-        {toggleNewer}
-        <div className="page-comments-list-current" id="page-comments-list-current">
-          {currentElements}
-        </div>
-        {toggleOlder}
-        <div className="page-comments-list-older collapse in" id="page-comments-list-older">
-          {olderElements}
-        </div>
+        {commentsElements}
 
         <DeleteCommentModal
           isShown={this.state.isDeleteConfirmModalShown}

File diff suppressed because it is too large
+ 271 - 226
yarn.lock


Some files were not shown because too many files changed in this diff