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

Merge branch 'master' into imprv/brushup-group-select-ui

# Conflicts:
#	lib/routes/page.js
Tatsuya Ise 8 лет назад
Родитель
Сommit
1e4143ef2e
100 измененных файлов с 1073 добавлено и 1401 удалено
  1. 7 0
      .eslintignore
  2. 18 11
      .eslintrc.js
  3. 8 1
      CHANGES.md
  4. 1 1
      bin/generate-plugin-definitions-source.js
  5. 0 73
      bin/migration/0.0.1-0.0.2-convert_embedded_object_to_schema.js
  6. 0 25
      bin/password-hash-generator.js
  7. 0 112
      bin/revision-string-replacer.js
  8. 0 126
      bin/search.js
  9. 1 1
      bin/shrink-emojione-strategy.js
  10. 0 44
      bin/util.js
  11. 1 1
      config/env.dev.js
  12. 0 2
      config/helpers.js
  13. 10 5
      config/webpack.common.js
  14. 1 1
      config/webpack.dev.js
  15. 1 1
      config/webpack.prod.js
  16. 9 8
      lib/crowi/express-init.js
  17. 2 5
      lib/events/page.js
  18. 1 1
      lib/form/revision.js
  19. 9 9
      lib/models/attachment.js
  20. 1 1
      lib/models/comment.js
  21. 64 101
      lib/models/config.js
  22. 2 2
      lib/models/external-account.js
  23. 9 9
      lib/models/page-group-relation.js
  24. 139 131
      lib/models/page.js
  25. 9 6
      lib/models/revision.js
  26. 15 21
      lib/models/updatePost.js
  27. 6 6
      lib/models/user-group-relation.js
  28. 8 8
      lib/models/user-group.js
  29. 38 36
      lib/models/user.js
  30. 2 2
      lib/plugins/plugin-utils-v2.js
  31. 1 1
      lib/plugins/plugin-utils.js
  32. 97 80
      lib/routes/admin.js
  33. 9 8
      lib/routes/attachment.js
  34. 4 3
      lib/routes/bookmark.js
  35. 6 6
      lib/routes/comment.js
  36. 4 2
      lib/routes/index.js
  37. 3 2
      lib/routes/installer.js
  38. 10 13
      lib/routes/login-passport.js
  39. 45 39
      lib/routes/login.js
  40. 32 25
      lib/routes/me.js
  41. 51 44
      lib/routes/page.js
  42. 7 4
      lib/routes/revision.js
  43. 3 2
      lib/routes/search.js
  44. 8 6
      lib/routes/user.js
  45. 4 8
      lib/service/notification.js
  46. 4 4
      lib/service/passport.js
  47. 6 5
      lib/util/apiResponse.js
  48. 1 1
      lib/util/formUtil.js
  49. 11 10
      lib/util/mailer.js
  50. 16 11
      lib/util/middlewares.js
  51. 47 60
      lib/util/search.js
  52. 21 17
      lib/util/slack.js
  53. 48 44
      lib/util/swigFunctions.js
  54. 1 1
      lib/util/xss.js
  55. 12 5
      lib/views/admin/customize.html
  56. 12 11
      local_modules/crowi-fileupload-aws/index.js
  57. 1 1
      local_modules/crowi-fileupload-local/index.js
  58. 4 1
      package.json
  59. 4 6
      resource/js/app.js
  60. 2 2
      resource/js/components/Admin/CustomCssEditor.js
  61. 2 2
      resource/js/components/Admin/CustomHeaderEditor.js
  62. 2 2
      resource/js/components/Admin/CustomScriptEditor.js
  63. 2 1
      resource/js/components/BookmarkButton.js
  64. 2 2
      resource/js/components/CopyButton.js
  65. 2 3
      resource/js/components/HeaderSearchBox/SearchForm.js
  66. 1 7
      resource/js/components/NewPageNameInputter.js
  67. 2 2
      resource/js/components/Page.js
  68. 2 2
      resource/js/components/Page/RevisionBody.js
  69. 8 8
      resource/js/components/Page/RevisionPath.js
  70. 4 4
      resource/js/components/Page/RevisionUrl.js
  71. 1 1
      resource/js/components/PageAttachment/DeleteAttachmentModal.js
  72. 1 1
      resource/js/components/PageAttachment/PageAttachmentList.js
  73. 1 1
      resource/js/components/PageComment/Comment.js
  74. 2 2
      resource/js/components/PageComment/DeleteCommentModal.js
  75. 3 2
      resource/js/components/PageCommentFormBehavior.js
  76. 4 2
      resource/js/components/PageEditor/Editor.js
  77. 8 8
      resource/js/components/PageEditor/EmojiAutoCompleteHelper.js
  78. 2 2
      resource/js/components/PageEditor/MarkdownListUtil.js
  79. 9 7
      resource/js/components/PageEditor/MarkdownTableUtil.js
  80. 12 16
      resource/js/components/PageEditor/OptionsSelector.js
  81. 3 3
      resource/js/components/PageEditor/PasteHelper.js
  82. 1 1
      resource/js/components/PageEditor/Preview.js
  83. 60 56
      resource/js/components/PageEditor/ScrollSyncHelper.js
  84. 3 2
      resource/js/components/PageHistory.js
  85. 7 6
      resource/js/components/PageHistory/PageRevisionList.js
  86. 2 2
      resource/js/components/PageHistory/Revision.js
  87. 1 1
      resource/js/components/PageHistory/RevisionDiff.js
  88. 1 1
      resource/js/components/PageList/ListView.js
  89. 1 1
      resource/js/components/PageList/Page.js
  90. 4 4
      resource/js/components/PageListSearch.js
  91. 3 3
      resource/js/components/ReactUtils.js
  92. 4 4
      resource/js/components/SearchPage.js
  93. 2 4
      resource/js/components/SearchPage/DeletePageListModal.js
  94. 1 0
      resource/js/components/SearchPage/SearchForm.js
  95. 15 10
      resource/js/components/SearchPage/SearchResult.js
  96. 0 2
      resource/js/components/SearchPage/SearchResultList.js
  97. 3 3
      resource/js/components/SearchTypeahead.js
  98. 7 1
      resource/js/components/SeenUserList.js
  99. 11 12
      resource/js/legacy/crowi-admin.js
  100. 38 38
      resource/js/legacy/crowi-form.js

+ 7 - 0
.eslintignore

@@ -0,0 +1,7 @@
+/.github/**
+/.vscode/**
+/node_modules/**
+/public/**
+/resource/js/legacy/thirdparty-js/**
+/test/**
+/tmp/**

+ 18 - 11
.eslintrc.js

@@ -10,8 +10,11 @@ module.exports = {
     "plugin:react/recommended"
   ],
   "globals": {
-    "window": true,
-    "emojione": true
+    "$": true,
+    "jquery": true,
+    "emojione": true,
+    "hljs": true,
+    "window": true
   },
   "parserOptions": {
     "ecmaFeatures": {
@@ -41,12 +44,18 @@ module.exports = {
       2,
       {
         "SwitchCase": 1,
-        "ignoredNodes": ['JSXElement *', 'JSXElement'],
-        "FunctionExpression": {"parameters": 2},
+        "ignoredNodes": ['JSXElement *', 'JSXElement', "JSXAttribute", "JSXSpreadAttribute"],
+        "FunctionDeclaration": {"body": 1, "parameters": 2},
+        "FunctionExpression": {"body": 1, "parameters": 2},
+        "MemberExpression": "off"
       }
     ],
     "key-spacing": [
-      "error", { "beforeColon": false, "afterColon": true }
+      "error", {
+        "beforeColon": false,
+        "afterColon": true,
+        "mode": "minimum"
+      }
     ],
     "keyword-spacing": [
       "error", {}
@@ -63,14 +72,12 @@ module.exports = {
       "error",
       "single"
     ],
-    "react/jsx-indent-props": [
-      "error",
-      2
-    ],
-    "react/no-string-refs": 'off',
+    "react/jsx-uses-vars": 1,
+    "react/no-string-refs": "off",
     "semi": [
       "error",
-      "always"
+      "always",
+      { "omitLastInOneLineBlock": true }
     ],
     "space-before-blocks": [
       "error",

+ 8 - 1
CHANGES.md

@@ -1,16 +1,23 @@
 CHANGES
 ========
 
-## 3.0.13-RC
+## 3.0.14
+
+* Improvement: Auto-format markdown tables which includes multibyte text
+* Improvement: Enable to switch show/hide border for highlight.js
+
+## 3.0.13
 
 * Improvement: Add Vim/Emacs/Sublime-Text icons for keybindings menu
 * Improvement: Add 'mono-blue' theme
 * Fix: Unportalize process failed silently
+* Fix: Sidebar breaks editor layouts
 * Support: Switch the logger from 'pino' to 'bunyan'
 * Support: Set the alias for 'debug' to the debug function of 'bunyan'
 * Support: Translate /admin/security
 * Support: Optimize bundles
     * upgrade 'markdown-it-toc-and-anchor-with-slugid' and omit 'uslug'
+* Support: Optimize .eslintrc.js
 
 ## 3.0.12
 

+ 1 - 1
bin/generate-plugin-definitions-source.js

@@ -22,7 +22,7 @@ if (process.env.NODE_ENV === 'development'
     && process.env.PLUGIN_NAMES_TOBE_LOADED !== undefined
     && process.env.PLUGIN_NAMES_TOBE_LOADED.length > 0) {
 
-  pluginNamesDev = process.env.PLUGIN_NAMES_TOBE_LOADED.split(',');
+  const pluginNamesDev = process.env.PLUGIN_NAMES_TOBE_LOADED.split(',');
 
   // merge and remove duplicates
   if (pluginNamesDev.length > 0) {

+ 0 - 73
bin/migration/0.0.1-0.0.2-convert_embedded_object_to_schema.js

@@ -1,73 +0,0 @@
-/**
- * Fakie::app.js
- *
- * @package Fakie
- * @author  Sotaro KARASAWA <sotarok@crocos.co.jp>
- * @version 0.0.0
- */
-
-var util    = require('util');
-var config  = require('config');
-var mongo   = require('mongoose');
-var async   = require('async');
-
-var user  =     require('../../lib/user');
-var page  =     require('../../lib/page');
-var revision  = require('../../lib/revision');
-
-mongo.connect('mongodb://' + config.mongodb.user + ':' + config.mongodb.password + '@' + config.mongodb.host + '/' + config.mongodb.dbname);
-module.exports = {
-  user: user
-  ,page: page
-  ,revision: revision
-};
-
-
-var options = {
-  offset: 0,
-  limit : 999,
-  revisionSlice: {$slice: 9999}
-};
-
-var q = page.Page.findListByStartWith('/', options, function(err, docs) {
-  var i = 0;
-  async.forEach(docs, function(data, cb) {
-    var ii = 0;
-    var path = data.path;
-    var pageId = data._id;
-    console.log("path: ", data._id, path);
-
-    async.forEachSeries(data.revisions, function(rData, rcb) {
-      var newRevision = new revision.Revision();
-      newRevision.path      = path;
-      newRevision.body      = rData.body;
-      newRevision.format    = rData.format;
-      newRevision.createdAt = rData.createdAt;
-      newRevision.save(function (err, n) {
-        if (!err) {
-          console.log("    ", path, ii++, rData.createdAt, " ... ok", n._id);
-        } else {
-          console.log("    ", path, ii++, rData.createdAt, " ... ERROR!");
-          console.log(err);
-        }
-        rcb();
-      });
-      //rcb();
-    }, function (rErr) {
-      console.log("    ", path, " all revision imported.");
-      revision.Revision.findLatestRevision(path, function(err, fr) {
-        page.Page.updateRevision(pageId, fr._id, function(err, frr) {
-          if (!err) {
-            console.log("        Page revision updated", pageId, path, i++);
-          } else {
-            console.log("        Page revision update ERROR", pageId, path, i++);
-          }
-          cb();
-        });
-      });
-    });
-  }, function(err) {
-    console.log('end');
-    mongo.disconnect();
-  });
-});

+ 0 - 25
bin/password-hash-generator.js

@@ -1,25 +0,0 @@
-var crypto = require('crypto')
-  , cli = require('cli')
-  ;
-
-function generatePassword (seed, password) {
-  var hasher = crypto.createHash('sha256');
-  hasher.update(seed + password);
-
-  cli.debug("seed is: " + seed);
-  cli.debug("password is: " + password);
-  return hasher.digest('hex');
-}
-
-cli.parse({
-    seed:      [false, 'Password seed', 'string', ''],
-    password:  [false, 'Password raw string', 'string'],
-});
-
-cli.main(function(args, options)
-{
-  console.log("args", args);
-  console.log("options", options);
-
-  this.output(generatePassword(options.seed, options.password) + '\n');
-});

+ 0 - 112
bin/revision-string-replacer.js

@@ -1,112 +0,0 @@
-var cli = require('cli')
- , mongo   = require('mongoose')
- , async   = require('async')
- ;
-
-cli.setUsage('MONGO_URI=mongodb://user:password@host/dbnae node bin/revision-string-replacer.js --from=\'aaa\' --to=\'bbb\'\n\n  This means that replace string "aaa" to "bbb" from all revisions.');
-cli.parse({
-    from: [false, 'Specified string is a target to replace.', 'string'],
-    to: [false, 'Replace string which specified by "from" to this string.', 'string'],
-    dry: [false, 'Dry run', 'boolean'],
-});
-
-
-cli.main(function(args, options)
-{
-  var app = {set: function(v) { }}
-    , c = this
-    , from = options.from
-    , to = options.to
-    , dry = options.dry
-    ;
-
-  console.log('This scriprt is not working now. Should be fixed.');
-  cli.exit(1);
-
-  if (!to || !from) {
-    cli.error('"to" and "from" options are required.\n');
-    cli.output(cli.getUsage());
-    cli.exit(1);
-    return ;
-  }
-
-  var mongoUri = process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || process.env.MONGO_URI || false;
-  if (!mongoUri) {
-    cli.error('Please set MONGO_URI env.\n');
-    cli.output(cli.getUsage());
-    cli.exit(1);
-    return;
-  }
-
-  mongo.connect(mongoUri);
-
-  // あー config 読み込み&model読み込み周りを app.js から切り離さないといけないにゃぁ
-  configModel = require('../lib/models/config')(app);
-
-  async.series([
-    function (next) {
-      configModel.loadAllConfig(function(err, doc) {
-
-        return next();
-      });
-    }, function (next) {
-      var config = app.set('config');
-
-      models = require('../lib/models')(app);
-      models.Config = configModel;
-
-      return next();
-    }, function (next) {
-      var limit = 100000;
-      c.spinner('Load revisions..');
-      models.Revision.find().limit(limit).exec(function(err, revs) {
-        c.spinner('Load revisions.. done!\n', true);
-        var count = Object.keys(revs).length
-          , i = 0
-          , matched = 0
-          , matchedWords = 0
-          ;
-
-        c.output('Found ' + count + ' revisions.\n');
-        c.output('Start replacing.\n');
-
-        async.each(revs, function(rev, cb) {
-          var regexp = new RegExp(from, 'g');
-          c.progress(++i/count);
-
-          var m = rev.body.match(regexp);
-          if (!m) {
-            return cb();
-          }
-
-          matched++;
-          matchedWords += m.length;
-          if (dry) {
-            return cb();
-          } else {
-            rev.body = rev.body.replace(regexp, to);
-            rev.save(function(err, s) {
-              if (err) {
-                c.error('Error on:' + rev.path);
-              } else {
-              }
-              return cb();
-            });
-          }
-        }, function(err) {
-          if (dry) {
-            cli.info(matchedWords + ' words in (' + matched + ' of ' + count + ') revisions will be replaced!');
-          } else {
-            cli.ok(matchedWords + ' words in (' + matched + ' of ' + count + ') revisions replaced!');
-          }
-          return next();
-        });
-      });
-    }
-  , function (next) {
-      cli.ok('Finished!');
-      mongo.disconnect();
-      return next();
-    }
-  ]);
-});

+ 0 - 126
bin/search.js

@@ -1,126 +0,0 @@
-
-var program = require('commander')
-  , debug = require('debug')('growi:console:search-util')
-  , colors = require('colors')
-  , crowi = new (require('../lib/crowi'))(__dirname + '/../', process.env)
-  ;
-
-crowi.init()
-  .then(function(app) {
-    program
-      .version(crowi.version);
-
-    program
-      .command('create-index')
-      .action(function (cmd, env) {
-        var search = crowi.getSearcher();
-
-        search.buildIndex()
-          .then(function(data) {
-            console.log(data);
-          })
-          .then(function() {
-            process.exit();
-          })
-          .catch(function(err) {
-            console.log("Error", err);
-
-          })
-      });
-
-    program
-      .command('add-pages')
-      .action(function (cmd, env) {
-        var search = crowi.getSearcher();
-
-        search.addAllPages()
-          .then(function(data) {
-            if (data.errors) {
-              console.error(colors.red.underline('Failed to index all pages.'));
-              console.error("");
-              data.items.forEach(function(item, i) {
-                var index = item.index || null;
-                if (index && index.status != 200) {
-                  console.error(colors.red('Error item: id=%s'), index._id)
-                  console.error('error.type=%s, error.reason=%s', index.error.type, index.error.reason);
-                  console.error(index.error.caused_by);
-                }
-                //debug('Item', i, item);
-              });
-            } else {
-              console.log('Data is successfully indexed.');
-            }
-            process.exit(0);
-          })
-          .catch(function(err) {
-            console.log("Error", err);
-          });
-      });
-
-    program
-      .command('rebuild-index')
-      .action(function (cmd, env) {
-        var search = crowi.getSearcher();
-
-        search.deleteIndex()
-          .then(function(data) {
-            if (!data.errors) {
-              console.log('Index deleted.');
-            }
-            return search.buildIndex();
-          })
-          .then(function(data) {
-            if (!data.errors) {
-              console.log('Index created.');
-            }
-            return search.addAllPages();
-          })
-          .then(function(data) {
-            if (!data.errors) {
-              console.log('Data is successfully indexed.');
-            }
-            process.exit(0);
-          })
-          .catch(function(err) {
-            console.error('Error', err);
-          });
-      });
-
-    program
-      .command('search')
-      .action(function (cmd, env) {
-        var Page = crowi.model('Page');
-        var search = crowi.getSearcher();
-        var keyword = cmd;
-
-        search.searchKeyword(keyword, {})
-          .then(function(data) {
-            debug('result is', data);
-            console.log(colors.green('Search result: %d of %d total. (%d ms)'), data.meta.results, data.meta.total, data.meta.took);
-
-            return Page.populatePageListToAnyObjects(data.data);
-          }).then(function(pages) {
-            pages.map(function(page) {
-              console.log(page._score, page._id, page.path);
-            });
-
-            process.exit(0);
-          })
-          .catch(function(err) {
-            console.error('Error', err);
-
-            process.exit(0);
-          });
-      });
-
-
-    program.parse(process.argv);
-
-  });
-
-
-//program
-//  .command('search [query]', 'search with optional query')
-//  .command('list', 'list packages installed', {isDefault: true})
-
-

+ 1 - 1
bin/shrink-emojione-strategy.js

@@ -14,7 +14,7 @@ const markdownItEmojiFull = require('markdown-it-emoji/lib/data/full.json');
 let shrinkedMap = {};
 for (let unicode in emojiStrategy) {
   const data = emojiStrategy[unicode];
-  const shortname = data.shortname.replace(/\:/g, '');
+  const shortname = data.shortname.replace(/:/g, '');
 
   // ignore if it isn't included in markdownItEmojiFull
   if (markdownItEmojiFull[shortname] == null) {

+ 0 - 44
bin/util.js

@@ -1,44 +0,0 @@
-var program = require('commander')
-  , debug = require('debug')('growi:console:util')
-  , colors = require('colors')
-  , crowi = new (require('../lib/crowi'))(__dirname + '/../', process.env)
-  ;
-
-crowi.init()
-  .then(function(app) {
-    program
-      .version(crowi.version);
-
-    program
-      .command('count-page-length')
-      .action(function (cmd, env) {
-        var Page = crowi.model('Page');
-        var stream = Page.getStreamOfFindAll();
-        var pages = [];
-
-        stream.on('data', function (doc) {
-          if (!doc.creator || !doc.revision) {
-            return ;
-          }
-
-          pages.push({
-            path: doc.path,
-            body: doc.revision.body,
-            author: doc.creator.username,
-          });
-        }).on('error', function (err) {
-          // TODO: handle err
-          debug('Error stream:', err);
-        }).on('close', function () {
-          // all done
-
-          pages.forEach(function(page, i) {
-            console.log('%d\t%s', page.body.length, page.path);
-          });
-
-          process.exit(0);
-        });
-      });
-
-    program.parse(process.argv);
-  });

+ 1 - 1
config/env.dev.js

@@ -2,7 +2,7 @@ module.exports = {
   NODE_ENV: 'development',
   FILE_UPLOAD: 'local',
   // MATHJAX: 1,
-  // ELASTICSEARCH_URI: 'http://localhost:9200/growi',
+  ELASTICSEARCH_URI: 'http://localhost:9200/growi',
   PLUGIN_NAMES_TOBE_LOADED: [
     // 'growi-plugin-lsx',
     // 'growi-plugin-pukiwiki-like-linker',

+ 0 - 2
config/helpers.js

@@ -5,8 +5,6 @@
 
 var path = require('path');
 
-const EVENT = process.env.npm_lifecycle_event || '';
-
 // Helper functions
 var ROOT = path.resolve(__dirname, '..');
 

+ 10 - 5
config/webpack.common.js

@@ -3,7 +3,6 @@
  */
 
 const webpack = require('webpack');
-const path = require('path');
 const helpers = require('./helpers');
 
 /*
@@ -44,8 +43,8 @@ module.exports = function(options) {
       extensions: ['.js', '.json'],
       modules: [helpers.root('src'), helpers.root('node_modules')],
       alias: {
-        '@root': path.resolve(__dirname, '../'),
-        '@alias/logger': path.resolve(__dirname, '../lib/service/logger'),
+        '@root': helpers.root('/'),
+        '@alias/logger': helpers.root('lib/service/logger'),
         // replace bunyan
         'bunyan': 'browser-bunyan',
       }
@@ -54,7 +53,13 @@ module.exports = function(options) {
       rules: [
         {
           test: /.jsx?$/,
-          exclude: /node_modules/,
+          exclude: {
+            test:    helpers.root('node_modules'),
+            exclude: [  // include as a result
+              helpers.root('node_modules/string-width'),
+              helpers.root('node_modules/is-fullwidth-code-point'), // depends from string-width
+            ]
+          },
           use: [{
             loader: 'babel-loader?cacheDirectory',
             options: {
@@ -82,7 +87,7 @@ module.exports = function(options) {
         /* File loader for supporting fonts, for example, in CSS files.
         */
         {
-          test: /\.(eot|woff2?|svg|ttf)([\?]?.*)$/,
+          test: /\.(eot|woff2?|svg|ttf)([?]?.*)$/,
           use: 'file-loader',
         }
       ]

+ 1 - 1
config/webpack.dev.js

@@ -99,4 +99,4 @@ module.exports = function(options) {
 
     ]
   });
-}
+};

+ 1 - 1
config/webpack.prod.js

@@ -116,4 +116,4 @@ module.exports = function(env) {
     ],
 
   });
-}
+};

+ 9 - 8
lib/crowi/express-init.js

@@ -70,11 +70,11 @@ module.exports = function(crowi, app) {
     res.locals.now      = now;
     res.locals.tzoffset = tzoffset;
     res.locals.consts   = {
-        pageGrants: Page.getGrantLabels(),
-        userStatus: User.getUserStatusLabels(),
-        language:   User.getLanguageLabels(),
-        restrictGuestMode: Config.getRestrictGuestModeLabels(),
-        registrationMode: Config.getRegistrationModeLabels(),
+      pageGrants: Page.getGrantLabels(),
+      userStatus: User.getUserStatusLabels(),
+      language:   User.getLanguageLabels(),
+      restrictGuestMode: Config.getRestrictGuestModeLabels(),
+      registrationMode: Config.getRegistrationModeLabels(),
     };
     res.locals.local_config = Config.getLocalconfig(config); // config for browser context
 
@@ -82,7 +82,7 @@ module.exports = function(crowi, app) {
   });
 
   app.set('port', crowi.port);
-  const staticOption = (crowi.node_env === 'production') ? {maxAge:'30d'} : {};
+  const staticOption = (crowi.node_env === 'production') ? {maxAge: '30d'} : {};
   app.use(express.static(crowi.publicDir, staticOption));
   app.engine('html', swig.renderFile);
   app.use(webpackAssets(
@@ -109,14 +109,15 @@ module.exports = function(crowi, app) {
       return basicAuth(
         config.crowi['security:basicName'],
         config.crowi['security:basicSecret'])(req, res, next);
-    } else {
+    }
+    else {
       next();
     }
   });
 
   // passport
   if (Config.isEnabledPassport(config)) {
-    debug('initialize Passport')
+    debug('initialize Passport');
     app.use(passport.initialize());
     app.use(passport.session());
   }

+ 2 - 5
lib/events/page.js

@@ -10,13 +10,10 @@ function PageEvent(crowi) {
 util.inherits(PageEvent, events.EventEmitter);
 
 PageEvent.prototype.onCreate = function(page, user) {
-  var User = this.crowi.model('User');
-  var Page = this.crowi.model('Page');
-
+  debug('onCreate event fired');
 };
 PageEvent.prototype.onUpdate = function(page, user) {
-  var User = this.crowi.model('User');
-  var Page = this.crowi.model('Page');
+  debug('onUpdate event fired');
 };
 
 module.exports = PageEvent;

+ 1 - 1
lib/form/revision.js

@@ -5,7 +5,7 @@ var form = require('express-form')
 
 module.exports = form(
   field('pageForm.path').required(),
-  field('pageForm.body').required().custom(function(value) { return value.replace(/\r/g, '\n'); }),
+  field('pageForm.body').required().custom(function(value) { return value.replace(/\r/g, '\n') }),
   field('pageForm.currentRevision'),
   field('pageForm.grant').toInt().required(),
   field('pageForm.notify')

+ 9 - 9
lib/models/attachment.js

@@ -3,9 +3,10 @@ module.exports = function(crowi) {
     , mongoose = require('mongoose')
     , ObjectId = mongoose.Schema.Types.ObjectId
     , fileUploader = require('../util/fileUploader')(crowi)
+    , attachmentSchema
   ;
 
-  function generateFileHash (fileName) {
+  function generateFileHash(fileName) {
     var hasher = require('crypto').createHash('md5');
     hasher.update(fileName);
 
@@ -105,7 +106,7 @@ module.exports = function(crowi) {
     });
   };
 
-  attachmentSchema.statics.guessExtByFileType = function (fileType) {
+  attachmentSchema.statics.guessExtByFileType = function(fileType) {
     let ext = '';
     const isImage = fileType.match(/^image\/(.+)/i);
 
@@ -116,14 +117,15 @@ module.exports = function(crowi) {
     return ext;
   };
 
-  attachmentSchema.statics.createAttachmentFilePath = function (pageId, fileName, fileType) {
+  attachmentSchema.statics.createAttachmentFilePath = function(pageId, fileName, fileType) {
     const Attachment = this;
     let ext = '';
     const fnExt = fileName.match(/(.*)(?:\.([^.]+$))/);
 
     if (fnExt) {
       ext = '.' + fnExt[2];
-    } else {
+    }
+    else {
       ext = Attachment.guessExtByFileType(fileType);
       if (ext !== '') {
         ext = '.' + ext;
@@ -139,7 +141,7 @@ module.exports = function(crowi) {
     return new Promise((resolve, reject) => {
       Attachment.getListByPageId(pageId)
       .then((attachments) => {
-        for (attachment of attachments) {
+        for (let attachment of attachments) {
           Attachment.removeAttachment(attachment).then((res) => {
             // do nothing
           }).catch((err) => {
@@ -156,10 +158,8 @@ module.exports = function(crowi) {
   };
 
   attachmentSchema.statics.findDeliveryFile = function(attachment, forceUpdate) {
-    var Attachment = this;
-
-    // TODO
-    var forceUpdate = forceUpdate || false;
+    // TODO force update
+    // var forceUpdate = forceUpdate || false;
 
     return fileUploader.findDeliveryFile(attachment._id, attachment.filePath);
   };

+ 1 - 1
lib/models/comment.js

@@ -110,7 +110,7 @@ module.exports = function(crowi) {
         }
 
         resolve(done);
-      });;
+      });
     });
   };
 

+ 64 - 101
lib/models/config.js

@@ -2,7 +2,6 @@ module.exports = function(crowi) {
   var mongoose = require('mongoose')
     , debug = require('debug')('growi:models:config')
     , uglifycss = require('uglifycss')
-    , ObjectId = mongoose.Schema.Types.ObjectId
     , configSchema
     , Config
 
@@ -39,8 +38,8 @@ module.exports = function(crowi) {
   /**
    * default values when migrated from Official Crowi
    */
-  function getDefaultCrowiConfigs()
-  {
+  function getDefaultCrowiConfigs() {
+    /* eslint-disable key-spacing */
     return {
       //'app:installed'     : "0.0.0",
       'app:confidential'  : '',
@@ -94,13 +93,14 @@ module.exports = function(crowi) {
       'customize:isSavedStatesOfTabChanges' : true,
       'customize:isEnabledAttachTitleHeader' : false,
     };
+    /* eslint-enable */
   }
 
   function getDefaultMarkdownConfigs() {
     return {
       'markdown:isEnabledLinebreaks': true,
       'markdown:isEnabledLinebreaksInComments': true,
-    }
+    };
   }
 
   function getValueForCrowiNS(config, key) {
@@ -112,8 +112,7 @@ module.exports = function(crowi) {
     return config.crowi[key];
   }
 
-  configSchema.statics.getRestrictGuestModeLabels = function()
-  {
+  configSchema.statics.getRestrictGuestModeLabels = function() {
     var labels = {};
     labels[SECURITY_RESTRICT_GUEST_MODE_DENY]     = 'security_setting.guest_mode.deny';
     labels[SECURITY_RESTRICT_GUEST_MODE_READONLY] = 'security_setting.guest_mode.readonly';
@@ -121,8 +120,7 @@ module.exports = function(crowi) {
     return labels;
   };
 
-  configSchema.statics.getRegistrationModeLabels = function()
-  {
+  configSchema.statics.getRegistrationModeLabels = function() {
     var labels = {};
     labels[SECURITY_REGISTRATION_MODE_OPEN]       = 'security_setting.registration_mode.open';
     labels[SECURITY_REGISTRATION_MODE_RESTRICTED] = 'security_setting.registration_mode.restricted';
@@ -131,11 +129,10 @@ module.exports = function(crowi) {
     return labels;
   };
 
-  configSchema.statics.updateConfigCache = function(ns, config)
-  {
+  configSchema.statics.updateConfigCache = function(ns, config) {
     var originalConfig = crowi.getConfig();
     var newNSConfig = originalConfig[ns] || {};
-    Object.keys(config).forEach(function (key) {
+    Object.keys(config).forEach(function(key) {
       if (config[key] || config[key] === '' || config[key] === false) {
         newNSConfig[key] = config[key];
       }
@@ -150,10 +147,9 @@ module.exports = function(crowi) {
   };
 
   // Execute only once for installing application
-  configSchema.statics.applicationInstall = function(callback)
-  {
+  configSchema.statics.applicationInstall = function(callback) {
     var Config = this;
-    Config.count({ ns: 'crowi' }, function (err, count) {
+    Config.count({ ns: 'crowi' }, function(err, count) {
       if (count > 0) {
         return callback(new Error('Application already installed'), null);
       }
@@ -165,8 +161,7 @@ module.exports = function(crowi) {
     });
   };
 
-  configSchema.statics.setupCofigFormData = function(ns, config)
-  {
+  configSchema.statics.setupCofigFormData = function(ns, config) {
     var defaultConfig = {};
 
     // set Default Settings
@@ -180,7 +175,7 @@ module.exports = function(crowi) {
     if (!defaultConfig[ns]) {
       defaultConfig[ns] = {};
     }
-    Object.keys(config[ns] || {}).forEach(function (key) {
+    Object.keys(config[ns] || {}).forEach(function(key) {
       if (config[ns][key] !== undefined) {
         defaultConfig[key] = config[ns][key];
       }
@@ -189,47 +184,43 @@ module.exports = function(crowi) {
   };
 
 
-  configSchema.statics.updateNamespaceByArray = function(ns, configs, callback)
-  {
+  configSchema.statics.updateNamespaceByArray = function(ns, configs, callback) {
     var Config = this;
     if (configs.length < 0) {
       return callback(new Error('Argument #1 is not array.'), null);
     }
 
-    Object.keys(configs).forEach(function (key) {
+    Object.keys(configs).forEach(function(key) {
       var value = configs[key];
 
       Config.findOneAndUpdate(
         { ns: ns, key: key },
         { ns: ns, key: key, value: JSON.stringify(value) },
         { upsert: true, },
-        function (err, config) {
+        function(err, config) {
           debug('Config.findAndUpdate', err, config);
-      });
+        });
     });
 
     return callback(null, configs);
   };
 
-  configSchema.statics.findAndUpdate = function(ns, key, value, callback)
-  {
+  configSchema.statics.findAndUpdate = function(ns, key, value, callback) {
     var Config = this;
     Config.findOneAndUpdate(
       { ns: ns, key: key },
       { ns: ns, key: key, value: JSON.stringify(value) },
       { upsert: true, },
-      function (err, config) {
+      function(err, config) {
         debug('Config.findAndUpdate', err, config);
         callback(err, config);
-    });
+      });
   };
 
-  configSchema.statics.getConfig = function(callback)
-  {
+  configSchema.statics.getConfig = function(callback) {
   };
 
-  configSchema.statics.loadAllConfig = function(callback)
-  {
+  configSchema.statics.loadAllConfig = function(callback) {
     var Config = this
       , config = {};
     config.crowi = {}; // crowi namespace
@@ -255,14 +246,12 @@ module.exports = function(crowi) {
       });
   };
 
-  configSchema.statics.appTitle = function(config)
-  {
+  configSchema.statics.appTitle = function(config) {
     const key = 'app:title';
     return getValueForCrowiNS(config, key) || 'GROWI';
   };
 
-  configSchema.statics.isEnabledPassport = function(config)
-  {
+  configSchema.statics.isEnabledPassport = function(config) {
     // always true if growi installed cleanly
     if (Object.keys(config.crowi).length == 0) {
       return true;
@@ -272,24 +261,21 @@ module.exports = function(crowi) {
     return getValueForCrowiNS(config, key);
   };
 
-  configSchema.statics.isEnabledPassportLdap = function(config)
-  {
+  configSchema.statics.isEnabledPassportLdap = function(config) {
     const key = 'security:passport-ldap:isEnabled';
     return getValueForCrowiNS(config, key);
   };
 
-  configSchema.statics.isSameUsernameTreatedAsIdenticalUser = function(config, providerType)
-  {
+  configSchema.statics.isSameUsernameTreatedAsIdenticalUser = function(config, providerType) {
     const key = `security:passport-${providerType}:isSameUsernameTreatedAsIdenticalUser`;
     return getValueForCrowiNS(config, key);
   };
 
-  configSchema.statics.isUploadable = function(config)
-  {
+  configSchema.statics.isUploadable = function(config) {
     var method = crowi.env.FILE_UPLOAD || 'aws';
 
     if (method == 'aws' && (
-        !config.crowi['aws:accessKeyId'] ||
+      !config.crowi['aws:accessKeyId'] ||
         !config.crowi['aws:secretAccessKey'] ||
         !config.crowi['aws:region'] ||
         !config.crowi['aws:bucket'])) {
@@ -299,8 +285,7 @@ module.exports = function(crowi) {
     return method != 'none';
   };
 
-  configSchema.statics.isGuesstAllowedToRead = function(config)
-  {
+  configSchema.statics.isGuesstAllowedToRead = function(config) {
     // return false if undefined
     if (undefined === config.crowi || undefined === config.crowi['security:restrictGuestMode']) {
       return false;
@@ -309,14 +294,12 @@ module.exports = function(crowi) {
     return SECURITY_RESTRICT_GUEST_MODE_READONLY === config.crowi['security:restrictGuestMode'];
   };
 
-  configSchema.statics.isEnabledPlugins = function(config)
-  {
+  configSchema.statics.isEnabledPlugins = function(config) {
     const key = 'plugin:isEnabledPlugins';
     return getValueForCrowiNS(config, key);
   };
 
-  configSchema.statics.isEnabledLinebreaks = function(config)
-  {
+  configSchema.statics.isEnabledLinebreaks = function(config) {
     const key = 'markdown:isEnabledLinebreaks';
 
     // return default value if undefined
@@ -327,8 +310,7 @@ module.exports = function(crowi) {
     return config.markdown[key];
   };
 
-  configSchema.statics.isEnabledLinebreaksInComments = function(config)
-  {
+  configSchema.statics.isEnabledLinebreaksInComments = function(config) {
     const key = 'markdown:isEnabledLinebreaksInComments';
 
     // return default value if undefined
@@ -342,46 +324,39 @@ module.exports = function(crowi) {
   /**
    * initialize custom css strings
    */
-  configSchema.statics.initCustomCss = function(config)
-  {
+  configSchema.statics.initCustomCss = function(config) {
     const key = 'customize:css';
     const rawCss = getValueForCrowiNS(config, key);
     // uglify and store
     this._customCss = uglifycss.processString(rawCss);
-  }
+  };
 
-  configSchema.statics.customCss = function(config)
-  {
+  configSchema.statics.customCss = function(config) {
     return this._customCss;
-  }
+  };
 
-  configSchema.statics.initCustomScript = function(config)
-  {
+  configSchema.statics.initCustomScript = function(config) {
     const key = 'customize:script';
     const rawScript = getValueForCrowiNS(config, key);
     // store as is
     this._customScript = rawScript;
-  }
+  };
 
-  configSchema.statics.customScript = function(config)
-  {
+  configSchema.statics.customScript = function(config) {
     return this._customScript;
-  }
+  };
 
-  configSchema.statics.customHeader = function(config)
-  {
+  configSchema.statics.customHeader = function(config) {
     const key = 'customize:header';
     return getValueForCrowiNS(config, key);
-  }
+  };
 
-  configSchema.statics.theme = function(config)
-  {
+  configSchema.statics.theme = function(config) {
     const key = 'customize:theme';
     return getValueForCrowiNS(config, key);
-  }
+  };
 
-  configSchema.statics.customTitle = function(config, page)
-  {
+  configSchema.statics.customTitle = function(config, page) {
     const key = 'customize:title';
     let customTitle = getValueForCrowiNS(config, key);
 
@@ -392,52 +367,44 @@ module.exports = function(crowi) {
     return customTitle
       .replace('{{sitename}}', this.appTitle(config))
       .replace('{{page}}', page);
-  }
+  };
 
-  configSchema.statics.behaviorType = function(config)
-  {
+  configSchema.statics.behaviorType = function(config) {
     const key = 'customize:behavior';
     return getValueForCrowiNS(config, key);
-  }
+  };
 
-  configSchema.statics.layoutType = function(config)
-  {
+  configSchema.statics.layoutType = function(config) {
     const key = 'customize:layout';
     return getValueForCrowiNS(config, key);
-  }
+  };
 
-  configSchema.statics.highlightJsStyle = function(config)
-  {
+  configSchema.statics.highlightJsStyle = function(config) {
     const key = 'customize:highlightJsStyle';
     return getValueForCrowiNS(config, key);
-  }
+  };
 
-  configSchema.statics.highlightJsStyleBorder = function(config)
-  {
+  configSchema.statics.highlightJsStyleBorder = function(config) {
     const key = 'customize:highlightJsStyleBorder';
     return getValueForCrowiNS(config, key);
-  }
+  };
 
-  configSchema.statics.isEnabledTimeline = function(config)
-  {
+  configSchema.statics.isEnabledTimeline = function(config) {
     const key = 'customize:isEnabledTimeline';
     return getValueForCrowiNS(config, key);
   };
 
-  configSchema.statics.isSavedStatesOfTabChanges = function(config)
-  {
+  configSchema.statics.isSavedStatesOfTabChanges = function(config) {
     const key = 'customize:isSavedStatesOfTabChanges';
     return getValueForCrowiNS(config, key);
   };
 
-  configSchema.statics.isEnabledAttachTitleHeader = function(config)
-  {
+  configSchema.statics.isEnabledAttachTitleHeader = function(config) {
     const key = 'customize:isEnabledAttachTitleHeader';
     return getValueForCrowiNS(config, key);
   };
 
-  configSchema.statics.fileUploadEnabled = function(config)
-  {
+  configSchema.statics.fileUploadEnabled = function(config) {
     const Config = this;
 
     if (!Config.isUploadable(config)) {
@@ -448,32 +415,28 @@ module.exports = function(crowi) {
     return !!config.crowi['app:fileUpload'];
   };
 
-  configSchema.statics.hasSlackConfig = function(config)
-  {
+  configSchema.statics.hasSlackConfig = function(config) {
     return Config.hasSlackToken(config) || Config.hasSlackIwhUrl(config);
   };
 
   /**
    * for Slack Incoming Webhooks
    */
-  configSchema.statics.hasSlackIwhUrl = function(config)
-  {
+  configSchema.statics.hasSlackIwhUrl = function(config) {
     if (!config.notification) {
       return false;
     }
     return (config.notification['slack:incomingWebhookUrl'] ? true : false);
   };
 
-  configSchema.statics.isIncomingWebhookPrioritized = function(config)
-  {
+  configSchema.statics.isIncomingWebhookPrioritized = function(config) {
     if (!config.notification) {
       return false;
     }
     return (config.notification['slack:isIncomingWebhookPrioritized'] ? true : false);
   };
 
-  configSchema.statics.hasSlackToken = function(config)
-  {
+  configSchema.statics.hasSlackToken = function(config) {
     if (!config.notification) {
       return false;
     }
@@ -481,8 +444,7 @@ module.exports = function(crowi) {
     return (config.notification['slack:token'] ? true : false);
   };
 
-  configSchema.statics.getLocalconfig = function(config)
-  {
+  configSchema.statics.getLocalconfig = function(config) {
     const Config = this;
     const env = crowi.getEnv();
 
@@ -498,6 +460,7 @@ module.exports = function(crowi) {
       behaviorType: Config.behaviorType(config),
       layoutType: Config.layoutType(config),
       isEnabledLineBreaks: Config.isEnabledLinebreaks(config),
+      highlightJsStyleBorder: Config.highlightJsStyleBorder(config),
       isSavedStatesOfTabChanges: Config.isSavedStatesOfTabChanges(config),
       env: {
         PLANTUML_URI: env.PLANTUML_URI || null,
@@ -506,7 +469,7 @@ module.exports = function(crowi) {
     };
 
     return local_config;
-  }
+  };
 
   /*
   configSchema.statics.isInstalled = function(config)

+ 2 - 2
lib/models/external-account.js

@@ -55,7 +55,7 @@ class ExternalAccount {
     return this.populate('user').execPopulate()
       .then((account) => {
         return account.user;
-      })
+      });
   }
 
   /**
@@ -154,4 +154,4 @@ module.exports = function(crowi) {
   ExternalAccount.crowi = crowi;
   schema.loadClass(ExternalAccount);
   return mongoose.model('ExternalAccount', schema);
-}
+};

+ 9 - 9
lib/models/page-group-relation.js

@@ -26,16 +26,16 @@ schema.plugin(mongoosePaginate);
  */
 class PageGroupRelation {
 
-    /**
+  /**
    * limit items num for pagination
    *
    * @readonly
    * @static
    * @memberof PageGroupRelation
    */
-   static get PAGE_ITEMS() {
-     return 50;
-    }
+  static get PAGE_ITEMS() {
+    return 50;
+  }
 
   static set crowi(crowi) {
     this._crowi = crowi;
@@ -167,11 +167,11 @@ class PageGroupRelation {
     return this.findByPage(pageData)
       .then((pageRelations) => {
         return pageRelations.map((pageRelation) => {
-          return UserGroupRelation.isRelatedUserForGroup(userData, pageRelation.relatedGroup)
+          return UserGroupRelation.isRelatedUserForGroup(userData, pageRelation.relatedGroup);
         });
       })
       .then((checkPromises) => {
-        return Promise.all(checkPromises)
+        return Promise.all(checkPromises);
       })
       .then((checkResults) => {
         var checkResult = false;
@@ -201,7 +201,7 @@ class PageGroupRelation {
       relatedGroup: userGroup.id,
       targetPage: page.id,
     });
-  };
+  }
 
   /**
    * remove all relation for UserGroup
@@ -276,8 +276,8 @@ class PageGroupRelation {
   }
 }
 
-module.exports = function (crowi) {
+module.exports = function(crowi) {
   PageGroupRelation.crowi = crowi;
   schema.loadClass(PageGroupRelation);
   return mongoose.model('PageGroupRelation', schema);
-}
+};

+ 139 - 131
lib/models/page.js

@@ -48,7 +48,8 @@ module.exports = function(crowi) {
       get: function(data) {
         try {
           return JSON.parse(data);
-        } catch(e) {
+        }
+        catch (e) {
           return data;
         }
       },
@@ -103,8 +104,9 @@ module.exports = function(crowi) {
 
     if (this.populated('creator') && this.creator._id.toString() === userData._id.toString()) {
       return true;
-    } else if (this.creator.toString() === userData._id.toString()) {
-      return true
+    }
+    else if (this.creator.toString() === userData._id.toString()) {
+      return true;
     }
 
     return false;
@@ -149,21 +151,22 @@ module.exports = function(crowi) {
     var self = this,
       Page = self;
 
-      return new Promise(function(resolve, reject) {
-        var added = self.liker.addToSet(userData._id);
-        if (added.length > 0) {
-          self.save(function(err, data) {
-            if (err) {
-              return reject(err);
-            }
-            debug('liker updated!', added);
-            return resolve(data);
-          });
-        } else {
-          debug('liker not updated');
-          return reject(self);
-        }
-      });
+    return new Promise(function(resolve, reject) {
+      var added = self.liker.addToSet(userData._id);
+      if (added.length > 0) {
+        self.save(function(err, data) {
+          if (err) {
+            return reject(err);
+          }
+          debug('liker updated!', added);
+          return resolve(data);
+        });
+      }
+      else {
+        debug('liker not updated');
+        return reject(self);
+      }
+    });
 
   };
 
@@ -171,21 +174,22 @@ module.exports = function(crowi) {
     var self = this,
       Page = self;
 
-      return new Promise(function(resolve, reject) {
-        var beforeCount = self.liker.length;
-        self.liker.pull(userData._id);
-        if (self.liker.length != beforeCount) {
-          self.save(function(err, data) {
-            if (err) {
-              return reject(err);
-            }
-            return resolve(data);
-          });
-        } else {
-          debug('liker not updated');
-          return reject(self);
-        }
-      });
+    return new Promise(function(resolve, reject) {
+      var beforeCount = self.liker.length;
+      self.liker.pull(userData._id);
+      if (self.liker.length != beforeCount) {
+        self.save(function(err, data) {
+          if (err) {
+            return reject(err);
+          }
+          return resolve(data);
+        });
+      }
+      else {
+        debug('liker not updated');
+        return reject(self);
+      }
+    });
 
   };
 
@@ -193,35 +197,35 @@ module.exports = function(crowi) {
     var self = this,
       Page = self;
 
-      return this.seenUsers.some(function(seenUser) {
-        return seenUser.equals(userData._id);
-      });
+    return this.seenUsers.some(function(seenUser) {
+      return seenUser.equals(userData._id);
+    });
   };
 
   pageSchema.methods.seen = function(userData) {
     var self = this,
       Page = self;
 
-      if (this.isSeenUser(userData)) {
-        debug('seenUsers not updated');
-        return Promise.resolve(this);
+    if (this.isSeenUser(userData)) {
+      debug('seenUsers not updated');
+      return Promise.resolve(this);
+    }
+
+    return new Promise(function(resolve, reject) {
+      if (!userData || !userData._id) {
+        reject(new Error('User data is not valid'));
       }
 
-      return new Promise(function(resolve, reject) {
-        if (!userData || !userData._id) {
-          reject(new Error('User data is not valid'));
+      var added = self.seenUsers.addToSet(userData);
+      self.save(function(err, data) {
+        if (err) {
+          return reject(err);
         }
 
-        var added = self.seenUsers.addToSet(userData);
-        self.save(function(err, data) {
-          if (err) {
-            return reject(err);
-          }
-
-          debug('seenUsers updated!', added);
-          return resolve(self);
-        });
+        debug('seenUsers updated!', added);
+        return resolve(self);
       });
+    });
   };
 
   pageSchema.methods.getSlackChannel = function() {
@@ -271,7 +275,7 @@ module.exports = function(crowi) {
         {path: 'revision', model: 'Revision'},
         //{path: 'liker', options: { limit: 11 }},
         //{path: 'seenUsers', options: { limit: 11 }},
-      ], function (err, pageData) {
+      ], function(err, pageData) {
         Page.populate(pageData, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS}, function(err, data) {
           if (err) {
             return reject(err);
@@ -307,10 +311,9 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.updateCommentCount = function (pageId)
-  {
+  pageSchema.statics.updateCommentCount = function(pageId) {
     var self = this;
-    var Comment = crowi.model("Comment");
+    var Comment = crowi.model('Comment');
     return Comment.countCommentByPageId(pageId)
     .then(function(count) {
       self.update({_id: pageId}, {commentCount: count}, {}, function(err, data) {
@@ -324,7 +327,7 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.hasPortalPage = function (path, user, revisionId) {
+  pageSchema.statics.hasPortalPage = function(path, user, revisionId) {
     var self = this;
     return new Promise(function(resolve, reject) {
       self.findPage(path, user, revisionId)
@@ -416,7 +419,7 @@ module.exports = function(crowi) {
   pageSchema.statics.fixToCreatableName = function(path) {
     return path
       .replace(/\/\//g, '/')
-      ;
+    ;
   };
 
   pageSchema.statics.updateRevision = function(pageId, revisionId, cb) {
@@ -497,22 +500,23 @@ module.exports = function(crowi) {
             return resolve(null);
           }
 
-          var pageNotFoundError = new Error('Page Not Found')
+          var pageNotFoundError = new Error('Page Not Found');
           pageNotFoundError.name = 'Crowi:Page:NotFound';
           return reject(pageNotFoundError);
         }
 
         if (!pageData.isGrantedFor(userData)) {
           PageGroupRelation.isExistsGrantedGroupForPageAndUser(pageData, userData)
-            .then(function (checkResult) {
+            .then(function(checkResult) {
               if (!checkResult) {
                 return reject(new Error('Page is not granted for the user')); //PAGE_GRANT_ERROR, null);
-              } else {
+              }
+              else {
                 // return resolve(pageData);
                 self.populatePageData(pageData, revisionId || null).then(resolve).catch(reject);
               }
             })
-            .catch(function (err) {
+            .catch(function(err) {
               return reject(err);
             });
         }
@@ -625,7 +629,7 @@ module.exports = function(crowi) {
     var Page = this
       , options = options || {}
       , publicOnly = options.publicOnly || true
-      , criteria = {redirectTo: null,}
+      , criteria = {redirectTo: null, }
       ;
 
     if (publicOnly) {
@@ -697,7 +701,7 @@ module.exports = function(crowi) {
           Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS})
           .then(resolve)
           .catch(reject);
-        })
+        });
     });
   };
 
@@ -753,7 +757,7 @@ module.exports = function(crowi) {
         {grant: GRANT_RESTRICTED, grantedUsers: userData._id},
         {grant: GRANT_SPECIFIED, grantedUsers: userData._id},
         {grant: GRANT_OWNER, grantedUsers: userData._id},
-      ],})
+      ], })
       .and({
         $or: pathCondition
       });
@@ -768,7 +772,7 @@ module.exports = function(crowi) {
     }
 
     return q;
-  }
+  };
 
   pageSchema.statics.updatePageProperty = function(page, updateData) {
     var Page = this;
@@ -784,7 +788,7 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.updateGrant = function (page, grant, userData, grantUserGroupId) {
+  pageSchema.statics.updateGrant = function(page, grant, userData, grantUserGroupId) {
     var Page = this;
 
     if (grant == GRANT_USER_GROUP && grantUserGroupId == null) {
@@ -794,7 +798,8 @@ module.exports = function(crowi) {
       page.grant = grant;
       if (grant == GRANT_PUBLIC || grant == GRANT_USER_GROUP) {
         page.grantedUsers = [];
-      } else {
+      }
+      else {
         page.grantedUsers = [];
         page.grantedUsers.push(userData._id);
       }
@@ -813,7 +818,7 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.updateGrantUserGroup = function (page, grant, grantUserGroupId, userData) {
+  pageSchema.statics.updateGrantUserGroup = function(page, grant, grantUserGroupId, userData) {
     var UserGroupRelation = crowi.model('UserGroupRelation');
     var PageGroupRelation = crowi.model('PageGroupRelation');
 
@@ -897,50 +902,50 @@ module.exports = function(crowi) {
       , grantUserGroupId = options.grantUserGroupId || null;
 
     // force public
-      if (isPortalPath(path)) {
-        grant = GRANT_PUBLIC;
-      }
+    if (isPortalPath(path)) {
+      grant = GRANT_PUBLIC;
+    }
 
-      return new Promise(function(resolve, reject) {
-        Page.findOne({path: path}, function(err, pageData) {
-          if (pageData) {
-            return reject(new Error('Cannot create new page to existed path'));
-          }
+    return new Promise(function(resolve, reject) {
+      Page.findOne({path: path}, function(err, pageData) {
+        if (pageData) {
+          return reject(new Error('Cannot create new page to existed path'));
+        }
 
-          var newPage = new Page();
-          newPage.path = path;
-          newPage.creator = user;
-          newPage.lastUpdateUser = user;
-          newPage.createdAt = Date.now();
-          newPage.updatedAt = Date.now();
-          newPage.redirectTo = redirectTo;
-          newPage.grant = grant;
-          newPage.status = STATUS_PUBLISHED;
-          newPage.grantedUsers = [];
-          newPage.grantedUsers.push(user);
-
-          newPage.save(function (err, newPage) {
-            if (err) {
-              return reject(err);
-            }
+        var newPage = new Page();
+        newPage.path = path;
+        newPage.creator = user;
+        newPage.lastUpdateUser = user;
+        newPage.createdAt = Date.now();
+        newPage.updatedAt = Date.now();
+        newPage.redirectTo = redirectTo;
+        newPage.grant = grant;
+        newPage.status = STATUS_PUBLISHED;
+        newPage.grantedUsers = [];
+        newPage.grantedUsers.push(user);
+
+        newPage.save(function(err, newPage) {
+          if (err) {
+            return reject(err);
+          }
 
-            if (newPage.grant == Page.GRANT_USER_GROUP && grantUserGroupId != null) {
-              Page.updateGrantUserGroup(newPage, grant, grantUserGroupId, user)
+          if (newPage.grant == Page.GRANT_USER_GROUP && grantUserGroupId != null) {
+            Page.updateGrantUserGroup(newPage, grant, grantUserGroupId, user)
               .catch((err) => {
                 return reject(err);
               });
-            }
-            var newRevision = Revision.prepareRevision(newPage, body, user, {format: format});
-            Page.pushRevision(newPage, newRevision, user).then(function(data) {
-              resolve(data);
-              pageEvent.emit('create', data, user);
-            }).catch(function(err) {
-              debug('Push Revision Error on create page', err);
-              return reject(err);
-            });
+          }
+          var newRevision = Revision.prepareRevision(newPage, body, user, {format: format});
+          Page.pushRevision(newPage, newRevision, user).then(function(data) {
+            resolve(data);
+            pageEvent.emit('create', data, user);
+          }).catch(function(err) {
+            debug('Push Revision Error on create page', err);
+            return reject(err);
           });
         });
       });
+    });
   };
 
   pageSchema.statics.updatePage = function(pageData, body, user, options) {
@@ -961,7 +966,8 @@ module.exports = function(crowi) {
             resolve(data);
             pageEvent.emit('update', data, user);
           });
-        } else {
+        }
+        else {
           resolve(pageData);
           pageEvent.emit('update', pageData, user);
         }
@@ -986,30 +992,31 @@ module.exports = function(crowi) {
           // が、 /trash 以下にページが有るのは、個別に作っていたケースのみ。
           // 一応しばらく前から uncreatable pages になっているのでこれでいいことにする
           debug('Deleted the page, and rename it', pageData.path, newPath);
-          return Page.rename(pageData, newPath, user, {createRedirectPage: true})
+          return Page.rename(pageData, newPath, user, {createRedirectPage: true});
         }).then(function(pageData) {
           resolve(pageData);
         }).catch(reject);
       });
-    } else {
+    }
+    else {
       return Promise.reject('Page is not deletable.');
     }
   };
 
-  pageSchema.statics.deletePageRecursively = function (pageData, user, options) {
+  pageSchema.statics.deletePageRecursively = function(pageData, user, options) {
     var Page = this
       , path = pageData.path
       , options = options || {}
       ;
 
-    return new Promise(function (resolve, reject) {
+    return new Promise(function(resolve, reject) {
       Page
       .generateQueryToListWithDescendants(path, user, options)
-      .then(function (pages) {
-        Promise.all(pages.map(function (page) {
+      .then(function(pages) {
+        Promise.all(pages.map(function(page) {
           return Page.deletePage(page, user, options);
         }))
-        .then(function (data) {
+        .then(function(data) {
           return resolve(pageData);
         });
       });
@@ -1034,12 +1041,12 @@ module.exports = function(crowi) {
 
         return Page.completelyDeletePage(originPageData);
       }).then(function(done) {
-        return Page.updatePageProperty(pageData, {status: STATUS_PUBLISHED, lastUpdateUser: user})
+        return Page.updatePageProperty(pageData, {status: STATUS_PUBLISHED, lastUpdateUser: user});
       }).then(function(done) {
         pageData.status = STATUS_PUBLISHED;
 
         debug('Revert deleted the page, and rename again it', pageData, newPath);
-        return Page.rename(pageData, newPath, user, {})
+        return Page.rename(pageData, newPath, user, {});
       }).then(function(done) {
         pageData.path = newPath;
         resolve(pageData);
@@ -1047,25 +1054,25 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.revertDeletedPageRecursively = function (pageData, user, options) {
+  pageSchema.statics.revertDeletedPageRecursively = function(pageData, user, options) {
     var Page = this
       , path = pageData.path
       , options = options || { includeDeletedPage: true}
       ;
 
-      return new Promise(function (resolve, reject) {
-        Page
+    return new Promise(function(resolve, reject) {
+      Page
         .generateQueryToListWithDescendants(path, user, options)
-        .then(function (pages) {
-          Promise.all(pages.map(function (page) {
+        .then(function(pages) {
+          Promise.all(pages.map(function(page) {
             return Page.revertDeletedPage(page, user, options);
           }))
-          .then(function (data) {
+          .then(function(data) {
             return resolve(data[0]);
           });
         });
-      });
-    };
+    });
+  };
 
   /**
    * This is danger.
@@ -1102,21 +1109,21 @@ module.exports = function(crowi) {
     });
   };
 
-  pageSchema.statics.completelyDeletePageRecursively = function (pageData, user, options) {
+  pageSchema.statics.completelyDeletePageRecursively = function(pageData, user, options) {
     // Delete Bookmarks, Attachments, Revisions, Pages and emit delete
     var Page = this
       , path = pageData.path
       , options = options || { includeDeletedPage: true }
       ;
 
-    return new Promise(function (resolve, reject) {
+    return new Promise(function(resolve, reject) {
       Page
       .generateQueryToListWithDescendants(path, user, options)
-      .then(function (pages) {
-        Promise.all(pages.map(function (page) {
+      .then(function(pages) {
+        Promise.all(pages.map(function(page) {
           return Page.completelyDeletePage(page, user, options);
         }))
-        .then(function (data) {
+        .then(function(data) {
           return resolve(data[0]);
         });
       });
@@ -1166,13 +1173,13 @@ module.exports = function(crowi) {
         return Page.removePageById(redirectOriginPageData.id)
           // remove recursive
           .then(() => {
-            return Page.removeRedirectOriginPageByPath(redirectOriginPageData.path)
+            return Page.removeRedirectOriginPageByPath(redirectOriginPageData.path);
           });
       })
       .catch((err) => {
         // do nothing if origin page doesn't exist
         return Promise.resolve();
-      })
+      });
   };
 
   pageSchema.statics.rename = function(pageData, newPagePath, user, options) {
@@ -1187,14 +1194,15 @@ module.exports = function(crowi) {
       Page.updatePageProperty(pageData, {updatedAt: Date.now(), path: newPagePath, lastUpdateUser: user})
       .then(function(data) {
         // reivisions の path を変更
-        return Revision.updateRevisionListByPath(path, {path: newPagePath}, {})
+        return Revision.updateRevisionListByPath(path, {path: newPagePath}, {});
       }).then(function(data) {
         pageData.path = newPagePath;
 
         if (createRedirectPage) {
           var body = 'redirect ' + newPagePath;
           Page.create(path, body, user, {redirectTo: newPagePath}).then(resolve).catch(reject);
-        } else {
+        }
+        else {
           resolve(data);
         }
         pageEvent.emit('update', pageData, user); // update as renamed page
@@ -1237,7 +1245,7 @@ module.exports = function(crowi) {
       returnPath += '/';
     }
     return returnPath;
-  }
+  };
 
   pageSchema.statics.GRANT_PUBLIC = GRANT_PUBLIC;
   pageSchema.statics.GRANT_RESTRICTED = GRANT_RESTRICTED;

+ 9 - 6
lib/models/revision.js

@@ -1,10 +1,13 @@
 module.exports = function(crowi) {
-  var debug = require('debug')('growi:models:revision')
-    , mongoose = require('mongoose')
-    , Xss = require('../util/xss')
+  /* eslint-disable no-unused-vars */
+  const logger = require('@alias/logger')('growi:models:revision');
+  /* eslint-enable */
+
+  var mongoose = require('mongoose')
     , ObjectId = mongoose.Schema.Types.ObjectId
     , revisionSchema;
 
+
   revisionSchema = new mongoose.Schema({
     path: { type: String, required: true },
     body: { type: String, required: true },
@@ -47,12 +50,12 @@ module.exports = function(crowi) {
 
           return resolve(data);
         });
-      });
+    });
   };
 
   revisionSchema.statics.findRevisions = function(ids) {
     var Revision = this,
-        User = crowi.model('User');
+      User = crowi.model('User');
 
     if (!Array.isArray(ids)) {
       return Promise.reject('The argument was not Array.');
@@ -82,7 +85,7 @@ module.exports = function(crowi) {
 
   revisionSchema.statics.findRevisionList = function(path, options) {
     var Revision = this,
-        User = crowi.model('User');
+      User = crowi.model('User');
 
     return new Promise(function(resolve, reject) {
       Revision.find({path: path})

+ 15 - 21
lib/models/updatePost.js

@@ -5,6 +5,7 @@ module.exports = function(crowi) {
   var debug = require('debug')('growi:models:updatePost')
     , mongoose = require('mongoose')
     , ObjectId = mongoose.Schema.Types.ObjectId
+    , updatePostSchema
   ;
 
   // TODO: slack 以外の対応
@@ -18,13 +19,11 @@ module.exports = function(crowi) {
     createdAt: { type: Date, default: Date.now }
   });
 
-  updatePostSchema.statics.normalizeChannelName = function(channel)
-  {
+  updatePostSchema.statics.normalizeChannelName = function(channel) {
     return channel.replace(/(#|,)/g, '');
-  }
+  };
 
-  updatePostSchema.statics.createPrefixesByPathPattern = function(pathPattern)
-  {
+  updatePostSchema.statics.createPrefixesByPathPattern = function(pathPattern) {
     var patternPrefix = ['*', '*'];
 
     // not begin with slash
@@ -42,10 +41,9 @@ module.exports = function(crowi) {
       patternPrefix[1] = pattern[1];
     }
     return patternPrefix;
-  }
+  };
 
-  updatePostSchema.statics.getRegExpByPattern = function(pattern)
-  {
+  updatePostSchema.statics.getRegExpByPattern = function(pattern) {
     var reg = pattern;
     if (!reg.match(/^\/.*/)) {
       reg = '/*' + reg + '*';
@@ -55,10 +53,9 @@ module.exports = function(crowi) {
     reg = reg.replace(/(\*)/g, '.*');
 
     return new RegExp(reg);
-  }
+  };
 
-  updatePostSchema.statics.findSettingsByPath = function(path)
-  {
+  updatePostSchema.statics.findSettingsByPath = function(path) {
     var UpdatePost = this;
     var prefixes = UpdatePost.createPrefixesByPathPattern(path);
 
@@ -83,8 +80,7 @@ module.exports = function(crowi) {
     });
   };
 
-  updatePostSchema.statics.findAll = function(offset)
-  {
+  updatePostSchema.statics.findAll = function(offset) {
     var UpdatePost = this;
     var offset = offset || 0;
 
@@ -107,8 +103,7 @@ module.exports = function(crowi) {
     });
   };
 
-  updatePostSchema.statics.create = function(pathPattern, channel, user)
-  {
+  updatePostSchema.statics.create = function(pathPattern, channel, user) {
     var UpdatePost = this;
     var provider = 'slack'; // now slack only
 
@@ -124,17 +119,16 @@ module.exports = function(crowi) {
 
     return new Promise(function(resolve, reject) {
       notif.save(function(err, doc) {
-       if (err) {
-         return reject(err);
-       }
+        if (err) {
+          return reject(err);
+        }
 
-       return resolve(doc);
+        return resolve(doc);
       });
     });
   };
 
-  updatePostSchema.statics.remove = function(id)
-  {
+  updatePostSchema.statics.remove = function(id) {
     var UpdatePost = this;
 
     return new Promise(function(resolve, reject) {

+ 6 - 6
lib/models/user-group-relation.js

@@ -54,7 +54,7 @@ class UserGroupRelation {
       .populate('relatedUser')
       .populate('relatedGroup')
       .exec();
-  };
+  }
 
   /**
    * find all user and group relation of UserGroup
@@ -146,7 +146,7 @@ class UserGroupRelation {
     const query = {
       relatedGroup: userGroupId,
       relatedUser: userData.id
-    }
+    };
 
     return this
       .findOne(query)
@@ -173,7 +173,7 @@ class UserGroupRelation {
         });
         const query = { _id: { $nin: relatedUserIds }, status: User.STATUS_ACTIVE };
 
-        debug("findUserByNotRelatedGroup ", query);
+        debug('findUserByNotRelatedGroup ', query);
         return User.find(query).exec();
       });
   }
@@ -191,7 +191,7 @@ class UserGroupRelation {
     const query = {
       relatedGroup: userGroup.id,
       relatedUser: userData.id
-    }
+    };
 
     return this
       .count(query)
@@ -272,8 +272,8 @@ class UserGroupRelation {
 
 }
 
-module.exports = function (crowi) {
+module.exports = function(crowi) {
   UserGroupRelation.crowi = crowi;
   schema.loadClass(UserGroupRelation);
   return mongoose.model('UserGroupRelation', schema);
-}
+};

+ 8 - 8
lib/models/user-group.js

@@ -43,18 +43,18 @@ class UserGroup {
    * model static methods
    */
 
-   // グループ画像パスの生成
+  // グループ画像パスの生成
   static createUserGroupPictureFilePath(userGroup, name) {
     var ext = '.' + name.match(/(.*)(?:\.([^.]+$))/)[2];
 
     return 'userGroup/' + userGroup._id + ext;
-  };
+  }
 
   // すべてのグループを取得(オプション指定可)
   static findAllGroups(option) {
 
     return this.find().exec();
-  };
+  }
 
   /**
    * find all entities with pagination
@@ -80,13 +80,13 @@ class UserGroup {
       .catch((err) => {
         debug('Error on pagination:', err);
       });
-  };
+  }
 
   // TBD: グループ名によるグループ検索
   static findUserGroupByName(name) {
     const query = { name: name };
     return this.findOne(query);
-  };
+  }
 
   // 登録可能グループ名確認
   static isRegisterableName(name) {
@@ -96,7 +96,7 @@ class UserGroup {
       .then((userGroupData) => {
         return (userGroupData == null);
       });
-  };
+  }
 
   // グループの完全削除
   static removeCompletelyById(id) {
@@ -143,9 +143,9 @@ class UserGroup {
 }
 
 
-module.exports = function (crowi) {
+module.exports = function(crowi) {
   UserGroup.crowi = crowi;
   schema.loadClass(UserGroup);
   return mongoose.model('UserGroup', schema);
-}
+};
 

+ 38 - 36
lib/models/user.js

@@ -57,7 +57,7 @@ module.exports = function(crowi) {
 
   userEvent.on('activated', userEvent.onActivated);
 
-  function decideUserStatusOnRegistration () {
+  function decideUserStatusOnRegistration() {
     var Config = crowi.model('Config'),
       config = crowi.getConfig();
 
@@ -79,10 +79,10 @@ module.exports = function(crowi) {
 
   function generateRandomEmail() {
     const randomstr = generateRandomTempPassword();
-    return `change-it-${randomstr}@example.com`
+    return `change-it-${randomstr}@example.com`;
   }
 
-  function generateRandomTempPassword () {
+  function generateRandomTempPassword() {
     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!=-_';
     var password = '';
     var len = 12;
@@ -95,14 +95,14 @@ module.exports = function(crowi) {
     return password;
   }
 
-  function generatePassword (password) {
+  function generatePassword(password) {
     var hasher = crypto.createHash('sha256');
     hasher.update(crowi.env.PASSWORD_SEED + password);
 
     return hasher.digest('hex');
   }
 
-  function generateApiToken (user) {
+  function generateApiToken(user) {
     var hasher = crypto.createHash('sha256');
     hasher.update((new Date).getTime() + user._id);
 
@@ -179,7 +179,8 @@ module.exports = function(crowi) {
       self.save(function(err, userData) {
         if (err) {
           return reject(err);
-        } else {
+        }
+        else {
           return resolve(userData);
         }
       });
@@ -339,7 +340,7 @@ module.exports = function(crowi) {
       .sort(sort)
       .skip(options.skip || 0)
       .limit(options.limit || 21)
-      .exec(function (err, userData) {
+      .exec(function(err, userData) {
         callback(err, userData);
       });
 
@@ -360,10 +361,10 @@ module.exports = function(crowi) {
     return new Promise(function(resolve, reject) {
       User
         .find()
-        .or(status.map(s => { return {status: s}; }))
+        .or(status.map(s => { return {status: s} }))
         .select(fields)
         .sort(sort)
-        .exec(function (err, userData) {
+        .exec(function(err, userData) {
           if (err) {
             return reject(err);
           }
@@ -387,7 +388,7 @@ module.exports = function(crowi) {
         .find({ _id: { $in: ids }, status: status })
         .select(fields)
         .sort(sort)
-        .exec(function (err, userData) {
+        .exec(function(err, userData) {
           if (err) {
             return reject(err);
           }
@@ -416,7 +417,7 @@ module.exports = function(crowi) {
       }
 
       return callback(err, result);
-    }, { sortBy : sort });
+    }, { sortBy: sort });
   };
 
   userSchema.statics.findUsersByPartOfEmail = function(emailPart, options) {
@@ -462,7 +463,7 @@ module.exports = function(crowi) {
     if (googleId == null) {
       callback(null, null);
     }
-    this.findOne({googleId}, function (err, userData) {
+    this.findOne({googleId}, function(err, userData) {
       callback(err, userData);
     });
   };
@@ -480,7 +481,7 @@ module.exports = function(crowi) {
 
   userSchema.statics.findUserByEmailAndPassword = function(email, password, callback) {
     var hashedPassword = generatePassword(password);
-    this.findOne({email: email, password: hashedPassword}, function (err, userData) {
+    this.findOne({email: email, password: hashedPassword}, function(err, userData) {
       callback(err, userData);
     });
   };
@@ -489,7 +490,7 @@ module.exports = function(crowi) {
     var User = this;
     var usernameUsable = true;
 
-    this.findOne({username: username}, function (err, userData) {
+    this.findOne({username: username}, function(err, userData) {
       if (userData) {
         usernameUsable = false;
       }
@@ -503,13 +504,13 @@ module.exports = function(crowi) {
     var usernameUsable = true;
 
     // username check
-    this.findOne({username: username}, function (err, userData) {
+    this.findOne({username: username}, function(err, userData) {
       if (userData) {
         usernameUsable = false;
       }
 
       // email check
-      User.findOne({email: email}, function (err, userData) {
+      User.findOne({email: email}, function(err, userData) {
         if (userData) {
           emailUsable = false;
         }
@@ -525,7 +526,7 @@ module.exports = function(crowi) {
 
   userSchema.statics.removeCompletelyById = function(id, callback) {
     var User = this;
-    User.findById(id, function (err, userData) {
+    User.findById(id, function(err, userData) {
       if (!userData) {
         return callback(err, null);
       }
@@ -551,7 +552,7 @@ module.exports = function(crowi) {
     var User = this;
 
     return new Promise(function(resolve, reject) {
-      User.findById(id, function (err, userData) {
+      User.findById(id, function(err, userData) {
         if (!userData) {
           return reject(new Error('User not found'));
         }
@@ -587,13 +588,13 @@ module.exports = function(crowi) {
       emailList,
       function(email, next) {
         var newUser = new User()
-          ,tmpUsername, password;
+          , tmpUsername, password;
 
         email = email.trim();
 
         // email check
         // TODO: 削除済みはチェック対象から外そう〜
-        User.findOne({email: email}, function (err, userData) {
+        User.findOne({email: email}, function(err, userData) {
           // The user is exists
           if (userData) {
             createdUserList.push({
@@ -622,7 +623,8 @@ module.exports = function(crowi) {
                 user: null,
               });
               debug('save failed!! ', err);
-            } else {
+            }
+            else {
               createdUserList.push({
                 email: email,
                 password: password,
@@ -650,20 +652,20 @@ module.exports = function(crowi) {
               }
 
               mailer.send({
-                  to: user.email,
-                  subject: 'Invitation to ' + Config.appTitle(config),
-                  template: 'admin/userInvitation.txt',
-                  vars: {
-                    email: user.email,
-                    password: user.password,
-                    url: config.crowi['app:url'],
-                    appTitle: Config.appTitle(config),
-                  }
-                },
-                function (err, s) {
-                  debug('completed to send email: ', err, s);
-                  next();
+                to: user.email,
+                subject: 'Invitation to ' + Config.appTitle(config),
+                template: 'admin/userInvitation.txt',
+                vars: {
+                  email: user.email,
+                  password: user.password,
+                  url: config.crowi['app:url'],
+                  appTitle: Config.appTitle(config),
                 }
+              },
+              function(err, s) {
+                debug('completed to send email: ', err, s);
+                next();
+              }
               );
             },
             function(err) {
@@ -705,7 +707,7 @@ module.exports = function(crowi) {
       }
       return callback(err, userData);
     });
-  }
+  };
 
   /**
    * A wrapper function of createUserByEmailAndPasswordAndStatus
@@ -732,7 +734,7 @@ module.exports = function(crowi) {
         return resolve(userData);
       });
     });
-  }
+  };
 
   userSchema.statics.createUserPictureFilePath = function(user, name) {
     var ext = '.' + name.match(/(.*)(?:\.([^.]+$))/)[2];

+ 2 - 2
lib/plugins/plugin-utils-v2.js

@@ -32,9 +32,9 @@ class PluginUtilsV2 {
       name,
       meta,
       entries,
-    }
+    };
   }
 
 }
 
-module.exports = PluginUtilsV2
+module.exports = PluginUtilsV2;

+ 1 - 1
lib/plugins/plugin-utils.js

@@ -91,4 +91,4 @@ class PluginUtils {
   }
 }
 
-module.exports = PluginUtils
+module.exports = PluginUtils;

+ 97 - 80
lib/routes/admin.js

@@ -43,14 +43,16 @@ module.exports = function(crowi, app) {
     if (pagerMin === 1) {
       if (MAX_PAGE_LIST < pagesCount) {
         pagerMax = MAX_PAGE_LIST;
-      } else {
+      }
+      else {
         pagerMax = pagesCount;
       }
     }
     if (pagerMax === pagesCount) {
       if ((pagerMax - MAX_PAGE_LIST) < 1) {
         pagerMin = 1;
-      } else {
+      }
+      else {
         pagerMin = pagerMax - MAX_PAGE_LIST;
       }
     }
@@ -121,7 +123,8 @@ module.exports = function(crowi, app) {
         req.flash('successMessage', ['Successfully updated!']);
         return res.redirect('/admin/markdown');
       });
-    } else {
+    }
+    else {
       req.flash('errorMessage', req.form.errors);
       return res.redirect('/admin/markdown');
     }
@@ -134,17 +137,17 @@ module.exports = function(crowi, app) {
     settingForm = Config.setupCofigFormData('crowi', req.config);
 
     const highlightJsCssSelectorOptions = {
-      "github":           { name: '[Light] Github',         border: false },
-      "github-gist":      { name: '[Light] Github Gist',    border: true },
-      "atom-one-light":   { name: '[Light] Atom One Light', border: true },
-      "xcode":            { name: '[Light] Xcode',          border: true },
-      "vs":               { name: '[Light] Vs',             border: true },
-      "atom-one-dark":    { name: '[Dark] Atom One Dark',   border: false },
-      "hybrid":           { name: '[Dark] Hybrid',          border: false },
-      "monokai":          { name: '[Dark] Monokai',         border: false },
-      "tomorrow-night":   { name: '[Dark] Tomorrow Night',  border: false },
-      "vs2015":           { name: '[Dark] Vs 2015',         border: false },
-    }
+      'github':           { name: '[Light] Github',         border: false },
+      'github-gist':      { name: '[Light] Github Gist',    border: true },
+      'atom-one-light':   { name: '[Light] Atom One Light', border: true },
+      'xcode':            { name: '[Light] Xcode',          border: true },
+      'vs':               { name: '[Light] Vs',             border: true },
+      'atom-one-dark':    { name: '[Dark] Atom One Dark',   border: false },
+      'hybrid':           { name: '[Dark] Hybrid',          border: false },
+      'monokai':          { name: '[Dark] Monokai',         border: false },
+      'tomorrow-night':   { name: '[Dark] Tomorrow Night',  border: false },
+      'vs2015':           { name: '[Dark] Vs 2015',         border: false },
+    };
 
     return res.render('admin/customize', {
       settingForm: settingForm,
@@ -198,7 +201,8 @@ module.exports = function(crowi, app) {
           return res.redirect('/admin/notification');
         });
       });
-    } else {
+    }
+    else {
       req.flash('errorMessage', req.form.errors);
       return res.redirect('/admin/notification');
     }
@@ -217,16 +221,17 @@ module.exports = function(crowi, app) {
     slack.getOauthAccessToken(code)
     .then(data => {
       debug('oauth response', data);
-        Config.updateNamespaceByArray('notification', {'slack:token': data.access_token}, function(err, config) {
-          if (err) {
-            req.flash('errorMessage', ['Failed to save access_token. Please try again.']);
-          } else {
-            Config.updateConfigCache('notification', config);
-            req.flash('successMessage', ['Successfully Connected!']);
-          }
+      Config.updateNamespaceByArray('notification', {'slack:token': data.access_token}, function(err, config) {
+        if (err) {
+          req.flash('errorMessage', ['Failed to save access_token. Please try again.']);
+        }
+        else {
+          Config.updateConfigCache('notification', config);
+          req.flash('successMessage', ['Successfully Connected!']);
+        }
 
-          return res.redirect('/admin/notification');
-        });
+        return res.redirect('/admin/notification');
+      });
     }).catch(err => {
       debug('oauth response ERROR', err);
       req.flash('errorMessage', ['Failed to fetch access_token. Please do connect again.']);
@@ -254,7 +259,8 @@ module.exports = function(crowi, app) {
           return res.redirect('/admin/notification#slack-incoming-webhooks');
         });
       });
-    } else {
+    }
+    else {
       req.flash('errorMessage', req.form.errors);
       return res.redirect('/admin/notification#slack-incoming-webhooks');
     }
@@ -290,7 +296,7 @@ module.exports = function(crowi, app) {
         });
     })
     .then(function() {
-      return search.buildIndex()
+      return search.buildIndex();
     })
     .then(function(data) {
       if (!data.errors) {
@@ -302,7 +308,8 @@ module.exports = function(crowi, app) {
       if (!data.errors) {
         debug('Data is successfully indexed.');
         req.flash('successMessage', 'Data is successfully indexed.');
-      } else {
+      }
+      else {
         debug('Data index error.', data.errors);
         req.flash('errorMessage', `Data index error: ${data.errors}`);
       }
@@ -336,12 +343,14 @@ module.exports = function(crowi, app) {
       User.createUsersByInvitation(form.emailList.split('\n'), toSendEmail, function(err, userList) {
         if (err) {
           req.flash('errorMessage', req.form.errors.join('\n'));
-        } else {
+        }
+        else {
           req.flash('createdUser', userList);
         }
         return res.redirect('/admin/users');
       });
-    } else {
+    }
+    else {
       req.flash('errorMessage', req.form.errors.join('\n'));
       return res.redirect('/admin/users');
     }
@@ -353,7 +362,8 @@ module.exports = function(crowi, app) {
       userData.makeAdmin(function(err, userData) {
         if (err === null) {
           req.flash('successMessage', userData.name + 'さんのアカウントを管理者に設定しました。');
-        } else {
+        }
+        else {
           req.flash('errorMessage', '更新に失敗しました。');
           debug(err, userData);
         }
@@ -368,7 +378,8 @@ module.exports = function(crowi, app) {
       userData.removeFromAdmin(function(err, userData) {
         if (err === null) {
           req.flash('successMessage', userData.name + 'さんのアカウントを管理者から外しました。');
-        } else {
+        }
+        else {
           req.flash('errorMessage', '更新に失敗しました。');
           debug(err, userData);
         }
@@ -383,7 +394,8 @@ module.exports = function(crowi, app) {
       userData.statusActivate(function(err, userData) {
         if (err === null) {
           req.flash('successMessage', userData.name + 'さんのアカウントを有効化しました');
-        } else {
+        }
+        else {
           req.flash('errorMessage', '更新に失敗しました。');
           debug(err, userData);
         }
@@ -399,7 +411,8 @@ module.exports = function(crowi, app) {
       userData.statusSuspend(function(err, userData) {
         if (err === null) {
           req.flash('successMessage', userData.name + 'さんのアカウントを利用停止にしました');
-        } else {
+        }
+        else {
           req.flash('errorMessage', '更新に失敗しました。');
           debug(err, userData);
         }
@@ -436,7 +449,7 @@ module.exports = function(crowi, app) {
           throw new Error(err.message);
         }
         return userData;
-      })
+      });
     })
     .then((userData) => {
       return Page.removePageByPath(`/user/${username}`)
@@ -461,7 +474,8 @@ module.exports = function(crowi, app) {
       if (err) {
         debug('Error while removing user.', err, id);
         req.flash('errorMessage', '完全な削除に失敗しました。');
-      } else {
+      }
+      else {
         req.flash('successMessage', '削除しました');
       }
       return res.redirect('/admin/users');
@@ -481,7 +495,7 @@ module.exports = function(crowi, app) {
       debug('Error on reseting password', err);
       return res.json(ApiResponse.error('Error'));
     });
-  }
+  };
 
   actions.externalAccount = {};
   actions.externalAccount.index = function(req, res) {
@@ -494,7 +508,7 @@ module.exports = function(crowi, app) {
         return res.render('admin/external-accounts', {
           accounts: result.docs,
           pager: pager
-        })
+        });
       });
   };
 
@@ -515,13 +529,13 @@ module.exports = function(crowi, app) {
   };
 
   actions.userGroup = {};
-  actions.userGroup.index = function (req, res) {
+  actions.userGroup.index = function(req, res) {
     var page = parseInt(req.query.page) || 1;
     var renderVar = {
-      userGroups : [],
-      userGroupRelations : new Map(),
-      pager : null,
-    }
+      userGroups: [],
+      userGroupRelations: new Map(),
+      pager: null,
+    };
 
     UserGroup.findUserGroupsWithPagination({ page: page })
       .then((result) => {
@@ -539,31 +553,31 @@ module.exports = function(crowi, app) {
         });
       })
       .then((allRelationsPromise) => {
-        return Promise.all(allRelationsPromise)
+        return Promise.all(allRelationsPromise);
       })
       .then((relations) => {
         renderVar.userGroupRelations = new Map(relations);
-        debug("in findUserGroupsWithPagination findAllRelationForUserGroupResult", renderVar.userGroupRelations);
+        debug('in findUserGroupsWithPagination findAllRelationForUserGroupResult', renderVar.userGroupRelations);
         return res.render('admin/user-groups', renderVar);
       })
       .catch( function(err) {
-          debug('Error on find all relations', err);
-          return res.json(ApiResponse.error('Error'));
+        debug('Error on find all relations', err);
+        return res.json(ApiResponse.error('Error'));
       });
   };
 
   // グループ詳細
-  actions.userGroup.detail = function (req, res) {
+  actions.userGroup.detail = function(req, res) {
     var name = req.params.name;
     var renderVar = {
       userGroup: null,
       userGroupRelations: [],
       pageGroupRelations: [],
       notRelatedusers: []
-    }
+    };
     var targetUserGroup = null;
     UserGroup.findUserGroupByName(name)
-      .then(function (userGroup) {
+      .then(function(userGroup) {
         targetUserGroup = userGroup;
         if (targetUserGroup == null) {
           req.flash('errorMessage', 'グループがありません');
@@ -595,15 +609,15 @@ module.exports = function(crowi, app) {
         debug('Error on get userGroupDetail', err);
         return res.redirect('/admin/user-groups');
       });
-  }
+  };
 
   //グループの生成
-  actions.userGroup.create = function (req, res) {
+  actions.userGroup.create = function(req, res) {
     var form = req.form.createGroupForm;
     if (req.form.isValid) {
       UserGroup.createGroupByName(form.userGroupName)
       .then((newUserGroup) => {
-        req.flash('successMessage', newUserGroup.name)
+        req.flash('successMessage', newUserGroup.name);
         req.flash('createdUserGroup', newUserGroup);
         return res.redirect('/admin/user-groups');
       })
@@ -611,14 +625,15 @@ module.exports = function(crowi, app) {
         debug('create userGroup error:', err);
         req.flash('errorMessage', '同じグループ名が既に存在します。');
       });
-    } else {
+    }
+    else {
       req.flash('errorMessage', req.form.errors.join('\n'));
       return res.redirect('/admin/user-groups');
     }
   };
 
   //
-  actions.userGroup.update = function (req, res) {
+  actions.userGroup.update = function(req, res) {
 
     var userGroupId = req.params.userGroupId;
     var name = req.body.name;
@@ -654,7 +669,7 @@ module.exports = function(crowi, app) {
     });
   };
 
-  actions.userGroup.uploadGroupPicture = function (req, res) {
+  actions.userGroup.uploadGroupPicture = function(req, res) {
     var fileUploader = require('../util/fileUploader')(crowi, app);
     //var storagePlugin = new pluginService('storage');
     //var storage = require('../service/storage').StorageService(config);
@@ -669,7 +684,7 @@ module.exports = function(crowi, app) {
       });
     }
 
-    UserGroup.findById(userGroupId, function (err, userGroupData) {
+    UserGroup.findById(userGroupId, function(err, userGroupData) {
       if (!userGroupData) {
         return res.json({
           'status': false,
@@ -691,11 +706,11 @@ module.exports = function(crowi, app) {
       var tmpFileStream = fs.createReadStream(tmpPath, { flags: 'r', encoding: null, fd: null, mode: '0666', autoClose: true });
 
       fileUploader.uploadFile(filePath, tmpFile.mimetype, tmpFileStream, {})
-        .then(function (data) {
+        .then(function(data) {
           var imageUrl = fileUploader.generateUrl(filePath);
           userGroupData.updateImage(imageUrl)
           .then(() => {
-            fs.unlink(tmpPath, function (err) {
+            fs.unlink(tmpPath, function(err) {
               if (err) {
                 debug('Error while deleting tmp file.', err);
               }
@@ -707,7 +722,7 @@ module.exports = function(crowi, app) {
               });
             });
           });
-        }).catch(function (err) {
+        }).catch(function(err) {
           debug('Uploading error', err);
 
           return res.json({
@@ -719,7 +734,7 @@ module.exports = function(crowi, app) {
 
   };
 
-  actions.userGroup.deletePicture = function (req, res) {
+  actions.userGroup.deletePicture = function(req, res) {
 
     var userGroupId = req.params.userGroupId;
     let userGroupName = null;
@@ -753,12 +768,12 @@ module.exports = function(crowi, app) {
   };
 
   // app.post('/_api/admin/user-group/delete' , admin.userGroup.removeCompletely);
-  actions.userGroup.removeCompletely = function (req, res) {
+  actions.userGroup.removeCompletely = function(req, res) {
     const id = req.body.user_group_id;
 
     UserGroup.removeCompletelyById(id)
     .then(() => {
-        req.flash('successMessage', '削除しました');
+      req.flash('successMessage', '削除しました');
       return res.redirect('/admin/user-groups');
     })
     .catch((err) => {
@@ -766,12 +781,12 @@ module.exports = function(crowi, app) {
       req.flash('errorMessage', '完全な削除に失敗しました。');
       return res.redirect('/admin/user-groups');
     });
-  }
+  };
 
   actions.userGroupRelation = {};
   actions.userGroupRelation.index = function(req, res) {
 
-  }
+  };
 
   actions.userGroupRelation.create = function(req, res) {
     const User = crowi.model('User');
@@ -795,18 +810,18 @@ module.exports = function(crowi, app) {
       userGroup = resolves[0];
       user = resolves[1];
       // Relation を作成
-      UserGroupRelation.createRelation(userGroup, user)
+      UserGroupRelation.createRelation(userGroup, user);
     })
     .then((result) => {
       return res.redirect('/admin/user-group-detail/' + userGroup.name);
     }).catch((err) => {
       debug('Error on create user-group relation', err);
       req.flash('errorMessage', 'Error on create user-group relation');
-          return res.redirect('/admin/user-group-detail/' + userGroup.name);
+      return res.redirect('/admin/user-group-detail/' + userGroup.name);
     });
-  }
+  };
 
-  actions.userGroupRelation.remove = function (req, res) {
+  actions.userGroupRelation.remove = function(req, res) {
     const UserGroupRelation = crowi.model('UserGroupRelation');
     var name = req.params.name;
     var relationId = req.params.relationId;
@@ -821,7 +836,7 @@ module.exports = function(crowi, app) {
       req.flash('errorMessage', 'グループのユーザ削除に失敗しました。');
     });
 
-  }
+  };
 
   actions.api = {};
   actions.api.appSetting = function(req, res) {
@@ -841,10 +856,12 @@ module.exports = function(crowi, app) {
 
           return saveSetting(req, res, form);
         });
-      } else {
+      }
+      else {
         return saveSetting(req, res, form);
       }
-    } else {
+    }
+    else {
       return res.json({status: false, message: req.form.errors.join('\n')});
     }
   };
@@ -855,7 +872,8 @@ module.exports = function(crowi, app) {
     if (req.form.isValid) {
       debug('form content', form);
       return saveSetting(req, res, form);
-    } else {
+    }
+    else {
       return res.json({status: false, message: req.form.errors.join('\n')});
     }
   };
@@ -891,10 +909,11 @@ module.exports = function(crowi, app) {
     if (req.form.isValid) {
       debug('form content', form);
       return saveSetting(req, res, form);
-    } else {
+    }
+    else {
       return res.json({status: false, message: req.form.errors.join('\n')});
     }
-  }
+  };
 
   // app.post('/_api/admin/notifications.add'    , admin.api.notificationAdd);
   actions.api.notificationAdd = function(req, res) {
@@ -955,8 +974,7 @@ module.exports = function(crowi, app) {
    * @param {any} res
    * @param {any} form
    */
-  function saveSetting(req, res, form)
-  {
+  function saveSetting(req, res, form) {
     Config.updateNamespaceByArray('crowi', form, function(err, config) {
       Config.updateConfigCache('crowi', config);
       return res.json({status: true});
@@ -973,8 +991,8 @@ module.exports = function(crowi, app) {
     return new Promise((resolve, reject) => {
       Config.updateNamespaceByArray('crowi', form, (err, config) => {
         if (err) {
-          return reject(err)
-        };
+          return reject(err);
+        }
 
         Config.updateConfigCache('crowi', config);
         return resolve();
@@ -982,8 +1000,7 @@ module.exports = function(crowi, app) {
     });
   }
 
-  function validateMailSetting(req, form, callback)
-  {
+  function validateMailSetting(req, form, callback) {
     var mailer = crowi.mailer;
     var option = {
       host: form['mail:smtpHost'],

+ 9 - 8
lib/routes/attachment.js

@@ -54,7 +54,7 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} page_id
    */
-  api.list = function(req, res){
+  api.list = function(req, res) {
     var id = req.query.page_id || null;
     if (!id) {
       return res.json(ApiResponse.error('Parameters page_id is required.'));
@@ -90,7 +90,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} page_id
    * @apiParam {File} file
    */
-  api.add = function(req, res){
+  api.add = function(req, res) {
     var id = req.body.page_id || 0,
       path = decodeURIComponent(req.body.path) || null,
       pageCreated = false,
@@ -116,7 +116,8 @@ module.exports = function(crowi, app) {
             resolve(page);
           })
           .catch(reject);
-      } else {
+      }
+      else {
         Page.findPageById(id).then(resolve).catch(reject);
       }
     }).then(function(pageData) {
@@ -152,20 +153,20 @@ module.exports = function(crowi, app) {
           result.attachment.creator = User.filterToPublicFields(result.attachment.creator);
 
           // delete anyway
-          fs.unlink(tmpPath, function (err) { if (err) { debug('Error while deleting tmp file.'); } });
+          fs.unlink(tmpPath, function(err) { if (err) { debug('Error while deleting tmp file.') } });
 
           return res.json(ApiResponse.success(result));
-        }).catch(function (err) {
+        }).catch(function(err) {
           debug('Error on saving attachment data', err);
           // @TODO
           // Remove from S3
 
           // delete anyway
-          fs.unlink(tmpPath, function (err) { if (err) { debug('Error while deleting tmp file.'); } });
+          fs.unlink(tmpPath, function(err) { if (err) { debug('Error while deleting tmp file.') } });
 
           return res.json(ApiResponse.error('Error while uploading.'));
         });
-      ;
+      
     }).catch(function(err) {
       debug('Attachement upload error', err);
       return res.json(ApiResponse.error('Error.'));
@@ -179,7 +180,7 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} attachment_id
    */
-  api.remove = function(req, res){
+  api.remove = function(req, res) {
     const id = req.body.attachment_id;
 
     Attachment.findById(id)

+ 4 - 3
lib/routes/bookmark.js

@@ -19,7 +19,7 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} page_id Page Id.
    */
-  actions.api.get = function (req, res) {
+  actions.api.get = function(req, res) {
     var pageId = req.query.page_id;
 
     Bookmark.findByPageIdAndUserId(pageId, req.user)
@@ -50,7 +50,8 @@ module.exports = function(crowi, app) {
     .then(function(pageData) {
       if (pageData) {
         return Bookmark.add(pageData, req.user);
-      } else {
+      }
+      else {
         return res.json(ApiResponse.success({bookmark: null}));
       }
     }).then(function(data) {
@@ -72,7 +73,7 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} page_id Page Id.
    */
-  actions.api.remove = function(req, res){
+  actions.api.remove = function(req, res) {
     var pageId = req.body.page_id;
 
     Bookmark.removeBookmark(pageId, req.user)

+ 6 - 6
lib/routes/comment.js

@@ -19,7 +19,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} page_id Page 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;
 
@@ -50,7 +50,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} comment Comment body
    * @apiParam {Number} comment_position=-1 Line number of the comment
    */
-  api.add = function(req, res){
+  api.add = function(req, res) {
     var form = req.form.commentForm;
 
     if (!req.form.isValid) {
@@ -79,20 +79,20 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} comment_id Comment Id.
    */
-  api.remove = function(req, res){
+  api.remove = function(req, res) {
     var commentId = req.body.comment_id;
     if (!commentId) {
-      return Promise.resolve(res.json(ApiResponse.error(`'comment_id' is undefined`)));
+      return Promise.resolve(res.json(ApiResponse.error('\'comment_id\' is undefined')));
     }
 
     return Comment.findById(commentId).exec()
       .then(function(comment) {
         return comment.remove()
         .then(function() {
-           return Page.updateCommentCount(comment.page);
+          return Page.updateCommentCount(comment.page);
         })
         .then(function() {
-           return res.json(ApiResponse.success({})); 
+          return res.json(ApiResponse.success({})); 
         });
       })
       .catch(function(err) {

+ 4 - 2
lib/routes/index.js

@@ -24,6 +24,8 @@ module.exports = function(crowi, app) {
     , Config    = crowi.model('Config')
     ;
 
+  /* eslint-disable comma-spacing */
+
   app.get('/'                        , middleware.applicationInstalled(), loginRequired(crowi, app, false) , page.pageListShow);
 
   app.get('/installer'               , middleware.applicationNotInstalled() , middleware.checkSearchIndicesGenerated(crowi, app) , installer.index);
@@ -119,8 +121,8 @@ module.exports = function(crowi, app) {
   app.post('/_api/admin/user-group/:userGroupId/picture/upload', loginRequired(crowi, app), uploads.single('userGroupPicture'), admin.userGroup.uploadGroupPicture);
 
   // user-group-relations admin
-  app.post('/admin/user-group-relation/create', loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroupRelation.create)
-  app.post('/admin/user-group-relation/:name/remove-relation/:relationId', loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroupRelation.remove)
+  app.post('/admin/user-group-relation/create', loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroupRelation.create);
+  app.post('/admin/user-group-relation/:name/remove-relation/:relationId', loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroupRelation.remove);
 
   app.get('/me'                       , loginRequired(crowi, app) , me.index);
   app.get('/me/password'              , loginRequired(crowi, app) , me.password);

+ 3 - 2
lib/routes/installer.js

@@ -58,7 +58,7 @@ module.exports = function(crowi, app) {
 
             // login with passport
             req.logIn(userData, (err) => {
-              if (err) { return next(); }
+              if (err) { return next() }
               else {
                 req.flash('successMessage', 'GROWI のインストールが完了しました!はじめに、このページで各種設定を確認してください。');
                 return res.redirect('/admin/app');
@@ -70,7 +70,8 @@ module.exports = function(crowi, app) {
           createInitialPages(userData, language);
         });
       });
-    } else {
+    }
+    else {
       return res.render('installer');
     }
   };

+ 10 - 13
lib/routes/login-passport.js

@@ -27,7 +27,8 @@ module.exports = function(crowi, app) {
     if (jumpTo) {
       req.session.jumpTo = null;
       return res.redirect(jumpTo);
-    } else {
+    }
+    else {
       return res.redirect('/');
     }
   };
@@ -73,7 +74,7 @@ module.exports = function(crowi, app) {
     const loginForm = req.body.loginForm;
 
     if (!req.form.isValid) {
-      debug("invalid form");
+      debug('invalid form');
       return res.render('login', {
       });
     }
@@ -94,7 +95,7 @@ module.exports = function(crowi, app) {
       }
 
       // authentication failure
-      if (!ldapAccountInfo) { return next(); }
+      if (!ldapAccountInfo) { return next() }
       // check groups
       if (!isValidLdapUserByGroupFilter(ldapAccountInfo)) {
         return loginFailure(req, res, next);
@@ -130,7 +131,7 @@ module.exports = function(crowi, app) {
         .then((user) => {
           // login
           req.logIn(user, (err) => {
-            if (err) { return next(); }
+            if (err) { return next() }
             else {
               return loginSuccess(req, res, user);
             }
@@ -147,7 +148,7 @@ module.exports = function(crowi, app) {
         });
 
     })(req, res, next);
-  }
+  };
 
   /**
    * middleware that test credentials with LdapStrategy
@@ -164,8 +165,6 @@ module.exports = function(crowi, app) {
       });
     }
 
-    const loginForm = req.body.loginForm;
-
     passport.authenticate('ldapauth', (err, user, info) => {
       if (res.headersSent) {  // dirty hack -- 2017.09.25
         return;               // cz: somehow passport.authenticate called twice when ECONNREFUSED error occurred
@@ -198,7 +197,7 @@ module.exports = function(crowi, app) {
         });
       }
     })(req, res, () => {});
-  }
+  };
 
   /**
    * middleware that login with LocalStrategy
@@ -207,8 +206,6 @@ module.exports = function(crowi, app) {
    * @param {*} next
    */
   const loginWithLocal = (req, res, next) => {
-    const loginForm = req.body.loginForm;
-
     if (!req.form.isValid) {
       return res.render('login', {
       });
@@ -224,15 +221,15 @@ module.exports = function(crowi, app) {
         req.flash('warningMessage', 'Database Server Error occured.');
         return next(); // pass and the flash message is displayed when all of authentications are failed.
       }
-      if (!user) { return next(); }
+      if (!user) { return next() }
       req.logIn(user, (err) => {
-        if (err) { return next(); }
+        if (err) { return next() }
         else {
           return loginSuccess(req, res, user);
         }
       });
     })(req, res, next);
-  }
+  };
 
   return {
     loginFailure,

+ 45 - 39
lib/routes/login.js

@@ -1,26 +1,22 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var googleapis = require('googleapis')
-    , debug = require('debug')('growi:routes:login')
+  var debug = require('debug')('growi:routes:login')
     , async    = require('async')
-    , passport = require('passport')
     , config = crowi.getConfig()
     , mailer = crowi.getMailer()
-    , Page = crowi.model('Page')
     , User = crowi.model('User')
     , Config = crowi.model('Config')
-    , Revision = crowi.model('Revision')
     , actions = {};
 
 
   var clearGoogleSession = function(req) {
-      req.session.googleAuthCode
-        = req.session.googleId
-        = req.session.googleEmail
-        = req.session.googleName
-        = req.session.googleImage
-        = null;
+    req.session.googleAuthCode
+      = req.session.googleId
+      = req.session.googleEmail
+      = req.session.googleName
+      = req.session.googleImage
+      = null;
   };
   var loginSuccess = function(req, res, userData) {
     req.user = req.session.user = userData;
@@ -43,7 +39,8 @@ module.exports = function(crowi, app) {
     if (jumpTo) {
       req.session.jumpTo = null;
       return res.redirect(jumpTo);
-    } else {
+    }
+    else {
       return res.redirect('/');
     }
   };
@@ -70,9 +67,9 @@ module.exports = function(crowi, app) {
 
     if (reason === 'suspended') {
       reasonMessage = 'This account is suspended.';
-    } else if (reason === 'registered') {
+    }
+    else if (reason === 'registered') {
       reasonMessage = 'Wait for approved by administrators.';
-    } else {
     }
 
     return res.render('login/error', {
@@ -90,14 +87,15 @@ module.exports = function(crowi, app) {
 
       // find user
       User.findUserByUsernameOrEmail(username, password, (err, user) => {
-        if (err) { return loginFailure(req, res); }
+        if (err) { return loginFailure(req, res) }
         // check existence and password
         if (!user || !user.isPasswordValid(password)) {
           return loginFailure(req, res);
         }
         return loginSuccess(req, res, user);
       });
-    } else { // method GET
+    }
+    else { // method GET
       if (req.form) {
         debug(req.form.errors);
       }
@@ -119,7 +117,8 @@ module.exports = function(crowi, app) {
         req.session.googleCallbackAction = '/login/google';
         return res.redirect(redirectUrl);
       });
-    } else {
+    }
+    else {
       googleAuth.handleCallback(req, function(err, tokenInfo) {
         debug('handleCallback', err, tokenInfo);
         if (err) {
@@ -164,7 +163,7 @@ module.exports = function(crowi, app) {
       var googleImage = registerForm.googleImage || null;
 
       // email と username の unique チェックする
-      User.isRegisterable(email, username, function (isRegisterable, errOn) {
+      User.isRegisterable(email, username, function(isRegisterable, errOn) {
         var isError = false;
         if (!User.isEmailValid(email)) {
           isError = true;
@@ -190,7 +189,8 @@ module.exports = function(crowi, app) {
           if (err) {
             req.flash('registerWarningMessage', 'Failed to register.');
             return res.redirect('/register');
-          } else {
+          }
+          else {
 
             // 作成後、承認が必要なモードなら、管理者に通知する
             const appTitle = Config.appTitle(config);
@@ -201,20 +201,20 @@ module.exports = function(crowi, app) {
                   admins,
                   function(adminUser, next) {
                     mailer.send({
-                        to: adminUser.email,
-                        subject: '[' + appTitle + ':admin] A New User Created and Waiting for Activation',
-                        template: 'admin/userWaitingActivation.txt',
-                        vars: {
-                          createdUser: userData,
-                          adminUser: adminUser,
-                          url: config.crowi['app:url'],
-                          appTitle: appTitle,
-                        }
-                      },
-                      function (err, s) {
-                        debug('completed to send email: ', err, s);
-                        next();
+                      to: adminUser.email,
+                      subject: '[' + appTitle + ':admin] A New User Created and Waiting for Activation',
+                      template: 'admin/userWaitingActivation.txt',
+                      vars: {
+                        createdUser: userData,
+                        adminUser: adminUser,
+                        url: config.crowi['app:url'],
+                        appTitle: appTitle,
                       }
+                    },
+                    function(err, s) {
+                      debug('completed to send email: ', err, s);
+                      next();
+                    }
                     );
                   },
                   function(err) {
@@ -255,13 +255,14 @@ module.exports = function(crowi, app) {
                       }
                       // DONE
                     });
-                  }).catch(function (err) { // ignore
+                  }).catch(function(err) { // ignore
                     debug('Upload error', err);
                   });
                 }).catch(function() { // ignore
                 });
               }
-            } else {
+            }
+            else {
               // add a flash message to inform the user that processing was successful -- 2017.09.23 Yuki Takei
               // cz. loginSuccess method doesn't work on it's own when using passport
               //      because `req.login()` prepared by passport is not called.
@@ -272,7 +273,8 @@ module.exports = function(crowi, app) {
           }
         });
       });
-    } else { // method GET of form is not valid
+    }
+    else { // method GET of form is not valid
       debug('session is', req.session);
       var isRegistering = true;
       // google callback を受ける可能性もある
@@ -306,7 +308,8 @@ module.exports = function(crowi, app) {
           }
           return res.render('login', { isRegistering, googleId, googleEmail, googleName, googleImage, });
         });
-      } else {
+      }
+      else {
         return res.render('login', { isRegistering, googleId, googleEmail, googleName, googleImage, });
       }
     }
@@ -342,17 +345,20 @@ module.exports = function(crowi, app) {
             if (err) {
               req.flash('warningMessage', 'アクティベートに失敗しました。');
               return res.render('invited');
-            } else {
+            }
+            else {
               return res.redirect('/');
             }
           });
-        } else {
+        }
+        else {
           req.flash('warningMessage', '利用できないユーザーIDです。');
           debug('username', username);
           return res.render('invited');
         }
       });
-    } else {
+    }
+    else {
       return res.render('invited', {
       });
     }

+ 32 - 25
lib/routes/me.js

@@ -16,7 +16,7 @@ module.exports = function(crowi, app) {
 
   actions.api = api;
 
-  api.uploadPicture = function (req, res) {
+  api.uploadPicture = function(req, res) {
     var fileUploader = require('../util/fileUploader')(crowi, app);
     //var storagePlugin = new pluginService('storage');
     //var storage = require('../service/storage').StorageService(config);
@@ -53,7 +53,7 @@ module.exports = function(crowi, app) {
     .then(function(data) {
       var imageUrl = fileUploader.generateUrl(filePath);
       req.user.updateImage(imageUrl, function(err, data) {
-        fs.unlink(tmpPath, function (err) {
+        fs.unlink(tmpPath, function(err) {
           // エラー自体は無視
           if (err) {
             debug('Error while deleting tmp file.', err);
@@ -66,7 +66,7 @@ module.exports = function(crowi, app) {
           });
         });
       });
-    }).catch(function (err) {
+    }).catch(function(err) {
       debug('Uploading error', err);
 
       return res.json({
@@ -99,7 +99,7 @@ module.exports = function(crowi, app) {
         { email: userData.email },                  // query
         { name, email, lang, isEmailPublished },                      // updating data
         { runValidators: true, context: 'query' },  // for validation
-                                                    //   see https://www.npmjs.com/package/mongoose-unique-validator#find--updates -- 2017.09.24 Yuki Takei
+        //   see https://www.npmjs.com/package/mongoose-unique-validator#find--updates -- 2017.09.24 Yuki Takei
         (err) => {
           if (err) {
             Object.keys(err.errors).forEach((e) => {
@@ -112,7 +112,8 @@ module.exports = function(crowi, app) {
           return res.redirect('/me');
         });
 
-    } else { // method GET
+    }
+    else { // method GET
       /*
        * disabled because the system no longer allows undefined email -- 2017.10.06 Yuki Takei
        *
@@ -127,7 +128,7 @@ module.exports = function(crowi, app) {
     }
   };
 
-  actions.imagetype = function(req,res) {
+  actions.imagetype = function(req, res) {
     if (req.method !== 'POST') {
       // do nothing
       return;
@@ -155,7 +156,7 @@ module.exports = function(crowi, app) {
       req.flash('successMessage', req.t('Updated'));
       return res.redirect('/me');
     });
-  }
+  };
 
   actions.externalAccounts = {};
   actions.externalAccounts.list = function(req, res) {
@@ -176,7 +177,7 @@ module.exports = function(crowi, app) {
           return res.render('me/external-accounts', renderVars);
         }
       });
-  }
+  };
 
   actions.externalAccounts.disassociate = function(req, res) {
     const userData = req.user;
@@ -184,7 +185,7 @@ module.exports = function(crowi, app) {
     const redirectWithFlash = (type, msg) => {
       req.flash(type, msg);
       return res.redirect('/me/external-accounts');
-    }
+    };
 
     if (req.body == null) {
       redirectWithFlash('errorMessage', 'Invalid form.');
@@ -198,7 +199,7 @@ module.exports = function(crowi, app) {
       else {
         ExternalAccount.count({user: userData})
           .then((count) => {
-            resolve(count > 1)
+            resolve(count > 1);
           });
       }
     })
@@ -233,7 +234,7 @@ module.exports = function(crowi, app) {
       }
     });
 
-  }
+  };
 
   actions.externalAccounts.associateLdap = function(req, res) {
     const passport = require('passport');
@@ -242,7 +243,7 @@ module.exports = function(crowi, app) {
     const redirectWithFlash = (type, msg) => {
       req.flash(type, msg);
       return res.redirect('/me/external-accounts');
-    }
+    };
 
     if (!passportService.isLdapStrategySetup) {
       debug('LdapStrategy has not been set up');
@@ -280,7 +281,7 @@ module.exports = function(crowi, app) {
     })(req, res, () => {});
 
 
-  }
+  };
 
   actions.password = function(req, res) {
     var passwordForm = req.body.mePassword;
@@ -310,7 +311,8 @@ module.exports = function(crowi, app) {
       // check password confirm
       if (newPassword != newPasswordConfirm) {
         req.form.errors.push('Failed to verify passwords');
-      } else {
+      }
+      else {
         userData.updatePassword(newPassword, function(err, userData) {
           if (err) {
             for (var e in err.errors) {
@@ -325,7 +327,8 @@ module.exports = function(crowi, app) {
           return res.redirect('/me/password');
         });
       }
-    } else { // method GET
+    }
+    else { // method GET
       return res.render('me/password', {
       });
     }
@@ -338,16 +341,17 @@ module.exports = function(crowi, app) {
     if (req.method == 'POST' && req.form.isValid) {
       userData.updateApiToken()
       .then(function(userData) {
-          req.flash('successMessage', 'API Token updated');
-          return res.redirect('/me/apiToken');
+        req.flash('successMessage', 'API Token updated');
+        return res.redirect('/me/apiToken');
       })
       .catch(function(err) {
-          //req.flash('successMessage',);
-          req.form.errors.push('Failed to update API Token');
-          return res.render('me/api_token', {
-          });
+        //req.flash('successMessage',);
+        req.form.errors.push('Failed to update API Token');
+        return res.render('me/api_token', {
+        });
       });
-    } else {
+    }
+    else {
       return res.render('me/api_token', {
       });
     }
@@ -379,7 +383,8 @@ module.exports = function(crowi, app) {
 
         return res.redirect('/me');
       });
-    } else if (toConnect) {
+    }
+    else if (toConnect) {
       googleAuth.createAuthUrl(req, function(err, redirectUrl) {
         if (err) {
           // TODO
@@ -388,7 +393,8 @@ module.exports = function(crowi, app) {
         req.session.googleCallbackAction = '/me/auth/google/callback';
         return res.redirect(redirectUrl);
       });
-    } else {
+    }
+    else {
       return res.redirect('/me');
     }
   };
@@ -414,7 +420,8 @@ module.exports = function(crowi, app) {
         if (!err && googleUser) {
           req.flash('warningMessage.auth.google', 'This Google\'s account is connected by another user');
           return res.redirect('/me');
-        } else {
+        }
+        else {
           userData.updateGoogleId(googleId, function(err, userData) {
             if (err) {
               debug('Failed to updateGoogleId', err);

+ 51 - 44
lib/routes/page.js

@@ -55,7 +55,8 @@ module.exports = function(crowi, app) {
 
     if (length < limit) {
       next = null;
-    } else {
+    }
+    else {
       next = offset + limit;
     }
 
@@ -78,7 +79,7 @@ module.exports = function(crowi, app) {
     else {
       return actions.pageListShowForCrowiPlus(req, res);
     }
-  }
+  };
   /**
    * switch action by behaviorType
    */
@@ -91,7 +92,7 @@ module.exports = function(crowi, app) {
     else {
       return actions.pageShowForCrowiPlus(req, res);
     }
-  }
+  };
   /**
    * switch action by behaviorType
    */
@@ -106,7 +107,7 @@ module.exports = function(crowi, app) {
       // redirect to '/trash'
       return res.redirect('/trash');
     }
-  }
+  };
   /**
    * switch action by behaviorType
    */
@@ -122,7 +123,7 @@ module.exports = function(crowi, app) {
       return actions.deletedPageListShow(req, res);
     }
 
-  }
+  };
   /**
    * switch action by behaviorType
    */
@@ -137,7 +138,7 @@ module.exports = function(crowi, app) {
       const path = '/trash' + getPathFromRequest(req);
       return res.redirect(path);
     }
-  }
+  };
 
 
   actions.pageListShow = function(req, res) {
@@ -152,11 +153,11 @@ module.exports = function(crowi, app) {
     // index page
     var pagerOptions = {
       offset: offset,
-      limit : limit
+      limit: limit
     };
     var queryOptions = {
       offset: offset,
-      limit : limit + 1,
+      limit: limit + 1,
       isPopulateRevisionBody: Config.isEnabledTimeline(config),
     };
 
@@ -176,7 +177,8 @@ module.exports = function(crowi, app) {
       if (portalPage) {
         renderVars.revision = portalPage.revision;
         return Revision.findRevisionList(portalPage.path, {});
-      } else {
+      }
+      else {
         return Promise.resolve([]);
       }
     }).then(function(tree) {
@@ -208,7 +210,7 @@ module.exports = function(crowi, app) {
     path = path.replace((/\/$/), '');
     // redirect
     return res.redirect(path);
-  }
+  };
 
   actions.pageShowForCrowiPlus = function(req, res) {
     var path = getPathFromRequest(req);
@@ -220,11 +222,11 @@ module.exports = function(crowi, app) {
     // index page
     var pagerOptions = {
       offset: offset,
-      limit : limit
+      limit: limit
     };
     var queryOptions = {
       offset: offset,
-      limit : limit + 1,
+      limit: limit + 1,
       isPopulateRevisionBody: Config.isEnabledTimeline(config),
       includeDeletedPage: path.startsWith('/trash/'),
     };
@@ -298,7 +300,8 @@ module.exports = function(crowi, app) {
             return Promise.resolve();
           }
         });
-      } else {
+      }
+      else {
         return Promise.resolve();
       }
     })
@@ -341,7 +344,7 @@ module.exports = function(crowi, app) {
     })
     .then(function() {
       return UserGroupRelation.findAllRelationForUser(req.user);
-    }).then(function (groupRelations) {
+    }).then(function(groupRelations) {
       if (groupRelations != null) {
         renderVars.userRelatedGroups = groupRelations.map(relation => relation.relatedGroup);
       }
@@ -354,7 +357,7 @@ module.exports = function(crowi, app) {
 
       return Promise.resolve();
     });
-  }
+  };
 
   actions.deletedPageListShow = function(req, res) {
     var path = '/trash' + getPathFromRequest(req);
@@ -364,11 +367,11 @@ module.exports = function(crowi, app) {
     // index page
     var pagerOptions = {
       offset: offset,
-      limit : limit
+      limit: limit
     };
     var queryOptions = {
       offset: offset,
-      limit : limit + 1,
+      limit: limit + 1,
       includeDeletedPage: true,
     };
 
@@ -483,7 +486,8 @@ module.exports = function(crowi, app) {
           debug('Error on finding user related entities', err);
           // pass
         });
-      } else {
+      }
+      else {
         return Promise.resolve();
       }
     }).then(function() {
@@ -548,11 +552,12 @@ module.exports = function(crowi, app) {
       .then(function(page) {
         if (page) {
           return res.redirect(pagePathUtil.encodePagePath(path) + '/');
-        } else {
+        }
+        else {
 
-          var fixed = Page.fixToCreatableName(path)
+          var fixed = Page.fixToCreatableName(path);
           if (fixed !== path) {
-            debug('fixed page name', fixed)
+            debug('fixed page name', fixed);
             res.redirect(pagePathUtil.encodePagePath(fixed));
             return ;
           }
@@ -579,7 +584,7 @@ module.exports = function(crowi, app) {
     var currentRevision = pageForm.currentRevision;
     var grant = pageForm.grant;
     var path = pageForm.path;
-    var grantUserGroupId = pageForm.grantUserGroupId
+    var grantUserGroupId = pageForm.grantUserGroupId;
 
     // TODO: make it pluggable
     var notify = pageForm.notify || {};
@@ -619,7 +624,8 @@ module.exports = function(crowi, app) {
       if (data) {
         previousRevision = data.revision;
         return Page.updatePage(data, body, req.user, { grant: grant, grantUserGroupId: grantUserGroupId});
-      } else {
+      }
+      else {
         // new page
         updateOrCreate = 'create';
         return Page.create(path, body, req.user, { grant: grant, grantUserGroupId: grantUserGroupId});
@@ -633,7 +639,7 @@ module.exports = function(crowi, app) {
       // TODO: move to events
       if (notify.slack) {
         if (notify.slack.on && notify.slack.channel) {
-          data.updateSlackChannel(notify.slack.channel).then(function(){}).catch(function(){});
+          data.updateSlackChannel(notify.slack.channel).then(function() {}).catch(function() {});
 
           if (crowi.slack) {
             notify.slack.channel.split(',').map(function(chan) {
@@ -664,7 +670,7 @@ module.exports = function(crowi, app) {
   /**
    * redirector
    */
-  api.redirector = function(req, res){
+  api.redirector = function(req, res) {
     var id = req.params.id;
 
     Page.findPageById(id)
@@ -697,8 +703,8 @@ module.exports = function(crowi, app) {
     var limit = 50;
     var offset = parseInt(req.query.offset) || 0;
 
-    var pagerOptions = { offset: offset, limit : limit };
-    var queryOptions = { offset: offset, limit : limit + 1};
+    var pagerOptions = { offset: offset, limit: limit };
+    var queryOptions = { offset: offset, limit: limit + 1};
 
     // Accepts only one of these
     if (username === null && path === null) {
@@ -717,7 +723,8 @@ module.exports = function(crowi, app) {
         }
         return Page.findListByCreator(user, queryOptions, req.user);
       });
-    } else {
+    }
+    else {
       pageFetcher = Page.findListByStartWith(path, req.user, queryOptions);
     }
 
@@ -745,7 +752,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} path
    * @apiParam {String} grant
    */
-  api.create = function(req, res){
+  api.create = function(req, res) {
     var body = req.body.body || null;
     var pagePath = req.body.path || null;
     var grant = req.body.grant || null;
@@ -774,7 +781,7 @@ module.exports = function(crowi, app) {
       return res.json(ApiResponse.success(result));
     }).catch(function(err) {
       return res.json(ApiResponse.error(err));
-    });;
+    });
 
   };
 
@@ -792,7 +799,7 @@ module.exports = function(crowi, app) {
    * - If revision_id is specified => update the page,
    * - If revision_id is not specified => force update by the new contents.
    */
-  api.update = function(req, res){
+  api.update = function(req, res) {
     var pageBody = req.body.body || null;
     var pageId = req.body.page_id || null;
     var revisionId = req.body.revision_id || null;
@@ -807,7 +814,7 @@ module.exports = function(crowi, app) {
     .then(function(pageData) {
       if (pageData && revisionId !== null && !pageData.isUpdatable(revisionId)) {
         throw new Error('Revision error.');
-      };
+      }
 
       var grantOption = {grant: pageData.grant};
       if (grant !== null) {
@@ -838,7 +845,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} path
    * @apiParam {String} revision_id
    */
-  api.get = function(req, res){
+  api.get = function(req, res) {
     const pagePath = req.query.path || null;
     const pageId = req.query.page_id || null; // TODO: handling
     const revisionId = req.query.revision_id || null;
@@ -850,7 +857,8 @@ module.exports = function(crowi, app) {
     let pageFinder;
     if (pageId) { // prioritized
       pageFinder = Page.findPageByIdAndGrantedUser(pageId, req.user);
-    } else if (pagePath) {
+    }
+    else if (pagePath) {
       pageFinder = Page.findPage(pagePath, req.user, revisionId);
     }
 
@@ -871,7 +879,7 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} page_id Page Id.
    */
-  api.seen = function(req, res){
+  api.seen = function(req, res) {
     var pageId = req.body.page_id;
     if (!pageId) {
       return res.json(ApiResponse.error('page_id required'));
@@ -898,7 +906,7 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} page_id Page Id.
    */
-  api.like = function(req, res){
+  api.like = function(req, res) {
     var id = req.body.page_id;
 
     Page.findPageByIdAndGrantedUser(id, req.user)
@@ -920,7 +928,7 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} page_id Page Id.
    */
-  api.unlike = function(req, res){
+  api.unlike = function(req, res) {
     var id = req.body.page_id;
 
     Page.findPageByIdAndGrantedUser(id, req.user)
@@ -972,7 +980,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} page_id Page Id.
    * @apiParam {String} revision_id
    */
-  api.remove = function(req, res){
+  api.remove = function(req, res) {
     var pageId = req.body.page_id;
     var previousRevision = req.body.revision_id || null;
 
@@ -1025,7 +1033,7 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} page_id Page Id.
    */
-  api.revertRemove = function(req, res){
+  api.revertRemove = function(req, res) {
     var pageId = req.body.page_id;
 
     // get recursively flag
@@ -1063,7 +1071,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} new_path
    * @apiParam {Bool} create_redirect
    */
-  api.rename = function(req, res){
+  api.rename = function(req, res) {
     var pageId = req.body.page_id;
     var previousRevision = req.body.revision_id || null;
     var newPagePath = Page.normalizePath(req.body.new_path);
@@ -1117,13 +1125,12 @@ module.exports = function(crowi, app) {
    * @apiParam {String} page_id Page Id.
    * @apiParam {String} new_path
    */
-  api.duplicate = function (req, res) {
+  api.duplicate = function(req, res) {
     var pageId = req.body.page_id;
     var newPagePath = Page.normalizePath(req.body.new_path);
-    var page = {};
 
     Page.findPageById(pageId)
-      .then(function (pageData) {
+      .then(function(pageData) {
         req.body.path = newPagePath;
         req.body.body = pageData.revision.body;
         req.body.grant = pageData.grant;
@@ -1140,7 +1147,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} page_id Page Id.
    * @apiParam {String} revision_id
    */
-  api.unlink = function(req, res){
+  api.unlink = function(req, res) {
     var pageId = req.body.page_id;
 
     Page.findPageByIdAndGrantedUser(pageId, req.user)

+ 7 - 4
lib/routes/revision.js

@@ -24,7 +24,7 @@ module.exports = function(crowi, app) {
       .then(function(revisionData) {
         var result = {
           revision: revisionData,
-        }
+        };
         return res.json(ApiResponse.success(result));
       })
       .catch(function(err) {
@@ -53,7 +53,8 @@ module.exports = function(crowi, app) {
       }).catch(function(err) {
         return res.json(ApiResponse.error(err));
       });
-    } else {
+    }
+    else {
       return res.json(ApiResponse.error('Parameter error.'));
     }
   };
@@ -80,14 +81,16 @@ module.exports = function(crowi, app) {
       }).catch(function(err) {
         return res.json(ApiResponse.error(err));
       });
-    } else if (revisionIds.length > 0) {
+    }
+    else if (revisionIds.length > 0) {
       Revision.findRevisions(revisionIds)
       .then(function(revisions) {
         return res.json(ApiResponse.success(revisions));
       }).catch(function(err) {
         return res.json(ApiResponse.error(err));
       });
-    } else {
+    }
+    else {
       return res.json(ApiResponse.error('Parameter error.'));
     }
   };

+ 3 - 2
lib/routes/search.js

@@ -29,7 +29,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} q keyword
    * @apiParam {String} path
    */
-  api.search = function(req, res){
+  api.search = function(req, res) {
     var keyword = req.query.q || null;
     var tree = req.query.tree || null;
     if (keyword === null || keyword === '') {
@@ -45,7 +45,8 @@ module.exports = function(crowi, app) {
     var doSearch;
     if (tree) {
       doSearch = search.searchKeywordUnderPath(keyword, tree, {});
-    } else {
+    }
+    else {
       doSearch = search.searchKeyword(keyword, {});
     }
     var result = {};

+ 8 - 6
lib/routes/user.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-   var Page = crowi.model('Page')
+  var Page = crowi.model('Page')
     , User = crowi.model('User')
     , Revision = crowi.model('Revision')
     , Bookmark = crowi.model('Bookmark')
@@ -16,7 +16,7 @@ module.exports = function(crowi, app) {
       skip: req.query.offset || 0,
       limit: req.query.limit || 50,
     };
-    Bookmark.findByUser(req.user, options, function (err, bookmarks) {
+    Bookmark.findByUser(req.user, options, function(err, bookmarks) {
       res.json(bookmarks);
     });
   };
@@ -28,7 +28,8 @@ module.exports = function(crowi, app) {
     .then(function(userData) {
       if (userData) {
         return res.json({ valid: false });
-      } else {
+      }
+      else {
         return res.json({ valid: true });
       }
     }).catch(function(err) {
@@ -48,9 +49,10 @@ module.exports = function(crowi, app) {
 
     var userFetcher;
     if (!userIds || userIds.split(',').length <= 0) {
-      userFetcher = User.findAllUsers()
-    } else {
-      userFetcher = User.findUsersByIds(userIds.split(','))
+      userFetcher = User.findAllUsers();
+    }
+    else {
+      userFetcher = User.findUsersByIds(userIds.split(','));
     }
 
     userFetcher

+ 4 - 8
lib/service/notification.js

@@ -1,13 +1,11 @@
 'use strict';
 
-function Notification (crowi)
-{
+function Notification(crowi) {
   this.crowi = crowi;
   this.config = crowi.getConfig();
 }
 
-Notification.prototype.hasSlackConfig = function()
-{
+Notification.prototype.hasSlackConfig = function() {
   if (!this.config.notification['slack']) {
     return false;
   }
@@ -15,12 +13,10 @@ Notification.prototype.hasSlackConfig = function()
   //var config = ;
 };
 
-Notification.prototype.noitfyByEmail = function()
-{
+Notification.prototype.noitfyByEmail = function() {
 };
 
-Notification.prototype.noitfyByChat = function()
-{
+Notification.prototype.noitfyByChat = function() {
 };
 
 module.exports = Notification;

+ 4 - 4
lib/service/passport.js

@@ -65,7 +65,7 @@ class PassportService {
       (username, password, done) => {
         // find user
         User.findUserByUsernameOrEmail(username, password, (err, user) => {
-          if (err) { return done(err); }
+          if (err) { return done(err) }
           // check existence and password
           if (!user || !user.isPasswordValid(password)) {
             return done(null, false, { message: 'Incorrect credentials.' });
@@ -114,7 +114,7 @@ class PassportService {
 
     passport.use(new LdapStrategy(this.getLdapConfigurationFunc(config, {passReqToCallback: true}),
       (req, ldapAccountInfo, done) => {
-        debug("LDAP authentication has succeeded", ldapAccountInfo);
+        debug('LDAP authentication has succeeded', ldapAccountInfo);
         done(null, ldapAccountInfo);
       }
     ));
@@ -196,8 +196,8 @@ class PassportService {
 
       // user bind
       const fixedBindDN = (isUserBind) ?
-          bindDN.replace(/{{username}}/, loginForm.username):
-          bindDN;
+        bindDN.replace(/{{username}}/, loginForm.username):
+        bindDN;
       const fixedBindCredentials = (isUserBind) ? loginForm.password : bindCredentials;
       let serverOpt = { url, bindDN: fixedBindDN, bindCredentials: fixedBindCredentials, searchBase, searchFilter };
 

+ 6 - 5
lib/util/apiResponse.js

@@ -1,9 +1,9 @@
 'use strict';
 
-function ApiResponse () {
-};
+function ApiResponse() {
+}
 
-ApiResponse.error = function (err) {
+ApiResponse.error = function(err) {
   var result = {};
 
   result = {
@@ -12,14 +12,15 @@ ApiResponse.error = function (err) {
 
   if (err instanceof Error) {
     result.error = err.toString();
-  } else {
+  }
+  else {
     result.error = err;
   }
 
   return result;
 };
 
-ApiResponse.success = function (data) {
+ApiResponse.success = function(data) {
   var result = data || {};
 
   result.ok = true;

+ 1 - 1
lib/util/formUtil.js

@@ -5,7 +5,7 @@ module.exports = {
     return value
       .replace(/\r\n/g, '\n')
       .replace(/\r/g, '\n')
-      ;
+    ;
   },
   stringToArrayFilter: function(value) {
     if (!value || value === '') {

+ 11 - 10
lib/util/mailer.js

@@ -16,8 +16,7 @@ module.exports = function(crowi) {
     ;
 
 
-  function createSMTPClient(option)
-  {
+  function createSMTPClient(option) {
     var client;
 
     debug('createSMTPClient option', option);
@@ -45,8 +44,7 @@ module.exports = function(crowi) {
     return client;
   }
 
-  function createSESClient(option)
-  {
+  function createSESClient(option) {
     var client;
 
     if (!option) {
@@ -70,14 +68,16 @@ module.exports = function(crowi) {
     }
 
     if (config.crowi['mail:smtpHost'] && config.crowi['mail:smtpPort']
-      ) {
+    ) {
       // SMTP 設定がある場合はそれを優先
       mailer = createSMTPClient();
 
-    } else if (config.crowi['aws:accessKeyId'] && config.crowi['aws:secretAccessKey']) {
+    }
+    else if (config.crowi['aws:accessKeyId'] && config.crowi['aws:secretAccessKey']) {
       // AWS 設定がある場合はSESを設定
       mailer = createSESClient();
-    } else {
+    }
+    else {
       mailer = undefined;
     }
 
@@ -87,7 +87,7 @@ module.exports = function(crowi) {
     debug('mailer initialized');
   }
 
-  function setupMailConfig (overrideConfig) {
+  function setupMailConfig(overrideConfig) {
     var c = overrideConfig
       , mc = {}
       ;
@@ -107,7 +107,7 @@ module.exports = function(crowi) {
       return swig.renderFile(
         MAIL_TEMPLATE_DIR + config.template,
         templateVars,
-        function (err, output) {
+        function(err, output) {
           if (err) {
             throw err;
           }
@@ -116,7 +116,8 @@ module.exports = function(crowi) {
           return mailer.sendMail(setupMailConfig(config), callback);
         }
       );
-    } else {
+    }
+    else {
       debug('Mailer is not completed to set up. Please set up SMTP or AWS setting.');
       return callback(new Error('Mailer is not completed to set up. Please set up SMTP or AWS setting.'), null);
     }

+ 16 - 11
lib/util/middlewares.js

@@ -10,8 +10,8 @@ exports.csrfKeyGenerator = function(crowi, app) {
     }
 
     next();
-  }
-}
+  };
+};
 
 exports.loginChecker = function(crowi, app) {
   return function(req, res, next) {
@@ -22,13 +22,15 @@ exports.loginChecker = function(crowi, app) {
       User.findById(req.session.user._id, function(err, userData) {
         if (err) {
           next();
-        } else {
+        }
+        else {
           req.user = req.session.user = userData;
           res.locals.user = req.user;
           next();
         }
       });
-    } else {
+    }
+    else {
       req.user = req.session.user = false;
       res.locals.user = req.user;
       next();
@@ -204,7 +206,7 @@ exports.adminRequired = function() {
  */
 exports.loginRequired = function(crowi, app, isStrictly = true) {
   return function(req, res, next) {
-    var User = crowi.model('User')
+    var User = crowi.model('User');
 
     // when the route is not strictly restricted
     if (!isStrictly) {
@@ -223,11 +225,14 @@ exports.loginRequired = function(crowi, app, isStrictly = true) {
       if (req.user.status === User.STATUS_ACTIVE) {
         // Active の人だけ先に進める
         return next();
-      } else if (req.user.status === User.STATUS_REGISTERED) {
+      }
+      else if (req.user.status === User.STATUS_REGISTERED) {
         return res.redirect('/login/error/registered');
-      } else if (req.user.status === User.STATUS_SUSPENDED) {
+      }
+      else if (req.user.status === User.STATUS_SUSPENDED) {
         return res.redirect('/login/error/suspended');
-      } else if (req.user.status === User.STATUS_INVITED) {
+      }
+      else if (req.user.status === User.STATUS_INVITED) {
         return res.redirect('/login/invited');
       }
     }
@@ -251,7 +256,7 @@ exports.accessTokenParser = function(crowi, app) {
       return next();
     }
 
-    var User = crowi.model('User')
+    var User = crowi.model('User');
 
     debug('accessToken is', accessToken);
     User.findUserByApiToken(accessToken)
@@ -306,7 +311,7 @@ exports.checkSearchIndicesGenerated = function(crowi, app) {
 
     return next();
   };
-}
+};
 
 exports.applicationInstalled = function() {
   return function(req, res, next) {
@@ -321,7 +326,7 @@ exports.applicationInstalled = function() {
 };
 
 exports.awsEnabled = function() {
-  return function (req, res, next) {
+  return function(req, res, next) {
     var config = req.config;
     if (config.crowi['aws:region'] !== '' && config.crowi['aws:bucket'] !== '' && config.crowi['aws:accessKeyId'] !== '' && config.crowi['aws:secretAccessKey'] !== '') {
       req.flash('globalError', 'AWS settings required to use this function. Please ask the administrator.');

+ 47 - 60
lib/util/search.js

@@ -33,9 +33,9 @@ SearchClient.prototype.checkESVersion = function() {
 
 SearchClient.prototype.registerUpdateEvent = function() {
   var pageEvent = this.crowi.event('page');
-  pageEvent.on('create', this.syncPageCreated.bind(this))
-  pageEvent.on('update', this.syncPageUpdated.bind(this))
-  pageEvent.on('delete', this.syncPageDeleted.bind(this))
+  pageEvent.on('create', this.syncPageCreated.bind(this));
+  pageEvent.on('update', this.syncPageUpdated.bind(this));
+  pageEvent.on('delete', this.syncPageDeleted.bind(this));
 };
 
 SearchClient.prototype.shouldIndexed = function(page) {
@@ -159,8 +159,7 @@ SearchClient.prototype.prepareBodyForDelete = function(body, page) {
 };
 
 
-SearchClient.prototype.addPages = function(pages)
-{
+SearchClient.prototype.addPages = function(pages) {
   var self = this;
   var body = [];
 
@@ -174,8 +173,7 @@ SearchClient.prototype.addPages = function(pages)
   });
 };
 
-SearchClient.prototype.updatePages = function(pages)
-{
+SearchClient.prototype.updatePages = function(pages) {
   var self = this;
   var body = [];
 
@@ -189,8 +187,7 @@ SearchClient.prototype.updatePages = function(pages)
   });
 };
 
-SearchClient.prototype.deletePages = function(pages)
-{
+SearchClient.prototype.deletePages = function(pages) {
   var self = this;
   var body = [];
 
@@ -204,8 +201,7 @@ SearchClient.prototype.deletePages = function(pages)
   });
 };
 
-SearchClient.prototype.addAllPages = function()
-{
+SearchClient.prototype.addAllPages = function() {
   var self = this;
   var Page = this.crowi.model('Page');
   var cursor = Page.getStreamOfFindAll();
@@ -214,7 +210,7 @@ SearchClient.prototype.addAllPages = function()
   var skipped = 0;
 
   return new Promise(function(resolve, reject) {
-    cursor.on('data', function (doc) {
+    cursor.on('data', function(doc) {
       if (!doc.creator || !doc.revision || !self.shouldIndexed(doc)) {
         //debug('Skipped', doc.path);
         skipped++;
@@ -230,17 +226,17 @@ SearchClient.prototype.addAllPages = function()
           body: body,
           requestTimeout: Infinity,
         }).then(res => {
-          debug('addAllPages add anyway (items, errors, took): ', (res.items || []).length, res.errors, res.took)
+          debug('addAllPages add anyway (items, errors, took): ', (res.items || []).length, res.errors, res.took);
         }).catch(err => {
-          debug('addAllPages error on add anyway: ', err)
+          debug('addAllPages error on add anyway: ', err);
         });
 
         body = [];
       }
-    }).on('error', function (err) {
+    }).on('error', function(err) {
       // TODO: handle err
       debug('Error cursor:', err);
-    }).on('close', function () {
+    }).on('close', function() {
       // all done
 
       // return if body is empty
@@ -272,8 +268,7 @@ SearchClient.prototype.addAllPages = function()
  *   data: [ pages ...],
  * }
  */
-SearchClient.prototype.search = function(query)
-{
+SearchClient.prototype.search = function(query) {
   var self = this;
 
   return new Promise(function(resolve, reject) {
@@ -297,8 +292,7 @@ SearchClient.prototype.search = function(query)
   });
 };
 
-SearchClient.prototype.createSearchQuerySortedByUpdatedAt = function(option)
-{
+SearchClient.prototype.createSearchQuerySortedByUpdatedAt = function(option) {
   // getting path by default is almost for debug
   var fields = ['path'];
   if (option) {
@@ -320,8 +314,7 @@ SearchClient.prototype.createSearchQuerySortedByUpdatedAt = function(option)
   return query;
 };
 
-SearchClient.prototype.createSearchQuerySortedByScore = function(option)
-{
+SearchClient.prototype.createSearchQuerySortedByScore = function(option) {
   var fields = ['path'];
   if (option) {
     fields = option.fields || fields;
@@ -342,14 +335,12 @@ SearchClient.prototype.createSearchQuerySortedByScore = function(option)
   return query;
 };
 
-SearchClient.prototype.appendResultSize = function(query, from, size)
-{
+SearchClient.prototype.appendResultSize = function(query, from, size) {
   query.from = from || this.DEFAULT_OFFSET;
   query.size = size || this.DEFAULT_LIMIT;
 };
 
-SearchClient.prototype.appendCriteriaForKeywordContains = function(query, keyword)
-{
+SearchClient.prototype.appendCriteriaForKeywordContains = function(query, keyword) {
   // query is created by createSearchQuerySortedByScore() or createSearchQuerySortedByUpdatedAt()
   if (!query.body.query.bool) {
     query.body.query.bool = {};
@@ -379,9 +370,9 @@ SearchClient.prototype.appendCriteriaForKeywordContains = function(query, keywor
         query: keywords.join(' '),
         // TODO: By user's i18n setting, change boost or search target fields
         fields: [
-          "path_ja^2",
-          "path_en^2",
-          "body_ja",
+          'path_ja^2',
+          'path_en^2',
+          'body_ja',
           // "path_en",
           // "body_en",
         ],
@@ -410,8 +401,8 @@ SearchClient.prototype.appendCriteriaForKeywordContains = function(query, keywor
           query: phrase, // each phrase is quoteted words
           type: 'phrase',
           fields: [ // Not use "*.ja" fields here, because we want to analyze (parse) search words
-            "path_raw^2",
-            "body_raw",
+            'path_raw^2',
+            'body_raw',
           ],
         }
       });
@@ -428,8 +419,8 @@ SearchClient.prototype.appendCriteriaForKeywordContains = function(query, keywor
           query: phrase, // each phrase is quoteted words
           type: 'phrase',
           fields: [ // Not use "*.ja" fields here, because we want to analyze (parse) search words
-            "path_raw^2",
-            "body_raw",
+            'path_raw^2',
+            'body_raw',
           ],
         }
       });
@@ -439,8 +430,7 @@ SearchClient.prototype.appendCriteriaForKeywordContains = function(query, keywor
   }
 };
 
-SearchClient.prototype.appendCriteriaForPathFilter = function(query, path)
-{
+SearchClient.prototype.appendCriteriaForPathFilter = function(query, path) {
   // query is created by createSearchQuerySortedByScore() or createSearchQuerySortedByUpdatedAt()
   if (!query.body.query.bool) {
     query.body.query.bool = {};
@@ -455,27 +445,26 @@ SearchClient.prototype.appendCriteriaForPathFilter = function(query, path)
   }
   query.body.query.bool.filter.push({
     wildcard: {
-      "path": path + "/*"
+      'path': path + '/*'
     }
   });
 };
 
-SearchClient.prototype.searchKeyword = function(keyword, option)
-{
+SearchClient.prototype.searchKeyword = function(keyword, option) {
+  /* eslint-disable no-unused-vars */
   var from = option.offset || null;
+  /* eslint-enable */
   var query = this.createSearchQuerySortedByScore();
   this.appendCriteriaForKeywordContains(query, keyword);
 
   return this.search(query);
 };
 
-SearchClient.prototype.searchByPath = function(keyword, prefix)
-{
+SearchClient.prototype.searchByPath = function(keyword, prefix) {
   // TODO path 名だけから検索
 };
 
-SearchClient.prototype.searchKeywordUnderPath = function(keyword, path, option)
-{
+SearchClient.prototype.searchKeywordUnderPath = function(keyword, path, option) {
   var from = option.offset || null;
   var query = this.createSearchQuerySortedByScore();
   this.appendCriteriaForKeywordContains(query, keyword);
@@ -488,8 +477,7 @@ SearchClient.prototype.searchKeywordUnderPath = function(keyword, path, option)
   return this.search(query);
 };
 
-SearchClient.prototype.getParsedKeywords = function(keyword)
-{
+SearchClient.prototype.getParsedKeywords = function(keyword) {
   var matchWords = [];
   var notMatchWords = [];
   var phraseWords = [];
@@ -507,9 +495,10 @@ SearchClient.prototype.getParsedKeywords = function(keyword)
 
     phrases.forEach(function(phrase) {
       phrase.trim();
-      if (phrase.match(/^\-/)) {
-        notPhraseWords.push(phrase.replace(/^\-/, ''));
-      } else {
+      if (phrase.match(/^-/)) {
+        notPhraseWords.push(phrase.replace(/^-/, ''));
+      }
+      else {
         phraseWords.push(phrase);
       }
     });
@@ -521,9 +510,10 @@ SearchClient.prototype.getParsedKeywords = function(keyword)
       return;
     }
 
-    if (word.match(/^\-(.+)$/)) {
+    if (word.match(/^-(.+)$/)) {
       notMatchWords.push((RegExp.$1));
-    } else {
+    }
+    else {
       matchWords.push(word);
     }
   });
@@ -534,10 +524,9 @@ SearchClient.prototype.getParsedKeywords = function(keyword)
     phrase: phraseWords,
     not_phrase: notPhraseWords,
   };
-}
+};
 
-SearchClient.prototype.syncPageCreated = function(page, user)
-{
+SearchClient.prototype.syncPageCreated = function(page, user) {
   debug('SearchClient.syncPageCreated', page.path);
 
   if (!this.shouldIndexed(page)) {
@@ -548,13 +537,12 @@ SearchClient.prototype.syncPageCreated = function(page, user)
   .then(function(res) {
     debug('ES Response', res);
   })
-  .catch(function(err){
+  .catch(function(err) {
     debug('ES Error', err);
   });
 };
 
-SearchClient.prototype.syncPageUpdated = function(page, user)
-{
+SearchClient.prototype.syncPageUpdated = function(page, user) {
   debug('SearchClient.syncPageUpdated', page.path);
   // TODO delete
   if (!this.shouldIndexed(page)) {
@@ -562,7 +550,7 @@ SearchClient.prototype.syncPageUpdated = function(page, user)
     .then(function(res) {
       debug('deletePages: ES Response', res);
     })
-    .catch(function(err){
+    .catch(function(err) {
       debug('deletePages:ES Error', err);
     });
 
@@ -573,20 +561,19 @@ SearchClient.prototype.syncPageUpdated = function(page, user)
   .then(function(res) {
     debug('ES Response', res);
   })
-  .catch(function(err){
+  .catch(function(err) {
     debug('ES Error', err);
   });
 };
 
-SearchClient.prototype.syncPageDeleted = function(page, user)
-{
+SearchClient.prototype.syncPageDeleted = function(page, user) {
   debug('SearchClient.syncPageDeleted', page.path);
 
   this.deletePages([page])
   .then(function(res) {
     debug('deletePages: ES Response', res);
   })
-  .catch(function(err){
+  .catch(function(err) {
     debug('deletePages:ES Error', err);
   });
 

+ 21 - 17
lib/util/slack.js

@@ -5,8 +5,6 @@
 module.exports = function(crowi) {
   'use strict';
 
-  const SLACK_URL = 'https://slack.com';
-
   const debug = require('debug')('growi:util:slack'),
     config = crowi.getConfig(),
     Config = crowi.model('Config'),
@@ -17,7 +15,7 @@ module.exports = function(crowi) {
     const client = new Slack();
     client.setWebhook(config.notification['slack:incomingWebhookUrl']);
     client.webhook(messageObj, callback);
-  }
+  };
 
   const postWithWebApi = function(messageObj, callback) {
     const client = new Slack(config.notification['slack:token']);
@@ -26,7 +24,7 @@ module.exports = function(crowi) {
       messageObj.attachments = JSON.stringify(messageObj.attachments);
     }
     client.api('chat.postMessage', messageObj, callback);
-  }
+  };
 
   const convertMarkdownToMrkdwn = function(body) {
     var url = '';
@@ -39,7 +37,7 @@ module.exports = function(crowi) {
       .replace(/#{1,}\s?(.+)/g, '\n*$1*')
       .replace(/(\[(.+)\]\((https?:\/\/.+)\))/g, '<$3|$2>')
       .replace(/(\[(.+)\]\((\/.+)\))/g, '<' + url + '$3|$2>')
-      ;
+    ;
 
     return body;
   };
@@ -55,25 +53,29 @@ module.exports = function(crowi) {
 
   const prepareAttachmentTextForUpdate = function(page, user, previousRevision) {
     var diff = require('diff');
-    var diffText = ''
+    var diffText = '';
 
     diff.diffLines(previousRevision.body, page.revision.body).forEach(function(line) {
-      debug('diff line', line)
+      debug('diff line', line);
+      /* eslint-disable no-unused-vars */
       var value = line.value.replace(/\r\n|\r/g, '\n');
+      /* eslint-enable */
       if (line.added) {
         diffText += `:pencil2: ...\n${line.value}`;
-      } else if (line.removed) {
+      }
+      else if (line.removed) {
         // diffText += '-' + line.value.replace(/(.+)?\n/g, '- $1\n');
         // 1以下は無視
         if (line.count > 1) {
           diffText += `:wastebasket: ... ${line.count} lines\n`;
         }
-      } else {
+      }
+      else {
         //diffText += '...\n';
       }
     });
 
-    debug('diff is', diffText)
+    debug('diff is', diffText);
 
     return diffText;
   };
@@ -84,7 +86,8 @@ module.exports = function(crowi) {
 
     if (updateType == 'create') {
       body = prepareAttachmentTextForCreate(page, user);
-    } else {
+    }
+    else {
       body = prepareAttachmentTextForUpdate(page, user, previousRevision);
     }
 
@@ -96,7 +99,7 @@ module.exports = function(crowi) {
       title: page.path,
       title_link: url + '/' + page._id,
       text: body,
-      mrkdwn_in: ["text"],
+      mrkdwn_in: ['text'],
     };
     if (user.image) {
       attachment.author_icon = user.image;
@@ -119,7 +122,8 @@ module.exports = function(crowi) {
     const pageUrl = `<${url}${path}|${path}>`;
     if (updateType == 'create') {
       text = `:white_check_mark: ${user.username} created a new page! ${pageUrl}`;
-    } else {
+    }
+    else {
       text = `:up: ${user.username} updated ${pageUrl}`;
     }
 
@@ -144,22 +148,22 @@ module.exports = function(crowi) {
       // when incoming Webhooks is prioritized
       if (Config.isIncomingWebhookPrioritized(config)) {
         if (Config.hasSlackIwhUrl(config)) {
-          debug(`posting message with IncomingWebhook`);
+          debug('posting message with IncomingWebhook');
           postWithIwh(messageObj, callback);
         }
         else if (Config.hasSlackToken(config)) {
-          debug(`posting message with Web API`);
+          debug('posting message with Web API');
           postWithWebApi(messageObj, callback);
         }
       }
       // else
       else {
         if (Config.hasSlackToken(config)) {
-          debug(`posting message with Web API`);
+          debug('posting message with Web API');
           postWithWebApi(messageObj, callback);
         }
         else if (Config.hasSlackIwhUrl(config)) {
-          debug(`posting message with IncomingWebhook`);
+          debug('posting message with IncomingWebhook');
           postWithIwh(messageObj, callback);
         }
       }

+ 48 - 44
lib/util/swigFunctions.js

@@ -1,24 +1,27 @@
 module.exports = function(crowi, app, req, locals) {
   var debug = require('debug')('growi:lib:swigFunctions')
+    , stringWidth = require('string-width')
     , Page = crowi.model('Page')
     , Config = crowi.model('Config')
     , User = crowi.model('User')
     , passportService = crowi.passportService
   ;
 
+  debug('initializing swigFunctions');
+
   locals.nodeVersion = function() {
     return crowi.runtimeVersions.versions.node ? crowi.runtimeVersions.versions.node.version : '-';
-  }
+  };
   locals.npmVersion = function() {
     return crowi.runtimeVersions.versions.npm ? crowi.runtimeVersions.versions.npm.version : '-';
-  }
+  };
   locals.yarnVersion = function() {
     return crowi.runtimeVersions.versions.yarn ? crowi.runtimeVersions.versions.yarn.version : '-';
-  }
+  };
 
   locals.growiVersion = function() {
     return crowi.version;
-  }
+  };
 
   // token getter
   locals.csrf = function() {
@@ -26,16 +29,17 @@ module.exports = function(crowi, app, req, locals) {
   };
 
   locals.getAppTitleFontSize = function(appTitle) {
+    let appTitleWidth = stringWidth(appTitle);
     let fontSize = 22;
-    if (appTitle.length < 13) { /* do nothing */ }
-    else if (appTitle.length < 21) {
-      fontSize -= 3 * (Math.floor((appTitle.length - 13) / 3) + 1);
+    if (appTitleWidth < 13) { /* do nothing */ }
+    else if (appTitleWidth < 21) {
+      fontSize -= 3 * (Math.floor((appTitleWidth - 13) / 3) + 1);
     }
     else  {
       fontSize = 11;
     }
     return fontSize;
-  }
+  };
 
   /**
    * return app title
@@ -43,7 +47,7 @@ module.exports = function(crowi, app, req, locals) {
   locals.appTitle = function() {
     var config = crowi.getConfig();
     return Config.appTitle(config);
-  }
+  };
 
   /**
    * return true if enabled
@@ -51,7 +55,7 @@ module.exports = function(crowi, app, req, locals) {
   locals.isEnabledPassport = function() {
     var config = crowi.getConfig();
     return Config.isEnabledPassport(config);
-  }
+  };
 
   /**
    * return true if local strategy has been setup successfully
@@ -59,23 +63,23 @@ module.exports = function(crowi, app, req, locals) {
    */
   locals.isPassportLocalStrategySetup = function() {
     return passportService != null && passportService.isLocalStrategySetup;
-  }
+  };
 
   /**
    * return true if enabled and strategy has been setup successfully
    */
   locals.isLdapSetup = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.isEnabledPassport(config) && Config.isEnabledPassportLdap(config) && passportService.isLdapStrategySetup;
-  }
+  };
 
   /**
    * return true if enabled but strategy has some problem
    */
   locals.isLdapSetupFailed = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.isEnabledPassport(config) && Config.isEnabledPassportLdap(config) && !passportService.isLdapStrategySetup;
-  }
+  };
 
   locals.googleLoginEnabled = function() {
     // return false if Passport is enabled
@@ -84,7 +88,7 @@ module.exports = function(crowi, app, req, locals) {
       return false;
     }
 
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return config.crowi['google:clientId'] && config.crowi['google:clientSecret'];
   };
 
@@ -96,70 +100,70 @@ module.exports = function(crowi, app, req, locals) {
   };
 
   locals.isEnabledPlugins = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.isEnabledPlugins(config);
-  }
+  };
 
   locals.isEnabledLinebreaks = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.isEnabledLinebreaks(config);
-  }
+  };
 
   locals.isEnabledLinebreaksInComments = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.isEnabledLinebreaksInComments(config);
-  }
+  };
 
   locals.customCss = function() {
     return Config.customCss();
-  }
+  };
 
   locals.customScript = function() {
     return Config.customScript();
-  }
+  };
 
   locals.customHeader = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.customHeader(config);
-  }
+  };
 
   locals.theme = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.theme(config);
-  }
+  };
 
   locals.customTitle = function(page) {
     const config = crowi.getConfig();
     return Config.customTitle(config, page);
-  }
+  };
 
   locals.behaviorType = function() {
     var config = crowi.getConfig();
     return Config.behaviorType(config);
-  }
+  };
 
   locals.layoutType = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.layoutType(config);
-  }
+  };
 
   locals.highlightJsStyle = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.highlightJsStyle(config);
-  }
+  };
 
   locals.highlightJsStyleBorder = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.highlightJsStyleBorder(config);
-  }
+  };
 
   locals.isEnabledTimeline = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.isEnabledTimeline(config);
-  }
+  };
 
   locals.slackConfigured = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     if (Config.hasSlackToken(config) || Config.hasSlackIwhUrl(config)) {
       return true;
     }
@@ -167,12 +171,12 @@ module.exports = function(crowi, app, req, locals) {
   };
 
   locals.isUploadable = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.isUploadable(config);
   };
 
   locals.isEnabledAttachTitleHeader = function() {
-    var config = crowi.getConfig()
+    var config = crowi.getConfig();
     return Config.isEnabledAttachTitleHeader(config);
   };
 
@@ -189,7 +193,7 @@ module.exports = function(crowi, app, req, locals) {
   };
 
   locals.isUserPageList = function(path) {
-    if (path.match(/^\/user\/[^\/]+\/$/)) {
+    if (path.match(/^\/user\/[^/]+\/$/)) {
       return true;
     }
 
@@ -229,7 +233,7 @@ module.exports = function(crowi, app, req, locals) {
   };
 
   locals.css = {
-    grant: function (pageData) {
+    grant: function(pageData) {
       if (!pageData) {
         return '';
       }
@@ -249,7 +253,7 @@ module.exports = function(crowi, app, req, locals) {
       }
       return '';
     },
-    userStatus: function (user) {
+    userStatus: function(user) {
       //debug('userStatus', user._id, user.usename, user.status);
 
       switch (user.status) {

+ 1 - 1
lib/util/xss.js

@@ -13,7 +13,7 @@ class Xss {
       // allow all attributes
       option.onTagAttr = function(tag, name, value, isWhiteAttr) {
         return `${name}="${value}"`;
-      }
+      };
     }
     // create the XSS Filter instance
     this.myxss = new xss.FilterXSS(option);

+ 12 - 5
lib/views/admin/customize.html

@@ -281,7 +281,7 @@
       </fieldset>
       </form>
 
-      <form action="/_api/admin/customize/highlightJsStyle" method="post" class="form-horizontal" id="cutomhighlightJsStyleSettingForm" role="form">
+      <form action="/_api/admin/customize/highlightJsStyle" method="post" class="form-horizontal" id="customhighlightJsStyleSettingForm" role="form">
         <fieldset>
           <legend>{{ t('customize_page.Code Highlight') }}</legend>
           <div class="form-group">
@@ -299,11 +299,11 @@
             <label for="settingForm[customize:highlightJsStyleBorder]" class="col-xs-3 control-label">(TBD) Border</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:highlightJsStyleBorder'] %}active{% endif %}" data-active-class="primary">
+                <label class="btn btn-default btn-rounded btn-outline {% if settingForm['customize:highlightJsStyleBorder'] %}active{% endif %}" data-active-class="primary" onclick="selectBorderOn()">
                   <input name="settingForm[customize:highlightJsStyleBorder]" value="true" type="radio"
                       {% if true === settingForm['customize:highlightJsStyleBorder'] %}checked{% endif %}> ON
                 </label>
-                <label class="btn btn-default btn-rounded btn-outline {% if !settingForm['customize:highlightJsStyleBorder'] %}active{% endif %}" data-active-class="default">
+                <label class="btn btn-default btn-rounded btn-outline {% if !settingForm['customize:highlightJsStyleBorder'] %}active{% endif %}" data-active-class="default" onclick="selectBorderOff()">
                   <input name="settingForm[customize:highlightJsStyleBorder]" value="false" type="radio"
                       {% if !settingForm['customize:highlightJsStyleBorder'] %}checked{% endif %}> OFF
                 </label>
@@ -315,7 +315,7 @@
 
           <p class="help-block">
             Examples:
-            <pre class="hljs"><code class="highlightjs-demo">function $initHighlight(block, cls) {
+            <pre class="hljs {% if !settingForm['customize:highlightJsStyleBorder'] %}hljs-no-border{% endif %}"><code class="highlightjs-demo">function $initHighlight(block, cls) {
   try {
     if (cls.search(/\bno\-highlight\b/) != -1)
       return process(block, true, 0x0F) +
@@ -507,7 +507,7 @@ window.addEventListener('load', (event) => {
 {% block body_end %}
   {% parent %}
   <script>
-    $(`#customthemeSettingForm, #cutomlayoutSettingForm, #cutombehaviorSettingForm, #cutomhighlightJsStyleSettingForm,
+    $(`#customthemeSettingForm, #cutomlayoutSettingForm, #cutombehaviorSettingForm, #customhighlightJsStyleSettingForm,
        #customfeaturesSettingForm, #cutomheaderSettingForm, #cutomcssSettingForm, #cutomscriptSettingForm, #customtitleSettingForm`
     ).each(function() {
       $(this).submit(function()
@@ -592,6 +592,13 @@ window.addEventListener('load', (event) => {
       $('#themeOptions .active').removeClass('active');
       $(`#themeOptions #theme-option-${theme}`).addClass('active');
     }
+
+    function selectBorderOn(){
+      $('.hljs-no-border').removeClass('hljs-no-border');
+    }
+    function selectBorderOff(){
+      $('#customhighlightJsStyleSettingForm .hljs').addClass('hljs-no-border')
+    }
   </script>
 
 </div>

+ 12 - 11
local_modules/crowi-fileupload-aws/index.js

@@ -9,14 +9,14 @@ module.exports = function(crowi) {
     , debug = require('debug')('growi:lib:fileUploaderAws')
     , lib = {}
     , getAwsConfig = function() {
-        var config = crowi.getConfig();
-        return {
-          accessKeyId: config.crowi['aws:accessKeyId'],
-          secretAccessKey: config.crowi['aws:secretAccessKey'],
-          region: config.crowi['aws:region'],
-          bucket: config.crowi['aws:bucket']
-        };
+      var config = crowi.getConfig();
+      return {
+        accessKeyId: config.crowi['aws:accessKeyId'],
+        secretAccessKey: config.crowi['aws:secretAccessKey'],
+        region: config.crowi['aws:region'],
+        bucket: config.crowi['aws:bucket']
       };
+    };
 
   function S3Factory() {
     const awsConfig = getAwsConfig();
@@ -88,7 +88,7 @@ module.exports = function(crowi) {
     return url;
   };
 
-  lib.findDeliveryFile = function (fileId, filePath) {
+  lib.findDeliveryFile = function(fileId, filePath) {
     var cacheFile = lib.createCacheFileName(fileId);
 
     return new Promise((resolve, reject) => {
@@ -102,7 +102,7 @@ module.exports = function(crowi) {
       var fileStream = fs.createWriteStream(cacheFile);
       var fileUrl = lib.generateUrl(filePath);
       debug('Load attachement file into local cache file', fileUrl, cacheFile);
-      var request = loader.get(fileUrl, function(response) {
+      loader.get(fileUrl, function(response) {
         response.pipe(fileStream, { end: false });
         response.on('end', () => {
           fileStream.end();
@@ -130,7 +130,7 @@ module.exports = function(crowi) {
       debug('Failed to delete cache file (file may not exists).', err);
       // through
     });
-  }
+  };
 
   // private
   lib.createCacheFileName = function(fileId) {
@@ -151,7 +151,8 @@ module.exports = function(crowi) {
         debug('Cache file found but the size is 0');
         return true;
       }
-    } catch (e) {
+    }
+    catch (e) {
       // no such file or directory
       debug('Stats error', e);
       return true;

+ 1 - 1
local_modules/crowi-fileupload-local/index.js

@@ -54,7 +54,7 @@ module.exports = function(crowi) {
     return path.posix.join('/uploads', filePath);
   };
 
-  lib.findDeliveryFile = function (fileId, filePath) {
+  lib.findDeliveryFile = function(fileId, filePath) {
     return Promise.resolve(lib.generateUrl(filePath));
   };
 

+ 4 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "3.0.13-RC",
+  "version": "3.0.14-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -31,6 +31,8 @@
     "clean:report": "rimraf -- report",
     "clean": "npm-run-all -p clean:*",
     "heroku-postbuild": "sh bin/heroku/install-plugins.sh && npm run build:prod",
+    "lint:fix": "eslint . --fix",
+    "lint": "eslint .",
     "mkdirp": "mkdirp",
     "plugin:def": "node bin/generate-plugin-definitions-source.js",
     "prebuild:dev": "env-cmd config/env.dev.js npm run plugin:def",
@@ -145,6 +147,7 @@
     "slack-node": "^0.1.8",
     "socket.io": "^2.0.3",
     "socket.io-client": "^2.0.3",
+    "string-width": "^2.1.1",
     "style-loader": "^0.21.0",
     "swig-templates": "^2.0.2",
     "throttle-debounce": "^1.0.1",

+ 4 - 6
resource/js/app.js

@@ -22,7 +22,6 @@ import RevisionPath     from './components/Page/RevisionPath';
 import RevisionUrl      from './components/Page/RevisionUrl';
 import BookmarkButton   from './components/BookmarkButton';
 import NewPageNameInputter from './components/NewPageNameInputter';
-import SearchTypeahead  from './components/SearchTypeahead';
 
 import CustomCssEditor  from './components/Admin/CustomCssEditor';
 import CustomScriptEditor from './components/Admin/CustomScriptEditor';
@@ -30,7 +29,6 @@ import CustomHeaderEditor from './components/Admin/CustomHeaderEditor';
 
 import * as entities from 'entities';
 
-
 if (!window) {
   window = {};
 }
@@ -152,7 +150,7 @@ if (pageEditorElem) {
     if (componentInstances.page != null) {
       componentInstances.page.setMarkdown(page.revision.body);
     }
-  }
+  };
 
   pageEditor = ReactDOM.render(
     <PageEditor crowi={crowi} crowiRenderer={crowiRenderer}
@@ -220,7 +218,7 @@ if (customCssEditorElem != null) {
   ReactDOM.render(
     <CustomCssEditor inputElem={customCssInputElem} />,
     customCssEditorElem
-  )
+  );
 }
 const customScriptEditorElem = document.getElementById('custom-script-editor');
 if (customScriptEditorElem != null) {
@@ -230,7 +228,7 @@ if (customScriptEditorElem != null) {
   ReactDOM.render(
     <CustomScriptEditor inputElem={customScriptInputElem} />,
     customScriptEditorElem
-  )
+  );
 }
 const customHeaderEditorElem = document.getElementById('custom-header-editor');
 if (customHeaderEditorElem != null) {
@@ -240,7 +238,7 @@ if (customHeaderEditorElem != null) {
   ReactDOM.render(
     <CustomHeaderEditor inputElem={customHeaderInputElem} />,
     customHeaderEditorElem
-  )
+  );
 }
 
 // うわーもうー (commented by Crowi team -- 2018.03.23 Yuki Takei)

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

@@ -35,7 +35,7 @@ export default class CustomCssEditor extends React.Component {
           autoRefresh: true,
           matchBrackets: true,
           autoCloseBrackets: true,
-          extraKeys: {"Ctrl-Space": "autocomplete"},
+          extraKeys: {'Ctrl-Space': 'autocomplete'},
         }}
         editorDidMount={(editor, next) => {
           // resizable with jquery.ui
@@ -49,7 +49,7 @@ export default class CustomCssEditor extends React.Component {
           this.props.inputElem.value = value;
         }}
       />
-    )
+    );
   }
 
 }

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

@@ -33,7 +33,7 @@ export default class CustomHeaderEditor extends React.Component {
           autoRefresh: true,
           matchBrackets: true,
           autoCloseBrackets: true,
-          extraKeys: {"Ctrl-Space": "autocomplete"},
+          extraKeys: {'Ctrl-Space': 'autocomplete'},
         }}
         editorDidMount={(editor, next) => {
           // resizable with jquery.ui
@@ -47,7 +47,7 @@ export default class CustomHeaderEditor extends React.Component {
           this.props.inputElem.value = value;
         }}
       />
-    )
+    );
   }
 
 }

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

@@ -35,7 +35,7 @@ export default class CustomScriptEditor extends React.Component {
           autoRefresh: true,
           matchBrackets: true,
           autoCloseBrackets: true,
-          extraKeys: {"Ctrl-Space": "autocomplete"},
+          extraKeys: {'Ctrl-Space': 'autocomplete'},
         }}
         editorDidMount={(editor, next) => {
           // resizable with jquery.ui
@@ -49,7 +49,7 @@ export default class CustomScriptEditor extends React.Component {
           this.props.inputElem.value = value;
         }}
       />
-    )
+    );
   }
 
 }

+ 2 - 1
resource/js/components/BookmarkButton.js

@@ -38,7 +38,8 @@ export default class BookmarkButton extends React.Component {
       .then(res => {
         this.markBookmarked();
       });
-    } else {
+    }
+    else {
       this.props.crowi.apiPost('/bookmarks.remove', {page_id: pageId})
       .then(res => {
         this.markUnBookmarked();

+ 2 - 2
resource/js/components/CopyButton.js

@@ -21,9 +21,9 @@ export default class CopyButton extends React.Component {
   render() {
     const containerStyle = {
       lineHeight: 0
-    }
+    };
     const style = Object.assign({
-      padding: "0 2px",
+      padding: '0 2px',
       verticalAlign: 'text-top',
     }, this.props.buttonStyle);
 

+ 2 - 3
resource/js/components/HeaderSearchBox/SearchForm.js

@@ -1,5 +1,4 @@
 import React from 'react';
-import PropTypes from 'prop-types';
 
 import FormGroup from 'react-bootstrap/es/FormGroup';
 import Button from 'react-bootstrap/es/Button';
@@ -47,8 +46,8 @@ export default class SearchForm extends React.Component {
 
   render() {
     const emptyLabel = (this.state.searchError !== null)
-        ? 'Error on searching.'
-        : 'No matches found on title... Hit [Enter] key so that search on contents.';
+      ? 'Error on searching.'
+      : 'No matches found on title... Hit [Enter] key so that search on contents.';
 
     return (
       <form

+ 1 - 7
resource/js/components/NewPageNameInputter.js

@@ -1,12 +1,6 @@
 import React from 'react';
-import { FormGroup } from 'react-bootstrap/es/FormGroup';
-import { Button } from 'react-bootstrap/es/Button';
-import { InputGroup } from 'react-bootstrap/es/InputGroup';
-
-import UserPicture from './User/UserPicture';
-import PageListMeta from './PageList/PageListMeta';
-import PagePath from './PageList/PagePath';
 import PropTypes from 'prop-types';
+
 import SearchTypeahead from './SearchTypeahead';
 
 export default class NewPageNameInputter extends React.Component {

+ 2 - 2
resource/js/components/Page.js

@@ -60,7 +60,7 @@ export default class Page extends React.Component {
         return;
       }
       const k = keyword
-            .replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
+            .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
             .replace(/(^"|"$)/g, ''); // for phrase (quoted) keyword
       const keywordExp = new RegExp(`(${k}(?!(.*?")))`, 'ig');
       returnBody = returnBody.replace(keywordExp, '<em class="highlighted">$&</em>');
@@ -117,7 +117,7 @@ export default class Page extends React.Component {
           isMathJaxEnabled={isMathJaxEnabled}
           renderMathJaxOnInit={true}
       />
-    )
+    );
   }
 }
 

+ 2 - 2
resource/js/components/Page/RevisionBody.js

@@ -35,7 +35,7 @@ export default class RevisionBody extends React.Component {
 
   renderMathJax() {
     const MathJax = window.MathJax;
-    MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.element]);
+    MathJax.Hub.Queue(['Typeset', MathJax.Hub, this.element]);
   }
 
   generateInnerHtml(html) {
@@ -53,7 +53,7 @@ export default class RevisionBody extends React.Component {
         }}
         className="wiki" dangerouslySetInnerHTML={this.generateInnerHtml(this.props.html)}>
       </div>
-    )
+    );
   }
 }
 

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

@@ -60,22 +60,22 @@ export default class RevisionPath extends React.Component {
       return <span>/</span>;
     }
     else {
-      return <span></span>
+      return <span></span>;
     }
   }
 
   render() {
     // define styles
     const rootStyle = {
-      marginRight: "0.2em",
-    }
+      marginRight: '0.2em',
+    };
     const separatorStyle = {
-      marginLeft: "0.2em",
-      marginRight: "0.2em",
-    }
+      marginLeft: '0.2em',
+      marginRight: '0.2em',
+    };
     const editButtonStyle = {
-      marginLeft: "0.5em",
-      padding: "0 2px",
+      marginLeft: '0.5em',
+      padding: '0 2px',
     };
 
     const pageLength = this.state.pages.length;

+ 4 - 4
resource/js/components/Page/RevisionUrl.js

@@ -7,12 +7,12 @@ export default class RevisionUrl extends React.Component {
 
   render() {
     const buttonStyle = {
-      fontSize: "1em"
-    }
+      fontSize: '1em'
+    };
 
     const url = (this.props.pageId == null)
-        ? decodeURIComponent(location.href)
-        : `${location.origin}/${this.props.pageId}`;
+      ? decodeURIComponent(location.href)
+      : `${location.origin}/${this.props.pageId}`;
     const copiedText = this.props.pagePath + '\n' + url;
 
     return (

+ 1 - 1
resource/js/components/PageAttachment/DeleteAttachmentModal.js

@@ -57,7 +57,7 @@ export default class DeleteAttachmentModal extends React.Component {
 
     let deletingIndicator = '';
     if (this.props.deleting) {
-      deletingIndicator = <div class="speeding-wheel-sm"></div>;
+      deletingIndicator = <div className="speeding-wheel-sm"></div>;
     }
     if (this.props.deleteError) {
       deletingIndicator = <span>{this.props.deleteError}</span>;

+ 1 - 1
resource/js/components/PageAttachment/PageAttachmentList.js

@@ -12,7 +12,7 @@ export default class PageAttachmentList extends React.Component {
     const attachmentList = this.props.attachments.map((attachment, idx) => {
       return (
         <Attachment
-          key={"page:attachment:" + attachment._id}
+          key={'page:attachment:' + attachment._id}
           attachment={attachment}
           inUse={this.props.inUse[attachment._id] || false}
           onAttachmentDeleteClicked={this.props.onAttachmentDeleteClicked}

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

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

+ 2 - 2
resource/js/components/PageComment/DeleteCommentModal.js

@@ -14,7 +14,7 @@ export default class DeleteCommentModal extends React.Component {
   /*
    * the threshold for omitting body
    */
-  static get OMIT_BODY_THRES() { return 400 };
+  static get OMIT_BODY_THRES() { return 400 }
 
   constructor(props) {
     super(props);
@@ -25,7 +25,7 @@ export default class DeleteCommentModal extends React.Component {
 
   render() {
     if (this.props.comment === undefined) {
-      return <div></div>
+      return <div></div>;
     }
 
     const comment = this.props.comment;

+ 3 - 2
resource/js/components/PageCommentFormBehavior.js

@@ -39,7 +39,8 @@ export default class PageCommentFormBehavior extends React.Component {
 
           $('#comment-form-comment').val('');
           $('#comment-form-message').text('');
-        } else {
+        }
+        else {
           $('#comment-form-message').text(data.error);
         }
       }).fail(function(data) {
@@ -54,7 +55,7 @@ export default class PageCommentFormBehavior extends React.Component {
 
   render() {
     // render nothing
-    return <div></div>
+    return <div></div>;
   }
 }
 

+ 4 - 2
resource/js/components/PageEditor/Editor.js

@@ -130,6 +130,8 @@ export default class Editor extends React.Component {
       this.getCodeMirror().focus();
       if (editor.hasFocus()) {
         clearInterval(intervalId);
+        // refresh
+        editor.refresh();
       }
     }, 100);
   }
@@ -485,7 +487,7 @@ export default class Editor extends React.Component {
                 'Enter': this.handleEnterKey,
                 'Tab': 'indentMore',
                 'Shift-Tab': 'indentLess',
-                'Ctrl-Q': (cm) => { cm.foldCode(cm.getCursor()); },
+                'Ctrl-Q': (cm) => { cm.foldCode(cm.getCursor()) },
               }
             }}
             onScroll={(editor, data) => {
@@ -511,7 +513,7 @@ export default class Editor extends React.Component {
         </Dropzone>
 
         <button type="button" className="btn btn-default btn-block btn-open-dropzone"
-          onClick={() => {this.refs.dropzone.open();}}>
+          onClick={() => {this.refs.dropzone.open()}}>
 
           <i className="icon-paper-clip" aria-hidden="true"></i>&nbsp;
           Attach files by dragging &amp; dropping,&nbsp;

+ 8 - 8
resource/js/components/PageEditor/EmojiAutoCompleteHelper.js

@@ -90,34 +90,34 @@ class EmojiAutoCompleteHelper {
     const maxLength = 12;
 
     let results1 = [], results2 = [], results3 = [], results4 = [];
-    const countLen1 = () => { results1.length; }
-    const countLen2 = () => { countLen1() + results2.length; }
-    const countLen3 = () => { countLen2() + results3.length; }
-    const countLen4 = () => { countLen3() + results4.length; }
+    const countLen1 = () => { results1.length };
+    const countLen2 = () => { countLen1() + results2.length };
+    const countLen3 = () => { countLen2() + results3.length };
+    const countLen4 = () => { countLen3() + results4.length };
     // TODO performance tune
     // when total length of all results is less than `maxLength`
     for (let unicode in this.emojiStrategy) {
       const data = this.emojiStrategy[unicode];
 
-      if (maxLength <= countLen1()) { break; }
+      if (maxLength <= countLen1()) { break }
       // prefix match to shortname
       else if (data.shortname.indexOf(`:${term}`) > -1) {
         results1.push(data.shortname);
         continue;
       }
-      else if (maxLength <= countLen2()) { continue; }
+      else if (maxLength <= countLen2()) { continue }
       // partial match to shortname
       else if (data.shortname.indexOf(term) > -1) {
         results2.push(data.shortname);
         continue;
       }
-      else if (maxLength <= countLen3()) { continue; }
+      else if (maxLength <= countLen3()) { continue }
       // partial match to elements of aliases
       else if ((data.aliases != null) && data.aliases.find(elem => elem.indexOf(term) > -1)) {
         results3.push(data.shortname);
         continue;
       }
-      else if (maxLength <= countLen4()) { continue; }
+      else if (maxLength <= countLen4()) { continue }
       // partial match to elements of keywords
       else if ((data.keywords != null) && data.keywords.find(elem => elem.indexOf(term) > -1)) {
         results4.push(data.shortname);

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

@@ -64,7 +64,7 @@ class MarkdownListUtil {
       // indent
       const replacedLines = lines.map((line) => {
         return indent + line;
-      })
+      });
 
       adjusted = replacedLines.join('\n');
     }
@@ -75,7 +75,7 @@ class MarkdownListUtil {
     // not listful data
     else {
       // append `indentAndMark` at the beginning of all lines (except the first line)
-      const replacedText = text.replace(/(\r\n|\r|\n)/g, "$1" + indentAndMark);
+      const replacedText = text.replace(/(\r\n|\r|\n)/g, '$1' + indentAndMark);
       // append `indentAndMark` to the first line
       adjusted = indentAndMark + replacedText;
     }

+ 9 - 7
resource/js/components/PageEditor/MarkdownTableUtil.js

@@ -1,4 +1,5 @@
-import markdown_table from 'markdown-table';
+import markdownTable from 'markdown-table';
+import stringWidth from 'string-width';
 
 /**
  * Utility for markdown table
@@ -111,23 +112,24 @@ class MarkdownTableUtil {
           { align: 'l', regex: /^:-+$/  },
           { align: 'r', regex: /^-+:$/  },
         ];
-        let lineText = "";
+        let lineText = '';
         lineText = line.replace(/^\||\|$/g, ''); // strip off pipe charactor which is placed head of line and last of line.
         lineText = lineText.replace(/\s*/g, '');
         aligns = lineText.split(/\|/).map(col => {
           const rule = alignRuleRE.find(rule => col.match(rule.regex));
           return (rule != undefined) ? rule.align : '';
         });
-      } else if (this.linePartOfTableRE.test(line)) {
+      }
+      else if (this.linePartOfTableRE.test(line)) {
         // parse line whether header or body
-        let lineText = "";
+        let lineText = '';
         lineText = line.replace(/\s*\|\s*/g, '|');
         lineText = lineText.replace(/^\||\|$/g, ''); // strip off pipe charactor which is placed head of line and last of line.
         const row = lineText.split(/\|/);
         contents.push(row);
       }
     }
-    return (new MarkdownTable(contents, { align: aligns }));
+    return (new MarkdownTable(contents, { align: aligns, stringLength: stringWidth }));
   }
 
   /**
@@ -164,7 +166,7 @@ class MarkdownTableUtil {
     let newTable = [];
     const options = mdtable_list[0].options; // use option of first markdown-table
     mdtable_list.forEach((mdtable) => {
-      newTable = newTable.concat(mdtable.table)
+      newTable = newTable.concat(mdtable.table);
     });
     return (new MarkdownTable(newTable, options));
   }
@@ -199,7 +201,7 @@ class MarkdownTable {
   }
 
   toString() {
-    return markdown_table(this.table, this.options);
+    return markdownTable(this.table, this.options);
   }
 }
 

+ 12 - 16
resource/js/components/PageEditor/OptionsSelector.js

@@ -4,14 +4,10 @@ import PropTypes from 'prop-types';
 import FormGroup from 'react-bootstrap/es/FormGroup';
 import FormControl from 'react-bootstrap/es/FormControl';
 import ControlLabel from 'react-bootstrap/es/ControlLabel';
-import Button from 'react-bootstrap/es/Button';
 
 import Dropdown from 'react-bootstrap/es/Dropdown';
 import MenuItem from 'react-bootstrap/es/MenuItem';
 
-import OverlayTrigger  from 'react-bootstrap/es/OverlayTrigger';
-import Tooltip from 'react-bootstrap/es/Tooltip';
-
 export default class OptionsSelector extends React.Component {
 
   constructor(props) {
@@ -25,7 +21,7 @@ export default class OptionsSelector extends React.Component {
       previewOptions: this.props.previewOptions || new PreviewOptions(),
       isCddMenuOpened: false,
       isMathJaxEnabled,
-    }
+    };
 
     this.availableThemes = [
       'eclipse', 'elegant', 'neo', 'mdn-like', 'material', 'dracula', 'monokai', 'twilight'
@@ -35,7 +31,7 @@ export default class OptionsSelector extends React.Component {
       vim: 'Vim',
       emacs: 'Emacs',
       sublime: 'Sublime Text',
-    }
+    };
 
     this.onChangeTheme = this.onChangeTheme.bind(this);
     this.onChangeKeymapMode = this.onChangeKeymapMode.bind(this);
@@ -135,7 +131,7 @@ export default class OptionsSelector extends React.Component {
 
         </FormControl>
       </FormGroup>
-    )
+    );
   }
 
   renderKeymapModeSelector() {
@@ -163,7 +159,7 @@ export default class OptionsSelector extends React.Component {
 
         </FormControl>
       </FormGroup>
-    )
+    );
   }
 
   renderConfigurationDropdown() {
@@ -186,15 +182,15 @@ export default class OptionsSelector extends React.Component {
         </Dropdown>
 
       </FormGroup>
-    )
+    );
   }
 
   renderActiveLineMenuItem() {
     const isActive = this.state.editorOptions.styleActiveLine;
 
-    const iconClasses = ['text-info']
+    const iconClasses = ['text-info'];
     if (isActive) {
-      iconClasses.push('ti-check')
+      iconClasses.push('ti-check');
     }
     const iconClassName = iconClasses.join(' ');
 
@@ -204,7 +200,7 @@ export default class OptionsSelector extends React.Component {
         <span className="menuitem-label">Show active line</span>
         <span className="icon-container"><i className={iconClassName}></i></span>
       </MenuItem>
-    )
+    );
   }
 
   renderRealtimeMathJaxMenuItem() {
@@ -215,9 +211,9 @@ export default class OptionsSelector extends React.Component {
     const isEnabled = this.state.isMathJaxEnabled;
     const isActive = isEnabled && this.state.previewOptions.renderMathJaxInRealtime;
 
-    const iconClasses = ['text-info']
+    const iconClasses = ['text-info'];
     if (isActive) {
-      iconClasses.push('ti-check')
+      iconClasses.push('ti-check');
     }
     const iconClassName = iconClasses.join(' ');
 
@@ -227,7 +223,7 @@ export default class OptionsSelector extends React.Component {
         <span className="menuitem-label">MathJax Rendering</span>
         <i className={iconClassName}></i>
       </MenuItem>
-    )
+    );
   }
 
   render() {
@@ -235,7 +231,7 @@ export default class OptionsSelector extends React.Component {
       <span className="m-l-5">{this.renderThemeSelector()}</span>
       <span className="m-l-5">{this.renderKeymapModeSelector()}</span>
       <span className="m-l-5">{this.renderConfigurationDropdown()}</span>
-    </span>
+    </span>;
   }
 }
 

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

@@ -1,4 +1,4 @@
-import accepts from 'attr-accept'
+import accepts from 'attr-accept';
 
 import markdownListUtil from './MarkdownListUtil';
 
@@ -33,7 +33,7 @@ class PasteHelper {
    * @param {*} accept
    */
   fileAccepted(file, accept) {
-    return file.type === 'application/x-moz-file' || accepts(file, accept)
+    return file.type === 'application/x-moz-file' || accepts(file, accept);
   }
   /**
    * transplanted from react-dropzone
@@ -44,7 +44,7 @@ class PasteHelper {
    * @param {number} minSize
    */
   fileMatchSize(file, maxSize, minSize) {
-    return file.size <= maxSize && file.size >= minSize
+    return file.size <= maxSize && file.size >= minSize;
   }
 }
 

+ 1 - 1
resource/js/components/PageEditor/Preview.js

@@ -34,7 +34,7 @@ export default class Preview extends React.Component {
           renderMathJaxInRealtime={renderMathJaxInRealtime}
         />
       </div>
-    )
+    );
   }
 }
 

+ 60 - 56
resource/js/components/PageEditor/ScrollSyncHelper.js

@@ -19,7 +19,7 @@ class ScrollSyncHelper {
         parentElement.getElementsByClassName('code-line'),
         element => {
           const line = +element.getAttribute('data-line');
-          return { element, line }
+          return { element, line };
         })
         .filter(x => !isNaN(x.line));
     }
@@ -37,18 +37,19 @@ class ScrollSyncHelper {
 	 *
 	 * @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
 	 */
-	getElementsForSourceLine(element, targetLine) {
-		const lines = this.getCodeLineElements(element);
-		let previous = lines[0] || null;
-		for (const entry of lines) {
-			if (entry.line === targetLine) {
-				return { previous: entry, next: null };
-			} else if (entry.line > targetLine) {
-				return { previous, next: entry };
-			}
-			previous = entry;
-		}
-		return { previous };
+  getElementsForSourceLine(element, targetLine) {
+    const lines = this.getCodeLineElements(element);
+    let previous = lines[0] || null;
+    for (const entry of lines) {
+      if (entry.line === targetLine) {
+        return { previous: entry, next: null };
+      }
+      else if (entry.line > targetLine) {
+        return { previous, next: entry };
+      }
+      previous = entry;
+    }
+    return { previous };
   }
 
   /**
@@ -59,51 +60,53 @@ class ScrollSyncHelper {
    *
 	 * @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
 	 */
-	getLineElementsAtPageOffset(parentElement, offset) {
-		const lines = this.getCodeLineElements(parentElement);
+  getLineElementsAtPageOffset(parentElement, offset) {
+    const lines = this.getCodeLineElements(parentElement);
 
     const position = offset - parentElement.scrollTop + this.getParentElementOffset(parentElement);
 
-		let lo = -1;
-		let hi = lines.length - 1;
-		while (lo + 1 < hi) {
-			const mid = Math.floor((lo + hi) / 2);
+    let lo = -1;
+    let hi = lines.length - 1;
+    while (lo + 1 < hi) {
+      const mid = Math.floor((lo + hi) / 2);
       const bounds = lines[mid].element.getBoundingClientRect();
-			if (bounds.top + bounds.height >= position) {
-				hi = mid;
-			} else {
-				lo = mid;
-			}
+      if (bounds.top + bounds.height >= position) {
+        hi = mid;
+      }
+      else {
+        lo = mid;
+      }
     }
 
-		const hiElement = lines[hi];
-		if (hi >= 1 && hiElement.element.getBoundingClientRect().top > position) {
-			const loElement = lines[lo];
-			const bounds = loElement.element.getBoundingClientRect();
+    const hiElement = lines[hi];
+    if (hi >= 1 && hiElement.element.getBoundingClientRect().top > position) {
+      const loElement = lines[lo];
+      const bounds = loElement.element.getBoundingClientRect();
       let previous = { element: loElement.element, line: loElement.line };
       if (bounds.height > 0) {
         previous.line += (position - bounds.top) / (bounds.height);
       }
       const next = { element: hiElement.element, line: hiElement.line, fractional: 0 };
-			return { previous, next };
-		}
+      return { previous, next };
+    }
 
-		const bounds = hiElement.element.getBoundingClientRect();
+    const bounds = hiElement.element.getBoundingClientRect();
     const previous = { element: hiElement.element, line: hiElement.line + (position - bounds.top) / (bounds.height) };
-		return { previous };
+    return { previous };
   }
 
   getEditorLineNumberForPageOffset(parentElement, offset) {
     const { previous, next } = this.getLineElementsAtPageOffset(parentElement, offset);
-		if (previous) {
-			if (next) {
+    if (previous) {
+      if (next) {
         const betweenProgress = (offset - parentElement.scrollTop - previous.element.getBoundingClientRect().top) / (next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top);
-				return previous.line + betweenProgress * (next.line - previous.line);
-			} else {
-				return previous.line;
-			}
-		}
-		return null;
+        return previous.line + betweenProgress * (next.line - previous.line);
+      }
+      else {
+        return previous.line;
+      }
+    }
+    return null;
   }
 
   /**
@@ -116,7 +119,7 @@ class ScrollSyncHelper {
     const style = window.getComputedStyle(parentElement, null);
     const paddingTop = +(style.paddingTop.replace('px', ''));
 
-		return offsetY + paddingTop;
+    return offsetY + paddingTop;
   }
 
   /**
@@ -125,23 +128,24 @@ class ScrollSyncHelper {
    * @param {Element} previewElement
 	 * @param {number} line
 	 */
-	scrollPreview(previewElement, line) {
-		const { previous, next } = this.getElementsForSourceLine(previewElement, line);
-		if (previous) {
-			let scrollTo = 0;
-			if (next) {
-				// Between two elements. Go to percentage offset between them.
-				const betweenProgress = (line - previous.line) / (next.line - previous.line);
-				const elementOffset = next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top;
-				scrollTo = previous.element.getBoundingClientRect().top + betweenProgress * elementOffset;
-			} else {
-				scrollTo = previous.element.getBoundingClientRect().top;
+  scrollPreview(previewElement, line) {
+    const { previous, next } = this.getElementsForSourceLine(previewElement, line);
+    if (previous) {
+      let scrollTo = 0;
+      if (next) {
+        // Between two elements. Go to percentage offset between them.
+        const betweenProgress = (line - previous.line) / (next.line - previous.line);
+        const elementOffset = next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top;
+        scrollTo = previous.element.getBoundingClientRect().top + betweenProgress * elementOffset;
+      }
+      else {
+        scrollTo = previous.element.getBoundingClientRect().top;
       }
 
       scrollTo -= this.getParentElementOffset(previewElement);
 
       previewElement.scroll(0, previewElement.scrollTop + scrollTo);
-		}
+    }
   }
 
   /**
@@ -151,8 +155,8 @@ class ScrollSyncHelper {
 	 * @param {number} line
 	 */
   scrollPreviewToRevealOverflowing(previewElement, line) {
-		const { previous, next } = this.getElementsForSourceLine(previewElement, line);
-		if (previous) {
+    const { previous, next } = this.getElementsForSourceLine(previewElement, line);
+    if (previous) {
       const parentElementOffset = this.getParentElementOffset(previewElement);
       const prevElmTop = previous.element.getBoundingClientRect().top - parentElementOffset;
       const prevElmBottom = previous.element.getBoundingClientRect().bottom - parentElementOffset;
@@ -172,7 +176,7 @@ class ScrollSyncHelper {
       }
 
       previewElement.scroll(0, scrollTo);
-		}
+    }
   }
 
   /**

+ 3 - 2
resource/js/components/PageHistory.js

@@ -38,7 +38,8 @@ export default class PageHistory extends React.Component {
 
         if (i === 0 || i === lastId) {
           diffOpened[revision._id] = true;
-        } else {
+        }
+        else {
           diffOpened[revision._id] = false;
         }
       });
@@ -106,7 +107,7 @@ export default class PageHistory extends React.Component {
 
             return rev;
           })
-        })
+        });
       }
     }).catch(err => {
 

+ 7 - 6
resource/js/components/PageHistory/PageRevisionList.js

@@ -12,29 +12,30 @@ export default class PageRevisionList extends React.Component {
 
     const revisionList = this.props.revisions.map((revision, idx) => {
       const revisionId = revision._id
-        , revisionDiffOpened = this.props.diffOpened[revisionId] || false
+        , revisionDiffOpened = this.props.diffOpened[revisionId] || false;
 
 
       let previousRevision;
       if (idx+1 < revisionCount) {
         previousRevision = revisions[idx + 1];
-      } else {
+      }
+      else {
         previousRevision = revision; // if it is the first revision, show full text as diff text
       }
 
       return (
-        <div className="revision-hisory-outer" key={"revision-history-" + revisionId}>
+        <div className="revision-hisory-outer" key={'revision-history-' + revisionId}>
           <Revision
             revision={revision}
             revisionDiffOpened={revisionDiffOpened}
             onDiffOpenClicked={this.props.onDiffOpenClicked}
-            key={"revision-history-rev-" + revisionId}
+            key={'revision-history-rev-' + revisionId}
             />
           <RevisionDiff
             revisionDiffOpened={revisionDiffOpened}
             currentRevision={revision}
             previousRevision={previousRevision}
-            key={"revision-diff-" + revisionId}
+            key={'revision-diff-' + revisionId}
           />
         </div>
       );
@@ -52,5 +53,5 @@ PageRevisionList.propTypes = {
   revisions: PropTypes.array,
   diffOpened: PropTypes.object,
   onDiffOpenClicked: PropTypes.func.isRequired,
-}
+};
 

+ 2 - 2
resource/js/components/PageHistory/Revision.js

@@ -46,7 +46,7 @@ export default class Revision extends React.Component {
               <a className="diff-view" onClick={this._onDiffOpenClicked}>
                 <i className={iconClass}></i> View diff
               </a>
-              <a href={"?revision=" + revision._id } className="m-l-10">
+              <a href={'?revision=' + revision._id } className="m-l-10">
                 <i className="icon-login"></i> Go to this version
               </a>
             </p>
@@ -61,5 +61,5 @@ Revision.propTypes = {
   revision: PropTypes.object,
   revisionDiffOpened: PropTypes.bool.isRequired,
   onDiffOpenClicked: PropTypes.func.isRequired,
-}
+};
 

+ 1 - 1
resource/js/components/PageHistory/RevisionDiff.js

@@ -40,4 +40,4 @@ RevisionDiff.propTypes = {
   currentRevision: PropTypes.object.isRequired,
   previousRevision: PropTypes.object.isRequired,
   revisionDiffOpened: PropTypes.bool.isRequired,
-}
+};

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

@@ -7,7 +7,7 @@ export default class ListView extends React.Component {
 
   render() {
     const listView = this.props.pages.map((page) => {
-      return <Page page={page} key={"page-list:list-view:" + page._id} />;
+      return <Page page={page} key={'page-list:list-view:' + page._id} />;
     });
 
     return (

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

@@ -16,7 +16,7 @@ export default class Page extends React.Component {
 
     const styleFlex = {
       flex: 1
-    }
+    };
 
     return (
       <li className="page-list-li d-flex align-items-center">

+ 4 - 4
resource/js/components/PageListSearch.js

@@ -18,7 +18,7 @@ export default class PageListSearch extends React.Component {
       searchedPages: [],
       searchResultMeta: {},
       searchError: null,
-    }
+    };
 
     this.changeURL = this.changeURL.bind(this);
     this.search = this.search.bind(this);
@@ -92,9 +92,9 @@ export default class PageListSearch extends React.Component {
     let hash = location.hash || '';
     // TODO 整理する
     if (refreshHash || this.state.searchedKeyword !== '') {
-        hash = '';
+      hash = '';
     }
-    if (window.history && window.history.pushState){
+    if (window.history && window.history.pushState) {
       window.history.pushState('', `Search - ${keyword}`, `${tree}?q=${keyword}${hash}`);
     }
   }
@@ -133,7 +133,7 @@ export default class PageListSearch extends React.Component {
         searchError: err,
       });
     });
-  };
+  }
 
   render() {
     return (

+ 3 - 3
resource/js/components/ReactUtils.js

@@ -14,10 +14,10 @@ export default class ReactUtils {
    * @memberOf ReactUtils
    */
   static nl2br(text) {
-    var regex = /(\n)/g
-    return text.split(regex).map(function (line) {
+    var regex = /(\n)/g;
+    return text.split(regex).map(function(line) {
       if (line.match(regex)) {
-        return React.createElement('br', {key: Math.random().toString(10).substr(2, 10)})
+        return React.createElement('br', {key: Math.random().toString(10).substr(2, 10)});
       }
       else {
         return line;

+ 4 - 4
resource/js/components/SearchPage.js

@@ -18,7 +18,7 @@ export default class SearchPage extends React.Component {
       searchedPages: [],
       searchResultMeta: {},
       searchError: null,
-    }
+    };
 
     this.search = this.search.bind(this);
     this.changeURL = this.changeURL.bind(this);
@@ -47,9 +47,9 @@ export default class SearchPage extends React.Component {
     let hash = location.hash || '';
     // TODO 整理する
     if (refreshHash || this.state.searchedKeyword !== '') {
-        hash = '';
+      hash = '';
     }
-    if (window.history && window.history.pushState){
+    if (window.history && window.history.pushState) {
       window.history.pushState('', `Search - ${keyword}`, `/_search?q=${keyword}${hash}`);
     }
   }
@@ -86,7 +86,7 @@ export default class SearchPage extends React.Component {
         searchError: err,
       });
     });
-  };
+  }
 
   render() {
     return (

+ 2 - 4
resource/js/components/SearchPage/DeletePageListModal.js

@@ -5,14 +5,12 @@ import Button from 'react-bootstrap/es/Button';
 import Modal from 'react-bootstrap/es/Modal';
 import Checkbox from 'react-bootstrap/es/Checkbox';
 
-import ReactUtils from '../ReactUtils';
-
 export default class DeletePageListModal extends React.Component {
 
   /*
    * the threshold for omitting body
    */
-  static get OMIT_BODY_THRES() { return 400 };
+  static get OMIT_BODY_THRES() { return 400 }
 
   constructor(props) {
     super(props);
@@ -23,7 +21,7 @@ export default class DeletePageListModal extends React.Component {
 
   render() {
     if (this.props.pages === undefined || this.props.pages.length == 0) {
-      return <div></div>
+      return <div></div>;
     }
 
     const listView = this.props.pages.map((page) => {

+ 1 - 0
resource/js/components/SearchPage/SearchForm.js

@@ -54,6 +54,7 @@ export default class SearchForm extends React.Component {
 }
 
 SearchForm.propTypes = {
+  keyword: PropTypes.string,
   onSearchFormChanged: PropTypes.func.isRequired,
 };
 SearchForm.defaultProps = {

+ 15 - 10
resource/js/components/SearchPage/SearchResult.js

@@ -10,12 +10,12 @@ export default class SearchResult extends React.Component {
   constructor(props) {
     super(props);
     this.state = {
-      deletionMode : false,
-      selectedPages : new Set(),
+      deletionMode: false,
+      selectedPages: new Set(),
       isDeleteCompletely: undefined,
       isDeleteConfirmModalShown: false,
       errorMessageForDeleting: undefined,
-    }
+    };
     this.toggleDeleteCompletely = this.toggleDeleteCompletely.bind(this);
     this.deleteSelectedPages = this.deleteSelectedPages.bind(this);
     this.closeDeleteConfirmModal = this.closeDeleteConfirmModal.bind(this);
@@ -45,7 +45,8 @@ export default class SearchResult extends React.Component {
   toggleCheckbox(page) {
     if (this.state.selectedPages.has(page)) {
       this.state.selectedPages.delete(page);
-    } else {
+    }
+    else {
       this.state.selectedPages.add(page);
     }
     this.setState({isDeleteConfirmModalShown: false});
@@ -120,7 +121,9 @@ export default class SearchResult extends React.Component {
           isDeleted = false;
         }
       }).catch(err => {
+        /* eslint-disable no-console */
         console.log(err.message);
+        /* eslint-enable */
         isDeleted = false;
         this.setState({errorMessageForDeleting: err.message});
       });
@@ -176,7 +179,7 @@ export default class SearchResult extends React.Component {
       }
       return (
         <div className="content-main">
-            <i className="icon-fw icon-info" /> No page found with "{this.props.searchingKeyword}"{under}
+            <i className="icon-fw icon-info" /> No page found with &quot;{this.props.searchingKeyword}&quot;{under}
         </div>
       );
 
@@ -194,7 +197,7 @@ export default class SearchResult extends React.Component {
         <button type="button" className="btn btn-rounded btn-danger btn-xs" onClick={() => this.showDeleteConfirmModal()} disabled={this.state.selectedPages.size == 0}>
           <i className="icon-trash"/> Delete
         </button>
-      </div>
+      </div>;
       allSelectCheck =
       <div>
         <label>
@@ -204,7 +207,7 @@ export default class SearchResult extends React.Component {
             checked={this.isAllSelected()} />
             &nbsp;Check All
         </label>
-      </div>
+      </div>;
     }
     else {
       deletionModeButtons =
@@ -212,11 +215,11 @@ export default class SearchResult extends React.Component {
         <button type="button" className="btn btn-default btn-rounded btn-xs" onClick={() => this.handleDeletionModeChange()}>
           <i className="ti-check-box"/> DeletionMode
         </button>
-      </div>
+      </div>;
     }
 
     const listView = this.props.pages.map((page) => {
-      const pageId = "#" + page._id;
+      const pageId = '#' + page._id;
       return (
         <Page page={page}
           linkTo={pageId}
@@ -255,7 +258,7 @@ export default class SearchResult extends React.Component {
                 {allSelectCheck}
               </div>
               <div className="search-result-meta">
-                <i className="icon-magnifier" /> Found {this.props.searchResultMeta.total} pages with "{this.props.searchingKeyword}"
+                <i className="icon-magnifier" /> Found {this.props.searchResultMeta.total} pages with &quot;{this.props.searchingKeyword}&quot;
               </div>
               <div className="clearfix"></div>
               <div className="page-list">
@@ -289,10 +292,12 @@ export default class SearchResult extends React.Component {
 
 SearchResult.propTypes = {
   crowi: PropTypes.object.isRequired,
+  crowiRenderer: PropTypes.object,
   tree: PropTypes.string.isRequired,
   pages: PropTypes.array.isRequired,
   searchingKeyword: PropTypes.string.isRequired,
   searchResultMeta: PropTypes.object.isRequired,
+  searchError: PropTypes.object
 };
 SearchResult.defaultProps = {
   tree: '',

+ 0 - 2
resource/js/components/SearchPage/SearchResultList.js

@@ -14,8 +14,6 @@ export default class SearchResultList extends React.Component {
   }
 
   render() {
-    var isEnabledLineBreaks = $('#content-main').data('linebreaks-enabled');
-
     const resultList = this.props.pages.map((page) => {
       const pageBody = page.revision.body;
       return (

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

@@ -98,7 +98,7 @@ export default class SearchTypeahead extends React.Component {
 
     // navigate to page
     if (page != null) {
-        window.location = page.path;
+      window.location = page.path;
     }
   }
 
@@ -139,7 +139,7 @@ export default class SearchTypeahead extends React.Component {
       ? 'Error on searching.'
       : 'No matches found on title...';
     const restoreFormButton = this.getRestoreFormButton();
-    const defaultSelected = (this.props.keywordOnInit != "")
+    const defaultSelected = (this.props.keywordOnInit != '')
       ? [{path: this.props.keywordOnInit}]
       : [];
 
@@ -148,7 +148,7 @@ export default class SearchTypeahead extends React.Component {
         <AsyncTypeahead
           {...this.props}
           ref="typeahead"
-          inputProps={{name: "q", autoComplete: "off"}}
+          inputProps={{name: 'q', autoComplete: 'off'}}
           isLoading={this.state.isLoading}
           labelKey="path"
           minLength={2}

+ 7 - 1
resource/js/components/SeenUserList.js

@@ -1,4 +1,6 @@
 import React from 'react';
+import PropTypes from 'prop-types';
+
 import UserList from './SeenUserList/UserList';
 
 export default class SeenUserList extends React.Component {
@@ -22,7 +24,7 @@ export default class SeenUserList extends React.Component {
 
   getSeenUserIds() {
     // FIXME: Consider another way to bind values.
-    const $seenUserList = $("#seen-user-list");
+    const $seenUserList = $('#seen-user-list');
     if ($seenUserList.length > 0) {
       const seenUsers = $seenUserList.data('seen-users');
       if (seenUsers) {
@@ -44,3 +46,7 @@ export default class SeenUserList extends React.Component {
     );
   }
 }
+
+SeenUserList.propTypes = {
+  crowi: PropTypes.object.isRequired,
+};

+ 11 - 12
resource/js/legacy/crowi-admin.js

@@ -2,8 +2,6 @@ require('bootstrap-select');
 require('./thirdparty-js/jQuery.style.switcher');
 
 $(function() {
-  var UpdatePost = {};
-
   $('#slackNotificationForm').on('submit', function(e) {
     $.post('/_api/admin/notification.add', $(this).serialize(), function(res) {
       if (res.ok) {
@@ -44,8 +42,8 @@ $(function() {
         $('#admin-password-reset-modal').modal('hide');
         $('#admin-password-reset-modal-done').modal('show');
 
-        $("#admin-password-reset-done-user").text(res.user.email);
-        $("#admin-password-reset-done-password").text(res.newPassword);
+        $('#admin-password-reset-done-user').text(res.user.email);
+        $('#admin-password-reset-done-password').text(res.newPassword);
         return ;
       }
 
@@ -56,7 +54,7 @@ $(function() {
     return false;
   });
 
-  $('#admin-delete-user-group-modal').on('show.bs.modal', function (button) {
+  $('#admin-delete-user-group-modal').on('show.bs.modal', function(button) {
     var data = $(button.relatedTarget);
     var userGroupId = data.data('user-group-id');
     var userGroupName = data.data('user-group-name');
@@ -65,15 +63,15 @@ $(function() {
     $('#admin-user-groups-delete input[name=user_group_id]').val(userGroupId);
   });
 
-  $('form#user-group-relation-create').on('submit', function (e) {
-    $.post('/admin/user-group-relation/create', $(this).serialize(), function (res) {
+  $('form#user-group-relation-create').on('submit', function(e) {
+    $.post('/admin/user-group-relation/create', $(this).serialize(), function(res) {
       $('#admin-add-user-group-relation-modal').modal('hide');
       return;
-     });
+    });
   });
 
 
-  $("#pictureUploadForm input[name=userGroupPicture]").on('change', function () {
+  $('#pictureUploadForm input[name=userGroupPicture]').on('change', function() {
     var $form = $('#pictureUploadForm');
     var fd = new FormData($form[0]);
     if ($(this).val() == '') {
@@ -81,19 +79,20 @@ $(function() {
     }
 
     $('#pictureUploadFormProgress').html('<img src="/images/loading_s.gif"> アップロード中...');
-    $.ajax($form.attr("action"), {
+    $.ajax($form.attr('action'), {
       type: 'post',
       processData: false,
       contentType: false,
       data: fd,
       dataType: 'json',
-      success: function (data) {
+      success: function(data) {
         if (data.status) {
           $('#settingUserPicture').attr('src', data.url + '?time=' + (new Date()));
           $('#pictureUploadFormMessage')
             .addClass('alert alert-success')
             .html('変更しました');
-        } else {
+        }
+        else {
           $('#pictureUploadFormMessage')
             .addClass('alert alert-danger')
             .html('変更中にエラーが発生しました。');

+ 38 - 38
resource/js/legacy/crowi-form.js

@@ -1,49 +1,49 @@
-  var pageId = $('#content-main').data('page-id');
-  var pagePath= $('#content-main').data('path');
-
-  require('bootstrap-select');
-  require('bootstrap-sass');
-
-  // show/hide
-  function FetchPagesUpdatePostAndInsert(path) {
-    $.get('/_api/pages.updatePost', {path: path}, function(res) {
-      if (res.ok) {
-        var $slackChannels = $('#page-form-slack-channel');
-        $slackChannels.val(res.updatePost.join(','));
-      }
-    });
-  }
+var pageId = $('#content-main').data('page-id');
+var pagePath= $('#content-main').data('path');
 
-  var slackConfigured = $('#page-form-setting').data('slack-configured');
+require('bootstrap-select');
+require('bootstrap-sass');
 
-  // for new page
-  if (!pageId) {
-    if (!pageId && pagePath.match(/(20\d{4}|20\d{6}|20\d{2}_\d{1,2}|20\d{2}_\d{1,2}_\d{1,2})/)) {
-      $('#page-warning-modal').modal('show');
+// show/hide
+function FetchPagesUpdatePostAndInsert(path) {
+  $.get('/_api/pages.updatePost', {path: path}, function(res) {
+    if (res.ok) {
+      var $slackChannels = $('#page-form-slack-channel');
+      $slackChannels.val(res.updatePost.join(','));
     }
+  });
+}
 
-    if (slackConfigured) {
-      FetchPagesUpdatePostAndInsert(pagePath);
-    }
+var slackConfigured = $('#page-form-setting').data('slack-configured');
+
+// for new page
+if (!pageId) {
+  if (!pageId && pagePath.match(/(20\d{4}|20\d{6}|20\d{2}_\d{1,2}|20\d{2}_\d{1,2}_\d{1,2})/)) {
+    $('#page-warning-modal').modal('show');
+  }
+
+  if (slackConfigured) {
+    FetchPagesUpdatePostAndInsert(pagePath);
   }
+}
 
-  $('a[data-toggle="tab"][href="#edit-form"]').on('show.bs.tab', function() {
-    $('body').addClass('on-edit');
+$('a[data-toggle="tab"][href="#edit-form"]').on('show.bs.tab', function() {
+  $('body').addClass('on-edit');
 
-    if (slackConfigured) {
-      var $slackChannels = $('#page-form-slack-channel');
-      var slackChannels = $slackChannels.val();
-      // if slackChannels is empty, then fetch default (admin setting)
-      // if not empty, it means someone specified this setting for the page.
-      if (slackChannels === '') {
-        FetchPagesUpdatePostAndInsert(pagePath);
-      }
+  if (slackConfigured) {
+    var $slackChannels = $('#page-form-slack-channel');
+    var slackChannels = $slackChannels.val();
+    // if slackChannels is empty, then fetch default (admin setting)
+    // if not empty, it means someone specified this setting for the page.
+    if (slackChannels === '') {
+      FetchPagesUpdatePostAndInsert(pagePath);
     }
-  });
+  }
+});
 
-  $('a[data-toggle="tab"][href="#edit-form"]').on('hide.bs.tab', function() {
-    $('body').removeClass('on-edit');
-  });
+$('a[data-toggle="tab"][href="#edit-form"]').on('hide.bs.tab', function() {
+  $('body').removeClass('on-edit');
+});
 
 /**
  * DOM ready
@@ -136,7 +136,7 @@ $(function() {
   $('#page-form').on('submit', function(e) {
     // avoid message
     // isFormChanged = false;
-    crowi.clearDraft(pagePath);
+    window.crowi.clearDraft(pagePath);
   });
   /*
   // This is a temporary implementation until porting to React.

Некоторые файлы не были показаны из-за большого количества измененных файлов