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

Merge pull request #355 from weseek/master

release v3.0.7
Yuki Takei 8 лет назад
Родитель
Сommit
14289d758c

+ 13 - 1
CHANGES.md

@@ -1,8 +1,19 @@
 CHANGES
 ========
 
-## 3.0.6-RC
+## 3.0.7-RC
 
+* Improvement: Enable to download an attached file with original name
+* Improvement: Use MongoDB for session store instead of Redis
+* Improvement: Update dropzone overlay icons and styles
+* Fix: Dropzone overlay elements doesn't show
+    * Introduced by 3.0.0
+* Fix: Broken page path of timeline
+    * Introduced by 3.0.4
+
+## 3.0.6
+
+* Improvement: Automatically bind external accounts newly logged in to local accounts when username match
 * Improvement: Simplify configuration for Slack Web API
 * Support: Use 'slack-node' instead of '@slack/client'
 * Support: Upgrade libs
@@ -15,6 +26,7 @@ CHANGES
 
 ## 3.0.5
 
+* Improvement: Update lsx icons and styles
 * Fix: lsx plugins doesn't show page names
 
 ## 3.0.4

+ 1 - 2
app.json

@@ -18,13 +18,12 @@
     },
     "INSTALL_PLUGINS": {
       "description": "Comma-separated list of plugin package names to install.",
-      "value": "growi-plugin-lsx",
+      "value": "growi-plugin-lsx,growi-plugin-pukiwiki-like-linker",
       "required": false
     }
   },
   "addons": [
     "mongolab",
-    "redistogo",
     {
       "plan": "bonsai:sandbox-6",
       "options": {

+ 0 - 1
config/env.dev.js

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

+ 19 - 20
lib/crowi/index.js

@@ -67,6 +67,14 @@ function Crowi (rootdir, env)
   }
 };
 
+function getMongoUrl(env) {
+  return env.MONGOLAB_URI || // for B.C.
+    env.MONGODB_URI || // MONGOLAB changes their env name
+    env.MONGOHQ_URL ||
+    env.MONGO_URI ||
+    ((process.env.NODE_ENV === 'test') ? 'mongodb://localhost/growi_test' : 'mongodb://localhost/growi');
+}
+
 Crowi.prototype.init = function() {
   var self = this;
 
@@ -144,12 +152,7 @@ Crowi.prototype.setupDatabase = function() {
   // mongoUri = mongodb://user:password@host/dbname
   mongoose.Promise = global.Promise;
 
-  var mongoUri = this.env.MONGOLAB_URI || // for B.C.
-    this.env.MONGODB_URI || // MONGOLAB changes their env name
-    this.env.MONGOHQ_URL ||
-    this.env.MONGO_URI ||
-    ((process.env.NODE_ENV === 'test') ? 'mongodb://localhost/growi_test' : 'mongodb://localhost/growi')
-    ;
+  const mongoUri = getMongoUrl(this.env);
 
   return mongoose.connect(mongoUri).then(
     () => {},
@@ -166,7 +169,7 @@ Crowi.prototype.setupSessionConfig = function() {
     , sessionConfig
     , sessionAge = (1000*3600*24*30)
     , redisUrl = this.env.REDISTOGO_URL || this.env.REDIS_URL || null
-    , RedisStore
+    , mongoUrl = getMongoUrl(this.env)
     ;
 
   return new Promise(function(resolve, reject) {
@@ -181,22 +184,18 @@ Crowi.prototype.setupSessionConfig = function() {
     };
 
     if (self.env.SESSION_NAME) {
-        sessionConfig.name = self.env.SESSION_NAME;
+      sessionConfig.name = self.env.SESSION_NAME;
     }
 
+    // use Redis for session store
     if (redisUrl) {
-      var ru   = require('url').parse(redisUrl);
-      var redis = require('redis');
-      var redisClient = redis.createClient(ru.port, ru.hostname);
-      if (ru.auth) {
-        redisClient.auth(ru.auth.split(':')[1]);
-      }
-
-      RedisStore = require('connect-redis')(session);
-      sessionConfig.store = new RedisStore({
-        prefix: 'crowi:sess:',
-        client: redisClient,
-      });
+      const RedisStore = require('connect-redis')(session);
+      sessionConfig.store = new RedisStore({ url: redisUrl });
+    }
+    // use MongoDB for session store
+    else {
+      const MongoStore = require('connect-mongo')(session);
+      sessionConfig.store = new MongoStore({ url: mongoUrl });
     }
 
     self.sessionConfig = sessionConfig;

+ 28 - 32
lib/routes/attachment.js

@@ -6,6 +6,7 @@ module.exports = function(crowi, app) {
     , User = crowi.model('User')
     , Page = crowi.model('Page')
     , config = crowi.getConfig()
+    , path = require('path')
     , fs = require('fs')
     , fileUploader = require('../util/fileUploader')(crowi, app)
     , ApiResponse = require('../util/apiResponse')
@@ -14,41 +15,36 @@ module.exports = function(crowi, app) {
 
   actions.api = api;
 
-  api.redirector = function(req, res, next){
-    var id = req.params.id;
+  api.download = function(req, res) {
+    const id = req.params.id;
 
     Attachment.findById(id)
-    .then(function(data) {
-
-      // TODO: file delivery plugin for cdn
-      Attachment.findDeliveryFile(data)
-      .then(fileName => {
-        const encodedFileName = encodeURIComponent(data.originalName);
-
-        var deliveryFile = {
-          fileName: fileName,
-          options: {
-            headers: {
-              'Content-Type': data.fileFormat,
-              'Content-Disposition': `inline;filename*=UTF-8''${encodedFileName}`,
-            },
-          },
-        };
-
-        if (deliveryFile.fileName.match(/^\/uploads/)) {
-          debug('Using loacal file module, just redirecting.')
-          return res.redirect(deliveryFile.fileName);
-        } else {
-          return res.sendFile(deliveryFile.fileName, deliveryFile.options);
-        }
-      }).catch(err => {
-        //debug('error', err);
-      });
-    }).catch((err) => {
-      //debug('err', err);
+      .then(function(data) {
+
+        Attachment.findDeliveryFile(data)
+          .then(fileName => {
+
+            // local
+            if (fileName.match(/^\/uploads/)) {
+              return res.download(path.join(crowi.publicDir, fileName), data.originalName);
+            }
+            // aws
+            else {
+              const options = {
+                headers: {
+                  'Content-Type': 'application/force-download',
+                  'Content-Disposition': `inline;filename*=UTF-8''${encodeURIComponent(data.originalName)}`,
+                }
+              };
+              return res.sendFile(fileName, options);
+            }
+          });
+      })
       // not found
-      return res.status(404).sendFile(crowi.publicDir + '/images/file-not-found.png');
-    });
+      .catch((err) => {
+        debug('download err', err);
+        return res.status(404).sendFile(crowi.publicDir + '/images/file-not-found.png');
+      });
   };
 
   /**

+ 1 - 1
lib/routes/index.js

@@ -141,7 +141,7 @@ module.exports = function(crowi, app) {
 
   app.get( '/:id([0-9a-z]{24})'       , loginRequired(crowi, app, false) , page.api.redirector);
   app.get( '/_r/:id([0-9a-z]{24})'    , loginRequired(crowi, app, false) , page.api.redirector); // alias
-  app.get( '/files/:id([0-9a-z]{24})' , loginRequired(crowi, app, false) , attachment.api.redirector);
+  app.get( '/download/:id([0-9a-z]{24})' , loginRequired(crowi, app, false) , attachment.api.download);
 
   app.get( '/_search'                 , loginRequired(crowi, app, false) , search.searchPage);
   app.get( '/_api/search'             , accessTokenParser , loginRequired(crowi, app, false) , search.api.search);

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

@@ -28,7 +28,7 @@
       {% for page in pages %}
       <div class="timeline-body" id="id-{{ page.id }}" data-page-path="{{ page.path }}">
         <div class="panel panel-timeline">
-          <div class="panel-heading"><a href="{{ page.path }}">{{ page.path }}</a></div>
+          <div class="panel-heading"><a href="{{ page.path }}">{{ decodeURIComponent(page.path) }}</a></div>
           <div class="panel-body">
             <div class="revision-body wiki"></div>
           </div>

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "3.0.6-RC",
+  "version": "3.0.7-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -64,6 +64,7 @@
     "check-node-version": "^3.1.1",
     "codemirror": "^5.36.0",
     "connect-flash": "~0.1.1",
+    "connect-mongo": "^2.0.1",
     "connect-redis": "^3.3.0",
     "cookie-parser": "^1.4.3",
     "cross-env": "^5.0.5",
@@ -132,7 +133,6 @@
     "react-codemirror2": "^4.2.1",
     "react-dom": "^16.2.0",
     "react-dropzone": "^4.2.7",
-    "redis": "^2.7.1",
     "reveal.js": "^3.5.0",
     "rimraf": "^2.6.1",
     "sass-loader": "^7.0.1",

+ 13 - 6
resource/js/components/PageAttachment/Attachment.js

@@ -34,6 +34,12 @@ export default class Attachment extends React.Component {
 
     const fileType = <span className="attachment-filetype label label-default">{attachment.fileFormat}</span>;
 
+    const btnDownload = (this.props.isUserLoggedIn)
+        ? <a className="attachment-download" href={`/download/${attachment._id}`}>
+            <i className="icon-cloud-download"></i>
+          </a>
+        : '';
+
     const btnTrash = (this.props.isUserLoggedIn)
         ? <a className="text-danger attachment-delete" onClick={this._onAttachmentDeleteClicked}>
             <i className="icon-trash"></i>
@@ -42,16 +48,17 @@ export default class Attachment extends React.Component {
 
     return (
       <li>
-          <User user={attachment.creator} />
-          <i className={formatIcon}></i>
+        <User user={attachment.creator} />
+        <i className={formatIcon}></i>
 
-          <a href={attachment.url}> {attachment.originalName}</a>
+        <a href={attachment.url}> {attachment.originalName}</a>
 
-          {fileType}
+        {fileType}
 
-          {fileInUse}
+        {fileInUse}
 
-          {btnTrash}
+        {btnDownload}
+        {btnTrash}
       </li>
     );
   }

+ 1 - 0
resource/js/components/PageEditor/Editor.js

@@ -296,6 +296,7 @@ export default class Editor extends React.Component {
   renderOverlay() {
     const overlayStyle = {
       position: 'absolute',
+      zIndex: 4,  // forward than .CodeMirror-gutters
       top: 0,
       right: 0,
       bottom: 0,

+ 5 - 0
resource/styles/scss/_attachments.scss

@@ -15,6 +15,11 @@
     font-weight: normal;
   }
 
+  .attachment-download {
+    cursor: pointer;
+    margin: 0 0 0 4px;
+  }
+
   .attachment-delete {
     cursor: pointer;
     margin: 0 0 0 4px;

+ 7 - 8
resource/styles/scss/_on-edit.scss

@@ -178,7 +178,7 @@ body.on-edit {
       @mixin insertFontAwesome($code) {
         &:before {
           margin-right: 0.2em;
-          font-family: FontAwesome;
+          font-family: 'simple-line-icons';
           content: $code;
         }
       }
@@ -193,9 +193,8 @@ body.on-edit {
         margin: 0 15px;
       }
       .dropzone-overlay-content {
-        font-size: 2em;
-        padding: 0.2em;
-        border-radius: 5px;
+        font-size: 2.5em;
+        padding: 0.5em;
       }
 
       // unuploadable or rejected
@@ -223,7 +222,7 @@ body.on-edit {
       &.dropzone-unuploadable {
         .dropzone-overlay-content {
           // insert content
-          @include insertFontAwesome("\f06a ");  // fa-exclamation-circle
+          @include insertFontAwesome("\e617");  // icon-exclamation
           &:after {
             content: "File uploading is disabled";
           }
@@ -238,7 +237,7 @@ body.on-edit {
           }
           .dropzone-overlay-content {
             // insert content
-            @include insertFontAwesome("\f093");  // fa-upload
+            @include insertFontAwesome("\e084");  // icon-cloud-upload
             &:after {
               content: "Drop here to upload";
             }
@@ -250,7 +249,7 @@ body.on-edit {
         // file type mismatch
         &.dropzone-rejected:not(.dropzone-uploadablefile) .dropzone-overlay-content {
           // insert content
-          @include insertFontAwesome("\f03e");  // fa-picture-o
+          @include insertFontAwesome("\e032");  // icon-picture
           &:after {
             content: "Only an image file is allowed";
           }
@@ -258,7 +257,7 @@ body.on-edit {
         // multiple files
         &.dropzone-accepted.dropzone-rejected .dropzone-overlay-content {
           // insert content
-          @include insertFontAwesome("\f071");  // fa-fa-exclamation-triangle
+          @include insertFontAwesome("\e617");  // icon-exclamation
           &:after {
             content: "Only 1 file is allowed";
           }

+ 44 - 3
yarn.lock

@@ -1293,6 +1293,10 @@ buffer-equal-constant-time@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
 
+buffer-shims@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
+
 buffer-xor@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
@@ -1724,6 +1728,12 @@ connect-injector@^0.4.2:
     stream-buffers "^0.2.3"
     uberproto "^1.1.0"
 
+connect-mongo@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/connect-mongo/-/connect-mongo-2.0.1.tgz#514d649cff1d5d5546c087193245bb54ff5b703b"
+  dependencies:
+    mongodb "^2.0.36"
+
 connect-redis@^3.3.0:
   version "3.3.3"
   resolved "https://registry.yarnpkg.com/connect-redis/-/connect-redis-3.3.3.tgz#0fb8f370192f62da75ec7a9507807599fbe15b37"
@@ -2435,6 +2445,10 @@ es6-map@^0.1.3:
     es6-symbol "~3.1.1"
     event-emitter "~0.3.5"
 
+es6-promise@3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.2.1.tgz#ec56233868032909207170c39448e24449dd1fc4"
+
 es6-set@~0.1.5:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
@@ -4592,6 +4606,13 @@ moment@^2.10.6:
   version "2.20.1"
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd"
 
+mongodb-core@2.1.19:
+  version "2.1.19"
+  resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.19.tgz#00fbd5e5a3573763b9171cfd844e60a8f2a3a18b"
+  dependencies:
+    bson "~1.0.4"
+    require_optional "~1.0.0"
+
 mongodb-core@3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-3.0.1.tgz#ff6dc36ee96ff596953d80a6840d6731bc92efed"
@@ -4605,6 +4626,14 @@ mongodb@3.0.1:
   dependencies:
     mongodb-core "3.0.1"
 
+mongodb@^2.0.36:
+  version "2.2.35"
+  resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-2.2.35.tgz#cd1b5af8a9463e3f9a787fa5b3d05565579730f9"
+  dependencies:
+    es6-promise "3.2.1"
+    mongodb-core "2.1.19"
+    readable-stream "2.2.7"
+
 mongoose-legacy-pluralize@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.1.tgz#31ae25db45c30f1448c0f93f52769e903367c701"
@@ -6041,6 +6070,18 @@ readable-stream@1.1.x:
     isarray "0.0.1"
     string_decoder "~0.10.x"
 
+readable-stream@2.2.7:
+  version "2.2.7"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.7.tgz#07057acbe2467b22042d36f98c5ad507054e95b1"
+  dependencies:
+    buffer-shims "~1.0.0"
+    core-util-is "~1.0.0"
+    inherits "~2.0.1"
+    isarray "~1.0.0"
+    process-nextick-args "~1.0.6"
+    string_decoder "~1.0.0"
+    util-deprecate "~1.0.1"
+
 readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.3.3:
   version "2.3.3"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
@@ -6083,7 +6124,7 @@ redis-parser@^2.6.0:
   version "2.6.0"
   resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b"
 
-redis@^2.1.0, redis@^2.7.1:
+redis@^2.1.0:
   version "2.8.0"
   resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02"
   dependencies:
@@ -6313,7 +6354,7 @@ require-uncached@^1.0.3:
     caller-path "^0.1.0"
     resolve-from "^1.0.0"
 
-require_optional@^1.0.1:
+require_optional@^1.0.1, require_optional@~1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e"
   dependencies:
@@ -6889,7 +6930,7 @@ string@^3.0.1:
   version "3.3.3"
   resolved "https://registry.yarnpkg.com/string/-/string-3.3.3.tgz#5ea211cd92d228e184294990a6cc97b366a77cb0"
 
-string_decoder@^1.0.0, string_decoder@~1.0.3:
+string_decoder@^1.0.0, string_decoder@~1.0.0, string_decoder@~1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
   dependencies: