浏览代码

Merge #616 into devel

Maxime Quandalle 9 年之前
父节点
当前提交
4f5cecf738
共有 9 个文件被更改,包括 242 次插入105 次删除
  1. 1 0
      .eslintrc.json
  2. 1 3
      client/components/cards/labels.js
  3. 3 0
      i18n/en.i18n.json
  4. 72 37
      models/boards.js
  5. 12 5
      models/cardComments.js
  6. 20 12
      models/cards.js
  7. 19 14
      models/lists.js
  8. 5 4
      models/unsavedEdits.js
  9. 109 30
      models/users.js

+ 1 - 0
.eslintrc.json

@@ -74,6 +74,7 @@
     "Avatars": true,
     "BlazeComponent": false,
     "BlazeLayout": false,
+    "CollectionHooks": false,
     "DocHead": false,
     "ESSearchResults": false,
     "FastRender": false,

+ 1 - 3
client/components/cards/labels.js

@@ -9,9 +9,7 @@ BlazeComponent.extendComponent({
   },
 
   labels() {
-    return labelColors.map((color) => {
-      return { color, name: '' };
-    });
+    return labelColors.map((color) => ({ color, name: '' }));
   },
 
   isSelected(color) {

+ 3 - 0
i18n/en.i18n.json

@@ -294,5 +294,8 @@
     "watch": "Watch",
     "watching": "Watching",
     "watching-info": "You will be notified of any change in this board",
+    "welcome-board": "Welcome Board",
+    "welcome-list1": "Basics",
+    "welcome-list2": "Advanced",
     "what-to-do": "What do you want to do?"
 }

+ 72 - 37
models/boards.js

@@ -6,25 +6,77 @@ Boards.attachSchema(new SimpleSchema({
   },
   slug: {
     type: String,
+    autoValue() { // eslint-disable-line consistent-return
+      // XXX We need to improve slug management. Only the id should be necessary
+      // to identify a board in the code.
+      // XXX If the board title is updated, the slug should also be updated.
+      // In some cases (Chinese and Japanese for instance) the `getSlug` function
+      // return an empty string. This is causes bugs in our application so we set
+      // a default slug in this case.
+      if (this.isInsert && !this.isSet) {
+        let slug = 'board';
+        const title = this.field('title');
+        if (title.isSet) {
+          slug = getSlug(title.value) || slug;
+        }
+        return slug;
+      }
+    },
   },
   archived: {
     type: Boolean,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return false;
+      }
+    },
   },
   createdAt: {
     type: Date,
-    denyUpdate: true,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert) {
+        return new Date();
+      } else {
+        this.unset();
+      }
+    },
   },
   // XXX Inconsistent field naming
   modifiedAt: {
     type: Date,
-    denyInsert: true,
     optional: true,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isUpdate) {
+        return new Date();
+      } else {
+        this.unset();
+      }
+    },
   },
   // De-normalized number of users that have starred this board
   stars: {
     type: Number,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert) {
+        return 0;
+      }
+    },
   },
   // De-normalized label system
+  'labels': {
+    type: [Object],
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        const colors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
+        const defaultLabelsColors = _.clone(colors).splice(0, 6);
+        return defaultLabelsColors.map((color) => ({
+          color,
+          _id: Random.id(6),
+          name: '',
+        }));
+      }
+    },
+  },
   'labels.$._id': {
     // We don't specify that this field must be unique in the board because that
     // will cause performance penalties and is not necessary since this field is
@@ -47,6 +99,19 @@ Boards.attachSchema(new SimpleSchema({
   // XXX We might want to maintain more informations under the member sub-
   // documents like de-normalized meta-data (the date the member joined the
   // board, the number of contributions, etc.).
+  'members': {
+    type: [Object],
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return [{
+          userId: this.userId,
+          isAdmin: true,
+          isActive: true,
+          isInvited: false,
+        }];
+      }
+    },
+  },
   'members.$.userId': {
     type: String,
   },
@@ -70,6 +135,11 @@ Boards.attachSchema(new SimpleSchema({
       'wisteria',
       'midnight',
     ],
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return Boards.simpleSchema()._schema.color.allowedValues[0];
+      }
+    },
   },
   description: {
     type: String,
@@ -338,41 +408,6 @@ if (Meteor.isServer) {
   });
 }
 
-Boards.before.insert((userId, doc) => {
-  // XXX We need to improve slug management. Only the id should be necessary
-  // to identify a board in the code.
-  // XXX If the board title is updated, the slug should also be updated.
-  // In some cases (Chinese and Japanese for instance) the `getSlug` function
-  // return an empty string. This is causes bugs in our application so we set
-  // a default slug in this case.
-  doc.slug = doc.slug || getSlug(doc.title) || 'board';
-  doc.createdAt = new Date();
-  doc.archived = false;
-  doc.members = doc.members || [{
-    userId,
-    isAdmin: true,
-    isActive: true,
-  }];
-  doc.stars = 0;
-  doc.color = Boards.simpleSchema()._schema.color.allowedValues[0];
-
-  // Handle labels
-  const colors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
-  const defaultLabelsColors = _.clone(colors).splice(0, 6);
-  doc.labels = defaultLabelsColors.map((color) => {
-    return {
-      color,
-      _id: Random.id(6),
-      name: '',
-    };
-  });
-});
-
-Boards.before.update((userId, doc, fieldNames, modifier) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = new Date();
-});
-
 if (Meteor.isServer) {
   // Let MongoDB ensure that a member is not included twice in the same board
   Meteor.startup(() => {

+ 12 - 5
models/cardComments.js

@@ -16,10 +16,22 @@ CardComments.attachSchema(new SimpleSchema({
   createdAt: {
     type: Date,
     denyUpdate: false,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert) {
+        return new Date();
+      } else {
+        this.unset();
+      }
+    },
   },
   // XXX Should probably be called `authorId`
   userId: {
     type: String,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return this.userId;
+      }
+    },
   },
 }));
 
@@ -44,11 +56,6 @@ CardComments.helpers({
 
 CardComments.hookOptions.after.update = { fetchPrevious: false };
 
-CardComments.before.insert((userId, doc) => {
-  doc.createdAt = new Date();
-  doc.userId = userId;
-});
-
 if (Meteor.isServer) {
   CardComments.after.insert((userId, doc) => {
     Activities.insert({

+ 20 - 12
models/cards.js

@@ -9,6 +9,11 @@ Cards.attachSchema(new SimpleSchema({
   },
   archived: {
     type: Boolean,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return false;
+      }
+    },
   },
   listId: {
     type: String,
@@ -25,10 +30,19 @@ Cards.attachSchema(new SimpleSchema({
   },
   createdAt: {
     type: Date,
-    denyUpdate: true,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert) {
+        return new Date();
+      } else {
+        this.unset();
+      }
+    },
   },
   dateLastActivity: {
     type: Date,
+    autoValue() {
+      return new Date();
+    },
   },
   description: {
     type: String,
@@ -46,6 +60,11 @@ Cards.attachSchema(new SimpleSchema({
   // the `members` field?
   userId: {
     type: String,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return this.userId;
+      }
+    },
   },
   sort: {
     type: Number,
@@ -190,17 +209,6 @@ Cards.mutations({
   },
 });
 
-Cards.before.insert((userId, doc) => {
-  doc.createdAt = new Date();
-  doc.dateLastActivity = new Date();
-  if(!doc.hasOwnProperty('archived')){
-    doc.archived = false;
-  }
-  if (!doc.userId) {
-    doc.userId = userId;
-  }
-});
-
 if (Meteor.isServer) {
   Cards.after.insert((userId, doc) => {
     Activities.insert({

+ 19 - 14
models/lists.js

@@ -6,13 +6,24 @@ Lists.attachSchema(new SimpleSchema({
   },
   archived: {
     type: Boolean,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return false;
+      }
+    },
   },
   boardId: {
     type: String,
   },
   createdAt: {
     type: Date,
-    denyUpdate: true,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert) {
+        return new Date();
+      } else {
+        this.unset();
+      }
+    },
   },
   sort: {
     type: Number,
@@ -22,8 +33,14 @@ Lists.attachSchema(new SimpleSchema({
   },
   updatedAt: {
     type: Date,
-    denyInsert: true,
     optional: true,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isUpdate) {
+        return new Date();
+      } else {
+        this.unset();
+      }
+    },
   },
 }));
 
@@ -73,18 +90,6 @@ Lists.mutations({
 
 Lists.hookOptions.after.update = { fetchPrevious: false };
 
-Lists.before.insert((userId, doc) => {
-  doc.createdAt = new Date();
-  doc.archived = false;
-  if (!doc.userId)
-    doc.userId = userId;
-});
-
-Lists.before.update((userId, doc, fieldNames, modifier) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = new Date();
-});
-
 if (Meteor.isServer) {
   Lists.after.insert((userId, doc) => {
     Activities.insert({

+ 5 - 4
models/unsavedEdits.js

@@ -14,6 +14,11 @@ UnsavedEditCollection.attachSchema(new SimpleSchema({
   },
   userId: {
     type: String,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return this.userId;
+      }
+    },
   },
 }));
 
@@ -28,7 +33,3 @@ if (Meteor.isServer) {
     fetch: ['userId'],
   });
 }
-
-UnsavedEditCollection.before.insert((userId, doc) => {
-  doc.userId = userId;
-});

+ 109 - 30
models/users.js

@@ -1,5 +1,95 @@
 Users = Meteor.users;
 
+Users.attachSchema(new SimpleSchema({
+  username: {
+    type: String,
+    optional: true,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        const name = this.field('profile.fullname');
+        if (name.isSet) {
+          return name.value.toLowerCase().replace(/\s/g, '');
+        }
+      }
+    },
+  },
+  emails: {
+    type: [Object],
+    optional: true,
+  },
+  'emails.$.address': {
+    type: String,
+    regEx: SimpleSchema.RegEx.Email,
+  },
+  'emails.$.verified': {
+    type: Boolean,
+  },
+  createdAt: {
+    type: Date,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert) {
+        return new Date();
+      } else {
+        this.unset();
+      }
+    },
+  },
+  profile: {
+    type: Object,
+    optional: true,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert && !this.isSet) {
+        return {};
+      }
+    },
+  },
+  'profile.avatarUrl': {
+    type: String,
+    optional: true,
+  },
+  'profile.emailBuffer': {
+    type: [String],
+    optional: true,
+  },
+  'profile.fullname': {
+    type: String,
+    optional: true,
+  },
+  'profile.initials': {
+    type: String,
+    optional: true,
+  },
+  'profile.invitedBoards': {
+    type: [String],
+    optional: true,
+  },
+  'profile.language': {
+    type: String,
+    optional: true,
+  },
+  'profile.notifications': {
+    type: [String],
+    optional: true,
+  },
+  'profile.starredBoards': {
+    type: [String],
+    optional: true,
+  },
+  'profile.tags': {
+    type: [String],
+    optional: true,
+  },
+  services: {
+    type: Object,
+    optional: true,
+    blackbox: true,
+  },
+  heartbeat: {
+    type: Date,
+    optional: true,
+  },
+}));
+
 // Search a user in the complete server database by its name or username. This
 // is used for instance to add a new user to a board.
 const searchInFields = ['username', 'profile.fullname'];
@@ -259,14 +349,6 @@ if (Meteor.isServer) {
   });
 }
 
-Users.before.insert((userId, doc) => {
-  doc.profile = doc.profile || {};
-
-  if (!doc.username && doc.profile.fullname) {
-    doc.username = doc.profile.fullname.toLowerCase().replace(/\s/g, '');
-  }
-});
-
 if (Meteor.isServer) {
   // Let mongoDB ensure username unicity
   Meteor.startup(() => {
@@ -306,32 +388,29 @@ if (Meteor.isServer) {
     incrementBoards(_.difference(newIds, oldIds), +1);
   });
 
-  // XXX i18n
+  const fakeUserId = new Meteor.EnvironmentVariable();
+  const getUserId = CollectionHooks.getUserId;
+  CollectionHooks.getUserId = () => {
+    return fakeUserId.get() || getUserId();
+  };
+
   Users.after.insert((userId, doc) => {
-    const ExampleBoard = {
-      title: 'Welcome Board',
-      userId: doc._id,
-      permission: 'private',
+    const fakeUser = {
+      extendAutoValueContext: {
+        userId: doc._id,
+      },
     };
 
-    // Insert the Welcome Board
-    Boards.insert(ExampleBoard, (err, boardId) => {
-
-      ['Basics', 'Advanced'].forEach((title) => {
-        const list = {
-          title,
-          boardId,
-          userId: ExampleBoard.userId,
-
-          // XXX Not certain this is a bug, but we except these fields get
-          // inserted by the Lists.before.insert collection-hook. Since this
-          // hook is not called in this case, we have to dublicate the logic and
-          // set them here.
-          archived: false,
-          createdAt: new Date(),
-        };
+    fakeUserId.withValue(doc._id, () => {
+      // Insert the Welcome Board
+      Boards.insert({
+        title: TAPi18n.__('welcome-board'),
+        permission: 'private',
+      }, fakeUser, (err, boardId) => {
 
-        Lists.insert(list);
+        ['welcome-list1', 'welcome-list2'].forEach((title) => {
+          Lists.insert({ title: TAPi18n.__(title), boardId }, fakeUser);
+        });
       });
     });
   });