Prettier & eslint project style update

This commit is contained in:
Justin Reynolds 2019-06-28 12:52:09 -05:00
parent a0a482aa8e
commit 3eb4d2c341
116 changed files with 6216 additions and 5240 deletions

View file

@ -12,11 +12,8 @@
"meteor": true "meteor": true
}, },
"parserOptions": { "parserOptions": {
"ecmaVersion": 2017, "ecmaVersion": 2018,
"sourceType": "module", "sourceType": "module"
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
}, },
"rules": { "rules": {
"strict": 0, "strict": 0,
@ -26,7 +23,7 @@
"consistent-return": 2, "consistent-return": 2,
"dot-notation": 2, "dot-notation": 2,
"eqeqeq": 2, "eqeqeq": 2,
"indent": [2, 2], "indent": 0,
"no-cond-assign": 2, "no-cond-assign": 2,
"no-constant-condition": 2, "no-constant-condition": 2,
"no-eval": 2, "no-eval": 2,
@ -50,7 +47,6 @@
"quotes": [2, "single"], "quotes": [2, "single"],
"semi-spacing": 2, "semi-spacing": 2,
"space-unary-ops": 2, "space-unary-ops": 2,
"arrow-parens": 2,
"arrow-spacing": 2, "arrow-spacing": 2,
"no-class-assign": 2, "no-class-assign": 2,
"no-dupe-class-members": 2, "no-dupe-class-members": 2,
@ -69,7 +65,8 @@
"singleQuote": true, "singleQuote": true,
"trailingComma": "all" "trailingComma": "all"
} }
] ],
"meteor/no-session": 0
}, },
"settings": { "settings": {
"import/resolver": { "import/resolver": {

View file

@ -13,7 +13,7 @@ BlazeComponent.extendComponent({
let thisId, searchId; let thisId, searchId;
if (mode === 'linkedcard' || mode === 'linkedboard') { if (mode === 'linkedcard' || mode === 'linkedboard') {
thisId = Session.get('currentCard'); thisId = Session.get('currentCard');
searchId = Cards.findOne({_id: thisId}).linkedId; searchId = Cards.findOne({ _id: thisId }).linkedId;
mode = mode.replace('linked', ''); mode = mode.replace('linked', '');
} else { } else {
thisId = Session.get(`current${capitalizedMode}`); thisId = Session.get(`current${capitalizedMode}`);
@ -22,8 +22,7 @@ BlazeComponent.extendComponent({
const limit = this.page.get() * activitiesPerPage; const limit = this.page.get() * activitiesPerPage;
const user = Meteor.user(); const user = Meteor.user();
const hideSystem = user ? user.hasHiddenSystemMessages() : false; const hideSystem = user ? user.hasHiddenSystemMessages() : false;
if (searchId === null) if (searchId === null) return;
return;
this.subscribe('activities', mode, searchId, limit, hideSystem, () => { this.subscribe('activities', mode, searchId, limit, hideSystem, () => {
this.loadNextPageLocked = false; this.loadNextPageLocked = false;
@ -50,9 +49,9 @@ BlazeComponent.extendComponent({
} }
}, },
checkItem(){ checkItem() {
const checkItemId = this.currentData().checklistItemId; const checkItemId = this.currentData().checklistItemId;
const checkItem = ChecklistItems.findOne({_id:checkItemId}); const checkItem = ChecklistItems.findOne({ _id: checkItemId });
return checkItem.title; return checkItem.title;
}, },
@ -66,42 +65,58 @@ BlazeComponent.extendComponent({
cardLink() { cardLink() {
const card = this.currentData().card(); const card = this.currentData().card();
return card && Blaze.toHTML(HTML.A({ return (
href: card.absoluteUrl(), card &&
'class': 'action-card', Blaze.toHTML(
}, card.title)); HTML.A(
{
href: card.absoluteUrl(),
class: 'action-card',
},
card.title,
),
)
);
}, },
lastLabel(){ lastLabel() {
const lastLabelId = this.currentData().labelId; const lastLabelId = this.currentData().labelId;
if (!lastLabelId) if (!lastLabelId) return null;
return null; const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(
const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(lastLabelId); lastLabelId,
if(lastLabel.name === undefined || lastLabel.name === ''){ );
if (lastLabel.name === undefined || lastLabel.name === '') {
return lastLabel.color; return lastLabel.color;
}else{ } else {
return lastLabel.name; return lastLabel.name;
} }
}, },
lastCustomField(){ lastCustomField() {
const lastCustomField = CustomFields.findOne(this.currentData().customFieldId); const lastCustomField = CustomFields.findOne(
if (!lastCustomField) this.currentData().customFieldId,
return null; );
if (!lastCustomField) return null;
return lastCustomField.name; return lastCustomField.name;
}, },
lastCustomFieldValue(){ lastCustomFieldValue() {
const lastCustomField = CustomFields.findOne(this.currentData().customFieldId); const lastCustomField = CustomFields.findOne(
if (!lastCustomField) this.currentData().customFieldId,
return null; );
if (!lastCustomField) return null;
const value = this.currentData().value; const value = this.currentData().value;
if (lastCustomField.settings.dropdownItems && lastCustomField.settings.dropdownItems.length > 0) { if (
const dropDownValue = _.find(lastCustomField.settings.dropdownItems, (item) => { lastCustomField.settings.dropdownItems &&
return item._id === value; lastCustomField.settings.dropdownItems.length > 0
}); ) {
if (dropDownValue) const dropDownValue = _.find(
return dropDownValue.name; lastCustomField.settings.dropdownItems,
item => {
return item._id === value;
},
);
if (dropDownValue) return dropDownValue.name;
} }
return value; return value;
}, },
@ -112,11 +127,16 @@ BlazeComponent.extendComponent({
sourceLink() { sourceLink() {
const source = this.currentData().source; const source = this.currentData().source;
if(source) { if (source) {
if(source.url) { if (source.url) {
return Blaze.toHTML(HTML.A({ return Blaze.toHTML(
href: source.url, HTML.A(
}, source.system)); {
href: source.url,
},
source.system,
),
);
} else { } else {
return source.system; return source.system;
} }
@ -133,38 +153,50 @@ BlazeComponent.extendComponent({
attachmentLink() { attachmentLink() {
const attachment = this.currentData().attachment(); const attachment = this.currentData().attachment();
// trying to display url before file is stored generates js errors // trying to display url before file is stored generates js errors
return attachment && attachment.url({ download: true }) && Blaze.toHTML(HTML.A({ return (
href: attachment.url({ download: true }), attachment &&
target: '_blank', attachment.url({ download: true }) &&
}, attachment.name())); Blaze.toHTML(
HTML.A(
{
href: attachment.url({ download: true }),
target: '_blank',
},
attachment.name(),
),
)
);
}, },
customField() { customField() {
const customField = this.currentData().customField(); const customField = this.currentData().customField();
if (!customField) if (!customField) return null;
return null;
return customField.name; return customField.name;
}, },
events() { events() {
return [{ return [
// XXX We should use Popup.afterConfirmation here {
'click .js-delete-comment'() { // XXX We should use Popup.afterConfirmation here
const commentId = this.currentData().commentId; 'click .js-delete-comment'() {
CardComments.remove(commentId); const commentId = this.currentData().commentId;
CardComments.remove(commentId);
},
'submit .js-edit-comment'(evt) {
evt.preventDefault();
const commentText = this.currentComponent()
.getValue()
.trim();
const commentId = Template.parentData().commentId;
if (commentText) {
CardComments.update(commentId, {
$set: {
text: commentText,
},
});
}
},
}, },
'submit .js-edit-comment'(evt) { ];
evt.preventDefault();
const commentText = this.currentComponent().getValue().trim();
const commentId = Template.parentData().commentId;
if (commentText) {
CardComments.update(commentId, {
$set: {
text: commentText,
},
});
}
},
}];
}, },
}).register('activities'); }).register('activities');

View file

@ -14,39 +14,41 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-new-comment:not(.focus)'() { {
commentFormIsOpen.set(true); 'click .js-new-comment:not(.focus)'() {
commentFormIsOpen.set(true);
},
'submit .js-new-comment-form'(evt) {
const input = this.getInput();
const text = input.val().trim();
const card = this.currentData();
let boardId = card.boardId;
let cardId = card._id;
if (card.isLinkedCard()) {
boardId = Cards.findOne(card.linkedId).boardId;
cardId = card.linkedId;
}
if (text) {
CardComments.insert({
text,
boardId,
cardId,
});
resetCommentInput(input);
Tracker.flush();
autosize.update(input);
}
evt.preventDefault();
},
// Pressing Ctrl+Enter should submit the form
'keydown form textarea'(evt) {
if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
this.find('button[type=submit]').click();
}
},
}, },
'submit .js-new-comment-form'(evt) { ];
const input = this.getInput();
const text = input.val().trim();
const card = this.currentData();
let boardId = card.boardId;
let cardId = card._id;
if (card.isLinkedCard()) {
boardId = Cards.findOne(card.linkedId).boardId;
cardId = card.linkedId;
}
if (text) {
CardComments.insert({
text,
boardId,
cardId,
});
resetCommentInput(input);
Tracker.flush();
autosize.update(input);
}
evt.preventDefault();
},
// Pressing Ctrl+Enter should submit the form
'keydown form textarea'(evt) {
if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
this.find('button[type=submit]').click();
}
},
}];
}, },
}).register('commentForm'); }).register('commentForm');
@ -69,7 +71,8 @@ Tracker.autorun(() => {
}); });
}); });
EscapeActions.register('inlinedForm', EscapeActions.register(
'inlinedForm',
() => { () => {
const draftKey = { const draftKey = {
fieldName: 'cardComment', fieldName: 'cardComment',
@ -84,7 +87,10 @@ EscapeActions.register('inlinedForm',
} }
resetCommentInput(commentInput); resetCommentInput(commentInput);
}, },
() => { return commentFormIsOpen.get(); }, { () => {
return commentFormIsOpen.get();
},
{
noClickEscapeOn: '.js-new-comment', noClickEscapeOn: '.js-new-comment',
} },
); );

View file

@ -4,36 +4,45 @@ BlazeComponent.extendComponent({
}, },
archivedBoards() { archivedBoards() {
return Boards.find({ archived: true }, { return Boards.find(
sort: ['title'], { archived: true },
}); {
sort: ['title'],
},
);
}, },
events() { events() {
return [{ return [
'click .js-restore-board'() { {
// TODO : Make isSandstorm variable global 'click .js-restore-board'() {
const isSandstorm = Meteor.settings && Meteor.settings.public && // TODO : Make isSandstorm variable global
Meteor.settings.public.sandstorm; const isSandstorm =
if (isSandstorm && Session.get('currentBoard')) { Meteor.settings &&
const currentBoard = Boards.findOne(Session.get('currentBoard')); Meteor.settings.public &&
currentBoard.archive(); Meteor.settings.public.sandstorm;
} if (isSandstorm && Session.get('currentBoard')) {
const board = this.currentData(); const currentBoard = Boards.findOne(Session.get('currentBoard'));
board.restore(); currentBoard.archive();
Utils.goBoardId(board._id); }
const board = this.currentData();
board.restore();
Utils.goBoardId(board._id);
},
'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
Popup.close();
const isSandstorm =
Meteor.settings &&
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && Session.get('currentBoard')) {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
Boards.remove(currentBoard._id);
}
Boards.remove(this._id);
FlowRouter.go('home');
}),
}, },
'click .js-delete-board': Popup.afterConfirm('boardDelete', function() { ];
Popup.close();
const isSandstorm = Meteor.settings && Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && Session.get('currentBoard')) {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
Boards.remove(currentBoard._id);
}
Boards.remove(this._id);
FlowRouter.go('home');
}),
}];
}, },
}).register('archivedBoards'); }).register('archivedBoards');

View file

@ -12,8 +12,7 @@ BlazeComponent.extendComponent({
// unfortunatly, Blaze doesn't have this notion. // unfortunatly, Blaze doesn't have this notion.
this.autorun(() => { this.autorun(() => {
const currentBoardId = Session.get('currentBoard'); const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) if (!currentBoardId) return;
return;
const handle = subManager.subscribe('board', currentBoardId, false); const handle = subManager.subscribe('board', currentBoardId, false);
Tracker.nonreactive(() => { Tracker.nonreactive(() => {
Tracker.autorun(() => { Tracker.autorun(() => {
@ -30,7 +29,6 @@ BlazeComponent.extendComponent({
goHome() { goHome() {
FlowRouter.go('home'); FlowRouter.go('home');
}, },
}).register('board'); }).register('board');
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
@ -47,7 +45,7 @@ BlazeComponent.extendComponent({
if (nullSortSwimlanes.count() > 0) { if (nullSortSwimlanes.count() > 0) {
const swimlanes = currentBoardData.swimlanes(); const swimlanes = currentBoardData.swimlanes();
let count = 0; let count = 0;
swimlanes.forEach((s) => { swimlanes.forEach(s => {
Swimlanes.update(s._id, { Swimlanes.update(s._id, {
$set: { $set: {
sort: count, sort: count,
@ -62,7 +60,7 @@ BlazeComponent.extendComponent({
if (nullSortLists.count() > 0) { if (nullSortLists.count() > 0) {
const lists = currentBoardData.lists(); const lists = currentBoardData.lists();
let count = 0; let count = 0;
lists.forEach((l) => { lists.forEach(l => {
Lists.update(l._id, { Lists.update(l._id, {
$set: { $set: {
sort: count, sort: count,
@ -110,12 +108,15 @@ BlazeComponent.extendComponent({
// resize all swimlanes + headers to be a total of 150 px per row // resize all swimlanes + headers to be a total of 150 px per row
// this could be achieved by setIsDragging(true) but we want immediate // this could be achieved by setIsDragging(true) but we want immediate
// result // result
ui.item.siblings('.js-swimlane').css('height', `${swimlaneWhileSortingHeight - 26}px`); ui.item
.siblings('.js-swimlane')
.css('height', `${swimlaneWhileSortingHeight - 26}px`);
// set the new scroll height after the resize and insertion of // set the new scroll height after the resize and insertion of
// the placeholder. We want the element under the cursor to stay // the placeholder. We want the element under the cursor to stay
// at the same place on the screen // at the same place on the screen
ui.item.parent().get(0).scrollTop = ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY; ui.item.parent().get(0).scrollTop =
ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
}, },
beforeStop(evt, ui) { beforeStop(evt, ui) {
const parentOffset = ui.item.parent().offset(); const parentOffset = ui.item.parent().offset();
@ -124,7 +125,8 @@ BlazeComponent.extendComponent({
// compute the new scroll height after the resize and removal of // compute the new scroll height after the resize and removal of
// the placeholder // the placeholder
const scrollTop = ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY; const scrollTop =
ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
// then reset the original view of the swimlane // then reset the original view of the swimlane
siblings.removeClass('moving-swimlane'); siblings.removeClass('moving-swimlane');
@ -154,11 +156,14 @@ BlazeComponent.extendComponent({
sort(evt, ui) { sort(evt, ui) {
// get the mouse position in the sortable // get the mouse position in the sortable
const parentOffset = ui.item.parent().offset(); const parentOffset = ui.item.parent().offset();
const cursorY = evt.pageY - parentOffset.top + ui.item.parent().scrollTop(); const cursorY =
evt.pageY - parentOffset.top + ui.item.parent().scrollTop();
// compute the intended index of the placeholder (we need to skip the // compute the intended index of the placeholder (we need to skip the
// slots between the headers and the list of cards) // slots between the headers and the list of cards)
const newplaceholderIndex = Math.floor(cursorY / swimlaneWhileSortingHeight); const newplaceholderIndex = Math.floor(
cursorY / swimlaneWhileSortingHeight,
);
let destPlaceholderIndex = (newplaceholderIndex + 1) * 2; let destPlaceholderIndex = (newplaceholderIndex + 1) * 2;
// if we are scrolling far away from the bottom of the list // if we are scrolling far away from the bottom of the list
@ -169,9 +174,17 @@ BlazeComponent.extendComponent({
// update the placeholder position in the DOM tree // update the placeholder position in the DOM tree
if (destPlaceholderIndex !== ui.placeholder.index()) { if (destPlaceholderIndex !== ui.placeholder.index()) {
if (destPlaceholderIndex < boardComponent.origPlaceholderIndex) { if (destPlaceholderIndex < boardComponent.origPlaceholderIndex) {
ui.placeholder.insertBefore(ui.placeholder.siblings().slice(destPlaceholderIndex - 2, destPlaceholderIndex - 1)); ui.placeholder.insertBefore(
ui.placeholder
.siblings()
.slice(destPlaceholderIndex - 2, destPlaceholderIndex - 1),
);
} else { } else {
ui.placeholder.insertAfter(ui.placeholder.siblings().slice(destPlaceholderIndex - 1, destPlaceholderIndex)); ui.placeholder.insertAfter(
ui.placeholder
.siblings()
.slice(destPlaceholderIndex - 1, destPlaceholderIndex),
);
} }
} }
}, },
@ -181,7 +194,11 @@ BlazeComponent.extendComponent({
enableClickOnTouch('.js-swimlane:not(.placeholder)'); enableClickOnTouch('.js-swimlane:not(.placeholder)');
function userIsMember() { function userIsMember() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
} }
// If there is no data in the board (ie, no lists) we autofocus the list // If there is no data in the board (ie, no lists) we autofocus the list
@ -195,45 +212,49 @@ BlazeComponent.extendComponent({
isViewSwimlanes() { isViewSwimlanes() {
const currentUser = Meteor.user(); const currentUser = Meteor.user();
if (!currentUser) return false; if (!currentUser) return false;
return ((currentUser.profile || {}).boardView === 'board-view-swimlanes'); return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
}, },
isViewLists() { isViewLists() {
const currentUser = Meteor.user(); const currentUser = Meteor.user();
if (!currentUser) return true; if (!currentUser) return true;
return ((currentUser.profile || {}).boardView === 'board-view-lists'); return (currentUser.profile || {}).boardView === 'board-view-lists';
}, },
isViewCalendar() { isViewCalendar() {
const currentUser = Meteor.user(); const currentUser = Meteor.user();
if (!currentUser) return false; if (!currentUser) return false;
return ((currentUser.profile || {}).boardView === 'board-view-cal'); return (currentUser.profile || {}).boardView === 'board-view-cal';
}, },
openNewListForm() { openNewListForm() {
if (this.isViewSwimlanes()) { if (this.isViewSwimlanes()) {
this.childComponents('swimlane')[0] this.childComponents('swimlane')[0]
.childComponents('addListAndSwimlaneForm')[0].open(); .childComponents('addListAndSwimlaneForm')[0]
.open();
} else if (this.isViewLists()) { } else if (this.isViewLists()) {
this.childComponents('listsGroup')[0] this.childComponents('listsGroup')[0]
.childComponents('addListForm')[0].open(); .childComponents('addListForm')[0]
.open();
} }
}, },
events() { events() {
return [{ return [
// XXX The board-overlay div should probably be moved to the parent {
// component. // XXX The board-overlay div should probably be moved to the parent
'mouseenter .board-overlay'() { // component.
if (this.mouseHasEnterCardDetails) { 'mouseenter .board-overlay'() {
this.showOverlay.set(false); if (this.mouseHasEnterCardDetails) {
} this.showOverlay.set(false);
}
},
mouseup() {
if (this._isDragging) {
this._isDragging = false;
}
},
}, },
'mouseup'() { ];
if (this._isDragging) {
this._isDragging = false;
}
},
}];
}, },
// XXX Flow components allow us to avoid creating these two setter methods by // XXX Flow components allow us to avoid creating these two setter methods by
@ -245,23 +266,24 @@ BlazeComponent.extendComponent({
scrollLeft(position = 0) { scrollLeft(position = 0) {
const swimlanes = this.$('.js-swimlanes'); const swimlanes = this.$('.js-swimlanes');
swimlanes && swimlanes.animate({ swimlanes &&
scrollLeft: position, swimlanes.animate({
}); scrollLeft: position,
});
}, },
scrollTop(position = 0) { scrollTop(position = 0) {
const swimlanes = this.$('.js-swimlanes'); const swimlanes = this.$('.js-swimlanes');
swimlanes && swimlanes.animate({ swimlanes &&
scrollTop: position, swimlanes.animate({
}); scrollTop: position,
});
}, },
}).register('boardBody'); }).register('boardBody');
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
onRendered() { onRendered() {
this.autorun(function(){ this.autorun(function() {
$('#calendar-view').fullCalendar('refetchEvents'); $('#calendar-view').fullCalendar('refetchEvents');
}); });
}, },
@ -273,7 +295,8 @@ BlazeComponent.extendComponent({
timezone: 'local', timezone: 'local',
header: { header: {
left: 'title today prev,next', left: 'title today prev,next',
center: 'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,timelineMonth timelineYear', center:
'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,timelineMonth timelineYear',
right: '', right: '',
}, },
// height: 'parent', nope, doesn't work as the parent might be small // height: 'parent', nope, doesn't work as the parent might be small
@ -283,7 +306,7 @@ BlazeComponent.extendComponent({
nowIndicator: true, nowIndicator: true,
businessHours: { businessHours: {
// days of week. an array of zero-based day of week integers (0=Sunday) // days of week. an array of zero-based day of week integers (0=Sunday)
dow: [ 1, 2, 3, 4, 5 ], // Monday - Friday dow: [1, 2, 3, 4, 5], // Monday - Friday
start: '8:00', start: '8:00',
end: '18:00', end: '18:00',
}, },
@ -291,20 +314,25 @@ BlazeComponent.extendComponent({
events(start, end, timezone, callback) { events(start, end, timezone, callback) {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
const events = []; const events = [];
currentBoard.cardsInInterval(start.toDate(), end.toDate()).forEach(function(card){ currentBoard
events.push({ .cardsInInterval(start.toDate(), end.toDate())
id: card._id, .forEach(function(card) {
title: card.title, events.push({
start: card.startAt, id: card._id,
end: card.endAt, title: card.title,
allDay: Math.abs(card.endAt.getTime() - card.startAt.getTime()) / 1000 === 24*3600, start: card.startAt,
url: FlowRouter.url('card', { end: card.endAt,
boardId: currentBoard._id, allDay:
slug: currentBoard.slug, Math.abs(card.endAt.getTime() - card.startAt.getTime()) /
cardId: card._id, 1000 ===
}), 24 * 3600,
url: FlowRouter.url('card', {
boardId: currentBoard._id,
slug: currentBoard.slug,
cardId: card._id,
}),
});
}); });
});
callback(events); callback(events);
}, },
eventResize(event, delta, revertFunc) { eventResize(event, delta, revertFunc) {
@ -339,6 +367,6 @@ BlazeComponent.extendComponent({
isViewCalendar() { isViewCalendar() {
const currentUser = Meteor.user(); const currentUser = Meteor.user();
if (!currentUser) return false; if (!currentUser) return false;
return ((currentUser.profile || {}).boardView === 'board-view-cal'); return (currentUser.profile || {}).boardView === 'board-view-cal';
}, },
}).register('calendarView'); }).register('calendarView');

View file

@ -45,15 +45,21 @@ Template.boardMenuPopup.helpers({
}); });
Template.boardChangeTitlePopup.events({ Template.boardChangeTitlePopup.events({
submit(evt, tpl) { submit(event, templateInstance) {
const newTitle = tpl.$('.js-board-name').val().trim(); const newTitle = templateInstance
const newDesc = tpl.$('.js-board-desc').val().trim(); .$('.js-board-name')
.val()
.trim();
const newDesc = templateInstance
.$('.js-board-desc')
.val()
.trim();
if (newTitle) { if (newTitle) {
this.rename(newTitle); this.rename(newTitle);
this.setDescription(newDesc); this.setDescription(newDesc);
Popup.close(); Popup.close();
} }
evt.preventDefault(); event.preventDefault();
}, },
}); });
@ -76,67 +82,79 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-edit-board-title': Popup.open('boardChangeTitle'), {
'click .js-star-board'() { 'click .js-edit-board-title': Popup.open('boardChangeTitle'),
Meteor.user().toggleBoardStar(Session.get('currentBoard')); 'click .js-star-board'() {
Meteor.user().toggleBoardStar(Session.get('currentBoard'));
},
'click .js-open-board-menu': Popup.open('boardMenu'),
'click .js-change-visibility': Popup.open('boardChangeVisibility'),
'click .js-watch-board': Popup.open('boardChangeWatch'),
'click .js-open-archived-board'() {
Modal.open('archivedBoards');
},
'click .js-toggle-board-view'() {
const currentUser = Meteor.user();
if (
(currentUser.profile || {}).boardView === 'board-view-swimlanes'
) {
currentUser.setBoardView('board-view-cal');
} else if (
(currentUser.profile || {}).boardView === 'board-view-lists'
) {
currentUser.setBoardView('board-view-swimlanes');
} else if (
(currentUser.profile || {}).boardView === 'board-view-cal'
) {
currentUser.setBoardView('board-view-lists');
} else {
currentUser.setBoardView('board-view-swimlanes');
}
},
'click .js-toggle-sidebar'() {
Sidebar.toggle();
},
'click .js-open-filter-view'() {
Sidebar.setView('filter');
},
'click .js-filter-reset'(event) {
event.stopPropagation();
Sidebar.setView();
Filter.reset();
},
'click .js-open-search-view'() {
Sidebar.setView('search');
},
'click .js-open-rules-view'() {
Modal.openWide('rulesMain');
},
'click .js-multiselection-activate'() {
const currentCard = Session.get('currentCard');
MultiSelection.activate();
if (currentCard) {
MultiSelection.add(currentCard);
}
},
'click .js-multiselection-reset'(event) {
event.stopPropagation();
MultiSelection.disable();
},
'click .js-log-in'() {
FlowRouter.go('atSignIn');
},
}, },
'click .js-open-board-menu': Popup.open('boardMenu'), ];
'click .js-change-visibility': Popup.open('boardChangeVisibility'),
'click .js-watch-board': Popup.open('boardChangeWatch'),
'click .js-open-archived-board'() {
Modal.open('archivedBoards');
},
'click .js-toggle-board-view'() {
const currentUser = Meteor.user();
if ((currentUser.profile || {}).boardView === 'board-view-swimlanes') {
currentUser.setBoardView('board-view-cal');
} else if ((currentUser.profile || {}).boardView === 'board-view-lists') {
currentUser.setBoardView('board-view-swimlanes');
} else if ((currentUser.profile || {}).boardView === 'board-view-cal') {
currentUser.setBoardView('board-view-lists');
} else {
currentUser.setBoardView('board-view-swimlanes');
}
},
'click .js-toggle-sidebar'() {
Sidebar.toggle();
},
'click .js-open-filter-view'() {
Sidebar.setView('filter');
},
'click .js-filter-reset'(evt) {
evt.stopPropagation();
Sidebar.setView();
Filter.reset();
},
'click .js-open-search-view'() {
Sidebar.setView('search');
},
'click .js-open-rules-view'() {
Modal.openWide('rulesMain');
},
'click .js-multiselection-activate'() {
const currentCard = Session.get('currentCard');
MultiSelection.activate();
if (currentCard) {
MultiSelection.add(currentCard);
}
},
'click .js-multiselection-reset'(evt) {
evt.stopPropagation();
MultiSelection.disable();
},
'click .js-log-in'() {
FlowRouter.go('atSignIn');
},
}];
}, },
}).register('boardHeaderBar'); }).register('boardHeaderBar');
Template.boardHeaderBar.helpers({ Template.boardHeaderBar.helpers({
canModifyBoard() { canModifyBoard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}); });
@ -164,15 +182,17 @@ const CreateBoard = BlazeComponent.extendComponent({
this.visibilityMenuIsOpen.set(!this.visibilityMenuIsOpen.get()); this.visibilityMenuIsOpen.set(!this.visibilityMenuIsOpen.get());
}, },
onSubmit(evt) { onSubmit(event) {
evt.preventDefault(); event.preventDefault();
const title = this.find('.js-new-board-title').value; const title = this.find('.js-new-board-title').value;
const visibility = this.visibility.get(); const visibility = this.visibility.get();
this.boardId.set(Boards.insert({ this.boardId.set(
title, Boards.insert({
permission: visibility, title,
})); permission: visibility,
}),
);
Swimlanes.insert({ Swimlanes.insert({
title: 'Default', title: 'Default',
@ -183,26 +203,28 @@ const CreateBoard = BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-select-visibility'() { {
this.setVisibility(this.currentData()); 'click .js-select-visibility'() {
this.setVisibility(this.currentData());
},
'click .js-change-visibility': this.toggleVisibilityMenu,
'click .js-import': Popup.open('boardImportBoard'),
submit: this.onSubmit,
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-board-template': Popup.open('searchElement'),
}, },
'click .js-change-visibility': this.toggleVisibilityMenu, ];
'click .js-import': Popup.open('boardImportBoard'),
submit: this.onSubmit,
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-board-template': Popup.open('searchElement'),
}];
}, },
}).register('createBoardPopup'); }).register('createBoardPopup');
(class HeaderBarCreateBoard extends CreateBoard { (class HeaderBarCreateBoard extends CreateBoard {
onSubmit(evt) { onSubmit(event) {
super.onSubmit(evt); super.onSubmit(event);
// Immediately star boards crated with the headerbar popup. // Immediately star boards crated with the headerbar popup.
Meteor.user().toggleBoardStar(this.boardId.get()); Meteor.user().toggleBoardStar(this.boardId.get());
} }
}).register('headerBarCreateBoardPopup'); }.register('headerBarCreateBoardPopup'));
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
visibilityCheck() { visibilityCheck() {
@ -218,9 +240,11 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-select-visibility': this.selectBoardVisibility, {
}]; 'click .js-select-visibility': this.selectBoardVisibility,
},
];
}, },
}).register('boardChangeVisibilityPopup'); }).register('boardChangeVisibilityPopup');
@ -235,13 +259,21 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-select-watch'() { {
const level = this.currentData(); 'click .js-select-watch'() {
Meteor.call('watch', 'board', Session.get('currentBoard'), level, (err, ret) => { const level = this.currentData();
if (!err && ret) Popup.close(); Meteor.call(
}); 'watch',
'board',
Session.get('currentBoard'),
level,
(err, ret) => {
if (!err && ret) Popup.close();
},
);
},
}, },
}]; ];
}, },
}).register('boardChangeWatchPopup'); }).register('boardChangeWatchPopup');

View file

@ -21,11 +21,14 @@ BlazeComponent.extendComponent({
}, },
boards() { boards() {
return Boards.find({ return Boards.find(
archived: false, {
'members.userId': Meteor.userId(), archived: false,
type: 'board', 'members.userId': Meteor.userId(),
}, { sort: ['title'] }); type: 'board',
},
{ sort: ['title'] },
);
}, },
isStarred() { isStarred() {
const user = Meteor.user(); const user = Meteor.user();
@ -48,46 +51,49 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-add-board': Popup.open('createBoard'), {
'click .js-star-board'(evt) { 'click .js-add-board': Popup.open('createBoard'),
const boardId = this.currentData()._id; 'click .js-star-board'(evt) {
Meteor.user().toggleBoardStar(boardId); const boardId = this.currentData()._id;
evt.preventDefault(); Meteor.user().toggleBoardStar(boardId);
}, evt.preventDefault();
'click .js-clone-board'(evt) { },
Meteor.call('cloneBoard', 'click .js-clone-board'(evt) {
this.currentData()._id, Meteor.call(
Session.get('fromBoard'), 'cloneBoard',
(err, res) => { this.currentData()._id,
if (err) { Session.get('fromBoard'),
this.setError(err.error); (err, res) => {
} else { if (err) {
Session.set('fromBoard', null); this.setError(err.error);
Utils.goBoardId(res); } else {
Session.set('fromBoard', null);
Utils.goBoardId(res);
}
},
);
evt.preventDefault();
},
'click .js-archive-board'(evt) {
const boardId = this.currentData()._id;
Meteor.call('archiveBoard', boardId);
evt.preventDefault();
},
'click .js-accept-invite'() {
const boardId = this.currentData()._id;
Meteor.user().removeInvite(boardId);
},
'click .js-decline-invite'() {
const boardId = this.currentData()._id;
Meteor.call('quitBoard', boardId, (err, ret) => {
if (!err && ret) {
Meteor.user().removeInvite(boardId);
FlowRouter.go('home');
} }
} });
); },
evt.preventDefault();
}, },
'click .js-archive-board'(evt) { ];
const boardId = this.currentData()._id;
Meteor.call('archiveBoard', boardId);
evt.preventDefault();
},
'click .js-accept-invite'() {
const boardId = this.currentData()._id;
Meteor.user().removeInvite(boardId);
},
'click .js-decline-invite'() {
const boardId = this.currentData()._id;
Meteor.call('quitBoard', boardId, (err, ret) => {
if (!err && ret) {
Meteor.user().removeInvite(boardId);
FlowRouter.go('home');
}
});
},
}];
}, },
}).register('boardList'); }).register('boardList');

View file

@ -1,10 +1,11 @@
Template.attachmentsGalery.events({ Template.attachmentsGalery.events({
'click .js-add-attachment': Popup.open('cardAttachments'), 'click .js-add-attachment': Popup.open('cardAttachments'),
'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete', 'click .js-confirm-delete': Popup.afterConfirm(
'attachmentDelete',
function() { function() {
Attachments.remove(this._id); Attachments.remove(this._id);
Popup.close(); Popup.close();
} },
), ),
// If we let this event bubble, FlowRouter will handle it and empty the page // If we let this event bubble, FlowRouter will handle it and empty the page
// content, see #101. // content, see #101.
@ -17,8 +18,8 @@ Template.attachmentsGalery.events({
'click .js-remove-cover'() { 'click .js-remove-cover'() {
Cards.findOne(this.cardId).unsetCover(); Cards.findOne(this.cardId).unsetCover();
}, },
'click .js-preview-image'(evt) { 'click .js-preview-image'(event) {
Popup.open('previewAttachedImage').call(this, evt); Popup.open('previewAttachedImage').call(this, event);
// when multiple thumbnails, if click one then another very fast, // when multiple thumbnails, if click one then another very fast,
// we might get a wrong width from previous img. // we might get a wrong width from previous img.
// when popup reused, onRendered() won't be called, so we cannot get there. // when popup reused, onRendered() won't be called, so we cannot get there.
@ -31,31 +32,29 @@ Template.attachmentsGalery.events({
// if the image is too large, we resize & center the popup. // if the image is too large, we resize & center the popup.
if (w > 300) { if (w > 300) {
$('div.pop-over').css({ $('div.pop-over').css({
width: (w + 20), width: w + 20,
position: 'absolute', position: 'absolute',
left: (window.innerWidth - w)/2, left: (window.innerWidth - w) / 2,
top: (window.innerHeight - h)/2, top: (window.innerHeight - h) / 2,
}); });
} }
}; };
const url = $(evt.currentTarget).attr('src'); const url = $(event.currentTarget).attr('src');
if (img.src === url && img.complete) if (img.src === url && img.complete) rePosPopup();
rePosPopup(); else img.onload = rePosPopup;
else
img.onload = rePosPopup;
}, },
}); });
Template.previewAttachedImagePopup.events({ Template.previewAttachedImagePopup.events({
'click .js-large-image-clicked'(){ 'click .js-large-image-clicked'() {
Popup.close(); Popup.close();
}, },
}); });
Template.cardAttachmentsPopup.events({ Template.cardAttachmentsPopup.events({
'change .js-attach-file'(evt) { 'change .js-attach-file'(event) {
const card = this; const card = this;
FS.Utility.eachFile(evt, (f) => { FS.Utility.eachFile(event, f => {
const file = new FS.File(f); const file = new FS.File(f);
if (card.isLinkedCard()) { if (card.isLinkedCard()) {
file.boardId = Cards.findOne(card.linkedId).boardId; file.boardId = Cards.findOne(card.linkedId).boardId;
@ -77,9 +76,9 @@ Template.cardAttachmentsPopup.events({
Popup.close(); Popup.close();
}); });
}, },
'click .js-computer-upload'(evt, tpl) { 'click .js-computer-upload'(event, templateInstance) {
tpl.find('.js-attach-file').click(); templateInstance.find('.js-attach-file').click();
evt.preventDefault(); event.preventDefault();
}, },
'click .js-upload-clipboard-image': Popup.open('previewClipboardImage'), 'click .js-upload-clipboard-image': Popup.open('previewClipboardImage'),
}); });
@ -88,7 +87,7 @@ let pastedResults = null;
Template.previewClipboardImagePopup.onRendered(() => { Template.previewClipboardImagePopup.onRendered(() => {
// we can paste image from clipboard // we can paste image from clipboard
$(document.body).pasteImageReader((results) => { $(document.body).pasteImageReader(results => {
if (results.dataURL.startsWith('data:image/')) { if (results.dataURL.startsWith('data:image/')) {
$('img.preview-clipboard-image').attr('src', results.dataURL); $('img.preview-clipboard-image').attr('src', results.dataURL);
pastedResults = results; pastedResults = results;
@ -96,7 +95,7 @@ Template.previewClipboardImagePopup.onRendered(() => {
}); });
// we can also drag & drop image file to it // we can also drag & drop image file to it
$(document.body).dropImageReader((results) => { $(document.body).dropImageReader(results => {
if (results.dataURL.startsWith('data:image/')) { if (results.dataURL.startsWith('data:image/')) {
$('img.preview-clipboard-image').attr('src', results.dataURL); $('img.preview-clipboard-image').attr('src', results.dataURL);
pastedResults = results; pastedResults = results;

View file

@ -7,22 +7,21 @@ Template.cardCustomFieldsPopup.helpers({
}); });
Template.cardCustomFieldsPopup.events({ Template.cardCustomFieldsPopup.events({
'click .js-select-field'(evt) { 'click .js-select-field'(event) {
const card = Cards.findOne(Session.get('currentCard')); const card = Cards.findOne(Session.get('currentCard'));
const customFieldId = this._id; const customFieldId = this._id;
card.toggleCustomField(customFieldId); card.toggleCustomField(customFieldId);
evt.preventDefault(); event.preventDefault();
}, },
'click .js-settings'(evt) { 'click .js-settings'(event) {
EscapeActions.executeUpTo('detailsPane'); EscapeActions.executeUpTo('detailsPane');
Sidebar.setView('customFields'); Sidebar.setView('customFields');
evt.preventDefault(); event.preventDefault();
}, },
}); });
// cardCustomField // cardCustomField
const CardCustomField = BlazeComponent.extendComponent({ const CardCustomField = BlazeComponent.extendComponent({
getTemplate() { getTemplate() {
return `cardCustomField-${this.data().definition.type}`; return `cardCustomField-${this.data().definition.type}`;
}, },
@ -34,52 +33,55 @@ const CardCustomField = BlazeComponent.extendComponent({
}, },
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}); });
CardCustomField.register('cardCustomField'); CardCustomField.register('cardCustomField');
// cardCustomField-text // cardCustomField-text
(class extends CardCustomField { (class extends CardCustomField {
onCreated() { onCreated() {
super.onCreated(); super.onCreated();
} }
events() { events() {
return [{ return [
'submit .js-card-customfield-text'(evt) { {
evt.preventDefault(); 'submit .js-card-customfield-text'(event) {
const value = this.currentComponent().getValue(); event.preventDefault();
this.card.setCustomField(this.customFieldId, value); const value = this.currentComponent().getValue();
this.card.setCustomField(this.customFieldId, value);
},
}, },
}]; ];
} }
}.register('cardCustomField-text'));
}).register('cardCustomField-text');
// cardCustomField-number // cardCustomField-number
(class extends CardCustomField { (class extends CardCustomField {
onCreated() { onCreated() {
super.onCreated(); super.onCreated();
} }
events() { events() {
return [{ return [
'submit .js-card-customfield-number'(evt) { {
evt.preventDefault(); 'submit .js-card-customfield-number'(event) {
const value = parseInt(this.find('input').value, 10); event.preventDefault();
this.card.setCustomField(this.customFieldId, value); const value = parseInt(this.find('input').value, 10);
this.card.setCustomField(this.customFieldId, value);
},
}, },
}]; ];
} }
}.register('cardCustomField-number'));
}).register('cardCustomField-number');
// cardCustomField-date // cardCustomField-date
(class extends CardCustomField { (class extends CardCustomField {
onCreated() { onCreated() {
super.onCreated(); super.onCreated();
const self = this; const self = this;
@ -108,8 +110,10 @@ CardCustomField.register('cardCustomField');
} }
classes() { classes() {
if (this.date.get().isBefore(this.now.get(), 'minute') && if (
this.now.get().isBefore(this.data().value)) { this.date.get().isBefore(this.now.get(), 'minute') &&
this.now.get().isBefore(this.data().value)
) {
return 'current'; return 'current';
} }
return ''; return '';
@ -120,12 +124,13 @@ CardCustomField.register('cardCustomField');
} }
events() { events() {
return [{ return [
'click .js-edit-date': Popup.open('cardCustomField-date'), {
}]; 'click .js-edit-date': Popup.open('cardCustomField-date'),
},
];
} }
}.register('cardCustomField-date'));
}).register('cardCustomField-date');
// cardCustomField-datePopup // cardCustomField-datePopup
(class extends DatePicker { (class extends DatePicker {
@ -144,11 +149,10 @@ CardCustomField.register('cardCustomField');
_deleteDate() { _deleteDate() {
this.card.setCustomField(this.customFieldId, ''); this.card.setCustomField(this.customFieldId, '');
} }
}).register('cardCustomField-datePopup'); }.register('cardCustomField-datePopup'));
// cardCustomField-dropdown // cardCustomField-dropdown
(class extends CardCustomField { (class extends CardCustomField {
onCreated() { onCreated() {
super.onCreated(); super.onCreated();
this._items = this.data().definition.settings.dropdownItems; this._items = this.data().definition.settings.dropdownItems;
@ -160,20 +164,23 @@ CardCustomField.register('cardCustomField');
} }
selectedItem() { selectedItem() {
const selected = this._items.find((item) => { const selected = this._items.find(item => {
return item._id === this.data().value; return item._id === this.data().value;
}); });
return (selected) ? selected.name : TAPi18n.__('custom-field-dropdown-unknown'); return selected
? selected.name
: TAPi18n.__('custom-field-dropdown-unknown');
} }
events() { events() {
return [{ return [
'submit .js-card-customfield-dropdown'(evt) { {
evt.preventDefault(); 'submit .js-card-customfield-dropdown'(event) {
const value = this.find('select').value; event.preventDefault();
this.card.setCustomField(this.customFieldId, value); const value = this.find('select').value;
this.card.setCustomField(this.customFieldId, value);
},
}, },
}]; ];
} }
}.register('cardCustomField-dropdown'));
}).register('cardCustomField-dropdown');

View file

@ -11,15 +11,20 @@ BlazeComponent.extendComponent({
}, },
onRendered() { onRendered() {
const $picker = this.$('.js-datepicker').datepicker({ const $picker = this.$('.js-datepicker')
todayHighlight: true, .datepicker({
todayBtn: 'linked', todayHighlight: true,
language: TAPi18n.getLanguage(), todayBtn: 'linked',
}).on('changeDate', function(evt) { language: TAPi18n.getLanguage(),
this.find('#date').value = moment(evt.date).format('L'); })
this.error.set(''); .on(
this.find('#time').focus(); 'changeDate',
}.bind(this)); function(evt) {
this.find('#date').value = moment(evt.date).format('L');
this.error.set('');
this.find('#time').focus();
}.bind(this),
);
if (this.date.get().isValid()) { if (this.date.get().isValid()) {
$picker.datepicker('update', this.date.get().toDate()); $picker.datepicker('update', this.date.get().toDate());
@ -27,13 +32,11 @@ BlazeComponent.extendComponent({
}, },
showDate() { showDate() {
if (this.date.get().isValid()) if (this.date.get().isValid()) return this.date.get().format('L');
return this.date.get().format('L');
return ''; return '';
}, },
showTime() { showTime() {
if (this.date.get().isValid()) if (this.date.get().isValid()) return this.date.get().format('LT');
return this.date.get().format('LT');
return ''; return '';
}, },
dateFormat() { dateFormat() {
@ -44,51 +47,58 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'keyup .js-date-field'() { {
// parse for localized date format in strict mode 'keyup .js-date-field'() {
const dateMoment = moment(this.find('#date').value, 'L', true); // parse for localized date format in strict mode
if (dateMoment.isValid()) { const dateMoment = moment(this.find('#date').value, 'L', true);
this.error.set(''); if (dateMoment.isValid()) {
this.$('.js-datepicker').datepicker('update', dateMoment.toDate()); this.error.set('');
} this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
}, }
'keyup .js-time-field'() { },
// parse for localized time format in strict mode 'keyup .js-time-field'() {
const dateMoment = moment(this.find('#time').value, 'LT', true); // parse for localized time format in strict mode
if (dateMoment.isValid()) { const dateMoment = moment(this.find('#time').value, 'LT', true);
this.error.set(''); if (dateMoment.isValid()) {
} this.error.set('');
}, }
'submit .edit-date'(evt) { },
evt.preventDefault(); 'submit .edit-date'(evt) {
evt.preventDefault();
// if no time was given, init with 12:00 // if no time was given, init with 12:00
const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT'); const time =
evt.target.time.value ||
moment(new Date().setHours(12, 0, 0)).format('LT');
const dateString = `${evt.target.date.value} ${time}`; const dateString = `${evt.target.date.value} ${time}`;
const newDate = moment(dateString, 'L LT', true); const newDate = moment(dateString, 'L LT', true);
if (newDate.isValid()) { if (newDate.isValid()) {
this._storeDate(newDate.toDate()); this._storeDate(newDate.toDate());
Popup.close();
} else {
this.error.set('invalid-date');
evt.target.date.focus();
}
},
'click .js-delete-date'(evt) {
evt.preventDefault();
this._deleteDate();
Popup.close(); Popup.close();
} },
else {
this.error.set('invalid-date');
evt.target.date.focus();
}
}, },
'click .js-delete-date'(evt) { ];
evt.preventDefault();
this._deleteDate();
Popup.close();
},
}];
}, },
}); });
Template.dateBadge.helpers({ Template.dateBadge.helpers({
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}); });
@ -96,7 +106,8 @@ Template.dateBadge.helpers({
(class extends DatePicker { (class extends DatePicker {
onCreated() { onCreated() {
super.onCreated(); super.onCreated();
this.data().getReceived() && this.date.set(moment(this.data().getReceived())); this.data().getReceived() &&
this.date.set(moment(this.data().getReceived()));
} }
_storeDate(date) { _storeDate(date) {
@ -106,8 +117,7 @@ Template.dateBadge.helpers({
_deleteDate() { _deleteDate() {
this.card.setReceived(null); this.card.setReceived(null);
} }
}).register('editCardReceivedDatePopup'); }.register('editCardReceivedDatePopup'));
// editCardStartDatePopup // editCardStartDatePopup
(class extends DatePicker { (class extends DatePicker {
@ -119,7 +129,10 @@ Template.dateBadge.helpers({
onRendered() { onRendered() {
super.onRendered(); super.onRendered();
if (moment.isDate(this.card.getReceived())) { if (moment.isDate(this.card.getReceived())) {
this.$('.js-datepicker').datepicker('setStartDate', this.card.getReceived()); this.$('.js-datepicker').datepicker(
'setStartDate',
this.card.getReceived(),
);
} }
} }
@ -130,7 +143,7 @@ Template.dateBadge.helpers({
_deleteDate() { _deleteDate() {
this.card.setStart(null); this.card.setStart(null);
} }
}).register('editCardStartDatePopup'); }.register('editCardStartDatePopup'));
// editCardDueDatePopup // editCardDueDatePopup
(class extends DatePicker { (class extends DatePicker {
@ -153,7 +166,7 @@ Template.dateBadge.helpers({
_deleteDate() { _deleteDate() {
this.card.setDue(null); this.card.setDue(null);
} }
}).register('editCardDueDatePopup'); }.register('editCardDueDatePopup'));
// editCardEndDatePopup // editCardEndDatePopup
(class extends DatePicker { (class extends DatePicker {
@ -176,8 +189,7 @@ Template.dateBadge.helpers({
_deleteDate() { _deleteDate() {
this.card.setEnd(null); this.card.setEnd(null);
} }
}).register('editCardEndDatePopup'); }.register('editCardEndDatePopup'));
// Display received, start, due & end dates // Display received, start, due & end dates
const CardDate = BlazeComponent.extendComponent({ const CardDate = BlazeComponent.extendComponent({
@ -224,17 +236,20 @@ class CardReceivedDate extends CardDate {
const startAt = this.data().getStart(); const startAt = this.data().getStart();
const theDate = this.date.get(); const theDate = this.date.get();
// if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged // if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
if (((startAt) && (theDate.isAfter(dueAt))) || if (
((endAt) && (theDate.isAfter(endAt))) || (startAt && theDate.isAfter(dueAt)) ||
((dueAt) && (theDate.isAfter(dueAt)))) (endAt && theDate.isAfter(endAt)) ||
(dueAt && theDate.isAfter(dueAt))
)
classes += 'long-overdue'; classes += 'long-overdue';
else else classes += 'current';
classes += 'current';
return classes; return classes;
} }
showTitle() { showTitle() {
return `${TAPi18n.__('card-received-on')} ${this.date.get().format('LLLL')}`; return `${TAPi18n.__('card-received-on')} ${this.date
.get()
.format('LLLL')}`;
} }
events() { events() {
@ -261,13 +276,10 @@ class CardStartDate extends CardDate {
const theDate = this.date.get(); const theDate = this.date.get();
const now = this.now.get(); const now = this.now.get();
// if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged // if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
if (((endAt) && (theDate.isAfter(endAt))) || if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt)))
((dueAt) && (theDate.isAfter(dueAt))))
classes += 'long-overdue'; classes += 'long-overdue';
else if (theDate.isBefore(now, 'minute')) else if (theDate.isBefore(now, 'minute')) classes += 'almost-due';
classes += 'almost-due'; else classes += 'current';
else
classes += 'current';
return classes; return classes;
} }
@ -298,17 +310,12 @@ class CardDueDate extends CardDate {
const theDate = this.date.get(); const theDate = this.date.get();
const now = this.now.get(); const now = this.now.get();
// if the due date is after the end date, green - done early // if the due date is after the end date, green - done early
if ((endAt) && (theDate.isAfter(endAt))) if (endAt && theDate.isAfter(endAt)) classes += 'current';
classes += 'current';
// if there is an end date, don't need to flag the due date // if there is an end date, don't need to flag the due date
else if (endAt) else if (endAt) classes += '';
classes += ''; else if (now.diff(theDate, 'days') >= 2) classes += 'long-overdue';
else if (now.diff(theDate, 'days') >= 2) else if (now.diff(theDate, 'minute') >= 0) classes += 'due';
classes += 'long-overdue'; else if (now.diff(theDate, 'days') >= -1) classes += 'almost-due';
else if (now.diff(theDate, 'minute') >= 0)
classes += 'due';
else if (now.diff(theDate, 'days') >= -1)
classes += 'almost-due';
return classes; return classes;
} }
@ -337,12 +344,9 @@ class CardEndDate extends CardDate {
let classes = 'end-date' + ' '; let classes = 'end-date' + ' ';
const dueAt = this.data().getDue(); const dueAt = this.data().getDue();
const theDate = this.date.get(); const theDate = this.date.get();
if (theDate.diff(dueAt, 'days') >= 2) if (theDate.diff(dueAt, 'days') >= 2) classes += 'long-overdue';
classes += 'long-overdue'; else if (theDate.diff(dueAt, 'days') >= 0) classes += 'due';
else if (theDate.diff(dueAt, 'days') >= 0) else if (theDate.diff(dueAt, 'days') >= -2) classes += 'almost-due';
classes += 'due';
else if (theDate.diff(dueAt, 'days') >= -2)
classes += 'almost-due';
return classes; return classes;
} }
@ -362,22 +366,22 @@ CardEndDate.register('cardEndDate');
showDate() { showDate() {
return this.date.get().format('l'); return this.date.get().format('l');
} }
}).register('minicardReceivedDate'); }.register('minicardReceivedDate'));
(class extends CardStartDate { (class extends CardStartDate {
showDate() { showDate() {
return this.date.get().format('l'); return this.date.get().format('l');
} }
}).register('minicardStartDate'); }.register('minicardStartDate'));
(class extends CardDueDate { (class extends CardDueDate {
showDate() { showDate() {
return this.date.get().format('l'); return this.date.get().format('l');
} }
}).register('minicardDueDate'); }.register('minicardDueDate'));
(class extends CardEndDate { (class extends CardEndDate {
showDate() { showDate() {
return this.date.get().format('l'); return this.date.get().format('l');
} }
}).register('minicardEndDate'); }.register('minicardEndDate'));

View file

@ -27,7 +27,7 @@ BlazeComponent.extendComponent({
onCreated() { onCreated() {
this.currentBoard = Boards.findOne(Session.get('currentBoard')); this.currentBoard = Boards.findOne(Session.get('currentBoard'));
this.isLoaded = new ReactiveVar(false); this.isLoaded = new ReactiveVar(false);
const boardBody = this.parentComponent().parentComponent(); const boardBody = this.parentComponent().parentComponent();
//in Miniview parent is Board, not BoardBody. //in Miniview parent is Board, not BoardBody.
if (boardBody !== null) { if (boardBody !== null) {
boardBody.showOverlay.set(true); boardBody.showOverlay.set(true);
@ -48,7 +48,11 @@ BlazeComponent.extendComponent({
}, },
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
scrollParentContainer() { scrollParentContainer() {
@ -80,18 +84,17 @@ BlazeComponent.extendComponent({
const cardContainerScrollTop = $cardContainer.scrollTop(); const cardContainerScrollTop = $cardContainer.scrollTop();
let topOffset = false; let topOffset = false;
if(cardViewStartTop !== 100){ if (cardViewStartTop !== 100) {
topOffset = cardViewStartTop - 100; topOffset = cardViewStartTop - 100;
} }
if(topOffset !== false) { if (topOffset !== false) {
bodyBoardComponent.scrollTop(cardContainerScrollTop + topOffset); bodyBoardComponent.scrollTop(cardContainerScrollTop + topOffset);
} }
}, },
presentParentTask() { presentParentTask() {
let result = this.currentBoard.presentParentTask; let result = this.currentBoard.presentParentTask;
if ((result === null) || (result === undefined)) { if (result === null || result === undefined) {
result = 'no-parent'; result = 'no-parent';
} }
return result; return result;
@ -116,7 +119,13 @@ BlazeComponent.extendComponent({
onRendered() { onRendered() {
if (!Utils.isMiniScreen()) { if (!Utils.isMiniScreen()) {
Meteor.setTimeout(() => { Meteor.setTimeout(() => {
$('.card-details').mCustomScrollbar({theme:'minimal-dark', setWidth: false, setLeft: 0, scrollbarPosition: 'outside', mouseWheel: true }); $('.card-details').mCustomScrollbar({
theme: 'minimal-dark',
setWidth: false,
setLeft: 0,
scrollbarPosition: 'outside',
mouseWheel: true,
});
this.scrollParentContainer(); this.scrollParentContainer();
}, 500); }, 500);
} }
@ -212,7 +221,7 @@ BlazeComponent.extendComponent({
}, },
onDestroyed() { onDestroyed() {
const parentComponent = this.parentComponent().parentComponent(); const parentComponent = this.parentComponent().parentComponent();
//on mobile view parent is Board, not board body. //on mobile view parent is Board, not board body.
if (parentComponent === null) return; if (parentComponent === null) return;
parentComponent.showOverlay.set(false); parentComponent.showOverlay.set(false);
@ -228,56 +237,64 @@ BlazeComponent.extendComponent({
}, },
}; };
return [{ return [
...events, {
'click .js-close-card-details' () { ...events,
Utils.goBoardId(this.data().boardId); 'click .js-close-card-details'() {
Utils.goBoardId(this.data().boardId);
},
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
'submit .js-card-description'(event) {
event.preventDefault();
const description = this.currentComponent().getValue();
this.data().setDescription(description);
},
'submit .js-card-details-title'(event) {
event.preventDefault();
const title = this.currentComponent()
.getValue()
.trim();
if (title) {
this.data().setTitle(title);
}
},
'submit .js-card-details-assigner'(event) {
event.preventDefault();
const assigner = this.currentComponent()
.getValue()
.trim();
if (assigner) {
this.data().setAssignedBy(assigner);
}
},
'submit .js-card-details-requester'(event) {
event.preventDefault();
const requester = this.currentComponent()
.getValue()
.trim();
if (requester) {
this.data().setRequestedBy(requester);
}
},
'click .js-member': Popup.open('cardMember'),
'click .js-add-members': Popup.open('cardMembers'),
'click .js-add-labels': Popup.open('cardLabels'),
'click .js-received-date': Popup.open('editCardReceivedDate'),
'click .js-start-date': Popup.open('editCardStartDate'),
'click .js-due-date': Popup.open('editCardDueDate'),
'click .js-end-date': Popup.open('editCardEndDate'),
'mouseenter .js-card-details'() {
const parentComponent = this.parentComponent().parentComponent();
//on mobile view parent is Board, not BoardBody.
if (parentComponent === null) return;
parentComponent.showOverlay.set(true);
parentComponent.mouseHasEnterCardDetails = true;
},
'click #toggleButton'() {
Meteor.call('toggleSystemMessages');
},
}, },
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'), ];
'submit .js-card-description' (evt) {
evt.preventDefault();
const description = this.currentComponent().getValue();
this.data().setDescription(description);
},
'submit .js-card-details-title' (evt) {
evt.preventDefault();
const title = this.currentComponent().getValue().trim();
if (title) {
this.data().setTitle(title);
}
},
'submit .js-card-details-assigner'(evt) {
evt.preventDefault();
const assigner = this.currentComponent().getValue().trim();
if (assigner) {
this.data().setAssignedBy(assigner);
}
},
'submit .js-card-details-requester'(evt) {
evt.preventDefault();
const requester = this.currentComponent().getValue().trim();
if (requester) {
this.data().setRequestedBy(requester);
}
},
'click .js-member': Popup.open('cardMember'),
'click .js-add-members': Popup.open('cardMembers'),
'click .js-add-labels': Popup.open('cardLabels'),
'click .js-received-date': Popup.open('editCardReceivedDate'),
'click .js-start-date': Popup.open('editCardStartDate'),
'click .js-due-date': Popup.open('editCardDueDate'),
'click .js-end-date': Popup.open('editCardEndDate'),
'mouseenter .js-card-details' () {
const parentComponent = this.parentComponent().parentComponent();
//on mobile view parent is Board, not BoardBody.
if (parentComponent === null) return;
parentComponent.showOverlay.set(true);
parentComponent.mouseHasEnterCardDetails = true;
},
'click #toggleButton'() {
Meteor.call('toggleSystemMessages');
},
}];
}, },
}).register('cardDetails'); }).register('cardDetails');
@ -297,7 +314,9 @@ BlazeComponent.extendComponent({
close(isReset = false) { close(isReset = false) {
if (this.isOpen.get() && !isReset) { if (this.isOpen.get() && !isReset) {
const draft = this.getValue().trim(); const draft = this.getValue().trim();
if (draft !== Cards.findOne(Session.get('currentCard')).getDescription()) { if (
draft !== Cards.findOne(Session.get('currentCard')).getDescription()
) {
UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue()); UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue());
} }
} }
@ -311,12 +330,14 @@ BlazeComponent.extendComponent({
events() { events() {
const parentEvents = InlinedForm.prototype.events()[0]; const parentEvents = InlinedForm.prototype.events()[0];
return [{ return [
...parentEvents, {
'click .js-close-inlined-form': this.reset, ...parentEvents,
}]; 'click .js-close-inlined-form': this.reset,
},
];
} }
}).register('inlinedCardDescription'); }.register('inlinedCardDescription'));
Template.cardDetailsActionsPopup.helpers({ Template.cardDetailsActionsPopup.helpers({
isWatching() { isWatching() {
@ -324,7 +345,11 @@ Template.cardDetailsActionsPopup.helpers({
}, },
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}); });
@ -342,23 +367,31 @@ Template.cardDetailsActionsPopup.events({
'click .js-copy-card': Popup.open('copyCard'), 'click .js-copy-card': Popup.open('copyCard'),
'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'), 'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
'click .js-set-card-color': Popup.open('setCardColor'), 'click .js-set-card-color': Popup.open('setCardColor'),
'click .js-move-card-to-top' (evt) { 'click .js-move-card-to-top'(event) {
evt.preventDefault(); event.preventDefault();
const minOrder = _.min(this.list().cards(this.swimlaneId).map((c) => c.sort)); const minOrder = _.min(
this.list()
.cards(this.swimlaneId)
.map(c => c.sort),
);
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1); this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
}, },
'click .js-move-card-to-bottom' (evt) { 'click .js-move-card-to-bottom'(event) {
evt.preventDefault(); event.preventDefault();
const maxOrder = _.max(this.list().cards(this.swimlaneId).map((c) => c.sort)); const maxOrder = _.max(
this.list()
.cards(this.swimlaneId)
.map(c => c.sort),
);
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1); this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
}, },
'click .js-archive' (evt) { 'click .js-archive'(event) {
evt.preventDefault(); event.preventDefault();
this.archive(); this.archive();
Popup.close(); Popup.close();
}, },
'click .js-more': Popup.open('cardMore'), 'click .js-more': Popup.open('cardMore'),
'click .js-toggle-watch-card' () { 'click .js-toggle-watch-card'() {
const currentCard = this; const currentCard = this;
const level = currentCard.findWatcher(Meteor.userId()) ? null : 'watching'; const level = currentCard.findWatcher(Meteor.userId()) ? null : 'watching';
Meteor.call('watch', 'card', currentCard._id, level, (err, ret) => { Meteor.call('watch', 'card', currentCard._id, level, (err, ret) => {
@ -367,15 +400,15 @@ Template.cardDetailsActionsPopup.events({
}, },
}); });
Template.editCardTitleForm.onRendered(function () { Template.editCardTitleForm.onRendered(function() {
autosize(this.$('.js-edit-card-title')); autosize(this.$('.js-edit-card-title'));
}); });
Template.editCardTitleForm.events({ Template.editCardTitleForm.events({
'keydown .js-edit-card-title' (evt) { 'keydown .js-edit-card-title'(event) {
// If enter key was pressed, submit the data // If enter key was pressed, submit the data
// Unless the shift key is also being pressed // Unless the shift key is also being pressed
if (evt.keyCode === 13 && !evt.shiftKey) { if (event.keyCode === 13 && !event.shiftKey) {
$('.js-submit-edit-card-title-form').click(); $('.js-submit-edit-card-title-form').click();
} }
}, },
@ -386,9 +419,9 @@ Template.editCardRequesterForm.onRendered(function() {
}); });
Template.editCardRequesterForm.events({ Template.editCardRequesterForm.events({
'keydown .js-edit-card-requester'(evt) { 'keydown .js-edit-card-requester'(event) {
// If enter key was pressed, submit the data // If enter key was pressed, submit the data
if (evt.keyCode === 13) { if (event.keyCode === 13) {
$('.js-submit-edit-card-requester-form').click(); $('.js-submit-edit-card-requester-form').click();
} }
}, },
@ -399,16 +432,16 @@ Template.editCardAssignerForm.onRendered(function() {
}); });
Template.editCardAssignerForm.events({ Template.editCardAssignerForm.events({
'keydown .js-edit-card-assigner'(evt) { 'keydown .js-edit-card-assigner'(event) {
// If enter key was pressed, submit the data // If enter key was pressed, submit the data
if (evt.keyCode === 13) { if (event.keyCode === 13) {
$('.js-submit-edit-card-assigner-form').click(); $('.js-submit-edit-card-assigner-form').click();
} }
}, },
}); });
Template.moveCardPopup.events({ Template.moveCardPopup.events({
'click .js-done' () { 'click .js-done'() {
// XXX We should *not* get the currentCard from the global state, but // XXX We should *not* get the currentCard from the global state, but
// instead from a “component” state. // instead from a “component” state.
const card = Cards.findOne(Session.get('currentCard')); const card = Cards.findOne(Session.get('currentCard'));
@ -429,13 +462,16 @@ BlazeComponent.extendComponent({
}, },
boards() { boards() {
const boards = Boards.find({ const boards = Boards.find(
archived: false, {
'members.userId': Meteor.userId(), archived: false,
_id: {$ne: Meteor.user().getTemplatesBoardId()}, 'members.userId': Meteor.userId(),
}, { _id: { $ne: Meteor.user().getTemplatesBoardId() },
sort: ['title'], },
}); {
sort: ['title'],
},
);
return boards; return boards;
}, },
@ -450,12 +486,14 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'change .js-select-boards'(evt) { {
this.selectedBoardId.set($(evt.currentTarget).val()); 'change .js-select-boards'(event) {
subManager.subscribe('board', this.selectedBoardId.get(), false); this.selectedBoardId.set($(event.currentTarget).val());
subManager.subscribe('board', this.selectedBoardId.get(), false);
},
}, },
}]; ];
}, },
}).register('boardsAndLists'); }).register('boardsAndLists');
@ -471,7 +509,9 @@ Template.copyCardPopup.events({
const textarea = $('#copy-card-title'); const textarea = $('#copy-card-title');
const title = textarea.val().trim(); const title = textarea.val().trim();
// insert new card to the bottom of new list // insert new card to the bottom of new list
card.sort = Lists.findOne(card.listId).cards().count(); card.sort = Lists.findOne(card.listId)
.cards()
.count();
if (title) { if (title) {
card.title = title; card.title = title;
@ -489,7 +529,7 @@ Template.copyCardPopup.events({
}); });
Template.copyChecklistToManyCardsPopup.events({ Template.copyChecklistToManyCardsPopup.events({
'click .js-done' () { 'click .js-done'() {
const card = Cards.findOne(Session.get('currentCard')); const card = Cards.findOne(Session.get('currentCard'));
const oldId = card._id; const oldId = card._id;
card._id = null; card._id = null;
@ -502,11 +542,13 @@ Template.copyChecklistToManyCardsPopup.events({
const textarea = $('#copy-card-title'); const textarea = $('#copy-card-title');
const titleEntry = textarea.val().trim(); const titleEntry = textarea.val().trim();
// insert new card to the bottom of new list // insert new card to the bottom of new list
card.sort = Lists.findOne(card.listId).cards().count(); card.sort = Lists.findOne(card.listId)
.cards()
.count();
if (titleEntry) { if (titleEntry) {
const titleList = JSON.parse(titleEntry); const titleList = JSON.parse(titleEntry);
for (let i = 0; i < titleList.length; i++){ for (let i = 0; i < titleList.length; i++) {
const obj = titleList[i]; const obj = titleList[i];
card.title = obj.title; card.title = obj.title;
card.description = obj.description; card.description = obj.description;
@ -519,12 +561,12 @@ Template.copyChecklistToManyCardsPopup.events({
Filter.addException(_id); Filter.addException(_id);
// copy checklists // copy checklists
Checklists.find({cardId: oldId}).forEach((ch) => { Checklists.find({ cardId: oldId }).forEach(ch => {
ch.copy(_id); ch.copy(_id);
}); });
// copy subtasks // copy subtasks
cursor = Cards.find({parentId: oldId}); cursor = Cards.find({ parentId: oldId });
cursor.forEach(function() { cursor.forEach(function() {
'use strict'; 'use strict';
const subtask = arguments[0]; const subtask = arguments[0];
@ -534,7 +576,7 @@ Template.copyChecklistToManyCardsPopup.events({
}); });
// copy card comments // copy card comments
CardComments.find({cardId: oldId}).forEach((cmt) => { CardComments.find({ cardId: oldId }).forEach(cmt => {
cmt.copy(_id); cmt.copy(_id);
}); });
} }
@ -550,7 +592,7 @@ BlazeComponent.extendComponent({
}, },
colors() { colors() {
return cardColors.map((color) => ({ color, name: '' })); return cardColors.map(color => ({ color, name: '' }));
}, },
isSelected(color) { isSelected(color) {
@ -561,19 +603,21 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-palette-color'() { {
this.currentColor.set(this.currentData().color); 'click .js-palette-color'() {
this.currentColor.set(this.currentData().color);
},
'click .js-submit'() {
this.currentCard.setColor(this.currentColor.get());
Popup.close();
},
'click .js-remove-color'() {
this.currentCard.setColor(null);
Popup.close();
},
}, },
'click .js-submit' () { ];
this.currentCard.setColor(this.currentColor.get());
Popup.close();
},
'click .js-remove-color'() {
this.currentCard.setColor(null);
Popup.close();
},
}];
}, },
}).register('setCardColorPopup'); }).register('setCardColorPopup');
@ -592,15 +636,18 @@ BlazeComponent.extendComponent({
}, },
boards() { boards() {
const boards = Boards.find({ const boards = Boards.find(
archived: false, {
'members.userId': Meteor.userId(), archived: false,
_id: { 'members.userId': Meteor.userId(),
$ne: Meteor.user().getTemplatesBoardId(), _id: {
$ne: Meteor.user().getTemplatesBoardId(),
},
}, },
}, { {
sort: ['title'], sort: ['title'],
}); },
);
return boards; return boards;
}, },
@ -609,7 +656,7 @@ BlazeComponent.extendComponent({
if (this.parentBoard.get()) { if (this.parentBoard.get()) {
return Cards.find({ return Cards.find({
boardId: this.parentBoard.get(), boardId: this.parentBoard.get(),
_id: {$ne: currentId}, _id: { $ne: currentId },
}); });
} else { } else {
return []; return [];
@ -642,63 +689,69 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-copy-card-link-to-clipboard' () { {
// Clipboard code from: 'click .js-copy-card-link-to-clipboard'() {
// https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser // Clipboard code from:
const StringToCopyElement = document.getElementById('cardURL'); // https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
StringToCopyElement.select(); const StringToCopyElement = document.getElementById('cardURL');
if (document.execCommand('copy')) { StringToCopyElement.select();
StringToCopyElement.blur(); if (document.execCommand('copy')) {
} else { StringToCopyElement.blur();
document.getElementById('cardURL').selectionStart = 0; } else {
document.getElementById('cardURL').selectionEnd = 999; document.getElementById('cardURL').selectionStart = 0;
document.execCommand('copy'); document.getElementById('cardURL').selectionEnd = 999;
if (window.getSelection) { document.execCommand('copy');
if (window.getSelection().empty) { // Chrome if (window.getSelection) {
window.getSelection().empty(); if (window.getSelection().empty) {
} else if (window.getSelection().removeAllRanges) { // Firefox // Chrome
window.getSelection().removeAllRanges(); window.getSelection().empty();
} else if (window.getSelection().removeAllRanges) {
// Firefox
window.getSelection().removeAllRanges();
}
} else if (document.selection) {
// IE?
document.selection.empty();
} }
} else if (document.selection) { // IE?
document.selection.empty();
} }
} },
'click .js-delete': Popup.afterConfirm('cardDelete', function() {
Popup.close();
Cards.remove(this._id);
Utils.goBoardId(this.boardId);
}),
'change .js-field-parent-board'(event) {
const selection = $(event.currentTarget).val();
const list = $('.js-field-parent-card');
if (selection === 'none') {
this.parentBoard.set(null);
} else {
subManager.subscribe('board', $(event.currentTarget).val(), false);
this.parentBoard.set(selection);
list.prop('disabled', false);
}
this.setParentCardId(null);
},
'change .js-field-parent-card'(event) {
const selection = $(event.currentTarget).val();
this.setParentCardId(selection);
},
}, },
'click .js-delete': Popup.afterConfirm('cardDelete', function () { ];
Popup.close();
Cards.remove(this._id);
Utils.goBoardId(this.boardId);
}),
'change .js-field-parent-board'(evt) {
const selection = $(evt.currentTarget).val();
const list = $('.js-field-parent-card');
if (selection === 'none') {
this.parentBoard.set(null);
} else {
subManager.subscribe('board', $(evt.currentTarget).val(), false);
this.parentBoard.set(selection);
list.prop('disabled', false);
}
this.setParentCardId(null);
},
'change .js-field-parent-card'(evt) {
const selection = $(evt.currentTarget).val();
this.setParentCardId(selection);
},
}];
}, },
}).register('cardMorePopup'); }).register('cardMorePopup');
// Close the card details pane by pressing escape // Close the card details pane by pressing escape
EscapeActions.register('detailsPane', EscapeActions.register(
'detailsPane',
() => { () => {
Utils.goBoardId(Session.get('currentBoard')); Utils.goBoardId(Session.get('currentBoard'));
}, },
() => { () => {
return !Session.equals('currentCard', null); return !Session.equals('currentCard', null);
}, { },
{
noClickEscapeOn: '.js-card-details,.board-sidebar,#header', noClickEscapeOn: '.js-card-details,.board-sidebar,#header',
} },
); );

View file

@ -20,29 +20,31 @@ BlazeComponent.extendComponent({
this.card.setSpentTime(null); this.card.setSpentTime(null);
}, },
events() { events() {
return [{ return [
//TODO : need checking this portion {
'submit .edit-time'(evt) { //TODO : need checking this portion
evt.preventDefault(); 'submit .edit-time'(evt) {
evt.preventDefault();
const spentTime = parseFloat(evt.target.time.value); const spentTime = parseFloat(evt.target.time.value);
const isOvertime = this.card.getIsOvertime(); const isOvertime = this.card.getIsOvertime();
if (spentTime >= 0) { if (spentTime >= 0) {
this.storeTime(spentTime, isOvertime); this.storeTime(spentTime, isOvertime);
Popup.close();
} else {
this.error.set('invalid-time');
evt.target.time.focus();
}
},
'click .js-delete-time'(evt) {
evt.preventDefault();
this.deleteTime();
Popup.close(); Popup.close();
} else { },
this.error.set('invalid-time'); 'click a.js-toggle-overtime': this.toggleOvertime,
evt.target.time.focus();
}
}, },
'click .js-delete-time'(evt) { ];
evt.preventDefault();
this.deleteTime();
Popup.close();
},
'click a.js-toggle-overtime': this.toggleOvertime,
}];
}, },
}).register('editCardSpentTimePopup'); }).register('editCardSpentTimePopup');
@ -56,23 +58,33 @@ BlazeComponent.extendComponent({
}, },
showTitle() { showTitle() {
if (this.data().getIsOvertime()) { if (this.data().getIsOvertime()) {
return `${TAPi18n.__('overtime')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`; return `${TAPi18n.__(
'overtime',
)} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
} else { } else {
return `${TAPi18n.__('card-spent')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`; return `${TAPi18n.__(
'card-spent',
)} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
} }
}, },
showTime() { showTime() {
return this.data().getSpentTime(); return this.data().getSpentTime();
}, },
events() { events() {
return [{ return [
'click .js-edit-time': Popup.open('editCardSpentTime'), {
}]; 'click .js-edit-time': Popup.open('editCardSpentTime'),
},
];
}, },
}).register('cardSpentTime'); }).register('cardSpentTime');
Template.timeBadge.helpers({ Template.timeBadge.helpers({
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}); });

View file

@ -64,20 +64,22 @@ BlazeComponent.extendComponent({
}, },
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}).register('checklistDetail'); }).register('checklistDetail');
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
addChecklist(event) { addChecklist(event) {
event.preventDefault(); event.preventDefault();
const textarea = this.find('textarea.js-add-checklist-item'); const textarea = this.find('textarea.js-add-checklist-item');
const title = textarea.value.trim(); const title = textarea.value.trim();
let cardId = this.currentData().cardId; let cardId = this.currentData().cardId;
const card = Cards.findOne(cardId); const card = Cards.findOne(cardId);
if (card.isLinked()) if (card.isLinked()) cardId = card.linkedId;
cardId = card.linkedId;
if (title) { if (title) {
Checklists.insert({ Checklists.insert({
@ -86,7 +88,9 @@ BlazeComponent.extendComponent({
sort: card.checklists().count(), sort: card.checklists().count(),
}); });
setTimeout(() => { setTimeout(() => {
this.$('.add-checklist-item').last().click(); this.$('.add-checklist-item')
.last()
.click();
}, 100); }, 100);
} }
textarea.value = ''; textarea.value = '';
@ -113,7 +117,11 @@ BlazeComponent.extendComponent({
}, },
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
deleteChecklist() { deleteChecklist() {
@ -167,37 +175,43 @@ BlazeComponent.extendComponent({
events() { events() {
const events = { const events = {
'click .toggle-delete-checklist-dialog'(event) { 'click .toggle-delete-checklist-dialog'(event) {
if($(event.target).hasClass('js-delete-checklist')){ if ($(event.target).hasClass('js-delete-checklist')) {
this.checklistToDelete = this.currentData().checklist; //Store data context this.checklistToDelete = this.currentData().checklist; //Store data context
} }
this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get()); this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
}, },
}; };
return [{ return [
...events, {
'submit .js-add-checklist': this.addChecklist, ...events,
'submit .js-edit-checklist-title': this.editChecklist, 'submit .js-add-checklist': this.addChecklist,
'submit .js-add-checklist-item': this.addChecklistItem, 'submit .js-edit-checklist-title': this.editChecklist,
'submit .js-edit-checklist-item': this.editChecklistItem, 'submit .js-add-checklist-item': this.addChecklistItem,
'click .js-delete-checklist-item': this.deleteItem, 'submit .js-edit-checklist-item': this.editChecklistItem,
'click .confirm-checklist-delete': this.deleteChecklist, 'click .js-delete-checklist-item': this.deleteItem,
keydown: this.pressKey, 'click .confirm-checklist-delete': this.deleteChecklist,
}]; keydown: this.pressKey,
},
];
}, },
}).register('checklists'); }).register('checklists');
Template.checklistDeleteDialog.onCreated(() => { Template.checklistDeleteDialog.onCreated(() => {
const $cardDetails = this.$('.card-details'); const $cardDetails = this.$('.card-details');
this.scrollState = { position: $cardDetails.scrollTop(), //save current scroll position this.scrollState = {
position: $cardDetails.scrollTop(), //save current scroll position
top: false, //required for smooth scroll animation top: false, //required for smooth scroll animation
}; };
//Callback's purpose is to only prevent scrolling after animation is complete //Callback's purpose is to only prevent scrolling after animation is complete
$cardDetails.animate({ scrollTop: 0 }, 500, () => { this.scrollState.top = true; }); $cardDetails.animate({ scrollTop: 0 }, 500, () => {
this.scrollState.top = true;
});
//Prevent scrolling while dialog is open //Prevent scrolling while dialog is open
$cardDetails.on('scroll', () => { $cardDetails.on('scroll', () => {
if(this.scrollState.top) { //If it's already in position, keep it there. Otherwise let animation scroll if (this.scrollState.top) {
//If it's already in position, keep it there. Otherwise let animation scroll
$cardDetails.scrollTop(0); $cardDetails.scrollTop(0);
} }
}); });
@ -206,12 +220,16 @@ Template.checklistDeleteDialog.onCreated(() => {
Template.checklistDeleteDialog.onDestroyed(() => { Template.checklistDeleteDialog.onDestroyed(() => {
const $cardDetails = this.$('.card-details'); const $cardDetails = this.$('.card-details');
$cardDetails.off('scroll'); //Reactivate scrolling $cardDetails.off('scroll'); //Reactivate scrolling
$cardDetails.animate( { scrollTop: this.scrollState.position }); $cardDetails.animate({ scrollTop: this.scrollState.position });
}); });
Template.checklistItemDetail.helpers({ Template.checklistItemDetail.helpers({
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}); });
@ -224,8 +242,10 @@ BlazeComponent.extendComponent({
} }
}, },
events() { events() {
return [{ return [
'click .js-checklist-item .check-box': this.toggleItem, {
}]; 'click .js-checklist-item .check-box': this.toggleItem,
},
];
}, },
}).register('checklistItemDetail'); }).register('checklistItemDetail');

View file

@ -9,7 +9,7 @@ BlazeComponent.extendComponent({
}, },
labels() { labels() {
return labelColors.map((color) => ({ color, name: '' })); return labelColors.map(color => ({ color, name: '' }));
}, },
isSelected(color) { isSelected(color) {
@ -17,11 +17,13 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-palette-color'() { {
this.currentColor.set(this.currentData().color); 'click .js-palette-color'() {
this.currentColor.set(this.currentData().color);
},
}, },
}]; ];
}, },
}).register('formLabel'); }).register('formLabel');
@ -38,19 +40,19 @@ Template.createLabelPopup.helpers({
}); });
Template.cardLabelsPopup.events({ Template.cardLabelsPopup.events({
'click .js-select-label'(evt) { 'click .js-select-label'(event) {
const card = Cards.findOne(Session.get('currentCard')); const card = Cards.findOne(Session.get('currentCard'));
const labelId = this._id; const labelId = this._id;
card.toggleLabel(labelId); card.toggleLabel(labelId);
evt.preventDefault(); event.preventDefault();
}, },
'click .js-edit-label': Popup.open('editLabel'), 'click .js-edit-label': Popup.open('editLabel'),
'click .js-add-label': Popup.open('createLabel'), 'click .js-add-label': Popup.open('createLabel'),
}); });
Template.formLabel.events({ Template.formLabel.events({
'click .js-palette-color'(evt) { 'click .js-palette-color'(event) {
const $this = $(evt.currentTarget); const $this = $(event.currentTarget);
// hide selected ll colors // hide selected ll colors
$('.js-palette-select').addClass('hide'); $('.js-palette-select').addClass('hide');
@ -62,11 +64,14 @@ Template.formLabel.events({
Template.createLabelPopup.events({ Template.createLabelPopup.events({
// Create the new label // Create the new label
'submit .create-label'(evt, tpl) { 'submit .create-label'(event, templateInstance) {
evt.preventDefault(); event.preventDefault();
const board = Boards.findOne(Session.get('currentBoard')); const board = Boards.findOne(Session.get('currentBoard'));
const name = tpl.$('#labelName').val().trim(); const name = templateInstance
const color = Blaze.getData(tpl.find('.fa-check')).color; .$('#labelName')
.val()
.trim();
const color = Blaze.getData(templateInstance.find('.fa-check')).color;
board.addLabel(name, color); board.addLabel(name, color);
Popup.back(); Popup.back();
}, },
@ -78,11 +83,14 @@ Template.editLabelPopup.events({
board.removeLabel(this._id); board.removeLabel(this._id);
Popup.back(2); Popup.back(2);
}), }),
'submit .edit-label'(evt, tpl) { 'submit .edit-label'(event, templateInstance) {
evt.preventDefault(); event.preventDefault();
const board = Boards.findOne(Session.get('currentBoard')); const board = Boards.findOne(Session.get('currentBoard'));
const name = tpl.$('#labelName').val().trim(); const name = templateInstance
const color = Blaze.getData(tpl.find('.fa-check')).color; .$('#labelName')
.val()
.trim();
const color = Blaze.getData(templateInstance.find('.fa-check')).color;
board.editLabel(this._id, name, color); board.editLabel(this._id, name, color);
Popup.back(); Popup.back();
}, },

View file

@ -8,13 +8,14 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-linked-link' () { {
if (this.data().isLinkedCard()) 'click .js-linked-link'() {
Utils.goCardId(this.data().linkedId); if (this.data().isLinkedCard()) Utils.goCardId(this.data().linkedId);
else if (this.data().isLinkedBoard()) else if (this.data().isLinkedBoard())
Utils.goBoardId(this.data().linkedId); Utils.goBoardId(this.data().linkedId);
},
}, },
}]; ];
}, },
}).register('minicard'); }).register('minicard');

View file

@ -1,11 +1,14 @@
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}).register('subtaskDetail'); }).register('subtaskDetail');
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
addSubtask(event) { addSubtask(event) {
event.preventDefault(); event.preventDefault();
const textarea = this.find('textarea.js-add-subtask-item'); const textarea = this.find('textarea.js-add-subtask-item');
@ -38,9 +41,10 @@ BlazeComponent.extendComponent({
// See https://github.com/wekan/wekan/issues/80 // See https://github.com/wekan/wekan/issues/80
Filter.addException(_id); Filter.addException(_id);
setTimeout(() => { setTimeout(() => {
this.$('.add-subtask-item').last().click(); this.$('.add-subtask-item')
.last()
.click();
}, 100); }, 100);
} }
textarea.value = ''; textarea.value = '';
@ -48,7 +52,11 @@ BlazeComponent.extendComponent({
}, },
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
deleteSubtask() { deleteSubtask() {
@ -85,13 +93,13 @@ BlazeComponent.extendComponent({
events() { events() {
const events = { const events = {
'click .toggle-delete-subtask-dialog'(event) { 'click .toggle-delete-subtask-dialog'(event) {
if($(event.target).hasClass('js-delete-subtask')){ if ($(event.target).hasClass('js-delete-subtask')) {
this.subtaskToDelete = this.currentData().subtask; //Store data context this.subtaskToDelete = this.currentData().subtask; //Store data context
} }
this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get()); this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
}, },
'click .js-view-subtask'(event) { 'click .js-view-subtask'(event) {
if($(event.target).hasClass('js-view-subtask')){ if ($(event.target).hasClass('js-view-subtask')) {
const subtask = this.currentData().subtask; const subtask = this.currentData().subtask;
const board = subtask.board(); const board = subtask.board();
FlowRouter.go('card', { FlowRouter.go('card', {
@ -103,27 +111,33 @@ BlazeComponent.extendComponent({
}, },
}; };
return [{ return [
...events, {
'submit .js-add-subtask': this.addSubtask, ...events,
'submit .js-edit-subtask-title': this.editSubtask, 'submit .js-add-subtask': this.addSubtask,
'click .confirm-subtask-delete': this.deleteSubtask, 'submit .js-edit-subtask-title': this.editSubtask,
keydown: this.pressKey, 'click .confirm-subtask-delete': this.deleteSubtask,
}]; keydown: this.pressKey,
},
];
}, },
}).register('subtasks'); }).register('subtasks');
Template.subtaskDeleteDialog.onCreated(() => { Template.subtaskDeleteDialog.onCreated(() => {
const $cardDetails = this.$('.card-details'); const $cardDetails = this.$('.card-details');
this.scrollState = { position: $cardDetails.scrollTop(), //save current scroll position this.scrollState = {
position: $cardDetails.scrollTop(), //save current scroll position
top: false, //required for smooth scroll animation top: false, //required for smooth scroll animation
}; };
//Callback's purpose is to only prevent scrolling after animation is complete //Callback's purpose is to only prevent scrolling after animation is complete
$cardDetails.animate({ scrollTop: 0 }, 500, () => { this.scrollState.top = true; }); $cardDetails.animate({ scrollTop: 0 }, 500, () => {
this.scrollState.top = true;
});
//Prevent scrolling while dialog is open //Prevent scrolling while dialog is open
$cardDetails.on('scroll', () => { $cardDetails.on('scroll', () => {
if(this.scrollState.top) { //If it's already in position, keep it there. Otherwise let animation scroll if (this.scrollState.top) {
//If it's already in position, keep it there. Otherwise let animation scroll
$cardDetails.scrollTop(0); $cardDetails.scrollTop(0);
} }
}); });
@ -132,12 +146,16 @@ Template.subtaskDeleteDialog.onCreated(() => {
Template.subtaskDeleteDialog.onDestroyed(() => { Template.subtaskDeleteDialog.onDestroyed(() => {
const $cardDetails = this.$('.card-details'); const $cardDetails = this.$('.card-details');
$cardDetails.off('scroll'); //Reactivate scrolling $cardDetails.off('scroll'); //Reactivate scrolling
$cardDetails.animate( { scrollTop: this.scrollState.position }); $cardDetails.animate({ scrollTop: this.scrollState.position });
}); });
Template.subtaskItemDetail.helpers({ Template.subtaskItemDetail.helpers({
canModifyCard() { canModifyCard() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}); });

View file

@ -56,7 +56,7 @@ BlazeComponent.extendComponent({
const membersMapping = this.membersToMap.get(); const membersMapping = this.membersToMap.get();
if (membersMapping) { if (membersMapping) {
const mappingById = {}; const mappingById = {};
membersMapping.forEach((member) => { membersMapping.forEach(member => {
if (member.wekanId) { if (member.wekanId) {
mappingById[member.id] = member.wekanId; mappingById[member.id] = member.wekanId;
} }
@ -64,7 +64,8 @@ BlazeComponent.extendComponent({
additionalData.membersMapping = mappingById; additionalData.membersMapping = mappingById;
} }
this.membersToMap.set([]); this.membersToMap.set([]);
Meteor.call('importBoard', Meteor.call(
'importBoard',
this.importedData.get(), this.importedData.get(),
additionalData, additionalData,
this.importSource, this.importSource,
@ -76,7 +77,7 @@ BlazeComponent.extendComponent({
Session.set('fromBoard', null); Session.set('fromBoard', null);
Utils.goBoardId(res); Utils.goBoardId(res);
} }
} },
); );
}, },
@ -84,12 +85,12 @@ BlazeComponent.extendComponent({
const importSource = Session.get('importSource'); const importSource = Session.get('importSource');
let membersToMap; let membersToMap;
switch (importSource) { switch (importSource) {
case 'trello': case 'trello':
membersToMap = trelloMembersMapper.getMembersToMap(dataObject); membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
break; break;
case 'wekan': case 'wekan':
membersToMap = wekanMembersMapper.getMembersToMap(dataObject); membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
break; break;
} }
return membersToMap; return membersToMap;
}, },
@ -109,22 +110,26 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
submit(evt) { {
return this.parentComponent().importData(evt); submit(evt) {
return this.parentComponent().importData(evt);
},
}, },
}]; ];
}, },
}).register('importTextarea'); }).register('importTextarea');
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
onCreated() { onCreated() {
this.autorun(() => { this.autorun(() => {
this.parentComponent().membersToMap.get().forEach(({ wekanId }) => { this.parentComponent()
if (wekanId) { .membersToMap.get()
this.subscribe('user-miniprofile', wekanId); .forEach(({ wekanId }) => {
} if (wekanId) {
}); this.subscribe('user-miniprofile', wekanId);
}
});
}); });
}, },
@ -149,23 +154,23 @@ BlazeComponent.extendComponent({
_setPropertyForMember(property, value, memberId, unset = false) { _setPropertyForMember(property, value, memberId, unset = false) {
const listOfMembers = this.members(); const listOfMembers = this.members();
let finder = null; let finder = null;
if(memberId) { if (memberId) {
finder = (member) => member.id === memberId; finder = member => member.id === memberId;
} else { } else {
finder = (member) => member.selected; finder = member => member.selected;
} }
listOfMembers.forEach((member) => { listOfMembers.forEach(member => {
if(finder(member)) { if (finder(member)) {
if(value !== null) { if (value !== null) {
member[property] = value; member[property] = value;
} else { } else {
delete member[property]; delete member[property];
} }
if(!unset) { if (!unset) {
// we shortcut if we don't care about unsetting the others // we shortcut if we don't care about unsetting the others
return false; return false;
} }
} else if(unset) { } else if (unset) {
delete member[property]; delete member[property];
} }
return true; return true;
@ -186,9 +191,9 @@ BlazeComponent.extendComponent({
const allMembers = this.members(); const allMembers = this.members();
let finder = null; let finder = null;
if (memberId) { if (memberId) {
finder = (user) => user.id === memberId; finder = user => user.id === memberId;
} else { } else {
finder = (user) => user.selected; finder = user => user.selected;
} }
return allMembers.find(finder); return allMembers.find(finder);
}, },
@ -197,7 +202,7 @@ BlazeComponent.extendComponent({
return this._setPropertyForMember('wekanId', wekanId, null); return this._setPropertyForMember('wekanId', wekanId, null);
}, },
unmapMember(memberId){ unmapMember(memberId) {
return this._setPropertyForMember('wekanId', null, memberId); return this._setPropertyForMember('wekanId', null, memberId);
}, },
@ -208,7 +213,7 @@ BlazeComponent.extendComponent({
onMapMember(evt) { onMapMember(evt) {
const memberToMap = this.currentData(); const memberToMap = this.currentData();
if(memberToMap.wekan) { if (memberToMap.wekan) {
// todo xxx ask for confirmation? // todo xxx ask for confirmation?
this.unmapMember(memberToMap.id); this.unmapMember(memberToMap.id);
} else { } else {
@ -218,10 +223,12 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'submit': this.onSubmit, {
'click .js-select-member': this.onMapMember, submit: this.onSubmit,
}]; 'click .js-select-member': this.onMapMember,
},
];
}, },
}).register('importMapMembers'); }).register('importMapMembers');
@ -230,14 +237,16 @@ BlazeComponent.extendComponent({
this.find('.js-map-member input').focus(); this.find('.js-map-member input').focus();
}, },
onSelectUser(){ onSelectUser() {
Popup.getOpenerComponent().mapSelectedMember(this.currentData()._id); Popup.getOpenerComponent().mapSelectedMember(this.currentData()._id);
Popup.back(); Popup.back();
}, },
events() { events() {
return [{ return [
'click .js-select-import': this.onSelectUser, {
}]; 'click .js-select-import': this.onSelectUser,
},
];
}, },
}).register('importMapMembersAddPopup'); }).register('importMapMembersAddPopup');

View file

@ -4,7 +4,7 @@ export function getMembersToMap(data) {
// imported member // imported member
const membersToMap = data.members; const membersToMap = data.members;
// auto-map based on username // auto-map based on username
membersToMap.forEach((importedMember) => { membersToMap.forEach(importedMember => {
const wekanUser = Users.findOne({ username: importedMember.username }); const wekanUser = Users.findOne({ username: importedMember.username });
if (wekanUser) { if (wekanUser) {
importedMember.wekanId = wekanUser._id; importedMember.wekanId = wekanUser._id;

View file

@ -5,10 +5,10 @@ export function getMembersToMap(data) {
const membersToMap = data.members; const membersToMap = data.members;
const users = data.users; const users = data.users;
// auto-map based on username // auto-map based on username
membersToMap.forEach((importedMember) => { membersToMap.forEach(importedMember => {
importedMember.id = importedMember.userId; importedMember.id = importedMember.userId;
delete importedMember.userId; delete importedMember.userId;
const user = users.filter((user) => { const user = users.filter(user => {
return user._id === importedMember.id; return user._id === importedMember.id;
})[0]; })[0];
if (user.profile && user.profile.fullname) { if (user.profile && user.profile.fullname) {

View file

@ -21,14 +21,18 @@ BlazeComponent.extendComponent({
const boardComponent = this.parentComponent().parentComponent(); const boardComponent = this.parentComponent().parentComponent();
function userIsMember() { function userIsMember() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
} }
const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)'; const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
const $cards = this.$('.js-minicards'); const $cards = this.$('.js-minicards');
if(window.matchMedia('(max-width: 1199px)').matches) { if (window.matchMedia('(max-width: 1199px)').matches) {
$( '.js-minicards' ).sortable({ $('.js-minicards').sortable({
handle: '.handle', handle: '.handle',
}); });
} }
@ -42,10 +46,16 @@ BlazeComponent.extendComponent({
if (MultiSelection.isActive()) { if (MultiSelection.isActive()) {
const andNOthers = $cards.find('.js-minicard.is-checked').length - 1; const andNOthers = $cards.find('.js-minicard.is-checked').length - 1;
if (andNOthers > 0) { if (andNOthers > 0) {
helper.append($(Blaze.toHTML(HTML.DIV( helper.append(
{ 'class': 'and-n-other' }, $(
TAPi18n.__('and-n-other-card', { count: andNOthers }) Blaze.toHTML(
)))); HTML.DIV(
{ class: 'and-n-other' },
TAPi18n.__('and-n-other-card', { count: andNOthers }),
),
),
),
);
} }
} }
return helper; return helper;
@ -70,9 +80,16 @@ BlazeComponent.extendComponent({
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
let swimlaneId = ''; let swimlaneId = '';
const boardView = (Meteor.user().profile || {}).boardView; const boardView = (Meteor.user().profile || {}).boardView;
if (boardView === 'board-view-swimlanes' || currentBoard.isTemplatesBoard()) if (
boardView === 'board-view-swimlanes' ||
currentBoard.isTemplatesBoard()
)
swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id; swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id;
else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal') || !boardView) else if (
boardView === 'board-view-lists' ||
boardView === 'board-view-cal' ||
!boardView
)
swimlaneId = currentBoard.getDefaultSwimline()._id; swimlaneId = currentBoard.getDefaultSwimline()._id;
// Normally the jquery-ui sortable library moves the dragged DOM element // Normally the jquery-ui sortable library moves the dragged DOM element
@ -86,7 +103,12 @@ BlazeComponent.extendComponent({
if (MultiSelection.isActive()) { if (MultiSelection.isActive()) {
Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => { Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => {
card.move(currentBoard._id, swimlaneId, listId, sortIndex.base + i * sortIndex.increment); card.move(
currentBoard._id,
swimlaneId,
listId,
sortIndex.base + i * sortIndex.increment,
);
}); });
} else { } else {
const cardDomElement = ui.item.get(0); const cardDomElement = ui.item.get(0);

View file

@ -16,7 +16,7 @@ BlazeComponent.extendComponent({
options.position = options.position || 'top'; options.position = options.position || 'top';
const forms = this.childComponents('inlinedForm'); const forms = this.childComponents('inlinedForm');
let form = forms.find((component) => { let form = forms.find(component => {
return component.data().position === options.position; return component.data().position === options.position;
}); });
if (!form && forms.length > 0) { if (!form && forms.length > 0) {
@ -52,11 +52,12 @@ BlazeComponent.extendComponent({
let cardType = 'cardType-card'; let cardType = 'cardType-card';
if (title) { if (title) {
if (board.isTemplatesBoard()) { if (board.isTemplatesBoard()) {
swimlaneId = this.parentComponent().parentComponent().data()._id; // Always swimlanes view swimlaneId = this.parentComponent()
.parentComponent()
.data()._id; // Always swimlanes view
const swimlane = Swimlanes.findOne(swimlaneId); const swimlane = Swimlanes.findOne(swimlaneId);
// If this is the card templates swimlane, insert a card template // If this is the card templates swimlane, insert a card template
if (swimlane.isCardTemplatesSwimlane()) if (swimlane.isCardTemplatesSwimlane()) cardType = 'template-card';
cardType = 'template-card';
// If this is the board templates swimlane, insert a board template and a linked card // If this is the board templates swimlane, insert a board template and a linked card
else if (swimlane.isBoardTemplatesSwimlane()) { else if (swimlane.isBoardTemplatesSwimlane()) {
linkedId = Boards.insert({ linkedId = Boards.insert({
@ -71,8 +72,14 @@ BlazeComponent.extendComponent({
cardType = 'cardType-linkedBoard'; cardType = 'cardType-linkedBoard';
} }
} else if (boardView === 'board-view-swimlanes') } else if (boardView === 'board-view-swimlanes')
swimlaneId = this.parentComponent().parentComponent().data()._id; swimlaneId = this.parentComponent()
else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal') || !boardView) .parentComponent()
.data()._id;
else if (
boardView === 'board-view-lists' ||
boardView === 'board-view-cal' ||
!boardView
)
swimlaneId = board.getDefaultSwimline()._id; swimlaneId = board.getDefaultSwimline()._id;
const _id = Cards.insert({ const _id = Cards.insert({
@ -91,7 +98,9 @@ BlazeComponent.extendComponent({
// if the displayed card count is less than the total cards in the list, // if the displayed card count is less than the total cards in the list,
// we need to increment the displayed card count to prevent the spinner // we need to increment the displayed card count to prevent the spinner
// to appear // to appear
const cardCount = this.data().cards(this.idOrNull(swimlaneId)).count(); const cardCount = this.data()
.cards(this.idOrNull(swimlaneId))
.count();
if (this.cardlimit.get() < cardCount) { if (this.cardlimit.get() < cardCount) {
this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter); this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
} }
@ -149,8 +158,12 @@ BlazeComponent.extendComponent({
idOrNull(swimlaneId) { idOrNull(swimlaneId) {
const currentUser = Meteor.user(); const currentUser = Meteor.user();
if ((currentUser.profile || {}).boardView === 'board-view-swimlanes' || if (
this.data().board().isTemplatesBoard()) (currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
this.data()
.board()
.isTemplatesBoard()
)
return swimlaneId; return swimlaneId;
return undefined; return undefined;
}, },
@ -161,8 +174,7 @@ BlazeComponent.extendComponent({
listId: this.currentData()._id, listId: this.currentData()._id,
archived: false, archived: false,
}; };
if (swimlaneId) if (swimlaneId) selector.swimlaneId = swimlaneId;
selector.swimlaneId = swimlaneId;
return Cards.find(Filter.mongoSelector(selector), { return Cards.find(Filter.mongoSelector(selector), {
sort: ['sort'], sort: ['sort'],
limit, limit,
@ -175,21 +187,32 @@ BlazeComponent.extendComponent({
}, },
canSeeAddCard() { canSeeAddCard() {
return !this.reachedWipLimit() && Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
!this.reachedWipLimit() &&
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
reachedWipLimit() { reachedWipLimit() {
const list = Template.currentData(); const list = Template.currentData();
return !list.getWipLimit('soft') && list.getWipLimit('enabled') && list.getWipLimit('value') <= list.cards().count(); return (
!list.getWipLimit('soft') &&
list.getWipLimit('enabled') &&
list.getWipLimit('value') <= list.cards().count()
);
}, },
events() { events() {
return [{ return [
'click .js-minicard': this.clickOnMiniCard, {
'click .js-toggle-multi-selection': this.toggleMultiSelection, 'click .js-minicard': this.clickOnMiniCard,
'click .open-minicard-composer': this.scrollToBottom, 'click .js-toggle-multi-selection': this.toggleMultiSelection,
submit: this.addCard, 'click .open-minicard-composer': this.scrollToBottom,
}]; submit: this.addCard,
},
];
}, },
}).register('listBody'); }).register('listBody');
@ -212,10 +235,15 @@ BlazeComponent.extendComponent({
const currentBoardId = Session.get('currentBoard'); const currentBoardId = Session.get('currentBoard');
arr = []; arr = [];
_.forEach(Boards.findOne(currentBoardId).customFields().fetch(), function(field){ _.forEach(
if(field.automaticallyOnCard) Boards.findOne(currentBoardId)
arr.push({_id: field._id, value: null}); .customFields()
}); .fetch(),
function(field) {
if (field.automaticallyOnCard)
arr.push({ _id: field._id, value: null });
},
);
this.customFields.set(arr); this.customFields.set(arr);
}, },
@ -227,7 +255,7 @@ BlazeComponent.extendComponent({
getLabels() { getLabels() {
const currentBoardId = Session.get('currentBoard'); const currentBoardId = Session.get('currentBoard');
return Boards.findOne(currentBoardId).labels.filter((label) => { return Boards.findOne(currentBoardId).labels.filter(label => {
return this.labels.get().indexOf(label._id) > -1; return this.labels.get().indexOf(label._id) > -1;
}); });
}, },
@ -257,18 +285,20 @@ BlazeComponent.extendComponent({
} }
BlazeComponent.getComponentForElement(nextList).openForm({ BlazeComponent.getComponentForElement(nextList).openForm({
position:this.data().position, position: this.data().position,
}); });
} }
}, },
events() { events() {
return [{ return [
keydown: this.pressKey, {
'click .js-link': Popup.open('linkCard'), keydown: this.pressKey,
'click .js-search': Popup.open('searchElement'), 'click .js-link': Popup.open('linkCard'),
'click .js-card-template': Popup.open('searchElement'), 'click .js-search': Popup.open('searchElement'),
}]; 'click .js-card-template': Popup.open('searchElement'),
},
];
}, },
onRendered() { onRendered() {
@ -277,66 +307,75 @@ BlazeComponent.extendComponent({
autosize($textarea); autosize($textarea);
$textarea.escapeableTextComplete([ $textarea.escapeableTextComplete(
// User mentions [
{ // User mentions
match: /\B@([\w.]*)$/, {
search(term, callback) { match: /\B@([\w.]*)$/,
const currentBoard = Boards.findOne(Session.get('currentBoard')); search(term, callback) {
callback($.map(currentBoard.activeMembers(), (member) => { const currentBoard = Boards.findOne(Session.get('currentBoard'));
const user = Users.findOne(member.userId); callback(
return user.username.indexOf(term) === 0 ? user : null; $.map(currentBoard.activeMembers(), member => {
})); const user = Users.findOne(member.userId);
return user.username.indexOf(term) === 0 ? user : null;
}),
);
},
template(user) {
return user.username;
},
replace(user) {
toggleValueInReactiveArray(editor.members, user._id);
return '';
},
index: 1,
}, },
template(user) {
return user.username;
},
replace(user) {
toggleValueInReactiveArray(editor.members, user._id);
return '';
},
index: 1,
},
// Labels // Labels
{
match: /\B#(\w*)$/,
search(term, callback) {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
callback(
$.map(currentBoard.labels, label => {
if (
label.name.indexOf(term) > -1 ||
label.color.indexOf(term) > -1
) {
return label;
}
return null;
}),
);
},
template(label) {
return Blaze.toHTMLWithData(Template.autocompleteLabelLine, {
hasNoName: !label.name,
colorName: label.color,
labelName: label.name || label.color,
});
},
replace(label) {
toggleValueInReactiveArray(editor.labels, label._id);
return '';
},
index: 1,
},
],
{ {
match: /\B#(\w*)$/, // When the autocomplete menu is shown we want both a press of both `Tab`
search(term, callback) { // or `Enter` to validation the auto-completion. We also need to stop the
const currentBoard = Boards.findOne(Session.get('currentBoard')); // event propagation to prevent the card from submitting (on `Enter`) or
callback($.map(currentBoard.labels, (label) => { // going on the next column (on `Tab`).
if (label.name.indexOf(term) > -1 || onKeydown(evt, commands) {
label.color.indexOf(term) > -1) { if (evt.keyCode === 9 || evt.keyCode === 13) {
return label; evt.stopPropagation();
} return commands.KEY_ENTER;
return null; }
})); return null;
}, },
template(label) {
return Blaze.toHTMLWithData(Template.autocompleteLabelLine, {
hasNoName: !label.name,
colorName: label.color,
labelName: label.name || label.color,
});
},
replace(label) {
toggleValueInReactiveArray(editor.labels, label._id);
return '';
},
index: 1,
}, },
], { );
// When the autocomplete menu is shown we want both a press of both `Tab`
// or `Enter` to validation the auto-completion. We also need to stop the
// event propagation to prevent the card from submitting (on `Enter`) or
// going on the next column (on `Tab`).
onKeydown(evt, commands) {
if (evt.keyCode === 9 || evt.keyCode === 13) {
evt.stopPropagation();
return commands.KEY_ENTER;
}
return null;
},
});
}, },
}).register('addCardForm'); }).register('addCardForm');
@ -354,24 +393,29 @@ BlazeComponent.extendComponent({
const list = $(Popup._getTopStack().openerElement).closest('.js-list'); const list = $(Popup._getTopStack().openerElement).closest('.js-list');
this.listId = Blaze.getData(list[0])._id; this.listId = Blaze.getData(list[0])._id;
// Swimlane where to insert card // Swimlane where to insert card
const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane'); const swimlane = $(Popup._getTopStack().openerElement).closest(
'.js-swimlane',
);
this.swimlaneId = ''; this.swimlaneId = '';
const boardView = (Meteor.user().profile || {}).boardView; const boardView = (Meteor.user().profile || {}).boardView;
if (boardView === 'board-view-swimlanes') if (boardView === 'board-view-swimlanes')
this.swimlaneId = Blaze.getData(swimlane[0])._id; this.swimlaneId = Blaze.getData(swimlane[0])._id;
else if (boardView === 'board-view-lists' || !boardView) else if (boardView === 'board-view-lists' || !boardView)
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id; this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
}, },
boards() { boards() {
const boards = Boards.find({ const boards = Boards.find(
archived: false, {
'members.userId': Meteor.userId(), archived: false,
_id: {$ne: Session.get('currentBoard')}, 'members.userId': Meteor.userId(),
type: 'board', _id: { $ne: Session.get('currentBoard') },
}, { type: 'board',
sort: ['title'], },
}); {
sort: ['title'],
},
);
return boards; return boards;
}, },
@ -379,7 +423,7 @@ BlazeComponent.extendComponent({
if (!this.selectedBoardId.get()) { if (!this.selectedBoardId.get()) {
return []; return [];
} }
const swimlanes = Swimlanes.find({boardId: this.selectedBoardId.get()}); const swimlanes = Swimlanes.find({ boardId: this.selectedBoardId.get() });
if (swimlanes.count()) if (swimlanes.count())
this.selectedSwimlaneId.set(swimlanes.fetch()[0]._id); this.selectedSwimlaneId.set(swimlanes.fetch()[0]._id);
return swimlanes; return swimlanes;
@ -389,9 +433,8 @@ BlazeComponent.extendComponent({
if (!this.selectedBoardId.get()) { if (!this.selectedBoardId.get()) {
return []; return [];
} }
const lists = Lists.find({boardId: this.selectedBoardId.get()}); const lists = Lists.find({ boardId: this.selectedBoardId.get() });
if (lists.count()) if (lists.count()) this.selectedListId.set(lists.fetch()[0]._id);
this.selectedListId.set(lists.fetch()[0]._id);
return lists; return lists;
}, },
@ -399,73 +442,84 @@ BlazeComponent.extendComponent({
if (!this.board) { if (!this.board) {
return []; return [];
} }
const ownCardsIds = this.board.cards().map((card) => { return card.linkedId || card._id; }); const ownCardsIds = this.board.cards().map(card => {
return card.linkedId || card._id;
});
return Cards.find({ return Cards.find({
boardId: this.selectedBoardId.get(), boardId: this.selectedBoardId.get(),
swimlaneId: this.selectedSwimlaneId.get(), swimlaneId: this.selectedSwimlaneId.get(),
listId: this.selectedListId.get(), listId: this.selectedListId.get(),
archived: false, archived: false,
linkedId: {$nin: ownCardsIds}, linkedId: { $nin: ownCardsIds },
_id: {$nin: ownCardsIds}, _id: { $nin: ownCardsIds },
type: {$nin: ['template-card']}, type: { $nin: ['template-card'] },
}); });
}, },
events() { events() {
return [{ return [
'change .js-select-boards'(evt) { {
subManager.subscribe('board', $(evt.currentTarget).val(), false); 'change .js-select-boards'(evt) {
this.selectedBoardId.set($(evt.currentTarget).val()); subManager.subscribe('board', $(evt.currentTarget).val(), false);
}, this.selectedBoardId.set($(evt.currentTarget).val());
'change .js-select-swimlanes'(evt) { },
this.selectedSwimlaneId.set($(evt.currentTarget).val()); 'change .js-select-swimlanes'(evt) {
}, this.selectedSwimlaneId.set($(evt.currentTarget).val());
'change .js-select-lists'(evt) { },
this.selectedListId.set($(evt.currentTarget).val()); 'change .js-select-lists'(evt) {
}, this.selectedListId.set($(evt.currentTarget).val());
'click .js-done' (evt) { },
// LINK CARD 'click .js-done'(evt) {
evt.stopPropagation(); // LINK CARD
evt.preventDefault(); evt.stopPropagation();
const linkedId = $('.js-select-cards option:selected').val(); evt.preventDefault();
if (!linkedId) { const linkedId = $('.js-select-cards option:selected').val();
if (!linkedId) {
Popup.close();
return;
}
const _id = Cards.insert({
title: $('.js-select-cards option:selected').text(), //dummy
listId: this.listId,
swimlaneId: this.swimlaneId,
boardId: this.boardId,
sort: Lists.findOne(this.listId)
.cards()
.count(),
type: 'cardType-linkedCard',
linkedId,
});
Filter.addException(_id);
Popup.close(); Popup.close();
return; },
} 'click .js-link-board'(evt) {
const _id = Cards.insert({ //LINK BOARD
title: $('.js-select-cards option:selected').text(), //dummy evt.stopPropagation();
listId: this.listId, evt.preventDefault();
swimlaneId: this.swimlaneId, const impBoardId = $('.js-select-boards option:selected').val();
boardId: this.boardId, if (
sort: Lists.findOne(this.listId).cards().count(), !impBoardId ||
type: 'cardType-linkedCard', Cards.findOne({ linkedId: impBoardId, archived: false })
linkedId, ) {
}); Popup.close();
Filter.addException(_id); return;
Popup.close(); }
}, const _id = Cards.insert({
'click .js-link-board' (evt) { title: $('.js-select-boards option:selected').text(), //dummy
//LINK BOARD listId: this.listId,
evt.stopPropagation(); swimlaneId: this.swimlaneId,
evt.preventDefault(); boardId: this.boardId,
const impBoardId = $('.js-select-boards option:selected').val(); sort: Lists.findOne(this.listId)
if (!impBoardId || Cards.findOne({linkedId: impBoardId, archived: false})) { .cards()
.count(),
type: 'cardType-linkedBoard',
linkedId: impBoardId,
});
Filter.addException(_id);
Popup.close(); Popup.close();
return; },
}
const _id = Cards.insert({
title: $('.js-select-boards option:selected').text(), //dummy
listId: this.listId,
swimlaneId: this.swimlaneId,
boardId: this.boardId,
sort: Lists.findOne(this.listId).cards().count(),
type: 'cardType-linkedBoard',
linkedId: impBoardId,
});
Filter.addException(_id);
Popup.close();
}, },
}]; ];
}, },
}).register('linkCardPopup'); }).register('linkCardPopup');
@ -475,11 +529,20 @@ BlazeComponent.extendComponent({
}, },
onCreated() { onCreated() {
this.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-card-template'); this.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-list-template'); 'js-card-template',
this.isSwimlaneTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-open-add-swimlane-menu'); );
this.isBoardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-add-board'); this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
this.isTemplateSearch = this.isCardTemplateSearch || 'js-list-template',
);
this.isSwimlaneTemplateSearch = $(
Popup._getTopStack().openerElement,
).hasClass('js-open-add-swimlane-menu');
this.isBoardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
'js-add-board',
);
this.isTemplateSearch =
this.isCardTemplateSearch ||
this.isListTemplateSearch || this.isListTemplateSearch ||
this.isSwimlaneTemplateSearch || this.isSwimlaneTemplateSearch ||
this.isBoardTemplateSearch; this.isBoardTemplateSearch;
@ -491,7 +554,12 @@ BlazeComponent.extendComponent({
board = Boards.findOne({ board = Boards.findOne({
archived: false, archived: false,
'members.userId': Meteor.userId(), 'members.userId': Meteor.userId(),
_id: {$nin: [Session.get('currentBoard'), (Meteor.user().profile || {}).templatesBoardId]}, _id: {
$nin: [
Session.get('currentBoard'),
(Meteor.user().profile || {}).templatesBoardId,
],
},
}); });
} }
if (!board) { if (!board) {
@ -509,11 +577,12 @@ BlazeComponent.extendComponent({
subManager.subscribe('board', this.boardId, false); subManager.subscribe('board', this.boardId, false);
this.swimlaneId = ''; this.swimlaneId = '';
// Swimlane where to insert card // Swimlane where to insert card
const swimlane = $(Popup._getTopStack().openerElement).parents('.js-swimlane'); const swimlane = $(Popup._getTopStack().openerElement).parents(
'.js-swimlane',
);
if ((Meteor.user().profile || {}).boardView === 'board-view-swimlanes') if ((Meteor.user().profile || {}).boardView === 'board-view-swimlanes')
this.swimlaneId = Blaze.getData(swimlane[0])._id; this.swimlaneId = Blaze.getData(swimlane[0])._id;
else else this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
// List where to insert card // List where to insert card
const list = $(Popup._getTopStack().openerElement).closest('.js-list'); const list = $(Popup._getTopStack().openerElement).closest('.js-list');
this.listId = Blaze.getData(list[0])._id; this.listId = Blaze.getData(list[0])._id;
@ -522,14 +591,17 @@ BlazeComponent.extendComponent({
}, },
boards() { boards() {
const boards = Boards.find({ const boards = Boards.find(
archived: false, {
'members.userId': Meteor.userId(), archived: false,
_id: {$ne: Session.get('currentBoard')}, 'members.userId': Meteor.userId(),
type: 'board', _id: { $ne: Session.get('currentBoard') },
}, { type: 'board',
sort: ['title'], },
}); {
sort: ['title'],
},
);
return boards; return boards;
}, },
@ -546,7 +618,7 @@ BlazeComponent.extendComponent({
return board.searchSwimlanes(this.term.get()); return board.searchSwimlanes(this.term.get());
} else if (this.isBoardTemplateSearch) { } else if (this.isBoardTemplateSearch) {
const boards = board.searchBoards(this.term.get()); const boards = board.searchBoards(this.term.get());
boards.forEach((board) => { boards.forEach(board => {
subManager.subscribe('board', board.linkedId, false); subManager.subscribe('board', board.linkedId, false);
}); });
return boards; return boards;
@ -556,60 +628,69 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'change .js-select-boards'(evt) { {
subManager.subscribe('board', $(evt.currentTarget).val(), false); 'change .js-select-boards'(evt) {
this.selectedBoardId.set($(evt.currentTarget).val()); subManager.subscribe('board', $(evt.currentTarget).val(), false);
}, this.selectedBoardId.set($(evt.currentTarget).val());
'submit .js-search-term-form'(evt) { },
evt.preventDefault(); 'submit .js-search-term-form'(evt) {
this.term.set(evt.target.searchTerm.value); evt.preventDefault();
}, this.term.set(evt.target.searchTerm.value);
'click .js-minicard'(evt) { },
// 0. Common 'click .js-minicard'(evt) {
const title = $('.js-element-title').val().trim(); // 0. Common
if (!title) const title = $('.js-element-title')
return; .val()
const element = Blaze.getData(evt.currentTarget); .trim();
element.title = title; if (!title) return;
let _id = ''; const element = Blaze.getData(evt.currentTarget);
if (!this.isTemplateSearch || this.isCardTemplateSearch) { element.title = title;
// Card insertion let _id = '';
// 1. Common if (!this.isTemplateSearch || this.isCardTemplateSearch) {
element.sort = Lists.findOne(this.listId).cards().count(); // Card insertion
// 1.A From template // 1. Common
if (this.isTemplateSearch) { element.sort = Lists.findOne(this.listId)
element.type = 'cardType-card'; .cards()
element.linkedId = ''; .count();
_id = element.copy(this.boardId, this.swimlaneId, this.listId); // 1.A From template
// 1.B Linked card if (this.isTemplateSearch) {
} else { element.type = 'cardType-card';
delete element._id; element.linkedId = '';
element.type = 'cardType-linkedCard'; _id = element.copy(this.boardId, this.swimlaneId, this.listId);
element.linkedId = element.linkedId || element._id; // 1.B Linked card
_id = Cards.insert(element); } else {
delete element._id;
element.type = 'cardType-linkedCard';
element.linkedId = element.linkedId || element._id;
_id = Cards.insert(element);
}
Filter.addException(_id);
// List insertion
} else if (this.isListTemplateSearch) {
element.sort = Swimlanes.findOne(this.swimlaneId)
.lists()
.count();
element.type = 'list';
_id = element.copy(this.boardId, this.swimlaneId);
} else if (this.isSwimlaneTemplateSearch) {
element.sort = Boards.findOne(this.boardId)
.swimlanes()
.count();
element.type = 'swimlalne';
_id = element.copy(this.boardId);
} else if (this.isBoardTemplateSearch) {
board = Boards.findOne(element.linkedId);
board.sort = Boards.find({ archived: false }).count();
board.type = 'board';
board.title = element.title;
delete board.slug;
_id = board.copy();
} }
Filter.addException(_id); Popup.close();
// List insertion },
} else if (this.isListTemplateSearch) {
element.sort = Swimlanes.findOne(this.swimlaneId).lists().count();
element.type = 'list';
_id = element.copy(this.boardId, this.swimlaneId);
} else if (this.isSwimlaneTemplateSearch) {
element.sort = Boards.findOne(this.boardId).swimlanes().count();
element.type = 'swimlalne';
_id = element.copy(this.boardId);
} else if (this.isBoardTemplateSearch) {
board = Boards.findOne(element.linkedId);
board.sort = Boards.find({archived: false}).count();
board.type = 'board';
board.title = element.title;
delete board.slug;
_id = board.copy();
}
Popup.close();
}, },
}]; ];
}, },
}).register('searchElementPopup'); }).register('searchElementPopup');
@ -622,15 +703,23 @@ BlazeComponent.extendComponent({
const boardView = (Meteor.user().profile || {}).boardView; const boardView = (Meteor.user().profile || {}).boardView;
if (boardView === 'board-view-swimlanes') if (boardView === 'board-view-swimlanes')
this.swimlaneId = this.parentComponent().parentComponent().parentComponent().data()._id; this.swimlaneId = this.parentComponent()
.parentComponent()
.parentComponent()
.data()._id;
}, },
onRendered() { onRendered() {
this.spinner = this.find('.sk-spinner-list'); this.spinner = this.find('.sk-spinner-list');
this.container = this.$(this.spinner).parents('.js-perfect-scrollbar')[0]; this.container = this.$(this.spinner).parents('.js-perfect-scrollbar')[0];
$(this.container).on(`scroll.spinner_${this.swimlaneId}_${this.listId}`, () => this.updateList()); $(this.container).on(
$(window).on(`resize.spinner_${this.swimlaneId}_${this.listId}`, () => this.updateList()); `scroll.spinner_${this.swimlaneId}_${this.listId}`,
() => this.updateList(),
);
$(window).on(`resize.spinner_${this.swimlaneId}_${this.listId}`, () =>
this.updateList(),
);
this.updateList(); this.updateList();
}, },
@ -660,5 +749,4 @@ BlazeComponent.extendComponent({
return bottomViewPosition > threshold; return bottomViewPosition > threshold;
}, },
}).register('spinnerList'); }).register('spinnerList');

View file

@ -6,12 +6,18 @@ Meteor.startup(() => {
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
canSeeAddCard() { canSeeAddCard() {
const list = Template.currentData(); const list = Template.currentData();
return !list.getWipLimit('enabled') || list.getWipLimit('soft') || !this.reachedWipLimit(); return (
!list.getWipLimit('enabled') ||
list.getWipLimit('soft') ||
!this.reachedWipLimit()
);
}, },
editTitle(evt) { editTitle(event) {
evt.preventDefault(); event.preventDefault();
const newTitle = this.childComponents('inlinedForm')[0].getValue().trim(); const newTitle = this.childComponents('inlinedForm')[0]
.getValue()
.trim();
const list = this.currentData(); const list = this.currentData();
if (newTitle) { if (newTitle) {
list.rename(newTitle.trim()); list.rename(newTitle.trim());
@ -32,14 +38,19 @@ BlazeComponent.extendComponent({
let swimlaneId = ''; let swimlaneId = '';
const boardView = (Meteor.user().profile || {}).boardView; const boardView = (Meteor.user().profile || {}).boardView;
if (boardView === 'board-view-swimlanes') if (boardView === 'board-view-swimlanes')
swimlaneId = this.parentComponent().parentComponent().data()._id; swimlaneId = this.parentComponent()
.parentComponent()
.data()._id;
return list.cards(swimlaneId).count(); return list.cards(swimlaneId).count();
}, },
reachedWipLimit() { reachedWipLimit() {
const list = Template.currentData(); const list = Template.currentData();
return list.getWipLimit('enabled') && list.getWipLimit('value') <= list.cards().count(); return (
list.getWipLimit('enabled') &&
list.getWipLimit('value') <= list.cards().count()
);
}, },
showCardsCountForList(count) { showCardsCountForList(count) {
@ -48,20 +59,24 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-open-list-menu': Popup.open('listAction'), {
'click .js-add-card' (evt) { 'click .js-open-list-menu': Popup.open('listAction'),
const listDom = $(evt.target).parents(`#js-list-${this.currentData()._id}`)[0]; 'click .js-add-card'(event) {
const listComponent = BlazeComponent.getComponentForElement(listDom); const listDom = $(event.target).parents(
listComponent.openForm({ `#js-list-${this.currentData()._id}`,
position: 'top', )[0];
}); const listComponent = BlazeComponent.getComponentForElement(listDom);
listComponent.openForm({
position: 'top',
});
},
'click .js-unselect-list'() {
Session.set('currentList', null);
},
submit: this.editTitle,
}, },
'click .js-unselect-list'() { ];
Session.set('currentList', null);
},
submit: this.editTitle,
}];
}, },
}).register('listHeader'); }).register('listHeader');
@ -76,22 +91,22 @@ Template.listActionPopup.helpers({
}); });
Template.listActionPopup.events({ Template.listActionPopup.events({
'click .js-list-subscribe' () {}, 'click .js-list-subscribe'() {},
'click .js-set-color-list': Popup.open('setListColor'), 'click .js-set-color-list': Popup.open('setListColor'),
'click .js-select-cards' () { 'click .js-select-cards'() {
const cardIds = this.allCards().map((card) => card._id); const cardIds = this.allCards().map(card => card._id);
MultiSelection.add(cardIds); MultiSelection.add(cardIds);
Popup.close(); Popup.close();
}, },
'click .js-toggle-watch-list' () { 'click .js-toggle-watch-list'() {
const currentList = this; const currentList = this;
const level = currentList.findWatcher(Meteor.userId()) ? null : 'watching'; const level = currentList.findWatcher(Meteor.userId()) ? null : 'watching';
Meteor.call('watch', 'list', currentList._id, level, (err, ret) => { Meteor.call('watch', 'list', currentList._id, level, (err, ret) => {
if (!err && ret) Popup.close(); if (!err && ret) Popup.close();
}); });
}, },
'click .js-close-list' (evt) { 'click .js-close-list'(event) {
evt.preventDefault(); event.preventDefault();
this.archive(); this.archive();
Popup.close(); Popup.close();
}, },
@ -102,10 +117,17 @@ Template.listActionPopup.events({
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
applyWipLimit() { applyWipLimit() {
const list = Template.currentData(); const list = Template.currentData();
const limit = parseInt(Template.instance().$('.wip-limit-value').val(), 10); const limit = parseInt(
Template.instance()
.$('.wip-limit-value')
.val(),
10,
);
if(limit < list.cards().count() && !list.getWipLimit('soft')){ if (limit < list.cards().count() && !list.getWipLimit('soft')) {
Template.instance().$('.wip-limit-error').click(); Template.instance()
.$('.wip-limit-error')
.click();
} else { } else {
Meteor.call('applyWipLimit', list._id, limit); Meteor.call('applyWipLimit', list._id, limit);
Popup.back(); Popup.back();
@ -115,7 +137,10 @@ BlazeComponent.extendComponent({
enableSoftLimit() { enableSoftLimit() {
const list = Template.currentData(); const list = Template.currentData();
if(list.getWipLimit('soft') && list.getWipLimit('value') < list.cards().count()){ if (
list.getWipLimit('soft') &&
list.getWipLimit('value') < list.cards().count()
) {
list.setWipLimit(list.cards().count()); list.setWipLimit(list.cards().count());
} }
Meteor.call('enableSoftLimit', Template.currentData()._id); Meteor.call('enableSoftLimit', Template.currentData()._id);
@ -124,7 +149,10 @@ BlazeComponent.extendComponent({
enableWipLimit() { enableWipLimit() {
const list = Template.currentData(); const list = Template.currentData();
// Prevent user from using previously stored wipLimit.value if it is less than the current number of cards in the list // Prevent user from using previously stored wipLimit.value if it is less than the current number of cards in the list
if(!list.getWipLimit('enabled') && list.getWipLimit('value') < list.cards().count()){ if (
!list.getWipLimit('enabled') &&
list.getWipLimit('value') < list.cards().count()
) {
list.setWipLimit(list.cards().count()); list.setWipLimit(list.cards().count());
} }
Meteor.call('enableWipLimit', list._id); Meteor.call('enableWipLimit', list._id);
@ -138,24 +166,26 @@ BlazeComponent.extendComponent({
return Template.currentData().getWipLimit('enabled'); return Template.currentData().getWipLimit('enabled');
}, },
wipLimitValue(){ wipLimitValue() {
return Template.currentData().getWipLimit('value'); return Template.currentData().getWipLimit('value');
}, },
events() { events() {
return [{ return [
'click .js-enable-wip-limit': this.enableWipLimit, {
'click .wip-limit-apply': this.applyWipLimit, 'click .js-enable-wip-limit': this.enableWipLimit,
'click .wip-limit-error': Popup.open('wipLimitError'), 'click .wip-limit-apply': this.applyWipLimit,
'click .materialCheckBox': this.enableSoftLimit, 'click .wip-limit-error': Popup.open('wipLimitError'),
}]; 'click .materialCheckBox': this.enableSoftLimit,
},
];
}, },
}).register('setWipLimitPopup'); }).register('setWipLimitPopup');
Template.listMorePopup.events({ Template.listMorePopup.events({
'click .js-delete': Popup.afterConfirm('listDelete', function () { 'click .js-delete': Popup.afterConfirm('listDelete', function() {
Popup.close(); Popup.close();
this.allCards().map((card) => Cards.remove(card._id)); this.allCards().map(card => Cards.remove(card._id));
Lists.remove(this._id); Lists.remove(this._id);
Utils.goBoardId(this.boardId); Utils.goBoardId(this.boardId);
}), }),
@ -168,7 +198,7 @@ BlazeComponent.extendComponent({
}, },
colors() { colors() {
return listsColors.map((color) => ({ color, name: '' })); return listsColors.map(color => ({ color, name: '' }));
}, },
isSelected(color) { isSelected(color) {
@ -176,18 +206,20 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-palette-color'() { {
this.currentColor.set(this.currentData().color); 'click .js-palette-color'() {
this.currentColor.set(this.currentData().color);
},
'click .js-submit'() {
this.currentList.setColor(this.currentColor.get());
Popup.close();
},
'click .js-remove-color'() {
this.currentList.setColor(null);
Popup.close();
},
}, },
'click .js-submit' () { ];
this.currentList.setColor(this.currentColor.get());
Popup.close();
},
'click .js-remove-color'() {
this.currentList.setColor(null);
Popup.close();
},
}];
}, },
}).register('setListColorPopup'); }).register('setListColorPopup');

View file

@ -9,10 +9,15 @@ Template.editor.onRendered(() => {
match: /\B@([\w.]*)$/, match: /\B@([\w.]*)$/,
search(term, callback) { search(term, callback) {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
callback(currentBoard.activeMembers().map((member) => { callback(
const username = Users.findOne(member.userId).username; currentBoard
return username.includes(term) ? username : null; .activeMembers()
}).filter(Boolean)); .map(member => {
const username = Users.findOne(member.userId).username;
return username.includes(term) ? username : null;
})
.filter(Boolean),
);
}, },
template(value) { template(value) {
return value; return value;
@ -33,69 +38,73 @@ import sanitizeXss from 'xss';
// compiled version to most users -- who don't need to edit. // compiled version to most users -- who don't need to edit.
// In the meantime, all the transformation are done on the client using the // In the meantime, all the transformation are done on the client using the
// Blaze API. // Blaze API.
const at = HTML.CharRef({html: '&commat;', str: '@'}); const at = HTML.CharRef({ html: '&commat;', str: '@' });
Blaze.Template.registerHelper('mentions', new Template('mentions', function() { Blaze.Template.registerHelper(
const view = this; 'mentions',
let content = Blaze.toHTML(view.templateContentBlock); new Template('mentions', function() {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const view = this;
if (!currentBoard) let content = Blaze.toHTML(view.templateContentBlock);
const currentBoard = Boards.findOne(Session.get('currentBoard'));
if (!currentBoard) return HTML.Raw(sanitizeXss(content));
const knowedUsers = currentBoard.members.map(member => {
const u = Users.findOne(member.userId);
if (u) {
member.username = u.username;
}
return member;
});
const mentionRegex = /\B@([\w.]*)/gi;
let currentMention;
while ((currentMention = mentionRegex.exec(content)) !== null) {
const [fullMention, username] = currentMention;
const knowedUser = _.findWhere(knowedUsers, { username });
if (!knowedUser) {
continue;
}
const linkValue = [' ', at, knowedUser.username];
let linkClass = 'atMention js-open-member';
if (knowedUser.userId === Meteor.userId()) {
linkClass += ' me';
}
const link = HTML.A(
{
class: linkClass,
// XXX Hack. Since we stringify this render function result below with
// `Blaze.toHTML` we can't rely on blaze data contexts to pass the
// `userId` to the popup as usual, and we need to store it in the DOM
// using a data attribute.
'data-userId': knowedUser.userId,
},
linkValue,
);
content = content.replace(fullMention, Blaze.toHTML(link));
}
return HTML.Raw(sanitizeXss(content)); return HTML.Raw(sanitizeXss(content));
const knowedUsers = currentBoard.members.map((member) => { }),
const u = Users.findOne(member.userId); );
if(u){
member.username = u.username;
}
return member;
});
const mentionRegex = /\B@([\w.]*)/gi;
let currentMention;
while ((currentMention = mentionRegex.exec(content)) !== null) {
const [fullMention, username] = currentMention;
const knowedUser = _.findWhere(knowedUsers, { username });
if (!knowedUser) {
continue;
}
const linkValue = [' ', at, knowedUser.username];
let linkClass = 'atMention js-open-member';
if (knowedUser.userId === Meteor.userId()) {
linkClass += ' me';
}
const link = HTML.A({
'class': linkClass,
// XXX Hack. Since we stringify this render function result below with
// `Blaze.toHTML` we can't rely on blaze data contexts to pass the
// `userId` to the popup as usual, and we need to store it in the DOM
// using a data attribute.
'data-userId': knowedUser.userId,
}, linkValue);
content = content.replace(fullMention, Blaze.toHTML(link));
}
return HTML.Raw(sanitizeXss(content));
}));
Template.viewer.events({ Template.viewer.events({
// Viewer sometimes have click-able wrapper around them (for instance to edit // Viewer sometimes have click-able wrapper around them (for instance to edit
// the corresponding text). Clicking a link shouldn't fire these actions, stop // the corresponding text). Clicking a link shouldn't fire these actions, stop
// we stop these event at the viewer component level. // we stop these event at the viewer component level.
'click a'(evt, tpl) { 'click a'(event, templateInstance) {
evt.stopPropagation(); event.stopPropagation();
// XXX We hijack the build-in browser action because we currently don't have // XXX We hijack the build-in browser action because we currently don't have
// `_blank` attributes in viewer links, and the transformer function is // `_blank` attributes in viewer links, and the transformer function is
// handled by a third party package that we can't configure easily. Fix that // handled by a third party package that we can't configure easily. Fix that
// by using directly `_blank` attribute in the rendered HTML. // by using directly `_blank` attribute in the rendered HTML.
evt.preventDefault(); event.preventDefault();
const userId = evt.currentTarget.dataset.userid; const userId = event.currentTarget.dataset.userid;
if (userId) { if (userId) {
Popup.open('member').call({ userId }, evt, tpl); Popup.open('member').call({ userId }, event, templateInstance);
} } else {
else { const href = event.currentTarget.href;
const href = evt.currentTarget.href;
if (href) { if (href) {
window.open(href, '_blank'); window.open(href, '_blank');
} }

View file

@ -20,13 +20,13 @@ Template.header.helpers({
}, },
hasAnnouncement() { hasAnnouncement() {
const announcements = Announcements.findOne(); const announcements = Announcements.findOne();
return announcements && announcements.enabled; return announcements && announcements.enabled;
}, },
announcement() { announcement() {
$('.announcement').show(); $('.announcement').show();
const announcements = Announcements.findOne(); const announcements = Announcements.findOne();
return announcements && announcements.body; return announcements && announcements.body;
}, },
}); });

View file

@ -1,6 +1,6 @@
BlazeLayout.setRoot('body'); BlazeLayout.setRoot('body');
const i18nTagToT9n = (i18nTag) => { const i18nTagToT9n = i18nTag => {
// t9n/i18n tags are same now, see: https://github.com/softwarerero/meteor-accounts-t9n/pull/129 // t9n/i18n tags are same now, see: https://github.com/softwarerero/meteor-accounts-t9n/pull/129
// but we keep this conversion function here, to be aware that that they are different system. // but we keep this conversion function here, to be aware that that they are different system.
return i18nTag; return i18nTag;
@ -21,20 +21,23 @@ const validator = {
}; };
Template.userFormsLayout.onCreated(function() { Template.userFormsLayout.onCreated(function() {
const instance = this; const templateInstance = this;
instance.currentSetting = new ReactiveVar(); templateInstance.currentSetting = new ReactiveVar();
instance.isLoading = new ReactiveVar(false); templateInstance.isLoading = new ReactiveVar(false);
Meteor.subscribe('setting', { Meteor.subscribe('setting', {
onReady() { onReady() {
instance.currentSetting.set(Settings.findOne()); templateInstance.currentSetting.set(Settings.findOne());
return this.stop(); return this.stop();
}, },
}); });
}); });
Template.userFormsLayout.onRendered(() => { Template.userFormsLayout.onRendered(() => {
AccountsTemplates.state.form.keys = new Proxy(AccountsTemplates.state.form.keys, validator); AccountsTemplates.state.form.keys = new Proxy(
AccountsTemplates.state.form.keys,
validator,
);
const i18nTag = navigator.language; const i18nTag = navigator.language;
if (i18nTag) { if (i18nTag) {
@ -89,18 +92,17 @@ Template.userFormsLayout.helpers({
}); });
Template.userFormsLayout.events({ Template.userFormsLayout.events({
'change .js-userform-set-language'(evt) { 'change .js-userform-set-language'(event) {
const i18nTag = $(evt.currentTarget).val(); const i18nTag = $(event.currentTarget).val();
T9n.setLanguage(i18nTagToT9n(i18nTag)); T9n.setLanguage(i18nTagToT9n(i18nTag));
evt.preventDefault(); event.preventDefault();
}, },
'click #at-btn'(event, instance) { 'click #at-btn'(event, templateInstance) {
if (FlowRouter.getRouteName() === 'atSignIn') { if (FlowRouter.getRouteName() === 'atSignIn') {
instance.isLoading.set(true); templateInstance.isLoading.set(true);
authentication(event, instance) authentication(event, templateInstance).then(() => {
.then(() => { templateInstance.isLoading.set(false);
instance.isLoading.set(false); });
});
} }
}, },
}); });
@ -111,13 +113,16 @@ Template.defaultLayout.events({
}, },
}); });
async function authentication(event, instance) { async function authentication(event, templateInstance) {
const match = $('#at-field-username_and_email').val(); const match = $('#at-field-username_and_email').val();
const password = $('#at-field-password').val(); const password = $('#at-field-password').val();
if (!match || !password) return undefined; if (!match || !password) return undefined;
const result = await getAuthenticationMethod(instance.currentSetting.get(), match); const result = await getAuthenticationMethod(
templateInstance.currentSetting.get(),
match,
);
if (result === 'password') return undefined; if (result === 'password') return undefined;
@ -126,26 +131,29 @@ async function authentication(event, instance) {
event.stopImmediatePropagation(); event.stopImmediatePropagation();
switch (result) { switch (result) {
case 'ldap': case 'ldap':
return new Promise((resolve) => { return new Promise(resolve => {
Meteor.loginWithLDAP(match, password, function() { Meteor.loginWithLDAP(match, password, function() {
resolve(FlowRouter.go('/')); resolve(FlowRouter.go('/'));
});
}); });
});
case 'cas': case 'cas':
return new Promise((resolve) => { return new Promise(resolve => {
Meteor.loginWithCas(match, password, function() { Meteor.loginWithCas(match, password, function() {
resolve(FlowRouter.go('/')); resolve(FlowRouter.go('/'));
});
}); });
});
default: default:
return undefined; return undefined;
} }
} }
function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthenticationMethod}, match) { function getAuthenticationMethod(
{ displayAuthenticationMethod, defaultAuthenticationMethod },
match,
) {
if (displayAuthenticationMethod) { if (displayAuthenticationMethod) {
return $('.select-authentication').val(); return $('.select-authentication').val();
} }
@ -153,7 +161,7 @@ function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthentica
} }
function getUserAuthenticationMethod(defaultAuthenticationMethod, match) { function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
return new Promise((resolve) => { return new Promise(resolve => {
try { try {
Meteor.subscribe('user-authenticationMethod', match, { Meteor.subscribe('user-authenticationMethod', match, {
onReady() { onReady() {
@ -166,7 +174,7 @@ function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
resolve(authenticationMethod); resolve(authenticationMethod);
}, },
}); });
} catch(error) { } catch (error) {
resolve(defaultAuthenticationMethod); resolve(defaultAuthenticationMethod);
} }
}); });

View file

@ -18,15 +18,17 @@ Mixins.InfiniteScrolling = BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
scroll(evt) { {
const domElement = evt.currentTarget; scroll(evt) {
let altitude = domElement.scrollTop + domElement.offsetHeight; const domElement = evt.currentTarget;
altitude += peakAnticipation; let altitude = domElement.scrollTop + domElement.offsetHeight;
if (altitude >= this.callFirstWith(null, 'getNextPeak')) { altitude += peakAnticipation;
this.mixinParent().callFirstWith(null, 'reachNextPeak'); if (altitude >= this.callFirstWith(null, 'getNextPeak')) {
} this.mixinParent().callFirstWith(null, 'reachNextPeak');
}
},
}, },
}]; ];
}, },
}); });

View file

@ -1,168 +1,165 @@
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
onCreated() { onCreated() {},
},
events() { events() {
return [{ return [
'click .js-create-card-action' (event) { {
const ruleName = this.data().ruleName.get(); 'click .js-create-card-action'(event) {
const trigger = this.data().triggerVar.get(); const ruleName = this.data().ruleName.get();
const cardName = this.find('#card-name').value; const trigger = this.data().triggerVar.get();
const listName = this.find('#list-name').value; const cardName = this.find('#card-name').value;
const swimlaneName = this.find('#swimlane-name2').value; const listName = this.find('#list-name').value;
const boardId = Session.get('currentBoard'); const swimlaneName = this.find('#swimlane-name2').value;
const desc = Utils.getTriggerActionDesc(event, this); const boardId = Session.get('currentBoard');
const triggerId = Triggers.insert(trigger); const desc = Utils.getTriggerActionDesc(event, this);
const actionId = Actions.insert({ const triggerId = Triggers.insert(trigger);
actionType: 'createCard', const actionId = Actions.insert({
swimlaneName, actionType: 'createCard',
cardName, swimlaneName,
listName, cardName,
boardId, listName,
desc, boardId,
}); desc,
Rules.insert({ });
title: ruleName, Rules.insert({
triggerId, title: ruleName,
actionId, triggerId,
boardId, actionId,
}); boardId,
});
},
'click .js-add-swimlane-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const swimlaneName = this.find('#swimlane-name').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'addSwimlane',
swimlaneName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
},
'click .js-add-spec-move-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#move-spec-action').value;
const listTitle = this.find('#listName').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'top') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToTop',
listTitle,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'bottom') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToBottom',
listTitle,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-gen-move-action'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const boardId = Session.get('currentBoard');
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#move-gen-action').value;
if (actionSelected === 'top') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToTop',
listTitle: '*',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'bottom') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToBottom',
listTitle: '*',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-arch-action'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const boardId = Session.get('currentBoard');
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#arch-action').value;
if (actionSelected === 'archive') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'archive',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'unarchive') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'unarchive',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
}, },
'click .js-add-swimlane-action' (event) { ];
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const swimlaneName = this.find('#swimlane-name').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'addSwimlane',
swimlaneName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
},
'click .js-add-spec-move-action' (event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#move-spec-action').value;
const listTitle = this.find('#listName').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'top') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToTop',
listTitle,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'bottom') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToBottom',
listTitle,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-gen-move-action' (event) {
const desc = Utils.getTriggerActionDesc(event, this);
const boardId = Session.get('currentBoard');
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#move-gen-action').value;
if (actionSelected === 'top') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToTop',
'listTitle': '*',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'bottom') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'moveCardToBottom',
'listTitle': '*',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-arch-action' (event) {
const desc = Utils.getTriggerActionDesc(event, this);
const boardId = Session.get('currentBoard');
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#arch-action').value;
if (actionSelected === 'archive') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'archive',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'unarchive') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'unarchive',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
}];
}, },
}).register('boardActions'); }).register('boardActions');
/* eslint-no-undef */ /* eslint-no-undef */

View file

@ -14,7 +14,7 @@ BlazeComponent.extendComponent({
}, },
cardColorButtonText() { cardColorButtonText() {
return `color-${ this.cardColorButtonValue.get() }`; return `color-${this.cardColorButtonValue.get()}`;
}, },
labels() { labels() {
@ -28,109 +28,24 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-set-date-action' (event) { {
const ruleName = this.data().ruleName.get(); 'click .js-set-date-action'(event) {
const trigger = this.data().triggerVar.get(); const ruleName = this.data().ruleName.get();
const triggerId = Triggers.insert(trigger); const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#setdate-action').value;
const dateFieldSelected = this.find('#setdate-datefield').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
const actionId = Actions.insert({
actionType: actionSelected,
dateField: dateFieldSelected,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
desc,
});
},
'click .js-remove-datevalue-action' (event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const triggerId = Triggers.insert(trigger);
const dateFieldSelected = this.find('#setdate-removedatefieldvalue').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
const actionId = Actions.insert({
actionType: 'removeDate',
dateField: dateFieldSelected,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
desc,
});
},
'click .js-add-label-action' (event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#label-action').value;
const labelId = this.find('#label-id').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'add') {
const triggerId = Triggers.insert(trigger); const triggerId = Triggers.insert(trigger);
const actionSelected = this.find('#setdate-action').value;
const dateFieldSelected = this.find('#setdate-datefield').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
const actionId = Actions.insert({ const actionId = Actions.insert({
actionType: 'addLabel', actionType: actionSelected,
labelId, dateField: dateFieldSelected,
boardId, boardId,
desc, desc,
}); });
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'remove') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'removeLabel',
labelId,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-member-action' (event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#member-action').value;
const username = this.find('#member-name').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'add') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'addMember',
username,
boardId,
desc,
});
Rules.insert({ Rules.insert({
title: ruleName, title: ruleName,
triggerId, triggerId,
@ -138,12 +53,118 @@ BlazeComponent.extendComponent({
boardId, boardId,
desc, desc,
}); });
} },
if (actionSelected === 'remove') {
'click .js-remove-datevalue-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const triggerId = Triggers.insert(trigger); const triggerId = Triggers.insert(trigger);
const dateFieldSelected = this.find('#setdate-removedatefieldvalue')
.value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
const actionId = Actions.insert({
actionType: 'removeDate',
dateField: dateFieldSelected,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
desc,
});
},
'click .js-add-label-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#label-action').value;
const labelId = this.find('#label-id').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'add') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'addLabel',
labelId,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'remove') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'removeLabel',
labelId,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-member-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#member-action').value;
const username = this.find('#member-name').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'add') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'addMember',
username,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
desc,
});
}
if (actionSelected === 'remove') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'removeMember',
username,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-removeall-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const triggerId = Triggers.insert(trigger);
const desc = Utils.getTriggerActionDesc(event, this);
const boardId = Session.get('currentBoard');
const actionId = Actions.insert({ const actionId = Actions.insert({
actionType: 'removeMember', actionType: 'removeMember',
username, username: '*',
boardId, boardId,
desc, desc,
}); });
@ -153,58 +174,38 @@ BlazeComponent.extendComponent({
actionId, actionId,
boardId, boardId,
}); });
} },
'click .js-show-color-palette'(event) {
const funct = Popup.open('setCardActionsColor');
const colorButton = this.find('#color-action');
if (colorButton.value === '') {
colorButton.value = 'green';
}
funct.call(this, event);
},
'click .js-set-color-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const selectedColor = this.cardColorButtonValue.get();
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'setColor',
selectedColor,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
},
}, },
'click .js-add-removeall-action' (event) { ];
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const triggerId = Triggers.insert(trigger);
const desc = Utils.getTriggerActionDesc(event, this);
const boardId = Session.get('currentBoard');
const actionId = Actions.insert({
actionType: 'removeMember',
'username': '*',
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
},
'click .js-show-color-palette'(event){
const funct = Popup.open('setCardActionsColor');
const colorButton = this.find('#color-action');
if (colorButton.value === '') {
colorButton.value = 'green';
}
funct.call(this, event);
},
'click .js-set-color-action' (event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const selectedColor = this.cardColorButtonValue.get();
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'setColor',
selectedColor,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
},
}];
}, },
}).register('cardActions'); }).register('cardActions');
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
@ -215,7 +216,7 @@ BlazeComponent.extendComponent({
}, },
colors() { colors() {
return cardColors.map((color) => ({ color, name: '' })); return cardColors.map(color => ({ color, name: '' }));
}, },
isSelected(color) { isSelected(color) {
@ -223,14 +224,16 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-palette-color'() { {
this.currentColor.set(this.currentData().color); 'click .js-palette-color'() {
this.currentColor.set(this.currentData().color);
},
'click .js-submit'() {
this.colorButtonValue.set(this.currentColor.get());
Popup.close();
},
}, },
'click .js-submit' () { ];
this.colorButtonValue.set(this.currentColor.get());
Popup.close();
},
}];
}, },
}).register('setCardActionsColorPopup'); }).register('setCardActionsColorPopup');

View file

@ -3,149 +3,148 @@ BlazeComponent.extendComponent({
this.subscribe('allRules'); this.subscribe('allRules');
}, },
events() { events() {
return [{ return [
'click .js-add-checklist-items-action' (event) { {
const ruleName = this.data().ruleName.get(); 'click .js-add-checklist-items-action'(event) {
const trigger = this.data().triggerVar.get(); const ruleName = this.data().ruleName.get();
const checklistName = this.find('#checklist-name-3').value; const trigger = this.data().triggerVar.get();
const checklistItems = this.find('#checklist-items').value; const checklistName = this.find('#checklist-name-3').value;
const boardId = Session.get('currentBoard'); const checklistItems = this.find('#checklist-items').value;
const desc = Utils.getTriggerActionDesc(event, this); const boardId = Session.get('currentBoard');
const triggerId = Triggers.insert(trigger); const desc = Utils.getTriggerActionDesc(event, this);
const actionId = Actions.insert({ const triggerId = Triggers.insert(trigger);
actionType: 'addChecklistWithItems', const actionId = Actions.insert({
checklistName, actionType: 'addChecklistWithItems',
checklistItems, checklistName,
boardId, checklistItems,
desc, boardId,
}); desc,
Rules.insert({ });
title: ruleName, Rules.insert({
triggerId, title: ruleName,
actionId, triggerId,
boardId, actionId,
}); boardId,
});
},
'click .js-add-checklist-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#check-action').value;
const checklistName = this.find('#checklist-name').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'add') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'addChecklist',
checklistName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'remove') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'removeChecklist',
checklistName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-checkall-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#checkall-action').value;
const checklistName = this.find('#checklist-name2').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'check') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'checkAll',
checklistName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'uncheck') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'uncheckAll',
checklistName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-check-item-action'(event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const checkItemName = this.find('#checkitem-name');
const checklistName = this.find('#checklist-name3');
const actionSelected = this.find('#check-item-action').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'check') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'checkItem',
checklistName,
checkItemName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'uncheck') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'uncheckItem',
checklistName,
checkItemName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
}, },
'click .js-add-checklist-action' (event) { ];
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#check-action').value;
const checklistName = this.find('#checklist-name').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'add') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'addChecklist',
checklistName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'remove') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'removeChecklist',
checklistName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-checkall-action' (event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#checkall-action').value;
const checklistName = this.find('#checklist-name2').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'check') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'checkAll',
checklistName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'uncheck') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'uncheckAll',
checklistName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
'click .js-add-check-item-action' (event) {
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const checkItemName = this.find('#checkitem-name');
const checklistName = this.find('#checklist-name3');
const actionSelected = this.find('#check-item-action').value;
const boardId = Session.get('currentBoard');
const desc = Utils.getTriggerActionDesc(event, this);
if (actionSelected === 'check') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'checkItem',
checklistName,
checkItemName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
if (actionSelected === 'uncheck') {
const triggerId = Triggers.insert(trigger);
const actionId = Actions.insert({
actionType: 'uncheckItem',
checklistName,
checkItemName,
boardId,
desc,
});
Rules.insert({
title: ruleName,
triggerId,
actionId,
boardId,
});
}
},
}];
}, },
}).register('checklistActions'); }).register('checklistActions');

View file

@ -1,35 +1,34 @@
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
onCreated() { onCreated() {},
},
events() { events() {
return [{ return [
'click .js-mail-action' (event) { {
const emailTo = this.find('#email-to').value; 'click .js-mail-action'(event) {
const emailSubject = this.find('#email-subject').value; const emailTo = this.find('#email-to').value;
const emailMsg = this.find('#email-msg').value; const emailSubject = this.find('#email-subject').value;
const trigger = this.data().triggerVar.get(); const emailMsg = this.find('#email-msg').value;
const ruleName = this.data().ruleName.get(); const trigger = this.data().triggerVar.get();
const triggerId = Triggers.insert(trigger); const ruleName = this.data().ruleName.get();
const boardId = Session.get('currentBoard'); const triggerId = Triggers.insert(trigger);
const desc = Utils.getTriggerActionDesc(event, this); const boardId = Session.get('currentBoard');
const actionId = Actions.insert({ const desc = Utils.getTriggerActionDesc(event, this);
actionType: 'sendEmail', const actionId = Actions.insert({
emailTo, actionType: 'sendEmail',
emailSubject, emailTo,
emailMsg, emailSubject,
boardId, emailMsg,
desc, boardId,
}); desc,
Rules.insert({ });
title: ruleName, Rules.insert({
triggerId, title: ruleName,
actionId, triggerId,
boardId, actionId,
}); boardId,
});
},
}, },
}]; ];
}, },
}).register('mailActions'); }).register('mailActions');

View file

@ -3,7 +3,6 @@ BlazeComponent.extendComponent({
this.subscribe('allRules'); this.subscribe('allRules');
this.subscribe('allTriggers'); this.subscribe('allTriggers');
this.subscribe('allActions'); this.subscribe('allActions');
}, },
trigger() { trigger() {
@ -34,5 +33,4 @@ BlazeComponent.extendComponent({
events() { events() {
return [{}]; return [{}];
}, },
}).register('ruleDetails'); }).register('ruleDetails');

View file

@ -40,19 +40,21 @@ BlazeComponent.extendComponent({
// console.log(this.data()); // console.log(this.data());
}, },
events() { events() {
return [{ return [
'click .js-set-board-actions'(){ {
this.setBoardActions(); 'click .js-set-board-actions'() {
this.setBoardActions();
},
'click .js-set-card-actions'() {
this.setCardActions();
},
'click .js-set-mail-actions'() {
this.setMailActions();
},
'click .js-set-checklist-actions'() {
this.setChecklistActions();
},
}, },
'click .js-set-card-actions'() { ];
this.setCardActions();
},
'click .js-set-mail-actions'() {
this.setMailActions();
},
'click .js-set-checklist-actions'() {
this.setChecklistActions();
},
}];
}, },
}).register('rulesActions'); }).register('rulesActions');

View file

@ -9,12 +9,12 @@ BlazeComponent.extendComponent({
setTrigger() { setTrigger() {
this.rulesCurrentTab.set('trigger'); this.rulesCurrentTab.set('trigger');
}, },
sanitizeObject(obj){ sanitizeObject(obj) {
Object.keys(obj).forEach((key) => { Object.keys(obj).forEach(key => {
if(obj[key] === '' || obj[key] === undefined){ if (obj[key] === '' || obj[key] === undefined) {
obj[key] = '*'; obj[key] = '*';
}} }
); });
}, },
setRulesList() { setRulesList() {
this.rulesCurrentTab.set('rulesList'); this.rulesCurrentTab.set('rulesList');
@ -29,69 +29,73 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-delete-rule' () { {
const rule = this.currentData(); 'click .js-delete-rule'() {
Rules.remove(rule._id); const rule = this.currentData();
Actions.remove(rule.actionId); Rules.remove(rule._id);
Triggers.remove(rule.triggerId); Actions.remove(rule.actionId);
Triggers.remove(rule.triggerId);
}, },
'click .js-goto-trigger' (event) { 'click .js-goto-trigger'(event) {
event.preventDefault(); event.preventDefault();
const ruleTitle = this.find('#ruleTitle').value; const ruleTitle = this.find('#ruleTitle').value;
if(ruleTitle !== undefined && ruleTitle !== ''){ if (ruleTitle !== undefined && ruleTitle !== '') {
this.find('#ruleTitle').value = ''; this.find('#ruleTitle').value = '';
this.ruleName.set(ruleTitle); this.ruleName.set(ruleTitle);
this.setTrigger(); this.setTrigger();
}
},
'click .js-goto-action' (event) {
event.preventDefault();
// Add user to the trigger
const username = $(event.currentTarget.offsetParent).find('.user-name').val();
let trigger = this.triggerVar.get();
trigger.userId = '*';
if(username !== undefined ){
const userFound = Users.findOne({username});
if(userFound !== undefined){
trigger.userId = userFound._id;
this.triggerVar.set(trigger);
} }
} },
// Sanitize trigger 'click .js-goto-action'(event) {
trigger = this.triggerVar.get(); event.preventDefault();
this.sanitizeObject(trigger); // Add user to the trigger
this.triggerVar.set(trigger); const username = $(event.currentTarget.offsetParent)
this.setAction(); .find('.user-name')
}, .val();
'click .js-show-user-field' (event) { let trigger = this.triggerVar.get();
event.preventDefault(); trigger.userId = '*';
$(event.currentTarget.offsetParent).find('.user-details').removeClass('hide-element'); if (username !== undefined) {
}, const userFound = Users.findOne({ username });
'click .js-goto-rules' (event) { if (userFound !== undefined) {
event.preventDefault(); trigger.userId = userFound._id;
this.setRulesList(); this.triggerVar.set(trigger);
}, }
'click .js-goback' (event) { }
event.preventDefault(); // Sanitize trigger
if(this.rulesCurrentTab.get() === 'trigger' || this.rulesCurrentTab.get() === 'ruleDetails' ){ trigger = this.triggerVar.get();
this.sanitizeObject(trigger);
this.triggerVar.set(trigger);
this.setAction();
},
'click .js-show-user-field'(event) {
event.preventDefault();
$(event.currentTarget.offsetParent)
.find('.user-details')
.removeClass('hide-element');
},
'click .js-goto-rules'(event) {
event.preventDefault();
this.setRulesList(); this.setRulesList();
} },
if(this.rulesCurrentTab.get() === 'action'){ 'click .js-goback'(event) {
this.setTrigger(); event.preventDefault();
} if (
this.rulesCurrentTab.get() === 'trigger' ||
this.rulesCurrentTab.get() === 'ruleDetails'
) {
this.setRulesList();
}
if (this.rulesCurrentTab.get() === 'action') {
this.setTrigger();
}
},
'click .js-goto-details'(event) {
event.preventDefault();
const rule = this.currentData();
this.ruleId.set(rule._id);
this.setRuleDetails();
},
}, },
'click .js-goto-details' (event) { ];
event.preventDefault();
const rule = this.currentData();
this.ruleId.set(rule._id);
this.setRuleDetails();
},
}];
}, },
}).register('rulesMain'); }).register('rulesMain');

View file

@ -38,16 +38,18 @@ BlazeComponent.extendComponent({
// console.log(this.data()); // console.log(this.data());
}, },
events() { events() {
return [{ return [
'click .js-set-board-triggers' () { {
this.setBoardTriggers(); 'click .js-set-board-triggers'() {
this.setBoardTriggers();
},
'click .js-set-card-triggers'() {
this.setCardTriggers();
},
'click .js-set-checklist-triggers'() {
this.setChecklistTriggers();
},
}, },
'click .js-set-card-triggers' () { ];
this.setCardTriggers();
},
'click .js-set-checklist-triggers' () {
this.setChecklistTriggers();
},
}];
}, },
}).register('rulesTriggers'); }).register('rulesTriggers');

View file

@ -4,116 +4,118 @@ BlazeComponent.extendComponent({
this.currentPopupTriggerId = 'def'; this.currentPopupTriggerId = 'def';
this.cardTitleFilters = {}; this.cardTitleFilters = {};
}, },
setNameFilter(name){ setNameFilter(name) {
this.cardTitleFilters[this.currentPopupTriggerId] = name; this.cardTitleFilters[this.currentPopupTriggerId] = name;
}, },
events() { events() {
return [{ return [
'click .js-open-card-title-popup'(event){ {
const funct = Popup.open('boardCardTitle'); 'click .js-open-card-title-popup'(event) {
const divId = $(event.currentTarget.parentNode.parentNode).attr('id'); const funct = Popup.open('boardCardTitle');
//console.log('current popup'); const divId = $(event.currentTarget.parentNode.parentNode).attr('id');
//console.log(this.currentPopupTriggerId); //console.log('current popup');
this.currentPopupTriggerId = divId; //console.log(this.currentPopupTriggerId);
funct.call(this, event); this.currentPopupTriggerId = divId;
}, funct.call(this, event);
'click .js-add-create-trigger' (event) { },
const desc = Utils.getTriggerActionDesc(event, this); 'click .js-add-create-trigger'(event) {
const datas = this.data(); const desc = Utils.getTriggerActionDesc(event, this);
const listName = this.find('#create-list-name').value; const datas = this.data();
const swimlaneName = this.find('#create-swimlane-name').value; const listName = this.find('#create-list-name').value;
const boardId = Session.get('currentBoard'); const swimlaneName = this.find('#create-swimlane-name').value;
const divId = $(event.currentTarget.parentNode).attr('id'); const boardId = Session.get('currentBoard');
const cardTitle = this.cardTitleFilters[divId]; const divId = $(event.currentTarget.parentNode).attr('id');
// move to generic funciont const cardTitle = this.cardTitleFilters[divId];
datas.triggerVar.set({ // move to generic funciont
activityType: 'createCard',
boardId,
cardTitle,
swimlaneName,
listName,
desc,
});
},
'click .js-add-moved-trigger' (event) {
const datas = this.data();
const desc = Utils.getTriggerActionDesc(event, this);
const swimlaneName = this.find('#create-swimlane-name-2').value;
const actionSelected = this.find('#move-action').value;
const listName = this.find('#move-list-name').value;
const boardId = Session.get('currentBoard');
const divId = $(event.currentTarget.parentNode).attr('id');
const cardTitle = this.cardTitleFilters[divId];
if (actionSelected === 'moved-to') {
datas.triggerVar.set({ datas.triggerVar.set({
activityType: 'moveCard', activityType: 'createCard',
boardId, boardId,
cardTitle,
swimlaneName,
listName, listName,
cardTitle,
swimlaneName,
'oldListName': '*',
desc, desc,
}); });
} },
if (actionSelected === 'moved-from') { 'click .js-add-moved-trigger'(event) {
const datas = this.data();
const desc = Utils.getTriggerActionDesc(event, this);
const swimlaneName = this.find('#create-swimlane-name-2').value;
const actionSelected = this.find('#move-action').value;
const listName = this.find('#move-list-name').value;
const boardId = Session.get('currentBoard');
const divId = $(event.currentTarget.parentNode).attr('id');
const cardTitle = this.cardTitleFilters[divId];
if (actionSelected === 'moved-to') {
datas.triggerVar.set({
activityType: 'moveCard',
boardId,
listName,
cardTitle,
swimlaneName,
oldListName: '*',
desc,
});
}
if (actionSelected === 'moved-from') {
datas.triggerVar.set({
activityType: 'moveCard',
boardId,
cardTitle,
swimlaneName,
listName: '*',
oldListName: listName,
desc,
});
}
},
'click .js-add-gen-moved-trigger'(event) {
const datas = this.data();
const desc = Utils.getTriggerActionDesc(event, this);
const boardId = Session.get('currentBoard');
datas.triggerVar.set({ datas.triggerVar.set({
activityType: 'moveCard', activityType: 'moveCard',
boardId, boardId,
cardTitle, swimlaneName: '*',
swimlaneName, listName: '*',
'listName': '*', oldListName: '*',
'oldListName': listName,
desc, desc,
}); });
} },
'click .js-add-arc-trigger'(event) {
const datas = this.data();
const desc = Utils.getTriggerActionDesc(event, this);
const actionSelected = this.find('#arch-action').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'archived') {
datas.triggerVar.set({
activityType: 'archivedCard',
boardId,
desc,
});
}
if (actionSelected === 'unarchived') {
datas.triggerVar.set({
activityType: 'restoredCard',
boardId,
desc,
});
}
},
}, },
'click .js-add-gen-moved-trigger' (event){ ];
const datas = this.data();
const desc = Utils.getTriggerActionDesc(event, this);
const boardId = Session.get('currentBoard');
datas.triggerVar.set({
'activityType': 'moveCard',
boardId,
'swimlaneName': '*',
'listName':'*',
'oldListName': '*',
desc,
});
},
'click .js-add-arc-trigger' (event) {
const datas = this.data();
const desc = Utils.getTriggerActionDesc(event, this);
const actionSelected = this.find('#arch-action').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'archived') {
datas.triggerVar.set({
activityType: 'archivedCard',
boardId,
desc,
});
}
if (actionSelected === 'unarchived') {
datas.triggerVar.set({
activityType: 'restoredCard',
boardId,
desc,
});
}
},
}];
}, },
}).register('boardTriggers'); }).register('boardTriggers');
Template.boardCardTitlePopup.events({ Template.boardCardTitlePopup.events({
submit(evt, tpl) { submit(event, templateInstance) {
const title = tpl.$('.js-card-filter-name').val().trim(); const title = templateInstance
.$('.js-card-filter-name')
.val()
.trim();
Popup.getOpenerComponent().setNameFilter(title); Popup.getOpenerComponent().setNameFilter(title);
evt.preventDefault(); event.preventDefault();
Popup.close(); Popup.close();
}, },
}); });

View file

@ -7,7 +7,7 @@ BlazeComponent.extendComponent({
for (let i = 0; i < labels.length; i++) { for (let i = 0; i < labels.length; i++) {
if (labels[i].name === '' || labels[i].name === undefined) { if (labels[i].name === '' || labels[i].name === undefined) {
labels[i].name = labels[i].color; labels[i].name = labels[i].color;
labels[i].translatedname = `${TAPi18n.__(`color-${ labels[i].color}`)}`; labels[i].translatedname = `${TAPi18n.__(`color-${labels[i].color}`)}`;
} else { } else {
labels[i].translatedname = labels[i].name; labels[i].translatedname = labels[i].name;
} }
@ -15,117 +15,119 @@ BlazeComponent.extendComponent({
return labels; return labels;
}, },
events() { events() {
return [{ return [
'click .js-add-gen-label-trigger' (event) { {
const desc = Utils.getTriggerActionDesc(event, this); 'click .js-add-gen-label-trigger'(event) {
const datas = this.data(); const desc = Utils.getTriggerActionDesc(event, this);
const actionSelected = this.find('#label-action').value; const datas = this.data();
const boardId = Session.get('currentBoard'); const actionSelected = this.find('#label-action').value;
if (actionSelected === 'added') { const boardId = Session.get('currentBoard');
datas.triggerVar.set({ if (actionSelected === 'added') {
activityType: 'addedLabel', datas.triggerVar.set({
boardId, activityType: 'addedLabel',
'labelId': '*', boardId,
desc, labelId: '*',
}); desc,
} });
if (actionSelected === 'removed') { }
datas.triggerVar.set({ if (actionSelected === 'removed') {
activityType: 'removedLabel', datas.triggerVar.set({
boardId, activityType: 'removedLabel',
'labelId': '*', boardId,
desc, labelId: '*',
}); desc,
} });
}
},
'click .js-add-spec-label-trigger'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#spec-label-action').value;
const labelId = this.find('#spec-label').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'added') {
datas.triggerVar.set({
activityType: 'addedLabel',
boardId,
labelId,
desc,
});
}
if (actionSelected === 'removed') {
datas.triggerVar.set({
activityType: 'removedLabel',
boardId,
labelId,
desc,
});
}
},
'click .js-add-gen-member-trigger'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#gen-member-action').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'added') {
datas.triggerVar.set({
activityType: 'joinMember',
boardId,
username: '*',
desc,
});
}
if (actionSelected === 'removed') {
datas.triggerVar.set({
activityType: 'unjoinMember',
boardId,
username: '*',
desc,
});
}
},
'click .js-add-spec-member-trigger'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#spec-member-action').value;
const username = this.find('#spec-member').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'added') {
datas.triggerVar.set({
activityType: 'joinMember',
boardId,
username,
desc,
});
}
if (actionSelected === 'removed') {
datas.triggerVar.set({
activityType: 'unjoinMember',
boardId,
username,
desc,
});
}
},
'click .js-add-attachment-trigger'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#attach-action').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'added') {
datas.triggerVar.set({
activityType: 'addAttachment',
boardId,
desc,
});
}
if (actionSelected === 'removed') {
datas.triggerVar.set({
activityType: 'deleteAttachment',
boardId,
desc,
});
}
},
}, },
'click .js-add-spec-label-trigger' (event) { ];
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#spec-label-action').value;
const labelId = this.find('#spec-label').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'added') {
datas.triggerVar.set({
activityType: 'addedLabel',
boardId,
labelId,
desc,
});
}
if (actionSelected === 'removed') {
datas.triggerVar.set({
activityType: 'removedLabel',
boardId,
labelId,
desc,
});
}
},
'click .js-add-gen-member-trigger' (event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#gen-member-action').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'added') {
datas.triggerVar.set({
activityType: 'joinMember',
boardId,
'username': '*',
desc,
});
}
if (actionSelected === 'removed') {
datas.triggerVar.set({
activityType: 'unjoinMember',
boardId,
'username': '*',
desc,
});
}
},
'click .js-add-spec-member-trigger' (event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#spec-member-action').value;
const username = this.find('#spec-member').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'added') {
datas.triggerVar.set({
activityType: 'joinMember',
boardId,
username,
desc,
});
}
if (actionSelected === 'removed') {
datas.triggerVar.set({
activityType: 'unjoinMember',
boardId,
username,
desc,
});
}
},
'click .js-add-attachment-trigger' (event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#attach-action').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'added') {
datas.triggerVar.set({
activityType: 'addAttachment',
boardId,
desc,
});
}
if (actionSelected === 'removed') {
datas.triggerVar.set({
activityType: 'deleteAttachment',
boardId,
desc,
});
}
},
}];
}, },
}).register('cardTriggers'); }).register('cardTriggers');

View file

@ -3,144 +3,145 @@ BlazeComponent.extendComponent({
this.subscribe('allRules'); this.subscribe('allRules');
}, },
events() { events() {
return [{ return [
'click .js-add-gen-check-trigger' (event) { {
const desc = Utils.getTriggerActionDesc(event, this); 'click .js-add-gen-check-trigger'(event) {
const datas = this.data(); const desc = Utils.getTriggerActionDesc(event, this);
const actionSelected = this.find('#gen-check-action').value; const datas = this.data();
const boardId = Session.get('currentBoard'); const actionSelected = this.find('#gen-check-action').value;
if (actionSelected === 'created') { const boardId = Session.get('currentBoard');
datas.triggerVar.set({ if (actionSelected === 'created') {
activityType: 'addChecklist', datas.triggerVar.set({
boardId, activityType: 'addChecklist',
'checklistName': '*', boardId,
desc, checklistName: '*',
}); desc,
} });
if (actionSelected === 'removed') { }
datas.triggerVar.set({ if (actionSelected === 'removed') {
activityType: 'removeChecklist', datas.triggerVar.set({
boardId, activityType: 'removeChecklist',
'checklistName': '*', boardId,
desc, checklistName: '*',
}); desc,
} });
}, }
'click .js-add-spec-check-trigger' (event) { },
const desc = Utils.getTriggerActionDesc(event, this); 'click .js-add-spec-check-trigger'(event) {
const datas = this.data(); const desc = Utils.getTriggerActionDesc(event, this);
const actionSelected = this.find('#spec-check-action').value; const datas = this.data();
const checklistId = this.find('#check-name').value; const actionSelected = this.find('#spec-check-action').value;
const boardId = Session.get('currentBoard'); const checklistId = this.find('#check-name').value;
if (actionSelected === 'created') { const boardId = Session.get('currentBoard');
datas.triggerVar.set({ if (actionSelected === 'created') {
activityType: 'addChecklist', datas.triggerVar.set({
boardId, activityType: 'addChecklist',
'checklistName': checklistId, boardId,
desc, checklistName: checklistId,
}); desc,
} });
if (actionSelected === 'removed') { }
datas.triggerVar.set({ if (actionSelected === 'removed') {
activityType: 'removeChecklist', datas.triggerVar.set({
boardId, activityType: 'removeChecklist',
'checklistName': checklistId, boardId,
desc, checklistName: checklistId,
}); desc,
} });
}, }
'click .js-add-gen-comp-trigger' (event) { },
const desc = Utils.getTriggerActionDesc(event, this); 'click .js-add-gen-comp-trigger'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data(); const datas = this.data();
const actionSelected = this.find('#gen-comp-check-action').value; const actionSelected = this.find('#gen-comp-check-action').value;
const boardId = Session.get('currentBoard'); const boardId = Session.get('currentBoard');
if (actionSelected === 'completed') { if (actionSelected === 'completed') {
datas.triggerVar.set({ datas.triggerVar.set({
activityType: 'completeChecklist', activityType: 'completeChecklist',
boardId, boardId,
'checklistName': '*', checklistName: '*',
desc, desc,
}); });
} }
if (actionSelected === 'uncompleted') { if (actionSelected === 'uncompleted') {
datas.triggerVar.set({ datas.triggerVar.set({
activityType: 'uncompleteChecklist', activityType: 'uncompleteChecklist',
boardId, boardId,
'checklistName': '*', checklistName: '*',
desc, desc,
}); });
} }
},
'click .js-add-spec-comp-trigger'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#spec-comp-check-action').value;
const checklistId = this.find('#spec-comp-check-name').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'completed') {
datas.triggerVar.set({
activityType: 'completeChecklist',
boardId,
checklistName: checklistId,
desc,
});
}
if (actionSelected === 'uncompleted') {
datas.triggerVar.set({
activityType: 'uncompleteChecklist',
boardId,
checklistName: checklistId,
desc,
});
}
},
'click .js-add-gen-check-item-trigger'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#check-item-gen-action').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'checked') {
datas.triggerVar.set({
activityType: 'checkedItem',
boardId,
checklistItemName: '*',
desc,
});
}
if (actionSelected === 'unchecked') {
datas.triggerVar.set({
activityType: 'uncheckedItem',
boardId,
checklistItemName: '*',
desc,
});
}
},
'click .js-add-spec-check-item-trigger'(event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#check-item-spec-action').value;
const checklistItemId = this.find('#check-item-name').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'checked') {
datas.triggerVar.set({
activityType: 'checkedItem',
boardId,
checklistItemName: checklistItemId,
desc,
});
}
if (actionSelected === 'unchecked') {
datas.triggerVar.set({
activityType: 'uncheckedItem',
boardId,
checklistItemName: checklistItemId,
desc,
});
}
},
}, },
'click .js-add-spec-comp-trigger' (event) { ];
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#spec-comp-check-action').value;
const checklistId = this.find('#spec-comp-check-name').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'completed') {
datas.triggerVar.set({
activityType: 'completeChecklist',
boardId,
'checklistName': checklistId,
desc,
});
}
if (actionSelected === 'uncompleted') {
datas.triggerVar.set({
activityType: 'uncompleteChecklist',
boardId,
'checklistName': checklistId,
desc,
});
}
},
'click .js-add-gen-check-item-trigger' (event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#check-item-gen-action').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'checked') {
datas.triggerVar.set({
activityType: 'checkedItem',
boardId,
'checklistItemName': '*',
desc,
});
}
if (actionSelected === 'unchecked') {
datas.triggerVar.set({
activityType: 'uncheckedItem',
boardId,
'checklistItemName': '*',
desc,
});
}
},
'click .js-add-spec-check-item-trigger' (event) {
const desc = Utils.getTriggerActionDesc(event, this);
const datas = this.data();
const actionSelected = this.find('#check-item-spec-action').value;
const checklistItemId = this.find('#check-item-name').value;
const boardId = Session.get('currentBoard');
if (actionSelected === 'checked') {
datas.triggerVar.set({
activityType: 'checkedItem',
boardId,
'checklistItemName': checklistItemId,
desc,
});
}
if (actionSelected === 'unchecked') {
datas.triggerVar.set({
activityType: 'uncheckedItem',
boardId,
'checklistItemName': checklistItemId,
desc,
});
}
},
}];
}, },
}).register('checklistTriggers'); }).register('checklistTriggers');

View file

@ -6,9 +6,11 @@ Template.connectionMethod.onCreated(function() {
// TODO : add a management of different languages // TODO : add a management of different languages
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')}) // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
this.authenticationMethods.set([ this.authenticationMethods.set([
{value: 'password'}, { value: 'password' },
// Gets only the authentication methods availables // Gets only the authentication methods availables
...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})), ...Object.entries(result)
.filter(e => e[1])
.map(e => ({ value: e[0] })),
]); ]);
} }
@ -24,7 +26,9 @@ Template.connectionMethod.onCreated(function() {
Template.connectionMethod.onRendered(() => { Template.connectionMethod.onRendered(() => {
// Moves the select boxe in the first place of the at-pwd-form div // Moves the select boxe in the first place of the at-pwd-form div
$('.at-form-authentication').detach().prependTo('.at-pwd-form'); $('.at-form-authentication')
.detach()
.prependTo('.at-pwd-form');
}); });
Template.connectionMethod.helpers({ Template.connectionMethod.helpers({

View file

@ -29,19 +29,23 @@ BlazeComponent.extendComponent({
}); });
}, },
events() { events() {
return [{ return [
'click #searchButton'() { {
this.filterPeople(); 'click #searchButton'() {
},
'keydown #searchInput'(event) {
if (event.keyCode === 13 && !event.shiftKey) {
this.filterPeople(); this.filterPeople();
} },
'keydown #searchInput'(event) {
if (event.keyCode === 13 && !event.shiftKey) {
this.filterPeople();
}
},
}, },
}]; ];
}, },
filterPeople() { filterPeople() {
const value = $('#searchInput').first().val(); const value = $('#searchInput')
.first()
.val();
if (value === '') { if (value === '') {
this.findUsersOptions.set({}); this.findUsersOptions.set({});
} else { } else {
@ -79,7 +83,7 @@ BlazeComponent.extendComponent({
}, },
peopleList() { peopleList() {
const users = Users.find(this.findUsersOptions.get(), { const users = Users.find(this.findUsersOptions.get(), {
fields: {_id: true}, fields: { _id: true },
}); });
this.number.set(users.count()); this.number.set(users.count());
return users; return users;
@ -105,9 +109,11 @@ Template.editUserPopup.onCreated(function() {
// TODO : add a management of different languages // TODO : add a management of different languages
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')}) // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
this.authenticationMethods.set([ this.authenticationMethods.set([
{value: 'password'}, { value: 'password' },
// Gets only the authentication methods availables // Gets only the authentication methods availables
...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})), ...Object.entries(result)
.filter(e => e[1])
.map(e => ({ value: e[0] })),
]); ]);
} }
}); });
@ -136,69 +142,79 @@ Template.editUserPopup.helpers({
}); });
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
onCreated() { onCreated() {},
},
user() { user() {
return Users.findOne(this.userId); return Users.findOne(this.userId);
}, },
events() { events() {
return [{ return [
'click a.edit-user': Popup.open('editUser'), {
}]; 'click a.edit-user': Popup.open('editUser'),
},
];
}, },
}).register('peopleRow'); }).register('peopleRow');
Template.editUserPopup.events({ Template.editUserPopup.events({
submit(evt, tpl) { submit(event, templateInstance) {
evt.preventDefault(); event.preventDefault();
const user = Users.findOne(this.userId); const user = Users.findOne(this.userId);
const fullname = tpl.find('.js-profile-fullname').value.trim(); const fullname = templateInstance.find('.js-profile-fullname').value.trim();
const username = tpl.find('.js-profile-username').value.trim(); const username = templateInstance.find('.js-profile-username').value.trim();
const password = tpl.find('.js-profile-password').value; const password = templateInstance.find('.js-profile-password').value;
const isAdmin = tpl.find('.js-profile-isadmin').value.trim(); const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
const isActive = tpl.find('.js-profile-isactive').value.trim(); const isActive = templateInstance.find('.js-profile-isactive').value.trim();
const email = tpl.find('.js-profile-email').value.trim(); const email = templateInstance.find('.js-profile-email').value.trim();
const authentication = tpl.find('.js-authenticationMethod').value.trim(); const authentication = templateInstance
.find('.js-authenticationMethod')
.value.trim();
const isChangePassword = password.length > 0; const isChangePassword = password.length > 0;
const isChangeUserName = username !== user.username; const isChangeUserName = username !== user.username;
const isChangeEmail = email.toLowerCase() !== user.emails[0].address.toLowerCase(); const isChangeEmail =
email.toLowerCase() !== user.emails[0].address.toLowerCase();
Users.update(this.userId, { Users.update(this.userId, {
$set: { $set: {
'profile.fullname': fullname, 'profile.fullname': fullname,
'isAdmin': isAdmin === 'true', isAdmin: isAdmin === 'true',
'loginDisabled': isActive === 'true', loginDisabled: isActive === 'true',
'authenticationMethod': authentication, authenticationMethod: authentication,
}, },
}); });
if(isChangePassword){ if (isChangePassword) {
Meteor.call('setPassword', password, this.userId); Meteor.call('setPassword', password, this.userId);
} }
if (isChangeUserName && isChangeEmail) { if (isChangeUserName && isChangeEmail) {
Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), this.userId, function (error) { Meteor.call(
const usernameMessageElement = tpl.$('.username-taken'); 'setUsernameAndEmail',
const emailMessageElement = tpl.$('.email-taken'); username,
if (error) { email.toLowerCase(),
const errorElement = error.error; this.userId,
if (errorElement === 'username-already-taken') { function(error) {
usernameMessageElement.show(); const usernameMessageElement = templateInstance.$('.username-taken');
emailMessageElement.hide(); const emailMessageElement = templateInstance.$('.email-taken');
} else if (errorElement === 'email-already-taken') { if (error) {
const errorElement = error.error;
if (errorElement === 'username-already-taken') {
usernameMessageElement.show();
emailMessageElement.hide();
} else if (errorElement === 'email-already-taken') {
usernameMessageElement.hide();
emailMessageElement.show();
}
} else {
usernameMessageElement.hide(); usernameMessageElement.hide();
emailMessageElement.show(); emailMessageElement.hide();
Popup.close();
} }
} else { },
usernameMessageElement.hide(); );
emailMessageElement.hide();
Popup.close();
}
});
} else if (isChangeUserName) { } else if (isChangeUserName) {
Meteor.call('setUsername', username, this.userId, function (error) { Meteor.call('setUsername', username, this.userId, function(error) {
const usernameMessageElement = tpl.$('.username-taken'); const usernameMessageElement = templateInstance.$('.username-taken');
if (error) { if (error) {
const errorElement = error.error; const errorElement = error.error;
if (errorElement === 'username-already-taken') { if (errorElement === 'username-already-taken') {
@ -210,8 +226,10 @@ Template.editUserPopup.events({
} }
}); });
} else if (isChangeEmail) { } else if (isChangeEmail) {
Meteor.call('setEmail', email.toLowerCase(), this.userId, function (error) { Meteor.call('setEmail', email.toLowerCase(), this.userId, function(
const emailMessageElement = tpl.$('.email-taken'); error,
) {
const emailMessageElement = templateInstance.$('.email-taken');
if (error) { if (error) {
const errorElement = error.error; const errorElement = error.error;
if (errorElement === 'email-already-taken') { if (errorElement === 'email-already-taken') {

View file

@ -25,7 +25,9 @@ BlazeComponent.extendComponent({
checkField(selector) { checkField(selector) {
const value = $(selector).val(); const value = $(selector).val();
if (!value || value.trim() === '') { if (!value || value.trim() === '') {
$(selector).parents('li.smtp-form').addClass('has-error'); $(selector)
.parents('li.smtp-form')
.addClass('has-error');
throw Error('blank field'); throw Error('blank field');
} else { } else {
return value; return value;
@ -37,18 +39,23 @@ BlazeComponent.extendComponent({
}, },
boards() { boards() {
return Boards.find({ return Boards.find(
archived: false, {
'members.userId': Meteor.userId(), archived: false,
'members.isAdmin': true, 'members.userId': Meteor.userId(),
}, { 'members.isAdmin': true,
sort: ['title'], },
}); {
sort: ['title'],
},
);
}, },
toggleRegistration() { toggleRegistration() {
this.setLoading(true); this.setLoading(true);
const registrationClosed = this.currentSetting().disableRegistration; const registrationClosed = this.currentSetting().disableRegistration;
Settings.update(Settings.findOne()._id, {$set: {disableRegistration: !registrationClosed}}); Settings.update(Settings.findOne()._id, {
$set: { disableRegistration: !registrationClosed },
});
this.setLoading(false); this.setLoading(false);
if (registrationClosed) { if (registrationClosed) {
$('.invite-people').slideUp(); $('.invite-people').slideUp();
@ -90,13 +97,19 @@ BlazeComponent.extendComponent({
}, },
inviteThroughEmail() { inviteThroughEmail() {
const emails = $('#email-to-invite').val().toLowerCase().trim().split('\n').join(',').split(','); const emails = $('#email-to-invite')
.val()
.toLowerCase()
.trim()
.split('\n')
.join(',')
.split(',');
const boardsToInvite = []; const boardsToInvite = [];
$('.js-toggle-board-choose .materialCheckBox.is-checked').each(function () { $('.js-toggle-board-choose .materialCheckBox.is-checked').each(function() {
boardsToInvite.push($(this).data('id')); boardsToInvite.push($(this).data('id'));
}); });
const validEmails = []; const validEmails = [];
emails.forEach((email) => { emails.forEach(email => {
if (email && SimpleSchema.RegEx.Email.test(email.trim())) { if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
validEmails.push(email.trim()); validEmails.push(email.trim());
} }
@ -119,14 +132,22 @@ BlazeComponent.extendComponent({
try { try {
const host = this.checkField('#mail-server-host'); const host = this.checkField('#mail-server-host');
const port = this.checkField('#mail-server-port'); const port = this.checkField('#mail-server-port');
const username = $('#mail-server-username').val().trim(); const username = $('#mail-server-username')
const password = $('#mail-server-password').val().trim(); .val()
.trim();
const password = $('#mail-server-password')
.val()
.trim();
const from = this.checkField('#mail-server-from'); const from = this.checkField('#mail-server-from');
const tls = $('#mail-server-tls.is-checked').length > 0; const tls = $('#mail-server-tls.is-checked').length > 0;
Settings.update(Settings.findOne()._id, { Settings.update(Settings.findOne()._id, {
$set: { $set: {
'mailServer.host': host, 'mailServer.port': port, 'mailServer.username': username, 'mailServer.host': host,
'mailServer.password': password, 'mailServer.enableTLS': tls, 'mailServer.from': from, 'mailServer.port': port,
'mailServer.username': username,
'mailServer.password': password,
'mailServer.enableTLS': tls,
'mailServer.from': from,
}, },
}); });
} catch (e) { } catch (e) {
@ -134,19 +155,25 @@ BlazeComponent.extendComponent({
} finally { } finally {
this.setLoading(false); this.setLoading(false);
} }
}, },
saveLayout() { saveLayout() {
this.setLoading(true); this.setLoading(true);
$('li').removeClass('has-error'); $('li').removeClass('has-error');
const productName = $('#product-name').val().trim(); const productName = $('#product-name')
const hideLogoChange = ($('input[name=hideLogo]:checked').val() === 'true'); .val()
const displayAuthenticationMethod = ($('input[name=displayAuthenticationMethod]:checked').val() === 'true'); .trim();
const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';
const displayAuthenticationMethod =
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val(); const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
const customHTMLafterBodyStart = $('#customHTMLafterBodyStart').val().trim(); const customHTMLafterBodyStart = $('#customHTMLafterBodyStart')
const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd').val().trim(); .val()
.trim();
const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd')
.val()
.trim();
try { try {
Settings.update(Settings.findOne()._id, { Settings.update(Settings.findOne()._id, {
@ -166,7 +193,6 @@ BlazeComponent.extendComponent({
} }
DocHead.setTitle(productName); DocHead.setTitle(productName);
}, },
sendSMTPTestEmail() { sendSMTPTestEmail() {
@ -183,31 +209,35 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click a.js-toggle-registration': this.toggleRegistration, {
'click a.js-toggle-tls': this.toggleTLS, 'click a.js-toggle-registration': this.toggleRegistration,
'click a.js-setting-menu': this.switchMenu, 'click a.js-toggle-tls': this.toggleTLS,
'click a.js-toggle-board-choose': this.checkBoard, 'click a.js-setting-menu': this.switchMenu,
'click button.js-email-invite': this.inviteThroughEmail, 'click a.js-toggle-board-choose': this.checkBoard,
'click button.js-save': this.saveMailServerInfo, 'click button.js-email-invite': this.inviteThroughEmail,
'click button.js-send-smtp-test-email': this.sendSMTPTestEmail, 'click button.js-save': this.saveMailServerInfo,
'click a.js-toggle-hide-logo': this.toggleHideLogo, 'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
'click button.js-save-layout': this.saveLayout, 'click a.js-toggle-hide-logo': this.toggleHideLogo,
'click a.js-toggle-display-authentication-method': this.toggleDisplayAuthenticationMethod, 'click button.js-save-layout': this.saveLayout,
}]; 'click a.js-toggle-display-authentication-method': this
.toggleDisplayAuthenticationMethod,
},
];
}, },
}).register('setting'); }).register('setting');
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
saveAccountsChange() { saveAccountsChange() {
const allowEmailChange = ($('input[name=allowEmailChange]:checked').val() === 'true'); const allowEmailChange =
const allowUserNameChange = ($('input[name=allowUserNameChange]:checked').val() === 'true'); $('input[name=allowEmailChange]:checked').val() === 'true';
const allowUserNameChange =
$('input[name=allowUserNameChange]:checked').val() === 'true';
AccountSettings.update('accounts-allowEmailChange', { AccountSettings.update('accounts-allowEmailChange', {
$set: {'booleanValue': allowEmailChange}, $set: { booleanValue: allowEmailChange },
}); });
AccountSettings.update('accounts-allowUserNameChange', { AccountSettings.update('accounts-allowUserNameChange', {
$set: {'booleanValue': allowUserNameChange}, $set: { booleanValue: allowUserNameChange },
}); });
}, },
@ -219,9 +249,11 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click button.js-accounts-save': this.saveAccountsChange, {
}]; 'click button.js-accounts-save': this.saveAccountsChange,
},
];
}, },
}).register('accountSettings'); }).register('accountSettings');
@ -239,9 +271,11 @@ BlazeComponent.extendComponent({
}, },
saveMessage() { saveMessage() {
const message = $('#admin-announcement').val().trim(); const message = $('#admin-announcement')
.val()
.trim();
Announcements.update(Announcements.findOne()._id, { Announcements.update(Announcements.findOne()._id, {
$set: {'body': message}, $set: { body: message },
}); });
}, },
@ -249,7 +283,7 @@ BlazeComponent.extendComponent({
this.setLoading(true); this.setLoading(true);
const isActive = this.currentSetting().enabled; const isActive = this.currentSetting().enabled;
Announcements.update(Announcements.findOne()._id, { Announcements.update(Announcements.findOne()._id, {
$set: {'enabled': !isActive}, $set: { enabled: !isActive },
}); });
this.setLoading(false); this.setLoading(false);
if (isActive) { if (isActive) {
@ -260,14 +294,15 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click a.js-toggle-activemessage': this.toggleActive, {
'click button.js-announcement-save': this.saveMessage, 'click a.js-toggle-activemessage': this.toggleActive,
}]; 'click button.js-announcement-save': this.saveMessage,
},
];
}, },
}).register('announcementSettings'); }).register('announcementSettings');
Template.selectAuthenticationMethod.onCreated(function() { Template.selectAuthenticationMethod.onCreated(function() {
this.authenticationMethods = new ReactiveVar([]); this.authenticationMethods = new ReactiveVar([]);
@ -276,9 +311,11 @@ Template.selectAuthenticationMethod.onCreated(function() {
// TODO : add a management of different languages // TODO : add a management of different languages
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')}) // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
this.authenticationMethods.set([ this.authenticationMethods.set([
{value: 'password'}, { value: 'password' },
// Gets only the authentication methods availables // Gets only the authentication methods availables
...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})), ...Object.entries(result)
.filter(e => e[1])
.map(e => ({ value: e[0] })),
]); ]);
} }
}); });

View file

@ -91,29 +91,34 @@ BlazeComponent.extendComponent({
}, },
showTongueTitle() { showTongueTitle() {
if (this.isOpen()) if (this.isOpen()) return `${TAPi18n.__('sidebar-close')}`;
return `${TAPi18n.__('sidebar-close')}`; else return `${TAPi18n.__('sidebar-open')}`;
else
return `${TAPi18n.__('sidebar-open')}`;
}, },
events() { events() {
return [{ return [
'click .js-hide-sidebar': this.hide, {
'click .js-toggle-sidebar': this.toggle, 'click .js-hide-sidebar': this.hide,
'click .js-back-home': this.setView, 'click .js-toggle-sidebar': this.toggle,
'click .js-shortcuts'() { 'click .js-back-home': this.setView,
FlowRouter.go('shortcuts'); 'click .js-shortcuts'() {
FlowRouter.go('shortcuts');
},
}, },
}]; ];
}, },
}).register('sidebar'); }).register('sidebar');
Blaze.registerHelper('Sidebar', () => Sidebar); Blaze.registerHelper('Sidebar', () => Sidebar);
EscapeActions.register('sidebarView', EscapeActions.register(
() => { Sidebar.setView(defaultView); }, 'sidebarView',
() => { return Sidebar && Sidebar.getView() !== defaultView; } () => {
Sidebar.setView(defaultView);
},
() => {
return Sidebar && Sidebar.getView() !== defaultView;
},
); );
Template.memberPopup.helpers({ Template.memberPopup.helpers({
@ -122,13 +127,13 @@ Template.memberPopup.helpers({
}, },
memberType() { memberType() {
const type = Users.findOne(this.userId).isBoardAdmin() ? 'admin' : 'normal'; const type = Users.findOne(this.userId).isBoardAdmin() ? 'admin' : 'normal';
if(type === 'normal'){ if (type === 'normal') {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
const commentOnly = currentBoard.hasCommentOnly(this.userId); const commentOnly = currentBoard.hasCommentOnly(this.userId);
const noComments = currentBoard.hasNoComments(this.userId); const noComments = currentBoard.hasNoComments(this.userId);
if(commentOnly){ if (commentOnly) {
return TAPi18n.__('comment-only').toLowerCase(); return TAPi18n.__('comment-only').toLowerCase();
} else if(noComments) { } else if (noComments) {
return TAPi18n.__('no-comments').toLowerCase(); return TAPi18n.__('no-comments').toLowerCase();
} else { } else {
return TAPi18n.__(type).toLowerCase(); return TAPi18n.__(type).toLowerCase();
@ -197,7 +202,7 @@ Template.memberPopup.events({
'click .js-remove-member': Popup.afterConfirm('removeMember', function() { 'click .js-remove-member': Popup.afterConfirm('removeMember', function() {
const boardId = Session.get('currentBoard'); const boardId = Session.get('currentBoard');
const memberId = this.userId; const memberId = this.userId;
Cards.find({ boardId, members: memberId }).forEach((card) => { Cards.find({ boardId, members: memberId }).forEach(card => {
card.unassignMember(memberId); card.unassignMember(memberId);
}); });
Boards.findOne(boardId).removeMember(memberId); Boards.findOne(boardId).removeMember(memberId);
@ -274,38 +279,40 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'submit'(evt) { {
evt.preventDefault(); submit(evt) {
const url = evt.target.url.value; evt.preventDefault();
const boardId = Session.get('currentBoard'); const url = evt.target.url.value;
let id = null; const boardId = Session.get('currentBoard');
let integration = null; let id = null;
if (evt.target.id) { let integration = null;
id = evt.target.id.value; if (evt.target.id) {
integration = this.integration(id); id = evt.target.id.value;
if (url) { integration = this.integration(id);
Integrations.update(integration._id, { if (url) {
$set: { Integrations.update(integration._id, {
url: `${url}`, $set: {
}, url: `${url}`,
},
});
} else {
Integrations.remove(integration._id);
}
} else if (url) {
Integrations.insert({
userId: Meteor.userId(),
enabled: true,
type: 'outgoing-webhooks',
url: `${url}`,
boardId: `${boardId}`,
activities: ['all'],
}); });
} else {
Integrations.remove(integration._id);
} }
} else if (url) { Popup.close();
Integrations.insert({ },
userId: Meteor.userId(),
enabled: true,
type: 'outgoing-webhooks',
url: `${url}`,
boardId: `${boardId}`,
activities: ['all'],
});
}
Popup.close();
}, },
}]; ];
}, },
}).register('outgoingWebhooksPopup'); }).register('outgoingWebhooksPopup');
@ -375,14 +382,16 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-select-background'(evt) { {
const currentBoard = Boards.findOne(Session.get('currentBoard')); 'click .js-select-background'(evt) {
const newColor = this.currentData().toString(); const currentBoard = Boards.findOne(Session.get('currentBoard'));
currentBoard.setColor(newColor); const newColor = this.currentData().toString();
evt.preventDefault(); currentBoard.setColor(newColor);
evt.preventDefault();
},
}, },
}]; ];
}, },
}).register('boardChangeColorPopup'); }).register('boardChangeColorPopup');
@ -400,25 +409,34 @@ BlazeComponent.extendComponent({
}, },
isNullBoardSelected() { isNullBoardSelected() {
return (this.currentBoard.subtasksDefaultBoardId === null) || (this.currentBoard.subtasksDefaultBoardId === undefined); return (
this.currentBoard.subtasksDefaultBoardId === null ||
this.currentBoard.subtasksDefaultBoardId === undefined
);
}, },
boards() { boards() {
return Boards.find({ return Boards.find(
archived: false, {
'members.userId': Meteor.userId(), archived: false,
}, { 'members.userId': Meteor.userId(),
sort: ['title'], },
}); {
sort: ['title'],
},
);
}, },
lists() { lists() {
return Lists.find({ return Lists.find(
boardId: this.currentBoard._id, {
archived: false, boardId: this.currentBoard._id,
}, { archived: false,
sort: ['title'], },
}); {
sort: ['title'],
},
);
}, },
hasLists() { hasLists() {
@ -431,54 +449,74 @@ BlazeComponent.extendComponent({
presentParentTask() { presentParentTask() {
let result = this.currentBoard.presentParentTask; let result = this.currentBoard.presentParentTask;
if ((result === null) || (result === undefined)) { if (result === null || result === undefined) {
result = 'no-parent'; result = 'no-parent';
} }
return result; return result;
}, },
events() { events() {
return [{ return [
'click .js-field-has-subtasks'(evt) { {
evt.preventDefault(); 'click .js-field-has-subtasks'(evt) {
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks; evt.preventDefault();
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks); this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
$('.js-field-has-subtasks .materialCheckBox').toggleClass('is-checked', this.currentBoard.allowsSubtasks); this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
$('.js-field-has-subtasks').toggleClass('is-checked', this.currentBoard.allowsSubtasks); $('.js-field-has-subtasks .materialCheckBox').toggleClass(
$('.js-field-deposit-board').prop('disabled', !this.currentBoard.allowsSubtasks); 'is-checked',
}, this.currentBoard.allowsSubtasks,
'change .js-field-deposit-board'(evt) { );
let value = evt.target.value; $('.js-field-has-subtasks').toggleClass(
if (value === 'null') { 'is-checked',
value = null; this.currentBoard.allowsSubtasks,
} );
this.currentBoard.setSubtasksDefaultBoardId(value); $('.js-field-deposit-board').prop(
evt.preventDefault(); 'disabled',
}, !this.currentBoard.allowsSubtasks,
'change .js-field-deposit-list'(evt) { );
this.currentBoard.setSubtasksDefaultListId(evt.target.value); },
evt.preventDefault(); 'change .js-field-deposit-board'(evt) {
}, let value = evt.target.value;
'click .js-field-show-parent-in-minicard'(evt) { if (value === 'null') {
const value = evt.target.id || $(evt.target).parent()[0].id || $(evt.target).parent()[0].parent()[0].id; value = null;
const options = [
'prefix-with-full-path',
'prefix-with-parent',
'subtext-with-full-path',
'subtext-with-parent',
'no-parent'];
options.forEach(function(element) {
if (element !== value) {
$(`#${element} .materialCheckBox`).toggleClass('is-checked', false);
$(`#${element}`).toggleClass('is-checked', false);
} }
}); this.currentBoard.setSubtasksDefaultBoardId(value);
$(`#${value} .materialCheckBox`).toggleClass('is-checked', true); evt.preventDefault();
$(`#${value}`).toggleClass('is-checked', true); },
this.currentBoard.setPresentParentTask(value); 'change .js-field-deposit-list'(evt) {
evt.preventDefault(); this.currentBoard.setSubtasksDefaultListId(evt.target.value);
evt.preventDefault();
},
'click .js-field-show-parent-in-minicard'(evt) {
const value =
evt.target.id ||
$(evt.target).parent()[0].id ||
$(evt.target)
.parent()[0]
.parent()[0].id;
const options = [
'prefix-with-full-path',
'prefix-with-parent',
'subtext-with-full-path',
'subtext-with-parent',
'no-parent',
];
options.forEach(function(element) {
if (element !== value) {
$(`#${element} .materialCheckBox`).toggleClass(
'is-checked',
false,
);
$(`#${element}`).toggleClass('is-checked', false);
}
});
$(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
$(`#${value}`).toggleClass('is-checked', true);
this.currentBoard.setPresentParentTask(value);
evt.preventDefault();
},
}, },
}]; ];
}, },
}).register('boardSubtaskSettingsPopup'); }).register('boardSubtaskSettingsPopup');
@ -528,35 +566,46 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'keyup input'() { {
this.setError(''); 'keyup input'() {
this.setError('');
},
'click .js-select-member'() {
const userId = this.currentData()._id;
const currentBoard = Boards.findOne(Session.get('currentBoard'));
if (!currentBoard.hasMember(userId)) {
this.inviteUser(userId);
}
},
'click .js-email-invite'() {
const idNameEmail = $('.js-search-member input').val();
if (idNameEmail.indexOf('@') < 0 || this.isValidEmail(idNameEmail)) {
this.inviteUser(idNameEmail);
} else this.setError('email-invalid');
},
}, },
'click .js-select-member'() { ];
const userId = this.currentData()._id;
const currentBoard = Boards.findOne(Session.get('currentBoard'));
if (!currentBoard.hasMember(userId)) {
this.inviteUser(userId);
}
},
'click .js-email-invite'() {
const idNameEmail = $('.js-search-member input').val();
if (idNameEmail.indexOf('@')<0 || this.isValidEmail(idNameEmail)) {
this.inviteUser(idNameEmail);
} else this.setError('email-invalid');
},
}];
}, },
}).register('addMemberPopup'); }).register('addMemberPopup');
Template.changePermissionsPopup.events({ Template.changePermissionsPopup.events({
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(event) { 'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(
event,
) {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
const memberId = this.userId; const memberId = this.userId;
const isAdmin = $(event.currentTarget).hasClass('js-set-admin'); const isAdmin = $(event.currentTarget).hasClass('js-set-admin');
const isCommentOnly = $(event.currentTarget).hasClass('js-set-comment-only'); const isCommentOnly = $(event.currentTarget).hasClass(
'js-set-comment-only',
);
const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments'); const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
currentBoard.setMemberPermission(memberId, isAdmin, isNoComments, isCommentOnly); currentBoard.setMemberPermission(
memberId,
isAdmin,
isNoComments,
isCommentOnly,
);
Popup.back(1); Popup.back(1);
}, },
}); });
@ -569,21 +618,33 @@ Template.changePermissionsPopup.helpers({
isNormal() { isNormal() {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
return !currentBoard.hasAdmin(this.userId) && !currentBoard.hasNoComments(this.userId) && !currentBoard.hasCommentOnly(this.userId); return (
!currentBoard.hasAdmin(this.userId) &&
!currentBoard.hasNoComments(this.userId) &&
!currentBoard.hasCommentOnly(this.userId)
);
}, },
isNoComments() { isNoComments() {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
return !currentBoard.hasAdmin(this.userId) && currentBoard.hasNoComments(this.userId); return (
!currentBoard.hasAdmin(this.userId) &&
currentBoard.hasNoComments(this.userId)
);
}, },
isCommentOnly() { isCommentOnly() {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
return !currentBoard.hasAdmin(this.userId) && currentBoard.hasCommentOnly(this.userId); return (
!currentBoard.hasAdmin(this.userId) &&
currentBoard.hasCommentOnly(this.userId)
);
}, },
isLastAdmin() { isLastAdmin() {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
return currentBoard.hasAdmin(this.userId) && (currentBoard.activeAdmins() === 1); return (
currentBoard.hasAdmin(this.userId) && currentBoard.activeAdmins() === 1
);
}, },
}); });

View file

@ -10,12 +10,11 @@ BlazeComponent.extendComponent({
// unfortunatly, Blaze doesn't have this notion. // unfortunatly, Blaze doesn't have this notion.
this.autorun(() => { this.autorun(() => {
const currentBoardId = Session.get('currentBoard'); const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) if (!currentBoardId) return;
return;
const handle = subManager.subscribe('board', currentBoardId, true); const handle = subManager.subscribe('board', currentBoardId, true);
Tracker.nonreactive(() => { Tracker.nonreactive(() => {
Tracker.autorun(() => { Tracker.autorun(() => {
this.isArchiveReady.set( handle.ready() ); this.isArchiveReady.set(handle.ready());
}); });
}); });
}); });
@ -59,74 +58,82 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-restore-card'() { {
const card = this.currentData(); 'click .js-restore-card'() {
if(card.canBeRestored()){ const card = this.currentData();
card.restore(); if (card.canBeRestored()) {
}
},
'click .js-restore-all-cards'() {
this.archivedCards().forEach((card) => {
if(card.canBeRestored()){
card.restore(); card.restore();
} }
}); },
}, 'click .js-restore-all-cards'() {
this.archivedCards().forEach(card => {
if (card.canBeRestored()) {
card.restore();
}
});
},
'click .js-delete-card': Popup.afterConfirm('cardDelete', function() { 'click .js-delete-card': Popup.afterConfirm('cardDelete', function() {
const cardId = this._id; const cardId = this._id;
Cards.remove(cardId); Cards.remove(cardId);
Popup.close(); Popup.close();
}), }),
'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', () => { 'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', () => {
this.archivedCards().forEach((card) => { this.archivedCards().forEach(card => {
Cards.remove(card._id); Cards.remove(card._id);
}); });
Popup.close(); Popup.close();
}), }),
'click .js-restore-list'() { 'click .js-restore-list'() {
const list = this.currentData(); const list = this.currentData();
list.restore();
},
'click .js-restore-all-lists'() {
this.archivedLists().forEach((list) => {
list.restore(); list.restore();
}); },
}, 'click .js-restore-all-lists'() {
this.archivedLists().forEach(list => {
list.restore();
});
},
'click .js-delete-list': Popup.afterConfirm('listDelete', function() { 'click .js-delete-list': Popup.afterConfirm('listDelete', function() {
this.remove(); this.remove();
Popup.close(); Popup.close();
}), }),
'click .js-delete-all-lists': Popup.afterConfirm('listDelete', () => { 'click .js-delete-all-lists': Popup.afterConfirm('listDelete', () => {
this.archivedLists().forEach((list) => { this.archivedLists().forEach(list => {
list.remove(); list.remove();
}); });
Popup.close(); Popup.close();
}), }),
'click .js-restore-swimlane'() { 'click .js-restore-swimlane'() {
const swimlane = this.currentData(); const swimlane = this.currentData();
swimlane.restore();
},
'click .js-restore-all-swimlanes'() {
this.archivedSwimlanes().forEach((swimlane) => {
swimlane.restore(); swimlane.restore();
}); },
}, 'click .js-restore-all-swimlanes'() {
this.archivedSwimlanes().forEach(swimlane => {
swimlane.restore();
});
},
'click .js-delete-swimlane': Popup.afterConfirm('swimlaneDelete', function() { 'click .js-delete-swimlane': Popup.afterConfirm(
this.remove(); 'swimlaneDelete',
Popup.close(); function() {
}), this.remove();
'click .js-delete-all-swimlanes': Popup.afterConfirm('swimlaneDelete', () => { Popup.close();
this.archivedSwimlanes().forEach((swimlane) => { },
swimlane.remove(); ),
}); 'click .js-delete-all-swimlanes': Popup.afterConfirm(
Popup.close(); 'swimlaneDelete',
}), () => {
}]; this.archivedSwimlanes().forEach(swimlane => {
swimlane.remove();
});
Popup.close();
},
),
},
];
}, },
}).register('archivesSidebar'); }).register('archivesSidebar');

View file

@ -1,37 +1,43 @@
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
customFields() { customFields() {
return CustomFields.find({ return CustomFields.find({
boardIds: {$in: [Session.get('currentBoard')]}, boardIds: { $in: [Session.get('currentBoard')] },
}); });
}, },
events() { events() {
return [{ return [
'click .js-open-create-custom-field': Popup.open('createCustomField'), {
'click .js-edit-custom-field': Popup.open('editCustomField'), 'click .js-open-create-custom-field': Popup.open('createCustomField'),
}]; 'click .js-edit-custom-field': Popup.open('editCustomField'),
},
];
}, },
}).register('customFieldsSidebar'); }).register('customFieldsSidebar');
const CreateCustomFieldPopup = BlazeComponent.extendComponent({ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
_types: ['text', 'number', 'date', 'dropdown'], _types: ['text', 'number', 'date', 'dropdown'],
onCreated() { onCreated() {
this.type = new ReactiveVar((this.data().type) ? this.data().type : this._types[0]); this.type = new ReactiveVar(
this.dropdownItems = new ReactiveVar((this.data().settings && this.data().settings.dropdownItems) ? this.data().settings.dropdownItems : []); this.data().type ? this.data().type : this._types[0],
);
this.dropdownItems = new ReactiveVar(
this.data().settings && this.data().settings.dropdownItems
? this.data().settings.dropdownItems
: [],
);
}, },
types() { types() {
const currentType = this.data().type; const currentType = this.data().type;
return this._types. return this._types.map(type => {
map((type) => {return { return {
value: type, value: type,
name: TAPi18n.__(`custom-field-${type}`), name: TAPi18n.__(`custom-field-${type}`),
selected: type === currentType, selected: type === currentType,
};}); };
});
}, },
isTypeNotSelected(type) { isTypeNotSelected(type) {
@ -40,113 +46,122 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
getDropdownItems() { getDropdownItems() {
const items = this.dropdownItems.get(); const items = this.dropdownItems.get();
Array.from(this.findAll('.js-field-settings-dropdown input')).forEach((el, index) => { Array.from(this.findAll('.js-field-settings-dropdown input')).forEach(
//console.log('each item!', index, el.value); (el, index) => {
if (!items[index]) items[index] = { //console.log('each item!', index, el.value);
_id: Random.id(6), if (!items[index])
}; items[index] = {
items[index].name = el.value.trim(); _id: Random.id(6),
}); };
items[index].name = el.value.trim();
},
);
return items; return items;
}, },
getSettings() { getSettings() {
const settings = {}; const settings = {};
switch (this.type.get()) { switch (this.type.get()) {
case 'dropdown': { case 'dropdown': {
const dropdownItems = this.getDropdownItems().filter((item) => !!item.name.trim()); const dropdownItems = this.getDropdownItems().filter(
settings.dropdownItems = dropdownItems; item => !!item.name.trim(),
break; );
} settings.dropdownItems = dropdownItems;
break;
}
} }
return settings; return settings;
}, },
events() { events() {
return [{ return [
'change .js-field-type'(evt) { {
const value = evt.target.value; 'change .js-field-type'(evt) {
this.type.set(value); const value = evt.target.value;
}, this.type.set(value);
'keydown .js-dropdown-item.last'(evt) { },
if (evt.target.value.trim() && evt.keyCode === 13) { 'keydown .js-dropdown-item.last'(evt) {
const items = this.getDropdownItems(); if (evt.target.value.trim() && evt.keyCode === 13) {
this.dropdownItems.set(items); const items = this.getDropdownItems();
evt.target.value = ''; this.dropdownItems.set(items);
} evt.target.value = '';
}, }
'click .js-field-show-on-card'(evt) { },
let $target = $(evt.target); 'click .js-field-show-on-card'(evt) {
if(!$target.hasClass('js-field-show-on-card')){ let $target = $(evt.target);
$target = $target.parent(); if (!$target.hasClass('js-field-show-on-card')) {
} $target = $target.parent();
$target.find('.materialCheckBox').toggleClass('is-checked'); }
$target.toggleClass('is-checked'); $target.find('.materialCheckBox').toggleClass('is-checked');
}, $target.toggleClass('is-checked');
'click .js-field-automatically-on-card'(evt) { },
let $target = $(evt.target); 'click .js-field-automatically-on-card'(evt) {
if(!$target.hasClass('js-field-automatically-on-card')){ let $target = $(evt.target);
$target = $target.parent(); if (!$target.hasClass('js-field-automatically-on-card')) {
} $target = $target.parent();
$target.find('.materialCheckBox').toggleClass('is-checked'); }
$target.toggleClass('is-checked'); $target.find('.materialCheckBox').toggleClass('is-checked');
}, $target.toggleClass('is-checked');
'click .js-field-showLabel-on-card'(evt) { },
let $target = $(evt.target); 'click .js-field-showLabel-on-card'(evt) {
if(!$target.hasClass('js-field-showLabel-on-card')){ let $target = $(evt.target);
$target = $target.parent(); if (!$target.hasClass('js-field-showLabel-on-card')) {
} $target = $target.parent();
$target.find('.materialCheckBox').toggleClass('is-checked'); }
$target.toggleClass('is-checked'); $target.find('.materialCheckBox').toggleClass('is-checked');
}, $target.toggleClass('is-checked');
'click .primary'(evt) { },
evt.preventDefault(); 'click .primary'(evt) {
evt.preventDefault();
const data = { const data = {
name: this.find('.js-field-name').value.trim(), name: this.find('.js-field-name').value.trim(),
type: this.type.get(), type: this.type.get(),
settings: this.getSettings(), settings: this.getSettings(),
showOnCard: this.find('.js-field-show-on-card.is-checked') !== null, showOnCard: this.find('.js-field-show-on-card.is-checked') !== null,
showLabelOnMiniCard: this.find('.js-field-showLabel-on-card.is-checked') !== null, showLabelOnMiniCard:
automaticallyOnCard: this.find('.js-field-automatically-on-card.is-checked') !== null, this.find('.js-field-showLabel-on-card.is-checked') !== null,
}; automaticallyOnCard:
this.find('.js-field-automatically-on-card.is-checked') !== null,
};
// insert or update // insert or update
if (!this.data()._id) { if (!this.data()._id) {
data.boardIds = [Session.get('currentBoard')]; data.boardIds = [Session.get('currentBoard')];
CustomFields.insert(data); CustomFields.insert(data);
} else { } else {
CustomFields.update(this.data()._id, {$set: data}); CustomFields.update(this.data()._id, { $set: data });
} }
Popup.back(); Popup.back();
},
'click .js-delete-custom-field': Popup.afterConfirm(
'deleteCustomField',
function() {
const customField = CustomFields.findOne(this._id);
if (customField.boardIds.length > 1) {
CustomFields.update(customField._id, {
$pull: {
boardIds: Session.get('currentBoard'),
},
});
} else {
CustomFields.remove(customField._id);
}
Popup.close();
},
),
}, },
'click .js-delete-custom-field': Popup.afterConfirm('deleteCustomField', function() { ];
const customField = CustomFields.findOne(this._id);
if (customField.boardIds.length > 1) {
CustomFields.update(customField._id, {
$pull: {
boardIds: Session.get('currentBoard'),
},
});
} else {
CustomFields.remove(customField._id);
}
Popup.close();
}),
}];
}, },
}); });
CreateCustomFieldPopup.register('createCustomFieldPopup'); CreateCustomFieldPopup.register('createCustomFieldPopup');
(class extends CreateCustomFieldPopup { (class extends CreateCustomFieldPopup {
template() { template() {
return 'createCustomFieldPopup'; return 'createCustomFieldPopup';
} }
}.register('editCustomFieldPopup'));
}).register('editCustomFieldPopup');
/*Template.deleteCustomFieldPopup.events({ /*Template.deleteCustomFieldPopup.events({
'submit'(evt) { 'submit'(evt) {

View file

@ -1,106 +1,108 @@
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
events() { events() {
return [{ return [
'click .js-toggle-label-filter'(evt) { {
evt.preventDefault(); 'click .js-toggle-label-filter'(evt) {
Filter.labelIds.toggle(this.currentData()._id); evt.preventDefault();
Filter.resetExceptions(); Filter.labelIds.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-member-filter'(evt) {
evt.preventDefault();
Filter.members.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-custom-fields-filter'(evt) {
evt.preventDefault();
Filter.customFields.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'change .js-field-advanced-filter'(evt) {
evt.preventDefault();
Filter.advanced.set(
this.find('.js-field-advanced-filter').value.trim(),
);
Filter.resetExceptions();
},
'click .js-clear-all'(evt) {
evt.preventDefault();
Filter.reset();
},
'click .js-filter-to-selection'(evt) {
evt.preventDefault();
const selectedCards = Cards.find(Filter.mongoSelector()).map(c => {
return c._id;
});
MultiSelection.add(selectedCards);
},
}, },
'click .js-toggle-member-filter'(evt) { ];
evt.preventDefault();
Filter.members.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-custom-fields-filter'(evt) {
evt.preventDefault();
Filter.customFields.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'change .js-field-advanced-filter'(evt) {
evt.preventDefault();
Filter.advanced.set(this.find('.js-field-advanced-filter').value.trim());
Filter.resetExceptions();
},
'click .js-clear-all'(evt) {
evt.preventDefault();
Filter.reset();
},
'click .js-filter-to-selection'(evt) {
evt.preventDefault();
const selectedCards = Cards.find(Filter.mongoSelector()).map((c) => {
return c._id;
});
MultiSelection.add(selectedCards);
},
}];
}, },
}).register('filterSidebar'); }).register('filterSidebar');
function mutateSelectedCards(mutationName, ...args) { function mutateSelectedCards(mutationName, ...args) {
Cards.find(MultiSelection.getMongoSelector()).forEach((card) => { Cards.find(MultiSelection.getMongoSelector()).forEach(card => {
card[mutationName](...args); card[mutationName](...args);
}); });
} }
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
mapSelection(kind, _id) { mapSelection(kind, _id) {
return Cards.find(MultiSelection.getMongoSelector()).map((card) => { return Cards.find(MultiSelection.getMongoSelector()).map(card => {
const methodName = kind === 'label' ? 'hasLabel' : 'isAssigned'; const methodName = kind === 'label' ? 'hasLabel' : 'isAssigned';
return card[methodName](_id); return card[methodName](_id);
}); });
}, },
allSelectedElementHave(kind, _id) { allSelectedElementHave(kind, _id) {
if (MultiSelection.isEmpty()) if (MultiSelection.isEmpty()) return false;
return false; else return _.every(this.mapSelection(kind, _id));
else
return _.every(this.mapSelection(kind, _id));
}, },
someSelectedElementHave(kind, _id) { someSelectedElementHave(kind, _id) {
if (MultiSelection.isEmpty()) if (MultiSelection.isEmpty()) return false;
return false; else return _.some(this.mapSelection(kind, _id));
else
return _.some(this.mapSelection(kind, _id));
}, },
events() { events() {
return [{ return [
'click .js-toggle-label-multiselection'(evt) { {
const labelId = this.currentData()._id; 'click .js-toggle-label-multiselection'(evt) {
const mappedSelection = this.mapSelection('label', labelId); const labelId = this.currentData()._id;
const mappedSelection = this.mapSelection('label', labelId);
if (_.every(mappedSelection)) { if (_.every(mappedSelection)) {
mutateSelectedCards('removeLabel', labelId); mutateSelectedCards('removeLabel', labelId);
} else if (_.every(mappedSelection, (bool) => !bool)) { } else if (_.every(mappedSelection, bool => !bool)) {
mutateSelectedCards('addLabel', labelId); mutateSelectedCards('addLabel', labelId);
} else { } else {
const popup = Popup.open('disambiguateMultiLabel'); const popup = Popup.open('disambiguateMultiLabel');
// XXX We need to have a better integration between the popup and the // XXX We need to have a better integration between the popup and the
// UI components systems. // UI components systems.
popup.call(this.currentData(), evt); popup.call(this.currentData(), evt);
} }
},
'click .js-toggle-member-multiselection'(evt) {
const memberId = this.currentData()._id;
const mappedSelection = this.mapSelection('member', memberId);
if (_.every(mappedSelection)) {
mutateSelectedCards('unassignMember', memberId);
} else if (_.every(mappedSelection, bool => !bool)) {
mutateSelectedCards('assignMember', memberId);
} else {
const popup = Popup.open('disambiguateMultiMember');
// XXX We need to have a better integration between the popup and the
// UI components systems.
popup.call(this.currentData(), evt);
}
},
'click .js-move-selection': Popup.open('moveSelection'),
'click .js-archive-selection'() {
mutateSelectedCards('archive');
EscapeActions.executeUpTo('multiselection');
},
}, },
'click .js-toggle-member-multiselection'(evt) { ];
const memberId = this.currentData()._id;
const mappedSelection = this.mapSelection('member', memberId);
if (_.every(mappedSelection)) {
mutateSelectedCards('unassignMember', memberId);
} else if (_.every(mappedSelection, (bool) => !bool)) {
mutateSelectedCards('assignMember', memberId);
} else {
const popup = Popup.open('disambiguateMultiMember');
// XXX We need to have a better integration between the popup and the
// UI components systems.
popup.call(this.currentData(), evt);
}
},
'click .js-move-selection': Popup.open('moveSelection'),
'click .js-archive-selection'() {
mutateSelectedCards('archive');
EscapeActions.executeUpTo('multiselection');
},
}];
}, },
}).register('multiselectionSidebar'); }).register('multiselectionSidebar');

View file

@ -9,11 +9,13 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'submit .js-search-term-form'(evt) { {
evt.preventDefault(); 'submit .js-search-term-form'(evt) {
this.term.set(evt.target.searchTerm.value); evt.preventDefault();
this.term.set(evt.target.searchTerm.value);
},
}, },
}]; ];
}, },
}).register('searchSidebar'); }).register('searchSidebar');

View file

@ -6,9 +6,11 @@ Meteor.startup(() => {
}); });
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
editTitle(evt) { editTitle(event) {
evt.preventDefault(); event.preventDefault();
const newTitle = this.childComponents('inlinedForm')[0].getValue().trim(); const newTitle = this.childComponents('inlinedForm')[0]
.getValue()
.trim();
const swimlane = this.currentData(); const swimlane = this.currentData();
if (newTitle) { if (newTitle) {
swimlane.rename(newTitle.trim()); swimlane.rename(newTitle.trim());
@ -16,18 +18,20 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-open-swimlane-menu': Popup.open('swimlaneAction'), {
'click .js-open-add-swimlane-menu': Popup.open('swimlaneAdd'), 'click .js-open-swimlane-menu': Popup.open('swimlaneAction'),
submit: this.editTitle, 'click .js-open-add-swimlane-menu': Popup.open('swimlaneAdd'),
}]; submit: this.editTitle,
},
];
}, },
}).register('swimlaneHeader'); }).register('swimlaneHeader');
Template.swimlaneActionPopup.events({ Template.swimlaneActionPopup.events({
'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'), 'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
'click .js-close-swimlane' (evt) { 'click .js-close-swimlane'(event) {
evt.preventDefault(); event.preventDefault();
this.archive(); this.archive();
Popup.close(); Popup.close();
}, },
@ -39,34 +43,42 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
submit(evt) { {
evt.preventDefault(); submit(event) {
const currentBoard = Boards.findOne(Session.get('currentBoard')); event.preventDefault();
const nextSwimlane = currentBoard.nextSwimlane(this.currentSwimlane); const currentBoard = Boards.findOne(Session.get('currentBoard'));
const titleInput = this.find('.swimlane-name-input'); const nextSwimlane = currentBoard.nextSwimlane(this.currentSwimlane);
const title = titleInput.value.trim(); const titleInput = this.find('.swimlane-name-input');
const sortValue = calculateIndexData(this.currentSwimlane, nextSwimlane, 1); const title = titleInput.value.trim();
const swimlaneType = (currentBoard.isTemplatesBoard())?'template-swimlane':'swimlane'; const sortValue = calculateIndexData(
this.currentSwimlane,
nextSwimlane,
1,
);
const swimlaneType = currentBoard.isTemplatesBoard()
? 'template-swimlane'
: 'swimlane';
if (title) { if (title) {
Swimlanes.insert({ Swimlanes.insert({
title, title,
boardId: Session.get('currentBoard'), boardId: Session.get('currentBoard'),
sort: sortValue.base, sort: sortValue.base,
type: swimlaneType, type: swimlaneType,
}); });
titleInput.value = ''; titleInput.value = '';
titleInput.focus(); titleInput.focus();
} }
// XXX ideally, we should move the popup to the newly // XXX ideally, we should move the popup to the newly
// created swimlane so a user can add more than one swimlane // created swimlane so a user can add more than one swimlane
// with a minimum of interactions // with a minimum of interactions
Popup.close(); Popup.close();
},
'click .js-swimlane-template': Popup.open('searchElement'),
}, },
'click .js-swimlane-template': Popup.open('searchElement'), ];
}];
}, },
}).register('swimlaneAddPopup'); }).register('swimlaneAddPopup');
@ -77,7 +89,7 @@ BlazeComponent.extendComponent({
}, },
colors() { colors() {
return swimlaneColors.map((color) => ({ color, name: '' })); return swimlaneColors.map(color => ({ color, name: '' }));
}, },
isSelected(color) { isSelected(color) {
@ -85,18 +97,20 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-palette-color'() { {
this.currentColor.set(this.currentData().color); 'click .js-palette-color'() {
this.currentColor.set(this.currentData().color);
},
'click .js-submit'() {
this.currentSwimlane.setColor(this.currentColor.get());
Popup.close();
},
'click .js-remove-color'() {
this.currentSwimlane.setColor(null);
Popup.close();
},
}, },
'click .js-submit' () { ];
this.currentSwimlane.setColor(this.currentColor.get());
Popup.close();
},
'click .js-remove-color'() {
this.currentSwimlane.setColor(null);
Popup.close();
},
}];
}, },
}).register('setSwimlaneColorPopup'); }).register('setSwimlaneColorPopup');

View file

@ -2,16 +2,27 @@ const { calculateIndex, enableClickOnTouch } = Utils;
function currentListIsInThisSwimlane(swimlaneId) { function currentListIsInThisSwimlane(swimlaneId) {
const currentList = Lists.findOne(Session.get('currentList')); const currentList = Lists.findOne(Session.get('currentList'));
return currentList && (currentList.swimlaneId === swimlaneId || currentList.swimlaneId === ''); return (
currentList &&
(currentList.swimlaneId === swimlaneId || currentList.swimlaneId === '')
);
} }
function currentCardIsInThisList(listId, swimlaneId) { function currentCardIsInThisList(listId, swimlaneId) {
const currentCard = Cards.findOne(Session.get('currentCard')); const currentCard = Cards.findOne(Session.get('currentCard'));
const currentUser = Meteor.user(); const currentUser = Meteor.user();
if (currentUser && currentUser.profile && currentUser.profile.boardView === 'board-view-swimlanes') if (
return currentCard && currentCard.listId === listId && currentCard.swimlaneId === swimlaneId; currentUser &&
else // Default view: board-view-lists currentUser.profile &&
return currentCard && currentCard.listId === listId; currentUser.profile.boardView === 'board-view-swimlanes'
)
return (
currentCard &&
currentCard.listId === listId &&
currentCard.swimlaneId === swimlaneId
);
// Default view: board-view-lists
else return currentCard && currentCard.listId === listId;
// https://github.com/wekan/wekan/issues/1623 // https://github.com/wekan/wekan/issues/1623
// https://github.com/ChronikEwok/wekan/commit/cad9b20451bb6149bfb527a99b5001873b06c3de // https://github.com/ChronikEwok/wekan/commit/cad9b20451bb6149bfb527a99b5001873b06c3de
// TODO: In public board, if you would like to switch between List/Swimlane view, you could // TODO: In public board, if you would like to switch between List/Swimlane view, you could
@ -79,7 +90,11 @@ function initSortable(boardComponent, $listsDom) {
enableClickOnTouch('.js-list:not(.js-list-composer)'); enableClickOnTouch('.js-list:not(.js-list-composer)');
function userIsMember() { function userIsMember() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
} }
// Disable drag-dropping while in multi-selection mode, or if the current user // Disable drag-dropping while in multi-selection mode, or if the current user
@ -87,8 +102,11 @@ function initSortable(boardComponent, $listsDom) {
boardComponent.autorun(() => { boardComponent.autorun(() => {
const $listDom = $listsDom; const $listDom = $listsDom;
if ($listDom.data('sortable')) { if ($listDom.data('sortable')) {
$listsDom.sortable('option', 'disabled', $listsDom.sortable(
MultiSelection.isActive() || !userIsMember()); 'option',
'disabled',
MultiSelection.isActive() || !userIsMember(),
);
} }
}); });
} }
@ -124,47 +142,60 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
// Click-and-drag action {
'mousedown .board-canvas'(evt) { // Click-and-drag action
// Translating the board canvas using the click-and-drag action can 'mousedown .board-canvas'(evt) {
// conflict with the build-in browser mechanism to select text. We // Translating the board canvas using the click-and-drag action can
// define a list of elements in which we disable the dragging because // conflict with the build-in browser mechanism to select text. We
// the user will legitimately expect to be able to select some text with // define a list of elements in which we disable the dragging because
// his mouse. // the user will legitimately expect to be able to select some text with
const noDragInside = ['a', 'input', 'textarea', 'p', '.js-list-header']; // his mouse.
if ($(evt.target).closest(noDragInside.join(',')).length === 0 && this.$('.swimlane').prop('clientHeight') > evt.offsetY) { const noDragInside = [
this._isDragging = true; 'a',
this._lastDragPositionX = evt.clientX; 'input',
} 'textarea',
'p',
'.js-list-header',
];
if (
$(evt.target).closest(noDragInside.join(',')).length === 0 &&
this.$('.swimlane').prop('clientHeight') > evt.offsetY
) {
this._isDragging = true;
this._lastDragPositionX = evt.clientX;
}
},
mouseup() {
if (this._isDragging) {
this._isDragging = false;
}
},
mousemove(evt) {
if (this._isDragging) {
// Update the canvas position
this.listsDom.scrollLeft -= evt.clientX - this._lastDragPositionX;
this._lastDragPositionX = evt.clientX;
// Disable browser text selection while dragging
evt.stopPropagation();
evt.preventDefault();
// Don't close opened card or inlined form at the end of the
// click-and-drag.
EscapeActions.executeUpTo('popup-close');
EscapeActions.preventNextClick();
}
},
}, },
'mouseup'() { ];
if (this._isDragging) {
this._isDragging = false;
}
},
'mousemove'(evt) {
if (this._isDragging) {
// Update the canvas position
this.listsDom.scrollLeft -= evt.clientX - this._lastDragPositionX;
this._lastDragPositionX = evt.clientX;
// Disable browser text selection while dragging
evt.stopPropagation();
evt.preventDefault();
// Don't close opened card or inlined form at the end of the
// click-and-drag.
EscapeActions.executeUpTo('popup-close');
EscapeActions.preventNextClick();
}
},
}];
}, },
}).register('swimlane'); }).register('swimlane');
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
onCreated() { onCreated() {
this.currentBoard = Boards.findOne(Session.get('currentBoard')); this.currentBoard = Boards.findOne(Session.get('currentBoard'));
this.isListTemplatesSwimlane = this.currentBoard.isTemplatesBoard() && this.currentData().isListTemplatesSwimlane(); this.isListTemplatesSwimlane =
this.currentBoard.isTemplatesBoard() &&
this.currentData().isListTemplatesSwimlane();
this.currentSwimlane = this.currentData(); this.currentSwimlane = this.currentData();
}, },
@ -174,32 +205,40 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
submit(evt) { {
evt.preventDefault(); submit(evt) {
const titleInput = this.find('.list-name-input'); evt.preventDefault();
const title = titleInput.value.trim(); const titleInput = this.find('.list-name-input');
if (title) { const title = titleInput.value.trim();
Lists.insert({ if (title) {
title, Lists.insert({
boardId: Session.get('currentBoard'), title,
sort: $('.list').length, boardId: Session.get('currentBoard'),
type: (this.isListTemplatesSwimlane)?'template-list':'list', sort: $('.list').length,
swimlaneId: (this.currentBoard.isTemplatesBoard())?this.currentSwimlane._id:'', type: this.isListTemplatesSwimlane ? 'template-list' : 'list',
}); swimlaneId: this.currentBoard.isTemplatesBoard()
? this.currentSwimlane._id
: '',
});
titleInput.value = ''; titleInput.value = '';
titleInput.focus(); titleInput.focus();
} }
},
'click .js-list-template': Popup.open('searchElement'),
}, },
'click .js-list-template': Popup.open('searchElement'), ];
}];
}, },
}).register('addListForm'); }).register('addListForm');
Template.swimlane.helpers({ Template.swimlane.helpers({
canSeeAddList() { canSeeAddList() {
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly()
);
}, },
}); });

View file

@ -22,14 +22,11 @@ Template.userAvatar.helpers({
presenceStatusClassName() { presenceStatusClassName() {
const user = Users.findOne(this.userId); const user = Users.findOne(this.userId);
const userPresence = presences.findOne({ userId: this.userId }); const userPresence = presences.findOne({ userId: this.userId });
if (user && user.isInvitedTo(Session.get('currentBoard'))) if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
return 'pending'; else if (!userPresence) return 'disconnected';
else if (!userPresence)
return 'disconnected';
else if (Session.equals('currentBoard', userPresence.state.currentBoardId)) else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
return 'active'; return 'active';
else else return 'idle';
return 'idle';
}, },
}); });
@ -45,7 +42,7 @@ Template.userAvatarInitials.helpers({
viewPortWidth() { viewPortWidth() {
const user = Users.findOne(this.userId); const user = Users.findOne(this.userId);
return (user && user.getInitials().length || 1) * 12; return ((user && user.getInitials().length) || 1) * 12;
}, },
}); });
@ -64,7 +61,7 @@ BlazeComponent.extendComponent({
}, },
uploadedAvatars() { uploadedAvatars() {
return Avatars.find({userId: Meteor.userId()}); return Avatars.find({ userId: Meteor.userId() });
}, },
isSelected() { isSelected() {
@ -89,46 +86,48 @@ BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-upload-avatar'() { {
this.$('.js-upload-avatar-input').click(); 'click .js-upload-avatar'() {
}, this.$('.js-upload-avatar-input').click();
'change .js-upload-avatar-input'(evt) { },
let file, fileUrl; 'change .js-upload-avatar-input'(event) {
let file, fileUrl;
FS.Utility.eachFile(evt, (f) => { FS.Utility.eachFile(event, f => {
try { try {
file = Avatars.insert(new FS.File(f)); file = Avatars.insert(new FS.File(f));
fileUrl = file.url(this.avatarUrlOptions()); fileUrl = file.url(this.avatarUrlOptions());
} catch (e) { } catch (e) {
this.setError('avatar-too-big'); this.setError('avatar-too-big');
}
});
if (fileUrl) {
this.setError('');
const fetchAvatarInterval = window.setInterval(() => {
$.ajax({
url: fileUrl,
success: () => {
this.setAvatar(file.url(this.avatarUrlOptions()));
window.clearInterval(fetchAvatarInterval);
},
});
}, 100);
} }
}); },
'click .js-select-avatar'() {
if (fileUrl) { const avatarUrl = this.currentData().url(this.avatarUrlOptions());
this.setError(''); this.setAvatar(avatarUrl);
const fetchAvatarInterval = window.setInterval(() => { },
$.ajax({ 'click .js-select-initials'() {
url: fileUrl, this.setAvatar('');
success: () => { },
this.setAvatar(file.url(this.avatarUrlOptions())); 'click .js-delete-avatar'() {
window.clearInterval(fetchAvatarInterval); Avatars.remove(this.currentData()._id);
}, },
});
}, 100);
}
}, },
'click .js-select-avatar'() { ];
const avatarUrl = this.currentData().url(this.avatarUrlOptions());
this.setAvatar(avatarUrl);
},
'click .js-select-initials'() {
this.setAvatar('');
},
'click .js-delete-avatar'() {
Avatars.remove(this.currentData()._id);
},
}];
}, },
}).register('changeAvatarPopup'); }).register('changeAvatarPopup');
@ -146,11 +145,11 @@ Template.cardMembersPopup.helpers({
}); });
Template.cardMembersPopup.events({ Template.cardMembersPopup.events({
'click .js-select-member'(evt) { 'click .js-select-member'(event) {
const card = Cards.findOne(Session.get('currentCard')); const card = Cards.findOne(Session.get('currentCard'));
const memberId = this.userId; const memberId = this.userId;
card.toggleMember(memberId); card.toggleMember(memberId);
evt.preventDefault(); event.preventDefault();
}, },
}); });

View file

@ -18,8 +18,8 @@ Template.memberMenuPopup.events({
'click .js-change-avatar': Popup.open('changeAvatar'), 'click .js-change-avatar': Popup.open('changeAvatar'),
'click .js-change-password': Popup.open('changePassword'), 'click .js-change-password': Popup.open('changePassword'),
'click .js-change-language': Popup.open('changeLanguage'), 'click .js-change-language': Popup.open('changeLanguage'),
'click .js-logout'(evt) { 'click .js-logout'(event) {
evt.preventDefault(); event.preventDefault();
AccountsTemplates.logout(); AccountsTemplates.logout();
}, },
@ -38,12 +38,12 @@ Template.editProfilePopup.helpers({
}); });
Template.editProfilePopup.events({ Template.editProfilePopup.events({
submit(evt, tpl) { submit(event, templateInstance) {
evt.preventDefault(); event.preventDefault();
const fullname = tpl.find('.js-profile-fullname').value.trim(); const fullname = templateInstance.find('.js-profile-fullname').value.trim();
const username = tpl.find('.js-profile-username').value.trim(); const username = templateInstance.find('.js-profile-username').value.trim();
const initials = tpl.find('.js-profile-initials').value.trim(); const initials = templateInstance.find('.js-profile-initials').value.trim();
const email = tpl.find('.js-profile-email').value.trim(); const email = templateInstance.find('.js-profile-email').value.trim();
let isChangeUserName = false; let isChangeUserName = false;
let isChangeEmail = false; let isChangeEmail = false;
Users.update(Meteor.userId(), { Users.update(Meteor.userId(), {
@ -53,29 +53,36 @@ Template.editProfilePopup.events({
}, },
}); });
isChangeUserName = username !== Meteor.user().username; isChangeUserName = username !== Meteor.user().username;
isChangeEmail = email.toLowerCase() !== Meteor.user().emails[0].address.toLowerCase(); isChangeEmail =
email.toLowerCase() !== Meteor.user().emails[0].address.toLowerCase();
if (isChangeUserName && isChangeEmail) { if (isChangeUserName && isChangeEmail) {
Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), Meteor.userId(), function (error) { Meteor.call(
const usernameMessageElement = tpl.$('.username-taken'); 'setUsernameAndEmail',
const emailMessageElement = tpl.$('.email-taken'); username,
if (error) { email.toLowerCase(),
const errorElement = error.error; Meteor.userId(),
if (errorElement === 'username-already-taken') { function(error) {
usernameMessageElement.show(); const usernameMessageElement = templateInstance.$('.username-taken');
emailMessageElement.hide(); const emailMessageElement = templateInstance.$('.email-taken');
} else if (errorElement === 'email-already-taken') { if (error) {
const errorElement = error.error;
if (errorElement === 'username-already-taken') {
usernameMessageElement.show();
emailMessageElement.hide();
} else if (errorElement === 'email-already-taken') {
usernameMessageElement.hide();
emailMessageElement.show();
}
} else {
usernameMessageElement.hide(); usernameMessageElement.hide();
emailMessageElement.show(); emailMessageElement.hide();
Popup.back();
} }
} else { },
usernameMessageElement.hide(); );
emailMessageElement.hide();
Popup.back();
}
});
} else if (isChangeUserName) { } else if (isChangeUserName) {
Meteor.call('setUsername', username, Meteor.userId(), function (error) { Meteor.call('setUsername', username, Meteor.userId(), function(error) {
const messageElement = tpl.$('.username-taken'); const messageElement = templateInstance.$('.username-taken');
if (error) { if (error) {
messageElement.show(); messageElement.show();
} else { } else {
@ -84,8 +91,10 @@ Template.editProfilePopup.events({
} }
}); });
} else if (isChangeEmail) { } else if (isChangeEmail) {
Meteor.call('setEmail', email.toLowerCase(), Meteor.userId(), function (error) { Meteor.call('setEmail', email.toLowerCase(), Meteor.userId(), function(
const messageElement = tpl.$('.email-taken'); error,
) {
const messageElement = templateInstance.$('.email-taken');
if (error) { if (error) {
messageElement.show(); messageElement.show();
} else { } else {
@ -104,7 +113,7 @@ Template.editProfilePopup.events({
// XXX For some reason the useraccounts autofocus isnt working in this case. // XXX For some reason the useraccounts autofocus isnt working in this case.
// See https://github.com/meteor-useraccounts/core/issues/384 // See https://github.com/meteor-useraccounts/core/issues/384
Template.changePasswordPopup.onRendered(function () { Template.changePasswordPopup.onRendered(function() {
this.find('#at-field-current_password').focus(); this.find('#at-field-current_password').focus();
}); });
@ -123,7 +132,7 @@ Template.changeLanguagePopup.helpers({
name = 'Occitan'; name = 'Occitan';
} }
return { tag, name }; return { tag, name };
}).sort(function (a, b) { }).sort(function(a, b) {
if (a.name === b.name) { if (a.name === b.name) {
return 0; return 0;
} else { } else {
@ -138,13 +147,13 @@ Template.changeLanguagePopup.helpers({
}); });
Template.changeLanguagePopup.events({ Template.changeLanguagePopup.events({
'click .js-set-language'(evt) { 'click .js-set-language'(event) {
Users.update(Meteor.userId(), { Users.update(Meteor.userId(), {
$set: { $set: {
'profile.language': this.tag, 'profile.language': this.tag,
}, },
}); });
evt.preventDefault(); event.preventDefault();
}, },
}); });
@ -161,9 +170,12 @@ Template.changeSettingsPopup.events({
'click .js-toggle-system-messages'() { 'click .js-toggle-system-messages'() {
Meteor.call('toggleSystemMessages'); Meteor.call('toggleSystemMessages');
}, },
'click .js-apply-show-cards-at'(evt, tpl) { 'click .js-apply-show-cards-at'(event, templateInstance) {
evt.preventDefault(); event.preventDefault();
const minLimit = parseInt(tpl.$('#show-cards-count-at').val(), 10); const minLimit = parseInt(
templateInstance.$('#show-cards-count-at').val(),
10,
);
if (!isNaN(minLimit)) { if (!isNaN(minLimit)) {
Meteor.call('changeLimitToShowCardsCount', minLimit); Meteor.call('changeLimitToShowCardsCount', minLimit);
Popup.back(); Popup.back();

View file

@ -25,7 +25,7 @@ Blaze.registerHelper('currentList', () => {
} }
}); });
Blaze.registerHelper('getUser', (userId) => Users.findOne(userId)); Blaze.registerHelper('getUser', userId => Users.findOne(userId));
Blaze.registerHelper('concat', (...args) => args.slice(0, -1).join('')); Blaze.registerHelper('concat', (...args) => args.slice(0, -1).join(''));

View file

@ -27,10 +27,7 @@ function copyTitleInAriaLabel(attributes) {
// which is a little bit hacky -- but still reasonable with our ES6 usage. If we // which is a little bit hacky -- but still reasonable with our ES6 usage. If we
// end up switching to React we will probably create lower level small // end up switching to React we will probably create lower level small
// components to handle that without overwriting any build-in function. // components to handle that without overwriting any build-in function.
const { const { A: superA, I: superI } = HTML;
A: superA,
I: superI,
} = HTML;
HTML.A = (attributes, ...others) => { HTML.A = (attributes, ...others) => {
return superA(copyTitleInAriaLabel(enforceHref(attributes)), ...others); return superA(copyTitleInAriaLabel(enforceHref(attributes)), ...others);

View file

@ -3,11 +3,11 @@
function whichTransitionEvent() { function whichTransitionEvent() {
const el = document.createElement('fakeelement'); const el = document.createElement('fakeelement');
const transitions = { const transitions = {
transition:'transitionend', transition: 'transitionend',
OTransition:'oTransitionEnd', OTransition: 'oTransitionEnd',
MSTransition:'msTransitionEnd', MSTransition: 'msTransitionEnd',
MozTransition:'transitionend', MozTransition: 'transitionend',
WebkitTransition:'webkitTransitionEnd', WebkitTransition: 'webkitTransitionEnd',
}; };
for (const t in transitions) { for (const t in transitions) {
@ -21,11 +21,11 @@ function whichTransitionEvent() {
function whichAnimationEvent() { function whichAnimationEvent() {
const el = document.createElement('fakeelement'); const el = document.createElement('fakeelement');
const transitions = { const transitions = {
animation:'animationend', animation: 'animationend',
OAnimation:'oAnimationEnd', OAnimation: 'oAnimationEnd',
MSTransition:'msAnimationEnd', MSTransition: 'msAnimationEnd',
MozAnimation:'animationend', MozAnimation: 'animationend',
WebkitAnimation:'webkitAnimationEnd', WebkitAnimation: 'webkitAnimationEnd',
}; };
for (const t in transitions) { for (const t in transitions) {

View file

@ -10,15 +10,20 @@ DatePicker = BlazeComponent.extendComponent({
}, },
onRendered() { onRendered() {
const $picker = this.$('.js-datepicker').datepicker({ const $picker = this.$('.js-datepicker')
todayHighlight: true, .datepicker({
todayBtn: 'linked', todayHighlight: true,
language: TAPi18n.getLanguage(), todayBtn: 'linked',
}).on('changeDate', function(evt) { language: TAPi18n.getLanguage(),
this.find('#date').value = moment(evt.date).format('L'); })
this.error.set(''); .on(
this.find('#time').focus(); 'changeDate',
}.bind(this)); function(evt) {
this.find('#date').value = moment(evt.date).format('L');
this.error.set('');
this.find('#time').focus();
}.bind(this),
);
if (this.date.get().isValid()) { if (this.date.get().isValid()) {
$picker.datepicker('update', this.date.get().toDate()); $picker.datepicker('update', this.date.get().toDate());
@ -26,13 +31,11 @@ DatePicker = BlazeComponent.extendComponent({
}, },
showDate() { showDate() {
if (this.date.get().isValid()) if (this.date.get().isValid()) return this.date.get().format('L');
return this.date.get().format('L');
return ''; return '';
}, },
showTime() { showTime() {
if (this.date.get().isValid()) if (this.date.get().isValid()) return this.date.get().format('LT');
return this.date.get().format('LT');
return ''; return '';
}, },
dateFormat() { dateFormat() {
@ -43,44 +46,47 @@ DatePicker = BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'keyup .js-date-field'() { {
// parse for localized date format in strict mode 'keyup .js-date-field'() {
const dateMoment = moment(this.find('#date').value, 'L', true); // parse for localized date format in strict mode
if (dateMoment.isValid()) { const dateMoment = moment(this.find('#date').value, 'L', true);
this.error.set(''); if (dateMoment.isValid()) {
this.$('.js-datepicker').datepicker('update', dateMoment.toDate()); this.error.set('');
} this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
}, }
'keyup .js-time-field'() { },
// parse for localized time format in strict mode 'keyup .js-time-field'() {
const dateMoment = moment(this.find('#time').value, 'LT', true); // parse for localized time format in strict mode
if (dateMoment.isValid()) { const dateMoment = moment(this.find('#time').value, 'LT', true);
this.error.set(''); if (dateMoment.isValid()) {
} this.error.set('');
}, }
'submit .edit-date'(evt) { },
evt.preventDefault(); 'submit .edit-date'(evt) {
evt.preventDefault();
// if no time was given, init with 12:00 // if no time was given, init with 12:00
const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT'); const time =
evt.target.time.value ||
moment(new Date().setHours(12, 0, 0)).format('LT');
const dateString = `${evt.target.date.value} ${time}`; const dateString = `${evt.target.date.value} ${time}`;
const newDate = moment(dateString, 'L LT', true); const newDate = moment(dateString, 'L LT', true);
if (newDate.isValid()) { if (newDate.isValid()) {
this._storeDate(newDate.toDate()); this._storeDate(newDate.toDate());
Popup.close();
} else {
this.error.set('invalid-date');
evt.target.date.focus();
}
},
'click .js-delete-date'(evt) {
evt.preventDefault();
this._deleteDate();
Popup.close(); Popup.close();
} },
else {
this.error.set('invalid-date');
evt.target.date.focus();
}
}, },
'click .js-delete-date'(evt) { ];
evt.preventDefault();
this._deleteDate();
Popup.close();
},
}];
}, },
}); });

View file

@ -11,7 +11,10 @@
$.event.fix = (function(originalFix) { $.event.fix = (function(originalFix) {
return function(event) { return function(event) {
event = originalFix.apply(this, arguments); event = originalFix.apply(this, arguments);
if (event.type.indexOf('drag') === 0 || event.type.indexOf('drop') === 0) { if (
event.type.indexOf('drag') === 0 ||
event.type.indexOf('drop') === 0
) {
event.dataTransfer = event.originalEvent.dataTransfer; event.dataTransfer = event.originalEvent.dataTransfer;
} }
return event; return event;
@ -23,7 +26,7 @@
matchType: /image.*/, matchType: /image.*/,
}; };
return $.fn.dropImageReader = function(options) { return ($.fn.dropImageReader = function(options) {
if (typeof options === 'function') { if (typeof options === 'function') {
options = { options = {
callback: options, callback: options,
@ -40,9 +43,9 @@
return $(element).bind('drop', function(event) { return $(element).bind('drop', function(event) {
stopFn(event); stopFn(event);
const files = event.dataTransfer.files; const files = event.dataTransfer.files;
for(let i=0; i<files.length; i++) { for (let i = 0; i < files.length; i++) {
const f = files[i]; const f = files[i];
if(f.type.match(options.matchType)) { if (f.type.match(options.matchType)) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function(evt) { reader.onload = function(evt) {
return options.callback.call(element, { return options.callback.call(element, {
@ -58,5 +61,5 @@
} }
}); });
}); });
}; });
})(jQuery); })(jQuery);

View file

@ -33,13 +33,19 @@ EscapeActions = {
const noClickEscapeOn = options.noClickEscapeOn; const noClickEscapeOn = options.noClickEscapeOn;
this._actions = _.sortBy([...this._actions, { this._actions = _.sortBy(
priority, [
condition, ...this._actions,
action, {
noClickEscapeOn, priority,
enabledOnClick, condition,
}], (action) => action.priority); action,
noClickEscapeOn,
enabledOnClick,
},
],
action => action.priority,
);
}, },
executeLowest() { executeLowest() {
@ -80,10 +86,8 @@ EscapeActions = {
}, },
_stopClick(action, clickTarget) { _stopClick(action, clickTarget) {
if (!_.isString(action.noClickEscapeOn)) if (!_.isString(action.noClickEscapeOn)) return false;
return false; else return $(clickTarget).closest(action.noClickEscapeOn).length > 0;
else
return $(clickTarget).closest(action.noClickEscapeOn).length > 0;
}, },
_execute(options) { _execute(options) {
@ -95,14 +99,11 @@ EscapeActions = {
let executedAtLeastOne = false; let executedAtLeastOne = false;
let maxPriority; let maxPriority;
if (!maxLabel) if (!maxLabel) maxPriority = Infinity;
maxPriority = Infinity; else maxPriority = this.hierarchy.indexOf(maxLabel);
else
maxPriority = this.hierarchy.indexOf(maxLabel);
for (const currentAction of this._actions) { for (const currentAction of this._actions) {
if (currentAction.priority > maxPriority) if (currentAction.priority > maxPriority) return executedAtLeastOne;
return executedAtLeastOne;
if (isClick && this._stopClick(currentAction, clickTarget)) if (isClick && this._stopClick(currentAction, clickTarget))
return executedAtLeastOne; return executedAtLeastOne;
@ -111,8 +112,7 @@ EscapeActions = {
if (isEnabled && currentAction.condition()) { if (isEnabled && currentAction.condition()) {
currentAction.action(); currentAction.action();
executedAtLeastOne = true; executedAtLeastOne = true;
if (!multipleActions) if (!multipleActions) return executedAtLeastOne;
return executedAtLeastOne;
} }
} }
return executedAtLeastOne; return executedAtLeastOne;
@ -128,13 +128,15 @@ Mousetrap.bindGlobal('esc', () => {
// On a left click on the document, we try to exectute one escape action (eg, // On a left click on the document, we try to exectute one escape action (eg,
// close the popup). We don't execute any action if the user has clicked on a // close the popup). We don't execute any action if the user has clicked on a
// link or a button. // link or a button.
$(document).on('click', (evt) => { $(document).on('click', evt => {
if (evt.button === 0 && if (
$(evt.target).closest('a,button,.is-editable').length === 0) { evt.button === 0 &&
$(evt.target).closest('a,button,.is-editable').length === 0
) {
EscapeActions.clickExecute(evt.target, 'multiselection'); EscapeActions.clickExecute(evt.target, 'multiselection');
} }
}); });
$(document).on('click', 'a[href=\\#]', (evt) => { $(document).on('click', 'a[href=\\#]', evt => {
evt.preventDefault(); evt.preventDefault();
}); });

View file

@ -71,18 +71,19 @@ class SetFilter {
_getEmptySelector() { _getEmptySelector() {
this._dep.depend(); this._dep.depend();
let includeEmpty = false; let includeEmpty = false;
this._selectedElements.forEach((el) => { this._selectedElements.forEach(el => {
if (el === undefined) { if (el === undefined) {
includeEmpty = true; includeEmpty = true;
} }
}); });
return includeEmpty ? { return includeEmpty
$eq: [], ? {
} : null; $eq: [],
}
: null;
} }
} }
// Advanced filter forms a MongoSelector from a users String. // Advanced filter forms a MongoSelector from a users String.
// Build by: Ignatz 19.05.2018 (github feuerball11) // Build by: Ignatz 19.05.2018 (github feuerball11)
class AdvancedFilter { class AdvancedFilter {
@ -128,7 +129,8 @@ class AdvancedFilter {
current += char; current += char;
continue; continue;
} }
if (char === '\'') { // eslint-disable-next-line quotes
if (char === "'") {
string = !string; string = !string;
if (string) wasString = true; if (string) wasString = true;
continue; continue;
@ -139,8 +141,8 @@ class AdvancedFilter {
} }
if (char === ' ' && !string) { if (char === ' ' && !string) {
commands.push({ commands.push({
'cmd': current, cmd: current,
'string': wasString, string: wasString,
regex, regex,
}); });
wasString = false; wasString = false;
@ -151,8 +153,8 @@ class AdvancedFilter {
} }
if (current !== '') { if (current !== '') {
commands.push({ commands.push({
'cmd': current, cmd: current,
'string': wasString, string: wasString,
regex, regex,
}); });
} }
@ -161,16 +163,19 @@ class AdvancedFilter {
_fieldNameToId(field) { _fieldNameToId(field) {
const found = CustomFields.findOne({ const found = CustomFields.findOne({
'name': field, name: field,
}); });
return found._id; return found._id;
} }
_fieldValueToId(field, value) { _fieldValueToId(field, value) {
const found = CustomFields.findOne({ const found = CustomFields.findOne({
'name': field, name: field,
}); });
if (found.settings.dropdownItems && found.settings.dropdownItems.length > 0) { if (
found.settings.dropdownItems &&
found.settings.dropdownItems.length > 0
) {
for (let i = 0; i < found.settings.dropdownItems.length; i++) { for (let i = 0; i < found.settings.dropdownItems.length; i++) {
if (found.settings.dropdownItems[i].name === value) { if (found.settings.dropdownItems[i].name === value) {
return found.settings.dropdownItems[i]._id; return found.settings.dropdownItems[i]._id;
@ -202,37 +207,32 @@ class AdvancedFilter {
for (let i = 0; i < commands.length; i++) { for (let i = 0; i < commands.length; i++) {
if (commands[i].cmd) { if (commands[i].cmd) {
switch (commands[i].cmd) { switch (commands[i].cmd) {
case '(': case '(': {
{ level++;
level++; if (start === -1) start = i;
if (start === -1) start = i; continue;
continue; }
} case ')': {
case ')': level--;
{
level--;
commands.splice(i, 1);
i--;
continue;
}
default:
{
if (level > 0) {
subcommands.push(commands[i]);
commands.splice(i, 1); commands.splice(i, 1);
i--; i--;
continue; continue;
} }
} default: {
if (level > 0) {
subcommands.push(commands[i]);
commands.splice(i, 1);
i--;
continue;
}
}
} }
} }
} }
if (start !== -1) { if (start !== -1) {
this._processSubCommands(subcommands); this._processSubCommands(subcommands);
if (subcommands.length === 1) if (subcommands.length === 1) commands.splice(start, 0, subcommands[0]);
commands.splice(start, 0, subcommands[0]); else commands.splice(start, 0, subcommands);
else
commands.splice(start, 0, subcommands);
} }
this._processConditions(commands); this._processConditions(commands);
this._processLogicalOperators(commands); this._processLogicalOperators(commands);
@ -242,149 +242,139 @@ class AdvancedFilter {
for (let i = 0; i < commands.length; i++) { for (let i = 0; i < commands.length; i++) {
if (!commands[i].string && commands[i].cmd) { if (!commands[i].string && commands[i].cmd) {
switch (commands[i].cmd) { switch (commands[i].cmd) {
case '=': case '=':
case '==': case '==':
case '===': case '===': {
{ const field = commands[i - 1].cmd;
const field = commands[i - 1].cmd; const str = commands[i + 1].cmd;
const str = commands[i + 1].cmd; if (commands[i + 1].regex) {
if (commands[i + 1].regex) { const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
const match = str.match(new RegExp('^/(.*?)/([gimy]*)$')); let regex = null;
let regex = null; if (match.length > 2) regex = new RegExp(match[1], match[2]);
if (match.length > 2) else regex = new RegExp(match[1]);
regex = new RegExp(match[1], match[2]); commands[i] = {
else 'customFields._id': this._fieldNameToId(field),
regex = new RegExp(match[1]); 'customFields.value': regex,
commands[i] = { };
'customFields._id': this._fieldNameToId(field), } else {
'customFields.value': regex, commands[i] = {
}; 'customFields._id': this._fieldNameToId(field),
} else { 'customFields.value': {
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$in: [this._fieldValueToId(field, str), parseInt(str, 10)],
},
};
}
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '!=':
case '!==':
{
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
if (commands[i + 1].regex) {
const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
let regex = null;
if (match.length > 2)
regex = new RegExp(match[1], match[2]);
else
regex = new RegExp(match[1]);
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$not: regex,
},
};
} else {
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$not: {
$in: [this._fieldValueToId(field, str), parseInt(str, 10)], $in: [this._fieldValueToId(field, str), parseInt(str, 10)],
}, },
};
}
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '!=':
case '!==': {
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
if (commands[i + 1].regex) {
const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
let regex = null;
if (match.length > 2) regex = new RegExp(match[1], match[2]);
else regex = new RegExp(match[1]);
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$not: regex,
},
};
} else {
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$not: {
$in: [this._fieldValueToId(field, str), parseInt(str, 10)],
},
},
};
}
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '>':
case 'gt':
case 'Gt':
case 'GT': {
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$gt: parseInt(str, 10),
}, },
}; };
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '>=':
case '>==':
case 'gte':
case 'Gte':
case 'GTE': {
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$gte: parseInt(str, 10),
},
};
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '<':
case 'lt':
case 'Lt':
case 'LT': {
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$lt: parseInt(str, 10),
},
};
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '<=':
case '<==':
case 'lte':
case 'Lte':
case 'LTE': {
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$lte: parseInt(str, 10),
},
};
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
} }
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '>':
case 'gt':
case 'Gt':
case 'GT':
{
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$gt: parseInt(str, 10),
},
};
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '>=':
case '>==':
case 'gte':
case 'Gte':
case 'GTE':
{
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$gte: parseInt(str, 10),
},
};
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '<':
case 'lt':
case 'Lt':
case 'LT':
{
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$lt: parseInt(str, 10),
},
};
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
case '<=':
case '<==':
case 'lte':
case 'Lte':
case 'LTE':
{
const field = commands[i - 1].cmd;
const str = commands[i + 1].cmd;
commands[i] = {
'customFields._id': this._fieldNameToId(field),
'customFields.value': {
$lte: parseInt(str, 10),
},
};
commands.splice(i - 1, 1);
commands.splice(i, 1);
//changed = true;
i--;
break;
}
} }
} }
} }
@ -394,54 +384,51 @@ class AdvancedFilter {
for (let i = 0; i < commands.length; i++) { for (let i = 0; i < commands.length; i++) {
if (!commands[i].string && commands[i].cmd) { if (!commands[i].string && commands[i].cmd) {
switch (commands[i].cmd) { switch (commands[i].cmd) {
case 'or': case 'or':
case 'Or': case 'Or':
case 'OR': case 'OR':
case '|': case '|':
case '||': case '||': {
{ const op1 = commands[i - 1];
const op1 = commands[i - 1]; const op2 = commands[i + 1];
const op2 = commands[i + 1]; commands[i] = {
commands[i] = { $or: [op1, op2],
$or: [op1, op2], };
}; commands.splice(i - 1, 1);
commands.splice(i - 1, 1); commands.splice(i, 1);
commands.splice(i, 1); //changed = true;
//changed = true; i--;
i--; break;
break; }
} case 'and':
case 'and': case 'And':
case 'And': case 'AND':
case 'AND': case '&':
case '&': case '&&': {
case '&&': const op1 = commands[i - 1];
{ const op2 = commands[i + 1];
const op1 = commands[i - 1]; commands[i] = {
const op2 = commands[i + 1]; $and: [op1, op2],
commands[i] = { };
$and: [op1, op2], commands.splice(i - 1, 1);
}; commands.splice(i, 1);
commands.splice(i - 1, 1); //changed = true;
commands.splice(i, 1); i--;
//changed = true; break;
i--; }
break; case 'not':
} case 'Not':
case 'not': case 'NOT':
case 'Not': case '!': {
case 'NOT': const op1 = commands[i + 1];
case '!': commands[i] = {
{ $not: op1,
const op1 = commands[i + 1]; };
commands[i] = { commands.splice(i + 1, 1);
$not: op1, //changed = true;
}; i--;
commands.splice(i + 1, 1); break;
//changed = true; }
i--;
break;
}
} }
} }
} }
@ -452,7 +439,6 @@ class AdvancedFilter {
const commands = this._filterToCommands(); const commands = this._filterToCommands();
return this._arrayToSelector(commands); return this._arrayToSelector(commands);
} }
} }
// The global Filter object. // The global Filter object.
@ -477,23 +463,26 @@ Filter = {
_exceptionsDep: new Tracker.Dependency(), _exceptionsDep: new Tracker.Dependency(),
isActive() { isActive() {
return _.any(this._fields, (fieldName) => { return (
return this[fieldName]._isActive(); _.any(this._fields, fieldName => {
}) || this.advanced._isActive(); return this[fieldName]._isActive();
}) || this.advanced._isActive()
);
}, },
_getMongoSelector() { _getMongoSelector() {
if (!this.isActive()) if (!this.isActive()) return {};
return {};
const filterSelector = {}; const filterSelector = {};
const emptySelector = {}; const emptySelector = {};
let includeEmptySelectors = false; let includeEmptySelectors = false;
this._fields.forEach((fieldName) => { this._fields.forEach(fieldName => {
const filter = this[fieldName]; const filter = this[fieldName];
if (filter._isActive()) { if (filter._isActive()) {
if (filter.subField !== '') { if (filter.subField !== '') {
filterSelector[`${fieldName}.${filter.subField}`] = filter._getMongoSelector(); filterSelector[
`${fieldName}.${filter.subField}`
] = filter._getMongoSelector();
} else { } else {
filterSelector[fieldName] = filter._getMongoSelector(); filterSelector[fieldName] = filter._getMongoSelector();
} }
@ -513,11 +502,15 @@ Filter = {
const selectors = [exceptionsSelector]; const selectors = [exceptionsSelector];
if (_.any(this._fields, (fieldName) => { if (
return this[fieldName]._isActive(); _.any(this._fields, fieldName => {
})) selectors.push(filterSelector); return this[fieldName]._isActive();
})
)
selectors.push(filterSelector);
if (includeEmptySelectors) selectors.push(emptySelector); if (includeEmptySelectors) selectors.push(emptySelector);
if (this.advanced._isActive()) selectors.push(this.advanced._getMongoSelector()); if (this.advanced._isActive())
selectors.push(this.advanced._getMongoSelector());
return { return {
$or: selectors, $or: selectors,
@ -526,8 +519,7 @@ Filter = {
mongoSelector(additionalSelector) { mongoSelector(additionalSelector) {
const filterSelector = this._getMongoSelector(); const filterSelector = this._getMongoSelector();
if (_.isUndefined(additionalSelector)) if (_.isUndefined(additionalSelector)) return filterSelector;
return filterSelector;
else else
return { return {
$and: [filterSelector, additionalSelector], $and: [filterSelector, additionalSelector],
@ -535,7 +527,7 @@ Filter = {
}, },
reset() { reset() {
this._fields.forEach((fieldName) => { this._fields.forEach(fieldName => {
const filter = this[fieldName]; const filter = this[fieldName];
filter.reset(); filter.reset();
}); });

View file

@ -12,7 +12,7 @@ Meteor.startup(() => {
} }
if (!language) { if (!language) {
if(navigator.languages) { if (navigator.languages) {
language = navigator.languages[0]; language = navigator.languages[0];
} else { } else {
language = navigator.language || navigator.userLanguage; language = navigator.language || navigator.userLanguage;

View file

@ -48,35 +48,43 @@ InlinedForm = BlazeComponent.extendComponent({
}, },
events() { events() {
return [{ return [
'click .js-close-inlined-form': this.close, {
'click .js-open-inlined-form': this.open, 'click .js-close-inlined-form': this.close,
'click .js-open-inlined-form': this.open,
// Pressing Ctrl+Enter should submit the form // Pressing Ctrl+Enter should submit the form
'keydown form textarea'(evt) { 'keydown form textarea'(evt) {
if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) { if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
this.find('button[type=submit]').click(); this.find('button[type=submit]').click();
} }
}, },
// Close the inlined form when after its submission // Close the inlined form when after its submission
submit() { submit() {
if (this.currentData().autoclose !== false) { if (this.currentData().autoclose !== false) {
Tracker.afterFlush(() => { Tracker.afterFlush(() => {
this.close(); this.close();
}); });
} }
},
}, },
}]; ];
}, },
}).register('inlinedForm'); }).register('inlinedForm');
// Press escape to close the currently opened inlinedForm // Press escape to close the currently opened inlinedForm
EscapeActions.register('inlinedForm', EscapeActions.register(
() => { currentlyOpenedForm.get().close(); }, 'inlinedForm',
() => { return currentlyOpenedForm.get() !== null; }, { () => {
currentlyOpenedForm.get().close();
},
() => {
return currentlyOpenedForm.get() !== null;
},
{
enabledOnClick: false, enabledOnClick: false,
} },
); );
// submit on click outside // submit on click outside

View file

@ -40,8 +40,10 @@ Mousetrap.bind(['down', 'up'], (evt, key) => {
return; return;
} }
const nextFunc = (key === 'down' ? 'next' : 'prev'); const nextFunc = key === 'down' ? 'next' : 'prev';
const nextCard = $('.js-minicard.is-selected')[nextFunc]('.js-minicard').get(0); const nextCard = $('.js-minicard.is-selected')
[nextFunc]('.js-minicard')
.get(0);
if (nextCard) { if (nextCard) {
const nextCardId = Blaze.getData(nextCard)._id; const nextCardId = Blaze.getData(nextCard)._id;
Utils.goCardId(nextCardId); Utils.goCardId(nextCardId);
@ -49,7 +51,7 @@ Mousetrap.bind(['down', 'up'], (evt, key) => {
}); });
// XXX This shortcut should also work when hovering over a card in board view // XXX This shortcut should also work when hovering over a card in board view
Mousetrap.bind('space', (evt) => { Mousetrap.bind('space', evt => {
if (!Session.get('currentCard')) { if (!Session.get('currentCard')) {
return; return;
} }
@ -69,29 +71,38 @@ Mousetrap.bind('space', (evt) => {
}); });
Template.keyboardShortcuts.helpers({ Template.keyboardShortcuts.helpers({
mapping: [{ mapping: [
keys: ['W'], {
action: 'shortcut-toggle-sidebar', keys: ['W'],
}, { action: 'shortcut-toggle-sidebar',
keys: ['Q'], },
action: 'shortcut-filter-my-cards', {
}, { keys: ['Q'],
keys: ['F'], action: 'shortcut-filter-my-cards',
action: 'shortcut-toggle-filterbar', },
}, { {
keys: ['X'], keys: ['F'],
action: 'shortcut-clear-filters', action: 'shortcut-toggle-filterbar',
}, { },
keys: ['?'], {
action: 'shortcut-show-shortcuts', keys: ['X'],
}, { action: 'shortcut-clear-filters',
keys: ['ESC'], },
action: 'shortcut-close-dialog', {
}, { keys: ['?'],
keys: ['@'], action: 'shortcut-show-shortcuts',
action: 'shortcut-autocomplete-members', },
}, { {
keys: ['SPACE'], keys: ['ESC'],
action: 'shortcut-assign-self', action: 'shortcut-close-dialog',
}], },
{
keys: ['@'],
action: 'shortcut-autocomplete-members',
},
{
keys: ['SPACE'],
action: 'shortcut-assign-self',
},
],
}); });

View file

@ -1,6 +1,6 @@
const closedValue = null; const closedValue = null;
window.Modal = new class { window.Modal = new (class {
constructor() { constructor() {
this._currentModal = new ReactiveVar(closedValue); this._currentModal = new ReactiveVar(closedValue);
this._onCloseGoTo = ''; this._onCloseGoTo = '';
@ -21,7 +21,7 @@ window.Modal = new class {
return this.getTemplateName() !== closedValue; return this.getTemplateName() !== closedValue;
} }
isWide(){ isWide() {
return this._isWideModal; return this._isWideModal;
} }
@ -32,23 +32,23 @@ window.Modal = new class {
} }
} }
openWide(modalName, { header = '', onCloseGoTo = ''} = {}) { openWide(modalName, { header = '', onCloseGoTo = '' } = {}) {
this._currentModal.set({ header, modalName }); this._currentModal.set({ header, modalName });
this._onCloseGoTo = onCloseGoTo; this._onCloseGoTo = onCloseGoTo;
this._isWideModal = true; this._isWideModal = true;
} }
open(modalName, { header = '', onCloseGoTo = ''} = {}) { open(modalName, { header = '', onCloseGoTo = '' } = {}) {
this._currentModal.set({ header, modalName }); this._currentModal.set({ header, modalName });
this._onCloseGoTo = onCloseGoTo; this._onCloseGoTo = onCloseGoTo;
} }
}(); })();
Blaze.registerHelper('Modal', Modal); Blaze.registerHelper('Modal', Modal);
EscapeActions.register('modalWindow', EscapeActions.register(
'modalWindow',
() => Modal.close(), () => Modal.close(),
() => Modal.isOpen(), () => Modal.isOpen(),
{ noClickEscapeOn: '.modal-container' } { noClickEscapeOn: '.modal-container' },
); );

View file

@ -1,6 +1,4 @@
function getCardsBetween(idA, idB) { function getCardsBetween(idA, idB) {
function pluckId(doc) { function pluckId(doc) {
return doc._id; return doc._id;
} }
@ -15,7 +13,7 @@ function getCardsBetween(idA, idB) {
}).map(pluckId); }).map(pluckId);
} }
const cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], (c) => { const cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], c => {
return c.sort; return c.sort;
}); });
@ -31,17 +29,21 @@ function getCardsBetween(idA, idB) {
}; };
} else { } else {
selector = { selector = {
$or: [{ $or: [
listId: cards[0].listId, {
sort: { $lte: cards[0].sort }, listId: cards[0].listId,
}, { sort: { $lte: cards[0].sort },
listId: {
$in: getListsStrictlyBetween(cards[0].listId, cards[1].listId),
}, },
}, { {
listId: cards[1].listId, listId: {
sort: { $gte: cards[1].sort }, $in: getListsStrictlyBetween(cards[0].listId, cards[1].listId),
}], },
},
{
listId: cards[1].listId,
sort: { $gte: cards[1].sort },
},
],
archived: false, archived: false,
}; };
} }
@ -133,14 +135,12 @@ MultiSelection = {
const selectedCards = this._selectedCards.get(); const selectedCards = this._selectedCards.get();
cardIds.forEach((cardId) => { cardIds.forEach(cardId => {
const indexOfCard = selectedCards.indexOf(cardId); const indexOfCard = selectedCards.indexOf(cardId);
if (options.remove && indexOfCard > -1) if (options.remove && indexOfCard > -1)
selectedCards.splice(indexOfCard, 1); selectedCards.splice(indexOfCard, 1);
else if (options.add) selectedCards.push(cardId);
else if (options.add)
selectedCards.push(cardId);
}); });
this._selectedCards.set(selectedCards); this._selectedCards.set(selectedCards);
@ -153,9 +153,15 @@ MultiSelection = {
Blaze.registerHelper('MultiSelection', MultiSelection); Blaze.registerHelper('MultiSelection', MultiSelection);
EscapeActions.register('multiselection', EscapeActions.register(
() => { MultiSelection.disable(); }, 'multiselection',
() => { return MultiSelection.isActive(); }, { () => {
MultiSelection.disable();
},
() => {
return MultiSelection.isActive();
},
{
noClickEscapeOn: '.js-minicard,.js-board-sidebar-content', noClickEscapeOn: '.js-minicard,.js-board-sidebar-content',
} },
); );

View file

@ -11,7 +11,10 @@
$.event.fix = (function(originalFix) { $.event.fix = (function(originalFix) {
return function(event) { return function(event) {
event = originalFix.apply(this, arguments); event = originalFix.apply(this, arguments);
if (event.type.indexOf('copy') === 0 || event.type.indexOf('paste') === 0) { if (
event.type.indexOf('copy') === 0 ||
event.type.indexOf('paste') === 0
) {
event.clipboardData = event.originalEvent.clipboardData; event.clipboardData = event.originalEvent.clipboardData;
} }
return event; return event;
@ -23,7 +26,7 @@
matchType: /image.*/, matchType: /image.*/,
}; };
return $.fn.pasteImageReader = function(options) { return ($.fn.pasteImageReader = function(options) {
if (typeof options === 'function') { if (typeof options === 'function') {
options = { options = {
callback: options, callback: options,
@ -35,8 +38,11 @@
return $(element).bind('paste', function(event) { return $(element).bind('paste', function(event) {
const types = event.clipboardData.types; const types = event.clipboardData.types;
const items = event.clipboardData.items; const items = event.clipboardData.items;
for(let i=0; i<types.length; i++) { for (let i = 0; i < types.length; i++) {
if(types[i].match(options.matchType) || items[i].type.match(options.matchType)) { if (
types[i].match(options.matchType) ||
items[i].type.match(options.matchType)
) {
const f = items[i].getAsFile(); const f = items[i].getAsFile();
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function(evt) { reader.onload = function(evt) {
@ -53,5 +59,5 @@
} }
}); });
}); });
}; });
})(jQuery); })(jQuery);

View file

@ -1,4 +1,4 @@
window.Popup = new class { window.Popup = new (class {
constructor() { constructor() {
// The template we use to render popups // The template we use to render popups
this.template = Template.popup; this.template = Template.popup;
@ -67,7 +67,7 @@ window.Popup = new class {
title: self._getTitle(popupName), title: self._getTitle(popupName),
depth: self._stack.length, depth: self._stack.length,
offset: self._getOffset(openerElement), offset: self._getOffset(openerElement),
dataContext: this.currentData && this.currentData() || this, dataContext: (this.currentData && this.currentData()) || this,
}); });
// If there are no popup currently opened we use the Blaze API to render // If there are no popup currently opened we use the Blaze API to render
@ -80,11 +80,14 @@ window.Popup = new class {
// our internal dependency, and since we just changed the top element of // our internal dependency, and since we just changed the top element of
// our internal stack, the popup will be updated with the new data. // our internal stack, the popup will be updated with the new data.
if (!self.isOpen()) { if (!self.isOpen()) {
self.current = Blaze.renderWithData(self.template, () => { self.current = Blaze.renderWithData(
self._dep.depend(); self.template,
return { ...self._getTopStack(), stack: self._stack }; () => {
}, document.body); self._dep.depend();
return { ...self._getTopStack(), stack: self._stack };
},
document.body,
);
} else { } else {
self._dep.changed(); self._dep.changed();
} }
@ -101,7 +104,7 @@ window.Popup = new class {
const self = this; const self = this;
return function(evt, tpl) { return function(evt, tpl) {
const context = this.currentData && this.currentData() || this; const context = (this.currentData && this.currentData()) || this;
context.__afterConfirmAction = action; context.__afterConfirmAction = action;
self.open(name).call(context, evt, tpl); self.open(name).call(context, evt, tpl);
}; };
@ -136,7 +139,6 @@ window.Popup = new class {
const openerElement = this._getTopStack().openerElement; const openerElement = this._getTopStack().openerElement;
$(openerElement).removeClass('is-active'); $(openerElement).removeClass('is-active');
this._stack = []; this._stack = [];
} }
} }
@ -159,7 +161,7 @@ window.Popup = new class {
return () => { return () => {
Utils.windowResizeDep.depend(); Utils.windowResizeDep.depend();
if(Utils.isMiniScreen()) return { left:0, top:0 }; if (Utils.isMiniScreen()) return { left: 0, top: 0 };
const offset = $element.offset(); const offset = $element.offset();
const popupWidth = 300 + 15; const popupWidth = 300 + 15;
@ -188,18 +190,19 @@ window.Popup = new class {
return title !== translationKey ? title : defaultTitle; return title !== translationKey ? title : defaultTitle;
}; };
} }
}(); })();
// We close a potential opened popup on any left click on the document, or go // We close a potential opened popup on any left click on the document, or go
// one step back by pressing escape. // one step back by pressing escape.
const escapeActions = ['back', 'close']; const escapeActions = ['back', 'close'];
escapeActions.forEach((actionName) => { escapeActions.forEach(actionName => {
EscapeActions.register(`popup-${actionName}`, EscapeActions.register(
`popup-${actionName}`,
() => Popup[actionName](), () => Popup[actionName](),
() => Popup.isOpen(), () => Popup.isOpen(),
{ {
noClickEscapeOn: '.js-pop-over,.js-open-card-title-popup', noClickEscapeOn: '.js-pop-over,.js-open-card-title-popup',
enabledOnClick: actionName === 'close', enabledOnClick: actionName === 'close',
} },
); );
}); });

View file

@ -47,9 +47,6 @@ $.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) {
}); });
}; };
EscapeActions.register('textcomplete', EscapeActions.register('textcomplete', () => {}, () => dropdownMenuIsOpened, {
() => {}, noClickEscapeOn: '.textcomplete-dropdown',
() => dropdownMenuIsOpened, { });
noClickEscapeOn: '.textcomplete-dropdown',
}
);

View file

@ -34,13 +34,13 @@ UnsavedEdits = {
}, },
has({ fieldName, docId }) { has({ fieldName, docId }) {
return Boolean(this.get({fieldName, docId})); return Boolean(this.get({ fieldName, docId }));
}, },
set({ fieldName, docId }, value) { set({ fieldName, docId }, value) {
const currentDoc = this._getCollectionDocument(fieldName, docId); const currentDoc = this._getCollectionDocument(fieldName, docId);
if (currentDoc) { if (currentDoc) {
UnsavedEditCollection.update(currentDoc._id, { $set: { value }}); UnsavedEditCollection.update(currentDoc._id, { $set: { value } });
} else { } else {
UnsavedEditCollection.insert({ UnsavedEditCollection.insert({
fieldName, fieldName,
@ -58,7 +58,7 @@ UnsavedEdits = {
}, },
_getCollectionDocument(fieldName, docId) { _getCollectionDocument(fieldName, docId) {
return UnsavedEditCollection.findOne({fieldName, docId}); return UnsavedEditCollection.findOne({ fieldName, docId });
}, },
}; };

View file

@ -2,20 +2,26 @@ Utils = {
// XXX We should remove these two methods // XXX We should remove these two methods
goBoardId(_id) { goBoardId(_id) {
const board = Boards.findOne(_id); const board = Boards.findOne(_id);
return board && FlowRouter.go('board', { return (
id: board._id, board &&
slug: board.slug, FlowRouter.go('board', {
}); id: board._id,
slug: board.slug,
})
);
}, },
goCardId(_id) { goCardId(_id) {
const card = Cards.findOne(_id); const card = Cards.findOne(_id);
const board = Boards.findOne(card.boardId); const board = Boards.findOne(card.boardId);
return board && FlowRouter.go('card', { return (
cardId: card._id, board &&
boardId: board._id, FlowRouter.go('card', {
slug: board.slug, cardId: card._id,
}); boardId: board._id,
slug: board.slug,
})
);
}, },
capitalize(string) { capitalize(string) {
@ -104,13 +110,21 @@ Utils = {
return window.matchMedia(query).matches; return window.matchMedia(query).matches;
}; };
if (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) { if (
'ontouchstart' in window ||
(window.DocumentTouch && document instanceof window.DocumentTouch)
) {
return true; return true;
} }
// include the 'heartz' as a way to have a non matching MQ to help terminate the join // include the 'heartz' as a way to have a non matching MQ to help terminate the join
// https://git.io/vznFH // https://git.io/vznFH
const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''); const query = [
'(',
prefixes.join('touch-enabled),('),
'heartz',
')',
].join('');
return mq(query); return mq(query);
})(); })();
Utils.isTouchDevice = () => isTouchable; Utils.isTouchDevice = () => isTouchable;
@ -120,7 +134,7 @@ Utils = {
calculateTouchDistance(touchA, touchB) { calculateTouchDistance(touchA, touchB) {
return Math.sqrt( return Math.sqrt(
Math.pow(touchA.screenX - touchB.screenX, 2) + Math.pow(touchA.screenX - touchB.screenX, 2) +
Math.pow(touchA.screenY - touchB.screenY, 2) Math.pow(touchA.screenY - touchB.screenY, 2),
); );
}, },
@ -136,7 +150,11 @@ Utils = {
lastTouch = touches[touches.length - 1]; lastTouch = touches[touches.length - 1];
}); });
$(document).on('touchend', selector, function(e) { $(document).on('touchend', selector, function(e) {
if (touchStart && lastTouch && Utils.calculateTouchDistance(touchStart, lastTouch) <= 20) { if (
touchStart &&
lastTouch &&
Utils.calculateTouchDistance(touchStart, lastTouch) <= 20
) {
e.preventDefault(); e.preventDefault();
const clickEvent = document.createEvent('MouseEvents'); const clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true, true); clickEvent.initEvent('click', true, true);
@ -145,30 +163,30 @@ Utils = {
}); });
}, },
manageCustomUI(){ manageCustomUI() {
Meteor.call('getCustomUI', (err, data) => { Meteor.call('getCustomUI', (err, data) => {
if (err && err.error[0] === 'var-not-exist'){ if (err && err.error[0] === 'var-not-exist') {
Session.set('customUI', false); // siteId || address server not defined Session.set('customUI', false); // siteId || address server not defined
} }
if (!err){ if (!err) {
Utils.setCustomUI(data); Utils.setCustomUI(data);
} }
}); });
}, },
setCustomUI(data){ setCustomUI(data) {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
if (currentBoard) { if (currentBoard) {
DocHead.setTitle(`${currentBoard.title } - ${ data.productName}`); DocHead.setTitle(`${currentBoard.title} - ${data.productName}`);
} else { } else {
DocHead.setTitle(`${data.productName}`); DocHead.setTitle(`${data.productName}`);
} }
}, },
setMatomo(data){ setMatomo(data) {
window._paq = window._paq || []; window._paq = window._paq || [];
window._paq.push(['setDoNotTrack', data.doNotTrack]); window._paq.push(['setDoNotTrack', data.doNotTrack]);
if (data.withUserName){ if (data.withUserName) {
window._paq.push(['setUserId', Meteor.user().username]); window._paq.push(['setUserId', Meteor.user().username]);
} }
window._paq.push(['trackPageView']); window._paq.push(['trackPageView']);
@ -196,12 +214,12 @@ Utils = {
manageMatomo() { manageMatomo() {
const matomo = Session.get('matomo'); const matomo = Session.get('matomo');
if (matomo === undefined){ if (matomo === undefined) {
Meteor.call('getMatomoConf', (err, data) => { Meteor.call('getMatomoConf', (err, data) => {
if (err && err.error[0] === 'var-not-exist'){ if (err && err.error[0] === 'var-not-exist') {
Session.set('matomo', false); // siteId || address server not defined Session.set('matomo', false); // siteId || address server not defined
} }
if (!err){ if (!err) {
Utils.setMatomo(data); Utils.setMatomo(data);
} }
}); });
@ -220,15 +238,21 @@ Utils = {
finalString += element.text().toLowerCase(); finalString += element.text().toLowerCase();
} else if (element.hasClass('user-details')) { } else if (element.hasClass('user-details')) {
let username = element.find('input').val(); let username = element.find('input').val();
if(username === undefined || username === ''){ if (username === undefined || username === '') {
username = '*'; username = '*';
} }
finalString += `${element.find('.trigger-text').text().toLowerCase() } ${ username}`; finalString += `${element
.find('.trigger-text')
.text()
.toLowerCase()} ${username}`;
} else if (element.find('select').length > 0) { } else if (element.find('select').length > 0) {
finalString += element.find('select option:selected').text().toLowerCase(); finalString += element
.find('select option:selected')
.text()
.toLowerCase();
} else if (element.find('input').length > 0) { } else if (element.find('input').length > 0) {
let inputvalue = element.find('input').val(); let inputvalue = element.find('input').val();
if(inputvalue === undefined || inputvalue === ''){ if (inputvalue === undefined || inputvalue === '') {
inputvalue = '*'; inputvalue = '*';
} }
finalString += inputvalue; finalString += inputvalue;

View file

@ -1,20 +1,25 @@
const passwordField = AccountsTemplates.removeField('password'); const passwordField = AccountsTemplates.removeField('password');
const emailField = AccountsTemplates.removeField('email'); const emailField = AccountsTemplates.removeField('email');
AccountsTemplates.addFields([{ AccountsTemplates.addFields([
_id: 'username', {
type: 'text', _id: 'username',
displayName: 'username', type: 'text',
required: true, displayName: 'username',
minLength: 2, required: true,
}, emailField, passwordField, { minLength: 2,
_id: 'invitationcode', },
type: 'text', emailField,
displayName: 'Invitation Code', passwordField,
required: false, {
minLength: 6, _id: 'invitationcode',
template: 'invitationCode', type: 'text',
}]); displayName: 'Invitation Code',
required: false,
minLength: 6,
template: 'invitationCode',
},
]);
AccountsTemplates.configure({ AccountsTemplates.configure({
defaultLayout: 'userFormsLayout', defaultLayout: 'userFormsLayout',
@ -34,7 +39,8 @@ AccountsTemplates.configure({
}); });
['signIn', 'signUp', 'resetPwd', 'forgotPwd', 'enrollAccount'].forEach( ['signIn', 'signUp', 'resetPwd', 'forgotPwd', 'enrollAccount'].forEach(
(routeName) => AccountsTemplates.configureRoute(routeName)); routeName => AccountsTemplates.configureRoute(routeName),
);
// We display the form to change the password in a popup window that already // We display the form to change the password in a popup window that already
// have a title, so we unset the title automatically displayed by useraccounts. // have a title, so we unset the title automatically displayed by useraccounts.
@ -56,15 +62,25 @@ AccountsTemplates.configureRoute('changePwd', {
}); });
if (Meteor.isServer) { if (Meteor.isServer) {
[
['resetPassword-subject', 'resetPassword-text', 'verifyEmail-subject', 'verifyEmail-text', 'enrollAccount-subject', 'enrollAccount-text'].forEach((str) => { 'resetPassword-subject',
'resetPassword-text',
'verifyEmail-subject',
'verifyEmail-text',
'enrollAccount-subject',
'enrollAccount-text',
].forEach(str => {
const [templateName, field] = str.split('-'); const [templateName, field] = str.split('-');
Accounts.emailTemplates[templateName][field] = (user, url) => { Accounts.emailTemplates[templateName][field] = (user, url) => {
return TAPi18n.__(`email-${str}`, { return TAPi18n.__(
url, `email-${str}`,
user: user.getName(), {
siteName: Accounts.emailTemplates.siteName, url,
}, user.getLanguage()); user: user.getName(),
siteName: Accounts.emailTemplates.siteName,
},
user.getLanguage(),
);
}; };
}); });
} }

View file

@ -1,7 +1,9 @@
let previousPath; let previousPath;
FlowRouter.triggers.exit([({path}) => { FlowRouter.triggers.exit([
previousPath = path; ({ path }) => {
}]); previousPath = path;
},
]);
FlowRouter.route('/', { FlowRouter.route('/', {
name: 'home', name: 'home',
@ -192,9 +194,11 @@ const redirections = {
_.each(redirections, (newPath, oldPath) => { _.each(redirections, (newPath, oldPath) => {
FlowRouter.route(oldPath, { FlowRouter.route(oldPath, {
triggersEnter: [(context, redirect) => { triggersEnter: [
redirect(FlowRouter.path(newPath, context.params)); (context, redirect) => {
}], redirect(FlowRouter.path(newPath, context.params));
},
],
}); });
}); });

View file

@ -37,7 +37,7 @@ AccountSettings.attachSchema(
} }
}, },
}, },
}) }),
); );
AccountSettings.allow({ AccountSettings.allow({
@ -47,11 +47,6 @@ AccountSettings.allow({
}, },
}); });
AccountSettings.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
if (Meteor.isServer) { if (Meteor.isServer) {
Meteor.startup(() => { Meteor.startup(() => {
AccountSettings._collection._ensureIndex({ modifiedAt: -1 }); AccountSettings._collection._ensureIndex({ modifiedAt: -1 });
@ -62,7 +57,7 @@ if (Meteor.isServer) {
booleanValue: false, booleanValue: false,
sort: 0, sort: 0,
}, },
} },
); );
AccountSettings.upsert( AccountSettings.upsert(
{ _id: 'accounts-allowUserNameChange' }, { _id: 'accounts-allowUserNameChange' },
@ -71,7 +66,7 @@ if (Meteor.isServer) {
booleanValue: false, booleanValue: false,
sort: 1, sort: 1,
}, },
} },
); );
}); });
} }

View file

@ -20,11 +20,6 @@ Actions.helpers({
}, },
}); });
Actions.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
if (Meteor.isServer) { if (Meteor.isServer) {
Meteor.startup(() => { Meteor.startup(() => {
Actions._collection._ensureIndex({ modifiedAt: -1 }); Actions._collection._ensureIndex({ modifiedAt: -1 });

View file

@ -71,11 +71,6 @@ Activities.after.insert((userId, doc) => {
RulesHelper.executeRules(activity); RulesHelper.executeRules(activity);
}); });
Activities.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
if (Meteor.isServer) { if (Meteor.isServer) {
// For efficiency create indexes on the date of creation, and on the date of // For efficiency create indexes on the date of creation, and on the date of
// creation in conjunction with the card or board id, as corresponding views // creation in conjunction with the card or board id, as corresponding views
@ -87,15 +82,15 @@ if (Meteor.isServer) {
Activities._collection._ensureIndex({ boardId: 1, createdAt: -1 }); Activities._collection._ensureIndex({ boardId: 1, createdAt: -1 });
Activities._collection._ensureIndex( Activities._collection._ensureIndex(
{ commentId: 1 }, { commentId: 1 },
{ partialFilterExpression: { commentId: { $exists: true } } } { partialFilterExpression: { commentId: { $exists: true } } },
); );
Activities._collection._ensureIndex( Activities._collection._ensureIndex(
{ attachmentId: 1 }, { attachmentId: 1 },
{ partialFilterExpression: { attachmentId: { $exists: true } } } { partialFilterExpression: { attachmentId: { $exists: true } } },
); );
Activities._collection._ensureIndex( Activities._collection._ensureIndex(
{ customFieldId: 1 }, { customFieldId: 1 },
{ partialFilterExpression: { customFieldId: { $exists: true } } } { partialFilterExpression: { customFieldId: { $exists: true } } },
); );
// Label activity did not work yet, unable to edit labels when tried this. // Label activity did not work yet, unable to edit labels when tried this.
//Activities._collection._dropIndex({ labelId: 1 }, { "indexKey": -1 }); //Activities._collection._dropIndex({ labelId: 1 }, { "indexKey": -1 });
@ -205,20 +200,20 @@ if (Meteor.isServer) {
if (board) { if (board) {
const watchingUsers = _.pluck( const watchingUsers = _.pluck(
_.where(board.watchers, { level: 'watching' }), _.where(board.watchers, { level: 'watching' }),
'userId' 'userId',
); );
const trackingUsers = _.pluck( const trackingUsers = _.pluck(
_.where(board.watchers, { level: 'tracking' }), _.where(board.watchers, { level: 'tracking' }),
'userId' 'userId',
); );
watchers = _.union( watchers = _.union(
watchers, watchers,
watchingUsers, watchingUsers,
_.intersection(participants, trackingUsers) _.intersection(participants, trackingUsers),
); );
} }
Notifications.getUsers(watchers).forEach((user) => { Notifications.getUsers(watchers).forEach(user => {
Notifications.notify(user, title, description, params); Notifications.notify(user, title, description, params);
}); });

View file

@ -42,7 +42,7 @@ Announcements.attachSchema(
} }
}, },
}, },
}) }),
); );
Announcements.allow({ Announcements.allow({
@ -52,11 +52,6 @@ Announcements.allow({
}, },
}); });
Announcements.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
if (Meteor.isServer) { if (Meteor.isServer) {
Meteor.startup(() => { Meteor.startup(() => {
Announcements._collection._ensureIndex({ modifiedAt: -1 }); Announcements._collection._ensureIndex({ modifiedAt: -1 });

View file

@ -12,7 +12,7 @@ Attachments = new FS.Collection('attachments', {
// XXX Should we use `beforeWrite` option of CollectionFS instead of // XXX Should we use `beforeWrite` option of CollectionFS instead of
// collection-hooks? // collection-hooks?
// We should use `beforeWrite`. // We should use `beforeWrite`.
beforeWrite: (fileObj) => { beforeWrite: fileObj => {
if (!fileObj.isImage()) { if (!fileObj.isImage()) {
return { return {
type: 'application/octet-stream', type: 'application/octet-stream',
@ -84,7 +84,7 @@ if (Meteor.isServer) {
$unset: { $unset: {
source: '', source: '',
}, },
} },
); );
} }
}); });

View file

@ -101,7 +101,7 @@ Boards.attachSchema(
const colors = Boards.simpleSchema()._schema['labels.$.color'] const colors = Boards.simpleSchema()._schema['labels.$.color']
.allowedValues; .allowedValues;
const defaultLabelsColors = _.clone(colors).splice(0, 6); const defaultLabelsColors = _.clone(colors).splice(0, 6);
return defaultLabelsColors.map((color) => ({ return defaultLabelsColors.map(color => ({
color, color,
_id: Random.id(6), _id: Random.id(6),
name: '', name: '',
@ -342,7 +342,7 @@ Boards.attachSchema(
type: String, type: String,
defaultValue: 'board', defaultValue: 'board',
}, },
}) }),
); );
Boards.helpers({ Boards.helpers({
@ -355,7 +355,7 @@ Boards.helpers({
Swimlanes.find({ Swimlanes.find({
boardId: oldId, boardId: oldId,
archived: false, archived: false,
}).forEach((swimlane) => { }).forEach(swimlane => {
swimlane.type = 'swimlane'; swimlane.type = 'swimlane';
swimlane.copy(_id); swimlane.copy(_id);
}); });
@ -382,7 +382,7 @@ Boards.helpers({
isActiveMember(userId) { isActiveMember(userId) {
if (userId) { if (userId) {
return this.members.find( return this.members.find(
(member) => member.userId === userId && member.isActive member => member.userId === userId && member.isActive,
); );
} else { } else {
return false; return false;
@ -396,14 +396,14 @@ Boards.helpers({
cards() { cards() {
return Cards.find( return Cards.find(
{ boardId: this._id, archived: false }, { boardId: this._id, archived: false },
{ sort: { title: 1 } } { sort: { title: 1 } },
); );
}, },
lists() { lists() {
return Lists.find( return Lists.find(
{ boardId: this._id, archived: false }, { boardId: this._id, archived: false },
{ sort: { sort: 1 } } { sort: { sort: 1 } },
); );
}, },
@ -418,7 +418,7 @@ Boards.helpers({
swimlanes() { swimlanes() {
return Swimlanes.find( return Swimlanes.find(
{ boardId: this._id, archived: false }, { boardId: this._id, archived: false },
{ sort: { sort: 1 } } { sort: { sort: 1 } },
); );
}, },
@ -432,7 +432,7 @@ Boards.helpers({
}, },
{ {
sort: { sort: 1 }, sort: { sort: 1 },
} },
); );
}, },
@ -535,7 +535,7 @@ Boards.helpers({
customFields() { customFields() {
return CustomFields.find( return CustomFields.find(
{ boardIds: { $in: [this._id] } }, { boardIds: { $in: [this._id] } },
{ sort: { name: 1 } } { sort: { name: 1 } },
); );
}, },
@ -848,7 +848,7 @@ Boards.mutations({
isAdmin, isAdmin,
isNoComments, isNoComments,
isCommentOnly, isCommentOnly,
currentUserId = Meteor.userId() currentUserId = Meteor.userId(),
) { ) {
const memberIndex = this.memberIndex(memberId); const memberIndex = this.memberIndex(memberId);
// do not allow change permission of self // do not allow change permission of self
@ -884,9 +884,9 @@ Boards.mutations({
function boardRemover(userId, doc) { function boardRemover(userId, doc) {
[Cards, Lists, Swimlanes, Integrations, Rules, Activities].forEach( [Cards, Lists, Swimlanes, Integrations, Rules, Activities].forEach(
(element) => { element => {
element.remove({ boardId: doc._id }); element.remove({ boardId: doc._id });
} },
); );
} }
@ -927,7 +927,7 @@ if (Meteor.isServer) {
_.findWhere(doc.members, { _.findWhere(doc.members, {
userId: removedMemberId, userId: removedMemberId,
isAdmin: true, isAdmin: true,
}) }),
); );
}, },
fetch: ['members'], fetch: ['members'],
@ -973,7 +973,7 @@ if (Meteor.isServer) {
_id: 1, _id: 1,
'members.userId': 1, 'members.userId': 1,
}, },
{ unique: true } { unique: true },
); );
Boards._collection._ensureIndex({ 'members.userId': 1 }); Boards._collection._ensureIndex({ 'members.userId': 1 });
}); });
@ -1009,12 +1009,12 @@ if (Meteor.isServer) {
labelIds: removedLabelId, labelIds: removedLabelId,
}, },
}, },
{ multi: true } { multi: true },
); );
}); });
const foreachRemovedMember = (doc, modifier, callback) => { const foreachRemovedMember = (doc, modifier, callback) => {
Object.keys(modifier).forEach((set) => { Object.keys(modifier).forEach(set => {
if (modifier[set] !== false) { if (modifier[set] !== false) {
return; return;
} }
@ -1030,11 +1030,6 @@ if (Meteor.isServer) {
}); });
}; };
Boards.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
// Remove a member from all objects of the board before leaving the board // Remove a member from all objects of the board before leaving the board
Boards.before.update((userId, doc, fieldNames, modifier) => { Boards.before.update((userId, doc, fieldNames, modifier) => {
if (!_.contains(fieldNames, 'members')) { if (!_.contains(fieldNames, 'members')) {
@ -1043,7 +1038,7 @@ if (Meteor.isServer) {
if (modifier.$set) { if (modifier.$set) {
const boardId = doc._id; const boardId = doc._id;
foreachRemovedMember(doc, modifier.$set, (memberId) => { foreachRemovedMember(doc, modifier.$set, memberId => {
Cards.update( Cards.update(
{ boardId }, { boardId },
{ {
@ -1052,7 +1047,7 @@ if (Meteor.isServer) {
watchers: memberId, watchers: memberId,
}, },
}, },
{ multi: true } { multi: true },
); );
Lists.update( Lists.update(
@ -1062,7 +1057,7 @@ if (Meteor.isServer) {
watchers: memberId, watchers: memberId,
}, },
}, },
{ multi: true } { multi: true },
); );
const board = Boards._transform(doc); const board = Boards._transform(doc);
@ -1112,7 +1107,7 @@ if (Meteor.isServer) {
// Say goodbye to the former member // Say goodbye to the former member
if (modifier.$set) { if (modifier.$set) {
foreachRemovedMember(doc, modifier.$set, (memberId) => { foreachRemovedMember(doc, modifier.$set, memberId => {
Activities.insert({ Activities.insert({
userId, userId,
memberId, memberId,
@ -1143,7 +1138,7 @@ if (Meteor.isServer) {
// admins can access boards of any user // admins can access boards of any user
Authentication.checkAdminOrCondition( Authentication.checkAdminOrCondition(
req.userId, req.userId,
req.userId === paramUserId req.userId === paramUserId,
); );
const data = Boards.find( const data = Boards.find(
@ -1153,7 +1148,7 @@ if (Meteor.isServer) {
}, },
{ {
sort: ['title'], sort: ['title'],
} },
).map(function(board) { ).map(function(board) {
return { return {
_id: board._id, _id: board._id,
@ -1332,7 +1327,7 @@ if (Meteor.isServer) {
if (!board.getLabel(name, color)) { if (!board.getLabel(name, color)) {
Boards.direct.update( Boards.direct.update(
{ _id: id }, { _id: id },
{ $push: { labels: { _id: labelId, name, color } } } { $push: { labels: { _id: labelId, name, color } } },
); );
JsonRoutes.sendResult(res, { JsonRoutes.sendResult(res, {
code: 200, code: 200,
@ -1364,7 +1359,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function( JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function(
req, req,
res res,
) { ) {
try { try {
const boardId = req.params.boardId; const boardId = req.params.boardId;
@ -1384,7 +1379,7 @@ if (Meteor.isServer) {
isTrue(isAdmin), isTrue(isAdmin),
isTrue(isNoComments), isTrue(isNoComments),
isTrue(isCommentOnly), isTrue(isCommentOnly),
req.userId req.userId,
); );
JsonRoutes.sendResult(res, { JsonRoutes.sendResult(res, {

View file

@ -64,7 +64,7 @@ CardComments.attachSchema(
} }
}, },
}, },
}) }),
); );
CardComments.allow({ CardComments.allow({
@ -107,11 +107,6 @@ function commentCreation(userId, doc) {
}); });
} }
CardComments.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
if (Meteor.isServer) { if (Meteor.isServer) {
// Comments are often fetched within a card, so we create an index to make these // Comments are often fetched within a card, so we create an index to make these
// queries more efficient. // queries more efficient.
@ -125,7 +120,6 @@ if (Meteor.isServer) {
}); });
CardComments.after.update((userId, doc) => { CardComments.after.update((userId, doc) => {
const activity = Activities.findOne({ commentId: doc._id });
const card = Cards.findOne(doc.cardId); const card = Cards.findOne(doc.cardId);
Activities.insert({ Activities.insert({
userId, userId,
@ -139,7 +133,6 @@ if (Meteor.isServer) {
}); });
CardComments.before.remove((userId, doc) => { CardComments.before.remove((userId, doc) => {
const activity = Activities.findOne({ commentId: doc._id });
const card = Cards.findOne(doc.cardId); const card = Cards.findOne(doc.cardId);
Activities.insert({ Activities.insert({
userId, userId,
@ -174,7 +167,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', function( JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', function(
req, req,
res res,
) { ) {
try { try {
Authentication.checkUserId(req.userId); Authentication.checkUserId(req.userId);
@ -233,7 +226,7 @@ if (Meteor.isServer) {
data: error, data: error,
}); });
} }
} },
); );
/** /**
@ -280,7 +273,7 @@ if (Meteor.isServer) {
data: error, data: error,
}); });
} }
} },
); );
/** /**
@ -318,7 +311,7 @@ if (Meteor.isServer) {
data: error, data: error,
}); });
} }
} },
); );
} }

File diff suppressed because it is too large Load diff

View file

@ -61,7 +61,7 @@ ChecklistItems.attachSchema(
} }
}, },
}, },
}) }),
); );
ChecklistItems.allow({ ChecklistItems.allow({
@ -225,11 +225,6 @@ if (Meteor.isServer) {
publishChekListUncompleted(userId, doc, fieldNames); publishChekListUncompleted(userId, doc, fieldNames);
}); });
ChecklistItems.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
ChecklistItems.after.insert((userId, doc) => { ChecklistItems.after.insert((userId, doc) => {
itemCreation(userId, doc); itemCreation(userId, doc);
}); });
@ -281,7 +276,7 @@ if (Meteor.isServer) {
code: 500, code: 500,
}); });
} }
} },
); );
/** /**
@ -308,13 +303,13 @@ if (Meteor.isServer) {
if (req.body.hasOwnProperty('isFinished')) { if (req.body.hasOwnProperty('isFinished')) {
ChecklistItems.direct.update( ChecklistItems.direct.update(
{ _id: paramItemId }, { _id: paramItemId },
{ $set: { isFinished: req.body.isFinished } } { $set: { isFinished: req.body.isFinished } },
); );
} }
if (req.body.hasOwnProperty('title')) { if (req.body.hasOwnProperty('title')) {
ChecklistItems.direct.update( ChecklistItems.direct.update(
{ _id: paramItemId }, { _id: paramItemId },
{ $set: { title: req.body.title } } { $set: { title: req.body.title } },
); );
} }
@ -324,7 +319,7 @@ if (Meteor.isServer) {
_id: paramItemId, _id: paramItemId,
}, },
}); });
} },
); );
/** /**
@ -353,7 +348,7 @@ if (Meteor.isServer) {
_id: paramItemId, _id: paramItemId,
}, },
}); });
} },
); );
} }

View file

@ -59,7 +59,7 @@ Checklists.attachSchema(
type: Number, type: Number,
decimal: true, decimal: true,
}, },
}) }),
); );
Checklists.helpers({ Checklists.helpers({
@ -68,7 +68,7 @@ Checklists.helpers({
this._id = null; this._id = null;
this.cardId = newCardId; this.cardId = newCardId;
const newChecklistId = Checklists.insert(this); const newChecklistId = Checklists.insert(this);
ChecklistItems.find({ checklistId: oldChecklistId }).forEach((item) => { ChecklistItems.find({ checklistId: oldChecklistId }).forEach(item => {
item._id = null; item._id = null;
item.checklistId = newChecklistId; item.checklistId = newChecklistId;
item.cardId = newCardId; item.cardId = newCardId;
@ -84,7 +84,7 @@ Checklists.helpers({
{ {
checklistId: this._id, checklistId: this._id,
}, },
{ sort: ['sort'] } { sort: ['sort'] },
); );
}, },
finishedCount() { finishedCount() {
@ -160,16 +160,11 @@ if (Meteor.isServer) {
}); });
}); });
Checklists.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
Checklists.before.remove((userId, doc) => { Checklists.before.remove((userId, doc) => {
const activities = Activities.find({ checklistId: doc._id }); const activities = Activities.find({ checklistId: doc._id });
const card = Cards.findOne(doc.cardId); const card = Cards.findOne(doc.cardId);
if (activities) { if (activities) {
activities.forEach((activity) => { activities.forEach(activity => {
Activities.remove(activity._id); Activities.remove(activity._id);
}); });
} }
@ -203,7 +198,7 @@ if (Meteor.isServer) {
Authentication.checkUserId(req.userId); Authentication.checkUserId(req.userId);
const paramCardId = req.params.cardId; const paramCardId = req.params.cardId;
const checklists = Checklists.find({ cardId: paramCardId }).map(function( const checklists = Checklists.find({ cardId: paramCardId }).map(function(
doc doc,
) { ) {
return { return {
_id: doc._id, _id: doc._id,
@ -220,7 +215,7 @@ if (Meteor.isServer) {
code: 500, code: 500,
}); });
} }
} },
); );
/** /**
@ -269,7 +264,7 @@ if (Meteor.isServer) {
code: 500, code: 500,
}); });
} }
} },
); );
/** /**
@ -313,7 +308,7 @@ if (Meteor.isServer) {
code: 400, code: 400,
}); });
} }
} },
); );
/** /**
@ -340,7 +335,7 @@ if (Meteor.isServer) {
_id: paramChecklistId, _id: paramChecklistId,
}, },
}); });
} },
); );
} }

View file

@ -95,7 +95,7 @@ CustomFields.attachSchema(
} }
}, },
}, },
}) }),
); );
CustomFields.mutations({ CustomFields.mutations({
@ -118,7 +118,7 @@ CustomFields.allow({
userId, userId,
Boards.find({ Boards.find({
_id: { $in: doc.boardIds }, _id: { $in: doc.boardIds },
}).fetch() }).fetch(),
); );
}, },
update(userId, doc) { update(userId, doc) {
@ -126,7 +126,7 @@ CustomFields.allow({
userId, userId,
Boards.find({ Boards.find({
_id: { $in: doc.boardIds }, _id: { $in: doc.boardIds },
}).fetch() }).fetch(),
); );
}, },
remove(userId, doc) { remove(userId, doc) {
@ -134,7 +134,7 @@ CustomFields.allow({
userId, userId,
Boards.find({ Boards.find({
_id: { $in: doc.boardIds }, _id: { $in: doc.boardIds },
}).fetch() }).fetch(),
); );
}, },
fetch: ['userId', 'boardIds'], fetch: ['userId', 'boardIds'],
@ -152,7 +152,7 @@ function customFieldCreation(userId, doc) {
}); });
} }
function customFieldDeletion(userId, doc){ function customFieldDeletion(userId, doc) {
Activities.insert({ Activities.insert({
userId, userId,
activityType: 'deleteCustomField', activityType: 'deleteCustomField',
@ -163,7 +163,7 @@ function customFieldDeletion(userId, doc){
// This has some bug, it does not show edited customField value at Outgoing Webhook, // This has some bug, it does not show edited customField value at Outgoing Webhook,
// instead it shows undefined, and no listId and swimlaneId. // instead it shows undefined, and no listId and swimlaneId.
function customFieldEdit(userId, doc){ function customFieldEdit(userId, doc) {
const card = Cards.findOne(doc.cardId); const card = Cards.findOne(doc.cardId);
Activities.insert({ Activities.insert({
userId, userId,
@ -185,17 +185,12 @@ if (Meteor.isServer) {
customFieldCreation(userId, doc); customFieldCreation(userId, doc);
}); });
CustomFields.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
CustomFields.before.update((userId, doc, fieldNames, modifier) => { CustomFields.before.update((userId, doc, fieldNames, modifier) => {
if (_.contains(fieldNames, 'boardIds') && modifier.$pull) { if (_.contains(fieldNames, 'boardIds') && modifier.$pull) {
Cards.update( Cards.update(
{ boardId: modifier.$pull.boardIds, 'customFields._id': doc._id }, { boardId: modifier.$pull.boardIds, 'customFields._id': doc._id },
{ $pull: { customFields: { _id: doc._id } } }, { $pull: { customFields: { _id: doc._id } } },
{ multi: true } { multi: true },
); );
customFieldEdit(userId, doc); customFieldEdit(userId, doc);
Activities.remove({ Activities.remove({
@ -223,7 +218,7 @@ if (Meteor.isServer) {
Cards.update( Cards.update(
{ boardId: { $in: doc.boardIds }, 'customFields._id': doc._id }, { boardId: { $in: doc.boardIds }, 'customFields._id': doc._id },
{ $pull: { customFields: { _id: doc._id } } }, { $pull: { customFields: { _id: doc._id } } },
{ multi: true } { multi: true },
); );
}); });
} }
@ -241,7 +236,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function( JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function(
req, req,
res res,
) { ) {
Authentication.checkUserId(req.userId); Authentication.checkUserId(req.userId);
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
@ -254,7 +249,7 @@ if (Meteor.isServer) {
name: cf.name, name: cf.name,
type: cf.type, type: cf.type,
}; };
} },
), ),
}); });
}); });
@ -281,7 +276,7 @@ if (Meteor.isServer) {
boardIds: { $in: [paramBoardId] }, boardIds: { $in: [paramBoardId] },
}), }),
}); });
} },
); );
/** /**
@ -299,7 +294,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function( JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function(
req, req,
res res,
) { ) {
Authentication.checkUserId(req.userId); Authentication.checkUserId(req.userId);
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
@ -351,7 +346,7 @@ if (Meteor.isServer) {
_id: id, _id: id,
}, },
}); });
} },
); );
} }

View file

@ -57,7 +57,10 @@ export class Exporter {
build() { build() {
const byBoard = { boardId: this._boardId }; const byBoard = { boardId: this._boardId };
const byBoardNoLinked = { boardId: this._boardId, linkedId: {$in: ['', null] } }; const byBoardNoLinked = {
boardId: this._boardId,
linkedId: { $in: ['', null] },
};
// we do not want to retrieve boardId in related elements // we do not want to retrieve boardId in related elements
const noBoardId = { const noBoardId = {
fields: { fields: {
@ -67,15 +70,21 @@ export class Exporter {
const result = { const result = {
_format: 'wekan-board-1.0.0', _format: 'wekan-board-1.0.0',
}; };
_.extend(result, Boards.findOne(this._boardId, { _.extend(
fields: { result,
stars: 0, Boards.findOne(this._boardId, {
}, fields: {
})); stars: 0,
},
}),
);
result.lists = Lists.find(byBoard, noBoardId).fetch(); result.lists = Lists.find(byBoard, noBoardId).fetch();
result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch(); result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch(); result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
result.customFields = CustomFields.find({boardIds: {$in: [this.boardId]}}, {fields: {boardId: 0}}).fetch(); result.customFields = CustomFields.find(
{ boardIds: { $in: [this.boardId] } },
{ fields: { boardId: 0 } },
).fetch();
result.comments = CardComments.find(byBoard, noBoardId).fetch(); result.comments = CardComments.find(byBoard, noBoardId).fetch();
result.activities = Activities.find(byBoard, noBoardId).fetch(); result.activities = Activities.find(byBoard, noBoardId).fetch();
result.rules = Rules.find(byBoard, noBoardId).fetch(); result.rules = Rules.find(byBoard, noBoardId).fetch();
@ -84,24 +93,40 @@ export class Exporter {
result.subtaskItems = []; result.subtaskItems = [];
result.triggers = []; result.triggers = [];
result.actions = []; result.actions = [];
result.cards.forEach((card) => { result.cards.forEach(card => {
result.checklists.push(...Checklists.find({ result.checklists.push(
cardId: card._id, ...Checklists.find({
}).fetch()); cardId: card._id,
result.checklistItems.push(...ChecklistItems.find({ }).fetch(),
cardId: card._id, );
}).fetch()); result.checklistItems.push(
result.subtaskItems.push(...Cards.find({ ...ChecklistItems.find({
parentid: card._id, cardId: card._id,
}).fetch()); }).fetch(),
);
result.subtaskItems.push(
...Cards.find({
parentid: card._id,
}).fetch(),
);
}); });
result.rules.forEach((rule) => { result.rules.forEach(rule => {
result.triggers.push(...Triggers.find({ result.triggers.push(
_id: rule.triggerId, ...Triggers.find(
}, noBoardId).fetch()); {
result.actions.push(...Actions.find({ _id: rule.triggerId,
_id: rule.actionId, },
}, noBoardId).fetch()); noBoardId,
).fetch(),
);
result.actions.push(
...Actions.find(
{
_id: rule.actionId,
},
noBoardId,
).fetch(),
);
}); });
// [Old] for attachments we only export IDs and absolute url to original doc // [Old] for attachments we only export IDs and absolute url to original doc
@ -122,43 +147,45 @@ export class Exporter {
}); });
}; };
const getBase64DataSync = Meteor.wrapAsync(getBase64Data); const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
result.attachments = Attachments.find(byBoard).fetch().map((attachment) => { result.attachments = Attachments.find(byBoard)
return { .fetch()
_id: attachment._id, .map(attachment => {
cardId: attachment.cardId, return {
// url: FlowRouter.url(attachment.url()), _id: attachment._id,
file: getBase64DataSync(attachment), cardId: attachment.cardId,
name: attachment.original.name, // url: FlowRouter.url(attachment.url()),
type: attachment.original.type, file: getBase64DataSync(attachment),
}; name: attachment.original.name,
}); type: attachment.original.type,
};
});
// we also have to export some user data - as the other elements only // we also have to export some user data - as the other elements only
// include id but we have to be careful: // include id but we have to be careful:
// 1- only exports users that are linked somehow to that board // 1- only exports users that are linked somehow to that board
// 2- do not export any sensitive information // 2- do not export any sensitive information
const users = {}; const users = {};
result.members.forEach((member) => { result.members.forEach(member => {
users[member.userId] = true; users[member.userId] = true;
}); });
result.lists.forEach((list) => { result.lists.forEach(list => {
users[list.userId] = true; users[list.userId] = true;
}); });
result.cards.forEach((card) => { result.cards.forEach(card => {
users[card.userId] = true; users[card.userId] = true;
if (card.members) { if (card.members) {
card.members.forEach((memberId) => { card.members.forEach(memberId => {
users[memberId] = true; users[memberId] = true;
}); });
} }
}); });
result.comments.forEach((comment) => { result.comments.forEach(comment => {
users[comment.userId] = true; users[comment.userId] = true;
}); });
result.activities.forEach((activity) => { result.activities.forEach(activity => {
users[activity.userId] = true; users[activity.userId] = true;
}); });
result.checklists.forEach((checklist) => { result.checklists.forEach(checklist => {
users[checklist.userId] = true; users[checklist.userId] = true;
}); });
const byUserIds = { const byUserIds = {
@ -177,13 +204,15 @@ export class Exporter {
'profile.avatarUrl': 1, 'profile.avatarUrl': 1,
}, },
}; };
result.users = Users.find(byUserIds, userFields).fetch().map((user) => { result.users = Users.find(byUserIds, userFields)
// user avatar is stored as a relative url, we export absolute .fetch()
if ((user.profile || {}).avatarUrl) { .map(user => {
user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl); // user avatar is stored as a relative url, we export absolute
} if ((user.profile || {}).avatarUrl) {
return user; user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
}); }
return user;
});
return result; return result;
} }

View file

@ -1,6 +1,6 @@
import { TrelloCreator } from './trelloCreator'; import { TrelloCreator } from './trelloCreator';
import { WekanCreator } from './wekanCreator'; import { WekanCreator } from './wekanCreator';
import {Exporter} from './export'; import { Exporter } from './export';
import wekanMembersMapper from './wekanmapper'; import wekanMembersMapper from './wekanmapper';
Meteor.methods({ Meteor.methods({
@ -11,12 +11,12 @@ Meteor.methods({
check(currentBoard, Match.Maybe(String)); check(currentBoard, Match.Maybe(String));
let creator; let creator;
switch (importSource) { switch (importSource) {
case 'trello': case 'trello':
creator = new TrelloCreator(data); creator = new TrelloCreator(data);
break; break;
case 'wekan': case 'wekan':
creator = new WekanCreator(data); creator = new WekanCreator(data);
break; break;
} }
// 1. check all parameters are ok from a syntax point of view // 1. check all parameters are ok from a syntax point of view
@ -38,11 +38,9 @@ Meteor.methods({
const data = exporter.build(); const data = exporter.build();
const addData = {}; const addData = {};
addData.membersMapping = wekanMembersMapper.getMembersToMap(data); addData.membersMapping = wekanMembersMapper.getMembersToMap(data);
const creator = new WekanCreator(addData); const creator = new WekanCreator(addData);
//data.title = `${data.title } - ${ TAPi18n.__('copy-tag')}`; //data.title = `${data.title } - ${ TAPi18n.__('copy-tag')}`;
data.title = `${data.title}`; data.title = `${data.title}`;
return creator.create(data, currentBoardId); return creator.create(data, currentBoardId);
}, },
}); });

View file

@ -86,14 +86,9 @@ Integrations.attachSchema(
*/ */
type: String, type: String,
}, },
}) }),
); );
Integrations.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
Integrations.allow({ Integrations.allow({
insert(userId, doc) { insert(userId, doc) {
return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId)); return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
@ -123,7 +118,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function( JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function(
req, req,
res res,
) { ) {
try { try {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
@ -131,7 +126,7 @@ if (Meteor.isServer) {
const data = Integrations.find( const data = Integrations.find(
{ boardId: paramBoardId }, { boardId: paramBoardId },
{ fields: { token: 0 } } { fields: { token: 0 } },
).map(function(doc) { ).map(function(doc) {
return doc; return doc;
}); });
@ -155,7 +150,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function( JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function(
req, req,
res res,
) { ) {
try { try {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
@ -166,7 +161,7 @@ if (Meteor.isServer) {
code: 200, code: 200,
data: Integrations.findOne( data: Integrations.findOne(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ fields: { token: 0 } } { fields: { token: 0 } },
), ),
}); });
} catch (error) { } catch (error) {
@ -187,7 +182,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('POST', '/api/boards/:boardId/integrations', function( JsonRoutes.add('POST', '/api/boards/:boardId/integrations', function(
req, req,
res res,
) { ) {
try { try {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
@ -228,7 +223,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('PUT', '/api/boards/:boardId/integrations/:intId', function( JsonRoutes.add('PUT', '/api/boards/:boardId/integrations/:intId', function(
req, req,
res res,
) { ) {
try { try {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
@ -239,35 +234,35 @@ if (Meteor.isServer) {
const newEnabled = req.body.enabled; const newEnabled = req.body.enabled;
Integrations.direct.update( Integrations.direct.update(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ $set: { enabled: newEnabled } } { $set: { enabled: newEnabled } },
); );
} }
if (req.body.hasOwnProperty('title')) { if (req.body.hasOwnProperty('title')) {
const newTitle = req.body.title; const newTitle = req.body.title;
Integrations.direct.update( Integrations.direct.update(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ $set: { title: newTitle } } { $set: { title: newTitle } },
); );
} }
if (req.body.hasOwnProperty('url')) { if (req.body.hasOwnProperty('url')) {
const newUrl = req.body.url; const newUrl = req.body.url;
Integrations.direct.update( Integrations.direct.update(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ $set: { url: newUrl } } { $set: { url: newUrl } },
); );
} }
if (req.body.hasOwnProperty('token')) { if (req.body.hasOwnProperty('token')) {
const newToken = req.body.token; const newToken = req.body.token;
Integrations.direct.update( Integrations.direct.update(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ $set: { token: newToken } } { $set: { token: newToken } },
); );
} }
if (req.body.hasOwnProperty('activities')) { if (req.body.hasOwnProperty('activities')) {
const newActivities = req.body.activities; const newActivities = req.body.activities;
Integrations.direct.update( Integrations.direct.update(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ $set: { activities: newActivities } } { $set: { activities: newActivities } },
); );
} }
@ -306,14 +301,14 @@ if (Meteor.isServer) {
Integrations.direct.update( Integrations.direct.update(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ $pullAll: { activities: newActivities } } { $pullAll: { activities: newActivities } },
); );
JsonRoutes.sendResult(res, { JsonRoutes.sendResult(res, {
code: 200, code: 200,
data: Integrations.findOne( data: Integrations.findOne(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ fields: { _id: 1, activities: 1 } } { fields: { _id: 1, activities: 1 } },
), ),
}); });
} catch (error) { } catch (error) {
@ -322,7 +317,7 @@ if (Meteor.isServer) {
data: error, data: error,
}); });
} }
} },
); );
/** /**
@ -346,14 +341,14 @@ if (Meteor.isServer) {
Integrations.direct.update( Integrations.direct.update(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ $addToSet: { activities: { $each: newActivities } } } { $addToSet: { activities: { $each: newActivities } } },
); );
JsonRoutes.sendResult(res, { JsonRoutes.sendResult(res, {
code: 200, code: 200,
data: Integrations.findOne( data: Integrations.findOne(
{ _id: paramIntId, boardId: paramBoardId }, { _id: paramIntId, boardId: paramBoardId },
{ fields: { _id: 1, activities: 1 } } { fields: { _id: 1, activities: 1 } },
), ),
}); });
} catch (error) { } catch (error) {
@ -362,7 +357,7 @@ if (Meteor.isServer) {
data: error, data: error,
}); });
} }
} },
); );
/** /**
@ -375,7 +370,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId', function( JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId', function(
req, req,
res res,
) { ) {
try { try {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;

View file

@ -47,7 +47,7 @@ InvitationCodes.attachSchema(
type: Boolean, type: Boolean,
defaultValue: true, defaultValue: true,
}, },
}) }),
); );
InvitationCodes.helpers({ InvitationCodes.helpers({
@ -56,11 +56,6 @@ InvitationCodes.helpers({
}, },
}); });
InvitationCodes.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
// InvitationCodes.before.insert((userId, doc) => { // InvitationCodes.before.insert((userId, doc) => {
// doc.createdAt = new Date(); // doc.createdAt = new Date();
// doc.authorId = userId; // doc.authorId = userId;

View file

@ -156,7 +156,7 @@ Lists.attachSchema(
type: String, type: String,
defaultValue: 'list', defaultValue: 'list',
}, },
}) }),
); );
Lists.allow({ Lists.allow({
@ -198,7 +198,7 @@ Lists.helpers({
swimlaneId: oldSwimlaneId, swimlaneId: oldSwimlaneId,
listId: oldId, listId: oldId,
archived: false, archived: false,
}).forEach((card) => { }).forEach(card => {
card.copy(boardId, swimlaneId, _id); card.copy(boardId, swimlaneId, _id);
}); });
}, },
@ -262,7 +262,7 @@ Lists.mutations({
archive() { archive() {
if (this.isTemplateList()) { if (this.isTemplateList()) {
this.cards().forEach((card) => { this.cards().forEach(card => {
return card.archive(); return card.archive();
}); });
} }
@ -271,7 +271,7 @@ Lists.mutations({
restore() { restore() {
if (this.isTemplateList()) { if (this.isTemplateList()) {
this.allCards().forEach((card) => { this.allCards().forEach(card => {
return card.restore(); return card.restore();
}); });
} }
@ -346,15 +346,10 @@ if (Meteor.isServer) {
}); });
}); });
Lists.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
Lists.before.remove((userId, doc) => { Lists.before.remove((userId, doc) => {
const cards = Cards.find({ listId: doc._id }); const cards = Cards.find({ listId: doc._id });
if (cards) { if (cards) {
cards.forEach((card) => { cards.forEach(card => {
Cards.remove(card._id); Cards.remove(card._id);
}); });
} }
@ -404,7 +399,7 @@ if (Meteor.isServer) {
_id: doc._id, _id: doc._id,
title: doc.title, title: doc.title,
}; };
} },
), ),
}); });
} catch (error) { } catch (error) {
@ -425,7 +420,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', function( JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', function(
req, req,
res res,
) { ) {
try { try {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
@ -492,7 +487,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId', function( JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId', function(
req, req,
res res,
) { ) {
try { try {
Authentication.checkUserId(req.userId); Authentication.checkUserId(req.userId);

View file

@ -44,7 +44,7 @@ Rules.attachSchema(
} }
}, },
}, },
}) }),
); );
Rules.mutations({ Rules.mutations({
@ -74,11 +74,6 @@ Rules.allow({
}, },
}); });
Rules.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
if (Meteor.isServer) { if (Meteor.isServer) {
Meteor.startup(() => { Meteor.startup(() => {
Rules._collection._ensureIndex({ modifiedAt: -1 }); Rules._collection._ensureIndex({ modifiedAt: -1 });

View file

@ -76,7 +76,7 @@ Settings.attachSchema(
} }
}, },
}, },
}) }),
); );
Settings.helpers({ Settings.helpers({
mailUrl() { mailUrl() {
@ -88,7 +88,7 @@ Settings.helpers({
return `${protocol}${this.mailServer.host}:${this.mailServer.port}/`; return `${protocol}${this.mailServer.host}:${this.mailServer.port}/`;
} }
return `${protocol}${this.mailServer.username}:${encodeURIComponent( return `${protocol}${this.mailServer.username}:${encodeURIComponent(
this.mailServer.password this.mailServer.password,
)}@${this.mailServer.host}:${this.mailServer.port}/`; )}@${this.mailServer.host}:${this.mailServer.port}/`;
}, },
}); });
@ -99,11 +99,6 @@ Settings.allow({
}, },
}); });
Settings.before.update((userId, doc, fieldNames, modifier) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = new Date();
});
if (Meteor.isServer) { if (Meteor.isServer) {
Meteor.startup(() => { Meteor.startup(() => {
Settings._collection._ensureIndex({ modifiedAt: -1 }); Settings._collection._ensureIndex({ modifiedAt: -1 });
@ -111,7 +106,7 @@ if (Meteor.isServer) {
if (!setting) { if (!setting) {
const now = new Date(); const now = new Date();
const domain = process.env.ROOT_URL.match( const domain = process.env.ROOT_URL.match(
/\/\/(?:www\.)?(.*)?(?:\/)?/ /\/\/(?:www\.)?(.*)?(?:\/)?/,
)[1]; )[1];
const from = `Boards Support <support@${domain}>`; const from = `Boards Support <support@${domain}>`;
const defaultSetting = { const defaultSetting = {
@ -143,9 +138,7 @@ if (Meteor.isServer) {
if (_.contains(fieldNames, 'mailServer') && doc.mailServer.host) { if (_.contains(fieldNames, 'mailServer') && doc.mailServer.host) {
const protocol = doc.mailServer.enableTLS ? 'smtps://' : 'smtp://'; const protocol = doc.mailServer.enableTLS ? 'smtps://' : 'smtp://';
if (!doc.mailServer.username && !doc.mailServer.password) { if (!doc.mailServer.username && !doc.mailServer.password) {
process.env.MAIL_URL = `${protocol}${doc.mailServer.host}:${ process.env.MAIL_URL = `${protocol}${doc.mailServer.host}:${doc.mailServer.port}/`;
doc.mailServer.port
}/`;
} else { } else {
process.env.MAIL_URL = `${protocol}${ process.env.MAIL_URL = `${protocol}${
doc.mailServer.username doc.mailServer.username
@ -220,14 +213,14 @@ if (Meteor.isServer) {
if (!user.isAdmin) { if (!user.isAdmin) {
throw new Meteor.Error('not-allowed'); throw new Meteor.Error('not-allowed');
} }
emails.forEach((email) => { emails.forEach(email => {
if (email && SimpleSchema.RegEx.Email.test(email)) { if (email && SimpleSchema.RegEx.Email.test(email)) {
// Checks if the email is already link to an account. // Checks if the email is already link to an account.
const userExist = Users.findOne({ email }); const userExist = Users.findOne({ email });
if (userExist) { if (userExist) {
throw new Meteor.Error( throw new Meteor.Error(
'user-exist', 'user-exist',
`The user with the email ${email} has already an account.` `The user with the email ${email} has already an account.`,
); );
} }
// Checks if the email is already link to an invitation. // Checks if the email is already link to an invitation.
@ -253,10 +246,10 @@ if (Meteor.isServer) {
} else { } else {
throw new Meteor.Error( throw new Meteor.Error(
'invitation-generated-fail', 'invitation-generated-fail',
err.message err.message,
); );
} }
} },
); );
} }
} }
@ -284,7 +277,7 @@ if (Meteor.isServer) {
throw new Meteor.Error( throw new Meteor.Error(
'email-fail', 'email-fail',
`${TAPi18n.__('email-fail-text', { lng: lang })}: ${message}`, `${TAPi18n.__('email-fail-text', { lng: lang })}: ${message}`,
message message,
); );
} }
return { return {

View file

@ -120,7 +120,7 @@ Swimlanes.attachSchema(
type: String, type: String,
defaultValue: 'swimlane', defaultValue: 'swimlane',
}, },
}) }),
); );
Swimlanes.allow({ Swimlanes.allow({
@ -153,7 +153,7 @@ Swimlanes.helpers({
} }
// Copy all lists in swimlane // Copy all lists in swimlane
Lists.find(query).forEach((list) => { Lists.find(query).forEach(list => {
list.type = 'list'; list.type = 'list';
list.swimlaneId = oldId; list.swimlaneId = oldId;
list.boardId = boardId; list.boardId = boardId;
@ -167,7 +167,7 @@ Swimlanes.helpers({
swimlaneId: this._id, swimlaneId: this._id,
archived: false, archived: false,
}), }),
{ sort: ['sort'] } { sort: ['sort'] },
); );
}, },
@ -178,7 +178,7 @@ Swimlanes.helpers({
swimlaneId: { $in: [this._id, ''] }, swimlaneId: { $in: [this._id, ''] },
archived: false, archived: false,
}, },
{ sort: ['sort'] } { sort: ['sort'] },
); );
}, },
@ -234,7 +234,7 @@ Swimlanes.mutations({
archive() { archive() {
if (this.isTemplateSwimlane()) { if (this.isTemplateSwimlane()) {
this.myLists().forEach((list) => { this.myLists().forEach(list => {
return list.archive(); return list.archive();
}); });
} }
@ -243,7 +243,7 @@ Swimlanes.mutations({
restore() { restore() {
if (this.isTemplateSwimlane()) { if (this.isTemplateSwimlane()) {
this.myLists().forEach((list) => { this.myLists().forEach(list => {
return list.restore(); return list.restore();
}); });
} }
@ -262,11 +262,6 @@ Swimlanes.mutations({
}, },
}); });
Swimlanes.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
Swimlanes.hookOptions.after.update = { fetchPrevious: false }; Swimlanes.hookOptions.after.update = { fetchPrevious: false };
if (Meteor.isServer) { if (Meteor.isServer) {
@ -292,11 +287,11 @@ if (Meteor.isServer) {
swimlaneId: { $in: [doc._id, ''] }, swimlaneId: { $in: [doc._id, ''] },
archived: false, archived: false,
}, },
{ sort: ['sort'] } { sort: ['sort'] },
); );
if (lists.count() < 2) { if (lists.count() < 2) {
lists.forEach((list) => { lists.forEach(list => {
list.remove(); list.remove();
}); });
} else { } else {
@ -350,7 +345,7 @@ if (Meteor.isServer) {
_id: doc._id, _id: doc._id,
title: doc.title, title: doc.title,
}; };
} },
), ),
}); });
} catch (error) { } catch (error) {
@ -372,7 +367,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', function( JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', function(
req, req,
res res,
) { ) {
try { try {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
@ -459,7 +454,7 @@ if (Meteor.isServer) {
data: error, data: error,
}); });
} }
} },
); );
} }

View file

@ -1,4 +1,4 @@
const DateString = Match.Where(function (dateAsString) { const DateString = Match.Where(function(dateAsString) {
check(dateAsString, String); check(dateAsString, String);
return moment(dateAsString, moment.ISO_8601).isValid(); return moment(dateAsString, moment.ISO_8601).isValid();
}); });
@ -52,10 +52,10 @@ export class TrelloCreator {
* @param {String} dateString a properly formatted Date * @param {String} dateString a properly formatted Date
*/ */
_now(dateString) { _now(dateString) {
if(dateString) { if (dateString) {
return new Date(dateString); return new Date(dateString);
} }
if(!this._nowDate) { if (!this._nowDate) {
this._nowDate = new Date(); this._nowDate = new Date();
} }
return this._nowDate; return this._nowDate;
@ -67,75 +67,90 @@ export class TrelloCreator {
* Otherwise return current logged user. * Otherwise return current logged user.
* @param trelloUserId * @param trelloUserId
* @private * @private
*/ */
_user(trelloUserId) { _user(trelloUserId) {
if(trelloUserId && this.members[trelloUserId]) { if (trelloUserId && this.members[trelloUserId]) {
return this.members[trelloUserId]; return this.members[trelloUserId];
} }
return Meteor.userId(); return Meteor.userId();
} }
checkActions(trelloActions) { checkActions(trelloActions) {
check(trelloActions, [Match.ObjectIncluding({ check(trelloActions, [
data: Object, Match.ObjectIncluding({
date: DateString, data: Object,
type: String, date: DateString,
})]); type: String,
}),
]);
// XXX we could perform more thorough checks based on action type // XXX we could perform more thorough checks based on action type
} }
checkBoard(trelloBoard) { checkBoard(trelloBoard) {
check(trelloBoard, Match.ObjectIncluding({ check(
closed: Boolean, trelloBoard,
name: String, Match.ObjectIncluding({
prefs: Match.ObjectIncluding({ closed: Boolean,
// XXX refine control by validating 'background' against a list of name: String,
// allowed values (is it worth the maintenance?) prefs: Match.ObjectIncluding({
background: String, // XXX refine control by validating 'background' against a list of
permissionLevel: Match.Where((value) => { // allowed values (is it worth the maintenance?)
return ['org', 'private', 'public'].indexOf(value)>= 0; background: String,
permissionLevel: Match.Where(value => {
return ['org', 'private', 'public'].indexOf(value) >= 0;
}),
}), }),
}), }),
})); );
} }
checkCards(trelloCards) { checkCards(trelloCards) {
check(trelloCards, [Match.ObjectIncluding({ check(trelloCards, [
closed: Boolean, Match.ObjectIncluding({
dateLastActivity: DateString, closed: Boolean,
desc: String, dateLastActivity: DateString,
idLabels: [String], desc: String,
idMembers: [String], idLabels: [String],
name: String, idMembers: [String],
pos: Number, name: String,
})]); pos: Number,
}),
]);
} }
checkLabels(trelloLabels) { checkLabels(trelloLabels) {
check(trelloLabels, [Match.ObjectIncluding({ check(trelloLabels, [
// XXX refine control by validating 'color' against a list of allowed Match.ObjectIncluding({
// values (is it worth the maintenance?) // XXX refine control by validating 'color' against a list of allowed
name: String, // values (is it worth the maintenance?)
})]); name: String,
}),
]);
} }
checkLists(trelloLists) { checkLists(trelloLists) {
check(trelloLists, [Match.ObjectIncluding({ check(trelloLists, [
closed: Boolean, Match.ObjectIncluding({
name: String, closed: Boolean,
})]); name: String,
}),
]);
} }
checkChecklists(trelloChecklists) { checkChecklists(trelloChecklists) {
check(trelloChecklists, [Match.ObjectIncluding({ check(trelloChecklists, [
idBoard: String, Match.ObjectIncluding({
idCard: String, idBoard: String,
name: String, idCard: String,
checkItems: [Match.ObjectIncluding({
state: String,
name: String, name: String,
})], checkItems: [
})]); Match.ObjectIncluding({
state: String,
name: String,
}),
],
}),
]);
} }
// You must call parseActions before calling this one. // You must call parseActions before calling this one.
@ -146,31 +161,35 @@ export class TrelloCreator {
// very old boards won't have a creation activity so no creation date // very old boards won't have a creation activity so no creation date
createdAt: this._now(this.createdAt.board), createdAt: this._now(this.createdAt.board),
labels: [], labels: [],
members: [{ members: [
userId: Meteor.userId(), {
isAdmin: true, userId: Meteor.userId(),
isActive: true, isAdmin: true,
isNoComments: false, isActive: true,
isCommentOnly: false, isNoComments: false,
swimlaneId: false, isCommentOnly: false,
}], swimlaneId: false,
},
],
permission: this.getPermission(trelloBoard.prefs.permissionLevel), permission: this.getPermission(trelloBoard.prefs.permissionLevel),
slug: getSlug(trelloBoard.name) || 'board', slug: getSlug(trelloBoard.name) || 'board',
stars: 0, stars: 0,
title: trelloBoard.name, title: trelloBoard.name,
}; };
// now add other members // now add other members
if(trelloBoard.memberships) { if (trelloBoard.memberships) {
trelloBoard.memberships.forEach((trelloMembership) => { trelloBoard.memberships.forEach(trelloMembership => {
const trelloId = trelloMembership.idMember; const trelloId = trelloMembership.idMember;
// do we have a mapping? // do we have a mapping?
if(this.members[trelloId]) { if (this.members[trelloId]) {
const wekanId = this.members[trelloId]; const wekanId = this.members[trelloId];
// do we already have it in our list? // do we already have it in our list?
const wekanMember = boardToCreate.members.find((wekanMember) => wekanMember.userId === wekanId); const wekanMember = boardToCreate.members.find(
if(wekanMember) { wekanMember => wekanMember.userId === wekanId,
);
if (wekanMember) {
// we're already mapped, but maybe with lower rights // we're already mapped, but maybe with lower rights
if(!wekanMember.isAdmin) { if (!wekanMember.isAdmin) {
wekanMember.isAdmin = this.getAdmin(trelloMembership.memberType); wekanMember.isAdmin = this.getAdmin(trelloMembership.memberType);
} }
} else { } else {
@ -186,7 +205,7 @@ export class TrelloCreator {
} }
}); });
} }
trelloBoard.labels.forEach((label) => { trelloBoard.labels.forEach(label => {
const labelToCreate = { const labelToCreate = {
_id: Random.id(6), _id: Random.id(6),
color: label.color ? label.color : 'black', color: label.color ? label.color : 'black',
@ -198,7 +217,7 @@ export class TrelloCreator {
boardToCreate.labels.push(labelToCreate); boardToCreate.labels.push(labelToCreate);
}); });
const boardId = Boards.direct.insert(boardToCreate); const boardId = Boards.direct.insert(boardToCreate);
Boards.direct.update(boardId, {$set: {modifiedAt: this._now()}}); Boards.direct.update(boardId, { $set: { modifiedAt: this._now() } });
// log activity // log activity
Activities.direct.insert({ Activities.direct.insert({
activityType: 'importBoard', activityType: 'importBoard',
@ -225,7 +244,7 @@ export class TrelloCreator {
*/ */
createCards(trelloCards, boardId) { createCards(trelloCards, boardId) {
const result = []; const result = [];
trelloCards.forEach((card) => { trelloCards.forEach(card => {
const cardToCreate = { const cardToCreate = {
archived: card.closed, archived: card.closed,
boardId, boardId,
@ -243,26 +262,26 @@ export class TrelloCreator {
}; };
// add labels // add labels
if (card.idLabels) { if (card.idLabels) {
cardToCreate.labelIds = card.idLabels.map((trelloId) => { cardToCreate.labelIds = card.idLabels.map(trelloId => {
return this.labels[trelloId]; return this.labels[trelloId];
}); });
} }
// add members { // add members {
if(card.idMembers) { if (card.idMembers) {
const wekanMembers = []; const wekanMembers = [];
// we can't just map, as some members may not have been mapped // we can't just map, as some members may not have been mapped
card.idMembers.forEach((trelloId) => { card.idMembers.forEach(trelloId => {
if(this.members[trelloId]) { if (this.members[trelloId]) {
const wekanId = this.members[trelloId]; const wekanId = this.members[trelloId];
// we may map multiple Trello members to the same wekan user // we may map multiple Trello members to the same wekan user
// in which case we risk adding the same user multiple times // in which case we risk adding the same user multiple times
if(!wekanMembers.find((wId) => wId === wekanId)){ if (!wekanMembers.find(wId => wId === wekanId)) {
wekanMembers.push(wekanId); wekanMembers.push(wekanId);
} }
} }
return true; return true;
}); });
if(wekanMembers.length>0) { if (wekanMembers.length > 0) {
cardToCreate.members = wekanMembers; cardToCreate.members = wekanMembers;
} }
} }
@ -289,7 +308,7 @@ export class TrelloCreator {
// add comments // add comments
const comments = this.comments[card.id]; const comments = this.comments[card.id];
if (comments) { if (comments) {
comments.forEach((comment) => { comments.forEach(comment => {
const commentToCreate = { const commentToCreate = {
boardId, boardId,
cardId, cardId,
@ -318,15 +337,15 @@ export class TrelloCreator {
const attachments = this.attachments[card.id]; const attachments = this.attachments[card.id];
const trelloCoverId = card.idAttachmentCover; const trelloCoverId = card.idAttachmentCover;
if (attachments) { if (attachments) {
attachments.forEach((att) => { attachments.forEach(att => {
const file = new FS.File(); const file = new FS.File();
// Simulating file.attachData on the client generates multiple errors // Simulating file.attachData on the client generates multiple errors
// - HEAD returns null, which causes exception down the line // - HEAD returns null, which causes exception down the line
// - the template then tries to display the url to the attachment which causes other errors // - the template then tries to display the url to the attachment which causes other errors
// so we make it server only, and let UI catch up once it is done, forget about latency comp. // so we make it server only, and let UI catch up once it is done, forget about latency comp.
const self = this; const self = this;
if(Meteor.isServer) { if (Meteor.isServer) {
file.attachData(att.url, function (error) { file.attachData(att.url, function(error) {
file.boardId = boardId; file.boardId = boardId;
file.cardId = cardId; file.cardId = cardId;
file.userId = self._user(att.idMemberCreator); file.userId = self._user(att.idMemberCreator);
@ -334,15 +353,17 @@ export class TrelloCreator {
// attachments' related activities automatically // attachments' related activities automatically
file.source = 'import'; file.source = 'import';
if (error) { if (error) {
throw(error); throw error;
} else { } else {
const wekanAtt = Attachments.insert(file, () => { const wekanAtt = Attachments.insert(file, () => {
// we do nothing // we do nothing
}); });
self.attachmentIds[att.id] = wekanAtt._id; self.attachmentIds[att.id] = wekanAtt._id;
// //
if(trelloCoverId === att.id) { if (trelloCoverId === att.id) {
Cards.direct.update(cardId, { $set: {coverId: wekanAtt._id}}); Cards.direct.update(cardId, {
$set: { coverId: wekanAtt._id },
});
} }
} }
}); });
@ -357,7 +378,7 @@ export class TrelloCreator {
// Create labels if they do not exist and load this.labels. // Create labels if they do not exist and load this.labels.
createLabels(trelloLabels, board) { createLabels(trelloLabels, board) {
trelloLabels.forEach((label) => { trelloLabels.forEach(label => {
const color = label.color; const color = label.color;
const name = label.name; const name = label.name;
const existingLabel = board.getLabel(name, color); const existingLabel = board.getLabel(name, color);
@ -371,7 +392,7 @@ export class TrelloCreator {
} }
createLists(trelloLists, boardId) { createLists(trelloLists, boardId) {
trelloLists.forEach((list) => { trelloLists.forEach(list => {
const listToCreate = { const listToCreate = {
archived: list.closed, archived: list.closed,
boardId, boardId,
@ -384,7 +405,7 @@ export class TrelloCreator {
sort: list.pos, sort: list.pos,
}; };
const listId = Lists.direct.insert(listToCreate); const listId = Lists.direct.insert(listToCreate);
Lists.direct.update(listId, {$set: {'updatedAt': this._now()}}); Lists.direct.update(listId, { $set: { updatedAt: this._now() } });
this.lists[list.id] = listId; this.lists[list.id] = listId;
// log activity // log activity
// Activities.direct.insert({ // Activities.direct.insert({
@ -416,12 +437,12 @@ export class TrelloCreator {
sort: 1, sort: 1,
}; };
const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate); const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
Swimlanes.direct.update(swimlaneId, {$set: {'updatedAt': this._now()}}); Swimlanes.direct.update(swimlaneId, { $set: { updatedAt: this._now() } });
this.swimlane = swimlaneId; this.swimlane = swimlaneId;
} }
createChecklists(trelloChecklists) { createChecklists(trelloChecklists) {
trelloChecklists.forEach((checklist) => { trelloChecklists.forEach(checklist => {
if (this.cards[checklist.idCard]) { if (this.cards[checklist.idCard]) {
// Create the checklist // Create the checklist
const checklistToCreate = { const checklistToCreate = {
@ -435,7 +456,7 @@ export class TrelloCreator {
this.checklists[checklist.id] = checklistId; this.checklists[checklist.id] = checklistId;
// Now add the items to the checklistItems // Now add the items to the checklistItems
let counter = 0; let counter = 0;
checklist.checkItems.forEach((item) => { checklist.checkItems.forEach(item => {
counter++; counter++;
const checklistItemTocreate = { const checklistItemTocreate = {
_id: checklistId + counter, _id: checklistId + counter,
@ -458,15 +479,15 @@ export class TrelloCreator {
getColor(trelloColorCode) { getColor(trelloColorCode) {
// trello color name => wekan color // trello color name => wekan color
const mapColors = { const mapColors = {
'blue': 'belize', blue: 'belize',
'orange': 'pumpkin', orange: 'pumpkin',
'green': 'nephritis', green: 'nephritis',
'red': 'pomegranate', red: 'pomegranate',
'purple': 'wisteria', purple: 'wisteria',
'pink': 'pomegranate', pink: 'pomegranate',
'lime': 'nephritis', lime: 'nephritis',
'sky': 'belize', sky: 'belize',
'grey': 'midnight', grey: 'midnight',
}; };
const wekanColor = mapColors[trelloColorCode]; const wekanColor = mapColors[trelloColorCode];
return wekanColor || Boards.simpleSchema()._schema.color.allowedValues[0]; return wekanColor || Boards.simpleSchema()._schema.color.allowedValues[0];
@ -482,7 +503,7 @@ export class TrelloCreator {
} }
parseActions(trelloActions) { parseActions(trelloActions) {
trelloActions.forEach((action) => { trelloActions.forEach(action => {
if (action.type === 'addAttachmentToCard') { if (action.type === 'addAttachmentToCard') {
// We have to be cautious, because the attachment could have been removed later. // We have to be cautious, because the attachment could have been removed later.
// In that case Trello still reports its addition, but removes its 'url' field. // In that case Trello still reports its addition, but removes its 'url' field.
@ -490,11 +511,11 @@ export class TrelloCreator {
const trelloAttachment = action.data.attachment; const trelloAttachment = action.data.attachment;
// We need the idMemberCreator // We need the idMemberCreator
trelloAttachment.idMemberCreator = action.idMemberCreator; trelloAttachment.idMemberCreator = action.idMemberCreator;
if(trelloAttachment.url) { if (trelloAttachment.url) {
// we cannot actually create the Wekan attachment, because we don't yet // we cannot actually create the Wekan attachment, because we don't yet
// have the cards to attach it to, so we store it in the instance variable. // have the cards to attach it to, so we store it in the instance variable.
const trelloCardId = action.data.card.id; const trelloCardId = action.data.card.id;
if(!this.attachments[trelloCardId]) { if (!this.attachments[trelloCardId]) {
this.attachments[trelloCardId] = []; this.attachments[trelloCardId] = [];
} }
this.attachments[trelloCardId].push(trelloAttachment); this.attachments[trelloCardId].push(trelloAttachment);
@ -520,88 +541,89 @@ export class TrelloCreator {
} }
importActions(actions, boardId) { importActions(actions, boardId) {
actions.forEach((action) => { actions.forEach(action => {
switch (action.type) { switch (action.type) {
// Board related actions // Board related actions
// TODO: addBoardMember, removeBoardMember // TODO: addBoardMember, removeBoardMember
case 'createBoard': { case 'createBoard': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
type: 'board',
activityTypeId: boardId,
activityType: 'createBoard',
boardId,
createdAt: this._now(action.date),
});
break;
}
// List related activities
// TODO: removeList, archivedList
case 'createList': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
type: 'list',
activityType: 'createList',
listId: this.lists[action.data.list.id],
boardId,
createdAt: this._now(action.date),
});
break;
}
// Card related activities
// TODO: archivedCard, restoredCard, joinMember, unjoinMember
case 'createCard': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
activityType: 'createCard',
listId: this.lists[action.data.list.id],
cardId: this.cards[action.data.card.id],
boardId,
createdAt: this._now(action.date),
});
break;
}
case 'updateCard': {
if (action.data.old.idList) {
Activities.direct.insert({ Activities.direct.insert({
userId: this._user(action.idMemberCreator), userId: this._user(action.idMemberCreator),
oldListId: this.lists[action.data.old.idList], type: 'board',
activityType: 'moveCard', activityTypeId: boardId,
listId: this.lists[action.data.listAfter.id], activityType: 'createBoard',
boardId,
createdAt: this._now(action.date),
});
break;
}
// List related activities
// TODO: removeList, archivedList
case 'createList': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
type: 'list',
activityType: 'createList',
listId: this.lists[action.data.list.id],
boardId,
createdAt: this._now(action.date),
});
break;
}
// Card related activities
// TODO: archivedCard, restoredCard, joinMember, unjoinMember
case 'createCard': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
activityType: 'createCard',
listId: this.lists[action.data.list.id],
cardId: this.cards[action.data.card.id], cardId: this.cards[action.data.card.id],
boardId, boardId,
createdAt: this._now(action.date), createdAt: this._now(action.date),
}); });
break;
}
case 'updateCard': {
if (action.data.old.idList) {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
oldListId: this.lists[action.data.old.idList],
activityType: 'moveCard',
listId: this.lists[action.data.listAfter.id],
cardId: this.cards[action.data.card.id],
boardId,
createdAt: this._now(action.date),
});
}
break;
}
// Comment related activities
// Trello doesn't export the comment id
// Attachment related activities
case 'addAttachmentToCard': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
type: 'card',
activityType: 'addAttachment',
attachmentId: this.attachmentIds[action.data.attachment.id],
cardId: this.cards[action.data.card.id],
boardId,
createdAt: this._now(action.date),
});
break;
}
// Checklist related activities
case 'addChecklistToCard': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
activityType: 'addChecklist',
cardId: this.cards[action.data.card.id],
checklistId: this.checklists[action.data.checklist.id],
boardId,
createdAt: this._now(action.date),
});
break;
} }
break;
} }
// Comment related activities
// Trello doesn't export the comment id
// Attachment related activities
case 'addAttachmentToCard': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
type: 'card',
activityType: 'addAttachment',
attachmentId: this.attachmentIds[action.data.attachment.id],
cardId: this.cards[action.data.card.id],
boardId,
createdAt: this._now(action.date),
});
break;
}
// Checklist related activities
case 'addChecklistToCard': {
Activities.direct.insert({
userId: this._user(action.idMemberCreator),
activityType: 'addChecklist',
cardId: this.cards[action.data.card.id],
checklistId: this.checklists[action.data.checklist.id],
boardId,
createdAt: this._now(action.date),
});
break;
}}
// Trello doesn't have an add checklist item action // Trello doesn't have an add checklist item action
}); });
} }
@ -624,7 +646,9 @@ export class TrelloCreator {
create(board, currentBoardId) { create(board, currentBoardId) {
// TODO : Make isSandstorm variable global // TODO : Make isSandstorm variable global
const isSandstorm = Meteor.settings && Meteor.settings.public && const isSandstorm =
Meteor.settings &&
Meteor.settings.public &&
Meteor.settings.public.sandstorm; Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) { if (isSandstorm && currentBoardId) {
const currentBoard = Boards.findOne(currentBoardId); const currentBoard = Boards.findOne(currentBoardId);

View file

@ -51,18 +51,13 @@ Triggers.helpers({
labels() { labels() {
const boardLabels = this.board().labels; const boardLabels = this.board().labels;
const cardLabels = _.filter(boardLabels, (label) => { const cardLabels = _.filter(boardLabels, label => {
return _.contains(this.labelIds, label._id); return _.contains(this.labelIds, label._id);
}); });
return cardLabels; return cardLabels;
}, },
}); });
Triggers.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
if (Meteor.isServer) { if (Meteor.isServer) {
Meteor.startup(() => { Meteor.startup(() => {
Triggers._collection._ensureIndex({ modifiedAt: -1 }); Triggers._collection._ensureIndex({ modifiedAt: -1 });

View file

@ -46,14 +46,7 @@ UnsavedEditCollection.attachSchema(
} }
}, },
}, },
}) }),
);
UnsavedEditCollection.before.update(
(userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
}
); );
if (Meteor.isServer) { if (Meteor.isServer) {

View file

@ -247,7 +247,7 @@ Users.attachSchema(
optional: false, optional: false,
defaultValue: 'password', defaultValue: 'password',
}, },
}) }),
); );
Users.allow({ Users.allow({
@ -259,7 +259,7 @@ Users.allow({
const adminsNumber = Users.find({ isAdmin: true }).count(); const adminsNumber = Users.find({ isAdmin: true }).count();
const { isAdmin } = Users.findOne( const { isAdmin } = Users.findOne(
{ _id: userId }, { _id: userId },
{ fields: { isAdmin: 1 } } { fields: { isAdmin: 1 } },
); );
// Prevents remove of the only one administrator // Prevents remove of the only one administrator
@ -533,7 +533,7 @@ Meteor.methods({
check(email, String); check(email, String);
const existingUser = Users.findOne( const existingUser = Users.findOne(
{ 'emails.address': email }, { 'emails.address': email },
{ fields: { _id: 1 } } { fields: { _id: 1 } },
); );
if (existingUser) { if (existingUser) {
throw new Meteor.Error('email-already-taken'); throw new Meteor.Error('email-already-taken');
@ -700,7 +700,7 @@ if (Meteor.isServer) {
if (!options || !options.profile) { if (!options || !options.profile) {
throw new Meteor.Error( throw new Meteor.Error(
'error-invitation-code-blank', 'error-invitation-code-blank',
'The invitation code is required' 'The invitation code is required',
); );
} }
const invitationCode = InvitationCodes.findOne({ const invitationCode = InvitationCodes.findOne({
@ -711,7 +711,8 @@ if (Meteor.isServer) {
if (!invitationCode) { if (!invitationCode) {
throw new Meteor.Error( throw new Meteor.Error(
'error-invitation-code-not-exist', 'error-invitation-code-not-exist',
'The invitation code doesn\'t exist' // eslint-disable-next-line quotes
"The invitation code doesn't exist",
); );
} else { } else {
user.profile = { icode: options.profile.invitationcode }; user.profile = { icode: options.profile.invitationcode };
@ -722,18 +723,13 @@ if (Meteor.isServer) {
Meteor.bindEnvironment(() => { Meteor.bindEnvironment(() => {
InvitationCodes.remove({ _id: invitationCode._id }); InvitationCodes.remove({ _id: invitationCode._id });
}), }),
200 200,
); );
return user; return user;
} }
}); });
} }
Users.before.update((userId, doc, fieldNames, modifier, options) => {
modifier.$set = modifier.$set || {};
modifier.$set.modifiedAt = Date.now();
});
if (Meteor.isServer) { if (Meteor.isServer) {
// Let mongoDB ensure username unicity // Let mongoDB ensure username unicity
Meteor.startup(() => { Meteor.startup(() => {
@ -742,7 +738,7 @@ if (Meteor.isServer) {
{ {
username: 1, username: 1,
}, },
{ unique: true } { unique: true },
); );
}); });
@ -786,7 +782,7 @@ if (Meteor.isServer) {
// b. We use it to find deleted and newly inserted ids by using it in one // b. We use it to find deleted and newly inserted ids by using it in one
// direction and then in the other. // direction and then in the other.
function incrementBoards(boardsIds, inc) { function incrementBoards(boardsIds, inc) {
boardsIds.forEach((boardId) => { boardsIds.forEach(boardId => {
Boards.update(boardId, { $inc: { stars: inc } }); Boards.update(boardId, { $inc: { stars: inc } });
}); });
} }
@ -855,7 +851,7 @@ if (Meteor.isServer) {
Users.update(fakeUserId.get(), { Users.update(fakeUserId.get(), {
$set: { 'profile.cardTemplatesSwimlaneId': swimlaneId }, $set: { 'profile.cardTemplatesSwimlaneId': swimlaneId },
}); });
} },
); );
// Insert the list templates swimlane // Insert the list templates swimlane
@ -872,7 +868,7 @@ if (Meteor.isServer) {
Users.update(fakeUserId.get(), { Users.update(fakeUserId.get(), {
$set: { 'profile.listTemplatesSwimlaneId': swimlaneId }, $set: { 'profile.listTemplatesSwimlaneId': swimlaneId },
}); });
} },
); );
// Insert the board templates swimlane // Insert the board templates swimlane
@ -889,9 +885,9 @@ if (Meteor.isServer) {
Users.update(fakeUserId.get(), { Users.update(fakeUserId.get(), {
$set: { 'profile.boardTemplatesSwimlaneId': swimlaneId }, $set: { 'profile.boardTemplatesSwimlaneId': swimlaneId },
}); });
} },
); );
} },
); );
}); });
}); });
@ -921,7 +917,7 @@ if (Meteor.isServer) {
if (!invitationCode) { if (!invitationCode) {
throw new Meteor.Error('error-invitation-code-not-exist'); throw new Meteor.Error('error-invitation-code-not-exist');
} else { } else {
invitationCode.boardsToBeInvited.forEach((boardId) => { invitationCode.boardsToBeInvited.forEach(boardId => {
const board = Boards.findOne(boardId); const board = Boards.findOne(boardId);
board.addMember(doc._id); board.addMember(doc._id);
}); });
@ -1071,7 +1067,7 @@ if (Meteor.isServer) {
loginDisabled: true, loginDisabled: true,
'services.resume.loginTokens': '', 'services.resume.loginTokens': '',
}, },
} },
); );
} else if (action === 'enableLogin') { } else if (action === 'enableLogin') {
Users.update({ _id: id }, { $set: { loginDisabled: '' } }); Users.update({ _id: id }, { $set: { loginDisabled: '' } });
@ -1112,7 +1108,7 @@ if (Meteor.isServer) {
*/ */
JsonRoutes.add('POST', '/api/boards/:boardId/members/:userId/add', function( JsonRoutes.add('POST', '/api/boards/:boardId/members/:userId/add', function(
req, req,
res res,
) { ) {
try { try {
Authentication.checkUserId(req.userId); Authentication.checkUserId(req.userId);
@ -1136,7 +1132,7 @@ if (Meteor.isServer) {
isTrue(isAdmin), isTrue(isAdmin),
isTrue(isNoComments), isTrue(isNoComments),
isTrue(isCommentOnly), isTrue(isCommentOnly),
userId userId,
); );
} }
return { return {
@ -1207,7 +1203,7 @@ if (Meteor.isServer) {
data: error, data: error,
}); });
} }
} },
); );
/** /**

View file

@ -1,5 +1,5 @@
// simple version, only toggle watch / unwatch // simple version, only toggle watch / unwatch
const simpleWatchable = (collection) => { const simpleWatchable = collection => {
collection.attachSchema({ collection.attachSchema({
watchers: { watchers: {
type: [String], type: [String],
@ -24,8 +24,8 @@ const simpleWatchable = (collection) => {
collection.mutations({ collection.mutations({
setWatcher(userId, level) { setWatcher(userId, level) {
// if level undefined or null or false, then remove // if level undefined or null or false, then remove
if (!level) return { $pull: { watchers: userId }}; if (!level) return { $pull: { watchers: userId } };
return { $addToSet: { watchers: userId }}; return { $addToSet: { watchers: userId } };
}, },
}); });
}; };
@ -34,7 +34,7 @@ const simpleWatchable = (collection) => {
const complexWatchOptions = ['watching', 'tracking', 'muted']; const complexWatchOptions = ['watching', 'tracking', 'muted'];
const complexWatchDefault = 'muted'; const complexWatchDefault = 'muted';
const complexWatchable = (collection) => { const complexWatchable = collection => {
collection.attachSchema({ collection.attachSchema({
'watchers.$.userId': { 'watchers.$.userId': {
type: String, type: String,
@ -72,9 +72,9 @@ const complexWatchable = (collection) => {
setWatcher(userId, level) { setWatcher(userId, level) {
// if level undefined or null or false, then remove // if level undefined or null or false, then remove
if (level === complexWatchDefault) level = null; if (level === complexWatchDefault) level = null;
if (!level) return { $pull: { watchers: { userId }}}; if (!level) return { $pull: { watchers: { userId } } };
const index = this.watcherIndex(userId); const index = this.watcherIndex(userId);
if (index<0) return { $push: { watchers: { userId, level }}}; if (index < 0) return { $push: { watchers: { userId, level } } };
return { return {
$set: { $set: {
[`watchers.${index}.level`]: level, [`watchers.${index}.level`]: level,

View file

@ -87,99 +87,120 @@ export class WekanCreator {
} }
checkActivities(wekanActivities) { checkActivities(wekanActivities) {
check(wekanActivities, [Match.ObjectIncluding({ check(wekanActivities, [
activityType: String, Match.ObjectIncluding({
createdAt: DateString, activityType: String,
})]); createdAt: DateString,
}),
]);
// XXX we could perform more thorough checks based on action type // XXX we could perform more thorough checks based on action type
} }
checkBoard(wekanBoard) { checkBoard(wekanBoard) {
check(wekanBoard, Match.ObjectIncluding({ check(
archived: Boolean, wekanBoard,
title: String, Match.ObjectIncluding({
// XXX refine control by validating 'color' against a list of archived: Boolean,
// allowed values (is it worth the maintenance?) title: String,
color: String, // XXX refine control by validating 'color' against a list of
permission: Match.Where((value) => { // allowed values (is it worth the maintenance?)
return ['private', 'public'].indexOf(value) >= 0; color: String,
permission: Match.Where(value => {
return ['private', 'public'].indexOf(value) >= 0;
}),
}), }),
})); );
} }
checkCards(wekanCards) { checkCards(wekanCards) {
check(wekanCards, [Match.ObjectIncluding({ check(wekanCards, [
archived: Boolean, Match.ObjectIncluding({
dateLastActivity: DateString, archived: Boolean,
labelIds: [String], dateLastActivity: DateString,
title: String, labelIds: [String],
sort: Number, title: String,
})]); sort: Number,
}),
]);
} }
checkLabels(wekanLabels) { checkLabels(wekanLabels) {
check(wekanLabels, [Match.ObjectIncluding({ check(wekanLabels, [
// XXX refine control by validating 'color' against a list of allowed Match.ObjectIncluding({
// values (is it worth the maintenance?) // XXX refine control by validating 'color' against a list of allowed
color: String, // values (is it worth the maintenance?)
})]); color: String,
}),
]);
} }
checkLists(wekanLists) { checkLists(wekanLists) {
check(wekanLists, [Match.ObjectIncluding({ check(wekanLists, [
archived: Boolean, Match.ObjectIncluding({
title: String, archived: Boolean,
})]); title: String,
}),
]);
} }
checkSwimlanes(wekanSwimlanes) { checkSwimlanes(wekanSwimlanes) {
check(wekanSwimlanes, [Match.ObjectIncluding({ check(wekanSwimlanes, [
archived: Boolean, Match.ObjectIncluding({
title: String, archived: Boolean,
})]); title: String,
}),
]);
} }
checkChecklists(wekanChecklists) { checkChecklists(wekanChecklists) {
check(wekanChecklists, [Match.ObjectIncluding({ check(wekanChecklists, [
cardId: String, Match.ObjectIncluding({
title: String, cardId: String,
})]); title: String,
}),
]);
} }
checkChecklistItems(wekanChecklistItems) { checkChecklistItems(wekanChecklistItems) {
check(wekanChecklistItems, [Match.ObjectIncluding({ check(wekanChecklistItems, [
cardId: String, Match.ObjectIncluding({
title: String, cardId: String,
})]); title: String,
}),
]);
} }
checkRules(wekanRules) { checkRules(wekanRules) {
check(wekanRules, [Match.ObjectIncluding({ check(wekanRules, [
triggerId: String, Match.ObjectIncluding({
actionId: String, triggerId: String,
title: String, actionId: String,
})]); title: String,
}),
]);
} }
checkTriggers(wekanTriggers) { checkTriggers(wekanTriggers) {
// XXX More check based on trigger type // XXX More check based on trigger type
check(wekanTriggers, [Match.ObjectIncluding({ check(wekanTriggers, [
activityType: String, Match.ObjectIncluding({
desc: String, activityType: String,
})]); desc: String,
}),
]);
} }
getMembersToMap(data) { getMembersToMap(data) {
// we will work on the list itself (an ordered array of objects) when a // we will work on the list itself (an ordered array of objects) when a
// mapping is done, we add a 'wekan' field to the object representing the // mapping is done, we add a 'wekan' field to the object representing the
// imported member // imported member
const membersToMap = data.members; const membersToMap = data.members;
const users = data.users; const users = data.users;
// auto-map based on username // auto-map based on username
membersToMap.forEach((importedMember) => { membersToMap.forEach(importedMember => {
importedMember.id = importedMember.userId; importedMember.id = importedMember.userId;
delete importedMember.userId; delete importedMember.userId;
const user = users.filter((user) => { const user = users.filter(user => {
return user._id === importedMember.id; return user._id === importedMember.id;
})[0]; })[0];
if (user.profile && user.profile.fullname) { if (user.profile && user.profile.fullname) {
@ -196,10 +217,12 @@ export class WekanCreator {
checkActions(wekanActions) { checkActions(wekanActions) {
// XXX More check based on action type // XXX More check based on action type
check(wekanActions, [Match.ObjectIncluding({ check(wekanActions, [
actionType: String, Match.ObjectIncluding({
desc: String, actionType: String,
})]); desc: String,
}),
]);
} }
// You must call parseActions before calling this one. // You must call parseActions before calling this one.
@ -210,15 +233,17 @@ export class WekanCreator {
// very old boards won't have a creation activity so no creation date // very old boards won't have a creation activity so no creation date
createdAt: this._now(boardToImport.createdAt), createdAt: this._now(boardToImport.createdAt),
labels: [], labels: [],
members: [{ members: [
userId: Meteor.userId(), {
wekanId: Meteor.userId(), userId: Meteor.userId(),
isActive: true, wekanId: Meteor.userId(),
isAdmin: true, isActive: true,
isNoComments: false, isAdmin: true,
isCommentOnly: false, isNoComments: false,
swimlaneId: false, isCommentOnly: false,
}], swimlaneId: false,
},
],
// Standalone Export has modifiedAt missing, adding modifiedAt to fix it // Standalone Export has modifiedAt missing, adding modifiedAt to fix it
modifiedAt: this._now(boardToImport.modifiedAt), modifiedAt: this._now(boardToImport.modifiedAt),
permission: boardToImport.permission, permission: boardToImport.permission,
@ -228,16 +253,20 @@ export class WekanCreator {
}; };
// now add other members // now add other members
if (boardToImport.members) { if (boardToImport.members) {
boardToImport.members.forEach((wekanMember) => { boardToImport.members.forEach(wekanMember => {
// do we already have it in our list? // do we already have it in our list?
if (!boardToCreate.members.some((member) => member.wekanId === wekanMember.wekanId)) if (
!boardToCreate.members.some(
member => member.wekanId === wekanMember.wekanId,
)
)
boardToCreate.members.push({ boardToCreate.members.push({
...wekanMember, ...wekanMember,
userId: wekanMember.wekanId, userId: wekanMember.wekanId,
}); });
}); });
} }
boardToImport.labels.forEach((label) => { boardToImport.labels.forEach(label => {
const labelToCreate = { const labelToCreate = {
_id: Random.id(6), _id: Random.id(6),
color: label.color, color: label.color,
@ -279,7 +308,7 @@ export class WekanCreator {
*/ */
createCards(wekanCards, boardId) { createCards(wekanCards, boardId) {
const result = []; const result = [];
wekanCards.forEach((card) => { wekanCards.forEach(card => {
const cardToCreate = { const cardToCreate = {
archived: card.archived, archived: card.archived,
boardId, boardId,
@ -300,7 +329,7 @@ export class WekanCreator {
}; };
// add labels // add labels
if (card.labelIds) { if (card.labelIds) {
cardToCreate.labelIds = card.labelIds.map((wekanId) => { cardToCreate.labelIds = card.labelIds.map(wekanId => {
return this.labels[wekanId]; return this.labels[wekanId];
}); });
} }
@ -308,12 +337,12 @@ export class WekanCreator {
if (card.members) { if (card.members) {
const wekanMembers = []; const wekanMembers = [];
// we can't just map, as some members may not have been mapped // we can't just map, as some members may not have been mapped
card.members.forEach((sourceMemberId) => { card.members.forEach(sourceMemberId => {
if (this.members[sourceMemberId]) { if (this.members[sourceMemberId]) {
const wekanId = this.members[sourceMemberId]; const wekanId = this.members[sourceMemberId];
// we may map multiple Wekan members to the same wekan user // we may map multiple Wekan members to the same wekan user
// in which case we risk adding the same user multiple times // in which case we risk adding the same user multiple times
if (!wekanMembers.find((wId) => wId === wekanId)) { if (!wekanMembers.find(wId => wId === wekanId)) {
wekanMembers.push(wekanId); wekanMembers.push(wekanId);
} }
} }
@ -349,7 +378,7 @@ export class WekanCreator {
// add comments // add comments
const comments = this.comments[card._id]; const comments = this.comments[card._id];
if (comments) { if (comments) {
comments.forEach((comment) => { comments.forEach(comment => {
const commentToCreate = { const commentToCreate = {
boardId, boardId,
cardId, cardId,
@ -377,7 +406,7 @@ export class WekanCreator {
const attachments = this.attachments[card._id]; const attachments = this.attachments[card._id];
const wekanCoverId = card.coverId; const wekanCoverId = card.coverId;
if (attachments) { if (attachments) {
attachments.forEach((att) => { attachments.forEach(att => {
const file = new FS.File(); const file = new FS.File();
// Simulating file.attachData on the client generates multiple errors // Simulating file.attachData on the client generates multiple errors
// - HEAD returns null, which causes exception down the line // - HEAD returns null, which causes exception down the line
@ -394,7 +423,7 @@ export class WekanCreator {
// attachments' related activities automatically // attachments' related activities automatically
file.source = 'import'; file.source = 'import';
if (error) { if (error) {
throw (error); throw error;
} else { } else {
const wekanAtt = Attachments.insert(file, () => { const wekanAtt = Attachments.insert(file, () => {
// we do nothing // we do nothing
@ -411,33 +440,37 @@ export class WekanCreator {
} }
}); });
} else if (att.file) { } else if (att.file) {
file.attachData(new Buffer(att.file, 'base64'), { file.attachData(
type: att.type, new Buffer(att.file, 'base64'),
}, (error) => { {
file.name(att.name); type: att.type,
file.boardId = boardId; },
file.cardId = cardId; error => {
file.userId = self._user(att.userId); file.name(att.name);
// The field source will only be used to prevent adding file.boardId = boardId;
// attachments' related activities automatically file.cardId = cardId;
file.source = 'import'; file.userId = self._user(att.userId);
if (error) { // The field source will only be used to prevent adding
throw (error); // attachments' related activities automatically
} else { file.source = 'import';
const wekanAtt = Attachments.insert(file, () => { if (error) {
// we do nothing throw error;
}); } else {
this.attachmentIds[att._id] = wekanAtt._id; const wekanAtt = Attachments.insert(file, () => {
// // we do nothing
if (wekanCoverId === att._id) {
Cards.direct.update(cardId, {
$set: {
coverId: wekanAtt._id,
},
}); });
this.attachmentIds[att._id] = wekanAtt._id;
//
if (wekanCoverId === att._id) {
Cards.direct.update(cardId, {
$set: {
coverId: wekanAtt._id,
},
});
}
} }
} },
}); );
} }
} }
// todo XXX set cover - if need be // todo XXX set cover - if need be
@ -450,7 +483,7 @@ export class WekanCreator {
// Create labels if they do not exist and load this.labels. // Create labels if they do not exist and load this.labels.
createLabels(wekanLabels, board) { createLabels(wekanLabels, board) {
wekanLabels.forEach((label) => { wekanLabels.forEach(label => {
const color = label.color; const color = label.color;
const name = label.name; const name = label.name;
const existingLabel = board.getLabel(name, color); const existingLabel = board.getLabel(name, color);
@ -479,7 +512,7 @@ export class WekanCreator {
const listId = Lists.direct.insert(listToCreate); const listId = Lists.direct.insert(listToCreate);
Lists.direct.update(listId, { Lists.direct.update(listId, {
$set: { $set: {
'updatedAt': this._now(), updatedAt: this._now(),
}, },
}); });
this.lists[list._id] = listId; this.lists[list._id] = listId;
@ -520,7 +553,7 @@ export class WekanCreator {
const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate); const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
Swimlanes.direct.update(swimlaneId, { Swimlanes.direct.update(swimlaneId, {
$set: { $set: {
'updatedAt': this._now(), updatedAt: this._now(),
}, },
}); });
this.swimlanes[swimlane._id] = swimlaneId; this.swimlanes[swimlane._id] = swimlaneId;
@ -545,7 +578,7 @@ export class WekanCreator {
} }
createTriggers(wekanTriggers, boardId) { createTriggers(wekanTriggers, boardId) {
wekanTriggers.forEach((trigger) => { wekanTriggers.forEach(trigger => {
if (trigger.hasOwnProperty('labelId')) { if (trigger.hasOwnProperty('labelId')) {
trigger.labelId = this.labels[trigger.labelId]; trigger.labelId = this.labels[trigger.labelId];
} }
@ -560,7 +593,7 @@ export class WekanCreator {
} }
createActions(wekanActions, boardId) { createActions(wekanActions, boardId) {
wekanActions.forEach((action) => { wekanActions.forEach(action => {
if (action.hasOwnProperty('labelId')) { if (action.hasOwnProperty('labelId')) {
action.labelId = this.labels[action.labelId]; action.labelId = this.labels[action.labelId];
} }
@ -575,7 +608,7 @@ export class WekanCreator {
} }
createRules(wekanRules, boardId) { createRules(wekanRules, boardId) {
wekanRules.forEach((rule) => { wekanRules.forEach(rule => {
// Create the rule // Create the rule
rule.boardId = boardId; rule.boardId = boardId;
rule.triggerId = this.triggers[rule.triggerId]; rule.triggerId = this.triggers[rule.triggerId];
@ -595,189 +628,178 @@ export class WekanCreator {
sort: checklistitem.sort ? checklistitem.sort : checklistitemIndex, sort: checklistitem.sort ? checklistitem.sort : checklistitemIndex,
isFinished: checklistitem.isFinished, isFinished: checklistitem.isFinished,
}; };
const checklistItemId = ChecklistItems.direct.insert(checklistItemTocreate); const checklistItemId = ChecklistItems.direct.insert(
checklistItemTocreate,
);
this.checklistItems[checklistitem._id] = checklistItemId; this.checklistItems[checklistitem._id] = checklistItemId;
}); });
} }
parseActivities(wekanBoard) { parseActivities(wekanBoard) {
wekanBoard.activities.forEach((activity) => { wekanBoard.activities.forEach(activity => {
switch (activity.activityType) { switch (activity.activityType) {
case 'addAttachment': case 'addAttachment': {
{ // We have to be cautious, because the attachment could have been removed later.
// We have to be cautious, because the attachment could have been removed later. // In that case Wekan still reports its addition, but removes its 'url' field.
// In that case Wekan still reports its addition, but removes its 'url' field. // So we test for that
// So we test for that const wekanAttachment = wekanBoard.attachments.filter(attachment => {
const wekanAttachment = wekanBoard.attachments.filter((attachment) => { return attachment._id === activity.attachmentId;
return attachment._id === activity.attachmentId; })[0];
})[0];
if (typeof wekanAttachment !== 'undefined' && wekanAttachment) { if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
if (wekanAttachment.url || wekanAttachment.file) { if (wekanAttachment.url || wekanAttachment.file) {
// we cannot actually create the Wekan attachment, because we don't yet // we cannot actually create the Wekan attachment, because we don't yet
// have the cards to attach it to, so we store it in the instance variable. // have the cards to attach it to, so we store it in the instance variable.
const wekanCardId = activity.cardId; const wekanCardId = activity.cardId;
if (!this.attachments[wekanCardId]) { if (!this.attachments[wekanCardId]) {
this.attachments[wekanCardId] = []; this.attachments[wekanCardId] = [];
}
this.attachments[wekanCardId].push(wekanAttachment);
} }
this.attachments[wekanCardId].push(wekanAttachment);
} }
break;
} }
break; case 'addComment': {
} const wekanComment = wekanBoard.comments.filter(comment => {
case 'addComment': return comment._id === activity.commentId;
{ })[0];
const wekanComment = wekanBoard.comments.filter((comment) => { const id = activity.cardId;
return comment._id === activity.commentId; if (!this.comments[id]) {
})[0]; this.comments[id] = [];
const id = activity.cardId; }
if (!this.comments[id]) { this.comments[id].push(wekanComment);
this.comments[id] = []; break;
}
case 'createBoard': {
this.createdAt.board = activity.createdAt;
break;
}
case 'createCard': {
const cardId = activity.cardId;
this.createdAt.cards[cardId] = activity.createdAt;
this.createdBy.cards[cardId] = activity.userId;
break;
}
case 'createList': {
const listId = activity.listId;
this.createdAt.lists[listId] = activity.createdAt;
break;
}
case 'createSwimlane': {
const swimlaneId = activity.swimlaneId;
this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
break;
} }
this.comments[id].push(wekanComment);
break;
}
case 'createBoard':
{
this.createdAt.board = activity.createdAt;
break;
}
case 'createCard':
{
const cardId = activity.cardId;
this.createdAt.cards[cardId] = activity.createdAt;
this.createdBy.cards[cardId] = activity.userId;
break;
}
case 'createList':
{
const listId = activity.listId;
this.createdAt.lists[listId] = activity.createdAt;
break;
}
case 'createSwimlane':
{
const swimlaneId = activity.swimlaneId;
this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
break;
}
} }
}); });
} }
importActivities(activities, boardId) { importActivities(activities, boardId) {
activities.forEach((activity) => { activities.forEach(activity => {
switch (activity.activityType) { switch (activity.activityType) {
// Board related activities // Board related activities
// TODO: addBoardMember, removeBoardMember // TODO: addBoardMember, removeBoardMember
case 'createBoard': case 'createBoard': {
{ Activities.direct.insert({
Activities.direct.insert({ userId: this._user(activity.userId),
userId: this._user(activity.userId), type: 'board',
type: 'board', activityTypeId: boardId,
activityTypeId: boardId, activityType: activity.activityType,
activityType: activity.activityType, boardId,
boardId, createdAt: this._now(activity.createdAt),
createdAt: this._now(activity.createdAt), });
}); break;
break; }
} // List related activities
// List related activities // TODO: removeList, archivedList
// TODO: removeList, archivedList case 'createList': {
case 'createList': Activities.direct.insert({
{ userId: this._user(activity.userId),
Activities.direct.insert({ type: 'list',
userId: this._user(activity.userId), activityType: activity.activityType,
type: 'list', listId: this.lists[activity.listId],
activityType: activity.activityType, boardId,
listId: this.lists[activity.listId], createdAt: this._now(activity.createdAt),
boardId, });
createdAt: this._now(activity.createdAt), break;
}); }
break; // Card related activities
} // TODO: archivedCard, restoredCard, joinMember, unjoinMember
// Card related activities case 'createCard': {
// TODO: archivedCard, restoredCard, joinMember, unjoinMember Activities.direct.insert({
case 'createCard': userId: this._user(activity.userId),
{ activityType: activity.activityType,
Activities.direct.insert({ listId: this.lists[activity.listId],
userId: this._user(activity.userId), cardId: this.cards[activity.cardId],
activityType: activity.activityType, boardId,
listId: this.lists[activity.listId], createdAt: this._now(activity.createdAt),
cardId: this.cards[activity.cardId], });
boardId, break;
createdAt: this._now(activity.createdAt), }
}); case 'moveCard': {
break; Activities.direct.insert({
} userId: this._user(activity.userId),
case 'moveCard': oldListId: this.lists[activity.oldListId],
{ activityType: activity.activityType,
Activities.direct.insert({ listId: this.lists[activity.listId],
userId: this._user(activity.userId), cardId: this.cards[activity.cardId],
oldListId: this.lists[activity.oldListId], boardId,
activityType: activity.activityType, createdAt: this._now(activity.createdAt),
listId: this.lists[activity.listId], });
cardId: this.cards[activity.cardId], break;
boardId, }
createdAt: this._now(activity.createdAt), // Comment related activities
}); case 'addComment': {
break; Activities.direct.insert({
} userId: this._user(activity.userId),
// Comment related activities activityType: activity.activityType,
case 'addComment': cardId: this.cards[activity.cardId],
{ commentId: this.commentIds[activity.commentId],
Activities.direct.insert({ boardId,
userId: this._user(activity.userId), createdAt: this._now(activity.createdAt),
activityType: activity.activityType, });
cardId: this.cards[activity.cardId], break;
commentId: this.commentIds[activity.commentId], }
boardId, // Attachment related activities
createdAt: this._now(activity.createdAt), case 'addAttachment': {
}); Activities.direct.insert({
break; userId: this._user(activity.userId),
} type: 'card',
// Attachment related activities activityType: activity.activityType,
case 'addAttachment': attachmentId: this.attachmentIds[activity.attachmentId],
{ cardId: this.cards[activity.cardId],
Activities.direct.insert({ boardId,
userId: this._user(activity.userId), createdAt: this._now(activity.createdAt),
type: 'card', });
activityType: activity.activityType, break;
attachmentId: this.attachmentIds[activity.attachmentId], }
cardId: this.cards[activity.cardId], // Checklist related activities
boardId, case 'addChecklist': {
createdAt: this._now(activity.createdAt), Activities.direct.insert({
}); userId: this._user(activity.userId),
break; activityType: activity.activityType,
} cardId: this.cards[activity.cardId],
// Checklist related activities checklistId: this.checklists[activity.checklistId],
case 'addChecklist': boardId,
{ createdAt: this._now(activity.createdAt),
Activities.direct.insert({ });
userId: this._user(activity.userId), break;
activityType: activity.activityType, }
cardId: this.cards[activity.cardId], case 'addChecklistItem': {
checklistId: this.checklists[activity.checklistId], Activities.direct.insert({
boardId, userId: this._user(activity.userId),
createdAt: this._now(activity.createdAt), activityType: activity.activityType,
}); cardId: this.cards[activity.cardId],
break; checklistId: this.checklists[activity.checklistId],
} checklistItemId: activity.checklistItemId.replace(
case 'addChecklistItem': activity.checklistId,
{ this.checklists[activity.checklistId],
Activities.direct.insert({ ),
userId: this._user(activity.userId), boardId,
activityType: activity.activityType, createdAt: this._now(activity.createdAt),
cardId: this.cards[activity.cardId], });
checklistId: this.checklists[activity.checklistId], break;
checklistItemId: activity.checklistItemId.replace( }
activity.checklistId,
this.checklists[activity.checklistId]),
boardId,
createdAt: this._now(activity.createdAt),
});
break;
}
} }
}); });
} }
@ -788,7 +810,6 @@ export class WekanCreator {
// check(data, { // check(data, {
// membersMapping: Match.Optional(Object), // membersMapping: Match.Optional(Object),
// }); // });
// this.checkActivities(board.activities); // this.checkActivities(board.activities);
// this.checkBoard(board); // this.checkBoard(board);
// this.checkLabels(board.labels); // this.checkLabels(board.labels);
@ -807,7 +828,9 @@ export class WekanCreator {
create(board, currentBoardId) { create(board, currentBoardId) {
// TODO : Make isSandstorm variable global // TODO : Make isSandstorm variable global
const isSandstorm = Meteor.settings && Meteor.settings.public && const isSandstorm =
Meteor.settings &&
Meteor.settings.public &&
Meteor.settings.public.sandstorm; Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) { if (isSandstorm && currentBoardId) {
const currentBoard = Boards.findOne(currentBoardId); const currentBoard = Boards.findOne(currentBoardId);

View file

@ -5,10 +5,10 @@ export function getMembersToMap(data) {
const membersToMap = data.members; const membersToMap = data.members;
const users = data.users; const users = data.users;
// auto-map based on username // auto-map based on username
membersToMap.forEach((importedMember) => { membersToMap.forEach(importedMember => {
importedMember.id = importedMember.userId; importedMember.id = importedMember.userId;
delete importedMember.userId; delete importedMember.userId;
const user = users.filter((user) => { const user = users.filter(user => {
return user._id === importedMember.id; return user._id === importedMember.id;
})[0]; })[0];
if (user.profile && user.profile.fullname) { if (user.profile && user.profile.fullname) {

View file

@ -8,7 +8,7 @@
"lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .", "lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
"lint:staged": "lint-staged", "lint:staged": "lint-staged",
"prettify": "prettier --write '**/*.js' '**/*.jsx'", "prettify": "prettier --write '**/*.js' '**/*.jsx'",
"test": "npm run --silent lint" "test": "npm run lint"
}, },
"lint-staged": { "lint-staged": {
"*.js": [ "*.js": [

View file

@ -1,7 +1,7 @@
// Sandstorm context is detected using the METEOR_SETTINGS environment variable // Sandstorm context is detected using the METEOR_SETTINGS environment variable
// in the package definition. // in the package definition.
const isSandstorm = Meteor.settings && Meteor.settings.public && const isSandstorm =
Meteor.settings.public.sandstorm; Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm;
// In sandstorm we only have one board per sandstorm instance. Since we want to // In sandstorm we only have one board per sandstorm instance. Since we want to
// keep most of our code unchanged, we simply hard-code a board `_id` and // keep most of our code unchanged, we simply hard-code a board `_id` and
@ -28,15 +28,17 @@ if (isSandstorm && Meteor.isServer) {
const Package = Capnp.importSystem('sandstorm/package.capnp'); const Package = Capnp.importSystem('sandstorm/package.capnp');
const Powerbox = Capnp.importSystem('sandstorm/powerbox.capnp'); const Powerbox = Capnp.importSystem('sandstorm/powerbox.capnp');
const Identity = Capnp.importSystem('sandstorm/identity.capnp'); const Identity = Capnp.importSystem('sandstorm/identity.capnp');
const SandstormHttpBridge = const SandstormHttpBridge = Capnp.importSystem(
Capnp.importSystem('sandstorm/sandstorm-http-bridge.capnp').SandstormHttpBridge; 'sandstorm/sandstorm-http-bridge.capnp',
).SandstormHttpBridge;
let httpBridge = null; let httpBridge = null;
let capnpConnection = null; let capnpConnection = null;
const bridgeConfig = Capnp.parse( const bridgeConfig = Capnp.parse(
Package.BridgeConfig, Package.BridgeConfig,
fs.readFileSync('/sandstorm-http-bridge-config')); fs.readFileSync('/sandstorm-http-bridge-config'),
);
function getHttpBridge() { function getHttpBridge() {
if (!httpBridge) { if (!httpBridge) {
@ -54,9 +56,13 @@ if (isSandstorm && Meteor.isServer) {
const parsedDescriptor = Capnp.parse( const parsedDescriptor = Capnp.parse(
Powerbox.PowerboxDescriptor, Powerbox.PowerboxDescriptor,
new Buffer(descriptor, 'base64'), new Buffer(descriptor, 'base64'),
{ packed: true }); { packed: true },
);
const tag = Capnp.parse(Identity.Identity.PowerboxTag, parsedDescriptor.tags[0].value); const tag = Capnp.parse(
Identity.Identity.PowerboxTag,
parsedDescriptor.tags[0].value,
);
const permissions = []; const permissions = [];
if (tag.permissions[1]) { if (tag.permissions[1]) {
permissions.push('configure'); permissions.push('configure');
@ -71,35 +77,43 @@ if (isSandstorm && Meteor.isServer) {
const session = httpBridge.getSessionContext(sessionId).context; const session = httpBridge.getSessionContext(sessionId).context;
const api = httpBridge.getSandstormApi(sessionId).api; const api = httpBridge.getSandstormApi(sessionId).api;
Meteor.wrapAsync((done) => { Meteor.wrapAsync(done => {
session.claimRequest(token).then((response) => { session
const identity = response.cap.castAs(Identity.Identity); .claimRequest(token)
const promises = [api.getIdentityId(identity), identity.getProfile(), .then(response => {
httpBridge.saveIdentity(identity)]; const identity = response.cap.castAs(Identity.Identity);
return Promise.all(promises).then((responses) => { const promises = [
const identityId = responses[0].id.toString('hex').slice(0, 32); api.getIdentityId(identity),
const profile = responses[1].profile; identity.getProfile(),
return profile.picture.getUrl().then((response) => { httpBridge.saveIdentity(identity),
const sandstormInfo = { ];
id: identityId, return Promise.all(promises).then(responses => {
name: profile.displayName.defaultText, const identityId = responses[0].id.toString('hex').slice(0, 32);
permissions, const profile = responses[1].profile;
picture: `${response.protocol}://${response.hostPath}`, return profile.picture.getUrl().then(response => {
preferredHandle: profile.preferredHandle, const sandstormInfo = {
pronouns: profile.pronouns, id: identityId,
}; name: profile.displayName.defaultText,
permissions,
picture: `${response.protocol}://${response.hostPath}`,
preferredHandle: profile.preferredHandle,
pronouns: profile.pronouns,
};
const login = Accounts.updateOrCreateUserFromExternalService( const login = Accounts.updateOrCreateUserFromExternalService(
'sandstorm', sandstormInfo, 'sandstorm',
{ profile: { name: sandstormInfo.name } }); sandstormInfo,
{ profile: { name: sandstormInfo.name } },
);
updateUserPermissions(login.userId, permissions); updateUserPermissions(login.userId, permissions);
done(); done();
});
}); });
})
.catch(e => {
done(e, null);
}); });
}).catch((e) => {
done(e, null);
});
})(); })();
}, },
}); });
@ -107,32 +121,39 @@ if (isSandstorm && Meteor.isServer) {
function reportActivity(sessionId, path, type, users, caption) { function reportActivity(sessionId, path, type, users, caption) {
const httpBridge = getHttpBridge(); const httpBridge = getHttpBridge();
const session = httpBridge.getSessionContext(sessionId).context; const session = httpBridge.getSessionContext(sessionId).context;
Meteor.wrapAsync((done) => { Meteor.wrapAsync(done => {
return Promise.all(users.map((user) => { return Promise.all(
return httpBridge.getSavedIdentity(user.id).then((response) => { users.map(user => {
// Call getProfile() to make sure that the identity successfully resolves. return httpBridge
// (In C++ we would instead call whenResolved() here.) .getSavedIdentity(user.id)
const identity = response.identity; .then(response => {
return identity.getProfile().then(() => { // Call getProfile() to make sure that the identity successfully resolves.
return { identity, // (In C++ we would instead call whenResolved() here.)
mentioned: !!user.mentioned, const identity = response.identity;
subscribed: !!user.subscribed, return identity.getProfile().then(() => {
}; return {
}); identity,
}).catch(() => { mentioned: !!user.mentioned,
// Ignore identities that fail to restore. Either they were added before we set subscribed: !!user.subscribed,
// `saveIdentityCaps` to true, or they have lost access to the board. };
}); });
})).then((maybeUsers) => { })
const users = maybeUsers.filter((u) => !!u); .catch(() => {
const event = { path, type, users }; // Ignore identities that fail to restore. Either they were added before we set
if (caption) { // `saveIdentityCaps` to true, or they have lost access to the board.
event.notification = { caption }; });
} }),
)
.then(maybeUsers => {
const users = maybeUsers.filter(u => !!u);
const event = { path, type, users };
if (caption) {
event.notification = { caption };
}
return session.activity(event); return session.activity(event);
}).then(() => done(), })
(e) => done(e)); .then(() => done(), e => done(e));
})(); })();
} }
@ -146,7 +167,9 @@ if (isSandstorm && Meteor.isServer) {
const eventTypes = bridgeConfig.viewInfo.eventTypes; const eventTypes = bridgeConfig.viewInfo.eventTypes;
const defIdx = eventTypes.findIndex((def) => def.name === doc.activityType ); const defIdx = eventTypes.findIndex(
def => def.name === doc.activityType,
);
if (defIdx >= 0) { if (defIdx >= 0) {
const users = {}; const users = {};
function ensureUserListed(userId) { function ensureUserListed(userId) {
@ -188,10 +211,14 @@ if (isSandstorm && Meteor.isServer) {
if (doc.activityType === 'addComment') { if (doc.activityType === 'addComment') {
const comment = CardComments.findOne(doc.commentId); const comment = CardComments.findOne(doc.commentId);
caption = { defaultText: comment.text }; caption = { defaultText: comment.text };
const activeMembers = const activeMembers = _.pluck(
_.pluck(Boards.findOne(sandstormBoard._id).activeMembers(), 'userId'); Boards.findOne(sandstormBoard._id).activeMembers(),
(comment.text.match(/\B@([\w.]*)/g) || []).forEach((username) => { 'userId',
const user = Meteor.users.findOne({ username: username.slice(1)}); );
(comment.text.match(/\B@([\w.]*)/g) || []).forEach(username => {
const user = Meteor.users.findOne({
username: username.slice(1),
});
if (user && activeMembers.indexOf(user._id) !== -1) { if (user && activeMembers.indexOf(user._id) !== -1) {
mentionedUser(user._id); mentionedUser(user._id);
} }
@ -209,18 +236,22 @@ if (isSandstorm && Meteor.isServer) {
const isAdmin = permissions.indexOf('configure') > -1; const isAdmin = permissions.indexOf('configure') > -1;
const isCommentOnly = false; const isCommentOnly = false;
const isNoComments = false; const isNoComments = false;
const permissionDoc = { userId, isActive, isAdmin, isNoComments, isCommentOnly }; const permissionDoc = {
userId,
isActive,
isAdmin,
isNoComments,
isCommentOnly,
};
const boardMembers = Boards.findOne(sandstormBoard._id).members; const boardMembers = Boards.findOne(sandstormBoard._id).members;
const memberIndex = _.pluck(boardMembers, 'userId').indexOf(userId); const memberIndex = _.pluck(boardMembers, 'userId').indexOf(userId);
let modifier; let modifier;
if (memberIndex > -1) if (memberIndex > -1)
modifier = { $set: { [`members.${memberIndex}`]: permissionDoc }}; modifier = { $set: { [`members.${memberIndex}`]: permissionDoc } };
else if (!isActive) else if (!isActive) modifier = {};
modifier = {}; else modifier = { $push: { members: permissionDoc } };
else
modifier = { $push: { members: permissionDoc }};
Boards.update(sandstormBoard._id, modifier); Boards.update(sandstormBoard._id, modifier);
} }
@ -259,7 +290,7 @@ if (isSandstorm && Meteor.isServer) {
}); });
Activities.update( Activities.update(
{ activityTypeId: sandstormBoard._id }, { activityTypeId: sandstormBoard._id },
{ $set: { userId: doc._id }} { $set: { userId: doc._id } },
); );
} }
@ -274,10 +305,12 @@ if (isSandstorm && Meteor.isServer) {
const username = doc.services.sandstorm.preferredHandle; const username = doc.services.sandstorm.preferredHandle;
let appendNumber = 0; let appendNumber = 0;
while (Users.findOne({ while (
_id: { $ne: doc._id }, Users.findOne({
username: generateUniqueUsername(username, appendNumber), _id: { $ne: doc._id },
})) { username: generateUniqueUsername(username, appendNumber),
})
) {
appendNumber += 1; appendNumber += 1;
} }
@ -321,40 +354,43 @@ if (isSandstorm && Meteor.isServer) {
// is now handled by Sandstorm. // is now handled by Sandstorm.
// See https://github.com/wekan/wekan/issues/346 // See https://github.com/wekan/wekan/issues/346
Migrations.add('enforce-public-visibility-for-sandstorm', () => { Migrations.add('enforce-public-visibility-for-sandstorm', () => {
Boards.update('sandstorm', { $set: { permission: 'public' }}); Boards.update('sandstorm', { $set: { permission: 'public' } });
}); });
// Monkey patch to work around the problem described in // Monkey patch to work around the problem described in
// https://github.com/sandstorm-io/meteor-accounts-sandstorm/pull/31 // https://github.com/sandstorm-io/meteor-accounts-sandstorm/pull/31
const _httpMethods = HTTP.methods; const _httpMethods = HTTP.methods;
HTTP.methods = (newMethods) => { HTTP.methods = newMethods => {
Object.keys(newMethods).forEach((key) => { Object.keys(newMethods).forEach(key => {
if (newMethods[key].auth) { if (newMethods[key].auth) {
newMethods[key].auth = function() { newMethods[key].auth = function() {
const sandstormID = this.req.headers['x-sandstorm-user-id']; const sandstormID = this.req.headers['x-sandstorm-user-id'];
const user = Meteor.users.findOne({'services.sandstorm.id': sandstormID}); const user = Meteor.users.findOne({
'services.sandstorm.id': sandstormID,
});
return user && user._id; return user && user._id;
}; };
} }
}); });
_httpMethods(newMethods); _httpMethods(newMethods);
}; };
} }
if (isSandstorm && Meteor.isClient) { if (isSandstorm && Meteor.isClient) {
let rpcCounter = 0; let rpcCounter = 0;
const rpcs = {}; const rpcs = {};
window.addEventListener('message', (event) => { window.addEventListener('message', event => {
if (event.source === window) { if (event.source === window) {
// Meteor likes to postmessage itself. // Meteor likes to postmessage itself.
return; return;
} }
if ((event.source !== window.parent) || if (
typeof event.data !== 'object' || event.source !== window.parent ||
typeof event.data.rpcId !== 'number') { typeof event.data !== 'object' ||
typeof event.data.rpcId !== 'number'
) {
throw new Error(`got unexpected postMessage: ${event}`); throw new Error(`got unexpected postMessage: ${event}`);
} }
@ -374,7 +410,7 @@ if (isSandstorm && Meteor.isClient) {
obj[name] = message; obj[name] = message;
window.parent.postMessage(obj, '*'); window.parent.postMessage(obj, '*');
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
rpcs[id] = (response) => { rpcs[id] = response => {
if (response.error) { if (response.error) {
reject(new Error(response.error)); reject(new Error(response.error));
} else { } else {
@ -401,16 +437,20 @@ if (isSandstorm && Meteor.isClient) {
function doRequest(serializedPowerboxDescriptor, onSuccess) { function doRequest(serializedPowerboxDescriptor, onSuccess) {
return sendRpc('powerboxRequest', { return sendRpc('powerboxRequest', {
query: [serializedPowerboxDescriptor], query: [serializedPowerboxDescriptor],
}).then((response) => { }).then(response => {
if (!response.canceled) { if (!response.canceled) {
onSuccess(response); onSuccess(response);
} }
}); });
} }
window.sandstormRequestIdentity = function () { window.sandstormRequestIdentity = function() {
doRequest(powerboxDescriptors.identity, (response) => { doRequest(powerboxDescriptors.identity, response => {
Meteor.call('sandstormClaimIdentityRequest', response.token, response.descriptor); Meteor.call(
'sandstormClaimIdentityRequest',
response.token,
response.descriptor,
);
}); });
}; };
@ -422,9 +462,11 @@ if (isSandstorm && Meteor.isClient) {
return window.parent.postMessage(msg, '*'); return window.parent.postMessage(msg, '*');
} }
FlowRouter.triggers.enter([({ path }) => { FlowRouter.triggers.enter([
updateSandstormMetaData({ setPath: path }); ({ path }) => {
}]); updateSandstormMetaData({ setPath: path });
},
]);
Tracker.autorun(() => { Tracker.autorun(() => {
updateSandstormMetaData({ setTitle: DocHead.getTitle() }); updateSandstormMetaData({ setTitle: DocHead.getTitle() });

View file

@ -2,15 +2,20 @@
// server // server
import { AccountsLockout } from 'meteor/lucasantoniassi:accounts-lockout'; import { AccountsLockout } from 'meteor/lucasantoniassi:accounts-lockout';
(new AccountsLockout({ new AccountsLockout({
knownUsers: { knownUsers: {
failuresBeforeLockout: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE || 3, failuresBeforeLockout:
process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE || 3,
lockoutPeriod: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD || 60, lockoutPeriod: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD || 60,
failureWindow: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW || 15, failureWindow:
process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW || 15,
}, },
unknownUsers: { unknownUsers: {
failuresBeforeLockout: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE || 3, failuresBeforeLockout:
lockoutPeriod: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD || 60, process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE || 3,
failureWindow: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW || 15, lockoutPeriod:
process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD || 60,
failureWindow:
process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW || 15,
}, },
})).startup(); }).startup();

View file

@ -1,21 +1,20 @@
import Fiber from 'fibers'; import Fiber from 'fibers';
Meteor.startup(() => { Meteor.startup(() => {
// Node Fibers 100% CPU usage issue // Node Fibers 100% CPU usage issue
// https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161 // https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
// https://github.com/meteor/meteor/issues/9796#issuecomment-381676326 // https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
// https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129 // https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
Fiber.poolSize = 1e9; Fiber.poolSize = 1e9;
Accounts.validateLoginAttempt(function (options) { Accounts.validateLoginAttempt(function(options) {
const user = options.user || {}; const user = options.user || {};
return !user.loginDisabled; return !user.loginDisabled;
}); });
Authentication = {}; Authentication = {};
Authentication.checkUserId = function (userId) { Authentication.checkUserId = function(userId) {
if (userId === undefined) { if (userId === undefined) {
const error = new Meteor.Error('Unauthorized', 'Unauthorized'); const error = new Meteor.Error('Unauthorized', 'Unauthorized');
error.statusCode = 401; error.statusCode = 401;
@ -28,13 +27,12 @@ Meteor.startup(() => {
error.statusCode = 403; error.statusCode = 403;
throw error; throw error;
} }
}; };
// This will only check if the user is logged in. // This will only check if the user is logged in.
// The authorization checks for the user will have to be done inside each API endpoint // The authorization checks for the user will have to be done inside each API endpoint
Authentication.checkLoggedIn = function(userId) { Authentication.checkLoggedIn = function(userId) {
if(userId === undefined) { if (userId === undefined) {
const error = new Meteor.Error('Unauthorized', 'Unauthorized'); const error = new Meteor.Error('Unauthorized', 'Unauthorized');
error.statusCode = 401; error.statusCode = 401;
throw error; throw error;
@ -44,7 +42,7 @@ Meteor.startup(() => {
// An admin should be authorized to access everything, so we use a separate check for admins // An admin should be authorized to access everything, so we use a separate check for admins
// This throws an error if otherReq is false and the user is not an admin // This throws an error if otherReq is false and the user is not an admin
Authentication.checkAdminOrCondition = function(userId, otherReq) { Authentication.checkAdminOrCondition = function(userId, otherReq) {
if(otherReq) return; if (otherReq) return;
const admin = Users.findOne({ _id: userId, isAdmin: true }); const admin = Users.findOne({ _id: userId, isAdmin: true });
if (admin === undefined) { if (admin === undefined) {
const error = new Meteor.Error('Forbidden', 'Forbidden'); const error = new Meteor.Error('Forbidden', 'Forbidden');
@ -58,14 +56,16 @@ Meteor.startup(() => {
Authentication.checkLoggedIn(userId); Authentication.checkLoggedIn(userId);
const board = Boards.findOne({ _id: boardId }); const board = Boards.findOne({ _id: boardId });
const normalAccess = board.permission === 'public' || board.members.some((e) => e.userId === userId); const normalAccess =
board.permission === 'public' ||
board.members.some(e => e.userId === userId);
Authentication.checkAdminOrCondition(userId, normalAccess); Authentication.checkAdminOrCondition(userId, normalAccess);
}; };
if (Meteor.isServer) { if (Meteor.isServer) {
if(process.env.OAUTH2_CLIENT_ID !== '') { if (process.env.OAUTH2_CLIENT_ID !== '') {
ServiceConfiguration.configurations.upsert(
ServiceConfiguration.configurations.upsert( // eslint-disable-line no-undef // eslint-disable-line no-undef
{ service: 'oidc' }, { service: 'oidc' },
{ {
$set: { $set: {
@ -76,15 +76,14 @@ Meteor.startup(() => {
authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT, authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT,
userinfoEndpoint: process.env.OAUTH2_USERINFO_ENDPOINT, userinfoEndpoint: process.env.OAUTH2_USERINFO_ENDPOINT,
tokenEndpoint: process.env.OAUTH2_TOKEN_ENDPOINT, tokenEndpoint: process.env.OAUTH2_TOKEN_ENDPOINT,
idTokenWhitelistFields: process.env.OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [], idTokenWhitelistFields:
process.env.OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
requestPermissions: process.env.OAUTH2_REQUEST_PERMISSIONS, requestPermissions: process.env.OAUTH2_REQUEST_PERMISSIONS,
}, },
// OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [], // OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
// OAUTH2_REQUEST_PERMISSIONS || 'openid profile email', // OAUTH2_REQUEST_PERMISSIONS || 'openid profile email',
} },
); );
} }
} }
}); });

View file

@ -1,23 +1,27 @@
Meteor.startup(() => { Meteor.startup(() => {
if (process.env.CORS) {
if ( process.env.CORS ) {
// Listen to incoming HTTP requests, can only be used on the server // Listen to incoming HTTP requests, can only be used on the server
WebApp.rawConnectHandlers.use(function(req, res, next) { WebApp.rawConnectHandlers.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', process.env.CORS); res.setHeader('Access-Control-Allow-Origin', process.env.CORS);
return next(); return next();
}); });
} }
if ( process.env.CORS_ALLOW_HEADERS ) { if (process.env.CORS_ALLOW_HEADERS) {
WebApp.rawConnectHandlers.use(function(req, res, next) { WebApp.rawConnectHandlers.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Headers', process.env.CORS_ALLOW_HEADERS); res.setHeader(
'Access-Control-Allow-Headers',
process.env.CORS_ALLOW_HEADERS,
);
return next(); return next();
}); });
} }
if ( process.env.CORS_EXPOSE_HEADERS ) { if (process.env.CORS_EXPOSE_HEADERS) {
WebApp.rawConnectHandlers.use(function(req, res, next) { WebApp.rawConnectHandlers.use(function(req, res, next) {
res.setHeader('Access-Control-Expose-Headers', process.env.CORS_EXPOSE_HEADERS); res.setHeader(
'Access-Control-Expose-Headers',
process.env.CORS_EXPOSE_HEADERS,
);
return next(); return next();
}); });
} }
}); });

View file

@ -1,10 +1,10 @@
Meteor.startup(() => { Meteor.startup(() => {
if (process.env.HEADER_LOGIN_ID) {
if ( process.env.HEADER_LOGIN_ID ) {
Meteor.settings.public.headerLoginId = process.env.HEADER_LOGIN_ID; Meteor.settings.public.headerLoginId = process.env.HEADER_LOGIN_ID;
Meteor.settings.public.headerLoginEmail = process.env.HEADER_LOGIN_EMAIL; Meteor.settings.public.headerLoginEmail = process.env.HEADER_LOGIN_EMAIL;
Meteor.settings.public.headerLoginFirstname = process.env.HEADER_LOGIN_FIRSTNAME; Meteor.settings.public.headerLoginFirstname =
Meteor.settings.public.headerLoginLastname = process.env.HEADER_LOGIN_LASTNAME; process.env.HEADER_LOGIN_FIRSTNAME;
Meteor.settings.public.headerLoginLastname =
process.env.HEADER_LOGIN_LASTNAME;
} }
}); });

Some files were not shown because too many files have changed in this diff Show more