فهرست منبع

Merge pull request #341 from weseek/master

release v3.0.4
Yuki Takei 8 سال پیش
والد
کامیت
cb5c9d686c

+ 7 - 1
CHANGES.md

@@ -1,7 +1,13 @@
 CHANGES
 ========
 
-## 3.0.3-RC
+## 3.0.4-RC
+
+* Improvement: The option that switch whether add h1 section when create new page
+* Improvement: Encode page path that includes special character
+* Fix: Page-saving error after new page creation
+
+## 3.0.3
 
 * Fix: Login page is broken in iOS
 * Fix: Hide presentation tab if portal page

+ 2 - 1
lib/form/admin/customfeatures.js

@@ -5,6 +5,7 @@ var form = require('express-form')
 
 module.exports = form(
   field('settingForm[customize:isEnabledTimeline]').trim().toBooleanStrict(),
-  field('settingForm[customize:isSavedStatesOfTabChanges]').trim().toBooleanStrict()
+  field('settingForm[customize:isSavedStatesOfTabChanges]').trim().toBooleanStrict(),
+  field('settingForm[customize:isEnabledAttachTitleHeader]').trim().toBooleanStrict()
 );
 

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

@@ -69,8 +69,8 @@
   "Show": "Show",
   "Hide": "Hide",
   "Disclose E-mail": "Disclose E-mail",
-  
-  
+
+
 
   "Create today's": "Create today's ...",
   "Memo": "memo",
@@ -306,7 +306,9 @@
     "reflect_change": "You need to reload the page to reflect the change.",
     "ctrl_space": "Ctrl+Space to Autocomplete",
     "Custom script": "Custom script",
-    "write_java": "You can write Javascript that is applied to whole system."
+    "write_java": "You can write Javascript that is applied to whole system.",
+    "attach_title_header": "Add h1 section when create new page automatically",
+    "attach_title_header_desc": "Add page path to the first line as h1 section when create new page"
   },
 
   "user_management": {

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

@@ -66,9 +66,9 @@
   "Show": "公開",
   "Hide": "非公開",
   "Disclose E-mail": "メールアドレスの公開",
-  
- 
-  
+
+
+
 
   "Create today's": "今日の◯◯を作成",
   "Memo": "メモ",
@@ -326,8 +326,9 @@
     "reflect_change": "変更の反映はページの更新が必要です。",
     "ctrl_space": "Ctrl+Space でコード補完",
     "Custom script": "カスタムスクリプト",
-    "write_java": "システム全体に適用されるJavaScriptを記述できます。"
-
+    "write_java": "システム全体に適用されるJavaScriptを記述できます。",
+    "attach_title_header": "新規ページ作成時の h1 セクション自動挿入",
+    "attach_title_header_desc": "新規作成したページの1行目に、ページのパスを h1 セクションとして挿入します。"
   },
 
   "user_management": {

+ 7 - 0
lib/models/config.js

@@ -91,6 +91,7 @@ module.exports = function(crowi) {
       'customize:layout' : 'crowi',
       'customize:isEnabledTimeline' : true,
       'customize:isSavedStatesOfTabChanges' : true,
+      'customize:isEnabledAttachTitleHeader' : false,
     };
   }
 
@@ -422,6 +423,12 @@ module.exports = function(crowi) {
     return getValueForCrowiNS(config, key);
   };
 
+  configSchema.statics.isEnabledAttachTitleHeader = function(config)
+  {
+    const key = 'customize:isEnabledAttachTitleHeader';
+    return getValueForCrowiNS(config, key);
+  };
+
   configSchema.statics.fileUploadEnabled = function(config)
   {
     const Config = this;

+ 10 - 9
lib/models/page.js

@@ -731,19 +731,20 @@ module.exports = function(crowi) {
     var includeDeletedPage = option.includeDeletedPage || false;
     var isRegExpEscapedFromPath = option.isRegExpEscapedFromPath || false;
 
+    let pathSlashOmitted = path;
+    if (path.match(/\/$/)) {
+      // add condition for finding the page completely match with `path`
+      pathSlashOmitted = path.substr(0, path.length -1);
+      pathCondition.push({path: pathSlashOmitted});
+    }
+
     // create forward match pattern
     var pattern = (isRegExpEscapedFromPath)
-      ? `^${escapeStringRegexp(path)}`  // escape
-      : `^${path}`;
-    var queryReg = new RegExp(pattern);
+      ? escapeStringRegexp(pathSlashOmitted)  // escape
+      : pathSlashOmitted;
+    var queryReg = new RegExp('^' + pattern);
     pathCondition.push({path: queryReg});
 
-    // add condition for finding the page completely match with `path`
-    if (path.match(/\/$/)) {
-      debug('Page list by ending with /, so find also upper level page');
-      pathCondition.push({path: path.substr(0, path.length -1)});
-    }
-
     var q = Page.find({
       redirectTo: null,
       $or: [

+ 13 - 12
lib/routes/page.js

@@ -11,6 +11,7 @@ module.exports = function(crowi, app) {
     , UserGroupRelation = crowi.model('UserGroupRelation')
     , ApiResponse = require('../util/apiResponse')
     , interceptorManager = crowi.getInterceptorManager()
+    , pagePathUtil = require('../util/pagePathUtil')
 
     , actions = {};
 
@@ -193,7 +194,7 @@ module.exports = function(crowi, app) {
         seener_threshold: SEENER_THRESHOLD,
       };
       renderVars.pager = generatePager(pagerOptions);
-      renderVars.pages = pageList;
+      renderVars.pages = pagePathUtil.encodePagesPath(pageList);
       res.render('customlayout-selector/page_list', renderVars);
     }).catch(function(err) {
       debug('Error on rendering pageListShow', err);
@@ -248,7 +249,7 @@ module.exports = function(crowi, app) {
       if (page.redirectTo) {
         debug(`Redirect to '${page.redirectTo}'`);
         isRedirect = true;
-        return res.redirect(encodeURI(page.redirectTo + '?redirectFrom=' + page.path));
+        return res.redirect(encodeURI(page.redirectTo + '?redirectFrom=' + pagePathUtil.encodePagePath(page.path)));
       }
 
       renderVars.page = page;
@@ -320,7 +321,7 @@ module.exports = function(crowi, app) {
             seener_threshold: SEENER_THRESHOLD,
           };
           renderVars.pager = generatePager(pagerOptions);
-          renderVars.pages = pageList;
+          renderVars.pages = pagePathUtil.encodePagesPath(pageList);
 
           return Promise.resolve();
         })
@@ -379,7 +380,7 @@ module.exports = function(crowi, app) {
       pagerOptions.length = pageList.length;
 
       renderVars.pager = generatePager(pagerOptions);
-      renderVars.pages = pageList;
+      renderVars.pages = pagePathUtil.encodePagesPath(pageList);
       res.render('customlayout-selector/page_list', renderVars);
     }).catch(function(err) {
       debug('Error on rendering deletedPageListShow', err);
@@ -408,7 +409,7 @@ module.exports = function(crowi, app) {
 
       res.render('customlayout-selector/page_list', {
         path: '/',
-        pages: pages,
+        pages: pagePathUtil.encodePagesPath(pages),
         pager: generatePager({offset: 0, limit: 50})
       });
     }).catch(function(err) {
@@ -426,7 +427,7 @@ module.exports = function(crowi, app) {
     }
 
     if (pageData.redirectTo) {
-      return res.redirect(encodeURI(pageData.redirectTo + '?redirectFrom=' + pageData.path));
+      return res.redirect(encodeURI(pageData.redirectTo + '?redirectFrom=' + pagePathUtil.encodePagePath(pageData.path)));
     }
 
     var renderVars = {
@@ -519,7 +520,7 @@ module.exports = function(crowi, app) {
         return ;
       }
       if (req.query.revision) {
-        return res.redirect(encodeURI(path));
+        return res.redirect(pagePathUtil.encodePagePath(path));
       }
 
       if (isMarkdown) {
@@ -529,13 +530,13 @@ module.exports = function(crowi, app) {
       Page.hasPortalPage(path + '/', req.user)
       .then(function(page) {
         if (page) {
-          return res.redirect(encodeURI(path) + '/');
+          return res.redirect(pagePathUtil.encodePagePath(path) + '/');
         } else {
 
           var fixed = Page.fixToCreatableName(path)
           if (fixed !== path) {
             debug('fixed page name', fixed)
-            res.redirect(encodeURI(fixed));
+            res.redirect(pagePathUtil.encodePagePath(fixed));
             return ;
           }
 
@@ -568,7 +569,7 @@ module.exports = function(crowi, app) {
 
     debug('notify: ', notify);
 
-    var redirectPath = encodeURI(path);
+    var redirectPath = pagePathUtil.encodePagePath(path);
     var pageData = {};
     var updateOrCreate;
     var previousRevision = false;
@@ -657,7 +658,7 @@ module.exports = function(crowi, app) {
       return Promise.resolve(pageData);
     }).then(function(page) {
 
-      return res.redirect(encodeURI(page.path));
+      return res.redirect(pagePathUtil.encodePagePath(page.path));
     }).catch(function(err) {
       return res.redirect('/');
     });
@@ -709,7 +710,7 @@ module.exports = function(crowi, app) {
       pagerOptions.length = pages.length;
 
       var result = {};
-      result.pages = pages;
+      result.pages = pagePathUtil.encodePagesPath(pages);
       return res.json(ApiResponse.success(result));
     }).catch(function(err) {
       return res.json(ApiResponse.error(err));

+ 24 - 0
lib/util/pagePathUtil.js

@@ -0,0 +1,24 @@
+'use strict';
+
+function encodePagesPath(pages) {
+  pages.forEach(function(page) {
+    if (!page.path) {
+      return;
+    }
+    page.path = encodePagePath(page.path);
+  });
+  return pages;
+}
+
+function encodePagePath(path) {
+  var paths = path.split('/');
+  paths.forEach(function(item, index) {
+    paths[index] = encodeURIComponent(item);
+  });
+  return paths.join('/');
+}
+
+module.exports = {
+  encodePagePath: encodePagePath,
+  encodePagesPath: encodePagesPath
+};

+ 5 - 0
lib/util/swigFunctions.js

@@ -171,6 +171,11 @@ module.exports = function(crowi, app, req, locals) {
     return Config.isUploadable(config);
   };
 
+  locals.isEnabledAttachTitleHeader = function() {
+    var config = crowi.getConfig()
+    return Config.isEnabledAttachTitleHeader(config);
+  };
+
   locals.parentPath = function(path) {
     if (path == '/') {
       return path;

+ 18 - 0
lib/views/admin/customize.html

@@ -244,6 +244,24 @@
           </div>
         </div>
 
+        <div class="form-group">
+          <label for="settingForm[customize:isEnabledAttachTitleHeader]" class="col-xs-3 control-label">{{ t("customize_page.attach_title_header") }}</label>
+          <div class="col-xs-9">
+            <div class="btn-group btn-toggle" data-toggle="buttons">
+              <label class="btn btn-default btn-rounded btn-outline {% if settingForm['customize:isEnabledAttachTitleHeader'] %}active{% endif %}" data-active-class="primary">
+                <input name="settingForm[customize:isEnabledAttachTitleHeader]" value="true" type="radio" {% if true===settingForm['customize:isEnabledAttachTitleHeader'] %}checked{% endif %}> ON
+              </label>
+              <label class="btn btn-default btn-rounded btn-outline {% if !settingForm['customize:isEnabledAttachTitleHeader'] %}active{% endif %}" data-active-class="default">
+                <input name="settingForm[customize:isEnabledAttachTitleHeader]" value="false" type="radio" {% if !settingForm['customize:isEnabledAttachTitleHeader'] %}checked{% endif %}> OFF
+              </label>
+            </div>
+
+            <p class="help-block">
+              {{ t("customize_page.attach_title_header_desc") }}
+            </p>
+          </div>
+        </div>
+
         <div class="form-group">
           <div class="col-xs-offset-3 col-xs-6">
             <input type="hidden" name="_csrf" value="{{ csrf() }}">

+ 3 - 1
lib/views/layout-crowi/page.html

@@ -29,7 +29,9 @@
 
 
 {% block content_main %}
-  {% include '../widget/page_content.html' %}
+  <div class="m-b-30">
+    {% include '../widget/page_content.html' %}
+  </div>
 {% endblock %}
 
 

+ 4 - 2
lib/views/layout-crowi/page_list.html

@@ -59,9 +59,11 @@
   {% endif %}
   {% endif %}
 
-  {% include '../widget/page_content.html' %}
+  <div class="{% if isPortal %}m-b-30{% endif %}">
+    {% include '../widget/page_content.html' %}
+  </div>
 
-  <div class="row page-list m-t-30">
+  <div class="row page-list">
     <div class="col-md-12">
       {% include '../widget/page_list_and_timeline.html' %}
     </div>

+ 2 - 2
lib/views/layout-growi/page.html

@@ -11,7 +11,7 @@
 
 
 {% block content_main %}
-  <div class="row">
+  <div class="row m-b-30">
 
     <div class="col-lg-10 col-md-9">
 
@@ -34,7 +34,7 @@
   </div>
 
   {% if 'growi' === behaviorType() || 'crowi-plus' === behaviorType() %}
-  <div class="row page-list m-t-30">
+  <div class="row page-list">
     <div class="col-md-12">
       {% include '../widget/page_list_and_timeline.html' %}
     </div>

+ 2 - 2
lib/views/layout-growi/page_list.html

@@ -11,7 +11,7 @@
 
 
 {% block content_main %}
-  <div class="row">
+  <div class="row m-b-30">
 
     <div class="col-lg-10 col-md-9">
 
@@ -33,7 +33,7 @@
 
   </div>
 
-  <div class="row page-list m-t-30">
+  <div class="row page-list">
     <div class="col-md-12">
       {% include '../widget/page_list_and_timeline.html' %}
     </div>

+ 2 - 2
lib/views/layout-growi/user_page.html

@@ -15,7 +15,7 @@
 
 
 {% block content_main %}
-  <div class="row">
+  <div class="row m-b-30">
 
     <div class="col-lg-10 col-md-9">
 
@@ -52,7 +52,7 @@
   </div>
 
   {% if 'growi' === behaviorType() || 'crowi-plus' === behaviorType() %}
-  <div class="row page-list m-t-30">
+  <div class="row page-list">
     <div class="col-md-12">
       {% include '../widget/page_list_and_timeline.html' %}
     </div>

+ 6 - 6
lib/views/layout/layout.html

@@ -161,24 +161,24 @@ gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js
 
       <ul class="nav navbar-top-links navbar-right pull-right">
         {% if user and user.admin %}
-        <li id="">
-          <a href="/admin" id="link-mypage">
-            <i class="icon-settings"></i> {{ t('Admin') }}
+        <li class="nav-item-admin">
+          <a href="/admin">
+            <i class="icon-settings"></i><span>{{ t('Admin') }}</span>
           </a>
         </li>
         {% endif %}
 
         {% if user %}
-        <li id="" class="dropdown">
+        <li class="nav-item-create-page">
           <a href="#" data-target="#create-page" data-toggle="modal">
-            <i class="icon-pencil"></i> {{ t('New') }}
+            <i class="icon-pencil"></i><span>{{ t('New') }}</span>
           </a>
         </li>
         <li class="dropdown">
           <a class="dropdown-toggle waves-effect waves-light" data-toggle="dropdown">
             <img src="{{ user|picture }}" class="picture img-circle" width="25" /> {{ user.name }}
           </a>
-          <ul class="dropdown-menu">
+          <ul class="dropdown-menu dropdown-menu-right">
             <li><a href="/user/{{ user.username }}"><i class="icon-fw icon-home"></i>{{ t('Home') }}</a></li>
             <li><a href="/me"><i class="icon-fw icon-wrench"></i>{{ t('User Settings') }}</a></li>
             <li role="separator" class="divider"></li>

+ 3 - 7
lib/views/widget/not_found_content.html

@@ -16,17 +16,13 @@
   {% include 'not_found_tabs.html' %}
 
   <div class="tab-content">
-    {#
-     # Commented out temporally -- 2018.04.07 Yuki Takei
-     # This will be fixed by https://github.com/weseek/growi/issues/324
-     #
-     #
+    {% if isEnabledAttachTitleHeader() %}
     <script type="text/template" id="raw-text-original"># {{ path|path2name }}</script>
-     #}
+    {% endif %}
     {# list view #}
     <div class="p-t-10 active tab-pane page-list-container" id="revision-body">
       {% if pages.length == 0 %}
-        <div class="m-t-20">
+        <div class="m-t-10">
           There are no pages under <strong>{{ path }}</strong>.
         </div>
       {% endif  %}

+ 1 - 1
lib/views/widget/page_list.html

@@ -12,7 +12,7 @@
   <a href="{{ page.path }}"
     class="page-list-link"
     data-path="{{ page.path }}"
-    data-short-path="{{ page.path|path2name }}">{{ page.path }}
+    data-short-path="{{ page.path|path2name }}">{{ decodeURIComponent(page.path) }}
   </a>
   <span class="page-list-meta">
     {% if page.isPortal() %}

+ 11 - 10
lib/views/widget/page_list_and_timeline.html

@@ -7,18 +7,19 @@
   </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="p-t-10 active tab-pane fade page-list-container in" id="view-list">
-      {% include 'page_list.html' with { pages: pages, pager: pager, viewConfig: viewConfig } %}
+      {% if pages.length == 0 %}
+        <div class="m-t-10">
+          {% if isTrashPage() %}
+          No deleted pages.
+          {% else %}
+          There are no pages under <strong>{{ path }}</strong>.
+          {% endif %}
+        </div>
+      {% else %}
+        {% include 'page_list.html' with { pages: pages, pager: pager, viewConfig: viewConfig } %}
+      {% endif %}
     </div>
 
     {# timeline view #}

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "3.0.3-RC",
+  "version": "3.0.4-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",

+ 1 - 1
resource/js/components/Page/RevisionPath.js

@@ -36,7 +36,7 @@ export default class RevisionPath extends React.Component {
     let parentPath = '/';
     splitted.forEach((pageName) => {
       pages.push({
-        pagePath: parentPath + pageName,
+        pagePath: parentPath + encodeURIComponent(pageName),
         pageName: pageName,
       });
       parentPath += pageName + '/';

+ 5 - 3
resource/js/components/PageEditor.js

@@ -22,6 +22,7 @@ export default class PageEditor extends React.Component {
     const isMathJaxEnabled = !!config.env.MATHJAX;
 
     this.state = {
+      pageId: this.props.pageId,
       revisionId: this.props.revisionId,
       markdown: this.props.markdown,
       isUploadable,
@@ -110,10 +111,10 @@ export default class PageEditor extends React.Component {
     let data;
 
     // update
-    if (this.props.pageId != null) {
+    if (this.state.pageId != null) {
       endpoint = '/pages.update';
       data = {
-        page_id: this.props.pageId,
+        page_id: this.state.pageId,
         revision_id: this.state.revisionId,
         body: this.state.markdown,
       };
@@ -295,9 +296,10 @@ export default class PageEditor extends React.Component {
   pageSavedHandler(page) {
     // update states
     this.setState({
+      pageId: page.id,
       revisionId: page.revision._id,
       markdown: page.revision.body
-    })
+    });
 
     // clear draft
     this.clearDraft();

+ 4 - 3
resource/js/legacy/crowi.js

@@ -15,6 +15,7 @@ require('bootstrap-sass');
 require('jquery.cookie');
 
 require('./thirdparty-js/agile-admin');
+const pagePathUtil = require('../../../lib/util/pagePathUtil');
 
 var Crowi = {};
 
@@ -233,7 +234,7 @@ $(function() {
     if (name.match(/.+\/$/)) {
       name = name.substr(0, name.length - 1);
     }
-    top.location.href = name + '#edit-form';
+    top.location.href = pagePathUtil.encodePagePath(name) + '#edit-form';
     return false;
   });
 
@@ -385,8 +386,8 @@ $(function() {
   $('.page-list-link').each(function() {
     var $link = $(this);
     var text = $link.text();
-    var path = $link.data('path');
-    var shortPath = String($link.data('short-path')); // convert to string
+    var path = decodeURIComponent($link.data('path'));
+    var shortPath = decodeURIComponent($link.data('short-path')); // convert to string
 
     if (path == null || shortPath == null) {
       // continue

+ 9 - 0
resource/styles/scss/_layout.scss

@@ -16,6 +16,15 @@
         // margin-right: 5px;
       }
     }
+
+    .nav-item-admin, .nav-item-create-page {
+      span {
+        margin-left: 0.5em;
+        @media (max-width: $screen-xs-min) {
+          display: none;
+        }
+      }
+    }
   } // }}}
 
   .main {