Sotaro KARASAWA 9 лет назад
Родитель
Сommit
83baee9365

+ 15 - 8
lib/models/page.js

@@ -10,7 +10,7 @@ module.exports = function(crowi) {
 
     , STATUS_WIP        = 'wip'
     , STATUS_PUBLISHED  = 'published'
-    , STATUS_DELEDED    = 'deleted'
+    , STATUS_DELETED    = 'deleted'
     , STATUS_DEPRECATED = 'deprecated'
 
     , pageEvent = crowi.event('page')
@@ -70,7 +70,7 @@ module.exports = function(crowi) {
   };
 
   pageSchema.methods.isDeleted = function() {
-    return this.status === STATUS_DELEDED;
+    return this.status === STATUS_DELETED;
   };
 
   pageSchema.methods.isDeprecated = function() {
@@ -582,6 +582,7 @@ module.exports = function(crowi) {
     var Page = this;
     var User = crowi.model('User');
     var pathCondition = [];
+    var includeDeletedPage = option.includeDeletedPage || false
 
     if (!option) {
       option = {sort: 'updatedAt', desc: -1, offset: 0, limit: 50};
@@ -622,6 +623,15 @@ module.exports = function(crowi) {
         .skip(opt.offset)
         .limit(opt.limit);
 
+      if (!includeDeletedPage) {
+        q.and({
+          $or: [
+            {status: null},
+            {status: STATUS_PUBLISHED},
+          ],
+        });
+      }
+
       q.exec()
       .then(function(pages) {
         Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS})
@@ -799,8 +809,9 @@ module.exports = function(crowi) {
       ;
     if (Page.isDeletableName(pageData.path)) {
       return new Promise(function(resolve, reject) {
-        Page.updatePageProperty(pageData, {status: STATUS_DELEDED})
-        .then(function(pageData) {
+        Page.updatePageProperty(pageData, {status: STATUS_DELETED})
+        .then(function(data) {
+          pageData.status = STATUS_DELETED;
           return Page.rename(pageData, newPath, user, {createRedirectPage: true})
         }).then(function(pageData) {
           resolve(pageData);
@@ -843,10 +854,6 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.deletePage = function(pageData, options) {
-    var Page = this;
-  };
-
   pageSchema.statics.getHistories = function() {
     // TODO
     return;

+ 2 - 0
lib/routes/index.js

@@ -114,6 +114,8 @@ module.exports = function(crowi, app) {
   //app.get('/_api/r/:revisionId'    , user.useUserData()         , page.api.get);
 
   app.post('/_/edit'                 , form.revision             , loginRequired(crowi, app) , page.pageEdit);
+  app.get('/trash/$'                 , loginRequired(crowi, app) , page.deletedPageListShow);
+  app.get('/trash/*/$'               , loginRequired(crowi, app) , page.deletedPageListShow);
   app.get('/*/$'                     , loginRequired(crowi, app) , page.pageListShow);
   app.get('/*'                       , loginRequired(crowi, app) , page.pageShow);
   //app.get('/*/edit'                , routes.edit);

+ 49 - 4
lib/routes/page.js

@@ -62,6 +62,7 @@ module.exports = function(crowi, app) {
     var SEENER_THRESHOLD = 10;
     path = path + (path == '/' ? '' : '/');
 
+    debug('Page list show', path);
     // index page
     var pagerOptions = {
       offset: offset,
@@ -102,6 +103,45 @@ module.exports = function(crowi, app) {
     });
   };
 
+  actions.deletedPageListShow = function(req, res) {
+    var path = '/trash' + getPathFromRequest(req);
+    var limit = 50;
+    var offset = parseInt(req.query.offset)  || 0;
+
+    // index page
+    var pagerOptions = {
+      offset: offset,
+      limit : limit
+    };
+    var queryOptions = {
+      offset: offset,
+      limit : limit + 1,
+      includeDeletedPage: true,
+    };
+
+    var renderVars = {
+      page: null,
+      path: path,
+      pages: [],
+    };
+
+    Page.findListByStartWith(path, req.user, queryOptions)
+    .then(function(pageList) {
+
+      if (pageList.length > limit) {
+        pageList.pop();
+      }
+
+      pagerOptions.length = pageList.length;
+
+      renderVars.pager = generatePager(pagerOptions);
+      renderVars.pages = pageList;
+      res.render('page_list', renderVars);
+    }).catch(function(err) {
+      debug('Error on rendering deletedPageListShow', err);
+    });
+  };
+
   actions.search = function(req, res) {
     // spec: ?q=query&sort=sort_order&author=author_filter
     var query = req.query.q;
@@ -244,7 +284,7 @@ module.exports = function(crowi, app) {
 
       // pageShow は /* にマッチしてる最後の砦なので、creatableName でない routing は
       // これ以前に定義されているはずなので、こうしてしまって問題ない。
-      if (!Page.isCreatableName(path) && page.isDeleted()) {
+      if (!Page.isCreatableName(path) && !page.isDeleted()) {
         // 削除済みページの場合 /trash 以下に移動しているので creatableName になっていないので、表示を許可
         debug('Page is not creatable name.', path);
         res.redirect('/');
@@ -601,15 +641,20 @@ module.exports = function(crowi, app) {
 
     Page.findPageByIdAndGrantedUser(pageId, req.user)
     .then(function(pageData) {
+      debug('Delete page', pageData._id, pageData.path);
+
+      if (!pageData.isUpdatable(previousRevision)) {
+        throw new Error('Someone could update this page, so couldn\'t delete.');
+      }
       return Page.deletePage(pageData, req.user);
     }).then(function(data) {
       debug('Page deleted', data);
       var result = {};
-      result.page = pageData;
+      result.page = data;
 
       return res.json(ApiResponse.success(result));
     }).catch(function(err) {
-      debug('Error occured while get setting', err);
+      debug('Error occured while get setting', err, err.stack);
       return res.json(ApiResponse.error('Failed to delete page.'));
     });
   };
@@ -649,7 +694,7 @@ module.exports = function(crowi, app) {
       .then(function(pageData) {
         page = pageData;
         if (!pageData.isUpdatable(previousRevision)) {
-          return res.json(ApiResponse.error('誰かが更新している可能性があります。ページを更新できません。'));
+          throw new Error('Someone could update this page, so couldn\'t delete.');
         }
 
         return Page.rename(pageData, newPagePath, req.user, options);

+ 1 - 1
lib/util/middlewares.js

@@ -25,7 +25,7 @@ exports.loginChecker = function(crowi, app) {
 
 exports.swigFunctions = function(crowi, app) {
   return function(req, res, next) {
-    require('../util/swigFunctions')(crowi, app, res.locals);
+    require('../util/swigFunctions')(crowi, app, req, res.locals);
     next();
   };
 };

+ 10 - 1
lib/util/swigFunctions.js

@@ -1,4 +1,4 @@
-module.exports = function(crowi, app, locals) {
+module.exports = function(crowi, app, req, locals) {
   var debug = require('debug')('crowi:lib:swigFunctions')
     , Page = crowi.model('Page')
     , Config = crowi.model('Config')
@@ -43,6 +43,15 @@ module.exports = function(crowi, app, locals) {
     return false;
   };
 
+  locals.isTrashPage = function() {
+    var path = req.path || '';
+    if (path.match(/^\/trash\/.*/)) {
+      return true;
+    }
+
+    return false;
+  };
+
   locals.userPageRoot = function(user) {
     if (!user || !user.username) {
       return '';

+ 1 - 1
lib/views/modal/widget_delete.html

@@ -10,7 +10,7 @@
         </div>
         <div class="modal-body">
           <ul>
-           <li>This page will be moved to the trash.</li>
+            <li>This page will be moved to the <a href="/trash/">trash</a>.</li>
           </ul>
             <div class="form-group">
               <label for="">This page:</label><br>

+ 20 - 13
lib/views/page.html

@@ -8,6 +8,7 @@
 {% endblock %}
 
 <div class="header-wrap">
+  {% if not page.isDeleted() %}
   <header id="page-header">
     <p class="stopper"><a href="#" data-affix-disable="#page-header"><i class="fa fa-chevron-up"></i></a></p>
 
@@ -17,6 +18,12 @@
     {% endif %}
     <h1 class="title" id="revision-path">{{ path|insertSpaceToEachSlashes }}</h1>
   </header>
+  {% else %}
+  {# trash/* #}
+  <header id="page-header">
+    <h1 class="title">{{ path|insertSpaceToEachSlashes }}</h1>
+  </header>
+  {% endif %}
 </div>
 
 {% block content_head_after %}
@@ -54,17 +61,14 @@
 
   {% else %}
 
-  <ul class="nav nav-tabs hidden-print">
-
   {% if page.isDeleted() %}
-    <li class="">
-      <a href="#revision-body" data-toggle="tab">
-        <i class="fa fa-trash-o" aria-hidden="true"></i> This page is in the trash.
-      </a>
-    </li>
+  <div class="alert alert-danger">
+    <i class="fa fa-trash-o" aria-hidden="true"></i> This page is in the trash.
+  </div>
   {% endif %}
 
   {% if not page.isDeleted() %}
+  <ul class="nav nav-tabs hidden-print">
     <li class=" {% if not req.body.pageForm %}active{% endif %}" data-toggle="tooltip" {# data-title="あなたの 確認待ち です" title="" data-placement="bottom" data-trigger="manual" data-tooltip-stay #}>
       <a href="#revision-body" data-toggle="tab">
       <i class="fa fa-magic"></i>
@@ -86,15 +90,14 @@
        <li><a href="#" data-target="#renamePage" data-toggle="modal"><i class="fa fa-share"></i> 移動</a></li>
        <li><a href="?presentation=1" class="toggle-presentation"><i class="fa fa-arrows-alt"></i> プレゼンモード (beta)</a></li>
        <li class="divider"></li>
-       <li class=""><a href="#" data-target="#deletePage" data-toggle="modal"><i class="fa fa-remove text-danger"></i> 削除</a></li>
+       <li class=""><a href="#" data-target="#deletePage" data-toggle="modal"><i class="fa fa-trash-o text-danger"></i> 削除</a></li>
       </ul>
     </li>
     {% if page %}
     <li class="pull-right"><a href="#revision-history" data-toggle="tab"><i class="fa fa-history"></i> History</a></li>
     {% endif %}
-
-  {% endif %}
   </ul>
+  {% endif %}
 
   {% include 'modal/widget_rename.html' %}
   {% include 'modal/widget_delete.html' %}
@@ -134,8 +137,8 @@
     {% if not page.isDeleted() %}
     <div class="edit-form tab-pane {% if req.body.pageForm %}active{% endif %}" id="edit-form">
       {% include '_form.html' %}
-    {% endif %}
     </div>
+    {% endif %}
 
     {# raw revision history #}
     <div class="tab-pane revision-history" id="revision-history">
@@ -198,11 +201,15 @@
 {% endblock %}
 
 {% block side_header %}
-  {% include 'widget/page_side_header.html' %}
+  {% if not page.isDeleted() %}
+    {% include 'widget/page_side_header.html' %}
+  {% endif %}
 {% endblock %} {# side_header #}
 
 {% block side_content %}
-  {% include 'widget/page_side_content.html' %}
+  {% if not page.isDeleted() %}
+    {% include 'widget/page_side_content.html' %}
+  {% endif %}
 {% endblock %}
 
 {% block footer %}

+ 3 - 3
lib/views/page_list.html

@@ -20,7 +20,7 @@
     {% endif %}
     <h1 class="title">
       <span class="" id="revision-path">{{ path|insertSpaceToEachSlashes }}</span>
-      {% if searchConfigured() && path != '/' %}
+      {% if searchConfigured() && path != '/' && !isTrashPage() %}
       <form class="input-group search-input-group hidden-xs hidden-sm" data-toggle="tooltip" data-placement="bottom" title="{{ path }} 以下から検索" id="search-listpage-form">
         <input type="text" class="search-listpage-input form-control" data-path="{{ path }}" id="search-listpage-input">
         <span class="input-group-btn search-listpage-submit-group">
@@ -46,7 +46,7 @@
  # but now the header and page list content is rendered separately by the server,
  # so now bind the values through the hidden fields.
  #}
-{% if searchConfigured() && path != '/' %}
+{% if searchConfigured() && path != '/' && !isTrashPage() %}
 <div id="page-list-search">
 </div>
 {% endif %}
@@ -163,7 +163,7 @@
 
 {% block side_header %}
 
-{% if not page and not isUserPageList(path) %}
+{% if not page and not isUserPageList(path) and !isTrashPage() %}
 <div class="portal-side">
   <div class="portal-form-button">
     <button class="btn btn-primary" id="create-portal-button">Create Portal</button>

+ 12 - 10
resource/js/crowi.js

@@ -726,16 +726,18 @@ $(function() {
     }
 
     var $seenUserList = $("#seen-user-list");
-    var seenUsers = $seenUserList.data('seen-users');
-    var seenUsersArray = seenUsers.split(',');
-    if (seenUsers && seenUsersArray.length > 0 && seenUsersArray.length <= 10) {
-      // FIXME: user data cache
-      $.get('/_api/users.list', {user_ids: seenUsers}, function(res) {
-        // ignore unless response has error
-        if (res.ok) {
-          AddToSeenUser(res.users);
-        }
-      });
+    if ($seenUserList && $seenUserList.length > 0) {
+      var seenUsers = $seenUserList.data('seen-users');
+      var seenUsersArray = seenUsers.split(',');
+      if (seenUsers && seenUsersArray.length > 0 && seenUsersArray.length <= 10) {
+        // FIXME: user data cache
+        $.get('/_api/users.list', {user_ids: seenUsers}, function(res) {
+          // ignore unless response has error
+          if (res.ok) {
+            AddToSeenUser(res.users);
+          }
+        });
+      }
     }
 
     function CreateUserLinkWithPicture (user) {