Sotaro KARASAWA %!s(int64=10) %!d(string=hai) anos
pai
achega
8f4841d73e

+ 1 - 0
lib/crowi/index.js

@@ -41,6 +41,7 @@ function Crowi (rootdir, env)
 
 
   this.events = {
   this.events = {
     user: new (require(self.eventsDir + 'user'))(this),
     user: new (require(self.eventsDir + 'user'))(this),
+    page: new (require(self.eventsDir + 'page'))(this),
   };
   };
 
 
   if (this.node_env == 'development') {
   if (this.node_env == 'development') {

+ 25 - 0
lib/events/page.js

@@ -0,0 +1,25 @@
+var debug = require('debug')('crowi:events:page');
+var util = require('util');
+var events = require('events');
+var sprintf = require('sprintf');
+
+function PageEvent(crowi) {
+  this.crowi = crowi;
+
+  events.EventEmitter.call(this);
+}
+util.inherits(PageEvent, events.EventEmitter);
+
+PageEvent.prototype.onCreate = function(context, page, user) {
+  var User = this.crowi.model('User');
+  var Page = this.crowi.model('Page');
+
+};
+
+PageEvent.prototype.onUpdate = function(context, page, user) {
+  var User = this.crowi.model('User');
+  var Page = this.crowi.model('Page');
+};
+
+module.exports = PageEvent;
+

+ 2 - 1
lib/form/revision.js

@@ -7,5 +7,6 @@ module.exports = form(
   field('pageForm.path').required(),
   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.currentRevision'),
-  field('pageForm.grant').toInt().required()
+  field('pageForm.grant').toInt().required(),
+  field('pageForm.notify')
 );
 );

+ 153 - 131
lib/models/page.js

@@ -7,6 +7,9 @@ module.exports = function(crowi) {
     , GRANT_SPECIFIED = 3
     , GRANT_SPECIFIED = 3
     , GRANT_OWNER = 4
     , GRANT_OWNER = 4
     , PAGE_GRANT_ERROR = 1
     , PAGE_GRANT_ERROR = 1
+
+    , pageEvent = crowi.event('page')
+
     , pageSchema;
     , pageSchema;
 
 
   function isPortalPath(path) {
   function isPortalPath(path) {
@@ -27,10 +30,27 @@ module.exports = function(crowi) {
     liker: [{ type: ObjectId, ref: 'User', index: true }],
     liker: [{ type: ObjectId, ref: 'User', index: true }],
     seenUsers: [{ type: ObjectId, ref: 'User', index: true }],
     seenUsers: [{ type: ObjectId, ref: 'User', index: true }],
     commentCount: { type: Number, default: 0 },
     commentCount: { type: Number, default: 0 },
+    extented: {
+      type: String,
+      default: '{}',
+      get: function(data) {
+        try {
+          return JSON.parse(data);
+        } catch(e) {
+          return data;
+        }
+      },
+      set: function(data) {
+        return JSON.stringify(data);
+      }
+    },
     createdAt: { type: Date, default: Date.now },
     createdAt: { type: Date, default: Date.now },
     updatedAt: Date
     updatedAt: Date
   });
   });
 
 
+  pageEvent.on('create', pageEvent.onCreate);
+  pageEvent.on('update', pageEvent.onUpdate);
+
   pageSchema.methods.isPublic = function() {
   pageSchema.methods.isPublic = function() {
     if (!this.grant || this.grant == GRANT_PUBLIC) {
     if (!this.grant || this.grant == GRANT_PUBLIC) {
       return true;
       return true;
@@ -92,21 +112,21 @@ module.exports = function(crowi) {
     var self = this,
     var self = this,
       Page = self;
       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);
+        }
+      });
 
 
   };
   };
 
 
@@ -114,21 +134,21 @@ module.exports = function(crowi) {
     var self = this,
     var self = this,
       Page = self;
       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);
+        }
+      });
 
 
   };
   };
 
 
@@ -136,35 +156,35 @@ module.exports = function(crowi) {
     var self = this,
     var self = this,
       Page = self;
       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) {
   pageSchema.methods.seen = function(userData) {
     var self = this,
     var self = this,
       Page = self;
       Page = self;
 
 
-    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'));
+      if (this.isSeenUser(userData)) {
+        debug('seenUsers not updated');
+        return Promise.resolve(this);
       }
       }
 
 
-      var added = self.seenUsers.addToSet(userData);
-      self.save(function(err, data) {
-        if (err) {
-          return reject(err);
+      return new Promise(function(resolve, reject) {
+        if (!userData || !userData._id) {
+          reject(new Error('User data is not valid'));
         }
         }
 
 
-        debug('seenUsers updated!', added);
-        return resolve(self);
+        var added = self.seenUsers.addToSet(userData);
+        self.save(function(err, data) {
+          if (err) {
+            return reject(err);
+          }
+
+          debug('seenUsers updated!', added);
+          return resolve(self);
+        });
       });
       });
-    });
   };
   };
 
 
   pageSchema.statics.populatePageData = function(pageData, revisionId) {
   pageSchema.statics.populatePageData = function(pageData, revisionId) {
@@ -279,13 +299,13 @@ module.exports = function(crowi) {
     var forbiddenPages = [
     var forbiddenPages = [
       /\^|\$|\*|\+|\#/,
       /\^|\$|\*|\+|\#/,
       /^\/_api\/.*/,
       /^\/_api\/.*/,
-      /^\/\-\/.*/,
-      /^\/_r\/.*/,
-      /^\/user\/[^\/]+\/(bookmarks|comments|activities|pages|recent-create|recent-edit)/, // reserved
-      /^http:\/\/.+$/, // avoid miss in renaming
-      /.+\/edit$/,
-      /.+\.md$/,
-      /^\/(installer|register|login|logout|admin|me|files|trash|paste|comments).+/,
+/^\/\-\/.*/,
+/^\/_r\/.*/,
+/^\/user\/[^\/]+\/(bookmarks|comments|activities|pages|recent-create|recent-edit)/, // reserved
+/^http:\/\/.+$/, // avoid miss in renaming
+/.+\/edit$/,
+/.+\.md$/,
+/^\/(installer|register|login|logout|admin|me|files|trash|paste|comments).+/,
     ];
     ];
 
 
     var isCreatable = true;
     var isCreatable = true;
@@ -308,13 +328,13 @@ module.exports = function(crowi) {
 
 
   pageSchema.statics.findUpdatedList = function(offset, limit, cb) {
   pageSchema.statics.findUpdatedList = function(offset, limit, cb) {
     this
     this
-      .find({})
-      .sort({updatedAt: -1})
-      .skip(offset)
-      .limit(limit)
-      .exec(function(err, data) {
-        cb(err, data);
-      });
+    .find({})
+    .sort({updatedAt: -1})
+    .skip(offset)
+    .limit(limit)
+    .exec(function(err, data) {
+      cb(err, data);
+    });
   };
   };
 
 
   pageSchema.statics.findPageById = function(id) {
   pageSchema.statics.findPageById = function(id) {
@@ -400,24 +420,24 @@ module.exports = function(crowi) {
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       Page
       Page
-        .find({ _id: { $in: ids }, grant: GRANT_PUBLIC })
-        //.sort({createdAt: -1}) // TODO optionize
-        .skip(offset)
-        .limit(limit)
-        .populate('revision')
-        .exec(function(err, pages) {
+      .find({ _id: { $in: ids }, grant: GRANT_PUBLIC })
+      //.sort({createdAt: -1}) // TODO optionize
+      .skip(offset)
+      .limit(limit)
+      .populate('revision')
+      .exec(function(err, pages) {
+        if (err) {
+          return reject(err);
+        }
+
+        Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS}, function(err, data) {
           if (err) {
           if (err) {
             return reject(err);
             return reject(err);
           }
           }
 
 
-          Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS}, function(err, data) {
-            if (err) {
-              return reject(err);
-            }
-
-            return resolve(data);
-          });
+          return resolve(data);
         });
         });
+      });
     });
     });
   };
   };
 
 
@@ -429,24 +449,24 @@ module.exports = function(crowi) {
 
 
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       Page
       Page
-        .find({ creator: user._id, grant: GRANT_PUBLIC })
-        .sort({createdAt: -1})
-        .skip(offset)
-        .limit(limit)
-        .populate('revision')
-        .exec(function(err, pages) {
+      .find({ creator: user._id, grant: GRANT_PUBLIC })
+      .sort({createdAt: -1})
+      .skip(offset)
+      .limit(limit)
+      .populate('revision')
+      .exec(function(err, pages) {
+        if (err) {
+          return reject(err);
+        }
+
+        Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS}, function(err, data) {
           if (err) {
           if (err) {
             return reject(err);
             return reject(err);
           }
           }
 
 
-          Page.populate(pages, {path: 'revision.author', model: 'User', select: User.USER_PUBLIC_FIELDS}, function(err, data) {
-            if (err) {
-              return reject(err);
-            }
-
-            return resolve(data);
-          });
+          return resolve(data);
         });
         });
+      });
     });
     });
   };
   };
 
 
@@ -471,20 +491,20 @@ module.exports = function(crowi) {
     return new Promise(function(resolve, reject) {
     return new Promise(function(resolve, reject) {
       // FIXME: might be heavy
       // FIXME: might be heavy
       var q = Page.find({
       var q = Page.find({
-          path: queryReg,
-          redirectTo: null,
-          $or: [
-            {grant: null},
-            {grant: GRANT_PUBLIC},
-            {grant: GRANT_RESTRICTED, grantedUsers: userData._id},
-            {grant: GRANT_SPECIFIED, grantedUsers: userData._id},
-            {grant: GRANT_OWNER, grantedUsers: userData._id},
-          ],
-        })
-        .populate('revision')
-        .sort(sortOpt)
-        .skip(opt.offset)
-        .limit(opt.limit);
+        path: queryReg,
+        redirectTo: null,
+        $or: [
+          {grant: null},
+          {grant: GRANT_PUBLIC},
+          {grant: GRANT_RESTRICTED, grantedUsers: userData._id},
+          {grant: GRANT_SPECIFIED, grantedUsers: userData._id},
+          {grant: GRANT_OWNER, grantedUsers: userData._id},
+        ],
+      })
+      .populate('revision')
+      .sort(sortOpt)
+      .skip(opt.offset)
+      .limit(opt.limit);
 
 
       q.exec(function(err, pages) {
       q.exec(function(err, pages) {
         if (err) {
         if (err) {
@@ -579,6 +599,7 @@ module.exports = function(crowi) {
           }
           }
 
 
           resolve(data);
           resolve(data);
+          pageEvent.emit('update', data, user);
         });
         });
       });
       });
     });
     });
@@ -592,41 +613,42 @@ module.exports = function(crowi) {
       , redirectTo = options.redirectTo || null;
       , redirectTo = options.redirectTo || null;
 
 
     // force public
     // force 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'));
-        }
+      if (isPortalPath(path)) {
+        grant = GRANT_PUBLIC;
+      }
 
 
-        var newPage = new Page();
-        newPage.path = path;
-        newPage.creator = user;
-        newPage.createdAt = Date.now();
-        newPage.updatedAt = Date.now();
-        newPage.redirectTo = redirectTo;
-        newPage.grant = grant;
-        newPage.grantedUsers = [];
-        newPage.grantedUsers.push(user);
-
-        newPage.save(function (err, newPage) {
-          if (err) {
-            return reject(err);
+      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 newRevision = Revision.prepareRevision(newPage, body, user, {format: format});
-          Page.pushRevision(newPage, newRevision, user).then(function(data) {
-            resolve(data);
-          }).catch(function(err) {
-            debug('Push Revision Error on create page', err);
-            return reject(err);
+          var newPage = new Page();
+          newPage.path = path;
+          newPage.creator = user;
+          newPage.createdAt = Date.now();
+          newPage.updatedAt = Date.now();
+          newPage.redirectTo = redirectTo;
+          newPage.grant = grant;
+          newPage.grantedUsers = [];
+          newPage.grantedUsers.push(user);
+
+          newPage.save(function (err, newPage) {
+            if (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.rename = function(pageData, newPagePath, user, options) {
   pageSchema.statics.rename = function(pageData, newPagePath, user, options) {

+ 5 - 0
lib/routes/page.js

@@ -213,6 +213,11 @@ module.exports = function(crowi, app) {
     var grant = pageForm.grant;
     var grant = pageForm.grant;
     var path = pageForm.path;
     var path = pageForm.path;
 
 
+    // TODO: make it pluggable
+    var notify = pageForm.notify || {};
+
+    debug('notify: ', notify);
+
     var redirectPath = encodeURI(path);
     var redirectPath = encodeURI(path);
 
 
     // set to render
     // set to render

+ 15 - 0
lib/service/notification.js

@@ -0,0 +1,15 @@
+'use strict';
+
+function Notification ()
+{
+}
+
+Notification.prototype.noitfyByEmail = function()
+{
+};
+
+Notification.prototype.noitfyByChat = function()
+{
+};
+
+module.exports = Notification;

+ 12 - 2
lib/views/_form.html

@@ -9,7 +9,7 @@
 </div>
 </div>
 {% endif %}
 {% endif %}
 <div id="form-box" class="row">
 <div id="form-box" class="row">
-  <form action="/_/edit" id="page-form" method="post" class="col-md-6 {% if isUploadable() %}uploadable{% endif %}">
+  <form action="/_/edit" id="page-form" method="post" class="col-md-6 {% if isUploadable() %}uploadable{% endif %} page-form">
     <textarea name="pageForm[body]" class="form-control" id="form-body">{% if pageForm.body %}{{ pageForm.body }}{% elseif not page.revision.body %}# {{ path|path2name }}{% else %}{{ page.revision.body }}{% endif %}</textarea>
     <textarea name="pageForm[body]" class="form-control" id="form-body">{% if pageForm.body %}{{ pageForm.body }}{% elseif not page.revision.body %}# {{ path|path2name }}{% else %}{{ page.revision.body }}{% endif %}</textarea>
 
 
     <input type="hidden" name="pageForm[path]" value="{{ path }}">
     <input type="hidden" name="pageForm[path]" value="{{ path }}">
@@ -20,7 +20,17 @@
         ファイルを追加 ...
         ファイルを追加 ...
       </button>#}
       </button>#}
 
 
-      <div class="pull-right form-inline">
+      <div class="pull-right form-inline page-form-setting">
+        <span class="input-group extended-setting">
+          <span class="input-group-addon">
+            <label>
+              <i class="fa fa-slack"></i>
+              <input class="" type="checkbox" name="pageForm[notify][slack][on]" value="1">
+            </label>
+          </span>
+          <input class="form-control" type="text" name="pageForm[notify][slack][channel]" value="{{ page.extented.slack|default('') }}" placeholder="#slack-channel-name">
+        </span>
+
         {% if forceGrant %}
         {% if forceGrant %}
         <input type="hidden" name="pageForm[grant]" value="{{ forceGrant }}">
         <input type="hidden" name="pageForm[grant]" value="{{ forceGrant }}">
         {% else %}
         {% else %}

+ 8 - 0
resource/css/_form.scss

@@ -76,6 +76,14 @@
             content: "Preview";
             content: "Preview";
           }
           }
         }
         }
+
+        .page-form-setting {
+          .extended-setting {
+            label {
+              margin-bottom: 0;
+            }
+          }
+        }
       }
       }
     }
     }