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

Merge pull request #102 from weseek/master

release v1.2.5
Yuki Takei 8 лет назад
Родитель
Сommit
45654d59b1

+ 9 - 1
CHANGES.md

@@ -1,13 +1,21 @@
 CHANGES
 ========
 
+## 1.2.5
+
+* Feature: crowi-plus Simplified Behavior
+    * `/page` and `/page/` both shows the page
+    * `/nonexistent_page` shows editing form
+    * All pages shows the list of sub pages
+* Improvement: Ensure to be able to disable Timeline feature
+
 ## 1.2.4
 
 * Fix: Internal Server Error has occurred when a guest user visited the page someone added "liked"
 
 ## 1.2.3
 
-* Improvement: Ensure to be enabled to use Presentation Mode even when not logged in
+* Improvement: Ensure to be able to use Presentation Mode even when not logged in
 * Improvement: Presentation Mode on IE11 (Experimental)
 * Fix: Broken Presentation Mode
 

+ 9 - 0
lib/form/admin/custombehavior.js

@@ -0,0 +1,9 @@
+'use strict';
+
+var form = require('express-form')
+  , field = form.field
+  ;
+
+module.exports = form(
+  field('settingForm[customize:behavior]')
+);

+ 9 - 0
lib/form/admin/customfeatures.js

@@ -0,0 +1,9 @@
+'use strict';
+
+var form = require('express-form')
+  , field = form.field;
+
+module.exports = form(
+  field('settingForm[customize:isEnabledTimeline]').trim().toBooleanStrict()
+);
+

+ 2 - 0
lib/form/index.js

@@ -19,7 +19,9 @@ module.exports = {
     plugin: require('./admin/plugin'),
     markdown: require('./admin/markdown'),
     customcss: require('./admin/customcss'),
+    custombehavior: require('./admin/custombehavior'),
     customlayout: require('./admin/customlayout'),
+    customfeatures: require('./admin/customfeatures'),
     userInvite: require('./admin/userInvite'),
     slackSetting: require('./admin/slackSetting'),
   },

+ 40 - 4
lib/models/config.js

@@ -20,7 +20,23 @@ module.exports = function(crowi) {
     value: { type: String, required: true }
   });
 
-  function getArrayForInstalling()
+  /**
+   * default values when crowi-plus installed
+   */
+  function getArrayForInstalling() {
+    let config = getDefaultCrowiConfigs();
+
+    // overwrite
+    config['customize:behavior'] = 'crowi-plus';
+    config['customize:layout'] = 'crowi-plus';
+
+    return config;
+  }
+
+  /**
+   * default values when migrated from Official Crowi
+   */
+  function getDefaultCrowiConfigs()
   {
     return {
       //'app:installed'     : "0.0.0",
@@ -51,7 +67,9 @@ module.exports = function(crowi) {
       'plugin:isEnabledPlugins' : true,
 
       'customize:css' : '',
+      'customize:behavior' : 'crowi',
       'customize:layout' : 'crowi',
+      'customize:isEnabledTimeline' : true,
     };
   }
 
@@ -120,7 +138,7 @@ module.exports = function(crowi) {
 
     // set Default Settings
     if (ns === 'crowi') {
-      defaultConfig = getArrayForInstalling();
+      defaultConfig = getDefaultCrowiConfigs();
     }
     else if (ns === 'markdown') {
       defaultConfig = getDefaultMarkdownConfigs();
@@ -230,7 +248,7 @@ module.exports = function(crowi) {
 
   configSchema.statics.isEnabledPlugins = function(config)
   {
-    var defaultValue = getArrayForInstalling()['plugin:isEnabledPlugins'];
+    var defaultValue = getDefaultCrowiConfigs()['plugin:isEnabledPlugins'];
 
     // return defaultValue if undefined
     if (undefined === config.crowi || undefined === config.crowi['plugin:isEnabledPlugins']) {
@@ -269,7 +287,7 @@ module.exports = function(crowi) {
    */
   configSchema.statics.generateUglifiedCustomCss = function(config)
   {
-    var rawCss = config.crowi['customize:css'] || getArrayForInstalling()['customize:css'];
+    var rawCss = config.crowi['customize:css'] || getDefaultCrowiConfigs()['customize:css'];
     this.uglifiedCustomCss = uglifycss.processString(rawCss);
   }
 
@@ -278,11 +296,28 @@ module.exports = function(crowi) {
     return this.uglifiedCustomCss;
   }
 
+  configSchema.statics.behaviorType = function(config)
+  {
+    return config.crowi['customize:behavior'] || 'crowi';
+  }
+
   configSchema.statics.layoutType = function(config)
   {
     return config.crowi['customize:layout'] || 'crowi';
   }
 
+  configSchema.statics.isEnabledTimeline = function(config)
+  {
+    var defaultValue = getDefaultCrowiConfigs()['customize:isEnabledTimeline'];
+
+    // return defaultValue if undefined
+    if (undefined === config.crowi || undefined === config.crowi['customize:isEnabledTimeline']) {
+      return defaultValue;
+    }
+
+    return config.crowi['customize:isEnabledTimeline'];
+  };
+
   configSchema.statics.fileUploadEnabled = function(config)
   {
     const Config = this;
@@ -334,6 +369,7 @@ module.exports = function(crowi) {
         image: Config.isUploadable(config),
         file: Config.fileUploadEnabled(config),
       },
+      behaviorType: Config.behaviorType(config),
       layoutType: Config.layoutType(config),
       env: {
         PLANTUML_URI: env.PLANTUML_URI || null,

+ 6 - 1
lib/models/page.js

@@ -633,7 +633,6 @@ module.exports = function(crowi) {
 
     return new Promise(function(resolve, reject) {
       var q = Page.generateQueryToListByStartWith(path, userData, option)
-        .populate('revision')
         .sort(sortOpt)
         .skip(opt.offset)
         .limit(opt.limit);
@@ -651,6 +650,7 @@ module.exports = function(crowi) {
     var Page = this;
     var pathCondition = [];
     var includeDeletedPage = option.includeDeletedPage || false;
+    var isPopulateRevision = option.isPopulateRevision || false;
 
     var queryReg = new RegExp('^' + path);
     pathCondition.push({path: queryReg});
@@ -681,6 +681,11 @@ module.exports = function(crowi) {
       });
     }
 
+    // retrieve revision data
+    if (isPopulateRevision) {
+      q = q.populate('revision');
+    }
+
     return q;
   }
 

+ 7 - 5
lib/routes/index.js

@@ -52,9 +52,11 @@ module.exports = function(crowi, app) {
   app.post('/admin/markdown/lineBreaksSetting', loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.markdown, admin.markdown.lineBreaksSetting);
 
   // markdown admin
-  app.get('/admin/customize'              , loginRequired(crowi, app) , middleware.adminRequired() , admin.customize.index);
-  app.post('/_api/admin/customize/css'    , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customcss, admin.api.customizeSetting);
-  app.post('/_api/admin/customize/layout' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customlayout, admin.api.customizeSetting);
+  app.get('/admin/customize'                , loginRequired(crowi, app) , middleware.adminRequired() , admin.customize.index);
+  app.post('/_api/admin/customize/css'      , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customcss, admin.api.customizeSetting);
+  app.post('/_api/admin/customize/behavior' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.custombehavior, admin.api.customizeSetting);
+  app.post('/_api/admin/customize/layout'   , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customlayout, admin.api.customizeSetting);
+  app.post('/_api/admin/customize/features' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customfeatures, admin.api.customizeSetting);
 
   // search admin
   app.get('/admin/search'              , loginRequired(crowi, app) , middleware.adminRequired() , admin.search.index);
@@ -139,7 +141,7 @@ module.exports = function(crowi, app) {
   app.post('/_/edit'                 , form.revision             , loginRequired(crowi, app) , csrf, page.pageEdit);
   app.get('/trash/$'                 , loginRequired(crowi, app, false) , page.deletedPageListShow);
   app.get('/trash/*/$'               , loginRequired(crowi, app, false) , page.deletedPageListShow);
-  app.get('/*/$'                     , loginRequired(crowi, app, false) , page.pageListShow);
-  app.get('/*'                       , loginRequired(crowi, app, false) , page.pageShow);
 
+  app.get('/*/$'                   , loginRequired(crowi, app, false) , page.pageListShowWrapper);
+  app.get('/*'                     , loginRequired(crowi, app, false) , page.pageShowWrapper);
 };

+ 149 - 2
lib/routes/page.js

@@ -4,6 +4,8 @@ module.exports = function(crowi, app) {
   var debug = require('debug')('crowi:routes:page')
     , Page = crowi.model('Page')
     , User = crowi.model('User')
+    , Config   = crowi.model('Config')
+    , config   = crowi.getConfig()
     , Revision = crowi.model('Revision')
     , Bookmark = crowi.model('Bookmark')
     , ApiResponse = require('../util/apiResponse')
@@ -61,12 +63,39 @@ module.exports = function(crowi, app) {
     };
   }
 
-  // routing
+  /**
+   * switch action by behaviorType
+   */
+  actions.pageListShowWrapper = function(req, res) {
+    const behaviorType = Config.behaviorType(config);
+
+    if ('crowi-plus' === behaviorType) {
+      return actions.pageListShowForCrowiPlus(req, res);
+    }
+    else {
+      return actions.pageListShow(req, res);
+    }
+  }
+  /**
+   * switch action by behaviorType
+   */
+  actions.pageShowWrapper = function(req, res) {
+    const behaviorType = Config.behaviorType(config);
+
+    if ('crowi-plus' === behaviorType) {
+      return actions.pageShowForCrowiPlus(req, res);
+    }
+    else {
+      return actions.pageShow(req, res);
+    }
+  }
+
   actions.pageListShow = function(req, res) {
     var path = getPathFromRequest(req);
     var limit = 50;
     var offset = parseInt(req.query.offset)  || 0;
     var SEENER_THRESHOLD = 10;
+    // add slash to the last
     path = path + (path == '/' ? '' : '/');
 
     debug('Page list show', path);
@@ -77,7 +106,8 @@ module.exports = function(crowi, app) {
     };
     var queryOptions = {
       offset: offset,
-      limit : limit + 1
+      limit : limit + 1,
+      isPopulateRevision: Config.isEnabledTimeline(config),
     };
 
     var renderVars = {
@@ -119,6 +149,123 @@ module.exports = function(crowi, app) {
     });
   };
 
+  actions.pageListShowForCrowiPlus = function(req, res) {
+    var path = getPathFromRequest(req);
+    // omit the slash of the last
+    path = path.replace((/\/$/), '');
+    // redirect
+    return res.redirect(path);
+  }
+
+  actions.pageShowForCrowiPlus = function(req, res) {
+    var path = getPathFromRequest(req);
+
+    var limit = 50;
+    var offset = parseInt(req.query.offset)  || 0;
+    var SEENER_THRESHOLD = 10;
+
+    // index page
+    var pagerOptions = {
+      offset: offset,
+      limit : limit
+    };
+    var queryOptions = {
+      offset: offset,
+      limit : limit + 1,
+      isPopulateRevision: Config.isEnabledTimeline(config),
+    };
+
+    var renderVars = {
+      path: path,
+      page: null,
+      revision: {},
+      author: false,
+      pages: [],
+      tree: [],
+    };
+
+    var pageTeamplate = 'customlayout-selector/page';
+
+    Page.findPage(path, req.user, req.query.revision)
+    .then(function(page) {
+      renderVars.page = page;
+
+      if (page) {
+        renderVars.path = page.path;
+        renderVars.revision = page.revision;
+        renderVars.author = page.revision.author;
+
+        return Revision.findRevisionList(page.path, {})
+        .then(function(tree) {
+          renderVars.tree = tree;
+          return Promise.resolve();
+        }).then(function() {
+          var userPage = isUserPage(page.path);
+          var userData = null;
+
+          if (userPage) {
+            // change template
+            pageTeamplate = 'customlayout-selector/user_page';
+
+            return User.findUserByUsername(User.getUsernameByPath(page.path))
+            .then(function(data) {
+              if (data === null) {
+                throw new Error('The user not found.');
+              }
+              userData = data;
+              renderVars.pageUser = userData;
+
+              return Bookmark.findByUser(userData, {limit: 10, populatePage: true, requestUser: req.user});
+            }).then(function(bookmarkList) {
+              renderVars.bookmarkList = bookmarkList;
+
+              return Page.findListByCreator(userData, {limit: 10}, req.user);
+            }).then(function(createdList) {
+              renderVars.createdList = createdList;
+              return Promise.resolve();
+            }).catch(function(err) {
+              debug('Error on finding user related entities', err);
+              // pass
+            });
+          }
+          else {
+            return Promise.resolve();
+          }
+        });
+      } else {
+        return Promise.resolve();
+      }
+    }).catch(function(err) {
+      // page not exists
+      // change template
+      pageTeamplate = 'crowi-plus/new_page';
+    }).then(function() {
+      return Page.findListByStartWith(path, req.user, queryOptions)
+        .then(function(pageList) {
+          if (pageList.length > limit) {
+            pageList.pop();
+          }
+
+          pagerOptions.length = pageList.length;
+
+          renderVars.viewConfig = {
+            seener_threshold: SEENER_THRESHOLD,
+          };
+          renderVars.pager = generatePager(pagerOptions);
+          renderVars.pages = pageList;
+
+          return Promise.resolve();
+        });
+    }).then(function() {
+      return interceptorManager.process('beforeRenderPage', req, res, renderVars);
+    }).then(function() {
+      res.render(req.query.presentation ? 'page_presentation' : pageTeamplate, renderVars);
+    }).catch(function(err) {
+      console.log(err);
+      debug('Error on rendering pageListShowForCrowiPlus', err);
+    });
+  }
+
   actions.deletedPageListShow = function(req, res) {
     var path = '/trash' + getPathFromRequest(req);
     var limit = 50;

+ 10 - 0
lib/util/swigFunctions.js

@@ -55,11 +55,21 @@ module.exports = function(crowi, app, req, locals) {
     return Config.customCss();
   }
 
+  locals.behaviorType = function() {
+    var config = crowi.getConfig()
+    return Config.behaviorType(config);
+  }
+
   locals.layoutType = function() {
     var config = crowi.getConfig()
     return Config.layoutType(config);
   }
 
+  locals.isEnabledTimeline = function() {
+    var config = crowi.getConfig()
+    return Config.isEnabledTimeline(config);
+  }
+
   locals.slackConfigured = function() {
     var config = crowi.getConfig()
     if (Config.hasSlackToken(config)) {

+ 106 - 1
lib/views/admin/customize.html

@@ -45,6 +45,52 @@
     </div>
     <div class="col-md-9">
 
+      <form action="/_api/admin/customize/behavior" method="post" class="form-horizontal" id="cutombehaviorSettingForm" role="form">
+      <fieldset>
+        <legend>挙動</legend>
+
+        <div class="form-group">
+          <div class="col-xs-6">
+            <h4>
+              <input type="radio" name="settingForm[customize:behavior]" value="crowi"
+                  {% if !settingForm['customize:behavior'] || 'crowi' === settingForm['customize:behavior'] %}checked="checked"{% endif %}>
+              Official Crowi Behavior
+            </h4>
+            <ul>
+              <li><code>/page</code> shows the page</li>
+              <li><code>/page/</code> shows the list of sub pages</li>
+              <ul>
+                <li>If portal is applied to <code>/page/</code> , the portal and the list of sub pages are shown</li>
+              </ul>
+              <li><code>/nonexistent_page</code> shows editing form</li>
+              <li><code>/nonexistent_page/</code> the list of sub pages</li>
+            </ul>
+          </div>
+          <div class="col-xs-6">
+            <h4>
+              <input type="radio" name="settingForm[customize:behavior]" value="crowi-plus"
+                  {% if 'crowi-plus' === settingForm['customize:behavior'] %}checked="checked"{% endif %}>
+              crowi-plus Simplified Behavior <small class="text-success">(Recommended)</small>
+            </h4>
+            <ul>
+              <li><code>/page</code> and <code>/page/</code> both shows the page</li>
+              <li><code>/nonexistent_page</code> shows editing form</li>
+              <li>All pages shows the list of sub pages</li>
+            </ul>
+          </div>
+        </div>
+
+        <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>
+          </div>
+        </div>
+
+      </fieldset>
+      </form>
+
+
       <form action="/_api/admin/customize/layout" method="post" class="form-horizontal" id="cutomlayoutSettingForm" role="form">
       <fieldset>
         <legend>レイアウト</legend>
@@ -98,6 +144,44 @@
       </form>
 
 
+      <form action="/_api/admin/customize/features" method="post" class="form-horizontal" id="customfeaturesSettingForm" role="form">
+      <fieldset>
+      <legend>機能</legend>
+        <p class="well">機能の有効/無効を選択できます。</p>
+
+        <div class="form-group">
+          <label for="settingForm[customize:isEnabledTimeline]" class="col-xs-3 control-label">タイムライン表示機能</label>
+          <div class="col-xs-9">
+            <div class="btn-group btn-toggle" data-toggle="buttons">
+              <label class="btn {% if settingForm['customize:isEnabledTimeline'] %}btn-primary active{% else %}btn-default{% endif %}">
+                <input name="settingForm[customize:isEnabledTimeline]" value="true" type="radio"
+                    {% if true === settingForm['customize:isEnabledTimeline'] %}checked{% endif %}> 有効
+              </label>
+              <label class="btn {% if !settingForm['customize:isEnabledTimeline'] %}btn-primary active{% else %}btn-default{% endif %}">
+                <input name="settingForm[customize:isEnabledTimeline]" value="false" type="radio"
+                    {% if !settingForm['customize:isEnabledTimeline'] %}checked{% endif %}> 無効
+              </label>
+            </div>
+
+            <p class="help-block">
+              無効化することで、リストページの表示を高速化できます。<br>
+              配下ページが多い場合に効果があります。
+            </p>
+          </div>
+
+        </div>
+
+        <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>
+          </div>
+        </div>
+
+      </fieldset>
+      </form>
+
+
       <form action="/_api/admin/customize/css" method="post" class="form-horizontal" id="cutomcssSettingForm" role="form">
       <fieldset>
         <legend>カスタムCSS</legend>
@@ -133,7 +217,7 @@
   </div>
 
   <script>
-    $('#cutomcssSettingForm, #cutomlayoutSettingForm').each(function() {
+    $('#cutomcssSettingForm, #cutomlayoutSettingForm, #cutombehaviorSettingForm, #customfeaturesSettingForm').each(function() {
       $(this).submit(function()
       {
         function showMessage(formId, msg, status) {
@@ -205,6 +289,27 @@
         editor.setSize($(this).width(), $(this).height());
       }
     });
+
+    // Bootstrap 3 Toggle Switch Snippet
+    // http://www.bootply.com/92189
+    $('.btn-toggle').click(function() {
+      $(this).find('.btn').toggleClass('active');
+
+      if ($(this).find('.btn-primary').length>0) {
+        $(this).find('.btn').toggleClass('btn-primary');
+      }
+      if ($(this).find('.btn-danger').length>0) {
+        $(this).find('.btn').toggleClass('btn-danger');
+      }
+      if ($(this).find('.btn-success').length>0) {
+        $(this).find('.btn').toggleClass('btn-success');
+      }
+      if ($(this).find('.btn-info').length>0) {
+        $(this).find('.btn').toggleClass('btn-info');
+      }
+
+      $(this).find('.btn').toggleClass('btn-default');
+    });
   </script>
 
 </div>

+ 123 - 0
lib/views/crowi-plus/new_page.html

@@ -0,0 +1,123 @@
+{% extends 'base/page_nosidebar.html' %}
+
+{% block main_css_class %}
+  main-crowi-plus-customized
+  {% parent %}
+{% endblock %}
+
+{% block content_head %}
+
+  {% block content_head_before %}
+  {% endblock %}
+
+  <div class="header-wrap">
+    <header id="page-header">
+      <p class="stopper"><a href="#" data-affix-disable="#page-header"><i class="fa fa-chevron-up"></i></a></p>
+
+      <div class="flex-title-line">
+        <div>
+          <h1 class="title flex-item-title" id="revision-path"></h1>
+          <div id="revision-url" class="url-line"></div>
+        </div>
+      </div>
+
+    </header>
+  </div>
+
+  {% block content_head_after %}
+  {% endblock %}
+
+{% endblock %} {# /content_head #}
+
+{% block content_main %}
+  <div class="container-fluid">
+    <div class="row">
+
+      <div class="col-lg-10 col-md-9">
+
+        {% block content_main_before %}
+        <h2 class="text-muted">
+          <i class="fa fa-info-circle" aria-hidden="true"></i>
+          Page is not exists
+        </h2>
+        {% endblock %}
+
+
+
+        <div id="content-main" class="content-main content-main-new-page page-list"
+          data-path="{{ path }}"
+          data-path-shortname="{{ path|path2name }}"
+          data-page-id="{% if page %}{{ page._id.toString() }}{% endif %}"
+          data-current-user="{% if user %}{{ user._id.toString() }}{% endif %}"
+          data-current-username="{% if user %}{{ user.username }}{% endif %}"
+          data-page-revision-id="{% if revision %}{{ revision._id.toString() }}{% endif %}"
+          data-page-revision-created="{% if revision %}{{ revision.createdAt|datetz('U') }}{% endif %}"
+          data-page-is-seen="{% if page and page.isSeenUser(user) %}1{% else %}0{% endif %}"
+          data-linebreaks-enabled="{{ isEnabledLinebreaks() }}"
+          data-csrftoken="{{ csrf() }}"
+          >
+
+          <ul class="nav nav-tabs hidden-print">
+            <li class="active"><a href="#revision-body" data-toggle="tab">{{ t('List View') }}</a></li>
+
+            <li>
+              <a {% if user %}href="#edit-form" data-toggle="tab"{% endif %} class="edit-button {% if not user %}edit-button-disabled{% endif %}">
+                <i class="fa fa-pencil-square-o"></i> {{ t('Create') }}
+              </a>
+            </li>
+
+            {% if user %}
+            <li class="dropdown pull-right">
+              <a href="#" onclick="history.back();"><i class="fa fa-times"></i> {{ t('Cancel') }}</a>
+            </li>
+            {% endif %}
+          </ul>
+
+          <div class="tab-content wiki-content">
+            {% if req.query.renamed %}
+            <div class="alert alert-info alert-moved">
+              <span>
+                <strong>{{ t('Moved') }}: </strong> {{ t('page_page.notice.moved', req.query.renamed) }}
+              </span>
+            </div>
+            {% endif %}
+            {% if req.query.unlinked %}
+            <div class="alert alert-info">
+              <strong>{{ t('Unlinked') }}: </strong> {{ t('page_page.notice.unlinked') }}
+            </div>
+            {% endif %}
+
+            {# list view #}
+            <div class="active tab-pane page-list-container" id="revision-body">
+              {% if pages.length == 0 %}
+                There are no pages under <strong>{{ path }}</strong>.
+              {% endif  %}
+
+              {% include '../widget/page_list.html' with { pages: pages, pager: pager, viewConfig: viewConfig } %}
+            </div>
+
+            {# edit view #}
+            <div class="edit-form tab-pane {% if req.body.pageForm %}active{% endif %}" id="edit-form">
+              {% include '../_form.html' %}
+            </div>
+
+          </div>
+        </div>
+
+        {% block content_main_after %}
+        {% endblock %}
+
+
+
+      </div> {# /.col- #}
+
+    </div>
+  </div>
+
+{% endblock %}
+
+{% block content_main_after %}
+{% endblock %}
+
+{% block content_footer %}
+{% endblock %}

+ 9 - 1
lib/views/crowi-plus/page.html

@@ -17,7 +17,6 @@
 
 {% endblock %} {# /content_head #}
 
-
 {% block content_main %}
   <div class="container-fluid">
     <div class="row">
@@ -39,6 +38,15 @@
       </div> {# /.col- #}
 
     </div>
+
+    {% if 'crowi-plus' === behaviorType() %}
+    <div class="row page-list">
+      <div class="col-md-12">
+        {% include './widget/page_list_container.html' %}
+      </div>
+    </div>
+    {% endif %}
+
   </div>
 {% endblock %}
 

+ 38 - 0
lib/views/crowi-plus/widget/page_list_container.html

@@ -0,0 +1,38 @@
+<div class="page-list-container crowi-plus-widget">
+  <ul class="nav nav-tabs">
+      <li class="active"><a href="#view-list" data-toggle="tab">{{ t('List View') }}</a></li>
+      {% if isEnabledTimeline() %}
+      <li><a href="#view-timeline" data-toggle="tab">{{ t('Timeline View') }}</a></li>
+      {% endif %}
+  </ul>
+
+  <div class="tab-content">
+    {% if pages.length == 0 %}
+
+      {% if isTrashPage() %}
+      No deleted pages.
+      {% else %}
+      There are no pages under <strong>{{ path }}</strong>.
+      {% endif %}
+    {% endif  %}
+
+    {# list view #}
+    <div class="active tab-pane fade page-list-container in" id="view-list">
+      {% include '../../widget/page_list.html' with { pages: pages, pager: pager, viewConfig: viewConfig } %}
+    </div>
+
+    {# timeline view #}
+    {% if isEnabledTimeline() %}
+    <div class="tab-pane" id="view-timeline" data-shown=0>
+      {% for page in pages %}
+      <div class="timeline-body" id="id-{{ page.id }}">
+        <h3 class="revision-path"><a href="{{ page.path }}">{{ page.path }}</a></h3>
+        <div class="revision-body wiki"></div>
+        <script type="text/template">{{ page.revision.body }}</script>
+      </div>
+      <hr>
+      {% endfor %}
+    </div>
+    {% endif %}
+  </div>
+</div>

+ 4 - 0
lib/views/page_list.html

@@ -178,7 +178,9 @@
 <div class="page-list-container">
   <ul class="nav nav-tabs">
       <li class="active"><a href="#view-list" data-toggle="tab">{{ t('List View') }}</a></li>
+      {% if isEnabledTimeline() %}
       <li><a href="#view-timeline" data-toggle="tab">{{ t('Timeline View') }}</a></li>
+      {% endif %}
   </ul>
 
   <div class="tab-content">
@@ -212,6 +214,7 @@
     </div>
 
     {# timeline view #}
+    {% if isEnabledTimeline() %}
     <div class="tab-pane" id="view-timeline" data-shown=0>
       {% for page in pages %}
       <div class="timeline-body" id="id-{{ page.id }}">
@@ -222,6 +225,7 @@
       <hr>
       {% endfor %}
     </div>
+    {% endif %}
   </div>
 </div>
 

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "crowi-plus",
-  "version": "1.2.4-RC",
+  "version": "1.2.5-RC",
   "description": "Enhanced Crowi",
   "tags": [
     "wiki",

+ 1 - 0
resource/css/_layout.scss

@@ -69,6 +69,7 @@
 
       .header-wrap {
         padding: 16px 16px 0 16px;
+        min-height: 70px;
       }
 
       .content-main {

+ 14 - 0
resource/css/_page_crowi-plus.scss

@@ -34,3 +34,17 @@
     }
   }
 }
+
+.crowi-plus .crowi-plus-widget.page-list-container {
+  margin-top: 30px;
+
+  .tab-content {
+    margin-top: 15px;
+  }
+}
+
+.crowi-plus .content-main-new-page {
+  .nav {
+    margin-top: 30px;
+  }
+}

+ 1 - 1
resource/css/_page_list.scss

@@ -1,5 +1,5 @@
 
-.crowi.main-container .main .content-main .timeline-body { // {{{
+.crowi.main-container .main .timeline-body { // {{{
   .revision-path {
     margin-top: 1.6em;
     margin-bottom: 0;

+ 1 - 1
resource/js/app.js

@@ -74,7 +74,7 @@ const componentMappings = {
 };
 // additional definitions if pagePath exists
 if (pagePath) {
-  componentMappings['revision-path'] = <RevisionPath pagePath={pagePath} />;
+  componentMappings['revision-path'] = <RevisionPath pagePath={pagePath} crowi={crowi} />;
   componentMappings['revision-url'] = <RevisionUrl pageId={pageId} pagePath={pagePath} />;
 }
 

+ 26 - 3
resource/js/components/Page/RevisionPath.js

@@ -1,4 +1,6 @@
 import React from 'react';
+import PropTypes from 'prop-types';
+
 import CopyButton from '../CopyButton';
 
 export default class RevisionPath extends React.Component {
@@ -9,6 +11,7 @@ export default class RevisionPath extends React.Component {
     this.state = {
       pages: [],
       isListPage: false,
+      isLinkToListPage: true,
     };
   }
 
@@ -17,6 +20,11 @@ export default class RevisionPath extends React.Component {
     const isListPage = this.props.pagePath.match(/\/$/);
     this.setState({ isListPage });
 
+    // whether set link to '/'
+    const behaviorType = this.props.crowi.getConfig()['behaviorType'];
+    const isLinkToListPage = ('crowi-plus' !== behaviorType);
+    this.setState({ isLinkToListPage });
+
     // generate pages obj
     let splitted = this.props.pagePath.split(/\//);
     splitted.shift();   // omit first element with shift()
@@ -44,6 +52,18 @@ export default class RevisionPath extends React.Component {
     }, 1000);
   }
 
+  generateLinkElementToListPage(pagePath, isLinkToListPage, isLastElement) {
+    if (isLinkToListPage) {
+      return <a href={pagePath+'/'} className={(isLastElement && !this.state.isListPage) ? 'last-path' : ''}>/</a>;
+    }
+    else if (!isLastElement) {
+      return <span className="text-primary">/</span>;
+    }
+    else {
+      return <span></span>
+    }
+  }
+
   render() {
     // define style
     const rootStyle = {
@@ -60,14 +80,16 @@ export default class RevisionPath extends React.Component {
     this.state.pages.forEach((page, index) => {
       const isLastElement = (index == pageLength-1);
 
-      // add elements
+      // add elements for page
       afterElements.push(
         <span key={page.pagePath} className="path-segment">
           <a href={page.pagePath}>{page.pageName}</a>
         </span>);
+
+      // add elements for '/'
       afterElements.push(
         <span key={page.pagePath+'/'} className="separator" style={separatorStyle}>
-          <a href={page.pagePath+'/'} className={(isLastElement && !this.state.isListPage) ? 'last-path' : ''}>/</a>
+          {this.generateLinkElementToListPage(page.pagePath, this.state.isLinkToListPage, isLastElement)}
         </span>
       );
     });
@@ -86,5 +108,6 @@ export default class RevisionPath extends React.Component {
 }
 
 RevisionPath.propTypes = {
-  pagePath: React.PropTypes.string.isRequired,
+  pagePath: PropTypes.string.isRequired,
+  crowi: PropTypes.object.isRequired,
 };

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

@@ -213,7 +213,7 @@ $(function() {
     if (input2 === '') {
       prefix2 = prefix2.slice(0, -1);
     }
-    top.location.href = prefix1 + input1 + prefix2 + input2;
+    top.location.href = prefix1 + input1 + prefix2 + input2 + '#edit-form';
     return false;
   });
 
@@ -225,7 +225,7 @@ $(function() {
     if (name.match(/.+\/$/)) {
       name = name.substr(0, name.length - 1);
     }
-    top.location.href = name;
+    top.location.href = name + '#edit-form';
     return false;
   });