mirror of
https://github.com/wekan/wekan.git
synced 2026-02-03 07:01:47 +01:00
Merge branch 'master' into lib-change
This commit is contained in:
commit
c3458855bd
425 changed files with 15176 additions and 28903 deletions
|
|
@ -108,7 +108,7 @@ if (Meteor.isServer) {
|
|||
let participants = [];
|
||||
let watchers = [];
|
||||
let title = 'act-activity-notify';
|
||||
let board = null;
|
||||
const board = Boards.findOne(activity.boardId);
|
||||
const description = `act-${activity.activityType}`;
|
||||
const params = {
|
||||
activityId: activity._id,
|
||||
|
|
@ -122,8 +122,11 @@ if (Meteor.isServer) {
|
|||
params.userId = activity.userId;
|
||||
}
|
||||
if (activity.boardId) {
|
||||
board = activity.board();
|
||||
params.board = board.title;
|
||||
if (board.title.length > 0) {
|
||||
params.board = board.title;
|
||||
} else {
|
||||
params.board = '';
|
||||
}
|
||||
title = 'act-withBoardTitle';
|
||||
params.url = board.absoluteUrl();
|
||||
params.boardId = activity.boardId;
|
||||
|
|
@ -283,7 +286,10 @@ if (Meteor.isServer) {
|
|||
);
|
||||
}
|
||||
Notifications.getUsers(watchers).forEach(user => {
|
||||
Notifications.notify(user, title, description, params);
|
||||
// don't notify a user of their own behavior
|
||||
if (user._id !== userId) {
|
||||
Notifications.notify(user, title, description, params);
|
||||
}
|
||||
});
|
||||
|
||||
const integrations = Integrations.find({
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ function onAttachmentUploaded(fileRef) {
|
|||
type: 'card',
|
||||
activityType: 'addAttachment',
|
||||
attachmentId: fileRef._id,
|
||||
// this preserves the name so that notifications can be meaningful after
|
||||
// this file is removed
|
||||
attachmentName: fileRef.versions.original.name,
|
||||
boardId: fileRef.meta.boardId,
|
||||
cardId: fileRef.meta.cardId,
|
||||
listId: fileRef.meta.listId,
|
||||
|
|
@ -70,6 +73,9 @@ function onAttachmentRemoving(cursor) {
|
|||
type: 'card',
|
||||
activityType: 'deleteAttachment',
|
||||
attachmentId: file._id,
|
||||
// this preserves the name so that notifications can be meaningful after
|
||||
// this file is removed
|
||||
attachmentName: file.versions.original.name,
|
||||
boardId: meta.boardId,
|
||||
cardId: meta.cardId,
|
||||
listId: meta.listId,
|
||||
|
|
|
|||
|
|
@ -493,6 +493,14 @@ Boards.attachSchema(
|
|||
type: String,
|
||||
defaultValue: 'board',
|
||||
},
|
||||
sort: {
|
||||
/**
|
||||
* Sort value
|
||||
*/
|
||||
type: Number,
|
||||
decimal: true,
|
||||
defaultValue: -1,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
@ -806,7 +814,11 @@ Boards.helpers({
|
|||
if (term) {
|
||||
const regex = new RegExp(term, 'i');
|
||||
|
||||
query.$or = [{ title: regex }, { description: regex }];
|
||||
query.$or = [
|
||||
{ title: regex },
|
||||
{ description: regex },
|
||||
{ customFields: { $elemMatch: { value: regex } } },
|
||||
];
|
||||
}
|
||||
|
||||
return Cards.find(query, projection);
|
||||
|
|
@ -1182,6 +1194,10 @@ Boards.mutations({
|
|||
setPresentParentTask(presentParentTask) {
|
||||
return { $set: { presentParentTask } };
|
||||
},
|
||||
|
||||
move(sortIndex) {
|
||||
return { $set: { sort: sortIndex } };
|
||||
},
|
||||
});
|
||||
|
||||
function boardRemover(userId, doc) {
|
||||
|
|
@ -1279,6 +1295,17 @@ if (Meteor.isServer) {
|
|||
});
|
||||
}
|
||||
|
||||
// Insert new board at last position in sort order.
|
||||
Boards.before.insert((userId, doc) => {
|
||||
const lastBoard = Boards.findOne(
|
||||
{ sort: { $exists: true } },
|
||||
{ sort: { sort: -1 } },
|
||||
);
|
||||
if (lastBoard && typeof lastBoard.sort !== 'undefined') {
|
||||
doc.sort = lastBoard.sort + 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// Let MongoDB ensure that a member is not included twice in the same board
|
||||
Meteor.startup(() => {
|
||||
|
|
@ -1462,7 +1489,7 @@ if (Meteor.isServer) {
|
|||
'members.userId': paramUserId,
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
).map(function(board) {
|
||||
return {
|
||||
|
|
@ -1492,7 +1519,12 @@ if (Meteor.isServer) {
|
|||
Authentication.checkUserId(req.userId);
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: Boards.find({ permission: 'public' }).map(function(doc) {
|
||||
data: Boards.find(
|
||||
{ permission: 'public' },
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
).map(function(doc) {
|
||||
return {
|
||||
_id: doc._id,
|
||||
title: doc.title,
|
||||
|
|
|
|||
132
models/cards.js
132
models/cards.js
|
|
@ -304,6 +304,42 @@ Cards.attachSchema(
|
|||
optional: true,
|
||||
defaultValue: '',
|
||||
},
|
||||
vote: {
|
||||
/**
|
||||
* vote object, see below
|
||||
*/
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
'vote.question': {
|
||||
type: String,
|
||||
defaultValue: '',
|
||||
},
|
||||
'vote.positive': {
|
||||
/**
|
||||
* list of members (user IDs)
|
||||
*/
|
||||
type: [String],
|
||||
optional: true,
|
||||
defaultValue: [],
|
||||
},
|
||||
'vote.negative': {
|
||||
/**
|
||||
* list of members (user IDs)
|
||||
*/
|
||||
type: [String],
|
||||
optional: true,
|
||||
defaultValue: [],
|
||||
},
|
||||
'vote.end': {
|
||||
type: Date,
|
||||
optional: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
'vote.public': {
|
||||
type: Boolean,
|
||||
defaultValue: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
@ -981,6 +1017,50 @@ Cards.helpers({
|
|||
}
|
||||
},
|
||||
|
||||
getVoteQuestion() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
if (card && card.vote) return card.vote.question;
|
||||
else return null;
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
if (board && board.vote) return board.vote.question;
|
||||
else return null;
|
||||
} else if (this.vote) {
|
||||
return this.vote.question;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
getVotePublic() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
if (card && card.vote) return card.vote.public;
|
||||
else return null;
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
if (board && board.vote) return board.vote.public;
|
||||
else return null;
|
||||
} else if (this.vote) {
|
||||
return this.vote.public;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
voteMemberPositive() {
|
||||
if (this.vote && this.vote.positive)
|
||||
return Users.find({ _id: { $in: this.vote.positive } });
|
||||
return [];
|
||||
},
|
||||
|
||||
voteMemberNegative() {
|
||||
if (this.vote && this.vote.negative)
|
||||
return Users.find({ _id: { $in: this.vote.negative } });
|
||||
return [];
|
||||
},
|
||||
|
||||
getId() {
|
||||
if (this.isLinked()) {
|
||||
return this.linkedId;
|
||||
|
|
@ -1397,6 +1477,58 @@ Cards.mutations({
|
|||
},
|
||||
};
|
||||
},
|
||||
setVoteQuestion(question, publicVote) {
|
||||
return {
|
||||
$set: {
|
||||
vote: {
|
||||
question,
|
||||
public: publicVote,
|
||||
positive: [],
|
||||
negative: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
unsetVote() {
|
||||
return {
|
||||
$unset: {
|
||||
vote: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
setVote(userId, forIt) {
|
||||
switch (forIt) {
|
||||
case true:
|
||||
// vote for it
|
||||
return {
|
||||
$pull: {
|
||||
'vote.negative': userId,
|
||||
},
|
||||
$addToSet: {
|
||||
'vote.positive': userId,
|
||||
},
|
||||
};
|
||||
case false:
|
||||
// vote against
|
||||
return {
|
||||
$pull: {
|
||||
'vote.positive': userId,
|
||||
},
|
||||
$addToSet: {
|
||||
'vote.negative': userId,
|
||||
},
|
||||
};
|
||||
|
||||
default:
|
||||
// Remove votes
|
||||
return {
|
||||
$pull: {
|
||||
'vote.positive': userId,
|
||||
'vote.negative': userId,
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
//FUNCTIONS FOR creation of Activities
|
||||
|
|
|
|||
|
|
@ -369,6 +369,9 @@ if (Meteor.isServer) {
|
|||
activityType: 'createList',
|
||||
boardId: doc.boardId,
|
||||
listId: doc._id,
|
||||
// this preserves the name so that the activity can be useful after the
|
||||
// list is deleted
|
||||
title: doc.title,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -397,6 +400,9 @@ if (Meteor.isServer) {
|
|||
activityType: 'archivedList',
|
||||
listId: doc._id,
|
||||
boardId: doc.boardId,
|
||||
// this preserves the name so that the activity can be useful after the
|
||||
// list is deleted
|
||||
title: doc.title,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ if (Meteor.isServer) {
|
|||
return process.env.CAS_ENABLED === 'true';
|
||||
}
|
||||
|
||||
function isApiEnabled() {
|
||||
return process.env.WITH_API === 'true';
|
||||
}
|
||||
|
||||
Meteor.methods({
|
||||
sendInvitation(emails, boards) {
|
||||
check(emails, [String]);
|
||||
|
|
@ -314,6 +318,10 @@ if (Meteor.isServer) {
|
|||
return isCasEnabled();
|
||||
},
|
||||
|
||||
_isApiEnabled() {
|
||||
return isApiEnabled();
|
||||
},
|
||||
|
||||
// Gets all connection methods to use it in the Template
|
||||
getAuthenticationsEnabled() {
|
||||
return {
|
||||
|
|
@ -326,6 +334,10 @@ if (Meteor.isServer) {
|
|||
getDefaultAuthenticationMethod() {
|
||||
return process.env.DEFAULT_AUTHENTICATION_METHOD;
|
||||
},
|
||||
|
||||
isPasswordLoginDisabled() {
|
||||
return process.env.PASSWORD_LOGIN_ENABLED === 'false';
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -285,6 +285,30 @@ export class TrelloCreator {
|
|||
cardToCreate.members = wekanMembers;
|
||||
}
|
||||
}
|
||||
// add vote
|
||||
if (card.idMembersVoted) {
|
||||
// Trello only know's positive votes
|
||||
const positiveVotes = [];
|
||||
card.idMembersVoted.forEach(trelloId => {
|
||||
if (this.members[trelloId]) {
|
||||
const wekanId = this.members[trelloId];
|
||||
// we may map multiple Trello members to the same wekan user
|
||||
// in which case we risk adding the same user multiple times
|
||||
if (!positiveVotes.find(wId => wId === wekanId)) {
|
||||
positiveVotes.push(wekanId);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (positiveVotes.length > 0) {
|
||||
cardToCreate.vote = {
|
||||
question: cardToCreate.title,
|
||||
public: true,
|
||||
positive: positiveVotes,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// insert card
|
||||
const cardId = Cards.direct.insert(cardToCreate);
|
||||
// keep track of Trello id => Wekan id
|
||||
|
|
|
|||
278
models/users.js
278
models/users.js
|
|
@ -1,3 +1,5 @@
|
|||
import { SyncedCron } from 'meteor/percolate:synced-cron';
|
||||
|
||||
// Sandstorm context is detected using the METEOR_SETTINGS environment variable
|
||||
// in the package definition.
|
||||
const isSandstorm =
|
||||
|
|
@ -165,7 +167,20 @@ Users.attachSchema(
|
|||
/**
|
||||
* enabled notifications for the user
|
||||
*/
|
||||
type: [String],
|
||||
type: [Object],
|
||||
optional: true,
|
||||
},
|
||||
'profile.notifications.$.activity': {
|
||||
/**
|
||||
* The id of the activity this notification references
|
||||
*/
|
||||
type: String,
|
||||
},
|
||||
'profile.notifications.$.read': {
|
||||
/**
|
||||
* the date on which this notification was read
|
||||
*/
|
||||
type: Date,
|
||||
optional: true,
|
||||
},
|
||||
'profile.showCardsCountAt': {
|
||||
|
|
@ -175,6 +190,13 @@ Users.attachSchema(
|
|||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
'profile.startDayOfWeek': {
|
||||
/**
|
||||
* startDayOfWeek field of the user
|
||||
*/
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
'profile.starredBoards': {
|
||||
/**
|
||||
* list of starred board IDs
|
||||
|
|
@ -362,8 +384,8 @@ if (Meteor.isClient) {
|
|||
return board && board.hasWorker(this._id);
|
||||
},
|
||||
|
||||
isBoardAdmin() {
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
isBoardAdmin(boardId = Session.get('currentBoard')) {
|
||||
const board = Boards.findOne(boardId);
|
||||
return board && board.hasAdmin(this._id);
|
||||
},
|
||||
});
|
||||
|
|
@ -371,12 +393,20 @@ if (Meteor.isClient) {
|
|||
|
||||
Users.helpers({
|
||||
boards() {
|
||||
return Boards.find({ 'members.userId': this._id });
|
||||
return Boards.find(
|
||||
{ 'members.userId': this._id },
|
||||
{ sort: { sort: 1 /* boards default sorting */ } },
|
||||
);
|
||||
},
|
||||
|
||||
starredBoards() {
|
||||
const { starredBoards = [] } = this.profile || {};
|
||||
return Boards.find({ archived: false, _id: { $in: starredBoards } });
|
||||
return Boards.find(
|
||||
{ archived: false, _id: { $in: starredBoards } },
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
hasStarred(boardId) {
|
||||
|
|
@ -386,7 +416,12 @@ Users.helpers({
|
|||
|
||||
invitedBoards() {
|
||||
const { invitedBoards = [] } = this.profile || {};
|
||||
return Boards.find({ archived: false, _id: { $in: invitedBoards } });
|
||||
return Boards.find(
|
||||
{ archived: false, _id: { $in: invitedBoards } },
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
isInvitedTo(boardId) {
|
||||
|
|
@ -429,6 +464,20 @@ Users.helpers({
|
|||
return _.contains(notifications, activityId);
|
||||
},
|
||||
|
||||
notifications() {
|
||||
const { notifications = [] } = this.profile || {};
|
||||
for (const index in notifications) {
|
||||
if (!notifications.hasOwnProperty(index)) continue;
|
||||
const notification = notifications[index];
|
||||
// this preserves their db sort order for editing
|
||||
notification.dbIndex = index;
|
||||
notification.activity = Activities.findOne(notification.activity);
|
||||
}
|
||||
// this sorts them newest to oldest to match Trello's behavior
|
||||
notifications.reverse();
|
||||
return notifications;
|
||||
},
|
||||
|
||||
hasShowDesktopDragHandles() {
|
||||
const profile = this.profile || {};
|
||||
return profile.showDesktopDragHandles || false;
|
||||
|
|
@ -479,6 +528,15 @@ Users.helpers({
|
|||
return profile.language || 'en';
|
||||
},
|
||||
|
||||
getStartDayOfWeek() {
|
||||
const profile = this.profile || {};
|
||||
if (typeof profile.startDayOfWeek === 'undefined') {
|
||||
// default is 'Monday' (1)
|
||||
return 1;
|
||||
}
|
||||
return profile.startDayOfWeek;
|
||||
},
|
||||
|
||||
getTemplatesBoardId() {
|
||||
return (this.profile || {}).templatesBoardId;
|
||||
},
|
||||
|
|
@ -573,7 +631,7 @@ Users.mutations({
|
|||
addNotification(activityId) {
|
||||
return {
|
||||
$addToSet: {
|
||||
'profile.notifications': activityId,
|
||||
'profile.notifications': { activity: activityId },
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
@ -581,7 +639,7 @@ Users.mutations({
|
|||
removeNotification(activityId) {
|
||||
return {
|
||||
$pull: {
|
||||
'profile.notifications': activityId,
|
||||
'profile.notifications': { activity: activityId },
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
@ -610,6 +668,10 @@ Users.mutations({
|
|||
return { $set: { 'profile.showCardsCountAt': limit } };
|
||||
},
|
||||
|
||||
setStartDayOfWeek(startDay) {
|
||||
return { $set: { 'profile.startDayOfWeek': startDay } };
|
||||
},
|
||||
|
||||
setBoardView(view) {
|
||||
return {
|
||||
$set: {
|
||||
|
|
@ -620,16 +682,6 @@ Users.mutations({
|
|||
});
|
||||
|
||||
Meteor.methods({
|
||||
setUsername(username, userId) {
|
||||
check(username, String);
|
||||
check(userId, String);
|
||||
const nUsersWithUsername = Users.find({ username }).count();
|
||||
if (nUsersWithUsername > 0) {
|
||||
throw new Meteor.Error('username-already-taken');
|
||||
} else {
|
||||
Users.update(userId, { $set: { username } });
|
||||
}
|
||||
},
|
||||
setListSortBy(value) {
|
||||
check(value, String);
|
||||
Meteor.user().setListSortBy(value);
|
||||
|
|
@ -650,51 +702,101 @@ Meteor.methods({
|
|||
check(limit, Number);
|
||||
Meteor.user().setShowCardsCountAt(limit);
|
||||
},
|
||||
setEmail(email, userId) {
|
||||
if (Array.isArray(email)) {
|
||||
email = email.shift();
|
||||
}
|
||||
check(email, String);
|
||||
const existingUser = Users.findOne(
|
||||
{ 'emails.address': email },
|
||||
{ fields: { _id: 1 } },
|
||||
);
|
||||
if (existingUser) {
|
||||
throw new Meteor.Error('email-already-taken');
|
||||
} else {
|
||||
Users.update(userId, {
|
||||
$set: {
|
||||
emails: [
|
||||
{
|
||||
address: email,
|
||||
verified: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
setUsernameAndEmail(username, email, userId) {
|
||||
check(username, String);
|
||||
if (Array.isArray(email)) {
|
||||
email = email.shift();
|
||||
}
|
||||
check(email, String);
|
||||
check(userId, String);
|
||||
Meteor.call('setUsername', username, userId);
|
||||
Meteor.call('setEmail', email, userId);
|
||||
},
|
||||
setPassword(newPassword, userId) {
|
||||
check(userId, String);
|
||||
check(newPassword, String);
|
||||
if (Meteor.user().isAdmin) {
|
||||
Accounts.setPassword(userId, newPassword);
|
||||
}
|
||||
changeStartDayOfWeek(startDay) {
|
||||
check(startDay, Number);
|
||||
Meteor.user().setStartDayOfWeek(startDay);
|
||||
},
|
||||
});
|
||||
|
||||
if (Meteor.isServer) {
|
||||
Meteor.methods({
|
||||
setCreateUser(fullname, username, password, isAdmin, isActive, email) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
check(fullname, String);
|
||||
check(username, String);
|
||||
check(password, String);
|
||||
check(isAdmin, String);
|
||||
check(isActive, String);
|
||||
check(email, String);
|
||||
|
||||
const nUsersWithUsername = Users.find({ username }).count();
|
||||
const nUsersWithEmail = Users.find({ email }).count();
|
||||
if (nUsersWithUsername > 0) {
|
||||
throw new Meteor.Error('username-already-taken');
|
||||
} else if (nUsersWithEmail > 0) {
|
||||
throw new Meteor.Error('email-already-taken');
|
||||
} else {
|
||||
Accounts.createUser({
|
||||
fullname,
|
||||
username,
|
||||
password,
|
||||
isAdmin,
|
||||
isActive,
|
||||
email: email.toLowerCase(),
|
||||
from: 'admin',
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
setUsername(username, userId) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
check(username, String);
|
||||
check(userId, String);
|
||||
const nUsersWithUsername = Users.find({ username }).count();
|
||||
if (nUsersWithUsername > 0) {
|
||||
throw new Meteor.Error('username-already-taken');
|
||||
} else {
|
||||
Users.update(userId, { $set: { username } });
|
||||
}
|
||||
}
|
||||
},
|
||||
setEmail(email, userId) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
if (Array.isArray(email)) {
|
||||
email = email.shift();
|
||||
}
|
||||
check(email, String);
|
||||
const existingUser = Users.findOne(
|
||||
{ 'emails.address': email },
|
||||
{ fields: { _id: 1 } },
|
||||
);
|
||||
if (existingUser) {
|
||||
throw new Meteor.Error('email-already-taken');
|
||||
} else {
|
||||
Users.update(userId, {
|
||||
$set: {
|
||||
emails: [
|
||||
{
|
||||
address: email,
|
||||
verified: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
setUsernameAndEmail(username, email, userId) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
check(username, String);
|
||||
if (Array.isArray(email)) {
|
||||
email = email.shift();
|
||||
}
|
||||
check(email, String);
|
||||
check(userId, String);
|
||||
Meteor.call('setUsername', username, userId);
|
||||
Meteor.call('setEmail', email, userId);
|
||||
}
|
||||
},
|
||||
setPassword(newPassword, userId) {
|
||||
if (Meteor.user() && Meteor.user().isAdmin) {
|
||||
check(userId, String);
|
||||
check(newPassword, String);
|
||||
if (Meteor.user().isAdmin) {
|
||||
Accounts.setPassword(userId, newPassword);
|
||||
}
|
||||
}
|
||||
},
|
||||
// we accept userId, username, email
|
||||
inviteUserToBoard(username, boardId) {
|
||||
check(username, String);
|
||||
|
|
@ -726,8 +828,9 @@ if (Meteor.isServer) {
|
|||
throw new Meteor.Error('error-user-notAllowSelf');
|
||||
} else {
|
||||
if (posAt <= 0) throw new Meteor.Error('error-user-doesNotExist');
|
||||
if (Settings.findOne().disableRegistration)
|
||||
if (Settings.findOne({ disableRegistration: true })) {
|
||||
throw new Meteor.Error('error-user-notCreated');
|
||||
}
|
||||
// Set in lowercase email before creating account
|
||||
const email = username.toLowerCase();
|
||||
username = email.substring(0, posAt);
|
||||
|
|
@ -748,6 +851,16 @@ if (Meteor.isServer) {
|
|||
board.addMember(user._id);
|
||||
user.addInvite(boardId);
|
||||
|
||||
//Check if there is a subtasks board
|
||||
if (board.subtasksDefaultBoardId) {
|
||||
const subBoard = Boards.findOne(board.subtasksDefaultBoardId);
|
||||
//If there is, also add user to that board
|
||||
if (subBoard) {
|
||||
subBoard.addMember(user._id);
|
||||
user.addInvite(subBoard._id);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
user: user.username,
|
||||
|
|
@ -862,6 +975,39 @@ if (Meteor.isServer) {
|
|||
});
|
||||
}
|
||||
|
||||
const addCronJob = _.debounce(
|
||||
Meteor.bindEnvironment(function notificationCleanupDebounced() {
|
||||
// passed in the removeAge has to be a number standing for the number of days after a notification is read before we remove it
|
||||
const envRemoveAge =
|
||||
process.env.NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE;
|
||||
// default notifications will be removed 2 days after they are read
|
||||
const defaultRemoveAge = 2;
|
||||
const removeAge = parseInt(envRemoveAge, 10) || defaultRemoveAge;
|
||||
|
||||
SyncedCron.add({
|
||||
name: 'notification_cleanup',
|
||||
schedule: parser => parser.text('every 1 days'),
|
||||
job: () => {
|
||||
for (const user of Users.find()) {
|
||||
if (!user.profile || !user.profile.notifications) continue;
|
||||
for (const notification of user.profile.notifications) {
|
||||
if (notification.read) {
|
||||
const removeDate = new Date(notification.read);
|
||||
removeDate.setDate(removeDate.getDate() + removeAge);
|
||||
if (removeDate <= new Date()) {
|
||||
user.removeNotification(notification.activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
SyncedCron.start();
|
||||
}),
|
||||
500,
|
||||
);
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// Let mongoDB ensure username unicity
|
||||
Meteor.startup(() => {
|
||||
|
|
@ -875,6 +1021,9 @@ if (Meteor.isServer) {
|
|||
},
|
||||
{ unique: true },
|
||||
);
|
||||
Meteor.defer(() => {
|
||||
addCronJob();
|
||||
});
|
||||
});
|
||||
|
||||
// OLD WAY THIS CODE DID WORK: When user is last admin of board,
|
||||
|
|
@ -1180,10 +1329,13 @@ if (Meteor.isServer) {
|
|||
let data = Meteor.users.findOne({ _id: id });
|
||||
if (data !== undefined) {
|
||||
if (action === 'takeOwnership') {
|
||||
data = Boards.find({
|
||||
'members.userId': id,
|
||||
'members.isAdmin': true,
|
||||
}).map(function(board) {
|
||||
data = Boards.find(
|
||||
{
|
||||
'members.userId': id,
|
||||
'members.isAdmin': true,
|
||||
},
|
||||
{ sort: { sort: 1 /* boards default sorting */ } },
|
||||
).map(function(board) {
|
||||
if (board.hasMember(req.userId)) {
|
||||
board.removeMember(req.userId);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue