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

Merge branch 'master' into feat/editor-component-comment-write

yusuketk 7 лет назад
Родитель
Сommit
93589a5114

+ 1 - 0
.eslintrc.js

@@ -69,6 +69,7 @@ module.exports = {
       "error",
       "error",
       { "args": "none" }
       { "args": "none" }
     ],
     ],
+    "no-var": [ "error" ],
     "quotes": [
     "quotes": [
       "error",
       "error",
       "single"
       "single"

+ 5 - 1
CHANGES.md

@@ -1,10 +1,14 @@
 CHANGES
 CHANGES
 ========
 ========
 
 
-## 3.1.8
+## 3.1.8-RC
 
 
+* Imprv: Place the commented page at the beginning of the list
+* Imprv: Resolve errors on IE11 (Experimental)
+* Support: Migrate to webpack 4 
 * Support: Upgrade libs
 * Support: Upgrade libs
     * react-bootstrap-typeahead
     * react-bootstrap-typeahead
+    * react-codemirror2
     * webpack
     * webpack
 
 
 ## 3.1.7
 ## 3.1.7

+ 5 - 5
lib/models/bookmark.js

@@ -13,9 +13,9 @@ module.exports = function(crowi) {
   bookmarkSchema.index({page: 1, user: 1}, {unique: true});
   bookmarkSchema.index({page: 1, user: 1}, {unique: true});
 
 
   bookmarkSchema.statics.populatePage = function(bookmarks, requestUser) {
   bookmarkSchema.statics.populatePage = function(bookmarks, requestUser) {
-    var Bookmark = this;
-    var User = crowi.model('User');
-    var Page = crowi.model('Page');
+    const Bookmark = this;
+    const User = crowi.model('User');
+    const Page = crowi.model('Page');
 
 
     requestUser = requestUser || null;
     requestUser = requestUser || null;
 
 
@@ -36,13 +36,13 @@ module.exports = function(crowi) {
           return bookmark.page.isGrantedFor(requestUser);
           return bookmark.page.isGrantedFor(requestUser);
         });
         });
 
 
-        return Bookmark.populate(bookmarks, {path: 'page.revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS});
+        return Bookmark.populate(bookmarks, {path: 'lastUpdateUser', model: 'User', select: User.USER_PUBLIC_FIELDS});
       });
       });
   };
   };
 
 
   // bookmark チェック用
   // bookmark チェック用
   bookmarkSchema.statics.findByPageIdAndUserId = function(pageId, userId) {
   bookmarkSchema.statics.findByPageIdAndUserId = function(pageId, userId) {
-    var Bookmark = this;
+    const Bookmark = this;
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       return Bookmark.findOne({ page: pageId, user: userId }, function(err, doc) {
       return Bookmark.findOne({ page: pageId, user: userId }, function(err, doc) {

+ 12 - 14
lib/models/page.js

@@ -37,8 +37,6 @@ module.exports = function(crowi) {
     grant: { type: Number, default: GRANT_PUBLIC, index: true },
     grant: { type: Number, default: GRANT_PUBLIC, index: true },
     grantedUsers: [{ type: ObjectId, ref: 'User' }],
     grantedUsers: [{ type: ObjectId, ref: 'User' }],
     creator: { type: ObjectId, ref: 'User', index: true },
     creator: { type: ObjectId, ref: 'User', index: true },
-    // lastUpdateUser: this schema is from 1.5.x (by deletion feature), and null is default.
-    // the last update user on the screen is by revesion.author for B.C.
     lastUpdateUser: { type: ObjectId, ref: 'User', index: true },
     lastUpdateUser: { type: ObjectId, ref: 'User', index: true },
     liker: [{ type: ObjectId, ref: 'User', index: true }],
     liker: [{ type: ObjectId, ref: 'User', index: true }],
     seenUsers: [{ type: ObjectId, ref: 'User', index: true }],
     seenUsers: [{ type: ObjectId, ref: 'User', index: true }],
@@ -263,8 +261,8 @@ module.exports = function(crowi) {
   };
   };
 
 
   pageSchema.statics.populatePageData = function(pageData, revisionId) {
   pageSchema.statics.populatePageData = function(pageData, revisionId) {
-    var Page = crowi.model('Page');
-    var User = crowi.model('User');
+    const Page = crowi.model('Page');
+    const User = crowi.model('User');
 
 
     pageData.latestRevision = pageData.revision;
     pageData.latestRevision = pageData.revision;
     if (revisionId) {
     if (revisionId) {
@@ -491,8 +489,8 @@ module.exports = function(crowi) {
 
 
   // find page and check if granted user
   // find page and check if granted user
   pageSchema.statics.findPage = function(path, userData, revisionId, ignoreNotFound) {
   pageSchema.statics.findPage = function(path, userData, revisionId, ignoreNotFound) {
-    var self = this;
-    var PageGroupRelation = crowi.model('PageGroupRelation');
+    const self = this;
+    const PageGroupRelation = crowi.model('PageGroupRelation');
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       self.findOne({path: path}, function(err, pageData) {
       self.findOne({path: path}, function(err, pageData) {
@@ -505,7 +503,7 @@ module.exports = function(crowi) {
             return resolve(null);
             return resolve(null);
           }
           }
 
 
-          var pageNotFoundError = new Error('Page Not Found');
+          const pageNotFoundError = new Error('Page Not Found');
           pageNotFoundError.name = 'Crowi:Page:NotFound';
           pageNotFoundError.name = 'Crowi:Page:NotFound';
           return reject(pageNotFoundError);
           return reject(pageNotFoundError);
         }
         }
@@ -641,12 +639,12 @@ module.exports = function(crowi) {
   };
   };
 
 
   pageSchema.statics.findListByPageIds = function(ids, options) {
   pageSchema.statics.findListByPageIds = function(ids, options) {
-    var Page = this;
-    var User = crowi.model('User');
-    var options = options || {}
-      , limit = options.limit || 50
+    const Page = this;
+    const User = crowi.model('User');
+    const limit = options.limit || 50
       , offset = options.skip || 0
       , offset = options.skip || 0
       ;
       ;
+    options = options || {};
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       Page
       Page
@@ -663,7 +661,7 @@ module.exports = function(crowi) {
           return reject(err);
           return reject(err);
         }
         }
 
 
-        Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS}, function(err, data) {
+        Page.populate(pages, {path: 'lastUpdateUser', model: 'User', select: User.USER_PUBLIC_FIELDS}, function(err, data) {
           if (err) {
           if (err) {
             return reject(err);
             return reject(err);
           }
           }
@@ -715,7 +713,7 @@ module.exports = function(crowi) {
       .populate('revision')
       .populate('revision')
       .exec()
       .exec()
       .then(function(pages) {
       .then(function(pages) {
-        return Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS}).then(resolve);
+        return Page.populate(pages, {path: 'lastUpdateUser', model: 'User', select: User.USER_PUBLIC_FIELDS}).then(resolve);
       });
       });
     });
     });
   };
   };
@@ -796,7 +794,7 @@ module.exports = function(crowi) {
 
 
       q.exec()
       q.exec()
         .then(function(pages) {
         .then(function(pages) {
-          Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS})
+          Page.populate(pages, {path: 'lastUpdateUser', model: 'User', select: User.USER_PUBLIC_FIELDS})
           .then(resolve)
           .then(resolve)
           .catch(reject);
           .catch(reject);
         });
         });

+ 11 - 12
lib/models/revision.js

@@ -3,12 +3,11 @@ module.exports = function(crowi) {
   const logger = require('@alias/logger')('growi:models:revision');
   const logger = require('@alias/logger')('growi:models:revision');
   /* eslint-enable */
   /* eslint-enable */
 
 
-  var mongoose = require('mongoose')
+  const mongoose = require('mongoose')
     , ObjectId = mongoose.Schema.Types.ObjectId
     , ObjectId = mongoose.Schema.Types.ObjectId
-    , revisionSchema;
+    ;
 
 
-
-  revisionSchema = new mongoose.Schema({
+  const revisionSchema = new mongoose.Schema({
     path: { type: String, required: true },
     path: { type: String, required: true },
     body: { type: String, required: true, get: (data) => {
     body: { type: String, required: true, get: (data) => {
       // replace CR/CRLF to LF above v3.1.5
       // replace CR/CRLF to LF above v3.1.5
@@ -42,7 +41,7 @@ module.exports = function(crowi) {
   };
   };
 
 
   revisionSchema.statics.findRevision = function(id) {
   revisionSchema.statics.findRevision = function(id) {
-    var Revision = this;
+    const Revision = this;
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       Revision.findById(id)
       Revision.findById(id)
@@ -58,7 +57,7 @@ module.exports = function(crowi) {
   };
   };
 
 
   revisionSchema.statics.findRevisions = function(ids) {
   revisionSchema.statics.findRevisions = function(ids) {
-    var Revision = this,
+    const Revision = this,
       User = crowi.model('User');
       User = crowi.model('User');
 
 
     if (!Array.isArray(ids)) {
     if (!Array.isArray(ids)) {
@@ -88,7 +87,7 @@ module.exports = function(crowi) {
   };
   };
 
 
   revisionSchema.statics.findRevisionList = function(path, options) {
   revisionSchema.statics.findRevisionList = function(path, options) {
-    var Revision = this,
+    const Revision = this,
       User = crowi.model('User');
       User = crowi.model('User');
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
@@ -106,7 +105,7 @@ module.exports = function(crowi) {
   };
   };
 
 
   revisionSchema.statics.updateRevisionListByPath = function(path, updateData, options) {
   revisionSchema.statics.updateRevisionListByPath = function(path, updateData, options) {
-    var Revision = this;
+    const Revision = this;
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       Revision.update({path: path}, {$set: updateData}, {multi: true}, function(err, data) {
       Revision.update({path: path}, {$set: updateData}, {multi: true}, function(err, data) {
@@ -120,18 +119,18 @@ module.exports = function(crowi) {
   };
   };
 
 
   revisionSchema.statics.prepareRevision = function(pageData, body, user, options) {
   revisionSchema.statics.prepareRevision = function(pageData, body, user, options) {
-    var Revision = this;
+    const Revision = this;
 
 
     if (!options) {
     if (!options) {
       options = {};
       options = {};
     }
     }
-    var format = options.format || 'markdown';
+    const format = options.format || 'markdown';
 
 
     if (!user._id) {
     if (!user._id) {
       throw new Error('Error: user should have _id');
       throw new Error('Error: user should have _id');
     }
     }
 
 
-    var newRevision = new Revision();
+    const newRevision = new Revision();
     newRevision.path = pageData.path;
     newRevision.path = pageData.path;
     newRevision.body = body;
     newRevision.body = body;
     newRevision.format = format;
     newRevision.format = format;
@@ -142,7 +141,7 @@ module.exports = function(crowi) {
   };
   };
 
 
   revisionSchema.statics.removeRevisionsByPath = function(path) {
   revisionSchema.statics.removeRevisionsByPath = function(path) {
-    var Revision = this;
+    const Revision = this;
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       Revision.remove({path: path}, function(err, data) {
       Revision.remove({path: path}, function(err, data) {

+ 21 - 17
lib/routes/comment.js

@@ -1,9 +1,8 @@
 module.exports = function(crowi, app) {
 module.exports = function(crowi, app) {
   'use strict';
   'use strict';
 
 
-  var debug = require('debug')('growi:routs:comment')
+  const debug = require('debug')('growi:routs:comment')
     , Comment = crowi.model('Comment')
     , Comment = crowi.model('Comment')
-    , User = crowi.model('User')
     , Page = crowi.model('Page')
     , Page = crowi.model('Page')
     , ApiResponse = require('../util/apiResponse')
     , ApiResponse = require('../util/apiResponse')
     , actions = {}
     , actions = {}
@@ -20,8 +19,8 @@ module.exports = function(crowi, app) {
    * @apiParam {String} revision_id Revision Id.
    * @apiParam {String} revision_id Revision Id.
    */
    */
   api.get = function(req, res) {
   api.get = function(req, res) {
-    var pageId = req.query.page_id;
-    var revisionId = req.query.revision_id;
+    const pageId = req.query.page_id;
+    const revisionId = req.query.revision_id;
 
 
     if (revisionId) {
     if (revisionId) {
       return Comment.getCommentsByRevisionId(revisionId)
       return Comment.getCommentsByRevisionId(revisionId)
@@ -50,27 +49,32 @@ module.exports = function(crowi, app) {
    * @apiParam {String} comment Comment body
    * @apiParam {String} comment Comment body
    * @apiParam {Number} comment_position=-1 Line number of the comment
    * @apiParam {Number} comment_position=-1 Line number of the comment
    */
    */
-  api.add = function(req, res) {
-    var form = req.form.commentForm;
+  api.add = async function(req, res) {
+    const form = req.form.commentForm;
 
 
     if (!req.form.isValid) {
     if (!req.form.isValid) {
       // return res.json(ApiResponse.error('Invalid comment.'));
       // return res.json(ApiResponse.error('Invalid comment.'));
       return res.json(ApiResponse.error('コメントを入力してください。'));
       return res.json(ApiResponse.error('コメントを入力してください。'));
     }
     }
 
 
-    var pageId = form.page_id;
-    var revisionId = form.revision_id;
-    var comment = form.comment;
-    var position = form.comment_position || -1;
-    var isMarkdown = form.is_markdown;
+    const pageId = form.page_id;
+    const revisionId = form.revision_id;
+    const comment = form.comment;
+    const position = form.comment_position || -1;
+    const isMarkdown = form.is_markdown;
 
 
-    return Comment.create(pageId, req.user._id, revisionId, comment, position, isMarkdown)
-      .then(function(createdComment) {
-        createdComment.creator = req.user;
-        return res.json(ApiResponse.success({comment: createdComment}));
-      }).catch(function(err) {
+    const createdComment = await Comment.create(pageId, req.user._id, revisionId, comment, position, isMarkdown)
+      .catch(function(err) {
         return res.json(ApiResponse.error(err));
         return res.json(ApiResponse.error(err));
       });
       });
+
+    // update page
+    await Page.findOneAndUpdate({ _id: pageId }, {
+      lastUpdateUser: req.user,
+      updatedAt: new Date()
+    });
+
+    return res.json(ApiResponse.success({comment: createdComment}));
   };
   };
 
 
   /**
   /**
@@ -81,7 +85,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} comment_id Comment Id.
    * @apiParam {String} comment_id Comment Id.
    */
    */
   api.remove = function(req, res) {
   api.remove = function(req, res) {
-    var commentId = req.body.comment_id;
+    const commentId = req.body.comment_id;
     if (!commentId) {
     if (!commentId) {
       return Promise.resolve(res.json(ApiResponse.error('\'comment_id\' is undefined')));
       return Promise.resolve(res.json(ApiResponse.error('\'comment_id\' is undefined')));
     }
     }

+ 3 - 3
lib/routes/revision.js

@@ -41,7 +41,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} page_id      Page Id.
    * @apiParam {String} page_id      Page Id.
    */
    */
   actions.api.ids = function(req, res) {
   actions.api.ids = function(req, res) {
-    var pageId = req.query.page_id || null;
+    const pageId = req.query.page_id || null;
 
 
     if (pageId && crowi.isPageId(pageId)) {
     if (pageId && crowi.isPageId(pageId)) {
       Page.findPageByIdAndGrantedUser(pageId, req.user)
       Page.findPageByIdAndGrantedUser(pageId, req.user)
@@ -68,8 +68,8 @@ module.exports = function(crowi, app) {
    * @apiParam {String} page_id      Page Id.
    * @apiParam {String} page_id      Page Id.
    */
    */
   actions.api.list = function(req, res) {
   actions.api.list = function(req, res) {
-    var revisionIds = (req.query.revision_ids || '').split(',');
-    var pageId = req.query.page_id || null;
+    const revisionIds = (req.query.revision_ids || '').split(',');
+    const pageId = req.query.page_id || null;
 
 
     if (pageId) {
     if (pageId) {
       Page.findPageByIdAndGrantedUser(pageId, req.user)
       Page.findPageByIdAndGrantedUser(pageId, req.user)

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

@@ -298,7 +298,7 @@
           </div>
           </div>
 
 
           <div class="form-group">
           <div class="form-group">
-            <label for="settingForm[customize:highlightJsStyleBorder]" class="col-xs-3 control-label">(TBD) Border</label>
+            <label for="settingForm[customize:highlightJsStyleBorder]" class="col-xs-3 control-label">Border</label>
             <div class="col-xs-9">
             <div class="col-xs-9">
               <div class="btn-group btn-toggle" data-toggle="buttons">
               <div class="btn-group btn-toggle" data-toggle="buttons">
                 <label class="btn btn-default btn-rounded btn-outline {% if settingForm['customize:highlightJsStyleBorder'] %}active{% endif %}" data-active-class="primary" onclick="selectBorderOn()">
                 <label class="btn btn-default btn-rounded btn-outline {% if settingForm['customize:highlightJsStyleBorder'] %}active{% endif %}" data-active-class="primary" onclick="selectBorderOn()">

+ 1 - 1
lib/views/admin/index.html

@@ -68,7 +68,7 @@
         <tr>
         <tr>
           <td>{{ pluginName }}</td>
           <td>{{ pluginName }}</td>
           <td class="text-center">{{ plugins[pluginName] }}</td>
           <td class="text-center">{{ plugins[pluginName] }}</td>
-          <td class="text-center">(TBD)</td>
+          <td class="text-center"><span class="tbd">(TBD)</span></td>
         </tr>
         </tr>
         {% endfor %}
         {% endfor %}
       </table>
       </table>

+ 1 - 1
lib/views/admin/markdown.html

@@ -62,7 +62,7 @@
           </div>
           </div>
         </div>
         </div>
 
 
-        <div class="form-group">
+        <div class="form-group tbd">
           <label for="markdownSetting[markdown:isEnabledLinebreaksInComments]" class="col-xs-4 control-label">
           <label for="markdownSetting[markdown:isEnabledLinebreaksInComments]" class="col-xs-4 control-label">
             (TBD)<br>{{ t("markdown_setting.Enable Line Break for comment") }}
             (TBD)<br>{{ t("markdown_setting.Enable Line Break for comment") }}
           </label>
           </label>

+ 8 - 8
lib/views/admin/security.html

@@ -120,10 +120,10 @@
               <ul>
               <ul>
                 <li>{{ t("security_setting.username_email_password") }}</li>
                 <li>{{ t("security_setting.username_email_password") }}</li>
                 <li>{{ t("security_setting.ldap_auth") }}</li>
                 <li>{{ t("security_setting.ldap_auth") }}</li>
-                <li class="text-muted">(TBD) <del>{{ t("security_setting.google_auth2") }}</del></li>
-                <li class="text-muted">(TBD) <del>{{ t("security_setting.facebook_auth2") }}</del></li>
-                <li class="text-muted">(TBD) <del>{{ t("security_setting.twitter_auth2") }}</del></li>
-                <li class="text-muted">(TBD) <del>{{ t("security_setting.github_auth2") }}</del></li>
+                <li class="text-muted tbd">(TBD) <del>{{ t("security_setting.google_auth2") }}</del></li>
+                <li class="text-muted tbd">(TBD) <del>{{ t("security_setting.facebook_auth2") }}</del></li>
+                <li class="text-muted tbd">(TBD) <del>{{ t("security_setting.twitter_auth2") }}</del></li>
+                <li class="text-muted tbd">(TBD) <del>{{ t("security_setting.github_auth2") }}</del></li>
               </ul>
               </ul>
             </div>
             </div>
             <div class="col-xs-6">
             <div class="col-xs-6">
@@ -232,16 +232,16 @@
             <li class="active">
             <li class="active">
               <a href="#passport-ldap" data-toggle="tab" role="tab"><i class="fa fa-sitemap"></i> LDAP</a>
               <a href="#passport-ldap" data-toggle="tab" role="tab"><i class="fa fa-sitemap"></i> LDAP</a>
             </li>
             </li>
-            <li>
+            <li class="tbd">
               <a href="#passport-google-oauth" data-toggle="tab" role="tab"><i class="fa fa-google"></i> (TBD) Google OAuth</a>
               <a href="#passport-google-oauth" data-toggle="tab" role="tab"><i class="fa fa-google"></i> (TBD) Google OAuth</a>
             </li>
             </li>
-            <li>
+            <li class="tbd">
               <a href="#passport-facebook" data-toggle="tab" role="tab"><i class="fa fa-facebook"></i> (TBD) Facebook</a>
               <a href="#passport-facebook" data-toggle="tab" role="tab"><i class="fa fa-facebook"></i> (TBD) Facebook</a>
             </li>
             </li>
-            <li>
+            <li class="tbd">
               <a href="#passport-twitter" data-toggle="tab" role="tab"><i class="fa fa-twitter"></i> (TBD) Twitter</a>
               <a href="#passport-twitter" data-toggle="tab" role="tab"><i class="fa fa-twitter"></i> (TBD) Twitter</a>
             </li>
             </li>
-            <li>
+            <li class="tbd">
               <a href="#passport-github" data-toggle="tab" role="tab"><i class="fa fa-github"></i> (TBD) Github</a>
               <a href="#passport-github" data-toggle="tab" role="tab"><i class="fa fa-github"></i> (TBD) Github</a>
             </li>
             </li>
           </ul>
           </ul>

+ 1 - 7
lib/views/layout-crowi/widget/page_side_header.html

@@ -13,13 +13,7 @@
       </p>
       </p>
       <p class="created-at">
       <p class="created-at">
         {{ t('Created') }}: {{ page.createdAt|datetz('Y/m/d H:i:s') }}<br>
         {{ t('Created') }}: {{ page.createdAt|datetz('Y/m/d H:i:s') }}<br>
-
-        {% if page.lastUpdateUser %}
-          {{ t('Last updated') }}: {{ page.updatedAt|datetz('Y/m/d H:i:s') }} <a href="/user/{{ page.lastUpdateUser.username }}"><img src="{{ page.lastUpdateUser|picture }}" class="picture picture-xs img-circle" alt="{{ page.lastUpdateUser.name }}"></a>
-        {% else %}
-          {# for BC 1.5.x #}
-          {{ t('Last updated') }}: {{ page.updatedAt|datetz('Y/m/d H:i:s') }} <a href="/user/{{ page.revision.author.username }}"><img src="{{ page.revision.author|picture }}" class="picture picture-xs img-circle" alt="{{ page.revision.author.name }}"></a>
-        {% endif %}
+        {{ t('Last updated') }}: {{ page.updatedAt|datetz('Y/m/d H:i:s') }} <a href="/user/{{ page.revision.author.username }}"><img src="{{ page.revision.author|picture }}" class="picture picture-xs img-circle" alt="{{ page.revision.author.name }}"></a>
       </p>
       </p>
     </div>
     </div>
   </div>
   </div>

+ 3 - 3
lib/views/layout-growi/widget/header.html

@@ -27,11 +27,11 @@
         </li>
         </li>
         <li class="m-t-5">
         <li class="m-t-5">
           <div class="d-flex align-items-center">
           <div class="d-flex align-items-center">
-            <a class="m-r-5" href="{{ userPageRoot(page.lastUpdateUser) }}">
-              <img src="{{ page.lastUpdateUser|default(author)|picture }}" class="picture img-circle">
+            <a class="m-r-5" href="{{ userPageRoot(page.revision.author) }}">
+              <img src="{{ page.revision.author|default(author)|picture }}" class="picture img-circle">
             </a>
             </a>
             <div>
             <div>
-              <div>Updated by <a href="{{ userPageRoot(page.lastUpdateUser) }}">{{ page.lastUpdateUser.name|default(author.name) }}</a></div>
+              <div>Updated by <a href="{{ userPageRoot(page.revision.author) }}">{{ page.revision.author.name|default(author.name) }}</a></div>
               <div class="text-muted">{{ page.updatedAt|datetz('Y/m/d H:i:s') }}</div>
               <div class="text-muted">{{ page.updatedAt|datetz('Y/m/d H:i:s') }}</div>
             </div>
             </div>
           </div>
           </div>

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

@@ -220,7 +220,7 @@ gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js
           {% endif %}
           {% endif %}
         </li>
         </li>
 
 
-        <li><a href="#">(TBD) Create /Sidebar</a></li>
+        <li class="tbd"><a href="#">(TBD) Create /Sidebar</a></li>
       </ul>
       </ul>
     </div>
     </div>
   </div>
   </div>

+ 4 - 4
lib/views/me/external-accounts.html

@@ -122,16 +122,16 @@
             <li class="active">
             <li class="active">
               <a href="#passport-ldap" data-toggle="tab" role="tab"><i class="fa fa-sitemap"></i> LDAP</a>
               <a href="#passport-ldap" data-toggle="tab" role="tab"><i class="fa fa-sitemap"></i> LDAP</a>
             </li>
             </li>
-            <li>
+            <li class="tbd">
               <a href="#passport-google-oauth" data-toggle="tab" role="tab"><i class="fa fa-google"></i> (TBD) Google OAuth</a>
               <a href="#passport-google-oauth" data-toggle="tab" role="tab"><i class="fa fa-google"></i> (TBD) Google OAuth</a>
             </li>
             </li>
-            <li>
+            <li class="tbd">
               <a href="#passport-facebook" data-toggle="tab" role="tab"><i class="fa fa-facebook"></i> (TBD) Facebook</a>
               <a href="#passport-facebook" data-toggle="tab" role="tab"><i class="fa fa-facebook"></i> (TBD) Facebook</a>
             </li>
             </li>
-            <li>
+            <li class="tbd">
               <a href="#passport-twitter" data-toggle="tab" role="tab"><i class="fa fa-twitter"></i> (TBD) Twitter</a>
               <a href="#passport-twitter" data-toggle="tab" role="tab"><i class="fa fa-twitter"></i> (TBD) Twitter</a>
             </li>
             </li>
-            <li>
+            <li class="tbd">
               <a href="#passport-github" data-toggle="tab" role="tab"><i class="fa fa-github"></i> (TBD) Github</a>
               <a href="#passport-github" data-toggle="tab" role="tab"><i class="fa fa-github"></i> (TBD) Github</a>
             </li>
             </li>
           </ul>
           </ul>

+ 2 - 9
lib/views/widget/page_attachments.html

@@ -4,15 +4,8 @@
       <div class="page-attachments" id="page-attachment"></div>
       <div class="page-attachments" id="page-attachment"></div>
 
 
       <p class="page-meta">
       <p class="page-meta">
-        Path: <span id="pagePath">{{ page.path }}</span><br>
-        {# for BC #}
-        {% if page.lastUpdateUser %}
-          Last updated at {{ page.updatedAt|datetz('Y-m-d H:i:s') }} by <img src="{{ page.lastUpdateUser|picture }}" class="picture img-circle"> {{ page.lastUpdateUser.name }}<br>
-        {% else %}
-          Last updated at {{ page.revision.createdAt|datetz('Y-m-d H:i:s') }} by <img src="{{ page.revision.author|picture }}" class="picture img-circle"> {{ page.revision.author.name }}<br>
-        {% endif %}
-        {# /for BC #}
-        Created at {{ page.createdAt|datetz('Y-m-d H:i:s') }} by <img src="{{ page.creator|default(page.creator)|picture }}" class="picture img-circle"> {{ page.creator.name }}<br>
+        <p>Last revision posted at {{ page.revision.createdAt|datetz('Y-m-d H:i:s') }} by <a href="/user/{{ page.revision.author.username }}"><img src="{{ page.revision.author|picture }}" class="picture picture-sm img-circle"> {{ page.revision.author.name }}</a></p>
+        <p>Created at {{ page.createdAt|datetz('Y-m-d H:i:s') }} by <a href="/user/{{ page.creator.username }}"><img src="{{ page.creator|default(page.creator)|picture }}" class="picture picture-sm img-circle"> {{ page.creator.name }}</a></p>
       </p>
       </p>
     </div>
     </div>
   </div>
   </div>

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

@@ -8,7 +8,7 @@
 {% endif %}
 {% endif %}
 
 
 <li>
 <li>
-  <img src="{{ page.revision.author|picture }}" class="picture img-circle">
+  <img src="{{ page.lastUpdateUser|picture }}" class="picture img-circle">
   <a href="{{ page.path }}"
   <a href="{{ page.path }}"
     class="page-list-link"
     class="page-list-link"
     data-path="{{ page.path }}"
     data-path="{{ page.path }}"

+ 1 - 1
package.json

@@ -169,7 +169,7 @@
     "react-bootstrap": "^0.32.1",
     "react-bootstrap": "^0.32.1",
     "react-bootstrap-typeahead": "^3.1.4",
     "react-bootstrap-typeahead": "^3.1.4",
     "react-clipboard.js": "^2.0.0",
     "react-clipboard.js": "^2.0.0",
-    "react-codemirror2": "^5.0.0",
+    "react-codemirror2": "^5.0.4",
     "react-dom": "^16.2.0",
     "react-dom": "^16.2.0",
     "react-dropzone": "^4.2.7",
     "react-dropzone": "^4.2.7",
     "react-i18next": "^7.6.1",
     "react-i18next": "^7.6.1",

+ 2 - 2
resource/js/components/Admin/CustomCssEditor.js

@@ -2,13 +2,13 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
 import { UnControlled as CodeMirror } from 'react-codemirror2';
 import { UnControlled as CodeMirror } from 'react-codemirror2';
-require('codemirror/addon/display/autorefresh');
 require('codemirror/addon/lint/css-lint');
 require('codemirror/addon/lint/css-lint');
 require('codemirror/addon/hint/css-hint');
 require('codemirror/addon/hint/css-hint');
 require('codemirror/addon/hint/show-hint');
 require('codemirror/addon/hint/show-hint');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/closebrackets');
 require('codemirror/addon/edit/closebrackets');
 require('codemirror/mode/css/css');
 require('codemirror/mode/css/css');
+require('../../util/codemirror/autorefresh.ext');
 
 
 require('jquery-ui/ui/widgets/resizable');
 require('jquery-ui/ui/widgets/resizable');
 
 
@@ -32,7 +32,7 @@ export default class CustomCssEditor extends React.Component {
           tabSize: 2,
           tabSize: 2,
           indentUnit: 2,
           indentUnit: 2,
           theme: 'eclipse',
           theme: 'eclipse',
-          autoRefresh: true,
+          autoRefresh: {force: true},   // force option is enabled by autorefresh.ext.js -- Yuki Takei
           matchBrackets: true,
           matchBrackets: true,
           autoCloseBrackets: true,
           autoCloseBrackets: true,
           extraKeys: {'Ctrl-Space': 'autocomplete'},
           extraKeys: {'Ctrl-Space': 'autocomplete'},

+ 2 - 2
resource/js/components/Admin/CustomHeaderEditor.js

@@ -2,11 +2,11 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
 import { UnControlled as CodeMirror } from 'react-codemirror2';
 import { UnControlled as CodeMirror } from 'react-codemirror2';
-require('codemirror/addon/display/autorefresh');
 require('codemirror/addon/hint/show-hint');
 require('codemirror/addon/hint/show-hint');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/closebrackets');
 require('codemirror/addon/edit/closebrackets');
 require('codemirror/mode/htmlmixed/htmlmixed');
 require('codemirror/mode/htmlmixed/htmlmixed');
+require('../../util/codemirror/autorefresh.ext');
 
 
 require('jquery-ui/ui/widgets/resizable');
 require('jquery-ui/ui/widgets/resizable');
 
 
@@ -30,7 +30,7 @@ export default class CustomHeaderEditor extends React.Component {
           tabSize: 2,
           tabSize: 2,
           indentUnit: 2,
           indentUnit: 2,
           theme: 'eclipse',
           theme: 'eclipse',
-          autoRefresh: true,
+          autoRefresh: {force: true},   // force option is enabled by autorefresh.ext.js -- Yuki Takei
           matchBrackets: true,
           matchBrackets: true,
           autoCloseBrackets: true,
           autoCloseBrackets: true,
           extraKeys: {'Ctrl-Space': 'autocomplete'},
           extraKeys: {'Ctrl-Space': 'autocomplete'},

+ 2 - 2
resource/js/components/Admin/CustomScriptEditor.js

@@ -2,13 +2,13 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
 import { UnControlled as CodeMirror } from 'react-codemirror2';
 import { UnControlled as CodeMirror } from 'react-codemirror2';
-require('codemirror/addon/display/autorefresh');
 require('codemirror/addon/lint/javascript-lint');
 require('codemirror/addon/lint/javascript-lint');
 require('codemirror/addon/hint/javascript-hint');
 require('codemirror/addon/hint/javascript-hint');
 require('codemirror/addon/hint/show-hint');
 require('codemirror/addon/hint/show-hint');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/closebrackets');
 require('codemirror/addon/edit/closebrackets');
 require('codemirror/mode/javascript/javascript');
 require('codemirror/mode/javascript/javascript');
+require('../../util/codemirror/autorefresh.ext');
 
 
 require('jquery-ui/ui/widgets/resizable');
 require('jquery-ui/ui/widgets/resizable');
 
 
@@ -32,7 +32,7 @@ export default class CustomScriptEditor extends React.Component {
           tabSize: 2,
           tabSize: 2,
           indentUnit: 2,
           indentUnit: 2,
           theme: 'eclipse',
           theme: 'eclipse',
-          autoRefresh: true,
+          autoRefresh: {force: true},   // force option is enabled by autorefresh.ext.js -- Yuki Takei
           matchBrackets: true,
           matchBrackets: true,
           autoCloseBrackets: true,
           autoCloseBrackets: true,
           extraKeys: {'Ctrl-Space': 'autocomplete'},
           extraKeys: {'Ctrl-Space': 'autocomplete'},

+ 2 - 2
resource/js/components/PageEditor/CodeMirrorEditor.js

@@ -10,7 +10,6 @@ const loadCssSync = require('load-css-file');
 import * as codemirror from 'codemirror';
 import * as codemirror from 'codemirror';
 
 
 import { UnControlled as ReactCodeMirror } from 'react-codemirror2';
 import { UnControlled as ReactCodeMirror } from 'react-codemirror2';
-require('codemirror/addon/display/autorefresh');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/matchtags');
 require('codemirror/addon/edit/matchtags');
 require('codemirror/addon/edit/closetag');
 require('codemirror/addon/edit/closetag');
@@ -27,6 +26,7 @@ require('codemirror/addon/fold/foldgutter.css');
 require('codemirror/addon/fold/markdown-fold');
 require('codemirror/addon/fold/markdown-fold');
 require('codemirror/addon/fold/brace-fold');
 require('codemirror/addon/fold/brace-fold');
 require('codemirror/mode/gfm/gfm');
 require('codemirror/mode/gfm/gfm');
+require('../../util/codemirror/autorefresh.ext');
 
 
 import pasteHelper from './PasteHelper';
 import pasteHelper from './PasteHelper';
 import EmojiAutoCompleteHelper from './EmojiAutoCompleteHelper';
 import EmojiAutoCompleteHelper from './EmojiAutoCompleteHelper';
@@ -428,7 +428,7 @@ export default class CodeMirrorEditor extends AbstractEditor {
           tabSize: 4,
           tabSize: 4,
           indentUnit: 4,
           indentUnit: 4,
           lineWrapping: true,
           lineWrapping: true,
-          autoRefresh: true,
+          autoRefresh: {force: true},   // force option is enabled by autorefresh.ext.js -- Yuki Takei
           autoCloseTags: true,
           autoCloseTags: true,
           matchBrackets: true,
           matchBrackets: true,
           matchTags: {bothTags: true},
           matchTags: {bothTags: true},

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

@@ -20,7 +20,7 @@ export default class Page extends React.Component {
 
 
     return (
     return (
       <li className="page-list-li d-flex align-items-center">
       <li className="page-list-li d-flex align-items-center">
-        <UserPicture user={page.revision.author} />
+        <UserPicture user={page.lastUpdateUser} />
         <a className="page-list-link" href={link}>
         <a className="page-list-link" href={link}>
           <PagePath page={page} excludePathString={this.props.excludePathString} />
           <PagePath page={page} excludePathString={this.props.excludePathString} />
         </a>
         </a>

+ 1 - 1
resource/js/components/SearchTypeahead.js

@@ -106,7 +106,7 @@ export default class SearchTypeahead extends React.Component {
     const page = option;
     const page = option;
     return (
     return (
       <span>
       <span>
-      <UserPicture user={page.revision.author} size="sm" />
+      <UserPicture user={page.lastUpdateUser} size="sm" />
       <PagePath page={page} />
       <PagePath page={page} />
       <PageListMeta page={page} />
       <PageListMeta page={page} />
       </span>
       </span>

+ 51 - 0
resource/js/util/codemirror/autorefresh.ext.js

@@ -0,0 +1,51 @@
+/**
+ * extends codemirror/addon/display/autorefresh
+ *
+ * @author Yuki Takei <yuki@weseek.co.jp>
+ * @see https://codemirror.net/addon/display/autorefresh.js
+ * @see https://github.com/scniro/react-codemirror2/issues/83#issuecomment-398825212
+ */
+/* eslint-disable */
+
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  mod(require("codemirror"));
+})(function(CodeMirror) {
+  "use strict"
+
+  CodeMirror.defineOption("autoRefresh", false, function(cm, val) {
+    if (cm.state.autoRefresh) {
+      stopListening(cm, cm.state.autoRefresh)
+      cm.state.autoRefresh = null
+    }
+    if (val && (val.force || cm.display.wrapper.offsetHeight == 0))
+      startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})
+  })
+
+  function startListening(cm, state) {
+    function check() {
+      if (cm.display.wrapper.offsetHeight) {
+        stopListening(cm, state)
+        if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)
+          cm.refresh()
+      } else {
+        state.timeout = setTimeout(check, state.delay)
+      }
+    }
+    state.timeout = setTimeout(check, state.delay)
+    state.hurry = function() {
+      clearTimeout(state.timeout)
+      state.timeout = setTimeout(check, 50)
+    }
+    CodeMirror.on(window, "mouseup", state.hurry)
+    CodeMirror.on(window, "keyup", state.hurry)
+  }
+
+  function stopListening(_cm, state) {
+    clearTimeout(state.timeout)
+    CodeMirror.off(window, "mouseup", state.hurry)
+    CodeMirror.off(window, "keyup", state.hurry)
+  }
+});

+ 3 - 3
yarn.lock

@@ -6666,9 +6666,9 @@ react-clipboard.js@^2.0.0:
     clipboard "^2.0.0"
     clipboard "^2.0.0"
     prop-types "^15.5.0"
     prop-types "^15.5.0"
 
 
-react-codemirror2@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-5.0.0.tgz#18e0d4495c3cfe0c4675dd9bfcea074ca3aa9f56"
+react-codemirror2@^5.0.4:
+  version "5.0.4"
+  resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-5.0.4.tgz#d44a2d7a63a96509ba65db9b771bd61a781b8a0d"
 
 
 react-dom@^16.2.0:
 react-dom@^16.2.0:
   version "16.2.0"
   version "16.2.0"