mirror of
https://github.com/wekan/wekan.git
synced 2025-04-20 12:07:11 -04:00
Merge #616 into devel
This commit is contained in:
commit
4f5cecf738
9 changed files with 242 additions and 105 deletions
|
@ -74,6 +74,7 @@
|
|||
"Avatars": true,
|
||||
"BlazeComponent": false,
|
||||
"BlazeLayout": false,
|
||||
"CollectionHooks": false,
|
||||
"DocHead": false,
|
||||
"ESSearchResults": false,
|
||||
"FastRender": false,
|
||||
|
|
|
@ -9,9 +9,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
labels() {
|
||||
return labelColors.map((color) => {
|
||||
return { color, name: '' };
|
||||
});
|
||||
return labelColors.map((color) => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
|
|
|
@ -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?"
|
||||
}
|
||||
|
|
109
models/boards.js
109
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(() => {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
139
models/users.js
139
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) => {
|
||||
fakeUserId.withValue(doc._id, () => {
|
||||
// Insert the Welcome Board
|
||||
Boards.insert({
|
||||
title: TAPi18n.__('welcome-board'),
|
||||
permission: 'private',
|
||||
}, fakeUser, (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(),
|
||||
};
|
||||
|
||||
Lists.insert(list);
|
||||
['welcome-list1', 'welcome-list2'].forEach((title) => {
|
||||
Lists.insert({ title: TAPi18n.__(title), boardId }, fakeUser);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue