Explorar o código

Merge remote-tracking branch 'origin/master' into imprv/enable-to-switch-`pre.hljs`-border

yusuketk %!s(int64=8) %!d(string=hai) anos
pai
achega
8e5e2ffdeb
Modificáronse 100 ficheiros con 1515 adicións e 1525 borrados
  1. 7 0
      .eslintignore
  2. 31 4
      .eslintrc.js
  3. 1 0
      .gitignore
  4. 34 1
      CHANGES.md
  5. 20 2
      app.js
  6. 1 1
      bin/generate-plugin-definitions-source.js
  7. 0 73
      bin/migration/0.0.1-0.0.2-convert_embedded_object_to_schema.js
  8. 0 25
      bin/password-hash-generator.js
  9. 0 112
      bin/revision-string-replacer.js
  10. 0 126
      bin/search.js
  11. 1 1
      bin/shrink-emojione-strategy.js
  12. 0 44
      bin/util.js
  13. 2 17
      config/env.dev.js
  14. 3 5
      config/env.prod.js
  15. 0 2
      config/helpers.js
  16. 13 0
      config/index.js
  17. 17 0
      config/logger/config.dev.js
  18. 3 0
      config/logger/config.prod.js
  19. 22 31
      config/webpack.common.js
  20. 34 11
      config/webpack.dev.js
  21. 22 5
      config/webpack.prod.js
  22. 5 5
      lib/crowi/dev.js
  23. 12 9
      lib/crowi/express-init.js
  24. 0 20
      lib/crowi/helpers.js
  25. 62 85
      lib/crowi/index.js
  26. 3 6
      lib/events/page.js
  27. 1 1
      lib/events/user.js
  28. 1 1
      lib/form/revision.js
  29. 3 3
      lib/locales/en-US/sandbox.md
  30. 87 17
      lib/locales/en-US/translation.json
  31. 3 3
      lib/locales/ja/sandbox.md
  32. 73 6
      lib/locales/ja/translation.json
  33. 10 10
      lib/models/attachment.js
  34. 1 1
      lib/models/bookmark.js
  35. 2 2
      lib/models/comment.js
  36. 69 107
      lib/models/config.js
  37. 3 3
      lib/models/external-account.js
  38. 10 10
      lib/models/page-group-relation.js
  39. 140 132
      lib/models/page.js
  40. 9 6
      lib/models/revision.js
  41. 16 22
      lib/models/updatePost.js
  42. 7 7
      lib/models/user-group-relation.js
  43. 9 9
      lib/models/user-group.js
  44. 39 37
      lib/models/user.js
  45. 2 2
      lib/plugins/plugin-utils-v2.js
  46. 1 1
      lib/plugins/plugin-utils.js
  47. 3 4
      lib/plugins/plugin.service.js
  48. 98 81
      lib/routes/admin.js
  49. 10 9
      lib/routes/attachment.js
  50. 5 4
      lib/routes/bookmark.js
  51. 7 7
      lib/routes/comment.js
  52. 4 2
      lib/routes/index.js
  53. 4 3
      lib/routes/installer.js
  54. 11 14
      lib/routes/login-passport.js
  55. 45 39
      lib/routes/login.js
  56. 33 26
      lib/routes/me.js
  57. 52 45
      lib/routes/page.js
  58. 8 5
      lib/routes/revision.js
  59. 4 3
      lib/routes/search.js
  60. 8 6
      lib/routes/user.js
  61. 11 0
      lib/service/logger/alias-for-debug.js
  62. 48 0
      lib/service/logger/index.js
  63. 16 0
      lib/service/logger/stream.dev.js
  64. 20 0
      lib/service/logger/stream.prod.js
  65. 4 8
      lib/service/notification.js
  66. 5 5
      lib/service/passport.js
  67. 6 5
      lib/util/apiResponse.js
  68. 1 1
      lib/util/fileUploader.js
  69. 1 1
      lib/util/formUtil.js
  70. 1 1
      lib/util/googleAuth.js
  71. 9 9
      lib/util/interceptor-manager.js
  72. 12 11
      lib/util/mailer.js
  73. 17 12
      lib/util/middlewares.js
  74. 48 61
      lib/util/search.js
  75. 22 18
      lib/util/slack.js
  76. 49 45
      lib/util/swigFunctions.js
  77. 1 1
      lib/util/xss.js
  78. 1 2
      lib/views/admin/app.html
  79. 16 4
      lib/views/admin/customize.html
  80. 30 32
      lib/views/admin/security.html
  81. 1 1
      lib/views/admin/widget/passport/facebook.html
  82. 1 1
      lib/views/admin/widget/passport/github.html
  83. 1 1
      lib/views/admin/widget/passport/google-oauth.html
  84. 45 36
      lib/views/admin/widget/passport/ldap.html
  85. 1 1
      lib/views/admin/widget/passport/twitter.html
  86. 2 2
      lib/views/layout-crowi/page_list.html
  87. 2 2
      lib/views/layout-growi/page.html
  88. 2 2
      lib/views/layout-growi/page_list.html
  89. 2 2
      lib/views/layout-growi/user_page.html
  90. 7 2
      lib/views/layout/layout.html
  91. 15 11
      lib/views/modal/unportalize.html
  92. 3 3
      lib/views/widget/page_alerts.html
  93. 13 12
      local_modules/crowi-fileupload-aws/index.js
  94. 2 2
      local_modules/crowi-fileupload-local/index.js
  95. 1 1
      local_modules/crowi-fileupload-none/index.js
  96. 24 11
      package.json
  97. BIN=BIN
      public/images/icons/emacs.png
  98. BIN=BIN
      public/images/icons/sublime.png
  99. BIN=BIN
      public/images/icons/vim.png
  100. 4 6
      resource/js/app.js

+ 7 - 0
.eslintignore

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

+ 31 - 4
.eslintrc.js

@@ -5,7 +5,17 @@ module.exports = {
     "es6": true,
     "node": true
   },
-  "extends": "eslint:recommended",
+  "extends": [
+    "eslint:recommended",
+    "plugin:react/recommended"
+  ],
+  "globals": {
+    "$": true,
+    "jquery": true,
+    "emojione": true,
+    "hljs": true,
+    "window": true
+  },
   "parserOptions": {
     "ecmaFeatures": {
       "experimentalObjectRestSpread": true,
@@ -32,10 +42,20 @@ module.exports = {
     "indent": [
       "error",
       2,
-      { "SwitchCase": 1 }
+      {
+        "SwitchCase": 1,
+        "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", {}
@@ -44,13 +64,20 @@ module.exports = {
       "error",
       "unix"
     ],
+    "no-unused-vars": [
+      "error",
+      { "args": "none" }
+    ],
     "quotes": [
       "error",
       "single"
     ],
+    "react/jsx-uses-vars": 1,
+    "react/no-string-refs": "off",
     "semi": [
       "error",
-      "always"
+      "always",
+      { "omitLastInOneLineBlock": true }
     ],
     "space-before-blocks": [
       "error",

+ 1 - 0
.gitignore

@@ -14,6 +14,7 @@ Thumbs.db
 /bower_components/
 npm-debug.log
 /npm-debug.log.*
+package-lock.json
 
 # Dist #
 /report/

+ 34 - 1
CHANGES.md

@@ -1,7 +1,40 @@
 CHANGES
 ========
 
-## 3.0.11-RC
+## 3.0.14
+
+* Improvement: Auto-format markdown tables which includes multibyte text
+
+## 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
+
+* Feature: Support Vim/Emacs/Sublime-Text keybindings
+* Improvement: Add some CodeMirror themes (Eclipse, Dracula)
+* Improvement: Dynamic loading for CodeMirror theme files from CDN
+* Improvement: Prevent XSS when move/redirect/duplicate
+
+## 3.0.11
+
+* Fix: login.html is broken in iOS
+* Fix: Removing attachment is crashed
+* Fix: File-attaching error after new page creation
+* Support: Optimize development build
+* Support: Upgrade libs
+    * env-cmd
+    * googleapis
+    * sinon
 
 ## 3.0.10
 

+ 20 - 2
app.js

@@ -5,7 +5,25 @@
  * @author  Yuki Takei <yuki@weseek.co.jp>
  */
 
-var growi = new (require('./lib/crowi'))(__dirname, process.env);
+require('module-alias/register');
+
+const logger = require('@alias/logger')('growi');
+const growi = new (require('./lib/crowi'))(__dirname, process.env);
+
+
+/************************************
+ *          Main Process
+ ***********************************/
+process.on('uncaughtException', (err) => {
+  logger.error('Uncaught Exception: ', err);
+});
+
+process.on('unhandledRejection', (reason, p) => {
+  logger.error('Unhandled Rejection: Promise:', p, 'Reason:', reason);
+});
 
 growi.start()
-  .catch(growi.exitOnerror);
+  .catch(err => {
+    logger.error('An error occurred, unable to start the server');
+    logger.error(err);
+  });

+ 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')('crowi: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);
-
-  }).catch(crowi.exitOnError);
-
-
-//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')('crowi: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);
-  }).catch(crowi.exitOnError);

+ 2 - 17
config/env.dev.js

@@ -2,24 +2,9 @@ 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',
   ],
-  // filters for debug
-  DEBUG: [
-    // 'express:*',
-    // 'crowi:*',
-    'crowi:crowi',
-    // 'crowi:crowi:dev',
-    'crowi:crowi:express-init',
-    'crowi:models:external-account',
-    // 'crowi:routes:login',
-    'crowi:routes:login-passport',
-    'crowi:service:PassportService',
-    // 'crowi:routes:page',
-    // 'crowi:plugins:*',
-    // 'crowi:InterceptorManager',
-  ].join(),
-}
+};

+ 3 - 5
config/env.prod.js

@@ -1,6 +1,4 @@
 module.exports = {
-  // logging settings
-  DEBUG: [
-    'crowi:crowi',
-  ].join(),
-}
+  NODE_ENV: 'production',
+  // FORMAT_NODE_LOG: false,
+};

+ 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, '..');
 

+ 13 - 0
config/index.js

@@ -0,0 +1,13 @@
+function envShortName() {
+  switch (process.env.NODE_ENV) {
+    case 'production':
+      return 'prod';
+    default:
+      return 'dev';
+  }
+}
+
+module.exports = {
+  env: require(`./env.${envShortName()}`),
+  logger: require(`./logger/config.${envShortName()}`),
+};

+ 17 - 0
config/logger/config.dev.js

@@ -0,0 +1,17 @@
+module.exports = {
+  default: 'info',
+
+  //// configure level for name
+  // 'express:*': 'debug',
+  // 'growi:*': 'debug',
+  'growi:crowi': 'debug',
+  // 'growi:crow:dev': 'debug',
+  'growi:crowi:express-init': 'debug',
+  'growi:models:external-account': 'debug',
+  // 'growi:routes:login': 'debug',
+  'growi:routes:login-passport': 'debug',
+  'growi:service:PassportService': 'debug',
+  // 'growi:routes:page': 'debug',
+  // 'growi-plugin:*': 'debug',
+  // 'growi:InterceptorManager': 'debug',
+};

+ 3 - 0
config/logger/config.prod.js

@@ -0,0 +1,3 @@
+module.exports = {
+  default: 'info',
+};

+ 22 - 31
config/webpack.common.js

@@ -10,15 +10,13 @@ const helpers = require('./helpers');
  */
 const AssetsPlugin = require('assets-webpack-plugin');
 const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
 
 /*
  * Webpack configuration
  *
  * See: http://webpack.github.io/docs/configuration.html#cli
  */
-module.exports = function (options) {
-  isProd = options.env === 'production';
+module.exports = function(options) {
   return {
     entry: {
       'app':                  './resource/js/app',
@@ -31,24 +29,37 @@ module.exports = function (options) {
       'style-theme-default':  './resource/styles/scss/theme/default.scss',
       'style-theme-default-dark':  './resource/styles/scss/theme/default-dark.scss',
       'style-theme-nature':   './resource/styles/scss/theme/nature.scss',
+      'style-theme-mono-blue':   './resource/styles/scss/theme/mono-blue.scss',
       'style-presentation':   './resource/styles/scss/style-presentation.scss',
     },
     externals: {
       // require("jquery") is external and available
       //  on the global var jQuery
-      "jquery": "jQuery",
-      "emojione": "emojione",
-      "hljs": "hljs",
+      'jquery': 'jQuery',
+      'emojione': 'emojione',
+      'hljs': 'hljs',
     },
     resolve: {
       extensions: ['.js', '.json'],
       modules: [helpers.root('src'), helpers.root('node_modules')],
+      alias: {
+        '@root': helpers.root('/'),
+        '@alias/logger': helpers.root('lib/service/logger'),
+        // replace bunyan
+        'bunyan': 'browser-bunyan',
+      }
     },
     module: {
       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: {
@@ -56,26 +67,6 @@ module.exports = function (options) {
             }
           }]
         },
-        {
-          test: /\.scss$/,
-          use: ExtractTextPlugin.extract({
-            fallback: 'style-loader',
-            use: [
-              { loader: 'css-loader', options: {
-                sourceMap: !isProd,
-                minimize: isProd
-              } },
-              { loader: 'postcss-loader', options: {
-                sourceMap: !isProd,
-                plugins: (loader) => [
-                  require('autoprefixer')()
-                ]
-              } },
-              { loader: 'sass-loader', options: { sourceMap: !isProd } }
-            ]
-          }),
-          include: [helpers.root('resource/styles/scss')]
-        },
         {
           test: /\.css$/,
           use: ['style-loader', 'css-loader'],
@@ -96,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',
         }
       ]
@@ -127,10 +118,10 @@ module.exports = function (options) {
       new webpack.IgnorePlugin(/^\.\/lib\/deflate\.js/, /markdown-it-plantuml/),
 
       new webpack.ProvidePlugin({ // refs externals
-        jQuery: "jquery",
-        $: "jquery",
+        jQuery: 'jquery',
+        $: 'jquery',
       }),
 
     ]
   };
-}
+};

+ 34 - 11
config/webpack.dev.js

@@ -12,8 +12,6 @@ const commonConfig = require('./webpack.common.js');
 /*
  * Webpack Plugins
  */
-const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
 const DllBundlesPlugin = require('webpack-dll-bundles-plugin').DllBundlesPlugin;
 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
 
@@ -22,17 +20,15 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
  */
 const ANALYZE = process.env.ANALYZE;
 const ENV = process.env.ENV = process.env.NODE_ENV = 'development';
-const HOST = process.env.HOST || '0.0.0.0';
-const PORT = process.env.PORT || 3000;
 
 /*
  * Webpack configuration
  *
  * See: http://webpack.github.io/docs/configuration.html#cli
  */
-module.exports = function (options) {
+module.exports = function(options) {
   return webpackMerge(commonConfig({ env: ENV }), {
-    devtool: 'cheap-module-source-map',
+    devtool: 'cheap-module-eval-source-map',
     entry: {
       dev: './resource/js/dev',
     },
@@ -40,7 +36,6 @@ module.exports = function (options) {
       path: helpers.root('public/js'),
       publicPath: '/js/',
       filename: '[name].bundle.js',
-      sourceMapFilename: '[file].map',
     },
     resolve: {
       extensions: ['.js', '.json'],
@@ -48,17 +43,45 @@ module.exports = function (options) {
     },
     module: {
       rules: [
+        {
+          test: /\.css$/,
+          use: [
+            'style-loader',
+            { loader: 'css-loader', options: { sourceMap: true } },
+          ],
+          include: [helpers.root('resource/styles/scss')]
+        },
+        {
+          test: /\.scss$/,
+          use: [
+            'style-loader',
+            { loader: 'css-loader', options: { sourceMap: true } },
+            { loader: 'sass-loader', options: { sourceMap: true } },
+          ],
+          include: [helpers.root('resource/styles/scss')]
+        },
       ],
     },
     plugins: [
 
-      new ExtractTextPlugin('[name].bundle.css'),
-
       new DllBundlesPlugin({
         bundles: {
           vendor: [
+            'axios',
+            'codemirror',
+            'date-fns',
+            'diff',
+            'diff2html',
+            'jquery-ui',
+            'markdown-it',
+            'metismenu',
             'react',
-            'react-dom'
+            'react-dom',
+            'react-bootstrap',
+            'react-bootstrap-typeahead',
+            'react-dropzone',
+            'socket.io-client',
+            'toastr',
           ],
         },
         dllDir: helpers.root('public/dll'),
@@ -76,4 +99,4 @@ module.exports = function (options) {
 
     ]
   });
-}
+};

+ 22 - 5
config/webpack.prod.js

@@ -10,7 +10,6 @@ const commonConfig = require('./webpack.common.js'); // the settings that are co
 /**
  * Webpack Plugins
  */
-const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
 const ExtractTextPlugin = require('extract-text-webpack-plugin');
 const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
 const OptimizeJsPlugin = require('optimize-js-plugin');
@@ -21,10 +20,8 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
  */
 const ANALYZE = process.env.ANALYZE;
 const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
-const HOST = process.env.HOST || 'localhost';
-const PORT = process.env.PORT || 3000;
 
-module.exports = function (env) {
+module.exports = function(env) {
   return webpackMerge(commonConfig({ env: ENV }), {
     devtool: undefined,
     output: {
@@ -36,6 +33,26 @@ module.exports = function (env) {
     },
     module: {
       rules: [
+        {
+          test: /\.scss$/,
+          use: ExtractTextPlugin.extract({
+            fallback: 'style-loader',
+            use: [
+              { loader: 'css-loader', options: {
+                sourceMap: false,
+                minimize: true
+              } },
+              { loader: 'postcss-loader', options: {
+                sourceMap: false,
+                plugins: (loader) => [
+                  require('autoprefixer')()
+                ]
+              } },
+              { loader: 'sass-loader', options: { sourceMap: false } }
+            ]
+          }),
+          include: [helpers.root('resource/styles/scss')]
+        }
       ]
     },
     plugins: [
@@ -99,4 +116,4 @@ module.exports = function (env) {
     ],
 
   });
-}
+};

+ 5 - 5
lib/crowi/dev.js

@@ -1,11 +1,9 @@
-const debug = require('debug')('crowi:crowi:dev');
+const debug = require('debug')('growi:crowi:dev');
 const fs = require('fs');
 const path = require('path');
-const webpack = require('webpack');
-const helpers = require('./helpers');
 
 const swig = require('swig-templates');
-const onHeaders = require('on-headers')
+const onHeaders = require('on-headers');
 
 
 class CrowiDev {
@@ -29,7 +27,9 @@ class CrowiDev {
 
   initPromiseRejectionWarningHandler() {
     // https://qiita.com/syuilo/items/0800d7e44e93203c7285
+    /* eslint-disable no-console */
     process.on('unhandledRejection', console.dir);
+    /* eslint-enable */
   }
 
   initSwig() {
@@ -103,4 +103,4 @@ class CrowiDev {
   }
 }
 
-module.exports = CrowiDev
+module.exports = CrowiDev;

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

@@ -1,7 +1,7 @@
 'use strict';
 
 module.exports = function(crowi, app) {
-  var debug = require('debug')('crowi:crowi:express-init')
+  var debug = require('debug')('growi:crowi:express-init')
     , path           = require('path')
     , express        = require('express')
     , bodyParser     = require('body-parser')
@@ -9,6 +9,7 @@ module.exports = function(crowi, app) {
     , methodOverride = require('method-override')
     , passport       = require('passport')
     , session        = require('express-session')
+    , sanitizer      = require('express-sanitizer')
     , basicAuth      = require('basic-auth-connect')
     , flash          = require('connect-flash')
     , swig           = require('swig-templates')
@@ -69,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
 
@@ -81,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(
@@ -94,6 +95,7 @@ module.exports = function(crowi, app) {
   app.use(methodOverride());
   app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
   app.use(bodyParser.json({limit: '50mb'}));
+  app.use(sanitizer());
   app.use(cookieParser());
   app.use(session(crowi.sessionConfig));
 
@@ -107,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());
   }

+ 0 - 20
lib/crowi/helpers.js

@@ -1,20 +0,0 @@
-/**
- * @author: @AngularClass
- * @author: Yuki Takei <yuki@weseek.co.jp>
- */
-
-var path = require('path');
-
-const EVENT = process.env.npm_lifecycle_event || '';
-
-// Helper functions
-var ROOT = path.resolve(__dirname, '..');
-
-function hasProcessFlag(flag) {
-  return process.argv.join('').indexOf(flag) > -1;
-}
-
-var root = path.join.bind(path, ROOT);
-
-exports.hasProcessFlag = hasProcessFlag;
-exports.root = root;

+ 62 - 85
lib/crowi/index.js

@@ -1,27 +1,22 @@
 'use strict';
 
 
-var debug = require('debug')('crowi:crowi')
-  , pkg = require('../../package.json')
+var debug = require('debug')('growi:crowi')
+  , logger = require('@alias/logger')('growi:crowi')
+  , pkg = require('@root/package.json')
   , path = require('path')
-  , fs = require('fs')
   , sep = path.sep
 
   , mongoose    = require('mongoose')
 
   , eazyLogger = require('eazy-logger')
-  , helpers = require('./helpers')
   , models = require('../models')
   ;
 
-function Crowi (rootdir, env)
-{
+function Crowi(rootdir, env) {
   var self = this;
-  // this.logger = easyLogger.Logger({
-  //   prefix: '[{green:GROWI}]'
-  // });
   this.logger = eazyLogger.Logger({
-    prefix: "[{green:GROWI}] ",
+    prefix: '[{green:GROWI}] ',
     useLevelPrefixes: false,
   });
 
@@ -52,9 +47,6 @@ function Crowi (rootdir, env)
 
   this.env = env;
   this.node_env = this.env.NODE_ENV || 'development';
-  if (helpers.hasProcessFlag('prod') || helpers.hasProcessFlag('production')) {
-    this.node_env = process.env.NODE_ENV = 'production';
-  }
 
   this.port = this.env.PORT || 3000;
 
@@ -63,9 +55,7 @@ function Crowi (rootdir, env)
     page: new (require(self.eventsDir + 'page'))(this),
   };
 
-  if (this.node_env == 'development') {
-  }
-};
+}
 
 function getMongoUrl(env) {
   return env.MONGOLAB_URI || // for B.C.
@@ -79,31 +69,31 @@ Crowi.prototype.init = function() {
   var self = this;
 
   return Promise.resolve()
-  .then(function() {
-    // setup database server and load all modesl
-    return self.setupDatabase();
-  }).then(function() {
-    return self.setupModels();
-  }).then(function() {
-    return self.setupSessionConfig();
-  }).then(function() {
-    return self.setupAppConfig();
-  }).then(function() {
-    return self.scanRuntimeVersions();
-  }).then(function() {
-    return self.setupPassport();
-  }).then(function() {
-    return self.setupSearcher();
-  }).then(function() {
-    return self.setupMailer();
-  }).then(function() {
-    return self.setupInterceptorManager();
-  }).then(function() {
-    return self.setupSlack();
-  }).then(function() {
-    return self.setupCsrf();
-  });
-}
+    .then(function() {
+      // setup database server and load all modesl
+      return self.setupDatabase();
+    }).then(function() {
+      return self.setupModels();
+    }).then(function() {
+      return self.setupSessionConfig();
+    }).then(function() {
+      return self.setupAppConfig();
+    }).then(function() {
+      return self.scanRuntimeVersions();
+    }).then(function() {
+      return self.setupPassport();
+    }).then(function() {
+      return self.setupSearcher();
+    }).then(function() {
+      return self.setupMailer();
+    }).then(function() {
+      return self.setupInterceptorManager();
+    }).then(function() {
+      return self.setupSlack();
+    }).then(function() {
+      return self.setupCsrf();
+    });
+};
 
 Crowi.prototype.isPageId = function(pageId) {
   if (!pageId) {
@@ -157,8 +147,8 @@ Crowi.prototype.setupDatabase = function() {
   return mongoose.connect(mongoUri).then(
     () => {},
     err => {
-      debug('DB Connect Error: ', err);
-      debug('DB Connect Error: ', mongoUri);
+      logger.error('DB Connect Error: ', err);
+      logger.error('DB Connect Error: ', mongoUri);
     }
   );
 };
@@ -217,7 +207,7 @@ Crowi.prototype.setupAppConfig = function() {
       return resolve();
     });
   });
-}
+};
 
 Crowi.prototype.setupModels = function() {
   var self = this
@@ -248,9 +238,9 @@ Crowi.prototype.scanRuntimeVersions = function() {
       }
       self.runtimeVersions = result;
       resolve();
-    })
+    });
   });
-}
+};
 
 Crowi.prototype.getSearcher = function() {
   return this.searcher;
@@ -262,7 +252,7 @@ Crowi.prototype.getMailer = function() {
 
 Crowi.prototype.getInterceptorManager = function() {
   return this.interceptorManager;
-}
+};
 
 Crowi.prototype.setupPassport = function() {
   const config = this.getConfig();
@@ -286,7 +276,7 @@ Crowi.prototype.setupPassport = function() {
   this.passportService.setupLdapStrategy();
 
   return Promise.resolve();
-}
+};
 
 Crowi.prototype.setupSearcher = function() {
   var self = this;
@@ -299,8 +289,9 @@ Crowi.prototype.setupSearcher = function() {
     if (searcherUri) {
       try {
         self.searcher = new (require(path.join(self.libDir, 'util', 'search')))(self, searcherUri);
-      } catch (e) {
-        debug('Error on setup searcher', e);
+      }
+      catch (e) {
+        logger.error('Error on setup searcher', e);
         self.searcher = null;
       }
     }
@@ -333,7 +324,8 @@ Crowi.prototype.setupSlack = function() {
   return new Promise(function(resolve, reject) {
     if (!Config.hasSlackConfig(config)) {
       self.slack = {};
-    } else {
+    }
+    else {
       self.slack = require('../util/slack')(self);
     }
 
@@ -343,7 +335,7 @@ Crowi.prototype.setupSlack = function() {
 
 Crowi.prototype.setupCsrf = function() {
   var Tokens = require('csrf');
-  var tokens = this.tokens = new Tokens();
+  this.tokens = new Tokens();
 
   return Promise.resolve();
 };
@@ -367,19 +359,19 @@ Crowi.prototype.start = function() {
 
   return Promise.resolve()
     .then(function() {
-      return self.init()
+      return self.init();
     })
     .then(function() {
       return self.buildServer();
     })
     .then(function(app) {
       server = http.createServer(app).listen(self.port, function() {
-        debug(`[${self.node_env}] Express server listening on port ${self.port}`);
+        logger.info(`[${self.node_env}] Express server listening on port ${self.port}`);
 
         self.logger.info('{bold:Server URLs:}');
-        self.logger.unprefixed('info','{grey:=======================================}');
-        self.logger.unprefixed('info',`         APP: {magenta:http://localhost:${self.port}}`);
-        self.logger.unprefixed('info','{grey:=======================================}');
+        self.logger.unprefixed('info', '{grey:=======================================}');
+        self.logger.unprefixed('info', `         APP: {magenta:http://localhost:${self.port}}`);
+        self.logger.unprefixed('info', '{grey:=======================================}');
 
         // setup for dev
         if (self.node_env === 'development') {
@@ -388,7 +380,7 @@ Crowi.prototype.start = function() {
       });
 
       io = require('socket.io')(server);
-      io.sockets.on('connection', function (socket) {
+      io.sockets.on('connection', function(socket) {
       });
       self.io = io;
 
@@ -421,28 +413,21 @@ Crowi.prototype.buildServer = function() {
     }
   }
 
-  if (env == 'development') {
-    //swig.setDefaults({ cache: false });   // moved to dev.js -- 2017.07.09 Yuki Takei
+  // use bunyan
+  if (env == 'production') {
+    const expressBunyanLogger = require('express-bunyan-logger');
+    const logger = require('@alias/logger')('express');
+    app.use(expressBunyanLogger({
+      logger,
+      excludes: ['*'],
+    }));
+  }
+  // use morgan
+  else {
     const morgan = require('morgan');
     app.use(morgan('dev'));
   }
 
-  if (env == 'production') {
-    /*
-     * commented out morgan because of using pino  -- 2017.06.27 Yuki Takei
-     *
-    var oneYear = 31557600000;
-    app.use(morgan('combined'));
-    app.use(function (err, req, res, next) {
-      res.status(500);
-      res.render('500', { error: err });
-    });
-     */
-    const pino = require('pino')({extreme: true}, process.stdout);
-    const expressPino = require('express-pino-logger')({pino});
-    app.use(expressPino);
-  }
-
   return Promise.resolve(app);
 };
 
@@ -452,7 +437,7 @@ Crowi.prototype.buildServer = function() {
  */
 Crowi.prototype.setupRoutesAtLast = function(app) {
   require('../routes')(this, app);
-}
+};
 
 /**
  * require API for plugins
@@ -464,14 +449,6 @@ Crowi.prototype.setupRoutesAtLast = function(app) {
  */
 Crowi.prototype.require = function(modulePath) {
   return require(modulePath);
-}
-
-Crowi.prototype.exitOnError = function(err) {
-  debug('Critical error occured.');
-  console.error(err);
-  console.error(err.stack);
-  process.exit(1);
 };
 
-
 module.exports = Crowi;

+ 3 - 6
lib/events/page.js

@@ -1,4 +1,4 @@
-var debug = require('debug')('crowi:events:page');
+var debug = require('debug')('growi:events:page');
 var util = require('util');
 var events = require('events');
 
@@ -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/events/user.js

@@ -1,4 +1,4 @@
-var debug = require('debug')('crowi:events:user');
+var debug = require('debug')('growi:events:user');
 var util = require('util');
 var events = require('events');
 

+ 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')

+ 3 - 3
lib/locales/en-US/sandbox.md

@@ -232,7 +232,7 @@ ___
 
 ```
 [/Sandbox]
-</user/admin1>
+&lt;/user/admin1>
 ```
 
 [/Sandbox]  
@@ -240,8 +240,8 @@ ___
 
 ## Pukiwiki like linker
 
-(available by [weseek/crowi-plugin-pukiwiki-like-linker
-](https://github.com/weseek/crowi-plugin-pukiwiki-like-linker) )
+(available by [weseek/growi-plugin-pukiwiki-like-linker
+](https://github.com/weseek/growi-plugin-pukiwiki-like-linker) )
 
 最も柔軟な Linker です。  
 記述中のページを基点とした相対リンクと、表示テキストに対するリンクを同時に実現できます。

+ 87 - 17
lib/locales/en-US/translation.json

@@ -253,7 +253,7 @@
     "No_SMTP_setting": "If you do not have SMTP settings, e-mails will be sent via SES. You need to verify from e-mail address and production settings.",
     "change_setting": "Caution:if you change this setting not completed, you will not be able to access files you have uploaded so far.",
     "region": "Region",
-    "Packet name": "Packet name",
+    "bucket name": "Bucket name",
     "Plugin settings": "Plugin settings",
     "Enable plugin loading": "Enable plugin loading",
     "Load plugins": "Load plugins",
@@ -262,23 +262,93 @@
 
   },
   "security_setting": {
-    "Basic authentication": "Basic authentication",
-    "Security settings": "Security settings",
-    "Guest users access": "Guest users access",
-    "Register limitation": "Register limitation",
-    "The whitelist of registration permission E-mail address": "The whitelist of registration permission E-mail address",
-    "Selecting authentication mechanism": "Selecting authentication mechanism",
-    "common_authentication": "If you set the basic authentication, common authentication is applied on the whole page." ,
-    "without_encryption": "Please be noted that your ID and Password will be sent wihtout encryption.",
-    "users_without_account": "Users without account is not accessible",
+		"Basic authentication": "Basic authentication",
+		"Security settings": "Security settings",
+		"Guest users access": "Guest users access",
+		"Register limitation": "Register limitation",
+		"The whitelist of registration permission E-mail address": "The whitelist of registration permission E-mail address",
+		"Selecting authentication mechanism": "Selecting authentication mechanism",
+		"common_authentication": "If you set the basic authentication, common authentication is applied on the whole page.",
+		"without_encryption": "Please be noted that your ID and Password will be sent wihtout encryption.",
+		"users_without_account": "Users without account is not accessible",
+    "example": "Example",
     "restrict_emails": "You can restrict registerable e-mail address.",
-    "for_instance":" For instance, if you use growi within a company, you can write ",
-    "only_those":" Only those whose e-mail address including the company address can register.",
-    "insert_single":"Please insert single e-mail address per line.",
-    "Authentication mechanism settings":"Authentication mechanism settings",
-    "Treat username matching as identical": "Automatically bind external accounts newly logged in to local accounts when <code>username</code> match",
-    "Treat username matching as identical_warn": "WARNING: Be aware of security because the system treats the same user as a match of <code>username</code>."
-  },
+		"for_instance": " For instance, if you use growi within a company, you can write ",
+		"only_those": " Only those whose e-mail address including the company address can register.",
+		"insert_single": "Please insert single e-mail address per line.",
+		"Authentication mechanism settings": "Authentication mechanism settings",
+    "note": "Note",
+    "require_server_restart_change_auth": "Restarting the server is required if you switch the auth mechanism.",
+    "auth_mechanism": "authentication mechanism",
+    "recommended": "Recommended",
+    "username_email_password": "Username, Email and Password authentication",
+    "ldap_auth": "LDAP authentication",
+    "google_auth2": "Google OAuth2 authentication",
+    "facebook_auth2": "Facebook OAuth2 authentication",
+    "twitter_auth2": "Twitter OAuth authentication",
+    "github_auth2": "Github OAuth2 authentication",
+    "crowi_auth": "Crowi classic authentication mechanism",
+		"require_server_restart": "Restarting the server is required.",
+		"server_on_passport_auth": "The server is running with Passport authentication mechanism.",
+		"server_on_crowi_auth": "The server is running with official Crowi authentication mechanism.",
+		"google_setting": "Google Setting",
+    "connect_api_manager": "You can use your Google account to sign up and login after creating OAuth2 ClientId at <a href=\"https://console.cloud.google.com/apis/credentials\">API Manager on Google Cloud Platform</a>",
+		"access_api_manager": "Access <a href=\"https://console.cloud.google.com/apis/credentials\">API Manager</a>",
+		"create_project": "Create Project if no projects have been created.",
+		"create_auth_to_oauth": "\"Create credentials\" -> \"OAuth clientID\"",
+		"select_webapp": "Select \"Web Application\"",
+    "change_redirect_url": "Enter <code>https://${crowi.host}/google/callback</code> <br>(where < code > $ {crowi.host} < /code> is your host name) for \"Authorized redirect URIs\".",
+    "clientID": "Client ID",
+    "client_secret": "クライアントシークレット",
+    "guest_mode": {
+      "deny": "Deny Unregistered Users",
+      "readonly": "View Only"
+    },
+    "registration_mode": {
+      "open": "Anyone",
+      "restricted": "Reuqire Admin permission",
+      "closed": "Invitation Only"
+    },
+    "configuration": "Configuration",
+    "optional": "Optional",
+    "ldap": {
+      "server_url_detail": "The LDAP URL of the directory service in the format <code>ldap://host:port/DN</code> or <code>ldaps://host:port/DN</code>.",
+      "bind_mode": "Binding Mode",
+      "bind_manager": "Manager Bind",
+      "bind_user": "User Bind",
+      "bind_DN_manager_detail": "The DN of the account that authenticates and queries the directory service",
+      "bind_DN_user_detail1": "The query used to bind with the directory service.",
+      "bind_DN_user_detail2": "Use <code>&#123;&#123;username&#125;&#125;</code> to reference the username entered in the login page.",
+      "bind_DN_password": "Bind DN Password",
+      "bind_DN_password_manager_detail": "The password for the Bind DN account.",
+      "bind_DN_password_user_detail": "The password that is entered in the login page will be used to bind.",
+      "search_filter": "Search Filter",
+      "search_filter_detail1": "The query used to locate the authenticated user.",
+      "search_filter_detail2": "Use <code>&#123;&#123;username&#125;&#125;</code> to reference the username entered in the login page.",
+      "search_filter_detail3": "If empty, the filter <code>(uid=&#123;&#123;username&#125;&#125;)</code> is used.",
+      "search_filter_example": "Example to match with 'uid' or 'mail'",
+      "username_detail": "Specification of mappings when creating new users",
+      "Treat username matching as identical": "Automatically bind external accounts newly logged in to local accounts when <code>username</code> match",
+  		"Treat username matching as identical_warn": "WARNING: Be aware of security because the system treats the same user as a match of <code>username</code>.",
+      "group_search_base_DN": "Group Search Base DN",
+      "group_search_base_DN_detail": "The base DN from which to search for groups. If defined, also <code>Group Search Filter</code> must be defined for the search to work.",
+      "group_search_filter": "Group Search Filter",
+      "group_search_filter_detail1": "The query used to filter for groups.",
+      "group_search_filter_detail2": "Use <code>&#123;&#123;dn&#125;&#125;</code> to have it replaced of the found user object.",
+      "group_search_filter_detail3": "<code>(&(cn=group1)(memberUid=&#123;&#123;dn&#125;&#125;))</code> hits the groups which has <code>cn=group1</code> and <code>memberUid</code> includes the user's <code>uid</code>(when <code>Group DN Property</code> is not changed from the default value.)",
+      "group_search_user_DN_property": "User DN Property",
+      "group_search_user_DN_property_detail": "The property of user object to use in <code>&#123;&#123;dn&#125;&#125;</code> interpolation of <code>Group Search Filter</code>.",
+      "test_config": "Test Saved Configuration"
+    },
+    "Google OAuth": {
+    },
+    "Facebook": {
+    },
+    "Twitter": {
+    },
+    "Github": {
+    }
+	},
 
   "markdown_setting": {
     "markdown_rendering": "You can change Markdown rendering settings.",

+ 3 - 3
lib/locales/ja/sandbox.md

@@ -232,7 +232,7 @@ ___
 
 ```
 [/Sandbox]
-</user/admin1>
+&lt;/user/admin1>
 ```
 
 [/Sandbox]  
@@ -240,8 +240,8 @@ ___
 
 ## Pukiwiki like linker
 
-(available by [weseek/crowi-plugin-pukiwiki-like-linker
-](https://github.com/weseek/crowi-plugin-pukiwiki-like-linker) )
+(available by [weseek/growi-plugin-pukiwiki-like-linker
+](https://github.com/weseek/growi-plugin-pukiwiki-like-linker) )
 
 最も柔軟な Linker です。  
 記述中のページを基点とした相対リンクと、表示テキストに対するリンクを同時に実現できます。

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

@@ -272,7 +272,7 @@
     "No_SMTP_setting": "また、SMTP の設定が無い場合、SES を利用したメール送信が行われます。FromメールアドレスのVerify、プロダクション利用設定をする必要があります。",
     "change_setting": "この設定を途中で変更すると、これまでにアップロードしたファイル等へのアクセスができなくなりますのでご注意下さい。",
     "region": "リージョン",
-    "Packet name": "パケット名",
+    "bucket name": "バケット名",
     "Plugin settings": "プラグイン設定",
     "Enable plugin loading": "プラグインの読み込みを有効にします。",
     "Load plugins": "プラグインを読み込む",
@@ -291,13 +291,81 @@
     "common_authentication": "Basic認証を設定すると、ページ全体に共通の認証がかかります。",
     "without_encryption": "IDとパスワードは暗号化されずに送信されるのでご注意下さい。",
     "users_without_account": "アカウントを持たないユーザーはアクセス不可",
+    "example": "例",
     "restrict_emails": "登録可能なメールアドレスを制限することができます。",
-    "for_instance":"例えば、会社で使う場合 などと記載すると、",
-    "only_those":"その会社のメールアドレスを持っている人のみ登録可能になります。",
+    "for_instance":"例えば、",
+    "only_those":"と記載することで、そのドメインのメールアドレスを持っている人のみ登録可能になります。",
     "insert_single":"1行に1メールアドレス入力してください。",
     "Authentication mechanism settings":"認証機構設定",
-    "Treat username matching as identical": "新規ログイン時、<code>username</code> が一致したローカルアカウントが存在した場合は自動的に紐付ける",
-    "Treat username matching as identical_warn": "WARNING: <code>username</code> の一致を以て同一ユーザーであるとみなすので、セキュリティに注意してください"
+    "note": "メモ",
+    "require_server_restart_change_auth": "認証機構の変更後はサーバーを再起動してください。",
+    "auth_mechanism": "認証機構",
+    "recommended": "推奨",
+    "username_email_password": "ユーザー名、Eメール、パスワードでの認証",
+    "ldap_auth": "LDAP 認証",
+    "google_auth2": "Google OAuth2 認証",
+    "facebook_auth2": "Facebook OAuth2 認証",
+    "twitter_auth2": "Twitter OAuth 認証",
+    "github_auth2": "Github OAuth2 認証",
+    "require_server_restart": "サーバーを再起動してください。",
+    "server_on_passport_auth": "Passport 認証機構でサーバーが稼働しています。",
+    "server_on_crowi_auth": "Crowi Classic 認証機構でサーバーが稼働しています。",
+    "google_setting": "Google 設定",
+    "connect_api_manager": "Google Cloud Platform の <a href=\"https://console.cloud.google.com/apis/credentials\">API Manager</a>から OAuth2 Client ID を作成すると、Google アカウントにコネクトして登録やログインが可能になります。",
+    "access_api_manager": "<a href=\"https://console.cloud.google.com/apis/credentials\">API Manager</a> へアクセス",
+    "create_project": "プロジェクトを作成していない場合は作成してください",
+    "create_auth_to_oauth": "「認証情報を作成」-> OAuthクライアントID",
+    "select_webapp": "「ウェブアプリケーション」を選択",
+    "change_redirect_url": "承認済みのリダイレクトURLに、 <code>https://${crowi.host}/google/callback</code> を入力<br>(<code>${crowi.host}</code>は環境に合わせて変更してください)",
+    "clientID": "クライアントID",
+    "client_secret": "クライアントシークレット",
+    "guest_mode": {
+      "deny": "アカウントを持たないユーザーはアクセス不可",
+      "readonly": "閲覧のみ許可"
+    },
+    "registration_mode": {
+      "open": "公開 (だれでも登録可能)",
+      "restricted": "制限 (登録完了には管理者の承認が必要)",
+      "closed": "非公開 (登録には管理者による招待が必要)"
+    },
+    "configuration": "コンフィギュレーション",
+    "optional": "オプション",
+    "ldap": {
+      "server_url_detail": "LDAP URLを <code>ldap://host:port/DN</code> または <code>ldaps://host:port/DN</code> の形式で入力してください。",
+      "bind_mode": "Bind モード",
+      "bind_manager": "管理者 Bind",
+      "bind_user": "ユーザー Bind",
+      "bind_DN_manager_detail": "ディレクトリーサービスに認証する際のアカウント DN",
+      "bind_DN_user_detail1": "ディレクトリーサービスに Bind するアカウント DN を決定するためのクエリ",
+      "bind_DN_user_detail2": "ログイン時に入力されるユーザー名を使用するには <code>&#123;&#123;username&#125;&#125;</code> の形式を使用してください。",
+      "bind_DN_password": "Bind DN パスワード",
+      "bind_DN_password_manager_detail": "Bind DN アカウントのパスワード",
+      "bind_DN_password_user_detail": "ログイン時のパスワードが使用されます。",
+      "search_filter": "検索フィルター",
+      "search_filter_detail1": "認証されるユーザーを一意に決定するための LDAP フィルタ",
+      "search_filter_detail2": "ログイン時のユーザー名を使用するには <code>&#123;&#123;username&#125;&#125;</code> の形式を使用してください。",
+      "search_filter_detail3": "空欄の場合 <code>(uid=&#123;&#123;username&#125;&#125;)</code> が使用されます。",
+      "search_filter_example": "'uid' または 'mail' に一致させる場合の例",
+      "username_detail": "新規ユーザーの関連付けを設定",
+      "Treat username matching as identical": "新規ログイン時、<code>username</code> が一致したローカルアカウントが存在した場合は自動的に紐付ける",
+      "Treat username matching as identical_warn": "WARNING: <code>username</code> の一致を以て同一ユーザーであるとみなすので、セキュリティに注意してください",
+      "group_search_base_DN": "グループ検索ベース DN",
+      "group_search_base_DN_detail": "グループ検索を実行するベース DN。利用する場合は <code>グループ検索フィルター</code> も入力する必要があります。",
+      "group_search_filter": "グループ検索フィルター",
+      "group_search_filter_detail1": "グループフィルターに用いるクエリ",
+      "group_search_filter_detail2": "ログイン対象ユーザーオブジェクトのプロパティーで置換する場合は <code>&#123;&#123;dn&#125;&#125;</code> を用いてください。",
+      "group_search_filter_detail3": "<code>(&(cn=group1)(memberUid=&#123;&#123;dn&#125;&#125;))</code> は <code>cn=group1</code> と、ユーザーの <code>uid</code> を含む <code>memberUid</code> を持つグループにヒットします(<code>ユーザーの DN プロパティー</code> がデフォルトから変更されていない場合)",
+      "group_search_user_DN_property": "ユーザーの DN プロパティー",
+      "group_search_user_DN_property_detail": "<code>グループ検索フィルター</code> 内の <code>&#123;&#123;dn&#125;&#125;</code> で置換される、ユーザーオブジェクトのプロパティー"
+    },
+    "Google OAuth": {
+    },
+    "Facebook": {
+    },
+    "Twitter": {
+    },
+    "Github": {
+    }
   },
   "markdown_setting": {
     "markdown_rendering": "Markdownレンダリングの設定を変更できます。",
@@ -351,4 +419,3 @@
   }
 
 }
-

+ 10 - 10
lib/models/attachment.js

@@ -1,11 +1,12 @@
 module.exports = function(crowi) {
-  var debug = require('debug')('crowi:models:attachment')
+  var debug = require('debug')('growi:models:attachment')
     , 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/bookmark.js

@@ -1,5 +1,5 @@
 module.exports = function(crowi) {
-  var debug = require('debug')('crowi:models:bookmark')
+  var debug = require('debug')('growi:models:bookmark')
     , mongoose = require('mongoose')
     , ObjectId = mongoose.Schema.Types.ObjectId
     , bookmarkSchema;

+ 2 - 2
lib/models/comment.js

@@ -1,5 +1,5 @@
 module.exports = function(crowi) {
-  var debug = require('debug')('crowi:models:comment')
+  var debug = require('debug')('growi:models:comment')
     , mongoose = require('mongoose')
     , ObjectId = mongoose.Schema.Types.ObjectId
     , USER_PUBLIC_FIELDS = '_id image isGravatarEnabled googleId name username email status createdAt' // TODO: どこか別の場所へ...
@@ -110,7 +110,7 @@ module.exports = function(crowi) {
         }
 
         resolve(done);
-      });;
+      });
     });
   };
 

+ 69 - 107
lib/models/config.js

@@ -1,8 +1,7 @@
 module.exports = function(crowi) {
   var mongoose = require('mongoose')
-    , debug = require('debug')('crowi:models:config')
+    , 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,30 +112,27 @@ module.exports = function(crowi) {
     return config.crowi[key];
   }
 
-  configSchema.statics.getRestrictGuestModeLabels = function()
-  {
+  configSchema.statics.getRestrictGuestModeLabels = function() {
     var labels = {};
-    labels[SECURITY_RESTRICT_GUEST_MODE_DENY]     = 'アカウントを持たないユーザーはアクセス不可';
-    labels[SECURITY_RESTRICT_GUEST_MODE_READONLY] = '閲覧のみ許可';
+    labels[SECURITY_RESTRICT_GUEST_MODE_DENY]     = 'security_setting.guest_mode.deny';
+    labels[SECURITY_RESTRICT_GUEST_MODE_READONLY] = 'security_setting.guest_mode.readonly';
 
     return labels;
   };
 
-  configSchema.statics.getRegistrationModeLabels = function()
-  {
+  configSchema.statics.getRegistrationModeLabels = function() {
     var labels = {};
-    labels[SECURITY_REGISTRATION_MODE_OPEN]       = '公開 (だれでも登録可能)';
-    labels[SECURITY_REGISTRATION_MODE_RESTRICTED] = '制限 (登録完了には管理者の承認が必要)';
-    labels[SECURITY_REGISTRATION_MODE_CLOSED]     = '非公開 (登録には管理者による招待が必要)';
+    labels[SECURITY_REGISTRATION_MODE_OPEN]       = 'security_setting.registration_mode.open';
+    labels[SECURITY_REGISTRATION_MODE_RESTRICTED] = 'security_setting.registration_mode.restricted';
+    labels[SECURITY_REGISTRATION_MODE_CLOSED]     = 'security_setting.registration_mode.closed';
 
     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();
 
@@ -506,7 +468,7 @@ module.exports = function(crowi) {
     };
 
     return local_config;
-  }
+  };
 
   /*
   configSchema.statics.isInstalled = function(config)

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

@@ -1,4 +1,4 @@
-const debug = require('debug')('crowi:models:external-account');
+const debug = require('debug')('growi:models:external-account');
 const mongoose = require('mongoose');
 const mongoosePaginate = require('mongoose-paginate');
 const uniqueValidator = require('mongoose-unique-validator');
@@ -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);
-}
+};

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

@@ -1,4 +1,4 @@
-const debug = require('debug')('crowi:models:pageGroupRelation');
+const debug = require('debug')('growi:models:pageGroupRelation');
 const mongoose = require('mongoose');
 const mongoosePaginate = require('mongoose-paginate');
 const ObjectId = mongoose.Schema.Types.ObjectId;
@@ -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;
@@ -164,11 +164,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;
@@ -198,7 +198,7 @@ class PageGroupRelation {
       relatedGroup: userGroup.id,
       targetPage: page.id,
     });
-  };
+  }
 
   /**
    * remove all relation for UserGroup
@@ -273,8 +273,8 @@ class PageGroupRelation {
   }
 }
 
-module.exports = function (crowi) {
+module.exports = function(crowi) {
   PageGroupRelation.crowi = crowi;
   schema.loadClass(PageGroupRelation);
   return mongoose.model('PageGroupRelation', schema);
-}
+};

+ 140 - 132
lib/models/page.js

@@ -1,5 +1,5 @@
 module.exports = function(crowi) {
-  var debug = require('debug')('crowi:models:page')
+  var debug = require('debug')('growi:models:page')
     , mongoose = require('mongoose')
     , escapeStringRegexp = require('escape-string-regexp')
     , ObjectId = mongoose.Schema.Types.ObjectId
@@ -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')('crowi: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})

+ 16 - 22
lib/models/updatePost.js

@@ -2,9 +2,10 @@
  * This is the setting for notify to 3rd party tool (like Slack).
  */
 module.exports = function(crowi) {
-  var debug = require('debug')('crowi:models:updatePost')
+  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) {

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

@@ -1,4 +1,4 @@
-const debug = require('debug')('crowi:models:userGroupRelation');
+const debug = require('debug')('growi:models:userGroupRelation');
 const mongoose = require('mongoose');
 const mongoosePaginate = require('mongoose-paginate');
 const ObjectId = mongoose.Schema.Types.ObjectId;
@@ -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);
-}
+};

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

@@ -1,4 +1,4 @@
-const debug = require('debug')('crowi:models:userGroup');
+const debug = require('debug')('growi:models:userGroup');
 const mongoose = require('mongoose');
 const mongoosePaginate = require('mongoose-paginate');
 const ObjectId = mongoose.Schema.Types.ObjectId;
@@ -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);
-}
+};
 

+ 39 - 37
lib/models/user.js

@@ -1,5 +1,5 @@
 module.exports = function(crowi) {
-  var debug = require('debug')('crowi:models:user')
+  var debug = require('debug')('growi:models:user')
     , mongoose = require('mongoose')
     , mongoosePaginate = require('mongoose-paginate')
     , uniqueValidator = require('mongoose-unique-validator')
@@ -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;

+ 3 - 4
lib/plugins/plugin.service.js

@@ -1,4 +1,4 @@
-const debug = require('debug')('crowi:plugins:PluginService');
+const logger = require('@alias/logger')('growi:plugins:PluginService');
 const PluginUtils = require('./plugin-utils');
 
 class PluginService {
@@ -34,12 +34,11 @@ class PluginService {
     switch (meta.pluginSchemaVersion) {
       // v1 is deprecated
       case 1:
-        console.warn('pluginSchemaVersion 1 is deprecated');
-        debug('pluginSchemaVersion 1 is deprecated');
+        logger.warn('pluginSchemaVersion 1 is deprecated');
         break;
       // v2 or above
       default:
-        debug(`load plugin '${definition.name}'`);
+        logger.info(`load plugin '${definition.name}'`);
         definition.entries.forEach((entryPath) => {
           const entry = require(entryPath);
           entry(this.crowi, this.app);

+ 98 - 81
lib/routes/admin.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routes:admin')
+  var debug = require('debug')('growi:routes:admin')
     , fs = require('fs')
     , models = crowi.models
     , Page = models.Page
@@ -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'],

+ 10 - 9
lib/routes/attachment.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routs:attachment')
+  var debug = require('debug')('growi:routs:attachment')
     , Attachment = crowi.model('Attachment')
     , User = crowi.model('User')
     , Page = crowi.model('Page')
@@ -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)

+ 5 - 4
lib/routes/bookmark.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routes:bookmark')
+  var debug = require('debug')('growi:routes:bookmark')
     , Bookmark = crowi.model('Bookmark')
     , Page = crowi.model('Page')
     , User = crowi.model('User')
@@ -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)

+ 7 - 7
lib/routes/comment.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routs:comment')
+  var debug = require('debug')('growi:routs:comment')
     , Comment = crowi.model('Comment')
     , User = crowi.model('User')
     , Page = crowi.model('Page')
@@ -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);

+ 4 - 3
lib/routes/installer.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routes:installer')
+  var debug = require('debug')('growi:routes:installer')
     , path = require('path')
     , fs = require('graceful-fs')
     , models = crowi.models
@@ -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');
     }
   };

+ 11 - 14
lib/routes/login-passport.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routes:login-passport')
+  var debug = require('debug')('growi:routes:login-passport')
     , passport = require('passport')
     , config = crowi.getConfig()
     , Config = crowi.model('Config')
@@ -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')('crowi: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', {
       });
     }

+ 33 - 26
lib/routes/me.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routes:me')
+  var debug = require('debug')('growi:routes:me')
     , fs = require('fs')
     , models = crowi.models
     , config = crowi.getConfig()
@@ -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);

+ 52 - 45
lib/routes/page.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routes:page')
+  var debug = require('debug')('growi:routes:page')
     , Page = crowi.model('Page')
     , User = crowi.model('User')
     , Config   = crowi.model('Config')
@@ -54,7 +54,8 @@ module.exports = function(crowi, app) {
 
     if (length < limit) {
       next = null;
-    } else {
+    }
+    else {
       next = offset + limit;
     }
 
@@ -77,7 +78,7 @@ module.exports = function(crowi, app) {
     else {
       return actions.pageListShowForCrowiPlus(req, res);
     }
-  }
+  };
   /**
    * switch action by behaviorType
    */
@@ -90,7 +91,7 @@ module.exports = function(crowi, app) {
     else {
       return actions.pageShowForCrowiPlus(req, res);
     }
-  }
+  };
   /**
    * switch action by behaviorType
    */
@@ -105,7 +106,7 @@ module.exports = function(crowi, app) {
       // redirect to '/trash'
       return res.redirect('/trash');
     }
-  }
+  };
   /**
    * switch action by behaviorType
    */
@@ -121,7 +122,7 @@ module.exports = function(crowi, app) {
       return actions.deletedPageListShow(req, res);
     }
 
-  }
+  };
   /**
    * switch action by behaviorType
    */
@@ -136,7 +137,7 @@ module.exports = function(crowi, app) {
       const path = '/trash' + getPathFromRequest(req);
       return res.redirect(path);
     }
-  }
+  };
 
 
   actions.pageListShow = function(req, res) {
@@ -151,11 +152,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),
     };
 
@@ -175,7 +176,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) {
@@ -207,7 +209,7 @@ module.exports = function(crowi, app) {
     path = path.replace((/\/$/), '');
     // redirect
     return res.redirect(path);
-  }
+  };
 
   actions.pageShowForCrowiPlus = function(req, res) {
     var path = getPathFromRequest(req);
@@ -219,11 +221,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/'),
     };
@@ -296,7 +298,8 @@ module.exports = function(crowi, app) {
             return Promise.resolve();
           }
         });
-      } else {
+      }
+      else {
         return Promise.resolve();
       }
     })
@@ -339,14 +342,14 @@ module.exports = function(crowi, app) {
     })
     .then(function() {
       return UserGroupRelation.findAllRelationForUser(req.user);
-    }).then(function (groupRelations) {
+    }).then(function(groupRelations) {
       debug('findPage : relatedGroups ', groupRelations);
       renderVars.userRelatedGroups = groupRelations.map(relation => relation.relatedGroup);
       debug('findPage : groups ', renderVars.userRelatedGroups);
 
       return Promise.resolve();
     });
-  }
+  };
 
   actions.deletedPageListShow = function(req, res) {
     var path = '/trash' + getPathFromRequest(req);
@@ -356,11 +359,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,
     };
 
@@ -466,7 +469,8 @@ module.exports = function(crowi, app) {
           debug('Error on finding user related entities', err);
           // pass
         });
-      } else {
+      }
+      else {
         return Promise.resolve();
       }
     }).then(function() {
@@ -531,11 +535,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 ;
           }
@@ -562,7 +567,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 || {};
@@ -602,7 +607,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});
@@ -616,7 +622,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) {
@@ -647,7 +653,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)
@@ -680,8 +686,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) {
@@ -700,7 +706,8 @@ module.exports = function(crowi, app) {
         }
         return Page.findListByCreator(user, queryOptions, req.user);
       });
-    } else {
+    }
+    else {
       pageFetcher = Page.findListByStartWith(path, req.user, queryOptions);
     }
 
@@ -728,7 +735,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;
@@ -757,7 +764,7 @@ module.exports = function(crowi, app) {
       return res.json(ApiResponse.success(result));
     }).catch(function(err) {
       return res.json(ApiResponse.error(err));
-    });;
+    });
 
   };
 
@@ -775,7 +782,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;
@@ -790,7 +797,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) {
@@ -821,7 +828,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;
@@ -833,7 +840,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);
     }
 
@@ -854,7 +862,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'));
@@ -881,7 +889,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)
@@ -903,7 +911,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)
@@ -955,7 +963,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;
 
@@ -1008,7 +1016,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
@@ -1046,7 +1054,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);
@@ -1100,13 +1108,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;
@@ -1123,7 +1130,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)

+ 8 - 5
lib/routes/revision.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routes:revision')
+  var debug = require('debug')('growi:routes:revision')
     , Page = crowi.model('Page')
     , Revision = crowi.model('Revision')
     , ApiResponse = require('../util/apiResponse')
@@ -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.'));
     }
   };

+ 4 - 3
lib/routes/search.js

@@ -1,7 +1,7 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('crowi:routes:search')
+  var debug = require('debug')('growi:routes:search')
     , Page = crowi.model('Page')
     , User = crowi.model('User')
     , ApiResponse = require('../util/apiResponse')
@@ -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

+ 11 - 0
lib/service/logger/alias-for-debug.js

@@ -0,0 +1,11 @@
+/**
+ * return 'debug' method of bunyan logger
+ *
+ * This is supposed to be used as an replacement of "require('debug')"
+ *
+ * @param {string} name
+ */
+module.exports = (name) => {
+  const bunyanLogger = require('./index')(name);
+  return bunyanLogger.debug.bind(bunyanLogger);
+};

+ 48 - 0
lib/service/logger/index.js

@@ -0,0 +1,48 @@
+const bunyan = require('bunyan');   // will be replaced to browser-bunyan on browser by webpack
+const minimatch = require('minimatch');
+
+const isBrowser = typeof window !== 'undefined';
+const isProd = process.env.NODE_ENV === 'production';
+
+const config = require('@root/config').logger;
+
+let stream = isProd ? require('./stream.prod') : require('./stream.dev');
+
+// logger store
+let loggers = {};
+
+/**
+ * determine logger level
+ * @param {string} name Logger name
+ */
+function determineLoggerLevel(name) {
+  if (isBrowser && isProd) {
+    'error';
+  }
+
+  let level = config.default;
+
+  // retrieve configured level
+  Object.keys(config).some(key => { // breakable forEach
+    // test whether 'name' matches to 'key'(blob)
+    if (minimatch(name, key)) {
+      level = config[key];
+      return;                       // break if match
+    }
+  });
+
+  return level;
+}
+
+module.exports = (name) => {
+  // create logger instance if absent
+  if (loggers[name] == null) {
+    loggers[name] = bunyan.createLogger({
+      name,
+      stream,
+      level: determineLoggerLevel(name),
+    });
+  }
+
+  return loggers[name];
+};

+ 16 - 0
lib/service/logger/stream.dev.js

@@ -0,0 +1,16 @@
+const isBrowser = typeof window !== 'undefined';
+
+let stream = undefined;
+
+// browser settings
+if (isBrowser) {
+  const ConsoleFormattedStream = require('@browser-bunyan/console-formatted-stream').ConsoleFormattedStream;
+  stream = new ConsoleFormattedStream();
+}
+// node settings
+else {
+  const bunyanFormat = require('bunyan-format');
+  stream = bunyanFormat({ outputMode: 'short' });
+}
+
+module.exports = stream;

+ 20 - 0
lib/service/logger/stream.prod.js

@@ -0,0 +1,20 @@
+const isBrowser = typeof window !== 'undefined';
+
+let stream = undefined;
+
+// browser settings
+if (isBrowser) {
+  const ConsoleFormattedStream = require('@browser-bunyan/console-formatted-stream').ConsoleFormattedStream;
+  stream = new ConsoleFormattedStream();
+}
+// node settings
+else {
+  const isFormat = !(process.env.FORMAT_NODE_LOG === 'false');
+
+  if (isFormat) {
+    const bunyanFormat = require('bunyan-format');
+    stream = bunyanFormat({ outputMode: 'long' });
+  }
+}
+
+module.exports = stream;

+ 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;

+ 5 - 5
lib/service/passport.js

@@ -1,4 +1,4 @@
-const debug = require('debug')('crowi:service:PassportService');
+const debug = require('debug')('growi:service:PassportService');
 const passport = require('passport');
 const LocalStrategy = require('passport-local').Strategy;
 const LdapStrategy = require('passport-ldapauth');
@@ -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/fileUploader.js

@@ -5,7 +5,7 @@
 module.exports = function(crowi) {
   'use strict';
 
-  var debug = require('debug')('crowi:lib:fileUploader')
+  var debug = require('debug')('growi:lib:fileUploader')
     , method = crowi.env.FILE_UPLOAD || 'aws'
     , lib = '../../local_modules/crowi-fileupload-' + method;
 

+ 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 === '') {

+ 1 - 1
lib/util/googleAuth.js

@@ -7,7 +7,7 @@ module.exports = function(config) {
 
   const { GoogleApis } = require('googleapis');
   var google = new GoogleApis()
-    , debug = require('debug')('crowi:lib:googleAuth')
+    , debug = require('debug')('growi:lib:googleAuth')
     , lib = {}
     ;
 

+ 9 - 9
lib/util/interceptor-manager.js

@@ -1,4 +1,4 @@
-const debug = require('debug')('crowi:InterceptorManager')
+const logger = require('@alias/logger')('growi:InterceptorManager');
 
 /**
  * the manager class of Interceptor
@@ -23,7 +23,7 @@ class InterceptorManager {
    */
   addInterceptors(interceptors) {
     const interceptorIds = interceptors.map((i) => i.getId());
-    debug(`adding interceptors '${interceptorIds}'`);
+    logger.debug(`adding interceptors '${interceptorIds}'`);
     this.interceptors = this.interceptors.concat(interceptors);
   }
 
@@ -34,7 +34,7 @@ class InterceptorManager {
    * @param {any} args
    */
   process(contextName, ...args) {
-    debug(`processing the context '${contextName}'`);
+    logger.debug(`processing the context '${contextName}'`);
 
     // filter only contexts matches to specified 'contextName'
     const matchInterceptors = this.interceptors.filter((i) => i.isInterceptWhen(contextName));
@@ -42,8 +42,8 @@ class InterceptorManager {
     const parallels = matchInterceptors.filter((i) => i.isProcessableParallel());
     const sequentials = matchInterceptors.filter((i) => !i.isProcessableParallel());
 
-    debug(`${parallels.length} parallel interceptors found.`);
-    debug(`${sequentials.length} sequencial interceptors found.`);
+    logger.debug(`${parallels.length} parallel interceptors found.`);
+    logger.debug(`${sequentials.length} sequencial interceptors found.`);
 
     return Promise.all(
       // parallel
@@ -57,7 +57,7 @@ class InterceptorManager {
         }, Promise.resolve(...args)/* initial Promise */)
       ])
     ).then(() => {
-      debug(`end processing the context '${contextName}'`);
+      logger.debug(`end processing the context '${contextName}'`);
       return;
     });
   }
@@ -65,12 +65,12 @@ class InterceptorManager {
   doProcess(interceptor, contextName, ...args) {
     return interceptor.process(contextName, ...args)
       .then((...results) => {
-        debug(`processing '${interceptor.getId()}' in the context '${contextName}'`);
+        logger.debug(`processing '${interceptor.getId()}' in the context '${contextName}'`);
         return Promise.resolve(...results);
       })
       .catch((reason) => {
-        debug(`failed when processing '${interceptor.getId()}' in the context '${contextName}'`);
-        debug(reason);
+        logger.debug(`failed when processing '${interceptor.getId()}' in the context '${contextName}'`);
+        logger.debug(reason);
         return Promise.resolve(...args);
       });
   }

+ 12 - 11
lib/util/mailer.js

@@ -5,7 +5,7 @@
 module.exports = function(crowi) {
   'use strict';
 
-  var debug = require('debug')('crowi:lib:mailer')
+  var debug = require('debug')('growi:lib:mailer')
     , nodemailer = require('nodemailer')
     , swig = require('swig-templates')
     , Config = crowi.model('Config')
@@ -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);
     }

+ 17 - 12
lib/util/middlewares.js

@@ -1,4 +1,4 @@
-var debug = require('debug')('crowi:lib:middlewares');
+var debug = require('debug')('growi:lib:middlewares');
 var md5 = require('md5');
 
 exports.csrfKeyGenerator = function(crowi, app) {
@@ -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.');

+ 48 - 61
lib/util/search.js

@@ -3,7 +3,7 @@
  */
 
 var elasticsearch = require('elasticsearch'),
-  debug = require('debug')('crowi:lib:search');
+  debug = require('debug')('growi:lib:search');
 
 function SearchClient(crowi, esUri) {
   this.DEFAULT_OFFSET = 0;
@@ -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);
   });
 

+ 22 - 18
lib/util/slack.js

@@ -5,9 +5,7 @@
 module.exports = function(crowi) {
   'use strict';
 
-  const SLACK_URL = 'https://slack.com';
-
-  const debug = require('debug')('crowi:util:slack'),
+  const debug = require('debug')('growi:util:slack'),
     config = crowi.getConfig(),
     Config = crowi.model('Config'),
     Slack = require('slack-node'),
@@ -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);
         }
       }

+ 49 - 45
lib/util/swigFunctions.js

@@ -1,24 +1,27 @@
 module.exports = function(crowi, app, req, locals) {
-  var debug = require('debug')('crowi:lib:swigFunctions')
+  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);

+ 1 - 2
lib/views/admin/app.html

@@ -147,7 +147,7 @@
         </div>
 
         <div class="form-group">
-          <label for="settingForm[aws:bucket]" class="col-xs-3 control-label">{{ t('app_setting.Packet name') }}</label>
+          <label for="settingForm[aws:bucket]" class="col-xs-3 control-label">{{ t('app_setting.bucket name') }}</label>
           <div class="col-xs-6">
             <input class="form-control" type="text" name="settingForm[aws:bucket]" placeholder="例: crowi"  value="{{ settingForm['aws:bucket'] }}">
           </div>
@@ -269,4 +269,3 @@
 
 {% block content_footer %}
 {% endblock content_footer %}
-

+ 16 - 4
lib/views/admin/customize.html

@@ -3,8 +3,13 @@
 {% block html_title %}{{ customTitle(t('Customize')) }} {% endblock %}
 
 {% block style_css_block %}
-  <link rel="stylesheet" href="{{ webpack_asset('style').css }}">
-  <link rel="stylesheet" id="jssDefault" {# append id for theme selector #} href="{{ webpack_asset('style-theme-' + theme()).css }}">
+  {% if env === 'development' %}
+    <script src="{{ webpack_asset('style').js }}"></script>
+    <script src="{{ webpack_asset('style-theme-' + theme()).js }}"></script>
+  {% else %}
+    <link rel="stylesheet" href="{{ webpack_asset('style').css }}">
+    <link rel="stylesheet" id="jssDefault" {# append id for theme selector #} href="{{ webpack_asset('style-theme-' + theme()).css }}">
+  {% endif %}
 {% endblock %}
 
 {% block html_additional_headers %}
@@ -52,11 +57,18 @@
         <fieldset>
           <legend>{{ t('customize_page.Theme') }}</legend>
 
+          {% if env === 'development' %}
+            <div class="alert alert-warning">
+              <strong>DEBUG MESSAGE:</strong> development build では、リアルタイムプレビューが無効になります
+            </div>
+          {% endif %}
+
           <div id="themeOptions">
             {# Light Themes #}
             <div class="d-flex">
               {% include 'widget/theme-colorbox.html' with { name: 'default',  bg: '#ffffff', topbar: '#334455', theme: '#112744' } %}
               {% include 'widget/theme-colorbox.html' with { name: 'nature',   bg: '#f9fff3', topbar: '#118050', theme: '#460039' } %}
+              {% include 'widget/theme-colorbox.html' with { name: 'mono-blue',   bg: '#F7FBFD', topbar: '#00587A', theme: '#00587A' } %}
             </div>
             {# Dark Themes #}
             <div class="d-flex">
@@ -501,12 +513,12 @@ window.addEventListener('load', (event) => {
       $(this).submit(function()
       {
         function showMessage(formId, msg, status) {
-          $('#' + formId + ' .alert').remove();
+          $('#' + formId + ' #alert-results').remove();
 
           if (!status) {
             status = 'success';
           }
-          var $message = $('<p class="alert"></p>');
+          var $message = $('<p id="alert-results" class="alert"></p>');
           $message.addClass('alert-' + status);
           $message.html(msg.replace('\n', '<br>'));
           $message.insertAfter('#' + formId + ' legend');

+ 30 - 32
lib/views/admin/security.html

@@ -63,7 +63,7 @@
             <div class="col-xs-6">
               <select class="form-control selectpicker" name="settingForm[security:restrictGuestMode]" value="{{ settingForm['security:restrictGuestMode'] }}">
                 {% for modeValue, modeLabel in consts.restrictGuestMode %}
-                <option value="{{ modeValue }}" {% if modeValue == settingForm['security:restrictGuestMode'] %}selected{% endif %} >{{ modeLabel }}</option>
+                <option value="{{ t(modeValue) }}" {% if modeValue == settingForm['security:restrictGuestMode'] %}selected{% endif %} >{{ t(modeLabel) }}</option>
                 {% endfor %}
               </select>
             </div>
@@ -74,7 +74,7 @@
             <div class="col-xs-6">
               <select class="form-control selectpicker" name="settingForm[security:registrationMode]" value="{{ settingForm['security:registrationMode'] }}">
                 {% for modeValue, modeLabel in consts.registrationMode %}
-                <option value="{{ modeValue }}" {% if modeValue == settingForm['security:registrationMode'] %}selected{% endif %} >{{ modeLabel }}</option>
+                <option value="{{ t(modeValue) }}" {% if modeValue == settingForm['security:registrationMode'] %}selected{% endif %} >{{ t(modeLabel) }}</option>
                 {% endfor %}
               </select>
               <p class="help-block">{{ t('The contents entered here will be shown in the header etc') }}</p>
@@ -84,8 +84,8 @@
           <div class="form-group">
             <label for="settingForm[security:registrationWhiteList]" class="col-xs-3 control-label">{{ t('The whitelist of registration permission E-mail address') }}</label>
             <div class="col-xs-8">
-              <textarea class="form-control" type="textarea" name="settingForm[security:registrationWhiteList]" placeholder=": @crowi.wiki">{{ settingForm['security:registrationWhiteList']|join('&#13')|raw }}</textarea>
-              <p class="help-block">{{ t("security_setting.restrict_emails") }}{{ t("security_setting.for_instance") }}<code>@crowi.wiki</code>{{ t("security_setting.only_those") }}<br>
+              <textarea class="form-control" type="textarea" name="settingForm[security:registrationWhiteList]" placeholder="{{ t('security_setting.example') }}: @crowi.wiki">{{ settingForm['security:registrationWhiteList']|join('&#13')|raw }}</textarea>
+              <p class="help-block">{{ t("security_setting.restrict_emails") }}{{ t("security_setting.for_instance") }}<code>@growi.org</code>{{ t("security_setting.only_those") }}<br>
               {{ t("security_setting.insert_single") }}</p>
             </div>
           </div>
@@ -103,7 +103,7 @@
       <form action="/_api/admin/security/mechanism" method="post" class="form-horizontal m-t-30" id="mechanismSetting" role="form">
         <fieldset>
           <legend class="alert-anchor">{{ t('Selecting authentication mechanism') }}</legend>
-          <p class="alert alert-info"><b>NOTE: </b>Restarting the server is needed if you switch the auth mechanism.</p>
+          <p class="alert alert-info"><b>{{ t("security_setting.note") }}: </b>{{ t("security_setting.require_server_restart_change_auth") }}</p>
           <div class="form-group">
             <div class="col-xs-6">
               <h4>
@@ -113,17 +113,17 @@
                   <label for="radioPassportAuthMech">
                     <a href="http://passportjs.org/">
                       <img src="/images/admin/security/passport-logo.svg" class="passport-logo"> Passport
-                    </a> authentication mechanism <small class="text-success">(Recommended)</small>
+                    </a> {{ t("security_setting.auth_mechanism") }} <small class="text-success">({{ t("security_setting.recommended") }})</small>
                   </label>
                 </div>
               </h4>
               <ul>
-                <li>Username, E-mail and Password authentication</li>
-                <li>LDAP authentication</li>
-                <li class="text-muted">(TBD) <del>Google OAuth2 authentication</del></li>
-                <li class="text-muted">(TBD) <del>Facebook OAuth2 authentication</del></li>
-                <li class="text-muted">(TBD) <del>Twitter OAuth authentication</del></li>
-                <li class="text-muted">(TBD) <del>Github OAuth2 authentication</del></li>
+                <li>{{ t("security_setting.username_email_password") }}</li>
+                <li>{{ t("security_setting.ldap_auth") }}</li>
+                <li class="text-muted">(TBD) <del>{{ t("security_setting.google_auth2") }}</del></li>
+                <li class="text-muted">(TBD) <del>{{ t("security_setting.facebook_auth2") }}</del></li>
+                <li class="text-muted">(TBD) <del>{{ t("security_setting.twitter_auth2") }}</del></li>
+                <li class="text-muted">(TBD) <del>{{ t("security_setting.github_auth2") }}</del></li>
               </ul>
             </div>
             <div class="col-xs-6">
@@ -132,13 +132,13 @@
                   <input type="radio" id="radioCrowiAuthMech" name="settingForm[security:isEnabledPassport]" value="false"
                       {% if !settingForm['security:isEnabledPassport'] %}checked="checked"{% endif %}>
                   <label for="radioCrowiAuthMech">
-                    Crowi classic authentication mechanism
+                    Crowi Classic {{ t("security_setting.auth_mechanism") }}
                   </label>
                 </div>
               </h4>
               <ul>
-                <li>Username, E-mail and Password authentication</li>
-                <li>Google OAuth2 authentication</li>
+                <li>{{ t("security_setting.username_email_password") }}</li>
+                <li>{{ t("security_setting.google_auth2") }}</li>
               </ul>
             </div>
           </div>
@@ -164,41 +164,39 @@
               {% if !isRestartingServerNeeded %}style="display: none;"{% endif %}>
             <b>
               <i class="icon-exclamation" aria-hidden="true"></i>
-              Restarting the server is needed.
+              {{ t("security_setting.require_server_restart") }}
             </b>
-            The server is running with Passport authentication mechanism.
+            {{ t("security_setting.server_on_passport_auth") }}
           </p>
 
           <form action="/_api/admin/security/google" method="post" class="form-horizontal" id="googleSetting" role="form"
               {% if isRestartingServerNeeded %}style="opacity: 0.4;"{% endif %}>
 
             <fieldset>
-              <h4>Google 設定</h4>
+              <h4>{{ t("security_setting.google_setting") }}</h4>
               <p class="well alert-anchor">
-                Google Cloud Platform の <a href="https://console.cloud.google.com/apis/credentials">API Manager</a>
-                から OAuth2 Client ID を作成すると、Google アカウントにコネクトして登録やログインが可能になります。
+                {{ t("security_setting.connect_api_manager") }}
               </p>
 
               <ol class="help-block">
-                <li><a href="https://console.cloud.google.com/apis/credentials">API Manager</a> へアクセス</li>
-                <li>プロジェクトを作成していない場合は作成してください</li>
-                <li>「認証情報を作成」-> OAuthクライアントID</li>
+                <li>{{ t("security_setting.access_api_manager") }}</li>
+                <li>{{ t("security_setting.create_project") }}</li>
+                <li>{{ t("security_setting.create_auth_to_oauth") }}</li>
                 <ol>
-                  <li>「ウェブアプリケーション」を選択</li>
-                  <li>承認済みのリダイレクトURLに、 <code>https://${crowi.host}/google/callback</code> を入力<br>
-                  (<code>${crowi.host}</code>は環境に合わせて変更してください)</li>
+                  <li>{{ t("security_setting.select_webapp") }}</li>
+                  <li>{{ t("security_setting.change_redirect_url") }}</li>
                 </ol>
               </ol>
 
               <div class="form-group">
-                <label for="settingForm[google:clientId]" class="col-xs-3 control-label">Client ID</label>
+                <label for="settingForm[google:clientId]" class="col-xs-3 control-label">{{ t("security_setting.clientID") }}</label>
                 <div class="col-xs-6">
                   <input class="form-control" type="text" name="settingForm[google:clientId]" value="{{ settingForm['google:clientId'] }}">
                 </div>
               </div>
 
               <div class="form-group">
-                <label for="settingForm[google:clientSecret]" class="col-xs-3 control-label">Client Secret</label>
+                <label for="settingForm[google:clientSecret]" class="col-xs-3 control-label">{{ t("security_setting.client_secret") }}</label>
                 <div class="col-xs-6">
                   <input class="form-control" type="text" name="settingForm[google:clientSecret]" value="{{ settingForm['google:clientSecret'] }}">
                 </div>
@@ -226,9 +224,9 @@
               {% if !isRestartingServerNeeded %}style="display: none;"{% endif %}>
             <b>
               <i class="icon-exclamation" aria-hidden="true"></i>
-              Restarting the server is needed.
+              {{ t("security_setting.require_server_restart") }}
             </b>
-            The server is running with Official Crowi authentication mechanism.
+            {{ t("security_setting.server_on_crowi_auth") }}
           </p>
           <ul class="nav nav-tabs" role="tablist" {% if isRestartingServerNeeded %}style="opacity: 0.4;"{% endif %}>
             <li class="active">
@@ -311,13 +309,13 @@
         var jqxhr = $.post($form.attr('action'), $form.serialize(), function(data)
           {
             if (data.status) {
-              showMessage($id, '更新しました');
+              showMessage($id, '更新しました Updated');
             } else {
               showMessage($id, data.message, 'danger');
             }
           })
           .fail(function() {
-            showMessage($id, 'エラーが発生しました', 'danger');
+            showMessage($id, 'エラーが発生しました Error', 'danger');
           })
           .always(function() {
             $button.prop('disabled', false);

+ 1 - 1
lib/views/admin/widget/passport/facebook.html

@@ -1,6 +1,6 @@
 <form action="" method="post" class="form-horizontal passportStrategy" id="facebookOauthSetting" role="form">
   <fieldset>
-    <legend>Facebook OAuth Configuration</legend>
+    <legend>Facebook OAuth {{ t("security_setting.configuration") }}</legend>
     <p class="well">(TBD)</p>
   </fieldset>
 </form>

+ 1 - 1
lib/views/admin/widget/passport/github.html

@@ -1,6 +1,6 @@
 <form action="" method="post" class="form-horizontal passportStrategy" id="githubOauthSetting" role="form">
   <fieldset>
-    <legend>Github OAuth Configuration</legend>
+    <legend>Github OAuth {{ t("security_setting.configuration") }}</legend>
     <p class="well">(TBD)</p>
   </fieldset>
 </form>

+ 1 - 1
lib/views/admin/widget/passport/google-oauth.html

@@ -1,6 +1,6 @@
 <form action="" method="post" class="form-horizontal passportStrategy" id="googleOauthSetting" role="form">
   <fieldset>
-    <legend>Google OAuth Configuration</legend>
+    <legend>Google OAuth {{ t("security_setting.configuration") }}</legend>
     <p class="well">(TBD)</p>
   </fieldset>
 </form>

+ 45 - 36
lib/views/admin/widget/passport/ldap.html

@@ -1,7 +1,7 @@
 <form action="/_api/admin/security/passport-ldap" method="post" class="form-horizontal" id="ldapSetting" role="form">
 
   <fieldset>
-    <legend>LDAP Configuration</legend>
+    <legend>LDAP {{ t("security_setting.configuration") }}</legend>
 
     {% set nameForIsLdapEnabled = "settingForm[security:passport-ldap:isEnabled]" %}
     {% set isLdapEnabled = settingForm['security:passport-ldap:isEnabled'] %}
@@ -30,8 +30,8 @@
               name="settingForm[security:passport-ldap:serverUrl]" value="{{ settingForm['security:passport-ldap:serverUrl'] || '' }}">
           <p class="help-block">
             <small>
-              The LDAP URL of the directory service in the format <code>ldap://host:port/DN</code> or <code>ldaps://host:port/DN</code>.<br>
-              Example: <code>ldaps://ldap.company.com/ou=people,dc=company,dc=com</code>
+              {{ t("security_setting.ldap.server_url_detail") }}<br>
+              {{ t("security_setting.example") }}: <code>ldaps://ldap.company.com/ou=people,dc=company,dc=com</code>
             </small>
           </p>
         </div>
@@ -40,16 +40,16 @@
       {% set nameForIsUserBind = "settingForm[security:passport-ldap:isUserBind]" %}
       {% set isUserBind = settingForm['security:passport-ldap:isUserBind'] %}
       <div class="form-group">
-        <label for="{{nameForIsUserBind}}" class="col-xs-3 control-label">Binding Mode</label>
+        <label for="{{nameForIsUserBind}}" class="col-xs-3 control-label">{{ t("security_setting.ldap.bind_mode") }}</label>
         <div class="col-xs-6">
           <div class="btn-group btn-toggle" data-toggle="buttons">
             <label class="btn btn-default btn-rounded btn-outline {% if !isUserBind %}active{% endif %}" data-active-class="primary">
               <input name="{{nameForIsUserBind}}" value="false" type="radio"
-                  {% if !isUserBind %}checked{% endif %}> Manager Bind
+                  {% if !isUserBind %}checked{% endif %}> {{ t("security_setting.ldap.bind_manager") }}
             </label>
             <label class="btn btn-default btn-rounded btn-outline {% if isUserBind %}active{% endif %}" data-active-class="primary">
               <input name="{{nameForIsUserBind}}" value="true" type="radio"
-                  {% if isUserBind %}checked{% endif %}> User Bind
+                  {% if isUserBind %}checked{% endif %}> {{ t("security_setting.ldap.bind_user") }}
             </label>
           </div>
         </div>
@@ -62,55 +62,58 @@
               name="settingForm[security:passport-ldap:bindDN]" value="{{ settingForm['security:passport-ldap:bindDN'] || '' }}">
           <p class="help-block passport-ldap-managerbind" {% if isUserBind %}style="display: none;"{% endif %}>
             <small>
-              The DN of the account that authenticates and queries the directory service
+              {{ t("security_setting.ldap.bind_DN_manager_detail") }}
             </small>
           </p>
           <p class="help-block passport-ldap-userbind" {% if !isUserBind %}style="display: none;"{% endif %}>
             <small>
-              The query used to bind with the directory service.<br>
-              Use <code>{% raw %}{{username}}{% endraw %}</code> to reference the username entered in the login page.<br>
-              Example: <code>uid={% raw %}{{username}}{% endraw %},dc=domain,dc=com</code><br>
+              {{ t("security_setting.ldap.bind_DN_user_detail1") }}<br>
+              {{ t("security_setting.ldap.bind_DN_user_detail2") }}<br>
+              {{ t("security_setting.example") }}: <code>uid={% raw %}{{username}}{% endraw %},dc=domain,dc=com</code><br>
             </small>
           </p>
           </div>
       </div>
 
       <div class="form-group">
-        <label for="settingForm[security:passport-ldap:bindDNPassword]" class="col-xs-3 control-label">Bind DN Password</label>
+        <label for="settingForm[security:passport-ldap:bindDNPassword]" class="col-xs-3 control-label">{{ t("security_setting.ldap.bind_DN_password") }}</label>
         <div class="col-xs-6">
           <input class="form-control passport-ldap-managerbind" type="text" {% if isUserBind %}style="display: none;"{% endif %}
               name="settingForm[security:passport-ldap:bindDNPassword]" value="{{ settingForm['security:passport-ldap:bindDNPassword'] || '' }}">
           <p class="help-block passport-ldap-managerbind">
             <small>
-              The password for the Bind DN account.
+              {{ t("security_setting.ldap.bind_DN_password_manager_detail") }}
             </small>
           </p>
           <p class="help-block passport-ldap-userbind" {% if !isUserBind %}style="display: none;"{% endif %}>
             <small>
-              The password that is entered in the login page will be used to bind.
+              {{ t("security_setting.ldap.bind_DN_password_user_detail") }}
             </small>
           </p>
         </div>
       </div>
 
       <div class="form-group">
-        <label for="settingForm[security:passport-ldap:searchFilter]" class="col-xs-3 control-label">Search Filter</label>
+        <label for="settingForm[security:passport-ldap:searchFilter]" class="col-xs-3 control-label">{{ t("security_setting.ldap.search_filter") }}</label>
         <div class="col-xs-6">
           <input class="form-control" type="text" placeholder="Default: (uid={% raw %}{{username}}{% endraw %})"
               name="settingForm[security:passport-ldap:searchFilter]" value="{{ settingForm['security:passport-ldap:searchFilter'] || '' }}">
           <p class="help-block">
             <small>
-              The query used to locate the authenticated user.<br>
-              Use <code>{% raw %}{{username}}{% endraw %}</code> to reference the username entered in the login page.<br>
-              If empty, the filter <code>(uid={% raw %}{{username}}{% endraw %})</code> is used.<br>
-              <br>
-              Example to match with 'uid' or 'mail': <code>(|(uid={% raw %}{{username}}{% endraw %})(mail={% raw %}{{username}}{% endraw %}))</code>
+              {{ t("security_setting.ldap.search_filter_detail1") }}<br>
+              {{ t("security_setting.ldap.search_filter_detail2") }}<br>
+              {{ t("security_setting.ldap.search_filter_detail3") }}
+            </small>
+          </p>
+          <p>
+            <small>
+              {{ t("security_setting.ldap.search_filter_example") }}: <code>(|(uid={% raw %}{{username}}{% endraw %})(mail={% raw %}{{username}}{% endraw %}))</code>
             </small>
           </p>
         </div>
       </div>
 
-      <h4>Attribute Mapping (Optional)</h4>
+      <h4>Attribute Mapping ({{ t("security_setting.optional") }})</h4>
 
       <div class="form-group">
         <label for="settingForm[security:passport-ldap:attrMapUsername]" class="col-xs-3 control-label">username</label>
@@ -119,7 +122,7 @@
               name="settingForm[security:passport-ldap:attrMapUsername]" value="{{ settingForm['security:passport-ldap:attrMapUsername'] || '' }}">
           <p class="help-block">
             <small>
-              Specification of mappings when creating new users
+              {{ t("security_setting.ldap.username_detail") }}
             </small>
           </p>
 
@@ -127,55 +130,61 @@
             <input type="checkbox" id="cbSameUsernameTreatedAsIdenticalUser" name="settingForm[security:passport-ldap:isSameUsernameTreatedAsIdenticalUser]" value="1"
                 {% if settingForm['security:passport-ldap:isSameUsernameTreatedAsIdenticalUser'] %}checked{% endif %} />
             <label for="cbSameUsernameTreatedAsIdenticalUser">
-              {{ t("security_setting.Treat username matching as identical") }}
+              {{ t("security_setting.ldap.Treat username matching as identical") }}
             </label>
+            <p class="help-block">
+              <small>
+                {{ t("security_setting.ldap.Treat username matching as identical_warn") }}
+              </small>
+            </p>
           </div>
         </div>
       </div>
 
 
-      <h4>Group Search Filter (Optional)</h4>
+      <h4>{{ t("security_setting.ldap.group_search_filter") }} ({{ t("security_setting.optional") }})</h4>
 
       <div class="form-group">
-        <label for="settingForm[security:passport-ldap:groupSearchBase]" class="col-xs-3 control-label">Group Search Base DN</label>
+        <label for="settingForm[security:passport-ldap:groupSearchBase]" class="col-xs-3 control-label">{{ t("security_setting.ldap.group_search_base_DN") }}</label>
         <div class="col-xs-6">
           <input class="form-control" type="text"
               name="settingForm[security:passport-ldap:groupSearchBase]" value="{{ settingForm['security:passport-ldap:groupSearchBase'] || '' }}">
           <p class="help-block">
             <small>
-              The base DN from which to search for groups. If defined, also <code>Group Search Filter</code> must be defined for the search to work.<br>
-              Example: <code>ou=groups,dc=domain,dc=com</code><br>
+              {{ t("security_setting.ldap.group_search_base_DN_detail") }}<br>
+              {{ t("security_setting.example") }}: <code>ou=groups,dc=domain,dc=com</code>
             </small>
           </p>
         </div>
       </div>
 
       <div class="form-group">
-        <label for="settingForm[security:passport-ldap:groupSearchFilter]" class="col-xs-3 control-label">Group Search Filter</label>
+        <label for="settingForm[security:passport-ldap:groupSearchFilter]" class="col-xs-3 control-label">{{ t("security_setting.ldap.group_search_filter") }}</label>
         <div class="col-xs-6">
           <input class="form-control" type="text"
               name="settingForm[security:passport-ldap:groupSearchFilter]" value="{{ settingForm['security:passport-ldap:groupSearchFilter'] || '' }}">
           <p class="help-block">
             <small>
-              The query used to filter for groups.<br>
-              Use <code>{% raw %}{{dn}}{% endraw %}</code> to have it replaced of the found user object.<br>
-              <br>
-              Example: <code>(&(cn=group1)(memberUid={% raw %}{{dn}}{% endraw %}))</code> hits the groups
-              which has <code>cn=group1</code> and <code>memberUid</code> includes the user's <code>uid</code>
-              (when <code>Group DN Property</code> is not changed from the default value.)
+              {{ t("security_setting.ldap.group_search_filter_detail1") }}<br>
+              {{ t("security_setting.ldap.group_search_filter_detail2") }}
+            </small>
+          </p>
+          <p class="help-block">
+            <small>
+              {{ t("security_setting.example") }}: {{ t("security_setting.ldap.group_search_filter_detail3") }}
             </small>
           </p>
         </div>
       </div>
 
       <div class="form-group">
-        <label for="settingForm[security:passport-ldap:groupSearchFilter]" class="col-xs-3 control-label">Group DN Property</label>
+        <label for="settingForm[security:passport-ldap:groupSearchFilter]" class="col-xs-3 control-label">{{ t("security_setting.ldap.group_search_user_DN_property") }}</label>
         <div class="col-xs-6">
           <input class="form-control" type="text" placeholder="Default: uid"
               name="settingForm[security:passport-ldap:groupDnProperty]" value="{{ settingForm['security:passport-ldap:groupDnProperty'] || '' }}">
           <p class="help-block">
             <small>
-              The property of user object to use in <code>{% raw %}{{dn}}{% endraw %}</code> interpolation of <code>Group Search Filter</code>.
+              {{ t("security_setting.ldap.group_search_user_DN_property_detail") }}
             </small>
           </p>
         </div>
@@ -193,7 +202,7 @@
             data-target="#test-ldap-account" data-toggle="modal"
             {%if !isLdapEnabled %}style="display: none;"{% endif %}>
 
-          Test Saved Configuration
+          {{ t("security_setting.ldap.test_config") }}
         </button>
       </div>
     </div>

+ 1 - 1
lib/views/admin/widget/passport/twitter.html

@@ -1,6 +1,6 @@
 <form action="" method="post" class="form-horizontal passportStrategy" id="twitterOauthSetting" role="form">
   <fieldset>
-    <legend>Twitter OAuth Configuration</legend>
+    <legend>Twitter OAuth {{ t("security_setting.configuration") }}</legend>
     <p class="well">(TBD)</p>
   </fieldset>
 </form>

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

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

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

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

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

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

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

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

+ 7 - 2
lib/views/layout/layout.html

@@ -83,8 +83,13 @@ gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js
 
   <!-- styles -->
   {% block style_css_block %}
-  <link rel="stylesheet" href="{{ webpack_asset('style').css }}">
-  <link rel="stylesheet" href="{{ webpack_asset('style-theme-' + theme()).css }}">
+    {% if env === 'development' %}
+      <script src="{{ webpack_asset('style').js }}"></script>
+      <script src="{{ webpack_asset('style-theme-' + theme()).js }}"></script>
+    {% else %}
+      <link rel="stylesheet" href="{{ webpack_asset('style').css }}">
+      <link rel="stylesheet" href="{{ webpack_asset('style-theme-' + theme()).css }}">
+    {% endif %}
   {% endblock %}
 
   <!-- Google Fonts -->

+ 15 - 11
lib/views/modal/unportalize.html

@@ -32,17 +32,21 @@
             </div>
         </div>
         <div class="modal-footer">
-          <p class="pull-left text-left">
-            <small id="newPageNameCheck"></small>
-            <br>
-            <span id="linkToNewPage"></span>
-          </p>
-          <input type="hidden" name="_csrf" value="{{ csrf() }}">
-          <input type="hidden" name="path" value="{{ page.path }}">
-          <input type="hidden" class="form-control" name="new_path" id="newPageName" value="{{ unportalizedPath }}">
-          <input type="hidden" name="page_id" value="{{ page._id.toString() }}">
-          <input type="hidden" name="revision_id" value="{{ page.revision._id.toString() }}">
-          <button type="submit" class="btn btn-warning">Unportalize</button>
+          <div class="d-flex justify-content-between">
+            <p>
+              <span class="text-danger msg-already-exists">
+                <strong><i class="icon-fw icon-ban"></i>{{ t('Page is already exists.') }}</strong>
+              </span>
+              <small id="linkToNewPage" class="msg-already-exists"></small>
+            </p>
+            <div>
+              <input type="hidden" name="_csrf" value="{{ csrf() }}">
+              <input type="hidden" name="path" value="{{ page.path }}">
+              <input type="hidden" class="form-control" name="new_path" id="newPageName" value="{{ unportalizedPath }}">
+              <input type="hidden" name="page_id" value="{{ page._id.toString() }}">
+              <input type="hidden" name="revision_id" value="{{ page.revision._id.toString() }}">
+              <button type="submit" class="btn btn-warning">Unportalize</button>
+            </div>
         </div>
 
       </form>

+ 3 - 3
lib/views/widget/page_alerts.html

@@ -29,7 +29,7 @@
     {% if req.query.renamed and not page.isDeleted() %}
     <div class="alert alert-info alert-moved">
       <span>
-        <strong>{{ t('Moved') }}: </strong> {{ t('page_page.notice.moved', req.query.renamed) }}
+        <strong>{{ t('Moved') }}: </strong> {{ t('page_page.notice.moved', req.sanitize(req.query.renamed)) }}
       </span>
     </div>
     {% endif %}
@@ -37,7 +37,7 @@
     {% if req.query.redirectFrom and not page.isDeleted() %}
     <div class="alert alert-info alert-moved d-flex align-items-center justify-content-between">
       <span>
-        <strong>{{ t('Moved') }}: </strong> {{ t('page_page.notice.moved', req.query.redirectFrom) }}
+        <strong>{{ t('Moved') }}: </strong> {{ t('page_page.notice.moved', req.sanitize(req.query.redirectFrom)) }}
       </span>
       {% if user %}
       <form role="form" id="unlink-page-form" onsubmit="return false;">
@@ -56,7 +56,7 @@
     {% if req.query.duplicated and not page.isDeleted() %}
     <div class="alert alert-success alert-moved">
       <span>
-        <strong>{{ t('Duplicated') }}: </strong> {{ t('page_page.notice.duplicated', req.query.duplicated) }}
+        <strong>{{ t('Duplicated') }}: </strong> {{ t('page_page.notice.duplicated', req.sanitize(req.query.duplicated)) }}
       </span>
     </div>
     {% endif %}

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

@@ -6,17 +6,17 @@ module.exports = function(crowi) {
   var aws = require('aws-sdk')
     , fs = require('fs')
     , path = require('path')
-    , debug = require('debug')('crowi:lib:fileUploaderAws')
+    , 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;

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

@@ -3,7 +3,7 @@
 module.exports = function(crowi) {
   'use strict';
 
-  var debug = require('debug')('crowi:lib:fileUploaderLocal')
+  var debug = require('debug')('growi:lib:fileUploaderLocal')
     , fs = require('fs')
     , path = require('path')
     , mkdir = require('mkdirp')
@@ -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));
   };
 

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

@@ -3,7 +3,7 @@
 module.exports = function(crowi) {
   'use strict';
 
-  var debug = require('debug')('crowi:lib:fileUploaderNone')
+  var debug = require('debug')('growi:lib:fileUploaderNone')
     , Config = crowi.model('Config')
     , config = crowi.getConfig()
     , lib = {};

+ 24 - 11
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "3.0.11-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",
@@ -39,7 +41,7 @@
     "postserver:prod:container": "echo ---------------------------------------- && echo [WARNING] && echo   'server:prod:container' is deprecated. && echo   Please use 'sever:prod' && echo ----------------------------------------",
     "server:dev": "env-cmd config/env.dev.js node-dev --respawn app.js",
     "server:prod:container": "npm run server:prod",
-    "server:prod": "env-cmd config/env.prod.js node app.js --production | pino-clf common",
+    "server:prod": "env-cmd config/env.prod.js node app.js",
     "server": "npm run server:dev",
     "start": "npm run server:prod",
     "test": "mocha --timeout 10000 -r test/bootstrap.js test/**/*.js",
@@ -61,6 +63,10 @@
     "body-parser": "^1.18.2",
     "bootstrap-sass": "~3.3.6",
     "bootstrap-select": "^1.12.4",
+    "browser-bunyan": "^1.3.0",
+    "bunyan": "^1.8.12",
+    "bunyan-debug": "^2.0.0",
+    "bunyan-format": "^0.2.1",
     "check-node-version": "^3.1.1",
     "codemirror": "^5.37.0",
     "connect-flash": "~0.1.1",
@@ -72,7 +78,6 @@
     "css-loader": "^0.28.0",
     "csv-to-markdown-table": "^0.4.0",
     "date-fns": "^1.29.0",
-    "debug": "^3.1.0",
     "diff": "^3.3.0",
     "diff2html": "^2.3.3",
     "eazy-logger": "^3.0.2",
@@ -81,13 +86,14 @@
     "env-cmd": "^8.0.1",
     "escape-string-regexp": "^1.0.5",
     "express": "^4.16.1",
+    "express-bunyan-logger": "^1.3.3",
     "express-form": "~0.12.0",
-    "express-pino-logger": "^3.0.1",
+    "express-sanitizer": "^1.0.4",
     "express-session": "~1.15.0",
     "express-webpack-assets": "^0.1.0",
     "extract-text-webpack-plugin": "^3.0.2",
     "file-loader": "^1.1.0",
-    "googleapis": "^28.1.0",
+    "googleapis": "^29.0.0",
     "graceful-fs": "^4.1.11",
     "growi-pluginkit": "^1.1.0",
     "i18next": "^11.1.1",
@@ -97,6 +103,7 @@
     "jquery-slimscroll": "^1.3.8",
     "jquery-ui": "^1.12.1",
     "jquery.cookie": "~1.4.1",
+    "load-css-file": "^1.0.0",
     "markdown-it": "^8.4.0",
     "markdown-it-emoji": "^1.4.0",
     "markdown-it-footnote": "^3.0.1",
@@ -104,12 +111,13 @@
     "markdown-it-named-headers": "^0.0.4",
     "markdown-it-plantuml": "^1.0.0",
     "markdown-it-task-lists": "^2.1.0",
-    "markdown-it-toc-and-anchor-with-slugid": "^1.1.2",
+    "markdown-it-toc-and-anchor-with-slugid": "^1.1.3",
     "markdown-table": "^1.1.1",
     "md5": "^2.2.1",
     "method-override": "^2.3.10",
     "metismenu": "^2.7.4",
     "mkdirp": "~0.5.1",
+    "module-alias": "^2.0.6",
     "mongoose": "^5.0.0",
     "mongoose-paginate": "^5.0.0",
     "mongoose-unique-validator": "^2.0.0",
@@ -123,7 +131,6 @@
     "passport": "^0.4.0",
     "passport-ldapauth": "^2.0.0",
     "passport-local": "^1.0.0",
-    "pino-clf": "^1.0.2",
     "plantuml-encoder": "^1.2.5",
     "postcss-loader": "^2.1.3",
     "react": "^16.2.0",
@@ -136,16 +143,17 @@
     "reveal.js": "^3.5.0",
     "rimraf": "^2.6.1",
     "sass-loader": "^7.0.1",
+    "simple-load-script": "^1.0.2",
     "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",
     "toastr": "^2.1.2",
     "uglifycss": "^0.0.29",
     "url-join": "^4.0.0",
-    "uslug": "^1.0.4",
     "webpack": "3.11.0",
     "webpack-bundle-analyzer": "^2.9.0",
     "webpack-merge": "~4.1.0",
@@ -158,16 +166,21 @@
     "colors": "^1.1.2",
     "commander": "^2.11.0",
     "connect-browser-sync": "^2.1.0",
-    "eslint": "^4.18.2",
+    "eslint": "^4.19.1",
     "eslint-plugin-react": "^7.7.0",
     "mocha": "^5.0.0",
-    "morgan": "^1.8.2",
+    "morgan": "^1.9.0",
     "node-dev": "^3.1.3",
     "on-headers": "^1.0.1",
-    "sinon": "^4.0.0",
+    "sinon": "^5.0.2",
     "sinon-chai": "^3.0.0",
     "webpack-dll-bundles-plugin": "^1.0.0-beta.5"
   },
+  "_moduleAliases": {
+    "@root": ".",
+    "@alias/logger": "lib/service/logger",
+    "debug": "lib/service/logger/alias-for-debug"
+  },
   "engines": {
     "node": ">=6.11 <9",
     "npm": ">=4",

BIN=BIN
public/images/icons/emacs.png


BIN=BIN
public/images/icons/sublime.png


BIN=BIN
public/images/icons/vim.png


+ 4 - 6
resource/js/app.js

@@ -21,7 +21,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';
@@ -29,7 +28,6 @@ import CustomHeaderEditor from './components/Admin/CustomHeaderEditor';
 
 import * as entities from 'entities';
 
-
 if (!window) {
   window = {};
 }
@@ -150,7 +148,7 @@ if (pageEditorElem) {
     if (componentInstances.page != null) {
       componentInstances.page.setMarkdown(page.revision.body);
     }
-  }
+  };
 
   pageEditor = ReactDOM.render(
     <PageEditor crowi={crowi} crowiRenderer={crowiRenderer}
@@ -190,7 +188,7 @@ if (customCssEditorElem != null) {
   ReactDOM.render(
     <CustomCssEditor inputElem={customCssInputElem} />,
     customCssEditorElem
-  )
+  );
 }
 const customScriptEditorElem = document.getElementById('custom-script-editor');
 if (customScriptEditorElem != null) {
@@ -200,7 +198,7 @@ if (customScriptEditorElem != null) {
   ReactDOM.render(
     <CustomScriptEditor inputElem={customScriptInputElem} />,
     customScriptEditorElem
-  )
+  );
 }
 const customHeaderEditorElem = document.getElementById('custom-header-editor');
 if (customHeaderEditorElem != null) {
@@ -210,7 +208,7 @@ if (customHeaderEditorElem != null) {
   ReactDOM.render(
     <CustomHeaderEditor inputElem={customHeaderInputElem} />,
     customHeaderEditorElem
-  )
+  );
 }
 
 // うわーもうー (commented by Crowi team -- 2018.03.23 Yuki Takei)

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio