mirror of
https://github.com/wekan/wekan.git
synced 2025-04-22 04:57:07 -04:00
Merge branch 'justinr1234-linting' into meteor-1.8
This commit is contained in:
commit
40b782788a
117 changed files with 6224 additions and 5243 deletions
|
@ -12,11 +12,8 @@
|
|||
"meteor": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true
|
||||
}
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"strict": 0,
|
||||
|
@ -26,7 +23,7 @@
|
|||
"consistent-return": 2,
|
||||
"dot-notation": 2,
|
||||
"eqeqeq": 2,
|
||||
"indent": [2, 2],
|
||||
"indent": 0,
|
||||
"no-cond-assign": 2,
|
||||
"no-constant-condition": 2,
|
||||
"no-eval": 2,
|
||||
|
@ -50,7 +47,6 @@
|
|||
"quotes": [2, "single"],
|
||||
"semi-spacing": 2,
|
||||
"space-unary-ops": 2,
|
||||
"arrow-parens": 2,
|
||||
"arrow-spacing": 2,
|
||||
"no-class-assign": 2,
|
||||
"no-dupe-class-members": 2,
|
||||
|
@ -69,7 +65,8 @@
|
|||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
]
|
||||
],
|
||||
"meteor/no-session": 0
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
|
|
|
@ -13,7 +13,7 @@ BlazeComponent.extendComponent({
|
|||
let thisId, searchId;
|
||||
if (mode === 'linkedcard' || mode === 'linkedboard') {
|
||||
thisId = Session.get('currentCard');
|
||||
searchId = Cards.findOne({_id: thisId}).linkedId;
|
||||
searchId = Cards.findOne({ _id: thisId }).linkedId;
|
||||
mode = mode.replace('linked', '');
|
||||
} else {
|
||||
thisId = Session.get(`current${capitalizedMode}`);
|
||||
|
@ -22,8 +22,7 @@ BlazeComponent.extendComponent({
|
|||
const limit = this.page.get() * activitiesPerPage;
|
||||
const user = Meteor.user();
|
||||
const hideSystem = user ? user.hasHiddenSystemMessages() : false;
|
||||
if (searchId === null)
|
||||
return;
|
||||
if (searchId === null) return;
|
||||
|
||||
this.subscribe('activities', mode, searchId, limit, hideSystem, () => {
|
||||
this.loadNextPageLocked = false;
|
||||
|
@ -50,9 +49,9 @@ BlazeComponent.extendComponent({
|
|||
}
|
||||
},
|
||||
|
||||
checkItem(){
|
||||
checkItem() {
|
||||
const checkItemId = this.currentData().checklistItemId;
|
||||
const checkItem = ChecklistItems.findOne({_id:checkItemId});
|
||||
const checkItem = ChecklistItems.findOne({ _id: checkItemId });
|
||||
return checkItem.title;
|
||||
},
|
||||
|
||||
|
@ -66,42 +65,58 @@ BlazeComponent.extendComponent({
|
|||
|
||||
cardLink() {
|
||||
const card = this.currentData().card();
|
||||
return card && Blaze.toHTML(HTML.A({
|
||||
href: card.absoluteUrl(),
|
||||
'class': 'action-card',
|
||||
}, card.title));
|
||||
return (
|
||||
card &&
|
||||
Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: card.absoluteUrl(),
|
||||
class: 'action-card',
|
||||
},
|
||||
card.title,
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
lastLabel(){
|
||||
lastLabel() {
|
||||
const lastLabelId = this.currentData().labelId;
|
||||
if (!lastLabelId)
|
||||
return null;
|
||||
const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(lastLabelId);
|
||||
if(lastLabel.name === undefined || lastLabel.name === ''){
|
||||
if (!lastLabelId) return null;
|
||||
const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(
|
||||
lastLabelId,
|
||||
);
|
||||
if (lastLabel.name === undefined || lastLabel.name === '') {
|
||||
return lastLabel.color;
|
||||
}else{
|
||||
} else {
|
||||
return lastLabel.name;
|
||||
}
|
||||
},
|
||||
|
||||
lastCustomField(){
|
||||
const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
|
||||
if (!lastCustomField)
|
||||
return null;
|
||||
lastCustomField() {
|
||||
const lastCustomField = CustomFields.findOne(
|
||||
this.currentData().customFieldId,
|
||||
);
|
||||
if (!lastCustomField) return null;
|
||||
return lastCustomField.name;
|
||||
},
|
||||
|
||||
lastCustomFieldValue(){
|
||||
const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
|
||||
if (!lastCustomField)
|
||||
return null;
|
||||
lastCustomFieldValue() {
|
||||
const lastCustomField = CustomFields.findOne(
|
||||
this.currentData().customFieldId,
|
||||
);
|
||||
if (!lastCustomField) return null;
|
||||
const value = this.currentData().value;
|
||||
if (lastCustomField.settings.dropdownItems && lastCustomField.settings.dropdownItems.length > 0) {
|
||||
const dropDownValue = _.find(lastCustomField.settings.dropdownItems, (item) => {
|
||||
return item._id === value;
|
||||
});
|
||||
if (dropDownValue)
|
||||
return dropDownValue.name;
|
||||
if (
|
||||
lastCustomField.settings.dropdownItems &&
|
||||
lastCustomField.settings.dropdownItems.length > 0
|
||||
) {
|
||||
const dropDownValue = _.find(
|
||||
lastCustomField.settings.dropdownItems,
|
||||
item => {
|
||||
return item._id === value;
|
||||
},
|
||||
);
|
||||
if (dropDownValue) return dropDownValue.name;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
@ -112,11 +127,16 @@ BlazeComponent.extendComponent({
|
|||
|
||||
sourceLink() {
|
||||
const source = this.currentData().source;
|
||||
if(source) {
|
||||
if(source.url) {
|
||||
return Blaze.toHTML(HTML.A({
|
||||
href: source.url,
|
||||
}, source.system));
|
||||
if (source) {
|
||||
if (source.url) {
|
||||
return Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: source.url,
|
||||
},
|
||||
source.system,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return source.system;
|
||||
}
|
||||
|
@ -133,38 +153,50 @@ BlazeComponent.extendComponent({
|
|||
attachmentLink() {
|
||||
const attachment = this.currentData().attachment();
|
||||
// trying to display url before file is stored generates js errors
|
||||
return attachment && attachment.url({ download: true }) && Blaze.toHTML(HTML.A({
|
||||
href: attachment.url({ download: true }),
|
||||
target: '_blank',
|
||||
}, attachment.name()));
|
||||
return (
|
||||
attachment &&
|
||||
attachment.url({ download: true }) &&
|
||||
Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: attachment.url({ download: true }),
|
||||
target: '_blank',
|
||||
},
|
||||
attachment.name(),
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
customField() {
|
||||
const customField = this.currentData().customField();
|
||||
if (!customField)
|
||||
return null;
|
||||
if (!customField) return null;
|
||||
return customField.name;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
// XXX We should use Popup.afterConfirmation here
|
||||
'click .js-delete-comment'() {
|
||||
const commentId = this.currentData().commentId;
|
||||
CardComments.remove(commentId);
|
||||
return [
|
||||
{
|
||||
// XXX We should use Popup.afterConfirmation here
|
||||
'click .js-delete-comment'() {
|
||||
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');
|
||||
|
|
|
@ -14,39 +14,41 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-new-comment:not(.focus)'() {
|
||||
commentFormIsOpen.set(true);
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
||||
|
@ -69,7 +71,8 @@ Tracker.autorun(() => {
|
|||
});
|
||||
});
|
||||
|
||||
EscapeActions.register('inlinedForm',
|
||||
EscapeActions.register(
|
||||
'inlinedForm',
|
||||
() => {
|
||||
const draftKey = {
|
||||
fieldName: 'cardComment',
|
||||
|
@ -84,7 +87,10 @@ EscapeActions.register('inlinedForm',
|
|||
}
|
||||
resetCommentInput(commentInput);
|
||||
},
|
||||
() => { return commentFormIsOpen.get(); }, {
|
||||
() => {
|
||||
return commentFormIsOpen.get();
|
||||
},
|
||||
{
|
||||
noClickEscapeOn: '.js-new-comment',
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -4,36 +4,45 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
archivedBoards() {
|
||||
return Boards.find({ archived: true }, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return Boards.find(
|
||||
{ archived: true },
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-restore-board'() {
|
||||
// TODO : Make isSandstorm variable global
|
||||
const isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
currentBoard.archive();
|
||||
}
|
||||
const board = this.currentData();
|
||||
board.restore();
|
||||
Utils.goBoardId(board._id);
|
||||
return [
|
||||
{
|
||||
'click .js-restore-board'() {
|
||||
// TODO : Make isSandstorm variable global
|
||||
const isSandstorm =
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
currentBoard.archive();
|
||||
}
|
||||
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');
|
||||
|
|
|
@ -12,8 +12,7 @@ BlazeComponent.extendComponent({
|
|||
// unfortunatly, Blaze doesn't have this notion.
|
||||
this.autorun(() => {
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (!currentBoardId)
|
||||
return;
|
||||
if (!currentBoardId) return;
|
||||
const handle = subManager.subscribe('board', currentBoardId, false);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
|
@ -30,7 +29,6 @@ BlazeComponent.extendComponent({
|
|||
goHome() {
|
||||
FlowRouter.go('home');
|
||||
},
|
||||
|
||||
}).register('board');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
@ -47,7 +45,7 @@ BlazeComponent.extendComponent({
|
|||
if (nullSortSwimlanes.count() > 0) {
|
||||
const swimlanes = currentBoardData.swimlanes();
|
||||
let count = 0;
|
||||
swimlanes.forEach((s) => {
|
||||
swimlanes.forEach(s => {
|
||||
Swimlanes.update(s._id, {
|
||||
$set: {
|
||||
sort: count,
|
||||
|
@ -62,7 +60,7 @@ BlazeComponent.extendComponent({
|
|||
if (nullSortLists.count() > 0) {
|
||||
const lists = currentBoardData.lists();
|
||||
let count = 0;
|
||||
lists.forEach((l) => {
|
||||
lists.forEach(l => {
|
||||
Lists.update(l._id, {
|
||||
$set: {
|
||||
sort: count,
|
||||
|
@ -110,12 +108,15 @@ BlazeComponent.extendComponent({
|
|||
// resize all swimlanes + headers to be a total of 150 px per row
|
||||
// this could be achieved by setIsDragging(true) but we want immediate
|
||||
// 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
|
||||
// the placeholder. We want the element under the cursor to stay
|
||||
// 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) {
|
||||
const parentOffset = ui.item.parent().offset();
|
||||
|
@ -124,7 +125,8 @@ BlazeComponent.extendComponent({
|
|||
|
||||
// compute the new scroll height after the resize and removal of
|
||||
// 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
|
||||
siblings.removeClass('moving-swimlane');
|
||||
|
@ -154,11 +156,14 @@ BlazeComponent.extendComponent({
|
|||
sort(evt, ui) {
|
||||
// get the mouse position in the sortable
|
||||
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
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
if (destPlaceholderIndex !== ui.placeholder.index()) {
|
||||
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 {
|
||||
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)');
|
||||
|
||||
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
|
||||
|
@ -195,45 +212,49 @@ BlazeComponent.extendComponent({
|
|||
isViewSwimlanes() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return ((currentUser.profile || {}).boardView === 'board-view-swimlanes');
|
||||
return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
|
||||
},
|
||||
|
||||
isViewLists() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return true;
|
||||
return ((currentUser.profile || {}).boardView === 'board-view-lists');
|
||||
return (currentUser.profile || {}).boardView === 'board-view-lists';
|
||||
},
|
||||
|
||||
isViewCalendar() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return ((currentUser.profile || {}).boardView === 'board-view-cal');
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
},
|
||||
|
||||
openNewListForm() {
|
||||
if (this.isViewSwimlanes()) {
|
||||
this.childComponents('swimlane')[0]
|
||||
.childComponents('addListAndSwimlaneForm')[0].open();
|
||||
.childComponents('addListAndSwimlaneForm')[0]
|
||||
.open();
|
||||
} else if (this.isViewLists()) {
|
||||
this.childComponents('listsGroup')[0]
|
||||
.childComponents('addListForm')[0].open();
|
||||
.childComponents('addListForm')[0]
|
||||
.open();
|
||||
}
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
// XXX The board-overlay div should probably be moved to the parent
|
||||
// component.
|
||||
'mouseenter .board-overlay'() {
|
||||
if (this.mouseHasEnterCardDetails) {
|
||||
this.showOverlay.set(false);
|
||||
}
|
||||
return [
|
||||
{
|
||||
// XXX The board-overlay div should probably be moved to the parent
|
||||
// component.
|
||||
'mouseenter .board-overlay'() {
|
||||
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
|
||||
|
@ -245,23 +266,24 @@ BlazeComponent.extendComponent({
|
|||
|
||||
scrollLeft(position = 0) {
|
||||
const swimlanes = this.$('.js-swimlanes');
|
||||
swimlanes && swimlanes.animate({
|
||||
scrollLeft: position,
|
||||
});
|
||||
swimlanes &&
|
||||
swimlanes.animate({
|
||||
scrollLeft: position,
|
||||
});
|
||||
},
|
||||
|
||||
scrollTop(position = 0) {
|
||||
const swimlanes = this.$('.js-swimlanes');
|
||||
swimlanes && swimlanes.animate({
|
||||
scrollTop: position,
|
||||
});
|
||||
swimlanes &&
|
||||
swimlanes.animate({
|
||||
scrollTop: position,
|
||||
});
|
||||
},
|
||||
|
||||
}).register('boardBody');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
this.autorun(function(){
|
||||
this.autorun(function() {
|
||||
$('#calendar-view').fullCalendar('refetchEvents');
|
||||
});
|
||||
},
|
||||
|
@ -273,7 +295,8 @@ BlazeComponent.extendComponent({
|
|||
timezone: 'local',
|
||||
header: {
|
||||
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: '',
|
||||
},
|
||||
// height: 'parent', nope, doesn't work as the parent might be small
|
||||
|
@ -283,7 +306,7 @@ BlazeComponent.extendComponent({
|
|||
nowIndicator: true,
|
||||
businessHours: {
|
||||
// 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',
|
||||
end: '18:00',
|
||||
},
|
||||
|
@ -291,20 +314,25 @@ BlazeComponent.extendComponent({
|
|||
events(start, end, timezone, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const events = [];
|
||||
currentBoard.cardsInInterval(start.toDate(), end.toDate()).forEach(function(card){
|
||||
events.push({
|
||||
id: card._id,
|
||||
title: card.title,
|
||||
start: card.startAt,
|
||||
end: card.endAt,
|
||||
allDay: Math.abs(card.endAt.getTime() - card.startAt.getTime()) / 1000 === 24*3600,
|
||||
url: FlowRouter.url('card', {
|
||||
boardId: currentBoard._id,
|
||||
slug: currentBoard.slug,
|
||||
cardId: card._id,
|
||||
}),
|
||||
currentBoard
|
||||
.cardsInInterval(start.toDate(), end.toDate())
|
||||
.forEach(function(card) {
|
||||
events.push({
|
||||
id: card._id,
|
||||
title: card.title,
|
||||
start: card.startAt,
|
||||
end: card.endAt,
|
||||
allDay:
|
||||
Math.abs(card.endAt.getTime() - card.startAt.getTime()) /
|
||||
1000 ===
|
||||
24 * 3600,
|
||||
url: FlowRouter.url('card', {
|
||||
boardId: currentBoard._id,
|
||||
slug: currentBoard.slug,
|
||||
cardId: card._id,
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
callback(events);
|
||||
},
|
||||
eventResize(event, delta, revertFunc) {
|
||||
|
@ -339,6 +367,6 @@ BlazeComponent.extendComponent({
|
|||
isViewCalendar() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return ((currentUser.profile || {}).boardView === 'board-view-cal');
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
},
|
||||
}).register('calendarView');
|
||||
|
|
|
@ -45,15 +45,21 @@ Template.boardMenuPopup.helpers({
|
|||
});
|
||||
|
||||
Template.boardChangeTitlePopup.events({
|
||||
submit(evt, tpl) {
|
||||
const newTitle = tpl.$('.js-board-name').val().trim();
|
||||
const newDesc = tpl.$('.js-board-desc').val().trim();
|
||||
submit(event, templateInstance) {
|
||||
const newTitle = templateInstance
|
||||
.$('.js-board-name')
|
||||
.val()
|
||||
.trim();
|
||||
const newDesc = templateInstance
|
||||
.$('.js-board-desc')
|
||||
.val()
|
||||
.trim();
|
||||
if (newTitle) {
|
||||
this.rename(newTitle);
|
||||
this.setDescription(newDesc);
|
||||
Popup.close();
|
||||
}
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -76,67 +82,79 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-edit-board-title': Popup.open('boardChangeTitle'),
|
||||
'click .js-star-board'() {
|
||||
Meteor.user().toggleBoardStar(Session.get('currentBoard'));
|
||||
return [
|
||||
{
|
||||
'click .js-edit-board-title': Popup.open('boardChangeTitle'),
|
||||
'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');
|
||||
|
||||
Template.boardHeaderBar.helpers({
|
||||
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());
|
||||
},
|
||||
|
||||
onSubmit(evt) {
|
||||
evt.preventDefault();
|
||||
onSubmit(event) {
|
||||
event.preventDefault();
|
||||
const title = this.find('.js-new-board-title').value;
|
||||
const visibility = this.visibility.get();
|
||||
|
||||
this.boardId.set(Boards.insert({
|
||||
title,
|
||||
permission: visibility,
|
||||
}));
|
||||
this.boardId.set(
|
||||
Boards.insert({
|
||||
title,
|
||||
permission: visibility,
|
||||
}),
|
||||
);
|
||||
|
||||
Swimlanes.insert({
|
||||
title: 'Default',
|
||||
|
@ -183,26 +203,28 @@ const CreateBoard = BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-visibility'() {
|
||||
this.setVisibility(this.currentData());
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
||||
(class HeaderBarCreateBoard extends CreateBoard {
|
||||
onSubmit(evt) {
|
||||
super.onSubmit(evt);
|
||||
onSubmit(event) {
|
||||
super.onSubmit(event);
|
||||
// Immediately star boards crated with the headerbar popup.
|
||||
Meteor.user().toggleBoardStar(this.boardId.get());
|
||||
}
|
||||
}).register('headerBarCreateBoardPopup');
|
||||
}.register('headerBarCreateBoardPopup'));
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
visibilityCheck() {
|
||||
|
@ -218,9 +240,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-visibility': this.selectBoardVisibility,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-select-visibility': this.selectBoardVisibility,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('boardChangeVisibilityPopup');
|
||||
|
||||
|
@ -235,13 +259,21 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-watch'() {
|
||||
const level = this.currentData();
|
||||
Meteor.call('watch', 'board', Session.get('currentBoard'), level, (err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
});
|
||||
return [
|
||||
{
|
||||
'click .js-select-watch'() {
|
||||
const level = this.currentData();
|
||||
Meteor.call(
|
||||
'watch',
|
||||
'board',
|
||||
Session.get('currentBoard'),
|
||||
level,
|
||||
(err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardChangeWatchPopup');
|
||||
|
|
|
@ -21,11 +21,14 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
type: 'board',
|
||||
}, { sort: ['title'] });
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
type: 'board',
|
||||
},
|
||||
{ sort: ['title'] },
|
||||
);
|
||||
},
|
||||
isStarred() {
|
||||
const user = Meteor.user();
|
||||
|
@ -48,46 +51,49 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-board': Popup.open('createBoard'),
|
||||
'click .js-star-board'(evt) {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.user().toggleBoardStar(boardId);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-clone-board'(evt) {
|
||||
Meteor.call('cloneBoard',
|
||||
this.currentData()._id,
|
||||
Session.get('fromBoard'),
|
||||
(err, res) => {
|
||||
if (err) {
|
||||
this.setError(err.error);
|
||||
} else {
|
||||
Session.set('fromBoard', null);
|
||||
Utils.goBoardId(res);
|
||||
return [
|
||||
{
|
||||
'click .js-add-board': Popup.open('createBoard'),
|
||||
'click .js-star-board'(evt) {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.user().toggleBoardStar(boardId);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-clone-board'(evt) {
|
||||
Meteor.call(
|
||||
'cloneBoard',
|
||||
this.currentData()._id,
|
||||
Session.get('fromBoard'),
|
||||
(err, res) => {
|
||||
if (err) {
|
||||
this.setError(err.error);
|
||||
} 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');
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
Template.attachmentsGalery.events({
|
||||
'click .js-add-attachment': Popup.open('cardAttachments'),
|
||||
'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete',
|
||||
'click .js-confirm-delete': Popup.afterConfirm(
|
||||
'attachmentDelete',
|
||||
function() {
|
||||
Attachments.remove(this._id);
|
||||
Popup.close();
|
||||
}
|
||||
},
|
||||
),
|
||||
// If we let this event bubble, FlowRouter will handle it and empty the page
|
||||
// content, see #101.
|
||||
|
@ -17,8 +18,8 @@ Template.attachmentsGalery.events({
|
|||
'click .js-remove-cover'() {
|
||||
Cards.findOne(this.cardId).unsetCover();
|
||||
},
|
||||
'click .js-preview-image'(evt) {
|
||||
Popup.open('previewAttachedImage').call(this, evt);
|
||||
'click .js-preview-image'(event) {
|
||||
Popup.open('previewAttachedImage').call(this, event);
|
||||
// when multiple thumbnails, if click one then another very fast,
|
||||
// we might get a wrong width from previous img.
|
||||
// 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 (w > 300) {
|
||||
$('div.pop-over').css({
|
||||
width: (w + 20),
|
||||
width: w + 20,
|
||||
position: 'absolute',
|
||||
left: (window.innerWidth - w)/2,
|
||||
top: (window.innerHeight - h)/2,
|
||||
left: (window.innerWidth - w) / 2,
|
||||
top: (window.innerHeight - h) / 2,
|
||||
});
|
||||
}
|
||||
};
|
||||
const url = $(evt.currentTarget).attr('src');
|
||||
if (img.src === url && img.complete)
|
||||
rePosPopup();
|
||||
else
|
||||
img.onload = rePosPopup;
|
||||
const url = $(event.currentTarget).attr('src');
|
||||
if (img.src === url && img.complete) rePosPopup();
|
||||
else img.onload = rePosPopup;
|
||||
},
|
||||
});
|
||||
|
||||
Template.previewAttachedImagePopup.events({
|
||||
'click .js-large-image-clicked'(){
|
||||
'click .js-large-image-clicked'() {
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAttachmentsPopup.events({
|
||||
'change .js-attach-file'(evt) {
|
||||
'change .js-attach-file'(event) {
|
||||
const card = this;
|
||||
FS.Utility.eachFile(evt, (f) => {
|
||||
FS.Utility.eachFile(event, f => {
|
||||
const file = new FS.File(f);
|
||||
if (card.isLinkedCard()) {
|
||||
file.boardId = Cards.findOne(card.linkedId).boardId;
|
||||
|
@ -77,9 +76,9 @@ Template.cardAttachmentsPopup.events({
|
|||
Popup.close();
|
||||
});
|
||||
},
|
||||
'click .js-computer-upload'(evt, tpl) {
|
||||
tpl.find('.js-attach-file').click();
|
||||
evt.preventDefault();
|
||||
'click .js-computer-upload'(event, templateInstance) {
|
||||
templateInstance.find('.js-attach-file').click();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-upload-clipboard-image': Popup.open('previewClipboardImage'),
|
||||
});
|
||||
|
@ -88,7 +87,7 @@ let pastedResults = null;
|
|||
|
||||
Template.previewClipboardImagePopup.onRendered(() => {
|
||||
// we can paste image from clipboard
|
||||
$(document.body).pasteImageReader((results) => {
|
||||
$(document.body).pasteImageReader(results => {
|
||||
if (results.dataURL.startsWith('data:image/')) {
|
||||
$('img.preview-clipboard-image').attr('src', results.dataURL);
|
||||
pastedResults = results;
|
||||
|
@ -96,7 +95,7 @@ Template.previewClipboardImagePopup.onRendered(() => {
|
|||
});
|
||||
|
||||
// we can also drag & drop image file to it
|
||||
$(document.body).dropImageReader((results) => {
|
||||
$(document.body).dropImageReader(results => {
|
||||
if (results.dataURL.startsWith('data:image/')) {
|
||||
$('img.preview-clipboard-image').attr('src', results.dataURL);
|
||||
pastedResults = results;
|
||||
|
|
|
@ -7,22 +7,21 @@ Template.cardCustomFieldsPopup.helpers({
|
|||
});
|
||||
|
||||
Template.cardCustomFieldsPopup.events({
|
||||
'click .js-select-field'(evt) {
|
||||
'click .js-select-field'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const customFieldId = this._id;
|
||||
card.toggleCustomField(customFieldId);
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-settings'(evt) {
|
||||
'click .js-settings'(event) {
|
||||
EscapeActions.executeUpTo('detailsPane');
|
||||
Sidebar.setView('customFields');
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
// cardCustomField
|
||||
const CardCustomField = BlazeComponent.extendComponent({
|
||||
|
||||
getTemplate() {
|
||||
return `cardCustomField-${this.data().definition.type}`;
|
||||
},
|
||||
|
@ -34,52 +33,55 @@ const CardCustomField = BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
CardCustomField.register('cardCustomField');
|
||||
|
||||
// cardCustomField-text
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-card-customfield-text'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = this.currentComponent().getValue();
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
return [
|
||||
{
|
||||
'submit .js-card-customfield-text'(event) {
|
||||
event.preventDefault();
|
||||
const value = this.currentComponent().getValue();
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-text');
|
||||
}.register('cardCustomField-text'));
|
||||
|
||||
// cardCustomField-number
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-card-customfield-number'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = parseInt(this.find('input').value, 10);
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
return [
|
||||
{
|
||||
'submit .js-card-customfield-number'(event) {
|
||||
event.preventDefault();
|
||||
const value = parseInt(this.find('input').value, 10);
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-number');
|
||||
}.register('cardCustomField-number'));
|
||||
|
||||
// cardCustomField-date
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
|
@ -108,8 +110,10 @@ CardCustomField.register('cardCustomField');
|
|||
}
|
||||
|
||||
classes() {
|
||||
if (this.date.get().isBefore(this.now.get(), 'minute') &&
|
||||
this.now.get().isBefore(this.data().value)) {
|
||||
if (
|
||||
this.date.get().isBefore(this.now.get(), 'minute') &&
|
||||
this.now.get().isBefore(this.data().value)
|
||||
) {
|
||||
return 'current';
|
||||
}
|
||||
return '';
|
||||
|
@ -120,12 +124,13 @@ CardCustomField.register('cardCustomField');
|
|||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-edit-date': Popup.open('cardCustomField-date'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-edit-date': Popup.open('cardCustomField-date'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-date');
|
||||
}.register('cardCustomField-date'));
|
||||
|
||||
// cardCustomField-datePopup
|
||||
(class extends DatePicker {
|
||||
|
@ -144,11 +149,10 @@ CardCustomField.register('cardCustomField');
|
|||
_deleteDate() {
|
||||
this.card.setCustomField(this.customFieldId, '');
|
||||
}
|
||||
}).register('cardCustomField-datePopup');
|
||||
}.register('cardCustomField-datePopup'));
|
||||
|
||||
// cardCustomField-dropdown
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this._items = this.data().definition.settings.dropdownItems;
|
||||
|
@ -160,20 +164,23 @@ CardCustomField.register('cardCustomField');
|
|||
}
|
||||
|
||||
selectedItem() {
|
||||
const selected = this._items.find((item) => {
|
||||
const selected = this._items.find(item => {
|
||||
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() {
|
||||
return [{
|
||||
'submit .js-card-customfield-dropdown'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = this.find('select').value;
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
return [
|
||||
{
|
||||
'submit .js-card-customfield-dropdown'(event) {
|
||||
event.preventDefault();
|
||||
const value = this.find('select').value;
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-dropdown');
|
||||
}.register('cardCustomField-dropdown'));
|
||||
|
|
|
@ -11,15 +11,20 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
onRendered() {
|
||||
const $picker = this.$('.js-datepicker').datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
}).on('changeDate', function(evt) {
|
||||
this.find('#date').value = moment(evt.date).format('L');
|
||||
this.error.set('');
|
||||
this.find('#time').focus();
|
||||
}.bind(this));
|
||||
const $picker = this.$('.js-datepicker')
|
||||
.datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
})
|
||||
.on(
|
||||
'changeDate',
|
||||
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()) {
|
||||
$picker.datepicker('update', this.date.get().toDate());
|
||||
|
@ -27,13 +32,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
showDate() {
|
||||
if (this.date.get().isValid())
|
||||
return this.date.get().format('L');
|
||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
||||
return '';
|
||||
},
|
||||
showTime() {
|
||||
if (this.date.get().isValid())
|
||||
return this.date.get().format('LT');
|
||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
||||
return '';
|
||||
},
|
||||
dateFormat() {
|
||||
|
@ -44,51 +47,58 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'keyup .js-date-field'() {
|
||||
// parse for localized date format in strict mode
|
||||
const dateMoment = moment(this.find('#date').value, 'L', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
|
||||
}
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(this.find('#time').value, 'LT', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
},
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
'keyup .js-date-field'() {
|
||||
// parse for localized date format in strict mode
|
||||
const dateMoment = moment(this.find('#date').value, 'L', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
|
||||
}
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(this.find('#time').value, 'LT', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
},
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
// if no time was given, init with 12:00
|
||||
const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
// 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 dateString = `${evt.target.date.value} ${time}`;
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
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();
|
||||
}
|
||||
else {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
Template.dateBadge.helpers({
|
||||
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 {
|
||||
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) {
|
||||
|
@ -106,8 +117,7 @@ Template.dateBadge.helpers({
|
|||
_deleteDate() {
|
||||
this.card.setReceived(null);
|
||||
}
|
||||
}).register('editCardReceivedDatePopup');
|
||||
|
||||
}.register('editCardReceivedDatePopup'));
|
||||
|
||||
// editCardStartDatePopup
|
||||
(class extends DatePicker {
|
||||
|
@ -119,7 +129,10 @@ Template.dateBadge.helpers({
|
|||
onRendered() {
|
||||
super.onRendered();
|
||||
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() {
|
||||
this.card.setStart(null);
|
||||
}
|
||||
}).register('editCardStartDatePopup');
|
||||
}.register('editCardStartDatePopup'));
|
||||
|
||||
// editCardDueDatePopup
|
||||
(class extends DatePicker {
|
||||
|
@ -153,7 +166,7 @@ Template.dateBadge.helpers({
|
|||
_deleteDate() {
|
||||
this.card.setDue(null);
|
||||
}
|
||||
}).register('editCardDueDatePopup');
|
||||
}.register('editCardDueDatePopup'));
|
||||
|
||||
// editCardEndDatePopup
|
||||
(class extends DatePicker {
|
||||
|
@ -176,8 +189,7 @@ Template.dateBadge.helpers({
|
|||
_deleteDate() {
|
||||
this.card.setEnd(null);
|
||||
}
|
||||
}).register('editCardEndDatePopup');
|
||||
|
||||
}.register('editCardEndDatePopup'));
|
||||
|
||||
// Display received, start, due & end dates
|
||||
const CardDate = BlazeComponent.extendComponent({
|
||||
|
@ -224,17 +236,20 @@ class CardReceivedDate extends CardDate {
|
|||
const startAt = this.data().getStart();
|
||||
const theDate = this.date.get();
|
||||
// if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
|
||||
if (((startAt) && (theDate.isAfter(dueAt))) ||
|
||||
((endAt) && (theDate.isAfter(endAt))) ||
|
||||
((dueAt) && (theDate.isAfter(dueAt))))
|
||||
if (
|
||||
(startAt && theDate.isAfter(dueAt)) ||
|
||||
(endAt && theDate.isAfter(endAt)) ||
|
||||
(dueAt && theDate.isAfter(dueAt))
|
||||
)
|
||||
classes += 'long-overdue';
|
||||
else
|
||||
classes += 'current';
|
||||
else classes += 'current';
|
||||
return classes;
|
||||
}
|
||||
|
||||
showTitle() {
|
||||
return `${TAPi18n.__('card-received-on')} ${this.date.get().format('LLLL')}`;
|
||||
return `${TAPi18n.__('card-received-on')} ${this.date
|
||||
.get()
|
||||
.format('LLLL')}`;
|
||||
}
|
||||
|
||||
events() {
|
||||
|
@ -261,13 +276,10 @@ class CardStartDate extends CardDate {
|
|||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
// if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
|
||||
if (((endAt) && (theDate.isAfter(endAt))) ||
|
||||
((dueAt) && (theDate.isAfter(dueAt))))
|
||||
if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt)))
|
||||
classes += 'long-overdue';
|
||||
else if (theDate.isBefore(now, 'minute'))
|
||||
classes += 'almost-due';
|
||||
else
|
||||
classes += 'current';
|
||||
else if (theDate.isBefore(now, 'minute')) classes += 'almost-due';
|
||||
else classes += 'current';
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
@ -298,17 +310,12 @@ class CardDueDate extends CardDate {
|
|||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
// if the due date is after the end date, green - done early
|
||||
if ((endAt) && (theDate.isAfter(endAt)))
|
||||
classes += 'current';
|
||||
if (endAt && theDate.isAfter(endAt)) classes += 'current';
|
||||
// if there is an end date, don't need to flag the due date
|
||||
else if (endAt)
|
||||
classes += '';
|
||||
else if (now.diff(theDate, 'days') >= 2)
|
||||
classes += 'long-overdue';
|
||||
else if (now.diff(theDate, 'minute') >= 0)
|
||||
classes += 'due';
|
||||
else if (now.diff(theDate, 'days') >= -1)
|
||||
classes += 'almost-due';
|
||||
else if (endAt) classes += '';
|
||||
else if (now.diff(theDate, 'days') >= 2) classes += 'long-overdue';
|
||||
else if (now.diff(theDate, 'minute') >= 0) classes += 'due';
|
||||
else if (now.diff(theDate, 'days') >= -1) classes += 'almost-due';
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
@ -337,12 +344,9 @@ class CardEndDate extends CardDate {
|
|||
let classes = 'end-date' + ' ';
|
||||
const dueAt = this.data().getDue();
|
||||
const theDate = this.date.get();
|
||||
if (theDate.diff(dueAt, 'days') >= 2)
|
||||
classes += 'long-overdue';
|
||||
else if (theDate.diff(dueAt, 'days') >= 0)
|
||||
classes += 'due';
|
||||
else if (theDate.diff(dueAt, 'days') >= -2)
|
||||
classes += 'almost-due';
|
||||
if (theDate.diff(dueAt, 'days') >= 2) classes += 'long-overdue';
|
||||
else if (theDate.diff(dueAt, 'days') >= 0) classes += 'due';
|
||||
else if (theDate.diff(dueAt, 'days') >= -2) classes += 'almost-due';
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
@ -362,22 +366,22 @@ CardEndDate.register('cardEndDate');
|
|||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardReceivedDate');
|
||||
}.register('minicardReceivedDate'));
|
||||
|
||||
(class extends CardStartDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardStartDate');
|
||||
}.register('minicardStartDate'));
|
||||
|
||||
(class extends CardDueDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardDueDate');
|
||||
}.register('minicardDueDate'));
|
||||
|
||||
(class extends CardEndDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardEndDate');
|
||||
}.register('minicardEndDate'));
|
||||
|
|
|
@ -27,7 +27,7 @@ BlazeComponent.extendComponent({
|
|||
onCreated() {
|
||||
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
this.isLoaded = new ReactiveVar(false);
|
||||
const boardBody = this.parentComponent().parentComponent();
|
||||
const boardBody = this.parentComponent().parentComponent();
|
||||
//in Miniview parent is Board, not BoardBody.
|
||||
if (boardBody !== null) {
|
||||
boardBody.showOverlay.set(true);
|
||||
|
@ -48,7 +48,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
|
||||
scrollParentContainer() {
|
||||
|
@ -80,18 +84,17 @@ BlazeComponent.extendComponent({
|
|||
const cardContainerScrollTop = $cardContainer.scrollTop();
|
||||
|
||||
let topOffset = false;
|
||||
if(cardViewStartTop !== 100){
|
||||
if (cardViewStartTop !== 100) {
|
||||
topOffset = cardViewStartTop - 100;
|
||||
}
|
||||
if(topOffset !== false) {
|
||||
if (topOffset !== false) {
|
||||
bodyBoardComponent.scrollTop(cardContainerScrollTop + topOffset);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
presentParentTask() {
|
||||
let result = this.currentBoard.presentParentTask;
|
||||
if ((result === null) || (result === undefined)) {
|
||||
if (result === null || result === undefined) {
|
||||
result = 'no-parent';
|
||||
}
|
||||
return result;
|
||||
|
@ -116,7 +119,13 @@ BlazeComponent.extendComponent({
|
|||
onRendered() {
|
||||
if (!Utils.isMiniScreen()) {
|
||||
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();
|
||||
}, 500);
|
||||
}
|
||||
|
@ -212,7 +221,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
onDestroyed() {
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not board body.
|
||||
if (parentComponent === null) return;
|
||||
parentComponent.showOverlay.set(false);
|
||||
|
@ -228,56 +237,64 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
};
|
||||
|
||||
return [{
|
||||
...events,
|
||||
'click .js-close-card-details' () {
|
||||
Utils.goBoardId(this.data().boardId);
|
||||
return [
|
||||
{
|
||||
...events,
|
||||
'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');
|
||||
|
||||
|
@ -297,7 +314,9 @@ BlazeComponent.extendComponent({
|
|||
close(isReset = false) {
|
||||
if (this.isOpen.get() && !isReset) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -311,12 +330,14 @@ BlazeComponent.extendComponent({
|
|||
|
||||
events() {
|
||||
const parentEvents = InlinedForm.prototype.events()[0];
|
||||
return [{
|
||||
...parentEvents,
|
||||
'click .js-close-inlined-form': this.reset,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
...parentEvents,
|
||||
'click .js-close-inlined-form': this.reset,
|
||||
},
|
||||
];
|
||||
}
|
||||
}).register('inlinedCardDescription');
|
||||
}.register('inlinedCardDescription'));
|
||||
|
||||
Template.cardDetailsActionsPopup.helpers({
|
||||
isWatching() {
|
||||
|
@ -324,7 +345,11 @@ Template.cardDetailsActionsPopup.helpers({
|
|||
},
|
||||
|
||||
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-checklist-cards': Popup.open('copyChecklistToManyCards'),
|
||||
'click .js-set-card-color': Popup.open('setCardColor'),
|
||||
'click .js-move-card-to-top' (evt) {
|
||||
evt.preventDefault();
|
||||
const minOrder = _.min(this.list().cards(this.swimlaneId).map((c) => c.sort));
|
||||
'click .js-move-card-to-top'(event) {
|
||||
event.preventDefault();
|
||||
const minOrder = _.min(
|
||||
this.list()
|
||||
.cards(this.swimlaneId)
|
||||
.map(c => c.sort),
|
||||
);
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
|
||||
},
|
||||
'click .js-move-card-to-bottom' (evt) {
|
||||
evt.preventDefault();
|
||||
const maxOrder = _.max(this.list().cards(this.swimlaneId).map((c) => c.sort));
|
||||
'click .js-move-card-to-bottom'(event) {
|
||||
event.preventDefault();
|
||||
const maxOrder = _.max(
|
||||
this.list()
|
||||
.cards(this.swimlaneId)
|
||||
.map(c => c.sort),
|
||||
);
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
|
||||
},
|
||||
'click .js-archive' (evt) {
|
||||
evt.preventDefault();
|
||||
'click .js-archive'(event) {
|
||||
event.preventDefault();
|
||||
this.archive();
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-more': Popup.open('cardMore'),
|
||||
'click .js-toggle-watch-card' () {
|
||||
'click .js-toggle-watch-card'() {
|
||||
const currentCard = this;
|
||||
const level = currentCard.findWatcher(Meteor.userId()) ? null : 'watching';
|
||||
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'));
|
||||
});
|
||||
|
||||
Template.editCardTitleForm.events({
|
||||
'keydown .js-edit-card-title' (evt) {
|
||||
'keydown .js-edit-card-title'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
// 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();
|
||||
}
|
||||
},
|
||||
|
@ -386,9 +419,9 @@ Template.editCardRequesterForm.onRendered(function() {
|
|||
});
|
||||
|
||||
Template.editCardRequesterForm.events({
|
||||
'keydown .js-edit-card-requester'(evt) {
|
||||
'keydown .js-edit-card-requester'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
if (evt.keyCode === 13) {
|
||||
if (event.keyCode === 13) {
|
||||
$('.js-submit-edit-card-requester-form').click();
|
||||
}
|
||||
},
|
||||
|
@ -399,16 +432,16 @@ Template.editCardAssignerForm.onRendered(function() {
|
|||
});
|
||||
|
||||
Template.editCardAssignerForm.events({
|
||||
'keydown .js-edit-card-assigner'(evt) {
|
||||
'keydown .js-edit-card-assigner'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
if (evt.keyCode === 13) {
|
||||
if (event.keyCode === 13) {
|
||||
$('.js-submit-edit-card-assigner-form').click();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.moveCardPopup.events({
|
||||
'click .js-done' () {
|
||||
'click .js-done'() {
|
||||
// XXX We should *not* get the currentCard from the global state, but
|
||||
// instead from a “component” state.
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
|
@ -429,13 +462,16 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Meteor.user().getTemplatesBoardId()},
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Meteor.user().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
|
@ -450,12 +486,14 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-select-boards'(evt) {
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
subManager.subscribe('board', this.selectedBoardId.get(), false);
|
||||
return [
|
||||
{
|
||||
'change .js-select-boards'(event) {
|
||||
this.selectedBoardId.set($(event.currentTarget).val());
|
||||
subManager.subscribe('board', this.selectedBoardId.get(), false);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardsAndLists');
|
||||
|
||||
|
@ -471,7 +509,9 @@ Template.copyCardPopup.events({
|
|||
const textarea = $('#copy-card-title');
|
||||
const title = textarea.val().trim();
|
||||
// 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) {
|
||||
card.title = title;
|
||||
|
@ -489,7 +529,7 @@ Template.copyCardPopup.events({
|
|||
});
|
||||
|
||||
Template.copyChecklistToManyCardsPopup.events({
|
||||
'click .js-done' () {
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const oldId = card._id;
|
||||
card._id = null;
|
||||
|
@ -502,11 +542,13 @@ Template.copyChecklistToManyCardsPopup.events({
|
|||
const textarea = $('#copy-card-title');
|
||||
const titleEntry = textarea.val().trim();
|
||||
// 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) {
|
||||
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];
|
||||
card.title = obj.title;
|
||||
card.description = obj.description;
|
||||
|
@ -519,12 +561,12 @@ Template.copyChecklistToManyCardsPopup.events({
|
|||
Filter.addException(_id);
|
||||
|
||||
// copy checklists
|
||||
Checklists.find({cardId: oldId}).forEach((ch) => {
|
||||
Checklists.find({ cardId: oldId }).forEach(ch => {
|
||||
ch.copy(_id);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
cursor = Cards.find({parentId: oldId});
|
||||
cursor = Cards.find({ parentId: oldId });
|
||||
cursor.forEach(function() {
|
||||
'use strict';
|
||||
const subtask = arguments[0];
|
||||
|
@ -534,7 +576,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
|||
});
|
||||
|
||||
// copy card comments
|
||||
CardComments.find({cardId: oldId}).forEach((cmt) => {
|
||||
CardComments.find({ cardId: oldId }).forEach(cmt => {
|
||||
cmt.copy(_id);
|
||||
});
|
||||
}
|
||||
|
@ -550,7 +592,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
colors() {
|
||||
return cardColors.map((color) => ({ color, name: '' }));
|
||||
return cardColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
|
@ -561,19 +603,21 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
||||
|
@ -592,15 +636,18 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {
|
||||
$ne: Meteor.user().getTemplatesBoardId(),
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {
|
||||
$ne: Meteor.user().getTemplatesBoardId(),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
|
@ -609,7 +656,7 @@ BlazeComponent.extendComponent({
|
|||
if (this.parentBoard.get()) {
|
||||
return Cards.find({
|
||||
boardId: this.parentBoard.get(),
|
||||
_id: {$ne: currentId},
|
||||
_id: { $ne: currentId },
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
|
@ -642,63 +689,69 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-copy-card-link-to-clipboard' () {
|
||||
// Clipboard code from:
|
||||
// https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
|
||||
const StringToCopyElement = document.getElementById('cardURL');
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
} else {
|
||||
document.getElementById('cardURL').selectionStart = 0;
|
||||
document.getElementById('cardURL').selectionEnd = 999;
|
||||
document.execCommand('copy');
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) { // Chrome
|
||||
window.getSelection().empty();
|
||||
} else if (window.getSelection().removeAllRanges) { // Firefox
|
||||
window.getSelection().removeAllRanges();
|
||||
return [
|
||||
{
|
||||
'click .js-copy-card-link-to-clipboard'() {
|
||||
// Clipboard code from:
|
||||
// https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
|
||||
const StringToCopyElement = document.getElementById('cardURL');
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
} else {
|
||||
document.getElementById('cardURL').selectionStart = 0;
|
||||
document.getElementById('cardURL').selectionEnd = 999;
|
||||
document.execCommand('copy');
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) {
|
||||
// Chrome
|
||||
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');
|
||||
|
||||
|
||||
// Close the card details pane by pressing escape
|
||||
EscapeActions.register('detailsPane',
|
||||
EscapeActions.register(
|
||||
'detailsPane',
|
||||
() => {
|
||||
Utils.goBoardId(Session.get('currentBoard'));
|
||||
},
|
||||
() => {
|
||||
return !Session.equals('currentCard', null);
|
||||
}, {
|
||||
},
|
||||
{
|
||||
noClickEscapeOn: '.js-card-details,.board-sidebar,#header',
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -20,29 +20,31 @@ BlazeComponent.extendComponent({
|
|||
this.card.setSpentTime(null);
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
//TODO : need checking this portion
|
||||
'submit .edit-time'(evt) {
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
//TODO : need checking this portion
|
||||
'submit .edit-time'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
const spentTime = parseFloat(evt.target.time.value);
|
||||
const isOvertime = this.card.getIsOvertime();
|
||||
const spentTime = parseFloat(evt.target.time.value);
|
||||
const isOvertime = this.card.getIsOvertime();
|
||||
|
||||
if (spentTime >= 0) {
|
||||
this.storeTime(spentTime, isOvertime);
|
||||
if (spentTime >= 0) {
|
||||
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();
|
||||
} else {
|
||||
this.error.set('invalid-time');
|
||||
evt.target.time.focus();
|
||||
}
|
||||
},
|
||||
'click a.js-toggle-overtime': this.toggleOvertime,
|
||||
},
|
||||
'click .js-delete-time'(evt) {
|
||||
evt.preventDefault();
|
||||
this.deleteTime();
|
||||
Popup.close();
|
||||
},
|
||||
'click a.js-toggle-overtime': this.toggleOvertime,
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('editCardSpentTimePopup');
|
||||
|
||||
|
@ -56,23 +58,33 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
showTitle() {
|
||||
if (this.data().getIsOvertime()) {
|
||||
return `${TAPi18n.__('overtime')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
return `${TAPi18n.__(
|
||||
'overtime',
|
||||
)} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
} else {
|
||||
return `${TAPi18n.__('card-spent')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
return `${TAPi18n.__(
|
||||
'card-spent',
|
||||
)} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
}
|
||||
},
|
||||
showTime() {
|
||||
return this.data().getSpentTime();
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-edit-time': Popup.open('editCardSpentTime'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-edit-time': Popup.open('editCardSpentTime'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('cardSpentTime');
|
||||
|
||||
Template.timeBadge.helpers({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -64,20 +64,22 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
}).register('checklistDetail');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
||||
addChecklist(event) {
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-add-checklist-item');
|
||||
const title = textarea.value.trim();
|
||||
let cardId = this.currentData().cardId;
|
||||
const card = Cards.findOne(cardId);
|
||||
if (card.isLinked())
|
||||
cardId = card.linkedId;
|
||||
if (card.isLinked()) cardId = card.linkedId;
|
||||
|
||||
if (title) {
|
||||
Checklists.insert({
|
||||
|
@ -86,7 +88,9 @@ BlazeComponent.extendComponent({
|
|||
sort: card.checklists().count(),
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.$('.add-checklist-item').last().click();
|
||||
this.$('.add-checklist-item')
|
||||
.last()
|
||||
.click();
|
||||
}, 100);
|
||||
}
|
||||
textarea.value = '';
|
||||
|
@ -113,7 +117,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
|
||||
deleteChecklist() {
|
||||
|
@ -167,37 +175,43 @@ BlazeComponent.extendComponent({
|
|||
events() {
|
||||
const events = {
|
||||
'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.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
|
||||
},
|
||||
};
|
||||
|
||||
return [{
|
||||
...events,
|
||||
'submit .js-add-checklist': this.addChecklist,
|
||||
'submit .js-edit-checklist-title': this.editChecklist,
|
||||
'submit .js-add-checklist-item': this.addChecklistItem,
|
||||
'submit .js-edit-checklist-item': this.editChecklistItem,
|
||||
'click .js-delete-checklist-item': this.deleteItem,
|
||||
'click .confirm-checklist-delete': this.deleteChecklist,
|
||||
keydown: this.pressKey,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
...events,
|
||||
'submit .js-add-checklist': this.addChecklist,
|
||||
'submit .js-edit-checklist-title': this.editChecklist,
|
||||
'submit .js-add-checklist-item': this.addChecklistItem,
|
||||
'submit .js-edit-checklist-item': this.editChecklistItem,
|
||||
'click .js-delete-checklist-item': this.deleteItem,
|
||||
'click .confirm-checklist-delete': this.deleteChecklist,
|
||||
keydown: this.pressKey,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('checklists');
|
||||
|
||||
Template.checklistDeleteDialog.onCreated(() => {
|
||||
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
|
||||
};
|
||||
//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
|
||||
$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);
|
||||
}
|
||||
});
|
||||
|
@ -206,12 +220,16 @@ Template.checklistDeleteDialog.onCreated(() => {
|
|||
Template.checklistDeleteDialog.onDestroyed(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
$cardDetails.off('scroll'); //Reactivate scrolling
|
||||
$cardDetails.animate( { scrollTop: this.scrollState.position });
|
||||
$cardDetails.animate({ scrollTop: this.scrollState.position });
|
||||
});
|
||||
|
||||
Template.checklistItemDetail.helpers({
|
||||
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() {
|
||||
return [{
|
||||
'click .js-checklist-item .check-box': this.toggleItem,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-checklist-item .check-box': this.toggleItem,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('checklistItemDetail');
|
||||
|
|
|
@ -9,7 +9,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
labels() {
|
||||
return labelColors.map((color) => ({ color, name: '' }));
|
||||
return labelColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
|
@ -17,11 +17,13 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('formLabel');
|
||||
|
||||
|
@ -38,19 +40,19 @@ Template.createLabelPopup.helpers({
|
|||
});
|
||||
|
||||
Template.cardLabelsPopup.events({
|
||||
'click .js-select-label'(evt) {
|
||||
'click .js-select-label'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const labelId = this._id;
|
||||
card.toggleLabel(labelId);
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-edit-label': Popup.open('editLabel'),
|
||||
'click .js-add-label': Popup.open('createLabel'),
|
||||
});
|
||||
|
||||
Template.formLabel.events({
|
||||
'click .js-palette-color'(evt) {
|
||||
const $this = $(evt.currentTarget);
|
||||
'click .js-palette-color'(event) {
|
||||
const $this = $(event.currentTarget);
|
||||
|
||||
// hide selected ll colors
|
||||
$('.js-palette-select').addClass('hide');
|
||||
|
@ -62,11 +64,14 @@ Template.formLabel.events({
|
|||
|
||||
Template.createLabelPopup.events({
|
||||
// Create the new label
|
||||
'submit .create-label'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
'submit .create-label'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
const name = tpl.$('#labelName').val().trim();
|
||||
const color = Blaze.getData(tpl.find('.fa-check')).color;
|
||||
const name = templateInstance
|
||||
.$('#labelName')
|
||||
.val()
|
||||
.trim();
|
||||
const color = Blaze.getData(templateInstance.find('.fa-check')).color;
|
||||
board.addLabel(name, color);
|
||||
Popup.back();
|
||||
},
|
||||
|
@ -78,11 +83,14 @@ Template.editLabelPopup.events({
|
|||
board.removeLabel(this._id);
|
||||
Popup.back(2);
|
||||
}),
|
||||
'submit .edit-label'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
'submit .edit-label'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
const name = tpl.$('#labelName').val().trim();
|
||||
const color = Blaze.getData(tpl.find('.fa-check')).color;
|
||||
const name = templateInstance
|
||||
.$('#labelName')
|
||||
.val()
|
||||
.trim();
|
||||
const color = Blaze.getData(templateInstance.find('.fa-check')).color;
|
||||
board.editLabel(this._id, name, color);
|
||||
Popup.back();
|
||||
},
|
||||
|
|
|
@ -8,13 +8,14 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-linked-link' () {
|
||||
if (this.data().isLinkedCard())
|
||||
Utils.goCardId(this.data().linkedId);
|
||||
else if (this.data().isLinkedBoard())
|
||||
Utils.goBoardId(this.data().linkedId);
|
||||
return [
|
||||
{
|
||||
'click .js-linked-link'() {
|
||||
if (this.data().isLinkedCard()) Utils.goCardId(this.data().linkedId);
|
||||
else if (this.data().isLinkedBoard())
|
||||
Utils.goBoardId(this.data().linkedId);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('minicard');
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
BlazeComponent.extendComponent({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
}).register('subtaskDetail');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
||||
addSubtask(event) {
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-add-subtask-item');
|
||||
|
@ -38,9 +41,10 @@ BlazeComponent.extendComponent({
|
|||
// See https://github.com/wekan/wekan/issues/80
|
||||
Filter.addException(_id);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
this.$('.add-subtask-item').last().click();
|
||||
this.$('.add-subtask-item')
|
||||
.last()
|
||||
.click();
|
||||
}, 100);
|
||||
}
|
||||
textarea.value = '';
|
||||
|
@ -48,7 +52,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
|
||||
deleteSubtask() {
|
||||
|
@ -85,13 +93,13 @@ BlazeComponent.extendComponent({
|
|||
events() {
|
||||
const events = {
|
||||
'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.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
|
||||
},
|
||||
'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 board = subtask.board();
|
||||
FlowRouter.go('card', {
|
||||
|
@ -103,27 +111,33 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
};
|
||||
|
||||
return [{
|
||||
...events,
|
||||
'submit .js-add-subtask': this.addSubtask,
|
||||
'submit .js-edit-subtask-title': this.editSubtask,
|
||||
'click .confirm-subtask-delete': this.deleteSubtask,
|
||||
keydown: this.pressKey,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
...events,
|
||||
'submit .js-add-subtask': this.addSubtask,
|
||||
'submit .js-edit-subtask-title': this.editSubtask,
|
||||
'click .confirm-subtask-delete': this.deleteSubtask,
|
||||
keydown: this.pressKey,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('subtasks');
|
||||
|
||||
Template.subtaskDeleteDialog.onCreated(() => {
|
||||
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
|
||||
};
|
||||
//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
|
||||
$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);
|
||||
}
|
||||
});
|
||||
|
@ -132,12 +146,16 @@ Template.subtaskDeleteDialog.onCreated(() => {
|
|||
Template.subtaskDeleteDialog.onDestroyed(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
$cardDetails.off('scroll'); //Reactivate scrolling
|
||||
$cardDetails.animate( { scrollTop: this.scrollState.position });
|
||||
$cardDetails.animate({ scrollTop: this.scrollState.position });
|
||||
});
|
||||
|
||||
Template.subtaskItemDetail.helpers({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ BlazeComponent.extendComponent({
|
|||
const membersMapping = this.membersToMap.get();
|
||||
if (membersMapping) {
|
||||
const mappingById = {};
|
||||
membersMapping.forEach((member) => {
|
||||
membersMapping.forEach(member => {
|
||||
if (member.wekanId) {
|
||||
mappingById[member.id] = member.wekanId;
|
||||
}
|
||||
|
@ -64,7 +64,8 @@ BlazeComponent.extendComponent({
|
|||
additionalData.membersMapping = mappingById;
|
||||
}
|
||||
this.membersToMap.set([]);
|
||||
Meteor.call('importBoard',
|
||||
Meteor.call(
|
||||
'importBoard',
|
||||
this.importedData.get(),
|
||||
additionalData,
|
||||
this.importSource,
|
||||
|
@ -76,7 +77,7 @@ BlazeComponent.extendComponent({
|
|||
Session.set('fromBoard', null);
|
||||
Utils.goBoardId(res);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -84,12 +85,12 @@ BlazeComponent.extendComponent({
|
|||
const importSource = Session.get('importSource');
|
||||
let membersToMap;
|
||||
switch (importSource) {
|
||||
case 'trello':
|
||||
membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
|
||||
break;
|
||||
case 'wekan':
|
||||
membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
|
||||
break;
|
||||
case 'trello':
|
||||
membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
|
||||
break;
|
||||
case 'wekan':
|
||||
membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
|
||||
break;
|
||||
}
|
||||
return membersToMap;
|
||||
},
|
||||
|
@ -109,22 +110,26 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
submit(evt) {
|
||||
return this.parentComponent().importData(evt);
|
||||
return [
|
||||
{
|
||||
submit(evt) {
|
||||
return this.parentComponent().importData(evt);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('importTextarea');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.autorun(() => {
|
||||
this.parentComponent().membersToMap.get().forEach(({ wekanId }) => {
|
||||
if (wekanId) {
|
||||
this.subscribe('user-miniprofile', wekanId);
|
||||
}
|
||||
});
|
||||
this.parentComponent()
|
||||
.membersToMap.get()
|
||||
.forEach(({ wekanId }) => {
|
||||
if (wekanId) {
|
||||
this.subscribe('user-miniprofile', wekanId);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -149,23 +154,23 @@ BlazeComponent.extendComponent({
|
|||
_setPropertyForMember(property, value, memberId, unset = false) {
|
||||
const listOfMembers = this.members();
|
||||
let finder = null;
|
||||
if(memberId) {
|
||||
finder = (member) => member.id === memberId;
|
||||
if (memberId) {
|
||||
finder = member => member.id === memberId;
|
||||
} else {
|
||||
finder = (member) => member.selected;
|
||||
finder = member => member.selected;
|
||||
}
|
||||
listOfMembers.forEach((member) => {
|
||||
if(finder(member)) {
|
||||
if(value !== null) {
|
||||
listOfMembers.forEach(member => {
|
||||
if (finder(member)) {
|
||||
if (value !== null) {
|
||||
member[property] = value;
|
||||
} else {
|
||||
delete member[property];
|
||||
}
|
||||
if(!unset) {
|
||||
if (!unset) {
|
||||
// we shortcut if we don't care about unsetting the others
|
||||
return false;
|
||||
}
|
||||
} else if(unset) {
|
||||
} else if (unset) {
|
||||
delete member[property];
|
||||
}
|
||||
return true;
|
||||
|
@ -186,9 +191,9 @@ BlazeComponent.extendComponent({
|
|||
const allMembers = this.members();
|
||||
let finder = null;
|
||||
if (memberId) {
|
||||
finder = (user) => user.id === memberId;
|
||||
finder = user => user.id === memberId;
|
||||
} else {
|
||||
finder = (user) => user.selected;
|
||||
finder = user => user.selected;
|
||||
}
|
||||
return allMembers.find(finder);
|
||||
},
|
||||
|
@ -197,7 +202,7 @@ BlazeComponent.extendComponent({
|
|||
return this._setPropertyForMember('wekanId', wekanId, null);
|
||||
},
|
||||
|
||||
unmapMember(memberId){
|
||||
unmapMember(memberId) {
|
||||
return this._setPropertyForMember('wekanId', null, memberId);
|
||||
},
|
||||
|
||||
|
@ -208,7 +213,7 @@ BlazeComponent.extendComponent({
|
|||
|
||||
onMapMember(evt) {
|
||||
const memberToMap = this.currentData();
|
||||
if(memberToMap.wekan) {
|
||||
if (memberToMap.wekan) {
|
||||
// todo xxx ask for confirmation?
|
||||
this.unmapMember(memberToMap.id);
|
||||
} else {
|
||||
|
@ -218,10 +223,12 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit': this.onSubmit,
|
||||
'click .js-select-member': this.onMapMember,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
submit: this.onSubmit,
|
||||
'click .js-select-member': this.onMapMember,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('importMapMembers');
|
||||
|
||||
|
@ -230,14 +237,16 @@ BlazeComponent.extendComponent({
|
|||
this.find('.js-map-member input').focus();
|
||||
},
|
||||
|
||||
onSelectUser(){
|
||||
onSelectUser() {
|
||||
Popup.getOpenerComponent().mapSelectedMember(this.currentData()._id);
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-import': this.onSelectUser,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-select-import': this.onSelectUser,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('importMapMembersAddPopup');
|
||||
|
|
|
@ -4,7 +4,7 @@ export function getMembersToMap(data) {
|
|||
// imported member
|
||||
const membersToMap = data.members;
|
||||
// auto-map based on username
|
||||
membersToMap.forEach((importedMember) => {
|
||||
membersToMap.forEach(importedMember => {
|
||||
const wekanUser = Users.findOne({ username: importedMember.username });
|
||||
if (wekanUser) {
|
||||
importedMember.wekanId = wekanUser._id;
|
||||
|
|
|
@ -5,10 +5,10 @@ export function getMembersToMap(data) {
|
|||
const membersToMap = data.members;
|
||||
const users = data.users;
|
||||
// auto-map based on username
|
||||
membersToMap.forEach((importedMember) => {
|
||||
membersToMap.forEach(importedMember => {
|
||||
importedMember.id = importedMember.userId;
|
||||
delete importedMember.userId;
|
||||
const user = users.filter((user) => {
|
||||
const user = users.filter(user => {
|
||||
return user._id === importedMember.id;
|
||||
})[0];
|
||||
if (user.profile && user.profile.fullname) {
|
||||
|
|
|
@ -21,14 +21,18 @@ BlazeComponent.extendComponent({
|
|||
const boardComponent = this.parentComponent().parentComponent();
|
||||
|
||||
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 $cards = this.$('.js-minicards');
|
||||
|
||||
if(window.matchMedia('(max-width: 1199px)').matches) {
|
||||
$( '.js-minicards' ).sortable({
|
||||
if (window.matchMedia('(max-width: 1199px)').matches) {
|
||||
$('.js-minicards').sortable({
|
||||
handle: '.handle',
|
||||
});
|
||||
}
|
||||
|
@ -42,10 +46,16 @@ BlazeComponent.extendComponent({
|
|||
if (MultiSelection.isActive()) {
|
||||
const andNOthers = $cards.find('.js-minicard.is-checked').length - 1;
|
||||
if (andNOthers > 0) {
|
||||
helper.append($(Blaze.toHTML(HTML.DIV(
|
||||
{ 'class': 'and-n-other' },
|
||||
TAPi18n.__('and-n-other-card', { count: andNOthers })
|
||||
))));
|
||||
helper.append(
|
||||
$(
|
||||
Blaze.toHTML(
|
||||
HTML.DIV(
|
||||
{ class: 'and-n-other' },
|
||||
TAPi18n.__('and-n-other-card', { count: andNOthers }),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return helper;
|
||||
|
@ -70,9 +80,16 @@ BlazeComponent.extendComponent({
|
|||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let swimlaneId = '';
|
||||
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;
|
||||
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;
|
||||
|
||||
// Normally the jquery-ui sortable library moves the dragged DOM element
|
||||
|
@ -86,7 +103,12 @@ BlazeComponent.extendComponent({
|
|||
|
||||
if (MultiSelection.isActive()) {
|
||||
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 {
|
||||
const cardDomElement = ui.item.get(0);
|
||||
|
|
|
@ -16,7 +16,7 @@ BlazeComponent.extendComponent({
|
|||
options.position = options.position || 'top';
|
||||
|
||||
const forms = this.childComponents('inlinedForm');
|
||||
let form = forms.find((component) => {
|
||||
let form = forms.find(component => {
|
||||
return component.data().position === options.position;
|
||||
});
|
||||
if (!form && forms.length > 0) {
|
||||
|
@ -52,11 +52,12 @@ BlazeComponent.extendComponent({
|
|||
let cardType = 'cardType-card';
|
||||
if (title) {
|
||||
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);
|
||||
// If this is the card templates swimlane, insert a card template
|
||||
if (swimlane.isCardTemplatesSwimlane())
|
||||
cardType = 'template-card';
|
||||
if (swimlane.isCardTemplatesSwimlane()) cardType = 'template-card';
|
||||
// If this is the board templates swimlane, insert a board template and a linked card
|
||||
else if (swimlane.isBoardTemplatesSwimlane()) {
|
||||
linkedId = Boards.insert({
|
||||
|
@ -71,8 +72,14 @@ BlazeComponent.extendComponent({
|
|||
cardType = 'cardType-linkedBoard';
|
||||
}
|
||||
} else if (boardView === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id;
|
||||
else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal') || !boardView)
|
||||
swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
else if (
|
||||
boardView === 'board-view-lists' ||
|
||||
boardView === 'board-view-cal' ||
|
||||
!boardView
|
||||
)
|
||||
swimlaneId = board.getDefaultSwimline()._id;
|
||||
|
||||
const _id = Cards.insert({
|
||||
|
@ -91,7 +98,9 @@ BlazeComponent.extendComponent({
|
|||
// 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
|
||||
// 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) {
|
||||
this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
|
||||
}
|
||||
|
@ -149,8 +158,12 @@ BlazeComponent.extendComponent({
|
|||
|
||||
idOrNull(swimlaneId) {
|
||||
const currentUser = Meteor.user();
|
||||
if ((currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
|
||||
this.data().board().isTemplatesBoard())
|
||||
if (
|
||||
(currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
|
||||
this.data()
|
||||
.board()
|
||||
.isTemplatesBoard()
|
||||
)
|
||||
return swimlaneId;
|
||||
return undefined;
|
||||
},
|
||||
|
@ -161,8 +174,7 @@ BlazeComponent.extendComponent({
|
|||
listId: this.currentData()._id,
|
||||
archived: false,
|
||||
};
|
||||
if (swimlaneId)
|
||||
selector.swimlaneId = swimlaneId;
|
||||
if (swimlaneId) selector.swimlaneId = swimlaneId;
|
||||
return Cards.find(Filter.mongoSelector(selector), {
|
||||
sort: ['sort'],
|
||||
limit,
|
||||
|
@ -175,21 +187,32 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
canSeeAddCard() {
|
||||
return !this.reachedWipLimit() && Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
!this.reachedWipLimit() &&
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
|
||||
reachedWipLimit() {
|
||||
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() {
|
||||
return [{
|
||||
'click .js-minicard': this.clickOnMiniCard,
|
||||
'click .js-toggle-multi-selection': this.toggleMultiSelection,
|
||||
'click .open-minicard-composer': this.scrollToBottom,
|
||||
submit: this.addCard,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-minicard': this.clickOnMiniCard,
|
||||
'click .js-toggle-multi-selection': this.toggleMultiSelection,
|
||||
'click .open-minicard-composer': this.scrollToBottom,
|
||||
submit: this.addCard,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('listBody');
|
||||
|
||||
|
@ -212,10 +235,15 @@ BlazeComponent.extendComponent({
|
|||
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
arr = [];
|
||||
_.forEach(Boards.findOne(currentBoardId).customFields().fetch(), function(field){
|
||||
if(field.automaticallyOnCard)
|
||||
arr.push({_id: field._id, value: null});
|
||||
});
|
||||
_.forEach(
|
||||
Boards.findOne(currentBoardId)
|
||||
.customFields()
|
||||
.fetch(),
|
||||
function(field) {
|
||||
if (field.automaticallyOnCard)
|
||||
arr.push({ _id: field._id, value: null });
|
||||
},
|
||||
);
|
||||
this.customFields.set(arr);
|
||||
},
|
||||
|
||||
|
@ -227,7 +255,7 @@ BlazeComponent.extendComponent({
|
|||
|
||||
getLabels() {
|
||||
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;
|
||||
});
|
||||
},
|
||||
|
@ -257,18 +285,20 @@ BlazeComponent.extendComponent({
|
|||
}
|
||||
|
||||
BlazeComponent.getComponentForElement(nextList).openForm({
|
||||
position:this.data().position,
|
||||
position: this.data().position,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
keydown: this.pressKey,
|
||||
'click .js-link': Popup.open('linkCard'),
|
||||
'click .js-search': Popup.open('searchElement'),
|
||||
'click .js-card-template': Popup.open('searchElement'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
keydown: this.pressKey,
|
||||
'click .js-link': Popup.open('linkCard'),
|
||||
'click .js-search': Popup.open('searchElement'),
|
||||
'click .js-card-template': Popup.open('searchElement'),
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
|
@ -277,66 +307,75 @@ BlazeComponent.extendComponent({
|
|||
|
||||
autosize($textarea);
|
||||
|
||||
$textarea.escapeableTextComplete([
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback($.map(currentBoard.activeMembers(), (member) => {
|
||||
const user = Users.findOne(member.userId);
|
||||
return user.username.indexOf(term) === 0 ? user : null;
|
||||
}));
|
||||
$textarea.escapeableTextComplete(
|
||||
[
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(
|
||||
$.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*)$/,
|
||||
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;
|
||||
}));
|
||||
// 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;
|
||||
},
|
||||
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');
|
||||
|
||||
|
@ -354,24 +393,29 @@ BlazeComponent.extendComponent({
|
|||
const list = $(Popup._getTopStack().openerElement).closest('.js-list');
|
||||
this.listId = Blaze.getData(list[0])._id;
|
||||
// Swimlane where to insert card
|
||||
const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane');
|
||||
const swimlane = $(Popup._getTopStack().openerElement).closest(
|
||||
'.js-swimlane',
|
||||
);
|
||||
this.swimlaneId = '';
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else if (boardView === 'board-view-lists' || !boardView)
|
||||
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
|
||||
this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
type: 'board',
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Session.get('currentBoard') },
|
||||
type: 'board',
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
|
@ -379,7 +423,7 @@ BlazeComponent.extendComponent({
|
|||
if (!this.selectedBoardId.get()) {
|
||||
return [];
|
||||
}
|
||||
const swimlanes = Swimlanes.find({boardId: this.selectedBoardId.get()});
|
||||
const swimlanes = Swimlanes.find({ boardId: this.selectedBoardId.get() });
|
||||
if (swimlanes.count())
|
||||
this.selectedSwimlaneId.set(swimlanes.fetch()[0]._id);
|
||||
return swimlanes;
|
||||
|
@ -389,9 +433,8 @@ BlazeComponent.extendComponent({
|
|||
if (!this.selectedBoardId.get()) {
|
||||
return [];
|
||||
}
|
||||
const lists = Lists.find({boardId: this.selectedBoardId.get()});
|
||||
if (lists.count())
|
||||
this.selectedListId.set(lists.fetch()[0]._id);
|
||||
const lists = Lists.find({ boardId: this.selectedBoardId.get() });
|
||||
if (lists.count()) this.selectedListId.set(lists.fetch()[0]._id);
|
||||
return lists;
|
||||
},
|
||||
|
||||
|
@ -399,73 +442,84 @@ BlazeComponent.extendComponent({
|
|||
if (!this.board) {
|
||||
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({
|
||||
boardId: this.selectedBoardId.get(),
|
||||
swimlaneId: this.selectedSwimlaneId.get(),
|
||||
listId: this.selectedListId.get(),
|
||||
archived: false,
|
||||
linkedId: {$nin: ownCardsIds},
|
||||
_id: {$nin: ownCardsIds},
|
||||
type: {$nin: ['template-card']},
|
||||
linkedId: { $nin: ownCardsIds },
|
||||
_id: { $nin: ownCardsIds },
|
||||
type: { $nin: ['template-card'] },
|
||||
});
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-select-boards'(evt) {
|
||||
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-lists'(evt) {
|
||||
this.selectedListId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'click .js-done' (evt) {
|
||||
// LINK CARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const linkedId = $('.js-select-cards option:selected').val();
|
||||
if (!linkedId) {
|
||||
return [
|
||||
{
|
||||
'change .js-select-boards'(evt) {
|
||||
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-lists'(evt) {
|
||||
this.selectedListId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'click .js-done'(evt) {
|
||||
// LINK CARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
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();
|
||||
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();
|
||||
},
|
||||
'click .js-link-board' (evt) {
|
||||
//LINK BOARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const impBoardId = $('.js-select-boards option:selected').val();
|
||||
if (!impBoardId || Cards.findOne({linkedId: impBoardId, archived: false})) {
|
||||
},
|
||||
'click .js-link-board'(evt) {
|
||||
//LINK BOARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const impBoardId = $('.js-select-boards option:selected').val();
|
||||
if (
|
||||
!impBoardId ||
|
||||
Cards.findOne({ linkedId: impBoardId, archived: false })
|
||||
) {
|
||||
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();
|
||||
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');
|
||||
|
||||
|
@ -475,11 +529,20 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
onCreated() {
|
||||
this.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-card-template');
|
||||
this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('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.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
|
||||
'js-card-template',
|
||||
);
|
||||
this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
|
||||
'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.isSwimlaneTemplateSearch ||
|
||||
this.isBoardTemplateSearch;
|
||||
|
@ -491,7 +554,12 @@ BlazeComponent.extendComponent({
|
|||
board = Boards.findOne({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$nin: [Session.get('currentBoard'), (Meteor.user().profile || {}).templatesBoardId]},
|
||||
_id: {
|
||||
$nin: [
|
||||
Session.get('currentBoard'),
|
||||
(Meteor.user().profile || {}).templatesBoardId,
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
if (!board) {
|
||||
|
@ -509,11 +577,12 @@ BlazeComponent.extendComponent({
|
|||
subManager.subscribe('board', this.boardId, false);
|
||||
this.swimlaneId = '';
|
||||
// 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')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else
|
||||
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
|
||||
else this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
|
||||
// List where to insert card
|
||||
const list = $(Popup._getTopStack().openerElement).closest('.js-list');
|
||||
this.listId = Blaze.getData(list[0])._id;
|
||||
|
@ -522,14 +591,17 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
type: 'board',
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Session.get('currentBoard') },
|
||||
type: 'board',
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
|
@ -546,7 +618,7 @@ BlazeComponent.extendComponent({
|
|||
return board.searchSwimlanes(this.term.get());
|
||||
} else if (this.isBoardTemplateSearch) {
|
||||
const boards = board.searchBoards(this.term.get());
|
||||
boards.forEach((board) => {
|
||||
boards.forEach(board => {
|
||||
subManager.subscribe('board', board.linkedId, false);
|
||||
});
|
||||
return boards;
|
||||
|
@ -556,60 +628,69 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-select-boards'(evt) {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val(), false);
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'submit .js-search-term-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.term.set(evt.target.searchTerm.value);
|
||||
},
|
||||
'click .js-minicard'(evt) {
|
||||
// 0. Common
|
||||
const title = $('.js-element-title').val().trim();
|
||||
if (!title)
|
||||
return;
|
||||
const element = Blaze.getData(evt.currentTarget);
|
||||
element.title = title;
|
||||
let _id = '';
|
||||
if (!this.isTemplateSearch || this.isCardTemplateSearch) {
|
||||
// Card insertion
|
||||
// 1. Common
|
||||
element.sort = Lists.findOne(this.listId).cards().count();
|
||||
// 1.A From template
|
||||
if (this.isTemplateSearch) {
|
||||
element.type = 'cardType-card';
|
||||
element.linkedId = '';
|
||||
_id = element.copy(this.boardId, this.swimlaneId, this.listId);
|
||||
// 1.B Linked card
|
||||
} else {
|
||||
delete element._id;
|
||||
element.type = 'cardType-linkedCard';
|
||||
element.linkedId = element.linkedId || element._id;
|
||||
_id = Cards.insert(element);
|
||||
return [
|
||||
{
|
||||
'change .js-select-boards'(evt) {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val(), false);
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'submit .js-search-term-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.term.set(evt.target.searchTerm.value);
|
||||
},
|
||||
'click .js-minicard'(evt) {
|
||||
// 0. Common
|
||||
const title = $('.js-element-title')
|
||||
.val()
|
||||
.trim();
|
||||
if (!title) return;
|
||||
const element = Blaze.getData(evt.currentTarget);
|
||||
element.title = title;
|
||||
let _id = '';
|
||||
if (!this.isTemplateSearch || this.isCardTemplateSearch) {
|
||||
// Card insertion
|
||||
// 1. Common
|
||||
element.sort = Lists.findOne(this.listId)
|
||||
.cards()
|
||||
.count();
|
||||
// 1.A From template
|
||||
if (this.isTemplateSearch) {
|
||||
element.type = 'cardType-card';
|
||||
element.linkedId = '';
|
||||
_id = element.copy(this.boardId, this.swimlaneId, this.listId);
|
||||
// 1.B Linked card
|
||||
} 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);
|
||||
// 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();
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('searchElementPopup');
|
||||
|
||||
|
@ -622,15 +703,23 @@ BlazeComponent.extendComponent({
|
|||
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
this.swimlaneId = this.parentComponent().parentComponent().parentComponent().data()._id;
|
||||
this.swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.spinner = this.find('.sk-spinner-list');
|
||||
this.container = this.$(this.spinner).parents('.js-perfect-scrollbar')[0];
|
||||
|
||||
$(this.container).on(`scroll.spinner_${this.swimlaneId}_${this.listId}`, () => this.updateList());
|
||||
$(window).on(`resize.spinner_${this.swimlaneId}_${this.listId}`, () => this.updateList());
|
||||
$(this.container).on(
|
||||
`scroll.spinner_${this.swimlaneId}_${this.listId}`,
|
||||
() => this.updateList(),
|
||||
);
|
||||
$(window).on(`resize.spinner_${this.swimlaneId}_${this.listId}`, () =>
|
||||
this.updateList(),
|
||||
);
|
||||
|
||||
this.updateList();
|
||||
},
|
||||
|
@ -660,5 +749,4 @@ BlazeComponent.extendComponent({
|
|||
|
||||
return bottomViewPosition > threshold;
|
||||
},
|
||||
|
||||
}).register('spinnerList');
|
||||
|
|
|
@ -6,12 +6,18 @@ Meteor.startup(() => {
|
|||
BlazeComponent.extendComponent({
|
||||
canSeeAddCard() {
|
||||
const list = Template.currentData();
|
||||
return !list.getWipLimit('enabled') || list.getWipLimit('soft') || !this.reachedWipLimit();
|
||||
return (
|
||||
!list.getWipLimit('enabled') ||
|
||||
list.getWipLimit('soft') ||
|
||||
!this.reachedWipLimit()
|
||||
);
|
||||
},
|
||||
|
||||
editTitle(evt) {
|
||||
evt.preventDefault();
|
||||
const newTitle = this.childComponents('inlinedForm')[0].getValue().trim();
|
||||
editTitle(event) {
|
||||
event.preventDefault();
|
||||
const newTitle = this.childComponents('inlinedForm')[0]
|
||||
.getValue()
|
||||
.trim();
|
||||
const list = this.currentData();
|
||||
if (newTitle) {
|
||||
list.rename(newTitle.trim());
|
||||
|
@ -32,14 +38,19 @@ BlazeComponent.extendComponent({
|
|||
let swimlaneId = '';
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id;
|
||||
swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
|
||||
return list.cards(swimlaneId).count();
|
||||
},
|
||||
|
||||
reachedWipLimit() {
|
||||
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) {
|
||||
|
@ -48,20 +59,24 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-open-list-menu': Popup.open('listAction'),
|
||||
'click .js-add-card' (evt) {
|
||||
const listDom = $(evt.target).parents(`#js-list-${this.currentData()._id}`)[0];
|
||||
const listComponent = BlazeComponent.getComponentForElement(listDom);
|
||||
listComponent.openForm({
|
||||
position: 'top',
|
||||
});
|
||||
return [
|
||||
{
|
||||
'click .js-open-list-menu': Popup.open('listAction'),
|
||||
'click .js-add-card'(event) {
|
||||
const listDom = $(event.target).parents(
|
||||
`#js-list-${this.currentData()._id}`,
|
||||
)[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');
|
||||
|
||||
|
@ -76,22 +91,22 @@ Template.listActionPopup.helpers({
|
|||
});
|
||||
|
||||
Template.listActionPopup.events({
|
||||
'click .js-list-subscribe' () {},
|
||||
'click .js-list-subscribe'() {},
|
||||
'click .js-set-color-list': Popup.open('setListColor'),
|
||||
'click .js-select-cards' () {
|
||||
const cardIds = this.allCards().map((card) => card._id);
|
||||
'click .js-select-cards'() {
|
||||
const cardIds = this.allCards().map(card => card._id);
|
||||
MultiSelection.add(cardIds);
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-toggle-watch-list' () {
|
||||
'click .js-toggle-watch-list'() {
|
||||
const currentList = this;
|
||||
const level = currentList.findWatcher(Meteor.userId()) ? null : 'watching';
|
||||
Meteor.call('watch', 'list', currentList._id, level, (err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
});
|
||||
},
|
||||
'click .js-close-list' (evt) {
|
||||
evt.preventDefault();
|
||||
'click .js-close-list'(event) {
|
||||
event.preventDefault();
|
||||
this.archive();
|
||||
Popup.close();
|
||||
},
|
||||
|
@ -102,10 +117,17 @@ Template.listActionPopup.events({
|
|||
BlazeComponent.extendComponent({
|
||||
applyWipLimit() {
|
||||
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')){
|
||||
Template.instance().$('.wip-limit-error').click();
|
||||
if (limit < list.cards().count() && !list.getWipLimit('soft')) {
|
||||
Template.instance()
|
||||
.$('.wip-limit-error')
|
||||
.click();
|
||||
} else {
|
||||
Meteor.call('applyWipLimit', list._id, limit);
|
||||
Popup.back();
|
||||
|
@ -115,7 +137,10 @@ BlazeComponent.extendComponent({
|
|||
enableSoftLimit() {
|
||||
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());
|
||||
}
|
||||
Meteor.call('enableSoftLimit', Template.currentData()._id);
|
||||
|
@ -124,7 +149,10 @@ BlazeComponent.extendComponent({
|
|||
enableWipLimit() {
|
||||
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
|
||||
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());
|
||||
}
|
||||
Meteor.call('enableWipLimit', list._id);
|
||||
|
@ -138,24 +166,26 @@ BlazeComponent.extendComponent({
|
|||
return Template.currentData().getWipLimit('enabled');
|
||||
},
|
||||
|
||||
wipLimitValue(){
|
||||
wipLimitValue() {
|
||||
return Template.currentData().getWipLimit('value');
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-enable-wip-limit': this.enableWipLimit,
|
||||
'click .wip-limit-apply': this.applyWipLimit,
|
||||
'click .wip-limit-error': Popup.open('wipLimitError'),
|
||||
'click .materialCheckBox': this.enableSoftLimit,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-enable-wip-limit': this.enableWipLimit,
|
||||
'click .wip-limit-apply': this.applyWipLimit,
|
||||
'click .wip-limit-error': Popup.open('wipLimitError'),
|
||||
'click .materialCheckBox': this.enableSoftLimit,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('setWipLimitPopup');
|
||||
|
||||
Template.listMorePopup.events({
|
||||
'click .js-delete': Popup.afterConfirm('listDelete', function () {
|
||||
'click .js-delete': Popup.afterConfirm('listDelete', function() {
|
||||
Popup.close();
|
||||
this.allCards().map((card) => Cards.remove(card._id));
|
||||
this.allCards().map(card => Cards.remove(card._id));
|
||||
Lists.remove(this._id);
|
||||
Utils.goBoardId(this.boardId);
|
||||
}),
|
||||
|
@ -168,7 +198,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
colors() {
|
||||
return listsColors.map((color) => ({ color, name: '' }));
|
||||
return listsColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
|
@ -176,18 +206,20 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
|
|
@ -9,10 +9,15 @@ Template.editor.onRendered(() => {
|
|||
match: /\B@([\w.]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(currentBoard.activeMembers().map((member) => {
|
||||
const username = Users.findOne(member.userId).username;
|
||||
return username.includes(term) ? username : null;
|
||||
}).filter(Boolean));
|
||||
callback(
|
||||
currentBoard
|
||||
.activeMembers()
|
||||
.map(member => {
|
||||
const username = Users.findOne(member.userId).username;
|
||||
return username.includes(term) ? username : null;
|
||||
})
|
||||
.filter(Boolean),
|
||||
);
|
||||
},
|
||||
template(value) {
|
||||
return value;
|
||||
|
@ -33,69 +38,73 @@ import sanitizeXss from 'xss';
|
|||
// compiled version to most users -- who don't need to edit.
|
||||
// In the meantime, all the transformation are done on the client using the
|
||||
// Blaze API.
|
||||
const at = HTML.CharRef({html: '@', str: '@'});
|
||||
Blaze.Template.registerHelper('mentions', new Template('mentions', function() {
|
||||
const view = this;
|
||||
let content = Blaze.toHTML(view.templateContentBlock);
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (!currentBoard)
|
||||
const at = HTML.CharRef({ html: '@', str: '@' });
|
||||
Blaze.Template.registerHelper(
|
||||
'mentions',
|
||||
new Template('mentions', function() {
|
||||
const view = this;
|
||||
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));
|
||||
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({
|
||||
// Viewer sometimes have click-able wrapper around them (for instance to edit
|
||||
// the corresponding text). Clicking a link shouldn't fire these actions, stop
|
||||
// we stop these event at the viewer component level.
|
||||
'click a'(evt, tpl) {
|
||||
evt.stopPropagation();
|
||||
'click a'(event, templateInstance) {
|
||||
event.stopPropagation();
|
||||
|
||||
// XXX We hijack the build-in browser action because we currently don't have
|
||||
// `_blank` attributes in viewer links, and the transformer function is
|
||||
// handled by a third party package that we can't configure easily. Fix that
|
||||
// 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) {
|
||||
Popup.open('member').call({ userId }, evt, tpl);
|
||||
}
|
||||
else {
|
||||
const href = evt.currentTarget.href;
|
||||
Popup.open('member').call({ userId }, event, templateInstance);
|
||||
} else {
|
||||
const href = event.currentTarget.href;
|
||||
if (href) {
|
||||
window.open(href, '_blank');
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@ Template.header.helpers({
|
|||
},
|
||||
|
||||
hasAnnouncement() {
|
||||
const announcements = Announcements.findOne();
|
||||
const announcements = Announcements.findOne();
|
||||
return announcements && announcements.enabled;
|
||||
},
|
||||
|
||||
announcement() {
|
||||
$('.announcement').show();
|
||||
const announcements = Announcements.findOne();
|
||||
const announcements = Announcements.findOne();
|
||||
return announcements && announcements.body;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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
|
||||
// but we keep this conversion function here, to be aware that that they are different system.
|
||||
return i18nTag;
|
||||
|
@ -21,20 +21,23 @@ const validator = {
|
|||
};
|
||||
|
||||
Template.userFormsLayout.onCreated(function() {
|
||||
const instance = this;
|
||||
instance.currentSetting = new ReactiveVar();
|
||||
instance.isLoading = new ReactiveVar(false);
|
||||
const templateInstance = this;
|
||||
templateInstance.currentSetting = new ReactiveVar();
|
||||
templateInstance.isLoading = new ReactiveVar(false);
|
||||
|
||||
Meteor.subscribe('setting', {
|
||||
onReady() {
|
||||
instance.currentSetting.set(Settings.findOne());
|
||||
templateInstance.currentSetting.set(Settings.findOne());
|
||||
return this.stop();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
if (i18nTag) {
|
||||
|
@ -89,18 +92,17 @@ Template.userFormsLayout.helpers({
|
|||
});
|
||||
|
||||
Template.userFormsLayout.events({
|
||||
'change .js-userform-set-language'(evt) {
|
||||
const i18nTag = $(evt.currentTarget).val();
|
||||
'change .js-userform-set-language'(event) {
|
||||
const i18nTag = $(event.currentTarget).val();
|
||||
T9n.setLanguage(i18nTagToT9n(i18nTag));
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click #at-btn'(event, instance) {
|
||||
'click #at-btn'(event, templateInstance) {
|
||||
if (FlowRouter.getRouteName() === 'atSignIn') {
|
||||
instance.isLoading.set(true);
|
||||
authentication(event, instance)
|
||||
.then(() => {
|
||||
instance.isLoading.set(false);
|
||||
});
|
||||
templateInstance.isLoading.set(true);
|
||||
authentication(event, templateInstance).then(() => {
|
||||
templateInstance.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 password = $('#at-field-password').val();
|
||||
|
||||
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;
|
||||
|
||||
|
@ -126,26 +131,29 @@ async function authentication(event, instance) {
|
|||
event.stopImmediatePropagation();
|
||||
|
||||
switch (result) {
|
||||
case 'ldap':
|
||||
return new Promise((resolve) => {
|
||||
Meteor.loginWithLDAP(match, password, function() {
|
||||
resolve(FlowRouter.go('/'));
|
||||
case 'ldap':
|
||||
return new Promise(resolve => {
|
||||
Meteor.loginWithLDAP(match, password, function() {
|
||||
resolve(FlowRouter.go('/'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
case 'cas':
|
||||
return new Promise((resolve) => {
|
||||
Meteor.loginWithCas(match, password, function() {
|
||||
resolve(FlowRouter.go('/'));
|
||||
case 'cas':
|
||||
return new Promise(resolve => {
|
||||
Meteor.loginWithCas(match, password, function() {
|
||||
resolve(FlowRouter.go('/'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthenticationMethod}, match) {
|
||||
function getAuthenticationMethod(
|
||||
{ displayAuthenticationMethod, defaultAuthenticationMethod },
|
||||
match,
|
||||
) {
|
||||
if (displayAuthenticationMethod) {
|
||||
return $('.select-authentication').val();
|
||||
}
|
||||
|
@ -153,7 +161,7 @@ function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthentica
|
|||
}
|
||||
|
||||
function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
try {
|
||||
Meteor.subscribe('user-authenticationMethod', match, {
|
||||
onReady() {
|
||||
|
@ -166,7 +174,7 @@ function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
|
|||
resolve(authenticationMethod);
|
||||
},
|
||||
});
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
resolve(defaultAuthenticationMethod);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -18,15 +18,17 @@ Mixins.InfiniteScrolling = BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
scroll(evt) {
|
||||
const domElement = evt.currentTarget;
|
||||
let altitude = domElement.scrollTop + domElement.offsetHeight;
|
||||
altitude += peakAnticipation;
|
||||
if (altitude >= this.callFirstWith(null, 'getNextPeak')) {
|
||||
this.mixinParent().callFirstWith(null, 'reachNextPeak');
|
||||
}
|
||||
return [
|
||||
{
|
||||
scroll(evt) {
|
||||
const domElement = evt.currentTarget;
|
||||
let altitude = domElement.scrollTop + domElement.offsetHeight;
|
||||
altitude += peakAnticipation;
|
||||
if (altitude >= this.callFirstWith(null, 'getNextPeak')) {
|
||||
this.mixinParent().callFirstWith(null, 'reachNextPeak');
|
||||
}
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,168 +1,165 @@
|
|||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
|
||||
},
|
||||
onCreated() {},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-create-card-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const cardName = this.find('#card-name').value;
|
||||
const listName = this.find('#list-name').value;
|
||||
const swimlaneName = this.find('#swimlane-name2').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'createCard',
|
||||
swimlaneName,
|
||||
cardName,
|
||||
listName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
'click .js-create-card-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const cardName = this.find('#card-name').value;
|
||||
const listName = this.find('#list-name').value;
|
||||
const swimlaneName = this.find('#swimlane-name2').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'createCard',
|
||||
swimlaneName,
|
||||
cardName,
|
||||
listName,
|
||||
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,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'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');
|
||||
/* eslint-no-undef */
|
||||
|
|
|
@ -14,7 +14,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
cardColorButtonText() {
|
||||
return `color-${ this.cardColorButtonValue.get() }`;
|
||||
return `color-${this.cardColorButtonValue.get()}`;
|
||||
},
|
||||
|
||||
labels() {
|
||||
|
@ -28,109 +28,24 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-set-date-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
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({
|
||||
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') {
|
||||
return [
|
||||
{
|
||||
'click .js-set-date-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
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({
|
||||
actionType: 'addLabel',
|
||||
labelId,
|
||||
actionType: actionSelected,
|
||||
dateField: dateFieldSelected,
|
||||
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,
|
||||
|
@ -138,12 +53,118 @@ BlazeComponent.extendComponent({
|
|||
boardId,
|
||||
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 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({
|
||||
actionType: 'removeMember',
|
||||
username,
|
||||
username: '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
|
@ -153,58 +174,38 @@ BlazeComponent.extendComponent({
|
|||
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,
|
||||
});
|
||||
},
|
||||
},
|
||||
'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');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
@ -215,7 +216,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
colors() {
|
||||
return cardColors.map((color) => ({ color, name: '' }));
|
||||
return cardColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
|
@ -223,14 +224,16 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
|
|
@ -3,149 +3,148 @@ BlazeComponent.extendComponent({
|
|||
this.subscribe('allRules');
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-checklist-items-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const checklistName = this.find('#checklist-name-3').value;
|
||||
const checklistItems = this.find('#checklist-items').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addChecklistWithItems',
|
||||
checklistName,
|
||||
checklistItems,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
'click .js-add-checklist-items-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const checklistName = this.find('#checklist-name-3').value;
|
||||
const checklistItems = this.find('#checklist-items').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addChecklistWithItems',
|
||||
checklistName,
|
||||
checklistItems,
|
||||
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,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'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');
|
||||
|
|
|
@ -1,35 +1,34 @@
|
|||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
|
||||
},
|
||||
onCreated() {},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-mail-action' (event) {
|
||||
const emailTo = this.find('#email-to').value;
|
||||
const emailSubject = this.find('#email-subject').value;
|
||||
const emailMsg = this.find('#email-msg').value;
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'sendEmail',
|
||||
emailTo,
|
||||
emailSubject,
|
||||
emailMsg,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
return [
|
||||
{
|
||||
'click .js-mail-action'(event) {
|
||||
const emailTo = this.find('#email-to').value;
|
||||
const emailSubject = this.find('#email-subject').value;
|
||||
const emailMsg = this.find('#email-msg').value;
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'sendEmail',
|
||||
emailTo,
|
||||
emailSubject,
|
||||
emailMsg,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
}).register('mailActions');
|
||||
|
|
|
@ -3,7 +3,6 @@ BlazeComponent.extendComponent({
|
|||
this.subscribe('allRules');
|
||||
this.subscribe('allTriggers');
|
||||
this.subscribe('allActions');
|
||||
|
||||
},
|
||||
|
||||
trigger() {
|
||||
|
@ -34,5 +33,4 @@ BlazeComponent.extendComponent({
|
|||
events() {
|
||||
return [{}];
|
||||
},
|
||||
|
||||
}).register('ruleDetails');
|
||||
|
|
|
@ -40,19 +40,21 @@ BlazeComponent.extendComponent({
|
|||
// console.log(this.data());
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-set-board-actions'(){
|
||||
this.setBoardActions();
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
|
|
@ -9,12 +9,12 @@ BlazeComponent.extendComponent({
|
|||
setTrigger() {
|
||||
this.rulesCurrentTab.set('trigger');
|
||||
},
|
||||
sanitizeObject(obj){
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if(obj[key] === '' || obj[key] === undefined){
|
||||
sanitizeObject(obj) {
|
||||
Object.keys(obj).forEach(key => {
|
||||
if (obj[key] === '' || obj[key] === undefined) {
|
||||
obj[key] = '*';
|
||||
}}
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
setRulesList() {
|
||||
this.rulesCurrentTab.set('rulesList');
|
||||
|
@ -29,69 +29,73 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-delete-rule' () {
|
||||
const rule = this.currentData();
|
||||
Rules.remove(rule._id);
|
||||
Actions.remove(rule.actionId);
|
||||
Triggers.remove(rule.triggerId);
|
||||
|
||||
},
|
||||
'click .js-goto-trigger' (event) {
|
||||
event.preventDefault();
|
||||
const ruleTitle = this.find('#ruleTitle').value;
|
||||
if(ruleTitle !== undefined && ruleTitle !== ''){
|
||||
this.find('#ruleTitle').value = '';
|
||||
this.ruleName.set(ruleTitle);
|
||||
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);
|
||||
return [
|
||||
{
|
||||
'click .js-delete-rule'() {
|
||||
const rule = this.currentData();
|
||||
Rules.remove(rule._id);
|
||||
Actions.remove(rule.actionId);
|
||||
Triggers.remove(rule.triggerId);
|
||||
},
|
||||
'click .js-goto-trigger'(event) {
|
||||
event.preventDefault();
|
||||
const ruleTitle = this.find('#ruleTitle').value;
|
||||
if (ruleTitle !== undefined && ruleTitle !== '') {
|
||||
this.find('#ruleTitle').value = '';
|
||||
this.ruleName.set(ruleTitle);
|
||||
this.setTrigger();
|
||||
}
|
||||
}
|
||||
// Sanitize trigger
|
||||
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();
|
||||
},
|
||||
'click .js-goback' (event) {
|
||||
event.preventDefault();
|
||||
if(this.rulesCurrentTab.get() === 'trigger' || this.rulesCurrentTab.get() === 'ruleDetails' ){
|
||||
},
|
||||
'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
|
||||
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();
|
||||
}
|
||||
if(this.rulesCurrentTab.get() === 'action'){
|
||||
this.setTrigger();
|
||||
}
|
||||
},
|
||||
'click .js-goback'(event) {
|
||||
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');
|
||||
|
||||
|
||||
|
|
|
@ -38,16 +38,18 @@ BlazeComponent.extendComponent({
|
|||
// console.log(this.data());
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-set-board-triggers' () {
|
||||
this.setBoardTriggers();
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
|
|
@ -4,116 +4,118 @@ BlazeComponent.extendComponent({
|
|||
this.currentPopupTriggerId = 'def';
|
||||
this.cardTitleFilters = {};
|
||||
},
|
||||
setNameFilter(name){
|
||||
this.cardTitleFilters[this.currentPopupTriggerId] = name;
|
||||
setNameFilter(name) {
|
||||
this.cardTitleFilters[this.currentPopupTriggerId] = name;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-open-card-title-popup'(event){
|
||||
const funct = Popup.open('boardCardTitle');
|
||||
const divId = $(event.currentTarget.parentNode.parentNode).attr('id');
|
||||
//console.log('current popup');
|
||||
//console.log(this.currentPopupTriggerId);
|
||||
this.currentPopupTriggerId = divId;
|
||||
funct.call(this, event);
|
||||
},
|
||||
'click .js-add-create-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const listName = this.find('#create-list-name').value;
|
||||
const swimlaneName = this.find('#create-swimlane-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const divId = $(event.currentTarget.parentNode).attr('id');
|
||||
const cardTitle = this.cardTitleFilters[divId];
|
||||
// move to generic funciont
|
||||
datas.triggerVar.set({
|
||||
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') {
|
||||
return [
|
||||
{
|
||||
'click .js-open-card-title-popup'(event) {
|
||||
const funct = Popup.open('boardCardTitle');
|
||||
const divId = $(event.currentTarget.parentNode.parentNode).attr('id');
|
||||
//console.log('current popup');
|
||||
//console.log(this.currentPopupTriggerId);
|
||||
this.currentPopupTriggerId = divId;
|
||||
funct.call(this, event);
|
||||
},
|
||||
'click .js-add-create-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const listName = this.find('#create-list-name').value;
|
||||
const swimlaneName = this.find('#create-swimlane-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const divId = $(event.currentTarget.parentNode).attr('id');
|
||||
const cardTitle = this.cardTitleFilters[divId];
|
||||
// move to generic funciont
|
||||
datas.triggerVar.set({
|
||||
activityType: 'moveCard',
|
||||
activityType: 'createCard',
|
||||
boardId,
|
||||
cardTitle,
|
||||
swimlaneName,
|
||||
listName,
|
||||
cardTitle,
|
||||
swimlaneName,
|
||||
'oldListName': '*',
|
||||
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({
|
||||
activityType: 'moveCard',
|
||||
boardId,
|
||||
cardTitle,
|
||||
swimlaneName,
|
||||
'listName': '*',
|
||||
'oldListName': listName,
|
||||
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,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'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');
|
||||
|
||||
|
||||
Template.boardCardTitlePopup.events({
|
||||
submit(evt, tpl) {
|
||||
const title = tpl.$('.js-card-filter-name').val().trim();
|
||||
submit(event, templateInstance) {
|
||||
const title = templateInstance
|
||||
.$('.js-card-filter-name')
|
||||
.val()
|
||||
.trim();
|
||||
Popup.getOpenerComponent().setNameFilter(title);
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ BlazeComponent.extendComponent({
|
|||
for (let i = 0; i < labels.length; i++) {
|
||||
if (labels[i].name === '' || labels[i].name === undefined) {
|
||||
labels[i].name = labels[i].color;
|
||||
labels[i].translatedname = `${TAPi18n.__(`color-${ labels[i].color}`)}`;
|
||||
labels[i].translatedname = `${TAPi18n.__(`color-${labels[i].color}`)}`;
|
||||
} else {
|
||||
labels[i].translatedname = labels[i].name;
|
||||
}
|
||||
|
@ -15,117 +15,119 @@ BlazeComponent.extendComponent({
|
|||
return labels;
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-gen-label-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#label-action').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,
|
||||
});
|
||||
}
|
||||
return [
|
||||
{
|
||||
'click .js-add-gen-label-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#label-action').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-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');
|
||||
|
|
|
@ -3,144 +3,145 @@ BlazeComponent.extendComponent({
|
|||
this.subscribe('allRules');
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-gen-check-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-check-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-check-action').value;
|
||||
const checklistId = this.find('#check-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-comp-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
return [
|
||||
{
|
||||
'click .js-add-gen-check-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
checklistName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
checklistName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-check-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-check-action').value;
|
||||
const checklistId = this.find('#check-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
checklistName: checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
checklistName: checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-comp-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-comp-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'completed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'completeChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncompleted') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncompleteChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-comp-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'completed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'completeChecklist',
|
||||
boardId,
|
||||
checklistName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncompleted') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncompleteChecklist',
|
||||
boardId,
|
||||
checklistName: '*',
|
||||
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');
|
||||
|
|
|
@ -6,9 +6,11 @@ Template.connectionMethod.onCreated(function() {
|
|||
// TODO : add a management of different languages
|
||||
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
|
||||
this.authenticationMethods.set([
|
||||
{value: 'password'},
|
||||
{ value: 'password' },
|
||||
// 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(() => {
|
||||
// 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({
|
||||
|
|
|
@ -29,19 +29,23 @@ BlazeComponent.extendComponent({
|
|||
});
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click #searchButton'() {
|
||||
this.filterPeople();
|
||||
},
|
||||
'keydown #searchInput'(event) {
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
return [
|
||||
{
|
||||
'click #searchButton'() {
|
||||
this.filterPeople();
|
||||
}
|
||||
},
|
||||
'keydown #searchInput'(event) {
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
this.filterPeople();
|
||||
}
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
filterPeople() {
|
||||
const value = $('#searchInput').first().val();
|
||||
const value = $('#searchInput')
|
||||
.first()
|
||||
.val();
|
||||
if (value === '') {
|
||||
this.findUsersOptions.set({});
|
||||
} else {
|
||||
|
@ -79,7 +83,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
peopleList() {
|
||||
const users = Users.find(this.findUsersOptions.get(), {
|
||||
fields: {_id: true},
|
||||
fields: { _id: true },
|
||||
});
|
||||
this.number.set(users.count());
|
||||
return users;
|
||||
|
@ -105,9 +109,11 @@ Template.editUserPopup.onCreated(function() {
|
|||
// TODO : add a management of different languages
|
||||
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
|
||||
this.authenticationMethods.set([
|
||||
{value: 'password'},
|
||||
{ value: 'password' },
|
||||
// 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({
|
||||
onCreated() {
|
||||
},
|
||||
onCreated() {},
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click a.edit-user': Popup.open('editUser'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click a.edit-user': Popup.open('editUser'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('peopleRow');
|
||||
|
||||
Template.editUserPopup.events({
|
||||
submit(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const user = Users.findOne(this.userId);
|
||||
const fullname = tpl.find('.js-profile-fullname').value.trim();
|
||||
const username = tpl.find('.js-profile-username').value.trim();
|
||||
const password = tpl.find('.js-profile-password').value;
|
||||
const isAdmin = tpl.find('.js-profile-isadmin').value.trim();
|
||||
const isActive = tpl.find('.js-profile-isactive').value.trim();
|
||||
const email = tpl.find('.js-profile-email').value.trim();
|
||||
const authentication = tpl.find('.js-authenticationMethod').value.trim();
|
||||
const fullname = templateInstance.find('.js-profile-fullname').value.trim();
|
||||
const username = templateInstance.find('.js-profile-username').value.trim();
|
||||
const password = templateInstance.find('.js-profile-password').value;
|
||||
const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
|
||||
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
|
||||
const email = templateInstance.find('.js-profile-email').value.trim();
|
||||
const authentication = templateInstance
|
||||
.find('.js-authenticationMethod')
|
||||
.value.trim();
|
||||
|
||||
const isChangePassword = password.length > 0;
|
||||
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, {
|
||||
$set: {
|
||||
'profile.fullname': fullname,
|
||||
'isAdmin': isAdmin === 'true',
|
||||
'loginDisabled': isActive === 'true',
|
||||
'authenticationMethod': authentication,
|
||||
isAdmin: isAdmin === 'true',
|
||||
loginDisabled: isActive === 'true',
|
||||
authenticationMethod: authentication,
|
||||
},
|
||||
});
|
||||
|
||||
if(isChangePassword){
|
||||
if (isChangePassword) {
|
||||
Meteor.call('setPassword', password, this.userId);
|
||||
}
|
||||
|
||||
if (isChangeUserName && isChangeEmail) {
|
||||
Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), this.userId, function (error) {
|
||||
const usernameMessageElement = tpl.$('.username-taken');
|
||||
const emailMessageElement = tpl.$('.email-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'username-already-taken') {
|
||||
usernameMessageElement.show();
|
||||
emailMessageElement.hide();
|
||||
} else if (errorElement === 'email-already-taken') {
|
||||
Meteor.call(
|
||||
'setUsernameAndEmail',
|
||||
username,
|
||||
email.toLowerCase(),
|
||||
this.userId,
|
||||
function(error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
const emailMessageElement = templateInstance.$('.email-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();
|
||||
emailMessageElement.show();
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
}
|
||||
} else {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
} else if (isChangeUserName) {
|
||||
Meteor.call('setUsername', username, this.userId, function (error) {
|
||||
const usernameMessageElement = tpl.$('.username-taken');
|
||||
Meteor.call('setUsername', username, this.userId, function(error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'username-already-taken') {
|
||||
|
@ -210,8 +226,10 @@ Template.editUserPopup.events({
|
|||
}
|
||||
});
|
||||
} else if (isChangeEmail) {
|
||||
Meteor.call('setEmail', email.toLowerCase(), this.userId, function (error) {
|
||||
const emailMessageElement = tpl.$('.email-taken');
|
||||
Meteor.call('setEmail', email.toLowerCase(), this.userId, function(
|
||||
error,
|
||||
) {
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'email-already-taken') {
|
||||
|
|
|
@ -25,7 +25,9 @@ BlazeComponent.extendComponent({
|
|||
checkField(selector) {
|
||||
const value = $(selector).val();
|
||||
if (!value || value.trim() === '') {
|
||||
$(selector).parents('li.smtp-form').addClass('has-error');
|
||||
$(selector)
|
||||
.parents('li.smtp-form')
|
||||
.addClass('has-error');
|
||||
throw Error('blank field');
|
||||
} else {
|
||||
return value;
|
||||
|
@ -37,18 +39,23 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
'members.isAdmin': true,
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
'members.isAdmin': true,
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
toggleRegistration() {
|
||||
this.setLoading(true);
|
||||
const registrationClosed = this.currentSetting().disableRegistration;
|
||||
Settings.update(Settings.findOne()._id, {$set: {disableRegistration: !registrationClosed}});
|
||||
Settings.update(Settings.findOne()._id, {
|
||||
$set: { disableRegistration: !registrationClosed },
|
||||
});
|
||||
this.setLoading(false);
|
||||
if (registrationClosed) {
|
||||
$('.invite-people').slideUp();
|
||||
|
@ -90,13 +97,19 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
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 = [];
|
||||
$('.js-toggle-board-choose .materialCheckBox.is-checked').each(function () {
|
||||
$('.js-toggle-board-choose .materialCheckBox.is-checked').each(function() {
|
||||
boardsToInvite.push($(this).data('id'));
|
||||
});
|
||||
const validEmails = [];
|
||||
emails.forEach((email) => {
|
||||
emails.forEach(email => {
|
||||
if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
|
||||
validEmails.push(email.trim());
|
||||
}
|
||||
|
@ -119,14 +132,22 @@ BlazeComponent.extendComponent({
|
|||
try {
|
||||
const host = this.checkField('#mail-server-host');
|
||||
const port = this.checkField('#mail-server-port');
|
||||
const username = $('#mail-server-username').val().trim();
|
||||
const password = $('#mail-server-password').val().trim();
|
||||
const username = $('#mail-server-username')
|
||||
.val()
|
||||
.trim();
|
||||
const password = $('#mail-server-password')
|
||||
.val()
|
||||
.trim();
|
||||
const from = this.checkField('#mail-server-from');
|
||||
const tls = $('#mail-server-tls.is-checked').length > 0;
|
||||
Settings.update(Settings.findOne()._id, {
|
||||
$set: {
|
||||
'mailServer.host': host, 'mailServer.port': port, 'mailServer.username': username,
|
||||
'mailServer.password': password, 'mailServer.enableTLS': tls, 'mailServer.from': from,
|
||||
'mailServer.host': host,
|
||||
'mailServer.port': port,
|
||||
'mailServer.username': username,
|
||||
'mailServer.password': password,
|
||||
'mailServer.enableTLS': tls,
|
||||
'mailServer.from': from,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
|
@ -134,19 +155,25 @@ BlazeComponent.extendComponent({
|
|||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
saveLayout() {
|
||||
this.setLoading(true);
|
||||
$('li').removeClass('has-error');
|
||||
|
||||
const productName = $('#product-name').val().trim();
|
||||
const hideLogoChange = ($('input[name=hideLogo]:checked').val() === 'true');
|
||||
const displayAuthenticationMethod = ($('input[name=displayAuthenticationMethod]:checked').val() === 'true');
|
||||
const productName = $('#product-name')
|
||||
.val()
|
||||
.trim();
|
||||
const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';
|
||||
const displayAuthenticationMethod =
|
||||
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
|
||||
const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
|
||||
const customHTMLafterBodyStart = $('#customHTMLafterBodyStart').val().trim();
|
||||
const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd').val().trim();
|
||||
const customHTMLafterBodyStart = $('#customHTMLafterBodyStart')
|
||||
.val()
|
||||
.trim();
|
||||
const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd')
|
||||
.val()
|
||||
.trim();
|
||||
|
||||
try {
|
||||
Settings.update(Settings.findOne()._id, {
|
||||
|
@ -166,7 +193,6 @@ BlazeComponent.extendComponent({
|
|||
}
|
||||
|
||||
DocHead.setTitle(productName);
|
||||
|
||||
},
|
||||
|
||||
sendSMTPTestEmail() {
|
||||
|
@ -183,31 +209,35 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click a.js-toggle-registration': this.toggleRegistration,
|
||||
'click a.js-toggle-tls': this.toggleTLS,
|
||||
'click a.js-setting-menu': this.switchMenu,
|
||||
'click a.js-toggle-board-choose': this.checkBoard,
|
||||
'click button.js-email-invite': this.inviteThroughEmail,
|
||||
'click button.js-save': this.saveMailServerInfo,
|
||||
'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
|
||||
'click a.js-toggle-hide-logo': this.toggleHideLogo,
|
||||
'click button.js-save-layout': this.saveLayout,
|
||||
'click a.js-toggle-display-authentication-method': this.toggleDisplayAuthenticationMethod,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click a.js-toggle-registration': this.toggleRegistration,
|
||||
'click a.js-toggle-tls': this.toggleTLS,
|
||||
'click a.js-setting-menu': this.switchMenu,
|
||||
'click a.js-toggle-board-choose': this.checkBoard,
|
||||
'click button.js-email-invite': this.inviteThroughEmail,
|
||||
'click button.js-save': this.saveMailServerInfo,
|
||||
'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
|
||||
'click a.js-toggle-hide-logo': this.toggleHideLogo,
|
||||
'click button.js-save-layout': this.saveLayout,
|
||||
'click a.js-toggle-display-authentication-method': this
|
||||
.toggleDisplayAuthenticationMethod,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('setting');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
||||
saveAccountsChange() {
|
||||
const allowEmailChange = ($('input[name=allowEmailChange]:checked').val() === 'true');
|
||||
const allowUserNameChange = ($('input[name=allowUserNameChange]:checked').val() === 'true');
|
||||
const allowEmailChange =
|
||||
$('input[name=allowEmailChange]:checked').val() === 'true';
|
||||
const allowUserNameChange =
|
||||
$('input[name=allowUserNameChange]:checked').val() === 'true';
|
||||
AccountSettings.update('accounts-allowEmailChange', {
|
||||
$set: {'booleanValue': allowEmailChange},
|
||||
$set: { booleanValue: allowEmailChange },
|
||||
});
|
||||
AccountSettings.update('accounts-allowUserNameChange', {
|
||||
$set: {'booleanValue': allowUserNameChange},
|
||||
$set: { booleanValue: allowUserNameChange },
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -219,9 +249,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click button.js-accounts-save': this.saveAccountsChange,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click button.js-accounts-save': this.saveAccountsChange,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('accountSettings');
|
||||
|
||||
|
@ -239,9 +271,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
saveMessage() {
|
||||
const message = $('#admin-announcement').val().trim();
|
||||
const message = $('#admin-announcement')
|
||||
.val()
|
||||
.trim();
|
||||
Announcements.update(Announcements.findOne()._id, {
|
||||
$set: {'body': message},
|
||||
$set: { body: message },
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -249,7 +283,7 @@ BlazeComponent.extendComponent({
|
|||
this.setLoading(true);
|
||||
const isActive = this.currentSetting().enabled;
|
||||
Announcements.update(Announcements.findOne()._id, {
|
||||
$set: {'enabled': !isActive},
|
||||
$set: { enabled: !isActive },
|
||||
});
|
||||
this.setLoading(false);
|
||||
if (isActive) {
|
||||
|
@ -260,14 +294,15 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click a.js-toggle-activemessage': this.toggleActive,
|
||||
'click button.js-announcement-save': this.saveMessage,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click a.js-toggle-activemessage': this.toggleActive,
|
||||
'click button.js-announcement-save': this.saveMessage,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('announcementSettings');
|
||||
|
||||
|
||||
Template.selectAuthenticationMethod.onCreated(function() {
|
||||
this.authenticationMethods = new ReactiveVar([]);
|
||||
|
||||
|
@ -276,9 +311,11 @@ Template.selectAuthenticationMethod.onCreated(function() {
|
|||
// TODO : add a management of different languages
|
||||
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
|
||||
this.authenticationMethods.set([
|
||||
{value: 'password'},
|
||||
{ value: 'password' },
|
||||
// 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] })),
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -91,29 +91,34 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
showTongueTitle() {
|
||||
if (this.isOpen())
|
||||
return `${TAPi18n.__('sidebar-close')}`;
|
||||
else
|
||||
return `${TAPi18n.__('sidebar-open')}`;
|
||||
if (this.isOpen()) return `${TAPi18n.__('sidebar-close')}`;
|
||||
else return `${TAPi18n.__('sidebar-open')}`;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-hide-sidebar': this.hide,
|
||||
'click .js-toggle-sidebar': this.toggle,
|
||||
'click .js-back-home': this.setView,
|
||||
'click .js-shortcuts'() {
|
||||
FlowRouter.go('shortcuts');
|
||||
return [
|
||||
{
|
||||
'click .js-hide-sidebar': this.hide,
|
||||
'click .js-toggle-sidebar': this.toggle,
|
||||
'click .js-back-home': this.setView,
|
||||
'click .js-shortcuts'() {
|
||||
FlowRouter.go('shortcuts');
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('sidebar');
|
||||
|
||||
Blaze.registerHelper('Sidebar', () => Sidebar);
|
||||
|
||||
EscapeActions.register('sidebarView',
|
||||
() => { Sidebar.setView(defaultView); },
|
||||
() => { return Sidebar && Sidebar.getView() !== defaultView; }
|
||||
EscapeActions.register(
|
||||
'sidebarView',
|
||||
() => {
|
||||
Sidebar.setView(defaultView);
|
||||
},
|
||||
() => {
|
||||
return Sidebar && Sidebar.getView() !== defaultView;
|
||||
},
|
||||
);
|
||||
|
||||
Template.memberPopup.helpers({
|
||||
|
@ -122,13 +127,13 @@ Template.memberPopup.helpers({
|
|||
},
|
||||
memberType() {
|
||||
const type = Users.findOne(this.userId).isBoardAdmin() ? 'admin' : 'normal';
|
||||
if(type === 'normal'){
|
||||
if (type === 'normal') {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const commentOnly = currentBoard.hasCommentOnly(this.userId);
|
||||
const noComments = currentBoard.hasNoComments(this.userId);
|
||||
if(commentOnly){
|
||||
if (commentOnly) {
|
||||
return TAPi18n.__('comment-only').toLowerCase();
|
||||
} else if(noComments) {
|
||||
} else if (noComments) {
|
||||
return TAPi18n.__('no-comments').toLowerCase();
|
||||
} else {
|
||||
return TAPi18n.__(type).toLowerCase();
|
||||
|
@ -197,7 +202,7 @@ Template.memberPopup.events({
|
|||
'click .js-remove-member': Popup.afterConfirm('removeMember', function() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
const memberId = this.userId;
|
||||
Cards.find({ boardId, members: memberId }).forEach((card) => {
|
||||
Cards.find({ boardId, members: memberId }).forEach(card => {
|
||||
card.unassignMember(memberId);
|
||||
});
|
||||
Boards.findOne(boardId).removeMember(memberId);
|
||||
|
@ -274,38 +279,40 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit'(evt) {
|
||||
evt.preventDefault();
|
||||
const url = evt.target.url.value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
let id = null;
|
||||
let integration = null;
|
||||
if (evt.target.id) {
|
||||
id = evt.target.id.value;
|
||||
integration = this.integration(id);
|
||||
if (url) {
|
||||
Integrations.update(integration._id, {
|
||||
$set: {
|
||||
url: `${url}`,
|
||||
},
|
||||
return [
|
||||
{
|
||||
submit(evt) {
|
||||
evt.preventDefault();
|
||||
const url = evt.target.url.value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
let id = null;
|
||||
let integration = null;
|
||||
if (evt.target.id) {
|
||||
id = evt.target.id.value;
|
||||
integration = this.integration(id);
|
||||
if (url) {
|
||||
Integrations.update(integration._id, {
|
||||
$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) {
|
||||
Integrations.insert({
|
||||
userId: Meteor.userId(),
|
||||
enabled: true,
|
||||
type: 'outgoing-webhooks',
|
||||
url: `${url}`,
|
||||
boardId: `${boardId}`,
|
||||
activities: ['all'],
|
||||
});
|
||||
}
|
||||
Popup.close();
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('outgoingWebhooksPopup');
|
||||
|
||||
|
@ -375,14 +382,16 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-background'(evt) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const newColor = this.currentData().toString();
|
||||
currentBoard.setColor(newColor);
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
'click .js-select-background'(evt) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const newColor = this.currentData().toString();
|
||||
currentBoard.setColor(newColor);
|
||||
evt.preventDefault();
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardChangeColorPopup');
|
||||
|
||||
|
@ -400,25 +409,34 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
isNullBoardSelected() {
|
||||
return (this.currentBoard.subtasksDefaultBoardId === null) || (this.currentBoard.subtasksDefaultBoardId === undefined);
|
||||
return (
|
||||
this.currentBoard.subtasksDefaultBoardId === null ||
|
||||
this.currentBoard.subtasksDefaultBoardId === undefined
|
||||
);
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
lists() {
|
||||
return Lists.find({
|
||||
boardId: this.currentBoard._id,
|
||||
archived: false,
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return Lists.find(
|
||||
{
|
||||
boardId: this.currentBoard._id,
|
||||
archived: false,
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
hasLists() {
|
||||
|
@ -431,54 +449,74 @@ BlazeComponent.extendComponent({
|
|||
|
||||
presentParentTask() {
|
||||
let result = this.currentBoard.presentParentTask;
|
||||
if ((result === null) || (result === undefined)) {
|
||||
if (result === null || result === undefined) {
|
||||
result = 'no-parent';
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-field-has-subtasks'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
|
||||
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks .materialCheckBox').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-deposit-board').prop('disabled', !this.currentBoard.allowsSubtasks);
|
||||
},
|
||||
'change .js-field-deposit-board'(evt) {
|
||||
let value = evt.target.value;
|
||||
if (value === 'null') {
|
||||
value = null;
|
||||
}
|
||||
this.currentBoard.setSubtasksDefaultBoardId(value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'change .js-field-deposit-list'(evt) {
|
||||
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);
|
||||
return [
|
||||
{
|
||||
'click .js-field-has-subtasks'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
|
||||
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks .materialCheckBox').toggleClass(
|
||||
'is-checked',
|
||||
this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
$('.js-field-has-subtasks').toggleClass(
|
||||
'is-checked',
|
||||
this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
$('.js-field-deposit-board').prop(
|
||||
'disabled',
|
||||
!this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
},
|
||||
'change .js-field-deposit-board'(evt) {
|
||||
let value = evt.target.value;
|
||||
if (value === 'null') {
|
||||
value = null;
|
||||
}
|
||||
});
|
||||
$(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
|
||||
$(`#${value}`).toggleClass('is-checked', true);
|
||||
this.currentBoard.setPresentParentTask(value);
|
||||
evt.preventDefault();
|
||||
this.currentBoard.setSubtasksDefaultBoardId(value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'change .js-field-deposit-list'(evt) {
|
||||
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');
|
||||
|
||||
|
@ -528,35 +566,46 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
||||
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 memberId = this.userId;
|
||||
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');
|
||||
currentBoard.setMemberPermission(memberId, isAdmin, isNoComments, isCommentOnly);
|
||||
currentBoard.setMemberPermission(
|
||||
memberId,
|
||||
isAdmin,
|
||||
isNoComments,
|
||||
isCommentOnly,
|
||||
);
|
||||
Popup.back(1);
|
||||
},
|
||||
});
|
||||
|
@ -569,21 +618,33 @@ Template.changePermissionsPopup.helpers({
|
|||
|
||||
isNormal() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return currentBoard.hasAdmin(this.userId) && (currentBoard.activeAdmins() === 1);
|
||||
return (
|
||||
currentBoard.hasAdmin(this.userId) && currentBoard.activeAdmins() === 1
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -10,12 +10,11 @@ BlazeComponent.extendComponent({
|
|||
// unfortunatly, Blaze doesn't have this notion.
|
||||
this.autorun(() => {
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (!currentBoardId)
|
||||
return;
|
||||
if (!currentBoardId) return;
|
||||
const handle = subManager.subscribe('board', currentBoardId, true);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isArchiveReady.set( handle.ready() );
|
||||
this.isArchiveReady.set(handle.ready());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -59,74 +58,82 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-restore-card'() {
|
||||
const card = this.currentData();
|
||||
if(card.canBeRestored()){
|
||||
card.restore();
|
||||
}
|
||||
},
|
||||
'click .js-restore-all-cards'() {
|
||||
this.archivedCards().forEach((card) => {
|
||||
if(card.canBeRestored()){
|
||||
return [
|
||||
{
|
||||
'click .js-restore-card'() {
|
||||
const card = this.currentData();
|
||||
if (card.canBeRestored()) {
|
||||
card.restore();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
'click .js-restore-all-cards'() {
|
||||
this.archivedCards().forEach(card => {
|
||||
if (card.canBeRestored()) {
|
||||
card.restore();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
'click .js-delete-card': Popup.afterConfirm('cardDelete', function() {
|
||||
const cardId = this._id;
|
||||
Cards.remove(cardId);
|
||||
Popup.close();
|
||||
}),
|
||||
'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', () => {
|
||||
this.archivedCards().forEach((card) => {
|
||||
Cards.remove(card._id);
|
||||
});
|
||||
Popup.close();
|
||||
}),
|
||||
'click .js-delete-card': Popup.afterConfirm('cardDelete', function() {
|
||||
const cardId = this._id;
|
||||
Cards.remove(cardId);
|
||||
Popup.close();
|
||||
}),
|
||||
'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', () => {
|
||||
this.archivedCards().forEach(card => {
|
||||
Cards.remove(card._id);
|
||||
});
|
||||
Popup.close();
|
||||
}),
|
||||
|
||||
'click .js-restore-list'() {
|
||||
const list = this.currentData();
|
||||
list.restore();
|
||||
},
|
||||
'click .js-restore-all-lists'() {
|
||||
this.archivedLists().forEach((list) => {
|
||||
'click .js-restore-list'() {
|
||||
const list = this.currentData();
|
||||
list.restore();
|
||||
});
|
||||
},
|
||||
},
|
||||
'click .js-restore-all-lists'() {
|
||||
this.archivedLists().forEach(list => {
|
||||
list.restore();
|
||||
});
|
||||
},
|
||||
|
||||
'click .js-delete-list': Popup.afterConfirm('listDelete', function() {
|
||||
this.remove();
|
||||
Popup.close();
|
||||
}),
|
||||
'click .js-delete-all-lists': Popup.afterConfirm('listDelete', () => {
|
||||
this.archivedLists().forEach((list) => {
|
||||
list.remove();
|
||||
});
|
||||
Popup.close();
|
||||
}),
|
||||
'click .js-delete-list': Popup.afterConfirm('listDelete', function() {
|
||||
this.remove();
|
||||
Popup.close();
|
||||
}),
|
||||
'click .js-delete-all-lists': Popup.afterConfirm('listDelete', () => {
|
||||
this.archivedLists().forEach(list => {
|
||||
list.remove();
|
||||
});
|
||||
Popup.close();
|
||||
}),
|
||||
|
||||
'click .js-restore-swimlane'() {
|
||||
const swimlane = this.currentData();
|
||||
swimlane.restore();
|
||||
},
|
||||
'click .js-restore-all-swimlanes'() {
|
||||
this.archivedSwimlanes().forEach((swimlane) => {
|
||||
'click .js-restore-swimlane'() {
|
||||
const swimlane = this.currentData();
|
||||
swimlane.restore();
|
||||
});
|
||||
},
|
||||
},
|
||||
'click .js-restore-all-swimlanes'() {
|
||||
this.archivedSwimlanes().forEach(swimlane => {
|
||||
swimlane.restore();
|
||||
});
|
||||
},
|
||||
|
||||
'click .js-delete-swimlane': Popup.afterConfirm('swimlaneDelete', function() {
|
||||
this.remove();
|
||||
Popup.close();
|
||||
}),
|
||||
'click .js-delete-all-swimlanes': Popup.afterConfirm('swimlaneDelete', () => {
|
||||
this.archivedSwimlanes().forEach((swimlane) => {
|
||||
swimlane.remove();
|
||||
});
|
||||
Popup.close();
|
||||
}),
|
||||
}];
|
||||
'click .js-delete-swimlane': Popup.afterConfirm(
|
||||
'swimlaneDelete',
|
||||
function() {
|
||||
this.remove();
|
||||
Popup.close();
|
||||
},
|
||||
),
|
||||
'click .js-delete-all-swimlanes': Popup.afterConfirm(
|
||||
'swimlaneDelete',
|
||||
() => {
|
||||
this.archivedSwimlanes().forEach(swimlane => {
|
||||
swimlane.remove();
|
||||
});
|
||||
Popup.close();
|
||||
},
|
||||
),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('archivesSidebar');
|
||||
|
|
|
@ -1,37 +1,43 @@
|
|||
BlazeComponent.extendComponent({
|
||||
|
||||
customFields() {
|
||||
return CustomFields.find({
|
||||
boardIds: {$in: [Session.get('currentBoard')]},
|
||||
boardIds: { $in: [Session.get('currentBoard')] },
|
||||
});
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-open-create-custom-field': Popup.open('createCustomField'),
|
||||
'click .js-edit-custom-field': Popup.open('editCustomField'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-open-create-custom-field': Popup.open('createCustomField'),
|
||||
'click .js-edit-custom-field': Popup.open('editCustomField'),
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
}).register('customFieldsSidebar');
|
||||
|
||||
const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
|
||||
_types: ['text', 'number', 'date', 'dropdown'],
|
||||
|
||||
onCreated() {
|
||||
this.type = new ReactiveVar((this.data().type) ? this.data().type : this._types[0]);
|
||||
this.dropdownItems = new ReactiveVar((this.data().settings && this.data().settings.dropdownItems) ? this.data().settings.dropdownItems : []);
|
||||
this.type = new ReactiveVar(
|
||||
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() {
|
||||
const currentType = this.data().type;
|
||||
return this._types.
|
||||
map((type) => {return {
|
||||
return this._types.map(type => {
|
||||
return {
|
||||
value: type,
|
||||
name: TAPi18n.__(`custom-field-${type}`),
|
||||
selected: type === currentType,
|
||||
};});
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
isTypeNotSelected(type) {
|
||||
|
@ -40,113 +46,122 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
|||
|
||||
getDropdownItems() {
|
||||
const items = this.dropdownItems.get();
|
||||
Array.from(this.findAll('.js-field-settings-dropdown input')).forEach((el, index) => {
|
||||
//console.log('each item!', index, el.value);
|
||||
if (!items[index]) items[index] = {
|
||||
_id: Random.id(6),
|
||||
};
|
||||
items[index].name = el.value.trim();
|
||||
});
|
||||
Array.from(this.findAll('.js-field-settings-dropdown input')).forEach(
|
||||
(el, index) => {
|
||||
//console.log('each item!', index, el.value);
|
||||
if (!items[index])
|
||||
items[index] = {
|
||||
_id: Random.id(6),
|
||||
};
|
||||
items[index].name = el.value.trim();
|
||||
},
|
||||
);
|
||||
return items;
|
||||
},
|
||||
|
||||
getSettings() {
|
||||
const settings = {};
|
||||
switch (this.type.get()) {
|
||||
case 'dropdown': {
|
||||
const dropdownItems = this.getDropdownItems().filter((item) => !!item.name.trim());
|
||||
settings.dropdownItems = dropdownItems;
|
||||
break;
|
||||
}
|
||||
case 'dropdown': {
|
||||
const dropdownItems = this.getDropdownItems().filter(
|
||||
item => !!item.name.trim(),
|
||||
);
|
||||
settings.dropdownItems = dropdownItems;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-field-type'(evt) {
|
||||
const value = evt.target.value;
|
||||
this.type.set(value);
|
||||
},
|
||||
'keydown .js-dropdown-item.last'(evt) {
|
||||
if (evt.target.value.trim() && evt.keyCode === 13) {
|
||||
const items = this.getDropdownItems();
|
||||
this.dropdownItems.set(items);
|
||||
evt.target.value = '';
|
||||
}
|
||||
},
|
||||
'click .js-field-show-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if(!$target.hasClass('js-field-show-on-card')){
|
||||
$target = $target.parent();
|
||||
}
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .js-field-automatically-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if(!$target.hasClass('js-field-automatically-on-card')){
|
||||
$target = $target.parent();
|
||||
}
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .js-field-showLabel-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if(!$target.hasClass('js-field-showLabel-on-card')){
|
||||
$target = $target.parent();
|
||||
}
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .primary'(evt) {
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
'change .js-field-type'(evt) {
|
||||
const value = evt.target.value;
|
||||
this.type.set(value);
|
||||
},
|
||||
'keydown .js-dropdown-item.last'(evt) {
|
||||
if (evt.target.value.trim() && evt.keyCode === 13) {
|
||||
const items = this.getDropdownItems();
|
||||
this.dropdownItems.set(items);
|
||||
evt.target.value = '';
|
||||
}
|
||||
},
|
||||
'click .js-field-show-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if (!$target.hasClass('js-field-show-on-card')) {
|
||||
$target = $target.parent();
|
||||
}
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .js-field-automatically-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if (!$target.hasClass('js-field-automatically-on-card')) {
|
||||
$target = $target.parent();
|
||||
}
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .js-field-showLabel-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if (!$target.hasClass('js-field-showLabel-on-card')) {
|
||||
$target = $target.parent();
|
||||
}
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .primary'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
const data = {
|
||||
name: this.find('.js-field-name').value.trim(),
|
||||
type: this.type.get(),
|
||||
settings: this.getSettings(),
|
||||
showOnCard: this.find('.js-field-show-on-card.is-checked') !== null,
|
||||
showLabelOnMiniCard: this.find('.js-field-showLabel-on-card.is-checked') !== null,
|
||||
automaticallyOnCard: this.find('.js-field-automatically-on-card.is-checked') !== null,
|
||||
};
|
||||
const data = {
|
||||
name: this.find('.js-field-name').value.trim(),
|
||||
type: this.type.get(),
|
||||
settings: this.getSettings(),
|
||||
showOnCard: this.find('.js-field-show-on-card.is-checked') !== null,
|
||||
showLabelOnMiniCard:
|
||||
this.find('.js-field-showLabel-on-card.is-checked') !== null,
|
||||
automaticallyOnCard:
|
||||
this.find('.js-field-automatically-on-card.is-checked') !== null,
|
||||
};
|
||||
|
||||
// insert or update
|
||||
if (!this.data()._id) {
|
||||
data.boardIds = [Session.get('currentBoard')];
|
||||
CustomFields.insert(data);
|
||||
} else {
|
||||
CustomFields.update(this.data()._id, {$set: data});
|
||||
}
|
||||
// insert or update
|
||||
if (!this.data()._id) {
|
||||
data.boardIds = [Session.get('currentBoard')];
|
||||
CustomFields.insert(data);
|
||||
} else {
|
||||
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');
|
||||
|
||||
(class extends CreateCustomFieldPopup {
|
||||
|
||||
template() {
|
||||
return 'createCustomFieldPopup';
|
||||
}
|
||||
|
||||
}).register('editCustomFieldPopup');
|
||||
}.register('editCustomFieldPopup'));
|
||||
|
||||
/*Template.deleteCustomFieldPopup.events({
|
||||
'submit'(evt) {
|
||||
|
|
|
@ -1,106 +1,108 @@
|
|||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [{
|
||||
'click .js-toggle-label-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.labelIds.toggle(this.currentData()._id);
|
||||
Filter.resetExceptions();
|
||||
return [
|
||||
{
|
||||
'click .js-toggle-label-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
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');
|
||||
|
||||
function mutateSelectedCards(mutationName, ...args) {
|
||||
Cards.find(MultiSelection.getMongoSelector()).forEach((card) => {
|
||||
Cards.find(MultiSelection.getMongoSelector()).forEach(card => {
|
||||
card[mutationName](...args);
|
||||
});
|
||||
}
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
mapSelection(kind, _id) {
|
||||
return Cards.find(MultiSelection.getMongoSelector()).map((card) => {
|
||||
return Cards.find(MultiSelection.getMongoSelector()).map(card => {
|
||||
const methodName = kind === 'label' ? 'hasLabel' : 'isAssigned';
|
||||
return card[methodName](_id);
|
||||
});
|
||||
},
|
||||
|
||||
allSelectedElementHave(kind, _id) {
|
||||
if (MultiSelection.isEmpty())
|
||||
return false;
|
||||
else
|
||||
return _.every(this.mapSelection(kind, _id));
|
||||
if (MultiSelection.isEmpty()) return false;
|
||||
else return _.every(this.mapSelection(kind, _id));
|
||||
},
|
||||
|
||||
someSelectedElementHave(kind, _id) {
|
||||
if (MultiSelection.isEmpty())
|
||||
return false;
|
||||
else
|
||||
return _.some(this.mapSelection(kind, _id));
|
||||
if (MultiSelection.isEmpty()) return false;
|
||||
else return _.some(this.mapSelection(kind, _id));
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-toggle-label-multiselection'(evt) {
|
||||
const labelId = this.currentData()._id;
|
||||
const mappedSelection = this.mapSelection('label', labelId);
|
||||
return [
|
||||
{
|
||||
'click .js-toggle-label-multiselection'(evt) {
|
||||
const labelId = this.currentData()._id;
|
||||
const mappedSelection = this.mapSelection('label', labelId);
|
||||
|
||||
if (_.every(mappedSelection)) {
|
||||
mutateSelectedCards('removeLabel', labelId);
|
||||
} else if (_.every(mappedSelection, (bool) => !bool)) {
|
||||
mutateSelectedCards('addLabel', labelId);
|
||||
} else {
|
||||
const popup = Popup.open('disambiguateMultiLabel');
|
||||
// XXX We need to have a better integration between the popup and the
|
||||
// UI components systems.
|
||||
popup.call(this.currentData(), evt);
|
||||
}
|
||||
if (_.every(mappedSelection)) {
|
||||
mutateSelectedCards('removeLabel', labelId);
|
||||
} else if (_.every(mappedSelection, bool => !bool)) {
|
||||
mutateSelectedCards('addLabel', labelId);
|
||||
} else {
|
||||
const popup = Popup.open('disambiguateMultiLabel');
|
||||
// XXX We need to have a better integration between the popup and the
|
||||
// UI components systems.
|
||||
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');
|
||||
|
||||
|
|
|
@ -9,11 +9,13 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-search-term-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.term.set(evt.target.searchTerm.value);
|
||||
return [
|
||||
{
|
||||
'submit .js-search-term-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.term.set(evt.target.searchTerm.value);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('searchSidebar');
|
||||
|
|
|
@ -6,9 +6,11 @@ Meteor.startup(() => {
|
|||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
editTitle(evt) {
|
||||
evt.preventDefault();
|
||||
const newTitle = this.childComponents('inlinedForm')[0].getValue().trim();
|
||||
editTitle(event) {
|
||||
event.preventDefault();
|
||||
const newTitle = this.childComponents('inlinedForm')[0]
|
||||
.getValue()
|
||||
.trim();
|
||||
const swimlane = this.currentData();
|
||||
if (newTitle) {
|
||||
swimlane.rename(newTitle.trim());
|
||||
|
@ -16,18 +18,20 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-open-swimlane-menu': Popup.open('swimlaneAction'),
|
||||
'click .js-open-add-swimlane-menu': Popup.open('swimlaneAdd'),
|
||||
submit: this.editTitle,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-open-swimlane-menu': Popup.open('swimlaneAction'),
|
||||
'click .js-open-add-swimlane-menu': Popup.open('swimlaneAdd'),
|
||||
submit: this.editTitle,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('swimlaneHeader');
|
||||
|
||||
Template.swimlaneActionPopup.events({
|
||||
'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
|
||||
'click .js-close-swimlane' (evt) {
|
||||
evt.preventDefault();
|
||||
'click .js-close-swimlane'(event) {
|
||||
event.preventDefault();
|
||||
this.archive();
|
||||
Popup.close();
|
||||
},
|
||||
|
@ -39,34 +43,42 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
submit(evt) {
|
||||
evt.preventDefault();
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const nextSwimlane = currentBoard.nextSwimlane(this.currentSwimlane);
|
||||
const titleInput = this.find('.swimlane-name-input');
|
||||
const title = titleInput.value.trim();
|
||||
const sortValue = calculateIndexData(this.currentSwimlane, nextSwimlane, 1);
|
||||
const swimlaneType = (currentBoard.isTemplatesBoard())?'template-swimlane':'swimlane';
|
||||
return [
|
||||
{
|
||||
submit(event) {
|
||||
event.preventDefault();
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const nextSwimlane = currentBoard.nextSwimlane(this.currentSwimlane);
|
||||
const titleInput = this.find('.swimlane-name-input');
|
||||
const title = titleInput.value.trim();
|
||||
const sortValue = calculateIndexData(
|
||||
this.currentSwimlane,
|
||||
nextSwimlane,
|
||||
1,
|
||||
);
|
||||
const swimlaneType = currentBoard.isTemplatesBoard()
|
||||
? 'template-swimlane'
|
||||
: 'swimlane';
|
||||
|
||||
if (title) {
|
||||
Swimlanes.insert({
|
||||
title,
|
||||
boardId: Session.get('currentBoard'),
|
||||
sort: sortValue.base,
|
||||
type: swimlaneType,
|
||||
});
|
||||
if (title) {
|
||||
Swimlanes.insert({
|
||||
title,
|
||||
boardId: Session.get('currentBoard'),
|
||||
sort: sortValue.base,
|
||||
type: swimlaneType,
|
||||
});
|
||||
|
||||
titleInput.value = '';
|
||||
titleInput.focus();
|
||||
}
|
||||
// XXX ideally, we should move the popup to the newly
|
||||
// created swimlane so a user can add more than one swimlane
|
||||
// with a minimum of interactions
|
||||
Popup.close();
|
||||
titleInput.value = '';
|
||||
titleInput.focus();
|
||||
}
|
||||
// XXX ideally, we should move the popup to the newly
|
||||
// created swimlane so a user can add more than one swimlane
|
||||
// with a minimum of interactions
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-swimlane-template': Popup.open('searchElement'),
|
||||
},
|
||||
'click .js-swimlane-template': Popup.open('searchElement'),
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('swimlaneAddPopup');
|
||||
|
||||
|
@ -77,7 +89,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
colors() {
|
||||
return swimlaneColors.map((color) => ({ color, name: '' }));
|
||||
return swimlaneColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
|
@ -85,18 +97,20 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'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');
|
||||
|
|
|
@ -2,16 +2,27 @@ const { calculateIndex, enableClickOnTouch } = Utils;
|
|||
|
||||
function currentListIsInThisSwimlane(swimlaneId) {
|
||||
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) {
|
||||
const currentCard = Cards.findOne(Session.get('currentCard'));
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser && currentUser.profile && currentUser.profile.boardView === 'board-view-swimlanes')
|
||||
return currentCard && currentCard.listId === listId && currentCard.swimlaneId === swimlaneId;
|
||||
else // Default view: board-view-lists
|
||||
return currentCard && currentCard.listId === listId;
|
||||
if (
|
||||
currentUser &&
|
||||
currentUser.profile &&
|
||||
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/ChronikEwok/wekan/commit/cad9b20451bb6149bfb527a99b5001873b06c3de
|
||||
// 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)');
|
||||
|
||||
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
|
||||
|
@ -87,8 +102,11 @@ function initSortable(boardComponent, $listsDom) {
|
|||
boardComponent.autorun(() => {
|
||||
const $listDom = $listsDom;
|
||||
if ($listDom.data('sortable')) {
|
||||
$listsDom.sortable('option', 'disabled',
|
||||
MultiSelection.isActive() || !userIsMember());
|
||||
$listsDom.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
MultiSelection.isActive() || !userIsMember(),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -124,47 +142,60 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
// Click-and-drag action
|
||||
'mousedown .board-canvas'(evt) {
|
||||
// Translating the board canvas using the click-and-drag action can
|
||||
// conflict with the build-in browser mechanism to select text. We
|
||||
// define a list of elements in which we disable the dragging because
|
||||
// the user will legitimately expect to be able to select some text with
|
||||
// his mouse.
|
||||
const noDragInside = ['a', '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;
|
||||
}
|
||||
return [
|
||||
{
|
||||
// Click-and-drag action
|
||||
'mousedown .board-canvas'(evt) {
|
||||
// Translating the board canvas using the click-and-drag action can
|
||||
// conflict with the build-in browser mechanism to select text. We
|
||||
// define a list of elements in which we disable the dragging because
|
||||
// the user will legitimately expect to be able to select some text with
|
||||
// his mouse.
|
||||
const noDragInside = [
|
||||
'a',
|
||||
'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');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
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();
|
||||
},
|
||||
|
||||
|
@ -174,32 +205,40 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
submit(evt) {
|
||||
evt.preventDefault();
|
||||
const titleInput = this.find('.list-name-input');
|
||||
const title = titleInput.value.trim();
|
||||
if (title) {
|
||||
Lists.insert({
|
||||
title,
|
||||
boardId: Session.get('currentBoard'),
|
||||
sort: $('.list').length,
|
||||
type: (this.isListTemplatesSwimlane)?'template-list':'list',
|
||||
swimlaneId: (this.currentBoard.isTemplatesBoard())?this.currentSwimlane._id:'',
|
||||
});
|
||||
return [
|
||||
{
|
||||
submit(evt) {
|
||||
evt.preventDefault();
|
||||
const titleInput = this.find('.list-name-input');
|
||||
const title = titleInput.value.trim();
|
||||
if (title) {
|
||||
Lists.insert({
|
||||
title,
|
||||
boardId: Session.get('currentBoard'),
|
||||
sort: $('.list').length,
|
||||
type: this.isListTemplatesSwimlane ? 'template-list' : 'list',
|
||||
swimlaneId: this.currentBoard.isTemplatesBoard()
|
||||
? this.currentSwimlane._id
|
||||
: '',
|
||||
});
|
||||
|
||||
titleInput.value = '';
|
||||
titleInput.focus();
|
||||
}
|
||||
titleInput.value = '';
|
||||
titleInput.focus();
|
||||
}
|
||||
},
|
||||
'click .js-list-template': Popup.open('searchElement'),
|
||||
},
|
||||
'click .js-list-template': Popup.open('searchElement'),
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('addListForm');
|
||||
|
||||
Template.swimlane.helpers({
|
||||
canSeeAddList() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -22,14 +22,11 @@ Template.userAvatar.helpers({
|
|||
presenceStatusClassName() {
|
||||
const user = Users.findOne(this.userId);
|
||||
const userPresence = presences.findOne({ userId: this.userId });
|
||||
if (user && user.isInvitedTo(Session.get('currentBoard')))
|
||||
return 'pending';
|
||||
else if (!userPresence)
|
||||
return 'disconnected';
|
||||
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
|
||||
else if (!userPresence) return 'disconnected';
|
||||
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
|
||||
return 'active';
|
||||
else
|
||||
return 'idle';
|
||||
else return 'idle';
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -45,7 +42,7 @@ Template.userAvatarInitials.helpers({
|
|||
|
||||
viewPortWidth() {
|
||||
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() {
|
||||
return Avatars.find({userId: Meteor.userId()});
|
||||
return Avatars.find({ userId: Meteor.userId() });
|
||||
},
|
||||
|
||||
isSelected() {
|
||||
|
@ -89,46 +86,48 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-upload-avatar'() {
|
||||
this.$('.js-upload-avatar-input').click();
|
||||
},
|
||||
'change .js-upload-avatar-input'(evt) {
|
||||
let file, fileUrl;
|
||||
return [
|
||||
{
|
||||
'click .js-upload-avatar'() {
|
||||
this.$('.js-upload-avatar-input').click();
|
||||
},
|
||||
'change .js-upload-avatar-input'(event) {
|
||||
let file, fileUrl;
|
||||
|
||||
FS.Utility.eachFile(evt, (f) => {
|
||||
try {
|
||||
file = Avatars.insert(new FS.File(f));
|
||||
fileUrl = file.url(this.avatarUrlOptions());
|
||||
} catch (e) {
|
||||
this.setError('avatar-too-big');
|
||||
FS.Utility.eachFile(event, f => {
|
||||
try {
|
||||
file = Avatars.insert(new FS.File(f));
|
||||
fileUrl = file.url(this.avatarUrlOptions());
|
||||
} catch (e) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
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'() {
|
||||
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);
|
||||
},
|
||||
},
|
||||
'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');
|
||||
|
||||
|
@ -146,11 +145,11 @@ Template.cardMembersPopup.helpers({
|
|||
});
|
||||
|
||||
Template.cardMembersPopup.events({
|
||||
'click .js-select-member'(evt) {
|
||||
'click .js-select-member'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const memberId = this.userId;
|
||||
card.toggleMember(memberId);
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ Template.memberMenuPopup.events({
|
|||
'click .js-change-avatar': Popup.open('changeAvatar'),
|
||||
'click .js-change-password': Popup.open('changePassword'),
|
||||
'click .js-change-language': Popup.open('changeLanguage'),
|
||||
'click .js-logout'(evt) {
|
||||
evt.preventDefault();
|
||||
'click .js-logout'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
AccountsTemplates.logout();
|
||||
},
|
||||
|
@ -38,12 +38,12 @@ Template.editProfilePopup.helpers({
|
|||
});
|
||||
|
||||
Template.editProfilePopup.events({
|
||||
submit(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const fullname = tpl.find('.js-profile-fullname').value.trim();
|
||||
const username = tpl.find('.js-profile-username').value.trim();
|
||||
const initials = tpl.find('.js-profile-initials').value.trim();
|
||||
const email = tpl.find('.js-profile-email').value.trim();
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const fullname = templateInstance.find('.js-profile-fullname').value.trim();
|
||||
const username = templateInstance.find('.js-profile-username').value.trim();
|
||||
const initials = templateInstance.find('.js-profile-initials').value.trim();
|
||||
const email = templateInstance.find('.js-profile-email').value.trim();
|
||||
let isChangeUserName = false;
|
||||
let isChangeEmail = false;
|
||||
Users.update(Meteor.userId(), {
|
||||
|
@ -53,29 +53,36 @@ Template.editProfilePopup.events({
|
|||
},
|
||||
});
|
||||
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) {
|
||||
Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), Meteor.userId(), function (error) {
|
||||
const usernameMessageElement = tpl.$('.username-taken');
|
||||
const emailMessageElement = tpl.$('.email-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'username-already-taken') {
|
||||
usernameMessageElement.show();
|
||||
emailMessageElement.hide();
|
||||
} else if (errorElement === 'email-already-taken') {
|
||||
Meteor.call(
|
||||
'setUsernameAndEmail',
|
||||
username,
|
||||
email.toLowerCase(),
|
||||
Meteor.userId(),
|
||||
function(error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
const emailMessageElement = templateInstance.$('.email-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();
|
||||
emailMessageElement.show();
|
||||
emailMessageElement.hide();
|
||||
Popup.back();
|
||||
}
|
||||
} else {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.hide();
|
||||
Popup.back();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
} else if (isChangeUserName) {
|
||||
Meteor.call('setUsername', username, Meteor.userId(), function (error) {
|
||||
const messageElement = tpl.$('.username-taken');
|
||||
Meteor.call('setUsername', username, Meteor.userId(), function(error) {
|
||||
const messageElement = templateInstance.$('.username-taken');
|
||||
if (error) {
|
||||
messageElement.show();
|
||||
} else {
|
||||
|
@ -84,8 +91,10 @@ Template.editProfilePopup.events({
|
|||
}
|
||||
});
|
||||
} else if (isChangeEmail) {
|
||||
Meteor.call('setEmail', email.toLowerCase(), Meteor.userId(), function (error) {
|
||||
const messageElement = tpl.$('.email-taken');
|
||||
Meteor.call('setEmail', email.toLowerCase(), Meteor.userId(), function(
|
||||
error,
|
||||
) {
|
||||
const messageElement = templateInstance.$('.email-taken');
|
||||
if (error) {
|
||||
messageElement.show();
|
||||
} else {
|
||||
|
@ -104,7 +113,7 @@ Template.editProfilePopup.events({
|
|||
|
||||
// XXX For some reason the useraccounts autofocus isnt working in this case.
|
||||
// See https://github.com/meteor-useraccounts/core/issues/384
|
||||
Template.changePasswordPopup.onRendered(function () {
|
||||
Template.changePasswordPopup.onRendered(function() {
|
||||
this.find('#at-field-current_password').focus();
|
||||
});
|
||||
|
||||
|
@ -123,7 +132,7 @@ Template.changeLanguagePopup.helpers({
|
|||
name = 'Occitan';
|
||||
}
|
||||
return { tag, name };
|
||||
}).sort(function (a, b) {
|
||||
}).sort(function(a, b) {
|
||||
if (a.name === b.name) {
|
||||
return 0;
|
||||
} else {
|
||||
|
@ -138,13 +147,13 @@ Template.changeLanguagePopup.helpers({
|
|||
});
|
||||
|
||||
Template.changeLanguagePopup.events({
|
||||
'click .js-set-language'(evt) {
|
||||
'click .js-set-language'(event) {
|
||||
Users.update(Meteor.userId(), {
|
||||
$set: {
|
||||
'profile.language': this.tag,
|
||||
},
|
||||
});
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -161,9 +170,12 @@ Template.changeSettingsPopup.events({
|
|||
'click .js-toggle-system-messages'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
},
|
||||
'click .js-apply-show-cards-at'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const minLimit = parseInt(tpl.$('#show-cards-count-at').val(), 10);
|
||||
'click .js-apply-show-cards-at'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const minLimit = parseInt(
|
||||
templateInstance.$('#show-cards-count-at').val(),
|
||||
10,
|
||||
);
|
||||
if (!isNaN(minLimit)) {
|
||||
Meteor.call('changeLimitToShowCardsCount', minLimit);
|
||||
Popup.back();
|
||||
|
|
|
@ -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(''));
|
||||
|
||||
|
|
|
@ -27,10 +27,7 @@ function copyTitleInAriaLabel(attributes) {
|
|||
// 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
|
||||
// components to handle that without overwriting any build-in function.
|
||||
const {
|
||||
A: superA,
|
||||
I: superI,
|
||||
} = HTML;
|
||||
const { A: superA, I: superI } = HTML;
|
||||
|
||||
HTML.A = (attributes, ...others) => {
|
||||
return superA(copyTitleInAriaLabel(enforceHref(attributes)), ...others);
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
function whichTransitionEvent() {
|
||||
const el = document.createElement('fakeelement');
|
||||
const transitions = {
|
||||
transition:'transitionend',
|
||||
OTransition:'oTransitionEnd',
|
||||
MSTransition:'msTransitionEnd',
|
||||
MozTransition:'transitionend',
|
||||
WebkitTransition:'webkitTransitionEnd',
|
||||
transition: 'transitionend',
|
||||
OTransition: 'oTransitionEnd',
|
||||
MSTransition: 'msTransitionEnd',
|
||||
MozTransition: 'transitionend',
|
||||
WebkitTransition: 'webkitTransitionEnd',
|
||||
};
|
||||
|
||||
for (const t in transitions) {
|
||||
|
@ -21,11 +21,11 @@ function whichTransitionEvent() {
|
|||
function whichAnimationEvent() {
|
||||
const el = document.createElement('fakeelement');
|
||||
const transitions = {
|
||||
animation:'animationend',
|
||||
OAnimation:'oAnimationEnd',
|
||||
MSTransition:'msAnimationEnd',
|
||||
MozAnimation:'animationend',
|
||||
WebkitAnimation:'webkitAnimationEnd',
|
||||
animation: 'animationend',
|
||||
OAnimation: 'oAnimationEnd',
|
||||
MSTransition: 'msAnimationEnd',
|
||||
MozAnimation: 'animationend',
|
||||
WebkitAnimation: 'webkitAnimationEnd',
|
||||
};
|
||||
|
||||
for (const t in transitions) {
|
||||
|
|
|
@ -10,15 +10,20 @@ DatePicker = BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
onRendered() {
|
||||
const $picker = this.$('.js-datepicker').datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
}).on('changeDate', function(evt) {
|
||||
this.find('#date').value = moment(evt.date).format('L');
|
||||
this.error.set('');
|
||||
this.find('#time').focus();
|
||||
}.bind(this));
|
||||
const $picker = this.$('.js-datepicker')
|
||||
.datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
})
|
||||
.on(
|
||||
'changeDate',
|
||||
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()) {
|
||||
$picker.datepicker('update', this.date.get().toDate());
|
||||
|
@ -26,13 +31,11 @@ DatePicker = BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
showDate() {
|
||||
if (this.date.get().isValid())
|
||||
return this.date.get().format('L');
|
||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
||||
return '';
|
||||
},
|
||||
showTime() {
|
||||
if (this.date.get().isValid())
|
||||
return this.date.get().format('LT');
|
||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
||||
return '';
|
||||
},
|
||||
dateFormat() {
|
||||
|
@ -43,44 +46,47 @@ DatePicker = BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'keyup .js-date-field'() {
|
||||
// parse for localized date format in strict mode
|
||||
const dateMoment = moment(this.find('#date').value, 'L', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
|
||||
}
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(this.find('#time').value, 'LT', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
},
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
'keyup .js-date-field'() {
|
||||
// parse for localized date format in strict mode
|
||||
const dateMoment = moment(this.find('#date').value, 'L', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
|
||||
}
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(this.find('#time').value, 'LT', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
},
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
// if no time was given, init with 12:00
|
||||
const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
// 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 dateString = `${evt.target.date.value} ${time}`;
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
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();
|
||||
}
|
||||
else {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
});
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
$.event.fix = (function(originalFix) {
|
||||
return function(event) {
|
||||
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;
|
||||
}
|
||||
return event;
|
||||
|
@ -23,7 +26,7 @@
|
|||
matchType: /image.*/,
|
||||
};
|
||||
|
||||
return $.fn.dropImageReader = function(options) {
|
||||
return ($.fn.dropImageReader = function(options) {
|
||||
if (typeof options === 'function') {
|
||||
options = {
|
||||
callback: options,
|
||||
|
@ -40,9 +43,9 @@
|
|||
return $(element).bind('drop', function(event) {
|
||||
stopFn(event);
|
||||
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];
|
||||
if(f.type.match(options.matchType)) {
|
||||
if (f.type.match(options.matchType)) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(evt) {
|
||||
return options.callback.call(element, {
|
||||
|
@ -58,5 +61,5 @@
|
|||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
})(jQuery);
|
||||
|
|
|
@ -33,13 +33,19 @@ EscapeActions = {
|
|||
|
||||
const noClickEscapeOn = options.noClickEscapeOn;
|
||||
|
||||
this._actions = _.sortBy([...this._actions, {
|
||||
priority,
|
||||
condition,
|
||||
action,
|
||||
noClickEscapeOn,
|
||||
enabledOnClick,
|
||||
}], (action) => action.priority);
|
||||
this._actions = _.sortBy(
|
||||
[
|
||||
...this._actions,
|
||||
{
|
||||
priority,
|
||||
condition,
|
||||
action,
|
||||
noClickEscapeOn,
|
||||
enabledOnClick,
|
||||
},
|
||||
],
|
||||
action => action.priority,
|
||||
);
|
||||
},
|
||||
|
||||
executeLowest() {
|
||||
|
@ -80,10 +86,8 @@ EscapeActions = {
|
|||
},
|
||||
|
||||
_stopClick(action, clickTarget) {
|
||||
if (!_.isString(action.noClickEscapeOn))
|
||||
return false;
|
||||
else
|
||||
return $(clickTarget).closest(action.noClickEscapeOn).length > 0;
|
||||
if (!_.isString(action.noClickEscapeOn)) return false;
|
||||
else return $(clickTarget).closest(action.noClickEscapeOn).length > 0;
|
||||
},
|
||||
|
||||
_execute(options) {
|
||||
|
@ -95,14 +99,11 @@ EscapeActions = {
|
|||
let executedAtLeastOne = false;
|
||||
let maxPriority;
|
||||
|
||||
if (!maxLabel)
|
||||
maxPriority = Infinity;
|
||||
else
|
||||
maxPriority = this.hierarchy.indexOf(maxLabel);
|
||||
if (!maxLabel) maxPriority = Infinity;
|
||||
else maxPriority = this.hierarchy.indexOf(maxLabel);
|
||||
|
||||
for (const currentAction of this._actions) {
|
||||
if (currentAction.priority > maxPriority)
|
||||
return executedAtLeastOne;
|
||||
if (currentAction.priority > maxPriority) return executedAtLeastOne;
|
||||
|
||||
if (isClick && this._stopClick(currentAction, clickTarget))
|
||||
return executedAtLeastOne;
|
||||
|
@ -111,8 +112,7 @@ EscapeActions = {
|
|||
if (isEnabled && currentAction.condition()) {
|
||||
currentAction.action();
|
||||
executedAtLeastOne = true;
|
||||
if (!multipleActions)
|
||||
return executedAtLeastOne;
|
||||
if (!multipleActions) 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,
|
||||
// close the popup). We don't execute any action if the user has clicked on a
|
||||
// link or a button.
|
||||
$(document).on('click', (evt) => {
|
||||
if (evt.button === 0 &&
|
||||
$(evt.target).closest('a,button,.is-editable').length === 0) {
|
||||
$(document).on('click', evt => {
|
||||
if (
|
||||
evt.button === 0 &&
|
||||
$(evt.target).closest('a,button,.is-editable').length === 0
|
||||
) {
|
||||
EscapeActions.clickExecute(evt.target, 'multiselection');
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', 'a[href=\\#]', (evt) => {
|
||||
$(document).on('click', 'a[href=\\#]', evt => {
|
||||
evt.preventDefault();
|
||||
});
|
||||
|
|
|
@ -71,18 +71,19 @@ class SetFilter {
|
|||
_getEmptySelector() {
|
||||
this._dep.depend();
|
||||
let includeEmpty = false;
|
||||
this._selectedElements.forEach((el) => {
|
||||
this._selectedElements.forEach(el => {
|
||||
if (el === undefined) {
|
||||
includeEmpty = true;
|
||||
}
|
||||
});
|
||||
return includeEmpty ? {
|
||||
$eq: [],
|
||||
} : null;
|
||||
return includeEmpty
|
||||
? {
|
||||
$eq: [],
|
||||
}
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Advanced filter forms a MongoSelector from a users String.
|
||||
// Build by: Ignatz 19.05.2018 (github feuerball11)
|
||||
class AdvancedFilter {
|
||||
|
@ -128,7 +129,8 @@ class AdvancedFilter {
|
|||
current += char;
|
||||
continue;
|
||||
}
|
||||
if (char === '\'') {
|
||||
// eslint-disable-next-line quotes
|
||||
if (char === "'") {
|
||||
string = !string;
|
||||
if (string) wasString = true;
|
||||
continue;
|
||||
|
@ -139,8 +141,8 @@ class AdvancedFilter {
|
|||
}
|
||||
if (char === ' ' && !string) {
|
||||
commands.push({
|
||||
'cmd': current,
|
||||
'string': wasString,
|
||||
cmd: current,
|
||||
string: wasString,
|
||||
regex,
|
||||
});
|
||||
wasString = false;
|
||||
|
@ -151,8 +153,8 @@ class AdvancedFilter {
|
|||
}
|
||||
if (current !== '') {
|
||||
commands.push({
|
||||
'cmd': current,
|
||||
'string': wasString,
|
||||
cmd: current,
|
||||
string: wasString,
|
||||
regex,
|
||||
});
|
||||
}
|
||||
|
@ -161,16 +163,19 @@ class AdvancedFilter {
|
|||
|
||||
_fieldNameToId(field) {
|
||||
const found = CustomFields.findOne({
|
||||
'name': field,
|
||||
name: field,
|
||||
});
|
||||
return found._id;
|
||||
}
|
||||
|
||||
_fieldValueToId(field, value) {
|
||||
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++) {
|
||||
if (found.settings.dropdownItems[i].name === value) {
|
||||
return found.settings.dropdownItems[i]._id;
|
||||
|
@ -202,37 +207,32 @@ class AdvancedFilter {
|
|||
for (let i = 0; i < commands.length; i++) {
|
||||
if (commands[i].cmd) {
|
||||
switch (commands[i].cmd) {
|
||||
case '(':
|
||||
{
|
||||
level++;
|
||||
if (start === -1) start = i;
|
||||
continue;
|
||||
}
|
||||
case ')':
|
||||
{
|
||||
level--;
|
||||
commands.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (level > 0) {
|
||||
subcommands.push(commands[i]);
|
||||
case '(': {
|
||||
level++;
|
||||
if (start === -1) start = i;
|
||||
continue;
|
||||
}
|
||||
case ')': {
|
||||
level--;
|
||||
commands.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
if (level > 0) {
|
||||
subcommands.push(commands[i]);
|
||||
commands.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start !== -1) {
|
||||
this._processSubCommands(subcommands);
|
||||
if (subcommands.length === 1)
|
||||
commands.splice(start, 0, subcommands[0]);
|
||||
else
|
||||
commands.splice(start, 0, subcommands);
|
||||
if (subcommands.length === 1) commands.splice(start, 0, subcommands[0]);
|
||||
else commands.splice(start, 0, subcommands);
|
||||
}
|
||||
this._processConditions(commands);
|
||||
this._processLogicalOperators(commands);
|
||||
|
@ -242,149 +242,139 @@ class AdvancedFilter {
|
|||
for (let i = 0; i < commands.length; i++) {
|
||||
if (!commands[i].string && commands[i].cmd) {
|
||||
switch (commands[i].cmd) {
|
||||
case '=':
|
||||
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': regex,
|
||||
};
|
||||
} else {
|
||||
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: {
|
||||
case '=':
|
||||
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': regex,
|
||||
};
|
||||
} else {
|
||||
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)],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
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++) {
|
||||
if (!commands[i].string && commands[i].cmd) {
|
||||
switch (commands[i].cmd) {
|
||||
case 'or':
|
||||
case 'Or':
|
||||
case 'OR':
|
||||
case '|':
|
||||
case '||':
|
||||
{
|
||||
const op1 = commands[i - 1];
|
||||
const op2 = commands[i + 1];
|
||||
commands[i] = {
|
||||
$or: [op1, op2],
|
||||
};
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case 'and':
|
||||
case 'And':
|
||||
case 'AND':
|
||||
case '&':
|
||||
case '&&':
|
||||
{
|
||||
const op1 = commands[i - 1];
|
||||
const op2 = commands[i + 1];
|
||||
commands[i] = {
|
||||
$and: [op1, op2],
|
||||
};
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case 'not':
|
||||
case 'Not':
|
||||
case 'NOT':
|
||||
case '!':
|
||||
{
|
||||
const op1 = commands[i + 1];
|
||||
commands[i] = {
|
||||
$not: op1,
|
||||
};
|
||||
commands.splice(i + 1, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case 'or':
|
||||
case 'Or':
|
||||
case 'OR':
|
||||
case '|':
|
||||
case '||': {
|
||||
const op1 = commands[i - 1];
|
||||
const op2 = commands[i + 1];
|
||||
commands[i] = {
|
||||
$or: [op1, op2],
|
||||
};
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case 'and':
|
||||
case 'And':
|
||||
case 'AND':
|
||||
case '&':
|
||||
case '&&': {
|
||||
const op1 = commands[i - 1];
|
||||
const op2 = commands[i + 1];
|
||||
commands[i] = {
|
||||
$and: [op1, op2],
|
||||
};
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case 'not':
|
||||
case 'Not':
|
||||
case 'NOT':
|
||||
case '!': {
|
||||
const op1 = commands[i + 1];
|
||||
commands[i] = {
|
||||
$not: op1,
|
||||
};
|
||||
commands.splice(i + 1, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -452,7 +439,6 @@ class AdvancedFilter {
|
|||
const commands = this._filterToCommands();
|
||||
return this._arrayToSelector(commands);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The global Filter object.
|
||||
|
@ -477,23 +463,26 @@ Filter = {
|
|||
_exceptionsDep: new Tracker.Dependency(),
|
||||
|
||||
isActive() {
|
||||
return _.any(this._fields, (fieldName) => {
|
||||
return this[fieldName]._isActive();
|
||||
}) || this.advanced._isActive();
|
||||
return (
|
||||
_.any(this._fields, fieldName => {
|
||||
return this[fieldName]._isActive();
|
||||
}) || this.advanced._isActive()
|
||||
);
|
||||
},
|
||||
|
||||
_getMongoSelector() {
|
||||
if (!this.isActive())
|
||||
return {};
|
||||
if (!this.isActive()) return {};
|
||||
|
||||
const filterSelector = {};
|
||||
const emptySelector = {};
|
||||
let includeEmptySelectors = false;
|
||||
this._fields.forEach((fieldName) => {
|
||||
this._fields.forEach(fieldName => {
|
||||
const filter = this[fieldName];
|
||||
if (filter._isActive()) {
|
||||
if (filter.subField !== '') {
|
||||
filterSelector[`${fieldName}.${filter.subField}`] = filter._getMongoSelector();
|
||||
filterSelector[
|
||||
`${fieldName}.${filter.subField}`
|
||||
] = filter._getMongoSelector();
|
||||
} else {
|
||||
filterSelector[fieldName] = filter._getMongoSelector();
|
||||
}
|
||||
|
@ -513,11 +502,15 @@ Filter = {
|
|||
|
||||
const selectors = [exceptionsSelector];
|
||||
|
||||
if (_.any(this._fields, (fieldName) => {
|
||||
return this[fieldName]._isActive();
|
||||
})) selectors.push(filterSelector);
|
||||
if (
|
||||
_.any(this._fields, fieldName => {
|
||||
return this[fieldName]._isActive();
|
||||
})
|
||||
)
|
||||
selectors.push(filterSelector);
|
||||
if (includeEmptySelectors) selectors.push(emptySelector);
|
||||
if (this.advanced._isActive()) selectors.push(this.advanced._getMongoSelector());
|
||||
if (this.advanced._isActive())
|
||||
selectors.push(this.advanced._getMongoSelector());
|
||||
|
||||
return {
|
||||
$or: selectors,
|
||||
|
@ -526,8 +519,7 @@ Filter = {
|
|||
|
||||
mongoSelector(additionalSelector) {
|
||||
const filterSelector = this._getMongoSelector();
|
||||
if (_.isUndefined(additionalSelector))
|
||||
return filterSelector;
|
||||
if (_.isUndefined(additionalSelector)) return filterSelector;
|
||||
else
|
||||
return {
|
||||
$and: [filterSelector, additionalSelector],
|
||||
|
@ -535,7 +527,7 @@ Filter = {
|
|||
},
|
||||
|
||||
reset() {
|
||||
this._fields.forEach((fieldName) => {
|
||||
this._fields.forEach(fieldName => {
|
||||
const filter = this[fieldName];
|
||||
filter.reset();
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ Meteor.startup(() => {
|
|||
}
|
||||
|
||||
if (!language) {
|
||||
if(navigator.languages) {
|
||||
if (navigator.languages) {
|
||||
language = navigator.languages[0];
|
||||
} else {
|
||||
language = navigator.language || navigator.userLanguage;
|
||||
|
|
|
@ -48,35 +48,43 @@ InlinedForm = BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-close-inlined-form': this.close,
|
||||
'click .js-open-inlined-form': this.open,
|
||||
return [
|
||||
{
|
||||
'click .js-close-inlined-form': this.close,
|
||||
'click .js-open-inlined-form': this.open,
|
||||
|
||||
// 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();
|
||||
}
|
||||
},
|
||||
// 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();
|
||||
}
|
||||
},
|
||||
|
||||
// Close the inlined form when after its submission
|
||||
submit() {
|
||||
if (this.currentData().autoclose !== false) {
|
||||
Tracker.afterFlush(() => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
// Close the inlined form when after its submission
|
||||
submit() {
|
||||
if (this.currentData().autoclose !== false) {
|
||||
Tracker.afterFlush(() => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('inlinedForm');
|
||||
|
||||
// Press escape to close the currently opened inlinedForm
|
||||
EscapeActions.register('inlinedForm',
|
||||
() => { currentlyOpenedForm.get().close(); },
|
||||
() => { return currentlyOpenedForm.get() !== null; }, {
|
||||
EscapeActions.register(
|
||||
'inlinedForm',
|
||||
() => {
|
||||
currentlyOpenedForm.get().close();
|
||||
},
|
||||
() => {
|
||||
return currentlyOpenedForm.get() !== null;
|
||||
},
|
||||
{
|
||||
enabledOnClick: false,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// submit on click outside
|
||||
|
|
|
@ -40,8 +40,10 @@ Mousetrap.bind(['down', 'up'], (evt, key) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const nextFunc = (key === 'down' ? 'next' : 'prev');
|
||||
const nextCard = $('.js-minicard.is-selected')[nextFunc]('.js-minicard').get(0);
|
||||
const nextFunc = key === 'down' ? 'next' : 'prev';
|
||||
const nextCard = $('.js-minicard.is-selected')
|
||||
[nextFunc]('.js-minicard')
|
||||
.get(0);
|
||||
if (nextCard) {
|
||||
const nextCardId = Blaze.getData(nextCard)._id;
|
||||
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
|
||||
Mousetrap.bind('space', (evt) => {
|
||||
Mousetrap.bind('space', evt => {
|
||||
if (!Session.get('currentCard')) {
|
||||
return;
|
||||
}
|
||||
|
@ -69,29 +71,38 @@ Mousetrap.bind('space', (evt) => {
|
|||
});
|
||||
|
||||
Template.keyboardShortcuts.helpers({
|
||||
mapping: [{
|
||||
keys: ['W'],
|
||||
action: 'shortcut-toggle-sidebar',
|
||||
}, {
|
||||
keys: ['Q'],
|
||||
action: 'shortcut-filter-my-cards',
|
||||
}, {
|
||||
keys: ['F'],
|
||||
action: 'shortcut-toggle-filterbar',
|
||||
}, {
|
||||
keys: ['X'],
|
||||
action: 'shortcut-clear-filters',
|
||||
}, {
|
||||
keys: ['?'],
|
||||
action: 'shortcut-show-shortcuts',
|
||||
}, {
|
||||
keys: ['ESC'],
|
||||
action: 'shortcut-close-dialog',
|
||||
}, {
|
||||
keys: ['@'],
|
||||
action: 'shortcut-autocomplete-members',
|
||||
}, {
|
||||
keys: ['SPACE'],
|
||||
action: 'shortcut-assign-self',
|
||||
}],
|
||||
mapping: [
|
||||
{
|
||||
keys: ['W'],
|
||||
action: 'shortcut-toggle-sidebar',
|
||||
},
|
||||
{
|
||||
keys: ['Q'],
|
||||
action: 'shortcut-filter-my-cards',
|
||||
},
|
||||
{
|
||||
keys: ['F'],
|
||||
action: 'shortcut-toggle-filterbar',
|
||||
},
|
||||
{
|
||||
keys: ['X'],
|
||||
action: 'shortcut-clear-filters',
|
||||
},
|
||||
{
|
||||
keys: ['?'],
|
||||
action: 'shortcut-show-shortcuts',
|
||||
},
|
||||
{
|
||||
keys: ['ESC'],
|
||||
action: 'shortcut-close-dialog',
|
||||
},
|
||||
{
|
||||
keys: ['@'],
|
||||
action: 'shortcut-autocomplete-members',
|
||||
},
|
||||
{
|
||||
keys: ['SPACE'],
|
||||
action: 'shortcut-assign-self',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const closedValue = null;
|
||||
|
||||
window.Modal = new class {
|
||||
window.Modal = new (class {
|
||||
constructor() {
|
||||
this._currentModal = new ReactiveVar(closedValue);
|
||||
this._onCloseGoTo = '';
|
||||
|
@ -21,7 +21,7 @@ window.Modal = new class {
|
|||
return this.getTemplateName() !== closedValue;
|
||||
}
|
||||
|
||||
isWide(){
|
||||
isWide() {
|
||||
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._onCloseGoTo = onCloseGoTo;
|
||||
this._isWideModal = true;
|
||||
}
|
||||
|
||||
open(modalName, { header = '', onCloseGoTo = ''} = {}) {
|
||||
open(modalName, { header = '', onCloseGoTo = '' } = {}) {
|
||||
this._currentModal.set({ header, modalName });
|
||||
this._onCloseGoTo = onCloseGoTo;
|
||||
|
||||
}
|
||||
}();
|
||||
})();
|
||||
|
||||
Blaze.registerHelper('Modal', Modal);
|
||||
|
||||
EscapeActions.register('modalWindow',
|
||||
EscapeActions.register(
|
||||
'modalWindow',
|
||||
() => Modal.close(),
|
||||
() => Modal.isOpen(),
|
||||
{ noClickEscapeOn: '.modal-container' }
|
||||
{ noClickEscapeOn: '.modal-container' },
|
||||
);
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
function getCardsBetween(idA, idB) {
|
||||
|
||||
function pluckId(doc) {
|
||||
return doc._id;
|
||||
}
|
||||
|
@ -15,7 +13,7 @@ function getCardsBetween(idA, idB) {
|
|||
}).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;
|
||||
});
|
||||
|
||||
|
@ -31,17 +29,21 @@ function getCardsBetween(idA, idB) {
|
|||
};
|
||||
} else {
|
||||
selector = {
|
||||
$or: [{
|
||||
listId: cards[0].listId,
|
||||
sort: { $lte: cards[0].sort },
|
||||
}, {
|
||||
listId: {
|
||||
$in: getListsStrictlyBetween(cards[0].listId, cards[1].listId),
|
||||
$or: [
|
||||
{
|
||||
listId: cards[0].listId,
|
||||
sort: { $lte: cards[0].sort },
|
||||
},
|
||||
}, {
|
||||
listId: cards[1].listId,
|
||||
sort: { $gte: cards[1].sort },
|
||||
}],
|
||||
{
|
||||
listId: {
|
||||
$in: getListsStrictlyBetween(cards[0].listId, cards[1].listId),
|
||||
},
|
||||
},
|
||||
{
|
||||
listId: cards[1].listId,
|
||||
sort: { $gte: cards[1].sort },
|
||||
},
|
||||
],
|
||||
archived: false,
|
||||
};
|
||||
}
|
||||
|
@ -133,14 +135,12 @@ MultiSelection = {
|
|||
|
||||
const selectedCards = this._selectedCards.get();
|
||||
|
||||
cardIds.forEach((cardId) => {
|
||||
cardIds.forEach(cardId => {
|
||||
const indexOfCard = selectedCards.indexOf(cardId);
|
||||
|
||||
if (options.remove && indexOfCard > -1)
|
||||
selectedCards.splice(indexOfCard, 1);
|
||||
|
||||
else if (options.add)
|
||||
selectedCards.push(cardId);
|
||||
else if (options.add) selectedCards.push(cardId);
|
||||
});
|
||||
|
||||
this._selectedCards.set(selectedCards);
|
||||
|
@ -153,9 +153,15 @@ MultiSelection = {
|
|||
|
||||
Blaze.registerHelper('MultiSelection', MultiSelection);
|
||||
|
||||
EscapeActions.register('multiselection',
|
||||
() => { MultiSelection.disable(); },
|
||||
() => { return MultiSelection.isActive(); }, {
|
||||
EscapeActions.register(
|
||||
'multiselection',
|
||||
() => {
|
||||
MultiSelection.disable();
|
||||
},
|
||||
() => {
|
||||
return MultiSelection.isActive();
|
||||
},
|
||||
{
|
||||
noClickEscapeOn: '.js-minicard,.js-board-sidebar-content',
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
$.event.fix = (function(originalFix) {
|
||||
return function(event) {
|
||||
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;
|
||||
}
|
||||
return event;
|
||||
|
@ -23,7 +26,7 @@
|
|||
matchType: /image.*/,
|
||||
};
|
||||
|
||||
return $.fn.pasteImageReader = function(options) {
|
||||
return ($.fn.pasteImageReader = function(options) {
|
||||
if (typeof options === 'function') {
|
||||
options = {
|
||||
callback: options,
|
||||
|
@ -35,8 +38,11 @@
|
|||
return $(element).bind('paste', function(event) {
|
||||
const types = event.clipboardData.types;
|
||||
const items = event.clipboardData.items;
|
||||
for(let i=0; i<types.length; i++) {
|
||||
if(types[i].match(options.matchType) || items[i].type.match(options.matchType)) {
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
if (
|
||||
types[i].match(options.matchType) ||
|
||||
items[i].type.match(options.matchType)
|
||||
) {
|
||||
const f = items[i].getAsFile();
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(evt) {
|
||||
|
@ -53,5 +59,5 @@
|
|||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
})(jQuery);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
window.Popup = new class {
|
||||
window.Popup = new (class {
|
||||
constructor() {
|
||||
// The template we use to render popups
|
||||
this.template = Template.popup;
|
||||
|
@ -67,7 +67,7 @@ window.Popup = new class {
|
|||
title: self._getTitle(popupName),
|
||||
depth: self._stack.length,
|
||||
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
|
||||
|
@ -80,11 +80,14 @@ window.Popup = new class {
|
|||
// our internal dependency, and since we just changed the top element of
|
||||
// our internal stack, the popup will be updated with the new data.
|
||||
if (!self.isOpen()) {
|
||||
self.current = Blaze.renderWithData(self.template, () => {
|
||||
self._dep.depend();
|
||||
return { ...self._getTopStack(), stack: self._stack };
|
||||
}, document.body);
|
||||
|
||||
self.current = Blaze.renderWithData(
|
||||
self.template,
|
||||
() => {
|
||||
self._dep.depend();
|
||||
return { ...self._getTopStack(), stack: self._stack };
|
||||
},
|
||||
document.body,
|
||||
);
|
||||
} else {
|
||||
self._dep.changed();
|
||||
}
|
||||
|
@ -101,7 +104,7 @@ window.Popup = new class {
|
|||
const self = this;
|
||||
|
||||
return function(evt, tpl) {
|
||||
const context = this.currentData && this.currentData() || this;
|
||||
const context = (this.currentData && this.currentData()) || this;
|
||||
context.__afterConfirmAction = action;
|
||||
self.open(name).call(context, evt, tpl);
|
||||
};
|
||||
|
@ -136,7 +139,6 @@ window.Popup = new class {
|
|||
const openerElement = this._getTopStack().openerElement;
|
||||
$(openerElement).removeClass('is-active');
|
||||
|
||||
|
||||
this._stack = [];
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +161,7 @@ window.Popup = new class {
|
|||
return () => {
|
||||
Utils.windowResizeDep.depend();
|
||||
|
||||
if(Utils.isMiniScreen()) return { left:0, top:0 };
|
||||
if (Utils.isMiniScreen()) return { left: 0, top: 0 };
|
||||
|
||||
const offset = $element.offset();
|
||||
const popupWidth = 300 + 15;
|
||||
|
@ -188,18 +190,19 @@ window.Popup = new class {
|
|||
return title !== translationKey ? title : defaultTitle;
|
||||
};
|
||||
}
|
||||
}();
|
||||
})();
|
||||
|
||||
// We close a potential opened popup on any left click on the document, or go
|
||||
// one step back by pressing escape.
|
||||
const escapeActions = ['back', 'close'];
|
||||
escapeActions.forEach((actionName) => {
|
||||
EscapeActions.register(`popup-${actionName}`,
|
||||
escapeActions.forEach(actionName => {
|
||||
EscapeActions.register(
|
||||
`popup-${actionName}`,
|
||||
() => Popup[actionName](),
|
||||
() => Popup.isOpen(),
|
||||
{
|
||||
noClickEscapeOn: '.js-pop-over,.js-open-card-title-popup',
|
||||
enabledOnClick: actionName === 'close',
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
@ -47,9 +47,6 @@ $.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) {
|
|||
});
|
||||
};
|
||||
|
||||
EscapeActions.register('textcomplete',
|
||||
() => {},
|
||||
() => dropdownMenuIsOpened, {
|
||||
noClickEscapeOn: '.textcomplete-dropdown',
|
||||
}
|
||||
);
|
||||
EscapeActions.register('textcomplete', () => {}, () => dropdownMenuIsOpened, {
|
||||
noClickEscapeOn: '.textcomplete-dropdown',
|
||||
});
|
||||
|
|
|
@ -34,13 +34,13 @@ UnsavedEdits = {
|
|||
},
|
||||
|
||||
has({ fieldName, docId }) {
|
||||
return Boolean(this.get({fieldName, docId}));
|
||||
return Boolean(this.get({ fieldName, docId }));
|
||||
},
|
||||
|
||||
set({ fieldName, docId }, value) {
|
||||
const currentDoc = this._getCollectionDocument(fieldName, docId);
|
||||
if (currentDoc) {
|
||||
UnsavedEditCollection.update(currentDoc._id, { $set: { value }});
|
||||
UnsavedEditCollection.update(currentDoc._id, { $set: { value } });
|
||||
} else {
|
||||
UnsavedEditCollection.insert({
|
||||
fieldName,
|
||||
|
@ -58,7 +58,7 @@ UnsavedEdits = {
|
|||
},
|
||||
|
||||
_getCollectionDocument(fieldName, docId) {
|
||||
return UnsavedEditCollection.findOne({fieldName, docId});
|
||||
return UnsavedEditCollection.findOne({ fieldName, docId });
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -2,20 +2,26 @@ Utils = {
|
|||
// XXX We should remove these two methods
|
||||
goBoardId(_id) {
|
||||
const board = Boards.findOne(_id);
|
||||
return board && FlowRouter.go('board', {
|
||||
id: board._id,
|
||||
slug: board.slug,
|
||||
});
|
||||
return (
|
||||
board &&
|
||||
FlowRouter.go('board', {
|
||||
id: board._id,
|
||||
slug: board.slug,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
goCardId(_id) {
|
||||
const card = Cards.findOne(_id);
|
||||
const board = Boards.findOne(card.boardId);
|
||||
return board && FlowRouter.go('card', {
|
||||
cardId: card._id,
|
||||
boardId: board._id,
|
||||
slug: board.slug,
|
||||
});
|
||||
return (
|
||||
board &&
|
||||
FlowRouter.go('card', {
|
||||
cardId: card._id,
|
||||
boardId: board._id,
|
||||
slug: board.slug,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
capitalize(string) {
|
||||
|
@ -104,13 +110,21 @@ Utils = {
|
|||
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;
|
||||
}
|
||||
|
||||
// include the 'heartz' as a way to have a non matching MQ to help terminate the join
|
||||
// https://git.io/vznFH
|
||||
const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
|
||||
const query = [
|
||||
'(',
|
||||
prefixes.join('touch-enabled),('),
|
||||
'heartz',
|
||||
')',
|
||||
].join('');
|
||||
return mq(query);
|
||||
})();
|
||||
Utils.isTouchDevice = () => isTouchable;
|
||||
|
@ -120,7 +134,7 @@ Utils = {
|
|||
calculateTouchDistance(touchA, touchB) {
|
||||
return Math.sqrt(
|
||||
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];
|
||||
});
|
||||
$(document).on('touchend', selector, function(e) {
|
||||
if (touchStart && lastTouch && Utils.calculateTouchDistance(touchStart, lastTouch) <= 20) {
|
||||
if (
|
||||
touchStart &&
|
||||
lastTouch &&
|
||||
Utils.calculateTouchDistance(touchStart, lastTouch) <= 20
|
||||
) {
|
||||
e.preventDefault();
|
||||
const clickEvent = document.createEvent('MouseEvents');
|
||||
clickEvent.initEvent('click', true, true);
|
||||
|
@ -145,30 +163,30 @@ Utils = {
|
|||
});
|
||||
},
|
||||
|
||||
manageCustomUI(){
|
||||
manageCustomUI() {
|
||||
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
|
||||
}
|
||||
if (!err){
|
||||
if (!err) {
|
||||
Utils.setCustomUI(data);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setCustomUI(data){
|
||||
setCustomUI(data) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (currentBoard) {
|
||||
DocHead.setTitle(`${currentBoard.title } - ${ data.productName}`);
|
||||
DocHead.setTitle(`${currentBoard.title} - ${data.productName}`);
|
||||
} else {
|
||||
DocHead.setTitle(`${data.productName}`);
|
||||
}
|
||||
},
|
||||
|
||||
setMatomo(data){
|
||||
setMatomo(data) {
|
||||
window._paq = window._paq || [];
|
||||
window._paq.push(['setDoNotTrack', data.doNotTrack]);
|
||||
if (data.withUserName){
|
||||
if (data.withUserName) {
|
||||
window._paq.push(['setUserId', Meteor.user().username]);
|
||||
}
|
||||
window._paq.push(['trackPageView']);
|
||||
|
@ -196,12 +214,12 @@ Utils = {
|
|||
|
||||
manageMatomo() {
|
||||
const matomo = Session.get('matomo');
|
||||
if (matomo === undefined){
|
||||
if (matomo === undefined) {
|
||||
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
|
||||
}
|
||||
if (!err){
|
||||
if (!err) {
|
||||
Utils.setMatomo(data);
|
||||
}
|
||||
});
|
||||
|
@ -220,15 +238,21 @@ Utils = {
|
|||
finalString += element.text().toLowerCase();
|
||||
} else if (element.hasClass('user-details')) {
|
||||
let username = element.find('input').val();
|
||||
if(username === undefined || username === ''){
|
||||
if (username === undefined || 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) {
|
||||
finalString += element.find('select option:selected').text().toLowerCase();
|
||||
finalString += element
|
||||
.find('select option:selected')
|
||||
.text()
|
||||
.toLowerCase();
|
||||
} else if (element.find('input').length > 0) {
|
||||
let inputvalue = element.find('input').val();
|
||||
if(inputvalue === undefined || inputvalue === ''){
|
||||
if (inputvalue === undefined || inputvalue === '') {
|
||||
inputvalue = '*';
|
||||
}
|
||||
finalString += inputvalue;
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
const passwordField = AccountsTemplates.removeField('password');
|
||||
const emailField = AccountsTemplates.removeField('email');
|
||||
|
||||
AccountsTemplates.addFields([{
|
||||
_id: 'username',
|
||||
type: 'text',
|
||||
displayName: 'username',
|
||||
required: true,
|
||||
minLength: 2,
|
||||
}, emailField, passwordField, {
|
||||
_id: 'invitationcode',
|
||||
type: 'text',
|
||||
displayName: 'Invitation Code',
|
||||
required: false,
|
||||
minLength: 6,
|
||||
template: 'invitationCode',
|
||||
}]);
|
||||
AccountsTemplates.addFields([
|
||||
{
|
||||
_id: 'username',
|
||||
type: 'text',
|
||||
displayName: 'username',
|
||||
required: true,
|
||||
minLength: 2,
|
||||
},
|
||||
emailField,
|
||||
passwordField,
|
||||
{
|
||||
_id: 'invitationcode',
|
||||
type: 'text',
|
||||
displayName: 'Invitation Code',
|
||||
required: false,
|
||||
minLength: 6,
|
||||
template: 'invitationCode',
|
||||
},
|
||||
]);
|
||||
|
||||
AccountsTemplates.configure({
|
||||
defaultLayout: 'userFormsLayout',
|
||||
|
@ -34,7 +39,8 @@ AccountsTemplates.configure({
|
|||
});
|
||||
|
||||
['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
|
||||
// have a title, so we unset the title automatically displayed by useraccounts.
|
||||
|
@ -56,15 +62,25 @@ AccountsTemplates.configureRoute('changePwd', {
|
|||
});
|
||||
|
||||
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('-');
|
||||
Accounts.emailTemplates[templateName][field] = (user, url) => {
|
||||
return TAPi18n.__(`email-${str}`, {
|
||||
url,
|
||||
user: user.getName(),
|
||||
siteName: Accounts.emailTemplates.siteName,
|
||||
}, user.getLanguage());
|
||||
return TAPi18n.__(
|
||||
`email-${str}`,
|
||||
{
|
||||
url,
|
||||
user: user.getName(),
|
||||
siteName: Accounts.emailTemplates.siteName,
|
||||
},
|
||||
user.getLanguage(),
|
||||
);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
let previousPath;
|
||||
FlowRouter.triggers.exit([({path}) => {
|
||||
previousPath = path;
|
||||
}]);
|
||||
FlowRouter.triggers.exit([
|
||||
({ path }) => {
|
||||
previousPath = path;
|
||||
},
|
||||
]);
|
||||
|
||||
FlowRouter.route('/', {
|
||||
name: 'home',
|
||||
|
@ -192,9 +194,11 @@ const redirections = {
|
|||
|
||||
_.each(redirections, (newPath, oldPath) => {
|
||||
FlowRouter.route(oldPath, {
|
||||
triggersEnter: [(context, redirect) => {
|
||||
redirect(FlowRouter.path(newPath, context.params));
|
||||
}],
|
||||
triggersEnter: [
|
||||
(context, redirect) => {
|
||||
redirect(FlowRouter.path(newPath, context.params));
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ AccountSettings.attachSchema(
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
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) {
|
||||
Meteor.startup(() => {
|
||||
AccountSettings._collection._ensureIndex({ modifiedAt: -1 });
|
||||
|
@ -62,7 +57,7 @@ if (Meteor.isServer) {
|
|||
booleanValue: false,
|
||||
sort: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
AccountSettings.upsert(
|
||||
{ _id: 'accounts-allowUserNameChange' },
|
||||
|
@ -71,7 +66,7 @@ if (Meteor.isServer) {
|
|||
booleanValue: false,
|
||||
sort: 1,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
Meteor.startup(() => {
|
||||
Actions._collection._ensureIndex({ modifiedAt: -1 });
|
||||
|
|
|
@ -71,11 +71,6 @@ Activities.after.insert((userId, doc) => {
|
|||
RulesHelper.executeRules(activity);
|
||||
});
|
||||
|
||||
Activities.before.update((userId, doc, fieldNames, modifier, options) => {
|
||||
modifier.$set = modifier.$set || {};
|
||||
modifier.$set.modifiedAt = Date.now();
|
||||
});
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// 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
|
||||
|
@ -87,15 +82,15 @@ if (Meteor.isServer) {
|
|||
Activities._collection._ensureIndex({ boardId: 1, createdAt: -1 });
|
||||
Activities._collection._ensureIndex(
|
||||
{ commentId: 1 },
|
||||
{ partialFilterExpression: { commentId: { $exists: true } } }
|
||||
{ partialFilterExpression: { commentId: { $exists: true } } },
|
||||
);
|
||||
Activities._collection._ensureIndex(
|
||||
{ attachmentId: 1 },
|
||||
{ partialFilterExpression: { attachmentId: { $exists: true } } }
|
||||
{ partialFilterExpression: { attachmentId: { $exists: true } } },
|
||||
);
|
||||
Activities._collection._ensureIndex(
|
||||
{ customFieldId: 1 },
|
||||
{ partialFilterExpression: { customFieldId: { $exists: true } } }
|
||||
{ partialFilterExpression: { customFieldId: { $exists: true } } },
|
||||
);
|
||||
// Label activity did not work yet, unable to edit labels when tried this.
|
||||
//Activities._collection._dropIndex({ labelId: 1 }, { "indexKey": -1 });
|
||||
|
@ -205,20 +200,20 @@ if (Meteor.isServer) {
|
|||
if (board) {
|
||||
const watchingUsers = _.pluck(
|
||||
_.where(board.watchers, { level: 'watching' }),
|
||||
'userId'
|
||||
'userId',
|
||||
);
|
||||
const trackingUsers = _.pluck(
|
||||
_.where(board.watchers, { level: 'tracking' }),
|
||||
'userId'
|
||||
'userId',
|
||||
);
|
||||
watchers = _.union(
|
||||
watchers,
|
||||
watchingUsers,
|
||||
_.intersection(participants, trackingUsers)
|
||||
_.intersection(participants, trackingUsers),
|
||||
);
|
||||
}
|
||||
|
||||
Notifications.getUsers(watchers).forEach((user) => {
|
||||
Notifications.getUsers(watchers).forEach(user => {
|
||||
Notifications.notify(user, title, description, params);
|
||||
});
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ Announcements.attachSchema(
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
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) {
|
||||
Meteor.startup(() => {
|
||||
Announcements._collection._ensureIndex({ modifiedAt: -1 });
|
||||
|
|
|
@ -12,7 +12,7 @@ Attachments = new FS.Collection('attachments', {
|
|||
// XXX Should we use `beforeWrite` option of CollectionFS instead of
|
||||
// collection-hooks?
|
||||
// We should use `beforeWrite`.
|
||||
beforeWrite: (fileObj) => {
|
||||
beforeWrite: fileObj => {
|
||||
if (!fileObj.isImage()) {
|
||||
return {
|
||||
type: 'application/octet-stream',
|
||||
|
@ -84,7 +84,7 @@ if (Meteor.isServer) {
|
|||
$unset: {
|
||||
source: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -101,7 +101,7 @@ Boards.attachSchema(
|
|||
const colors = Boards.simpleSchema()._schema['labels.$.color']
|
||||
.allowedValues;
|
||||
const defaultLabelsColors = _.clone(colors).splice(0, 6);
|
||||
return defaultLabelsColors.map((color) => ({
|
||||
return defaultLabelsColors.map(color => ({
|
||||
color,
|
||||
_id: Random.id(6),
|
||||
name: '',
|
||||
|
@ -342,7 +342,7 @@ Boards.attachSchema(
|
|||
type: String,
|
||||
defaultValue: 'board',
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Boards.helpers({
|
||||
|
@ -355,7 +355,7 @@ Boards.helpers({
|
|||
Swimlanes.find({
|
||||
boardId: oldId,
|
||||
archived: false,
|
||||
}).forEach((swimlane) => {
|
||||
}).forEach(swimlane => {
|
||||
swimlane.type = 'swimlane';
|
||||
swimlane.copy(_id);
|
||||
});
|
||||
|
@ -382,7 +382,7 @@ Boards.helpers({
|
|||
isActiveMember(userId) {
|
||||
if (userId) {
|
||||
return this.members.find(
|
||||
(member) => member.userId === userId && member.isActive
|
||||
member => member.userId === userId && member.isActive,
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
|
@ -396,14 +396,14 @@ Boards.helpers({
|
|||
cards() {
|
||||
return Cards.find(
|
||||
{ boardId: this._id, archived: false },
|
||||
{ sort: { title: 1 } }
|
||||
{ sort: { title: 1 } },
|
||||
);
|
||||
},
|
||||
|
||||
lists() {
|
||||
return Lists.find(
|
||||
{ boardId: this._id, archived: false },
|
||||
{ sort: { sort: 1 } }
|
||||
{ sort: { sort: 1 } },
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -418,7 +418,7 @@ Boards.helpers({
|
|||
swimlanes() {
|
||||
return Swimlanes.find(
|
||||
{ boardId: this._id, archived: false },
|
||||
{ sort: { sort: 1 } }
|
||||
{ sort: { sort: 1 } },
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -432,7 +432,7 @@ Boards.helpers({
|
|||
},
|
||||
{
|
||||
sort: { sort: 1 },
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -535,7 +535,7 @@ Boards.helpers({
|
|||
customFields() {
|
||||
return CustomFields.find(
|
||||
{ boardIds: { $in: [this._id] } },
|
||||
{ sort: { name: 1 } }
|
||||
{ sort: { name: 1 } },
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -848,7 +848,7 @@ Boards.mutations({
|
|||
isAdmin,
|
||||
isNoComments,
|
||||
isCommentOnly,
|
||||
currentUserId = Meteor.userId()
|
||||
currentUserId = Meteor.userId(),
|
||||
) {
|
||||
const memberIndex = this.memberIndex(memberId);
|
||||
// do not allow change permission of self
|
||||
|
@ -884,9 +884,9 @@ Boards.mutations({
|
|||
|
||||
function boardRemover(userId, doc) {
|
||||
[Cards, Lists, Swimlanes, Integrations, Rules, Activities].forEach(
|
||||
(element) => {
|
||||
element => {
|
||||
element.remove({ boardId: doc._id });
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -927,7 +927,7 @@ if (Meteor.isServer) {
|
|||
_.findWhere(doc.members, {
|
||||
userId: removedMemberId,
|
||||
isAdmin: true,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
fetch: ['members'],
|
||||
|
@ -973,7 +973,7 @@ if (Meteor.isServer) {
|
|||
_id: 1,
|
||||
'members.userId': 1,
|
||||
},
|
||||
{ unique: true }
|
||||
{ unique: true },
|
||||
);
|
||||
Boards._collection._ensureIndex({ 'members.userId': 1 });
|
||||
});
|
||||
|
@ -1009,12 +1009,12 @@ if (Meteor.isServer) {
|
|||
labelIds: removedLabelId,
|
||||
},
|
||||
},
|
||||
{ multi: true }
|
||||
{ multi: true },
|
||||
);
|
||||
});
|
||||
|
||||
const foreachRemovedMember = (doc, modifier, callback) => {
|
||||
Object.keys(modifier).forEach((set) => {
|
||||
Object.keys(modifier).forEach(set => {
|
||||
if (modifier[set] !== false) {
|
||||
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
|
||||
Boards.before.update((userId, doc, fieldNames, modifier) => {
|
||||
if (!_.contains(fieldNames, 'members')) {
|
||||
|
@ -1043,7 +1038,7 @@ if (Meteor.isServer) {
|
|||
|
||||
if (modifier.$set) {
|
||||
const boardId = doc._id;
|
||||
foreachRemovedMember(doc, modifier.$set, (memberId) => {
|
||||
foreachRemovedMember(doc, modifier.$set, memberId => {
|
||||
Cards.update(
|
||||
{ boardId },
|
||||
{
|
||||
|
@ -1052,7 +1047,7 @@ if (Meteor.isServer) {
|
|||
watchers: memberId,
|
||||
},
|
||||
},
|
||||
{ multi: true }
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
Lists.update(
|
||||
|
@ -1062,7 +1057,7 @@ if (Meteor.isServer) {
|
|||
watchers: memberId,
|
||||
},
|
||||
},
|
||||
{ multi: true }
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
const board = Boards._transform(doc);
|
||||
|
@ -1112,7 +1107,7 @@ if (Meteor.isServer) {
|
|||
|
||||
// Say goodbye to the former member
|
||||
if (modifier.$set) {
|
||||
foreachRemovedMember(doc, modifier.$set, (memberId) => {
|
||||
foreachRemovedMember(doc, modifier.$set, memberId => {
|
||||
Activities.insert({
|
||||
userId,
|
||||
memberId,
|
||||
|
@ -1143,7 +1138,7 @@ if (Meteor.isServer) {
|
|||
// admins can access boards of any user
|
||||
Authentication.checkAdminOrCondition(
|
||||
req.userId,
|
||||
req.userId === paramUserId
|
||||
req.userId === paramUserId,
|
||||
);
|
||||
|
||||
const data = Boards.find(
|
||||
|
@ -1153,7 +1148,7 @@ if (Meteor.isServer) {
|
|||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
}
|
||||
},
|
||||
).map(function(board) {
|
||||
return {
|
||||
_id: board._id,
|
||||
|
@ -1332,7 +1327,7 @@ if (Meteor.isServer) {
|
|||
if (!board.getLabel(name, color)) {
|
||||
Boards.direct.update(
|
||||
{ _id: id },
|
||||
{ $push: { labels: { _id: labelId, name, color } } }
|
||||
{ $push: { labels: { _id: labelId, name, color } } },
|
||||
);
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
|
@ -1364,7 +1359,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
const boardId = req.params.boardId;
|
||||
|
@ -1384,7 +1379,7 @@ if (Meteor.isServer) {
|
|||
isTrue(isAdmin),
|
||||
isTrue(isNoComments),
|
||||
isTrue(isCommentOnly),
|
||||
req.userId
|
||||
req.userId,
|
||||
);
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
|
|
|
@ -64,7 +64,7 @@ CardComments.attachSchema(
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
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) {
|
||||
// Comments are often fetched within a card, so we create an index to make these
|
||||
// queries more efficient.
|
||||
|
@ -125,7 +120,6 @@ if (Meteor.isServer) {
|
|||
});
|
||||
|
||||
CardComments.after.update((userId, doc) => {
|
||||
const activity = Activities.findOne({ commentId: doc._id });
|
||||
const card = Cards.findOne(doc.cardId);
|
||||
Activities.insert({
|
||||
userId,
|
||||
|
@ -139,7 +133,6 @@ if (Meteor.isServer) {
|
|||
});
|
||||
|
||||
CardComments.before.remove((userId, doc) => {
|
||||
const activity = Activities.findOne({ commentId: doc._id });
|
||||
const card = Cards.findOne(doc.cardId);
|
||||
Activities.insert({
|
||||
userId,
|
||||
|
@ -174,7 +167,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
Authentication.checkUserId(req.userId);
|
||||
|
@ -233,7 +226,7 @@ if (Meteor.isServer) {
|
|||
data: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -280,7 +273,7 @@ if (Meteor.isServer) {
|
|||
data: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -318,7 +311,7 @@ if (Meteor.isServer) {
|
|||
data: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
1554
models/cards.js
1554
models/cards.js
File diff suppressed because it is too large
Load diff
|
@ -61,7 +61,7 @@ ChecklistItems.attachSchema(
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
ChecklistItems.allow({
|
||||
|
@ -225,11 +225,6 @@ if (Meteor.isServer) {
|
|||
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) => {
|
||||
itemCreation(userId, doc);
|
||||
});
|
||||
|
@ -281,7 +276,7 @@ if (Meteor.isServer) {
|
|||
code: 500,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -308,13 +303,13 @@ if (Meteor.isServer) {
|
|||
if (req.body.hasOwnProperty('isFinished')) {
|
||||
ChecklistItems.direct.update(
|
||||
{ _id: paramItemId },
|
||||
{ $set: { isFinished: req.body.isFinished } }
|
||||
{ $set: { isFinished: req.body.isFinished } },
|
||||
);
|
||||
}
|
||||
if (req.body.hasOwnProperty('title')) {
|
||||
ChecklistItems.direct.update(
|
||||
{ _id: paramItemId },
|
||||
{ $set: { title: req.body.title } }
|
||||
{ $set: { title: req.body.title } },
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -324,7 +319,7 @@ if (Meteor.isServer) {
|
|||
_id: paramItemId,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -353,7 +348,7 @@ if (Meteor.isServer) {
|
|||
_id: paramItemId,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ Checklists.attachSchema(
|
|||
type: Number,
|
||||
decimal: true,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Checklists.helpers({
|
||||
|
@ -68,7 +68,7 @@ Checklists.helpers({
|
|||
this._id = null;
|
||||
this.cardId = newCardId;
|
||||
const newChecklistId = Checklists.insert(this);
|
||||
ChecklistItems.find({ checklistId: oldChecklistId }).forEach((item) => {
|
||||
ChecklistItems.find({ checklistId: oldChecklistId }).forEach(item => {
|
||||
item._id = null;
|
||||
item.checklistId = newChecklistId;
|
||||
item.cardId = newCardId;
|
||||
|
@ -84,7 +84,7 @@ Checklists.helpers({
|
|||
{
|
||||
checklistId: this._id,
|
||||
},
|
||||
{ sort: ['sort'] }
|
||||
{ sort: ['sort'] },
|
||||
);
|
||||
},
|
||||
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) => {
|
||||
const activities = Activities.find({ checklistId: doc._id });
|
||||
const card = Cards.findOne(doc.cardId);
|
||||
if (activities) {
|
||||
activities.forEach((activity) => {
|
||||
activities.forEach(activity => {
|
||||
Activities.remove(activity._id);
|
||||
});
|
||||
}
|
||||
|
@ -203,7 +198,7 @@ if (Meteor.isServer) {
|
|||
Authentication.checkUserId(req.userId);
|
||||
const paramCardId = req.params.cardId;
|
||||
const checklists = Checklists.find({ cardId: paramCardId }).map(function(
|
||||
doc
|
||||
doc,
|
||||
) {
|
||||
return {
|
||||
_id: doc._id,
|
||||
|
@ -220,7 +215,7 @@ if (Meteor.isServer) {
|
|||
code: 500,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -269,7 +264,7 @@ if (Meteor.isServer) {
|
|||
code: 500,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -313,7 +308,7 @@ if (Meteor.isServer) {
|
|||
code: 400,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -340,7 +335,7 @@ if (Meteor.isServer) {
|
|||
_id: paramChecklistId,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ CustomFields.attachSchema(
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
CustomFields.mutations({
|
||||
|
@ -118,7 +118,7 @@ CustomFields.allow({
|
|||
userId,
|
||||
Boards.find({
|
||||
_id: { $in: doc.boardIds },
|
||||
}).fetch()
|
||||
}).fetch(),
|
||||
);
|
||||
},
|
||||
update(userId, doc) {
|
||||
|
@ -126,7 +126,7 @@ CustomFields.allow({
|
|||
userId,
|
||||
Boards.find({
|
||||
_id: { $in: doc.boardIds },
|
||||
}).fetch()
|
||||
}).fetch(),
|
||||
);
|
||||
},
|
||||
remove(userId, doc) {
|
||||
|
@ -134,7 +134,7 @@ CustomFields.allow({
|
|||
userId,
|
||||
Boards.find({
|
||||
_id: { $in: doc.boardIds },
|
||||
}).fetch()
|
||||
}).fetch(),
|
||||
);
|
||||
},
|
||||
fetch: ['userId', 'boardIds'],
|
||||
|
@ -152,7 +152,7 @@ function customFieldCreation(userId, doc) {
|
|||
});
|
||||
}
|
||||
|
||||
function customFieldDeletion(userId, doc){
|
||||
function customFieldDeletion(userId, doc) {
|
||||
Activities.insert({
|
||||
userId,
|
||||
activityType: 'deleteCustomField',
|
||||
|
@ -163,7 +163,7 @@ function customFieldDeletion(userId, doc){
|
|||
|
||||
// This has some bug, it does not show edited customField value at Outgoing Webhook,
|
||||
// instead it shows undefined, and no listId and swimlaneId.
|
||||
function customFieldEdit(userId, doc){
|
||||
function customFieldEdit(userId, doc) {
|
||||
const card = Cards.findOne(doc.cardId);
|
||||
Activities.insert({
|
||||
userId,
|
||||
|
@ -185,17 +185,12 @@ if (Meteor.isServer) {
|
|||
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) => {
|
||||
if (_.contains(fieldNames, 'boardIds') && modifier.$pull) {
|
||||
Cards.update(
|
||||
{ boardId: modifier.$pull.boardIds, 'customFields._id': doc._id },
|
||||
{ $pull: { customFields: { _id: doc._id } } },
|
||||
{ multi: true }
|
||||
{ multi: true },
|
||||
);
|
||||
customFieldEdit(userId, doc);
|
||||
Activities.remove({
|
||||
|
@ -223,7 +218,7 @@ if (Meteor.isServer) {
|
|||
Cards.update(
|
||||
{ boardId: { $in: doc.boardIds }, '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(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
Authentication.checkUserId(req.userId);
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
@ -254,7 +249,7 @@ if (Meteor.isServer) {
|
|||
name: cf.name,
|
||||
type: cf.type,
|
||||
};
|
||||
}
|
||||
},
|
||||
),
|
||||
});
|
||||
});
|
||||
|
@ -281,7 +276,7 @@ if (Meteor.isServer) {
|
|||
boardIds: { $in: [paramBoardId] },
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -299,7 +294,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
Authentication.checkUserId(req.userId);
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
@ -351,7 +346,7 @@ if (Meteor.isServer) {
|
|||
_id: id,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
125
models/export.js
125
models/export.js
|
@ -57,7 +57,10 @@ export class Exporter {
|
|||
|
||||
build() {
|
||||
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
|
||||
const noBoardId = {
|
||||
fields: {
|
||||
|
@ -67,15 +70,21 @@ export class Exporter {
|
|||
const result = {
|
||||
_format: 'wekan-board-1.0.0',
|
||||
};
|
||||
_.extend(result, Boards.findOne(this._boardId, {
|
||||
fields: {
|
||||
stars: 0,
|
||||
},
|
||||
}));
|
||||
_.extend(
|
||||
result,
|
||||
Boards.findOne(this._boardId, {
|
||||
fields: {
|
||||
stars: 0,
|
||||
},
|
||||
}),
|
||||
);
|
||||
result.lists = Lists.find(byBoard, noBoardId).fetch();
|
||||
result.cards = Cards.find(byBoardNoLinked, 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.activities = Activities.find(byBoard, noBoardId).fetch();
|
||||
result.rules = Rules.find(byBoard, noBoardId).fetch();
|
||||
|
@ -84,24 +93,40 @@ export class Exporter {
|
|||
result.subtaskItems = [];
|
||||
result.triggers = [];
|
||||
result.actions = [];
|
||||
result.cards.forEach((card) => {
|
||||
result.checklists.push(...Checklists.find({
|
||||
cardId: card._id,
|
||||
}).fetch());
|
||||
result.checklistItems.push(...ChecklistItems.find({
|
||||
cardId: card._id,
|
||||
}).fetch());
|
||||
result.subtaskItems.push(...Cards.find({
|
||||
parentid: card._id,
|
||||
}).fetch());
|
||||
result.cards.forEach(card => {
|
||||
result.checklists.push(
|
||||
...Checklists.find({
|
||||
cardId: card._id,
|
||||
}).fetch(),
|
||||
);
|
||||
result.checklistItems.push(
|
||||
...ChecklistItems.find({
|
||||
cardId: card._id,
|
||||
}).fetch(),
|
||||
);
|
||||
result.subtaskItems.push(
|
||||
...Cards.find({
|
||||
parentid: card._id,
|
||||
}).fetch(),
|
||||
);
|
||||
});
|
||||
result.rules.forEach((rule) => {
|
||||
result.triggers.push(...Triggers.find({
|
||||
_id: rule.triggerId,
|
||||
}, noBoardId).fetch());
|
||||
result.actions.push(...Actions.find({
|
||||
_id: rule.actionId,
|
||||
}, noBoardId).fetch());
|
||||
result.rules.forEach(rule => {
|
||||
result.triggers.push(
|
||||
...Triggers.find(
|
||||
{
|
||||
_id: rule.triggerId,
|
||||
},
|
||||
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
|
||||
|
@ -122,43 +147,45 @@ export class Exporter {
|
|||
});
|
||||
};
|
||||
const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
|
||||
result.attachments = Attachments.find(byBoard).fetch().map((attachment) => {
|
||||
return {
|
||||
_id: attachment._id,
|
||||
cardId: attachment.cardId,
|
||||
// url: FlowRouter.url(attachment.url()),
|
||||
file: getBase64DataSync(attachment),
|
||||
name: attachment.original.name,
|
||||
type: attachment.original.type,
|
||||
};
|
||||
});
|
||||
result.attachments = Attachments.find(byBoard)
|
||||
.fetch()
|
||||
.map(attachment => {
|
||||
return {
|
||||
_id: attachment._id,
|
||||
cardId: attachment.cardId,
|
||||
// url: FlowRouter.url(attachment.url()),
|
||||
file: getBase64DataSync(attachment),
|
||||
name: attachment.original.name,
|
||||
type: attachment.original.type,
|
||||
};
|
||||
});
|
||||
|
||||
// we also have to export some user data - as the other elements only
|
||||
// include id but we have to be careful:
|
||||
// 1- only exports users that are linked somehow to that board
|
||||
// 2- do not export any sensitive information
|
||||
const users = {};
|
||||
result.members.forEach((member) => {
|
||||
result.members.forEach(member => {
|
||||
users[member.userId] = true;
|
||||
});
|
||||
result.lists.forEach((list) => {
|
||||
result.lists.forEach(list => {
|
||||
users[list.userId] = true;
|
||||
});
|
||||
result.cards.forEach((card) => {
|
||||
result.cards.forEach(card => {
|
||||
users[card.userId] = true;
|
||||
if (card.members) {
|
||||
card.members.forEach((memberId) => {
|
||||
card.members.forEach(memberId => {
|
||||
users[memberId] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
result.comments.forEach((comment) => {
|
||||
result.comments.forEach(comment => {
|
||||
users[comment.userId] = true;
|
||||
});
|
||||
result.activities.forEach((activity) => {
|
||||
result.activities.forEach(activity => {
|
||||
users[activity.userId] = true;
|
||||
});
|
||||
result.checklists.forEach((checklist) => {
|
||||
result.checklists.forEach(checklist => {
|
||||
users[checklist.userId] = true;
|
||||
});
|
||||
const byUserIds = {
|
||||
|
@ -177,13 +204,15 @@ export class Exporter {
|
|||
'profile.avatarUrl': 1,
|
||||
},
|
||||
};
|
||||
result.users = Users.find(byUserIds, userFields).fetch().map((user) => {
|
||||
// user avatar is stored as a relative url, we export absolute
|
||||
if ((user.profile || {}).avatarUrl) {
|
||||
user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
|
||||
}
|
||||
return user;
|
||||
});
|
||||
result.users = Users.find(byUserIds, userFields)
|
||||
.fetch()
|
||||
.map(user => {
|
||||
// user avatar is stored as a relative url, we export absolute
|
||||
if ((user.profile || {}).avatarUrl) {
|
||||
user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
|
||||
}
|
||||
return user;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TrelloCreator } from './trelloCreator';
|
||||
import { WekanCreator } from './wekanCreator';
|
||||
import {Exporter} from './export';
|
||||
import { Exporter } from './export';
|
||||
import wekanMembersMapper from './wekanmapper';
|
||||
|
||||
Meteor.methods({
|
||||
|
@ -11,12 +11,12 @@ Meteor.methods({
|
|||
check(currentBoard, Match.Maybe(String));
|
||||
let creator;
|
||||
switch (importSource) {
|
||||
case 'trello':
|
||||
creator = new TrelloCreator(data);
|
||||
break;
|
||||
case 'wekan':
|
||||
creator = new WekanCreator(data);
|
||||
break;
|
||||
case 'trello':
|
||||
creator = new TrelloCreator(data);
|
||||
break;
|
||||
case 'wekan':
|
||||
creator = new WekanCreator(data);
|
||||
break;
|
||||
}
|
||||
|
||||
// 1. check all parameters are ok from a syntax point of view
|
||||
|
@ -38,11 +38,9 @@ Meteor.methods({
|
|||
const data = exporter.build();
|
||||
const addData = {};
|
||||
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}`;
|
||||
return creator.create(data, currentBoardId);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -86,14 +86,9 @@ Integrations.attachSchema(
|
|||
*/
|
||||
type: String,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Integrations.before.update((userId, doc, fieldNames, modifier, options) => {
|
||||
modifier.$set = modifier.$set || {};
|
||||
modifier.$set.modifiedAt = Date.now();
|
||||
});
|
||||
|
||||
Integrations.allow({
|
||||
insert(userId, doc) {
|
||||
return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
|
||||
|
@ -123,7 +118,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
@ -131,7 +126,7 @@ if (Meteor.isServer) {
|
|||
|
||||
const data = Integrations.find(
|
||||
{ boardId: paramBoardId },
|
||||
{ fields: { token: 0 } }
|
||||
{ fields: { token: 0 } },
|
||||
).map(function(doc) {
|
||||
return doc;
|
||||
});
|
||||
|
@ -155,7 +150,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
@ -166,7 +161,7 @@ if (Meteor.isServer) {
|
|||
code: 200,
|
||||
data: Integrations.findOne(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ fields: { token: 0 } }
|
||||
{ fields: { token: 0 } },
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -187,7 +182,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/integrations', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
@ -228,7 +223,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('PUT', '/api/boards/:boardId/integrations/:intId', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
@ -239,35 +234,35 @@ if (Meteor.isServer) {
|
|||
const newEnabled = req.body.enabled;
|
||||
Integrations.direct.update(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ $set: { enabled: newEnabled } }
|
||||
{ $set: { enabled: newEnabled } },
|
||||
);
|
||||
}
|
||||
if (req.body.hasOwnProperty('title')) {
|
||||
const newTitle = req.body.title;
|
||||
Integrations.direct.update(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ $set: { title: newTitle } }
|
||||
{ $set: { title: newTitle } },
|
||||
);
|
||||
}
|
||||
if (req.body.hasOwnProperty('url')) {
|
||||
const newUrl = req.body.url;
|
||||
Integrations.direct.update(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ $set: { url: newUrl } }
|
||||
{ $set: { url: newUrl } },
|
||||
);
|
||||
}
|
||||
if (req.body.hasOwnProperty('token')) {
|
||||
const newToken = req.body.token;
|
||||
Integrations.direct.update(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ $set: { token: newToken } }
|
||||
{ $set: { token: newToken } },
|
||||
);
|
||||
}
|
||||
if (req.body.hasOwnProperty('activities')) {
|
||||
const newActivities = req.body.activities;
|
||||
Integrations.direct.update(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ $set: { activities: newActivities } }
|
||||
{ $set: { activities: newActivities } },
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -306,14 +301,14 @@ if (Meteor.isServer) {
|
|||
|
||||
Integrations.direct.update(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ $pullAll: { activities: newActivities } }
|
||||
{ $pullAll: { activities: newActivities } },
|
||||
);
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: Integrations.findOne(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ fields: { _id: 1, activities: 1 } }
|
||||
{ fields: { _id: 1, activities: 1 } },
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -322,7 +317,7 @@ if (Meteor.isServer) {
|
|||
data: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -346,14 +341,14 @@ if (Meteor.isServer) {
|
|||
|
||||
Integrations.direct.update(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ $addToSet: { activities: { $each: newActivities } } }
|
||||
{ $addToSet: { activities: { $each: newActivities } } },
|
||||
);
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: Integrations.findOne(
|
||||
{ _id: paramIntId, boardId: paramBoardId },
|
||||
{ fields: { _id: 1, activities: 1 } }
|
||||
{ fields: { _id: 1, activities: 1 } },
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -362,7 +357,7 @@ if (Meteor.isServer) {
|
|||
data: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -375,7 +370,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
|
|
@ -47,7 +47,7 @@ InvitationCodes.attachSchema(
|
|||
type: Boolean,
|
||||
defaultValue: true,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
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) => {
|
||||
// doc.createdAt = new Date();
|
||||
// doc.authorId = userId;
|
||||
|
|
|
@ -156,7 +156,7 @@ Lists.attachSchema(
|
|||
type: String,
|
||||
defaultValue: 'list',
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Lists.allow({
|
||||
|
@ -198,7 +198,7 @@ Lists.helpers({
|
|||
swimlaneId: oldSwimlaneId,
|
||||
listId: oldId,
|
||||
archived: false,
|
||||
}).forEach((card) => {
|
||||
}).forEach(card => {
|
||||
card.copy(boardId, swimlaneId, _id);
|
||||
});
|
||||
},
|
||||
|
@ -262,7 +262,7 @@ Lists.mutations({
|
|||
|
||||
archive() {
|
||||
if (this.isTemplateList()) {
|
||||
this.cards().forEach((card) => {
|
||||
this.cards().forEach(card => {
|
||||
return card.archive();
|
||||
});
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ Lists.mutations({
|
|||
|
||||
restore() {
|
||||
if (this.isTemplateList()) {
|
||||
this.allCards().forEach((card) => {
|
||||
this.allCards().forEach(card => {
|
||||
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) => {
|
||||
const cards = Cards.find({ listId: doc._id });
|
||||
if (cards) {
|
||||
cards.forEach((card) => {
|
||||
cards.forEach(card => {
|
||||
Cards.remove(card._id);
|
||||
});
|
||||
}
|
||||
|
@ -404,7 +399,7 @@ if (Meteor.isServer) {
|
|||
_id: doc._id,
|
||||
title: doc.title,
|
||||
};
|
||||
}
|
||||
},
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -425,7 +420,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
@ -492,7 +487,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
Authentication.checkUserId(req.userId);
|
||||
|
|
|
@ -44,7 +44,7 @@ Rules.attachSchema(
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
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) {
|
||||
Meteor.startup(() => {
|
||||
Rules._collection._ensureIndex({ modifiedAt: -1 });
|
||||
|
|
|
@ -76,7 +76,7 @@ Settings.attachSchema(
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
Settings.helpers({
|
||||
mailUrl() {
|
||||
|
@ -88,7 +88,7 @@ Settings.helpers({
|
|||
return `${protocol}${this.mailServer.host}:${this.mailServer.port}/`;
|
||||
}
|
||||
return `${protocol}${this.mailServer.username}:${encodeURIComponent(
|
||||
this.mailServer.password
|
||||
this.mailServer.password,
|
||||
)}@${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) {
|
||||
Meteor.startup(() => {
|
||||
Settings._collection._ensureIndex({ modifiedAt: -1 });
|
||||
|
@ -111,7 +106,7 @@ if (Meteor.isServer) {
|
|||
if (!setting) {
|
||||
const now = new Date();
|
||||
const domain = process.env.ROOT_URL.match(
|
||||
/\/\/(?:www\.)?(.*)?(?:\/)?/
|
||||
/\/\/(?:www\.)?(.*)?(?:\/)?/,
|
||||
)[1];
|
||||
const from = `Boards Support <support@${domain}>`;
|
||||
const defaultSetting = {
|
||||
|
@ -143,9 +138,7 @@ if (Meteor.isServer) {
|
|||
if (_.contains(fieldNames, 'mailServer') && doc.mailServer.host) {
|
||||
const protocol = doc.mailServer.enableTLS ? 'smtps://' : 'smtp://';
|
||||
if (!doc.mailServer.username && !doc.mailServer.password) {
|
||||
process.env.MAIL_URL = `${protocol}${doc.mailServer.host}:${
|
||||
doc.mailServer.port
|
||||
}/`;
|
||||
process.env.MAIL_URL = `${protocol}${doc.mailServer.host}:${doc.mailServer.port}/`;
|
||||
} else {
|
||||
process.env.MAIL_URL = `${protocol}${
|
||||
doc.mailServer.username
|
||||
|
@ -220,14 +213,14 @@ if (Meteor.isServer) {
|
|||
if (!user.isAdmin) {
|
||||
throw new Meteor.Error('not-allowed');
|
||||
}
|
||||
emails.forEach((email) => {
|
||||
emails.forEach(email => {
|
||||
if (email && SimpleSchema.RegEx.Email.test(email)) {
|
||||
// Checks if the email is already link to an account.
|
||||
const userExist = Users.findOne({ email });
|
||||
if (userExist) {
|
||||
throw new Meteor.Error(
|
||||
'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.
|
||||
|
@ -253,10 +246,10 @@ if (Meteor.isServer) {
|
|||
} else {
|
||||
throw new Meteor.Error(
|
||||
'invitation-generated-fail',
|
||||
err.message
|
||||
err.message,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +277,7 @@ if (Meteor.isServer) {
|
|||
throw new Meteor.Error(
|
||||
'email-fail',
|
||||
`${TAPi18n.__('email-fail-text', { lng: lang })}: ${message}`,
|
||||
message
|
||||
message,
|
||||
);
|
||||
}
|
||||
return {
|
||||
|
|
|
@ -120,7 +120,7 @@ Swimlanes.attachSchema(
|
|||
type: String,
|
||||
defaultValue: 'swimlane',
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Swimlanes.allow({
|
||||
|
@ -153,7 +153,7 @@ Swimlanes.helpers({
|
|||
}
|
||||
|
||||
// Copy all lists in swimlane
|
||||
Lists.find(query).forEach((list) => {
|
||||
Lists.find(query).forEach(list => {
|
||||
list.type = 'list';
|
||||
list.swimlaneId = oldId;
|
||||
list.boardId = boardId;
|
||||
|
@ -167,7 +167,7 @@ Swimlanes.helpers({
|
|||
swimlaneId: this._id,
|
||||
archived: false,
|
||||
}),
|
||||
{ sort: ['sort'] }
|
||||
{ sort: ['sort'] },
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -178,7 +178,7 @@ Swimlanes.helpers({
|
|||
swimlaneId: { $in: [this._id, ''] },
|
||||
archived: false,
|
||||
},
|
||||
{ sort: ['sort'] }
|
||||
{ sort: ['sort'] },
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -234,7 +234,7 @@ Swimlanes.mutations({
|
|||
|
||||
archive() {
|
||||
if (this.isTemplateSwimlane()) {
|
||||
this.myLists().forEach((list) => {
|
||||
this.myLists().forEach(list => {
|
||||
return list.archive();
|
||||
});
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ Swimlanes.mutations({
|
|||
|
||||
restore() {
|
||||
if (this.isTemplateSwimlane()) {
|
||||
this.myLists().forEach((list) => {
|
||||
this.myLists().forEach(list => {
|
||||
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 };
|
||||
|
||||
if (Meteor.isServer) {
|
||||
|
@ -292,11 +287,11 @@ if (Meteor.isServer) {
|
|||
swimlaneId: { $in: [doc._id, ''] },
|
||||
archived: false,
|
||||
},
|
||||
{ sort: ['sort'] }
|
||||
{ sort: ['sort'] },
|
||||
);
|
||||
|
||||
if (lists.count() < 2) {
|
||||
lists.forEach((list) => {
|
||||
lists.forEach(list => {
|
||||
list.remove();
|
||||
});
|
||||
} else {
|
||||
|
@ -350,7 +345,7 @@ if (Meteor.isServer) {
|
|||
_id: doc._id,
|
||||
title: doc.title,
|
||||
};
|
||||
}
|
||||
},
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -372,7 +367,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
|
@ -459,7 +454,7 @@ if (Meteor.isServer) {
|
|||
data: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const DateString = Match.Where(function (dateAsString) {
|
||||
const DateString = Match.Where(function(dateAsString) {
|
||||
check(dateAsString, String);
|
||||
return moment(dateAsString, moment.ISO_8601).isValid();
|
||||
});
|
||||
|
@ -52,10 +52,10 @@ export class TrelloCreator {
|
|||
* @param {String} dateString a properly formatted Date
|
||||
*/
|
||||
_now(dateString) {
|
||||
if(dateString) {
|
||||
if (dateString) {
|
||||
return new Date(dateString);
|
||||
}
|
||||
if(!this._nowDate) {
|
||||
if (!this._nowDate) {
|
||||
this._nowDate = new Date();
|
||||
}
|
||||
return this._nowDate;
|
||||
|
@ -67,75 +67,90 @@ export class TrelloCreator {
|
|||
* Otherwise return current logged user.
|
||||
* @param trelloUserId
|
||||
* @private
|
||||
*/
|
||||
*/
|
||||
_user(trelloUserId) {
|
||||
if(trelloUserId && this.members[trelloUserId]) {
|
||||
if (trelloUserId && this.members[trelloUserId]) {
|
||||
return this.members[trelloUserId];
|
||||
}
|
||||
return Meteor.userId();
|
||||
}
|
||||
|
||||
checkActions(trelloActions) {
|
||||
check(trelloActions, [Match.ObjectIncluding({
|
||||
data: Object,
|
||||
date: DateString,
|
||||
type: String,
|
||||
})]);
|
||||
check(trelloActions, [
|
||||
Match.ObjectIncluding({
|
||||
data: Object,
|
||||
date: DateString,
|
||||
type: String,
|
||||
}),
|
||||
]);
|
||||
// XXX we could perform more thorough checks based on action type
|
||||
}
|
||||
|
||||
checkBoard(trelloBoard) {
|
||||
check(trelloBoard, Match.ObjectIncluding({
|
||||
closed: Boolean,
|
||||
name: String,
|
||||
prefs: Match.ObjectIncluding({
|
||||
// XXX refine control by validating 'background' against a list of
|
||||
// allowed values (is it worth the maintenance?)
|
||||
background: String,
|
||||
permissionLevel: Match.Where((value) => {
|
||||
return ['org', 'private', 'public'].indexOf(value)>= 0;
|
||||
check(
|
||||
trelloBoard,
|
||||
Match.ObjectIncluding({
|
||||
closed: Boolean,
|
||||
name: String,
|
||||
prefs: Match.ObjectIncluding({
|
||||
// XXX refine control by validating 'background' against a list of
|
||||
// allowed values (is it worth the maintenance?)
|
||||
background: String,
|
||||
permissionLevel: Match.Where(value => {
|
||||
return ['org', 'private', 'public'].indexOf(value) >= 0;
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
);
|
||||
}
|
||||
|
||||
checkCards(trelloCards) {
|
||||
check(trelloCards, [Match.ObjectIncluding({
|
||||
closed: Boolean,
|
||||
dateLastActivity: DateString,
|
||||
desc: String,
|
||||
idLabels: [String],
|
||||
idMembers: [String],
|
||||
name: String,
|
||||
pos: Number,
|
||||
})]);
|
||||
check(trelloCards, [
|
||||
Match.ObjectIncluding({
|
||||
closed: Boolean,
|
||||
dateLastActivity: DateString,
|
||||
desc: String,
|
||||
idLabels: [String],
|
||||
idMembers: [String],
|
||||
name: String,
|
||||
pos: Number,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkLabels(trelloLabels) {
|
||||
check(trelloLabels, [Match.ObjectIncluding({
|
||||
// XXX refine control by validating 'color' against a list of allowed
|
||||
// values (is it worth the maintenance?)
|
||||
name: String,
|
||||
})]);
|
||||
check(trelloLabels, [
|
||||
Match.ObjectIncluding({
|
||||
// XXX refine control by validating 'color' against a list of allowed
|
||||
// values (is it worth the maintenance?)
|
||||
name: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkLists(trelloLists) {
|
||||
check(trelloLists, [Match.ObjectIncluding({
|
||||
closed: Boolean,
|
||||
name: String,
|
||||
})]);
|
||||
check(trelloLists, [
|
||||
Match.ObjectIncluding({
|
||||
closed: Boolean,
|
||||
name: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkChecklists(trelloChecklists) {
|
||||
check(trelloChecklists, [Match.ObjectIncluding({
|
||||
idBoard: String,
|
||||
idCard: String,
|
||||
name: String,
|
||||
checkItems: [Match.ObjectIncluding({
|
||||
state: String,
|
||||
check(trelloChecklists, [
|
||||
Match.ObjectIncluding({
|
||||
idBoard: String,
|
||||
idCard: String,
|
||||
name: String,
|
||||
})],
|
||||
})]);
|
||||
checkItems: [
|
||||
Match.ObjectIncluding({
|
||||
state: String,
|
||||
name: String,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
// 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
|
||||
createdAt: this._now(this.createdAt.board),
|
||||
labels: [],
|
||||
members: [{
|
||||
userId: Meteor.userId(),
|
||||
isAdmin: true,
|
||||
isActive: true,
|
||||
isNoComments: false,
|
||||
isCommentOnly: false,
|
||||
swimlaneId: false,
|
||||
}],
|
||||
members: [
|
||||
{
|
||||
userId: Meteor.userId(),
|
||||
isAdmin: true,
|
||||
isActive: true,
|
||||
isNoComments: false,
|
||||
isCommentOnly: false,
|
||||
swimlaneId: false,
|
||||
},
|
||||
],
|
||||
permission: this.getPermission(trelloBoard.prefs.permissionLevel),
|
||||
slug: getSlug(trelloBoard.name) || 'board',
|
||||
stars: 0,
|
||||
title: trelloBoard.name,
|
||||
};
|
||||
// now add other members
|
||||
if(trelloBoard.memberships) {
|
||||
trelloBoard.memberships.forEach((trelloMembership) => {
|
||||
if (trelloBoard.memberships) {
|
||||
trelloBoard.memberships.forEach(trelloMembership => {
|
||||
const trelloId = trelloMembership.idMember;
|
||||
// do we have a mapping?
|
||||
if(this.members[trelloId]) {
|
||||
if (this.members[trelloId]) {
|
||||
const wekanId = this.members[trelloId];
|
||||
// do we already have it in our list?
|
||||
const wekanMember = boardToCreate.members.find((wekanMember) => wekanMember.userId === wekanId);
|
||||
if(wekanMember) {
|
||||
const wekanMember = boardToCreate.members.find(
|
||||
wekanMember => wekanMember.userId === wekanId,
|
||||
);
|
||||
if (wekanMember) {
|
||||
// we're already mapped, but maybe with lower rights
|
||||
if(!wekanMember.isAdmin) {
|
||||
if (!wekanMember.isAdmin) {
|
||||
wekanMember.isAdmin = this.getAdmin(trelloMembership.memberType);
|
||||
}
|
||||
} else {
|
||||
|
@ -186,7 +205,7 @@ export class TrelloCreator {
|
|||
}
|
||||
});
|
||||
}
|
||||
trelloBoard.labels.forEach((label) => {
|
||||
trelloBoard.labels.forEach(label => {
|
||||
const labelToCreate = {
|
||||
_id: Random.id(6),
|
||||
color: label.color ? label.color : 'black',
|
||||
|
@ -198,7 +217,7 @@ export class TrelloCreator {
|
|||
boardToCreate.labels.push(labelToCreate);
|
||||
});
|
||||
const boardId = Boards.direct.insert(boardToCreate);
|
||||
Boards.direct.update(boardId, {$set: {modifiedAt: this._now()}});
|
||||
Boards.direct.update(boardId, { $set: { modifiedAt: this._now() } });
|
||||
// log activity
|
||||
Activities.direct.insert({
|
||||
activityType: 'importBoard',
|
||||
|
@ -225,7 +244,7 @@ export class TrelloCreator {
|
|||
*/
|
||||
createCards(trelloCards, boardId) {
|
||||
const result = [];
|
||||
trelloCards.forEach((card) => {
|
||||
trelloCards.forEach(card => {
|
||||
const cardToCreate = {
|
||||
archived: card.closed,
|
||||
boardId,
|
||||
|
@ -243,26 +262,26 @@ export class TrelloCreator {
|
|||
};
|
||||
// add labels
|
||||
if (card.idLabels) {
|
||||
cardToCreate.labelIds = card.idLabels.map((trelloId) => {
|
||||
cardToCreate.labelIds = card.idLabels.map(trelloId => {
|
||||
return this.labels[trelloId];
|
||||
});
|
||||
}
|
||||
// add members {
|
||||
if(card.idMembers) {
|
||||
if (card.idMembers) {
|
||||
const wekanMembers = [];
|
||||
// we can't just map, as some members may not have been mapped
|
||||
card.idMembers.forEach((trelloId) => {
|
||||
if(this.members[trelloId]) {
|
||||
card.idMembers.forEach(trelloId => {
|
||||
if (this.members[trelloId]) {
|
||||
const wekanId = this.members[trelloId];
|
||||
// we may map multiple Trello members to the same wekan user
|
||||
// in which case we risk adding the same user multiple times
|
||||
if(!wekanMembers.find((wId) => wId === wekanId)){
|
||||
if (!wekanMembers.find(wId => wId === wekanId)) {
|
||||
wekanMembers.push(wekanId);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(wekanMembers.length>0) {
|
||||
if (wekanMembers.length > 0) {
|
||||
cardToCreate.members = wekanMembers;
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +308,7 @@ export class TrelloCreator {
|
|||
// add comments
|
||||
const comments = this.comments[card.id];
|
||||
if (comments) {
|
||||
comments.forEach((comment) => {
|
||||
comments.forEach(comment => {
|
||||
const commentToCreate = {
|
||||
boardId,
|
||||
cardId,
|
||||
|
@ -318,15 +337,15 @@ export class TrelloCreator {
|
|||
const attachments = this.attachments[card.id];
|
||||
const trelloCoverId = card.idAttachmentCover;
|
||||
if (attachments) {
|
||||
attachments.forEach((att) => {
|
||||
attachments.forEach(att => {
|
||||
const file = new FS.File();
|
||||
// Simulating file.attachData on the client generates multiple errors
|
||||
// - HEAD returns null, which causes exception down the line
|
||||
// - 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.
|
||||
const self = this;
|
||||
if(Meteor.isServer) {
|
||||
file.attachData(att.url, function (error) {
|
||||
if (Meteor.isServer) {
|
||||
file.attachData(att.url, function(error) {
|
||||
file.boardId = boardId;
|
||||
file.cardId = cardId;
|
||||
file.userId = self._user(att.idMemberCreator);
|
||||
|
@ -334,15 +353,17 @@ export class TrelloCreator {
|
|||
// attachments' related activities automatically
|
||||
file.source = 'import';
|
||||
if (error) {
|
||||
throw(error);
|
||||
throw error;
|
||||
} else {
|
||||
const wekanAtt = Attachments.insert(file, () => {
|
||||
// we do nothing
|
||||
});
|
||||
self.attachmentIds[att.id] = wekanAtt._id;
|
||||
//
|
||||
if(trelloCoverId === att.id) {
|
||||
Cards.direct.update(cardId, { $set: {coverId: wekanAtt._id}});
|
||||
if (trelloCoverId === att.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.
|
||||
createLabels(trelloLabels, board) {
|
||||
trelloLabels.forEach((label) => {
|
||||
trelloLabels.forEach(label => {
|
||||
const color = label.color;
|
||||
const name = label.name;
|
||||
const existingLabel = board.getLabel(name, color);
|
||||
|
@ -371,7 +392,7 @@ export class TrelloCreator {
|
|||
}
|
||||
|
||||
createLists(trelloLists, boardId) {
|
||||
trelloLists.forEach((list) => {
|
||||
trelloLists.forEach(list => {
|
||||
const listToCreate = {
|
||||
archived: list.closed,
|
||||
boardId,
|
||||
|
@ -384,7 +405,7 @@ export class TrelloCreator {
|
|||
sort: list.pos,
|
||||
};
|
||||
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;
|
||||
// log activity
|
||||
// Activities.direct.insert({
|
||||
|
@ -416,12 +437,12 @@ export class TrelloCreator {
|
|||
sort: 1,
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
createChecklists(trelloChecklists) {
|
||||
trelloChecklists.forEach((checklist) => {
|
||||
trelloChecklists.forEach(checklist => {
|
||||
if (this.cards[checklist.idCard]) {
|
||||
// Create the checklist
|
||||
const checklistToCreate = {
|
||||
|
@ -435,7 +456,7 @@ export class TrelloCreator {
|
|||
this.checklists[checklist.id] = checklistId;
|
||||
// Now add the items to the checklistItems
|
||||
let counter = 0;
|
||||
checklist.checkItems.forEach((item) => {
|
||||
checklist.checkItems.forEach(item => {
|
||||
counter++;
|
||||
const checklistItemTocreate = {
|
||||
_id: checklistId + counter,
|
||||
|
@ -458,15 +479,15 @@ export class TrelloCreator {
|
|||
getColor(trelloColorCode) {
|
||||
// trello color name => wekan color
|
||||
const mapColors = {
|
||||
'blue': 'belize',
|
||||
'orange': 'pumpkin',
|
||||
'green': 'nephritis',
|
||||
'red': 'pomegranate',
|
||||
'purple': 'wisteria',
|
||||
'pink': 'pomegranate',
|
||||
'lime': 'nephritis',
|
||||
'sky': 'belize',
|
||||
'grey': 'midnight',
|
||||
blue: 'belize',
|
||||
orange: 'pumpkin',
|
||||
green: 'nephritis',
|
||||
red: 'pomegranate',
|
||||
purple: 'wisteria',
|
||||
pink: 'pomegranate',
|
||||
lime: 'nephritis',
|
||||
sky: 'belize',
|
||||
grey: 'midnight',
|
||||
};
|
||||
const wekanColor = mapColors[trelloColorCode];
|
||||
return wekanColor || Boards.simpleSchema()._schema.color.allowedValues[0];
|
||||
|
@ -482,7 +503,7 @@ export class TrelloCreator {
|
|||
}
|
||||
|
||||
parseActions(trelloActions) {
|
||||
trelloActions.forEach((action) => {
|
||||
trelloActions.forEach(action => {
|
||||
if (action.type === 'addAttachmentToCard') {
|
||||
// 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.
|
||||
|
@ -490,11 +511,11 @@ export class TrelloCreator {
|
|||
const trelloAttachment = action.data.attachment;
|
||||
// We need the idMemberCreator
|
||||
trelloAttachment.idMemberCreator = action.idMemberCreator;
|
||||
if(trelloAttachment.url) {
|
||||
if (trelloAttachment.url) {
|
||||
// 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.
|
||||
const trelloCardId = action.data.card.id;
|
||||
if(!this.attachments[trelloCardId]) {
|
||||
if (!this.attachments[trelloCardId]) {
|
||||
this.attachments[trelloCardId] = [];
|
||||
}
|
||||
this.attachments[trelloCardId].push(trelloAttachment);
|
||||
|
@ -520,88 +541,89 @@ export class TrelloCreator {
|
|||
}
|
||||
|
||||
importActions(actions, boardId) {
|
||||
actions.forEach((action) => {
|
||||
actions.forEach(action => {
|
||||
switch (action.type) {
|
||||
// Board related actions
|
||||
// TODO: addBoardMember, removeBoardMember
|
||||
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) {
|
||||
// Board related actions
|
||||
// TODO: addBoardMember, removeBoardMember
|
||||
case 'createBoard': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(action.idMemberCreator),
|
||||
oldListId: this.lists[action.data.old.idList],
|
||||
activityType: 'moveCard',
|
||||
listId: this.lists[action.data.listAfter.id],
|
||||
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({
|
||||
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
|
||||
});
|
||||
}
|
||||
|
@ -624,7 +646,9 @@ export class TrelloCreator {
|
|||
|
||||
create(board, currentBoardId) {
|
||||
// TODO : Make isSandstorm variable global
|
||||
const isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||
const isSandstorm =
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && currentBoardId) {
|
||||
const currentBoard = Boards.findOne(currentBoardId);
|
||||
|
|
|
@ -51,18 +51,13 @@ Triggers.helpers({
|
|||
|
||||
labels() {
|
||||
const boardLabels = this.board().labels;
|
||||
const cardLabels = _.filter(boardLabels, (label) => {
|
||||
const cardLabels = _.filter(boardLabels, label => {
|
||||
return _.contains(this.labelIds, label._id);
|
||||
});
|
||||
return cardLabels;
|
||||
},
|
||||
});
|
||||
|
||||
Triggers.before.update((userId, doc, fieldNames, modifier, options) => {
|
||||
modifier.$set = modifier.$set || {};
|
||||
modifier.$set.modifiedAt = Date.now();
|
||||
});
|
||||
|
||||
if (Meteor.isServer) {
|
||||
Meteor.startup(() => {
|
||||
Triggers._collection._ensureIndex({ modifiedAt: -1 });
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -247,7 +247,7 @@ Users.attachSchema(
|
|||
optional: false,
|
||||
defaultValue: 'password',
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Users.allow({
|
||||
|
@ -259,7 +259,7 @@ Users.allow({
|
|||
const adminsNumber = Users.find({ isAdmin: true }).count();
|
||||
const { isAdmin } = Users.findOne(
|
||||
{ _id: userId },
|
||||
{ fields: { isAdmin: 1 } }
|
||||
{ fields: { isAdmin: 1 } },
|
||||
);
|
||||
|
||||
// Prevents remove of the only one administrator
|
||||
|
@ -533,7 +533,7 @@ Meteor.methods({
|
|||
check(email, String);
|
||||
const existingUser = Users.findOne(
|
||||
{ 'emails.address': email },
|
||||
{ fields: { _id: 1 } }
|
||||
{ fields: { _id: 1 } },
|
||||
);
|
||||
if (existingUser) {
|
||||
throw new Meteor.Error('email-already-taken');
|
||||
|
@ -700,7 +700,7 @@ if (Meteor.isServer) {
|
|||
if (!options || !options.profile) {
|
||||
throw new Meteor.Error(
|
||||
'error-invitation-code-blank',
|
||||
'The invitation code is required'
|
||||
'The invitation code is required',
|
||||
);
|
||||
}
|
||||
const invitationCode = InvitationCodes.findOne({
|
||||
|
@ -711,7 +711,8 @@ if (Meteor.isServer) {
|
|||
if (!invitationCode) {
|
||||
throw new Meteor.Error(
|
||||
'error-invitation-code-not-exist',
|
||||
'The invitation code doesn\'t exist'
|
||||
// eslint-disable-next-line quotes
|
||||
"The invitation code doesn't exist",
|
||||
);
|
||||
} else {
|
||||
user.profile = { icode: options.profile.invitationcode };
|
||||
|
@ -722,18 +723,13 @@ if (Meteor.isServer) {
|
|||
Meteor.bindEnvironment(() => {
|
||||
InvitationCodes.remove({ _id: invitationCode._id });
|
||||
}),
|
||||
200
|
||||
200,
|
||||
);
|
||||
return user;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Users.before.update((userId, doc, fieldNames, modifier, options) => {
|
||||
modifier.$set = modifier.$set || {};
|
||||
modifier.$set.modifiedAt = Date.now();
|
||||
});
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// Let mongoDB ensure username unicity
|
||||
Meteor.startup(() => {
|
||||
|
@ -742,7 +738,7 @@ if (Meteor.isServer) {
|
|||
{
|
||||
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
|
||||
// direction and then in the other.
|
||||
function incrementBoards(boardsIds, inc) {
|
||||
boardsIds.forEach((boardId) => {
|
||||
boardsIds.forEach(boardId => {
|
||||
Boards.update(boardId, { $inc: { stars: inc } });
|
||||
});
|
||||
}
|
||||
|
@ -855,7 +851,7 @@ if (Meteor.isServer) {
|
|||
Users.update(fakeUserId.get(), {
|
||||
$set: { 'profile.cardTemplatesSwimlaneId': swimlaneId },
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Insert the list templates swimlane
|
||||
|
@ -872,7 +868,7 @@ if (Meteor.isServer) {
|
|||
Users.update(fakeUserId.get(), {
|
||||
$set: { 'profile.listTemplatesSwimlaneId': swimlaneId },
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Insert the board templates swimlane
|
||||
|
@ -889,9 +885,9 @@ if (Meteor.isServer) {
|
|||
Users.update(fakeUserId.get(), {
|
||||
$set: { 'profile.boardTemplatesSwimlaneId': swimlaneId },
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -921,7 +917,7 @@ if (Meteor.isServer) {
|
|||
if (!invitationCode) {
|
||||
throw new Meteor.Error('error-invitation-code-not-exist');
|
||||
} else {
|
||||
invitationCode.boardsToBeInvited.forEach((boardId) => {
|
||||
invitationCode.boardsToBeInvited.forEach(boardId => {
|
||||
const board = Boards.findOne(boardId);
|
||||
board.addMember(doc._id);
|
||||
});
|
||||
|
@ -1071,7 +1067,7 @@ if (Meteor.isServer) {
|
|||
loginDisabled: true,
|
||||
'services.resume.loginTokens': '',
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (action === 'enableLogin') {
|
||||
Users.update({ _id: id }, { $set: { loginDisabled: '' } });
|
||||
|
@ -1112,7 +1108,7 @@ if (Meteor.isServer) {
|
|||
*/
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/members/:userId/add', function(
|
||||
req,
|
||||
res
|
||||
res,
|
||||
) {
|
||||
try {
|
||||
Authentication.checkUserId(req.userId);
|
||||
|
@ -1136,7 +1132,7 @@ if (Meteor.isServer) {
|
|||
isTrue(isAdmin),
|
||||
isTrue(isNoComments),
|
||||
isTrue(isCommentOnly),
|
||||
userId
|
||||
userId,
|
||||
);
|
||||
}
|
||||
return {
|
||||
|
@ -1207,7 +1203,7 @@ if (Meteor.isServer) {
|
|||
data: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// simple version, only toggle watch / unwatch
|
||||
const simpleWatchable = (collection) => {
|
||||
const simpleWatchable = collection => {
|
||||
collection.attachSchema({
|
||||
watchers: {
|
||||
type: [String],
|
||||
|
@ -24,8 +24,8 @@ const simpleWatchable = (collection) => {
|
|||
collection.mutations({
|
||||
setWatcher(userId, level) {
|
||||
// if level undefined or null or false, then remove
|
||||
if (!level) return { $pull: { watchers: userId }};
|
||||
return { $addToSet: { watchers: userId }};
|
||||
if (!level) return { $pull: { watchers: userId } };
|
||||
return { $addToSet: { watchers: userId } };
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ const simpleWatchable = (collection) => {
|
|||
const complexWatchOptions = ['watching', 'tracking', 'muted'];
|
||||
const complexWatchDefault = 'muted';
|
||||
|
||||
const complexWatchable = (collection) => {
|
||||
const complexWatchable = collection => {
|
||||
collection.attachSchema({
|
||||
'watchers.$.userId': {
|
||||
type: String,
|
||||
|
@ -72,9 +72,9 @@ const complexWatchable = (collection) => {
|
|||
setWatcher(userId, level) {
|
||||
// if level undefined or null or false, then remove
|
||||
if (level === complexWatchDefault) level = null;
|
||||
if (!level) return { $pull: { watchers: { userId }}};
|
||||
if (!level) return { $pull: { watchers: { userId } } };
|
||||
const index = this.watcherIndex(userId);
|
||||
if (index<0) return { $push: { watchers: { userId, level }}};
|
||||
if (index < 0) return { $push: { watchers: { userId, level } } };
|
||||
return {
|
||||
$set: {
|
||||
[`watchers.${index}.level`]: level,
|
||||
|
|
|
@ -87,99 +87,120 @@ export class WekanCreator {
|
|||
}
|
||||
|
||||
checkActivities(wekanActivities) {
|
||||
check(wekanActivities, [Match.ObjectIncluding({
|
||||
activityType: String,
|
||||
createdAt: DateString,
|
||||
})]);
|
||||
check(wekanActivities, [
|
||||
Match.ObjectIncluding({
|
||||
activityType: String,
|
||||
createdAt: DateString,
|
||||
}),
|
||||
]);
|
||||
// XXX we could perform more thorough checks based on action type
|
||||
}
|
||||
|
||||
checkBoard(wekanBoard) {
|
||||
check(wekanBoard, Match.ObjectIncluding({
|
||||
archived: Boolean,
|
||||
title: String,
|
||||
// XXX refine control by validating 'color' against a list of
|
||||
// allowed values (is it worth the maintenance?)
|
||||
color: String,
|
||||
permission: Match.Where((value) => {
|
||||
return ['private', 'public'].indexOf(value) >= 0;
|
||||
check(
|
||||
wekanBoard,
|
||||
Match.ObjectIncluding({
|
||||
archived: Boolean,
|
||||
title: String,
|
||||
// XXX refine control by validating 'color' against a list of
|
||||
// allowed values (is it worth the maintenance?)
|
||||
color: String,
|
||||
permission: Match.Where(value => {
|
||||
return ['private', 'public'].indexOf(value) >= 0;
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
);
|
||||
}
|
||||
|
||||
checkCards(wekanCards) {
|
||||
check(wekanCards, [Match.ObjectIncluding({
|
||||
archived: Boolean,
|
||||
dateLastActivity: DateString,
|
||||
labelIds: [String],
|
||||
title: String,
|
||||
sort: Number,
|
||||
})]);
|
||||
check(wekanCards, [
|
||||
Match.ObjectIncluding({
|
||||
archived: Boolean,
|
||||
dateLastActivity: DateString,
|
||||
labelIds: [String],
|
||||
title: String,
|
||||
sort: Number,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkLabels(wekanLabels) {
|
||||
check(wekanLabels, [Match.ObjectIncluding({
|
||||
// XXX refine control by validating 'color' against a list of allowed
|
||||
// values (is it worth the maintenance?)
|
||||
color: String,
|
||||
})]);
|
||||
check(wekanLabels, [
|
||||
Match.ObjectIncluding({
|
||||
// XXX refine control by validating 'color' against a list of allowed
|
||||
// values (is it worth the maintenance?)
|
||||
color: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkLists(wekanLists) {
|
||||
check(wekanLists, [Match.ObjectIncluding({
|
||||
archived: Boolean,
|
||||
title: String,
|
||||
})]);
|
||||
check(wekanLists, [
|
||||
Match.ObjectIncluding({
|
||||
archived: Boolean,
|
||||
title: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkSwimlanes(wekanSwimlanes) {
|
||||
check(wekanSwimlanes, [Match.ObjectIncluding({
|
||||
archived: Boolean,
|
||||
title: String,
|
||||
})]);
|
||||
check(wekanSwimlanes, [
|
||||
Match.ObjectIncluding({
|
||||
archived: Boolean,
|
||||
title: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkChecklists(wekanChecklists) {
|
||||
check(wekanChecklists, [Match.ObjectIncluding({
|
||||
cardId: String,
|
||||
title: String,
|
||||
})]);
|
||||
check(wekanChecklists, [
|
||||
Match.ObjectIncluding({
|
||||
cardId: String,
|
||||
title: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkChecklistItems(wekanChecklistItems) {
|
||||
check(wekanChecklistItems, [Match.ObjectIncluding({
|
||||
cardId: String,
|
||||
title: String,
|
||||
})]);
|
||||
check(wekanChecklistItems, [
|
||||
Match.ObjectIncluding({
|
||||
cardId: String,
|
||||
title: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkRules(wekanRules) {
|
||||
check(wekanRules, [Match.ObjectIncluding({
|
||||
triggerId: String,
|
||||
actionId: String,
|
||||
title: String,
|
||||
})]);
|
||||
check(wekanRules, [
|
||||
Match.ObjectIncluding({
|
||||
triggerId: String,
|
||||
actionId: String,
|
||||
title: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
checkTriggers(wekanTriggers) {
|
||||
// XXX More check based on trigger type
|
||||
check(wekanTriggers, [Match.ObjectIncluding({
|
||||
activityType: String,
|
||||
desc: String,
|
||||
})]);
|
||||
check(wekanTriggers, [
|
||||
Match.ObjectIncluding({
|
||||
activityType: String,
|
||||
desc: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
getMembersToMap(data) {
|
||||
// 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
|
||||
// imported member
|
||||
// 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
|
||||
// imported member
|
||||
const membersToMap = data.members;
|
||||
const users = data.users;
|
||||
// auto-map based on username
|
||||
membersToMap.forEach((importedMember) => {
|
||||
membersToMap.forEach(importedMember => {
|
||||
importedMember.id = importedMember.userId;
|
||||
delete importedMember.userId;
|
||||
const user = users.filter((user) => {
|
||||
const user = users.filter(user => {
|
||||
return user._id === importedMember.id;
|
||||
})[0];
|
||||
if (user.profile && user.profile.fullname) {
|
||||
|
@ -196,10 +217,12 @@ export class WekanCreator {
|
|||
|
||||
checkActions(wekanActions) {
|
||||
// XXX More check based on action type
|
||||
check(wekanActions, [Match.ObjectIncluding({
|
||||
actionType: String,
|
||||
desc: String,
|
||||
})]);
|
||||
check(wekanActions, [
|
||||
Match.ObjectIncluding({
|
||||
actionType: String,
|
||||
desc: String,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
// 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
|
||||
createdAt: this._now(boardToImport.createdAt),
|
||||
labels: [],
|
||||
members: [{
|
||||
userId: Meteor.userId(),
|
||||
wekanId: Meteor.userId(),
|
||||
isActive: true,
|
||||
isAdmin: true,
|
||||
isNoComments: false,
|
||||
isCommentOnly: false,
|
||||
swimlaneId: false,
|
||||
}],
|
||||
members: [
|
||||
{
|
||||
userId: Meteor.userId(),
|
||||
wekanId: Meteor.userId(),
|
||||
isActive: true,
|
||||
isAdmin: true,
|
||||
isNoComments: false,
|
||||
isCommentOnly: false,
|
||||
swimlaneId: false,
|
||||
},
|
||||
],
|
||||
// Standalone Export has modifiedAt missing, adding modifiedAt to fix it
|
||||
modifiedAt: this._now(boardToImport.modifiedAt),
|
||||
permission: boardToImport.permission,
|
||||
|
@ -228,16 +253,20 @@ export class WekanCreator {
|
|||
};
|
||||
// now add other members
|
||||
if (boardToImport.members) {
|
||||
boardToImport.members.forEach((wekanMember) => {
|
||||
boardToImport.members.forEach(wekanMember => {
|
||||
// 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({
|
||||
...wekanMember,
|
||||
userId: wekanMember.wekanId,
|
||||
});
|
||||
});
|
||||
}
|
||||
boardToImport.labels.forEach((label) => {
|
||||
boardToImport.labels.forEach(label => {
|
||||
const labelToCreate = {
|
||||
_id: Random.id(6),
|
||||
color: label.color,
|
||||
|
@ -279,7 +308,7 @@ export class WekanCreator {
|
|||
*/
|
||||
createCards(wekanCards, boardId) {
|
||||
const result = [];
|
||||
wekanCards.forEach((card) => {
|
||||
wekanCards.forEach(card => {
|
||||
const cardToCreate = {
|
||||
archived: card.archived,
|
||||
boardId,
|
||||
|
@ -300,7 +329,7 @@ export class WekanCreator {
|
|||
};
|
||||
// add labels
|
||||
if (card.labelIds) {
|
||||
cardToCreate.labelIds = card.labelIds.map((wekanId) => {
|
||||
cardToCreate.labelIds = card.labelIds.map(wekanId => {
|
||||
return this.labels[wekanId];
|
||||
});
|
||||
}
|
||||
|
@ -308,12 +337,12 @@ export class WekanCreator {
|
|||
if (card.members) {
|
||||
const wekanMembers = [];
|
||||
// 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]) {
|
||||
const wekanId = this.members[sourceMemberId];
|
||||
// we may map multiple Wekan members to the same wekan user
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -349,7 +378,7 @@ export class WekanCreator {
|
|||
// add comments
|
||||
const comments = this.comments[card._id];
|
||||
if (comments) {
|
||||
comments.forEach((comment) => {
|
||||
comments.forEach(comment => {
|
||||
const commentToCreate = {
|
||||
boardId,
|
||||
cardId,
|
||||
|
@ -377,7 +406,7 @@ export class WekanCreator {
|
|||
const attachments = this.attachments[card._id];
|
||||
const wekanCoverId = card.coverId;
|
||||
if (attachments) {
|
||||
attachments.forEach((att) => {
|
||||
attachments.forEach(att => {
|
||||
const file = new FS.File();
|
||||
// Simulating file.attachData on the client generates multiple errors
|
||||
// - HEAD returns null, which causes exception down the line
|
||||
|
@ -394,7 +423,7 @@ export class WekanCreator {
|
|||
// attachments' related activities automatically
|
||||
file.source = 'import';
|
||||
if (error) {
|
||||
throw (error);
|
||||
throw error;
|
||||
} else {
|
||||
const wekanAtt = Attachments.insert(file, () => {
|
||||
// we do nothing
|
||||
|
@ -411,33 +440,37 @@ export class WekanCreator {
|
|||
}
|
||||
});
|
||||
} else if (att.file) {
|
||||
file.attachData(new Buffer(att.file, 'base64'), {
|
||||
type: att.type,
|
||||
}, (error) => {
|
||||
file.name(att.name);
|
||||
file.boardId = boardId;
|
||||
file.cardId = cardId;
|
||||
file.userId = self._user(att.userId);
|
||||
// The field source will only be used to prevent adding
|
||||
// attachments' related activities automatically
|
||||
file.source = 'import';
|
||||
if (error) {
|
||||
throw (error);
|
||||
} else {
|
||||
const wekanAtt = Attachments.insert(file, () => {
|
||||
// we do nothing
|
||||
});
|
||||
this.attachmentIds[att._id] = wekanAtt._id;
|
||||
//
|
||||
if (wekanCoverId === att._id) {
|
||||
Cards.direct.update(cardId, {
|
||||
$set: {
|
||||
coverId: wekanAtt._id,
|
||||
},
|
||||
file.attachData(
|
||||
new Buffer(att.file, 'base64'),
|
||||
{
|
||||
type: att.type,
|
||||
},
|
||||
error => {
|
||||
file.name(att.name);
|
||||
file.boardId = boardId;
|
||||
file.cardId = cardId;
|
||||
file.userId = self._user(att.userId);
|
||||
// The field source will only be used to prevent adding
|
||||
// attachments' related activities automatically
|
||||
file.source = 'import';
|
||||
if (error) {
|
||||
throw error;
|
||||
} else {
|
||||
const wekanAtt = Attachments.insert(file, () => {
|
||||
// we do nothing
|
||||
});
|
||||
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
|
||||
|
@ -450,7 +483,7 @@ export class WekanCreator {
|
|||
|
||||
// Create labels if they do not exist and load this.labels.
|
||||
createLabels(wekanLabels, board) {
|
||||
wekanLabels.forEach((label) => {
|
||||
wekanLabels.forEach(label => {
|
||||
const color = label.color;
|
||||
const name = label.name;
|
||||
const existingLabel = board.getLabel(name, color);
|
||||
|
@ -479,7 +512,7 @@ export class WekanCreator {
|
|||
const listId = Lists.direct.insert(listToCreate);
|
||||
Lists.direct.update(listId, {
|
||||
$set: {
|
||||
'updatedAt': this._now(),
|
||||
updatedAt: this._now(),
|
||||
},
|
||||
});
|
||||
this.lists[list._id] = listId;
|
||||
|
@ -520,7 +553,7 @@ export class WekanCreator {
|
|||
const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
|
||||
Swimlanes.direct.update(swimlaneId, {
|
||||
$set: {
|
||||
'updatedAt': this._now(),
|
||||
updatedAt: this._now(),
|
||||
},
|
||||
});
|
||||
this.swimlanes[swimlane._id] = swimlaneId;
|
||||
|
@ -545,7 +578,7 @@ export class WekanCreator {
|
|||
}
|
||||
|
||||
createTriggers(wekanTriggers, boardId) {
|
||||
wekanTriggers.forEach((trigger) => {
|
||||
wekanTriggers.forEach(trigger => {
|
||||
if (trigger.hasOwnProperty('labelId')) {
|
||||
trigger.labelId = this.labels[trigger.labelId];
|
||||
}
|
||||
|
@ -560,7 +593,7 @@ export class WekanCreator {
|
|||
}
|
||||
|
||||
createActions(wekanActions, boardId) {
|
||||
wekanActions.forEach((action) => {
|
||||
wekanActions.forEach(action => {
|
||||
if (action.hasOwnProperty('labelId')) {
|
||||
action.labelId = this.labels[action.labelId];
|
||||
}
|
||||
|
@ -575,7 +608,7 @@ export class WekanCreator {
|
|||
}
|
||||
|
||||
createRules(wekanRules, boardId) {
|
||||
wekanRules.forEach((rule) => {
|
||||
wekanRules.forEach(rule => {
|
||||
// Create the rule
|
||||
rule.boardId = boardId;
|
||||
rule.triggerId = this.triggers[rule.triggerId];
|
||||
|
@ -595,189 +628,178 @@ export class WekanCreator {
|
|||
sort: checklistitem.sort ? checklistitem.sort : checklistitemIndex,
|
||||
isFinished: checklistitem.isFinished,
|
||||
};
|
||||
const checklistItemId = ChecklistItems.direct.insert(checklistItemTocreate);
|
||||
const checklistItemId = ChecklistItems.direct.insert(
|
||||
checklistItemTocreate,
|
||||
);
|
||||
this.checklistItems[checklistitem._id] = checklistItemId;
|
||||
});
|
||||
}
|
||||
|
||||
parseActivities(wekanBoard) {
|
||||
wekanBoard.activities.forEach((activity) => {
|
||||
wekanBoard.activities.forEach(activity => {
|
||||
switch (activity.activityType) {
|
||||
case 'addAttachment':
|
||||
{
|
||||
// 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.
|
||||
// So we test for that
|
||||
const wekanAttachment = wekanBoard.attachments.filter((attachment) => {
|
||||
return attachment._id === activity.attachmentId;
|
||||
})[0];
|
||||
case 'addAttachment': {
|
||||
// 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.
|
||||
// So we test for that
|
||||
const wekanAttachment = wekanBoard.attachments.filter(attachment => {
|
||||
return attachment._id === activity.attachmentId;
|
||||
})[0];
|
||||
|
||||
if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
|
||||
if (wekanAttachment.url || wekanAttachment.file) {
|
||||
// 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.
|
||||
const wekanCardId = activity.cardId;
|
||||
if (!this.attachments[wekanCardId]) {
|
||||
this.attachments[wekanCardId] = [];
|
||||
if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
|
||||
if (wekanAttachment.url || wekanAttachment.file) {
|
||||
// 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.
|
||||
const wekanCardId = activity.cardId;
|
||||
if (!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) => {
|
||||
return comment._id === activity.commentId;
|
||||
})[0];
|
||||
const id = activity.cardId;
|
||||
if (!this.comments[id]) {
|
||||
this.comments[id] = [];
|
||||
case 'addComment': {
|
||||
const wekanComment = wekanBoard.comments.filter(comment => {
|
||||
return comment._id === activity.commentId;
|
||||
})[0];
|
||||
const id = activity.cardId;
|
||||
if (!this.comments[id]) {
|
||||
this.comments[id] = [];
|
||||
}
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
activities.forEach((activity) => {
|
||||
activities.forEach(activity => {
|
||||
switch (activity.activityType) {
|
||||
// Board related activities
|
||||
// TODO: addBoardMember, removeBoardMember
|
||||
case 'createBoard':
|
||||
{
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
type: 'board',
|
||||
activityTypeId: boardId,
|
||||
activityType: activity.activityType,
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// List related activities
|
||||
// TODO: removeList, archivedList
|
||||
case 'createList':
|
||||
{
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
type: 'list',
|
||||
activityType: activity.activityType,
|
||||
listId: this.lists[activity.listId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Card related activities
|
||||
// TODO: archivedCard, restoredCard, joinMember, unjoinMember
|
||||
case 'createCard':
|
||||
{
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
activityType: activity.activityType,
|
||||
listId: this.lists[activity.listId],
|
||||
cardId: this.cards[activity.cardId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'moveCard':
|
||||
{
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
oldListId: this.lists[activity.oldListId],
|
||||
activityType: activity.activityType,
|
||||
listId: this.lists[activity.listId],
|
||||
cardId: this.cards[activity.cardId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Comment related activities
|
||||
case 'addComment':
|
||||
{
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
activityType: activity.activityType,
|
||||
cardId: this.cards[activity.cardId],
|
||||
commentId: this.commentIds[activity.commentId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Attachment related activities
|
||||
case 'addAttachment':
|
||||
{
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
type: 'card',
|
||||
activityType: activity.activityType,
|
||||
attachmentId: this.attachmentIds[activity.attachmentId],
|
||||
cardId: this.cards[activity.cardId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Checklist related activities
|
||||
case 'addChecklist':
|
||||
{
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
activityType: activity.activityType,
|
||||
cardId: this.cards[activity.cardId],
|
||||
checklistId: this.checklists[activity.checklistId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'addChecklistItem':
|
||||
{
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
activityType: activity.activityType,
|
||||
cardId: this.cards[activity.cardId],
|
||||
checklistId: this.checklists[activity.checklistId],
|
||||
checklistItemId: activity.checklistItemId.replace(
|
||||
activity.checklistId,
|
||||
this.checklists[activity.checklistId]),
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Board related activities
|
||||
// TODO: addBoardMember, removeBoardMember
|
||||
case 'createBoard': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
type: 'board',
|
||||
activityTypeId: boardId,
|
||||
activityType: activity.activityType,
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// List related activities
|
||||
// TODO: removeList, archivedList
|
||||
case 'createList': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
type: 'list',
|
||||
activityType: activity.activityType,
|
||||
listId: this.lists[activity.listId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Card related activities
|
||||
// TODO: archivedCard, restoredCard, joinMember, unjoinMember
|
||||
case 'createCard': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
activityType: activity.activityType,
|
||||
listId: this.lists[activity.listId],
|
||||
cardId: this.cards[activity.cardId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'moveCard': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
oldListId: this.lists[activity.oldListId],
|
||||
activityType: activity.activityType,
|
||||
listId: this.lists[activity.listId],
|
||||
cardId: this.cards[activity.cardId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Comment related activities
|
||||
case 'addComment': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
activityType: activity.activityType,
|
||||
cardId: this.cards[activity.cardId],
|
||||
commentId: this.commentIds[activity.commentId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Attachment related activities
|
||||
case 'addAttachment': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
type: 'card',
|
||||
activityType: activity.activityType,
|
||||
attachmentId: this.attachmentIds[activity.attachmentId],
|
||||
cardId: this.cards[activity.cardId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Checklist related activities
|
||||
case 'addChecklist': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
activityType: activity.activityType,
|
||||
cardId: this.cards[activity.cardId],
|
||||
checklistId: this.checklists[activity.checklistId],
|
||||
boardId,
|
||||
createdAt: this._now(activity.createdAt),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'addChecklistItem': {
|
||||
Activities.direct.insert({
|
||||
userId: this._user(activity.userId),
|
||||
activityType: activity.activityType,
|
||||
cardId: this.cards[activity.cardId],
|
||||
checklistId: this.checklists[activity.checklistId],
|
||||
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, {
|
||||
// membersMapping: Match.Optional(Object),
|
||||
// });
|
||||
|
||||
// this.checkActivities(board.activities);
|
||||
// this.checkBoard(board);
|
||||
// this.checkLabels(board.labels);
|
||||
|
@ -807,7 +828,9 @@ export class WekanCreator {
|
|||
|
||||
create(board, currentBoardId) {
|
||||
// TODO : Make isSandstorm variable global
|
||||
const isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||
const isSandstorm =
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && currentBoardId) {
|
||||
const currentBoard = Boards.findOne(currentBoardId);
|
||||
|
|
|
@ -5,10 +5,10 @@ export function getMembersToMap(data) {
|
|||
const membersToMap = data.members;
|
||||
const users = data.users;
|
||||
// auto-map based on username
|
||||
membersToMap.forEach((importedMember) => {
|
||||
membersToMap.forEach(importedMember => {
|
||||
importedMember.id = importedMember.userId;
|
||||
delete importedMember.userId;
|
||||
const user = users.filter((user) => {
|
||||
const user = users.filter(user => {
|
||||
return user._id === importedMember.id;
|
||||
})[0];
|
||||
if (user.profile && user.profile.fullname) {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
|
||||
"lint:staged": "lint-staged",
|
||||
"prettify": "prettier --write '**/*.js' '**/*.jsx'",
|
||||
"test": "npm run --silent lint"
|
||||
"test": "npm run lint"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
|
|
|
@ -73,9 +73,14 @@ do
|
|||
if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
echo "Linux";
|
||||
# Debian, Ubuntu, Mint
|
||||
sudo apt-get install -y build-essential gcc g++ make git curl wget
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
sudo apt-get install -y build-essential gcc g++ make git curl wget npm nodejs
|
||||
sudo npm -g install npm
|
||||
sudo chown wekan:wekan ../../.npm -R
|
||||
#curl -0 -L https://npmjs.org/install.sh | sudo sh
|
||||
sudo npm -g install n
|
||||
sudo n 8.15.1
|
||||
#curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||
#sudo apt-get install -y nodejs
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
echo "macOS";
|
||||
pause '1) Install XCode 2) Install Node 8.x from https://nodejs.org/en/ 3) Press [Enter] key to continue.'
|
||||
|
|
222
sandstorm.js
222
sandstorm.js
|
@ -1,7 +1,7 @@
|
|||
// Sandstorm context is detected using the METEOR_SETTINGS environment variable
|
||||
// in the package definition.
|
||||
const isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
const isSandstorm =
|
||||
Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm;
|
||||
|
||||
// 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
|
||||
|
@ -28,15 +28,17 @@ if (isSandstorm && Meteor.isServer) {
|
|||
const Package = Capnp.importSystem('sandstorm/package.capnp');
|
||||
const Powerbox = Capnp.importSystem('sandstorm/powerbox.capnp');
|
||||
const Identity = Capnp.importSystem('sandstorm/identity.capnp');
|
||||
const SandstormHttpBridge =
|
||||
Capnp.importSystem('sandstorm/sandstorm-http-bridge.capnp').SandstormHttpBridge;
|
||||
const SandstormHttpBridge = Capnp.importSystem(
|
||||
'sandstorm/sandstorm-http-bridge.capnp',
|
||||
).SandstormHttpBridge;
|
||||
|
||||
let httpBridge = null;
|
||||
let capnpConnection = null;
|
||||
|
||||
const bridgeConfig = Capnp.parse(
|
||||
Package.BridgeConfig,
|
||||
fs.readFileSync('/sandstorm-http-bridge-config'));
|
||||
fs.readFileSync('/sandstorm-http-bridge-config'),
|
||||
);
|
||||
|
||||
function getHttpBridge() {
|
||||
if (!httpBridge) {
|
||||
|
@ -54,9 +56,13 @@ if (isSandstorm && Meteor.isServer) {
|
|||
const parsedDescriptor = Capnp.parse(
|
||||
Powerbox.PowerboxDescriptor,
|
||||
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 = [];
|
||||
if (tag.permissions[1]) {
|
||||
permissions.push('configure');
|
||||
|
@ -71,35 +77,43 @@ if (isSandstorm && Meteor.isServer) {
|
|||
const session = httpBridge.getSessionContext(sessionId).context;
|
||||
const api = httpBridge.getSandstormApi(sessionId).api;
|
||||
|
||||
Meteor.wrapAsync((done) => {
|
||||
session.claimRequest(token).then((response) => {
|
||||
const identity = response.cap.castAs(Identity.Identity);
|
||||
const promises = [api.getIdentityId(identity), identity.getProfile(),
|
||||
httpBridge.saveIdentity(identity)];
|
||||
return Promise.all(promises).then((responses) => {
|
||||
const identityId = responses[0].id.toString('hex').slice(0, 32);
|
||||
const profile = responses[1].profile;
|
||||
return profile.picture.getUrl().then((response) => {
|
||||
const sandstormInfo = {
|
||||
id: identityId,
|
||||
name: profile.displayName.defaultText,
|
||||
permissions,
|
||||
picture: `${response.protocol}://${response.hostPath}`,
|
||||
preferredHandle: profile.preferredHandle,
|
||||
pronouns: profile.pronouns,
|
||||
};
|
||||
Meteor.wrapAsync(done => {
|
||||
session
|
||||
.claimRequest(token)
|
||||
.then(response => {
|
||||
const identity = response.cap.castAs(Identity.Identity);
|
||||
const promises = [
|
||||
api.getIdentityId(identity),
|
||||
identity.getProfile(),
|
||||
httpBridge.saveIdentity(identity),
|
||||
];
|
||||
return Promise.all(promises).then(responses => {
|
||||
const identityId = responses[0].id.toString('hex').slice(0, 32);
|
||||
const profile = responses[1].profile;
|
||||
return profile.picture.getUrl().then(response => {
|
||||
const sandstormInfo = {
|
||||
id: identityId,
|
||||
name: profile.displayName.defaultText,
|
||||
permissions,
|
||||
picture: `${response.protocol}://${response.hostPath}`,
|
||||
preferredHandle: profile.preferredHandle,
|
||||
pronouns: profile.pronouns,
|
||||
};
|
||||
|
||||
const login = Accounts.updateOrCreateUserFromExternalService(
|
||||
'sandstorm', sandstormInfo,
|
||||
{ profile: { name: sandstormInfo.name } });
|
||||
const login = Accounts.updateOrCreateUserFromExternalService(
|
||||
'sandstorm',
|
||||
sandstormInfo,
|
||||
{ profile: { name: sandstormInfo.name } },
|
||||
);
|
||||
|
||||
updateUserPermissions(login.userId, permissions);
|
||||
done();
|
||||
updateUserPermissions(login.userId, permissions);
|
||||
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) {
|
||||
const httpBridge = getHttpBridge();
|
||||
const session = httpBridge.getSessionContext(sessionId).context;
|
||||
Meteor.wrapAsync((done) => {
|
||||
return Promise.all(users.map((user) => {
|
||||
return httpBridge.getSavedIdentity(user.id).then((response) => {
|
||||
// Call getProfile() to make sure that the identity successfully resolves.
|
||||
// (In C++ we would instead call whenResolved() here.)
|
||||
const identity = response.identity;
|
||||
return identity.getProfile().then(() => {
|
||||
return { identity,
|
||||
mentioned: !!user.mentioned,
|
||||
subscribed: !!user.subscribed,
|
||||
};
|
||||
});
|
||||
}).catch(() => {
|
||||
// Ignore identities that fail to restore. Either they were added before we set
|
||||
// `saveIdentityCaps` to true, or they have lost access to the board.
|
||||
});
|
||||
})).then((maybeUsers) => {
|
||||
const users = maybeUsers.filter((u) => !!u);
|
||||
const event = { path, type, users };
|
||||
if (caption) {
|
||||
event.notification = { caption };
|
||||
}
|
||||
Meteor.wrapAsync(done => {
|
||||
return Promise.all(
|
||||
users.map(user => {
|
||||
return httpBridge
|
||||
.getSavedIdentity(user.id)
|
||||
.then(response => {
|
||||
// Call getProfile() to make sure that the identity successfully resolves.
|
||||
// (In C++ we would instead call whenResolved() here.)
|
||||
const identity = response.identity;
|
||||
return identity.getProfile().then(() => {
|
||||
return {
|
||||
identity,
|
||||
mentioned: !!user.mentioned,
|
||||
subscribed: !!user.subscribed,
|
||||
};
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
// Ignore identities that fail to restore. Either they were added before we set
|
||||
// `saveIdentityCaps` to true, or they have lost access to the board.
|
||||
});
|
||||
}),
|
||||
)
|
||||
.then(maybeUsers => {
|
||||
const users = maybeUsers.filter(u => !!u);
|
||||
const event = { path, type, users };
|
||||
if (caption) {
|
||||
event.notification = { caption };
|
||||
}
|
||||
|
||||
return session.activity(event);
|
||||
}).then(() => done(),
|
||||
(e) => done(e));
|
||||
return session.activity(event);
|
||||
})
|
||||
.then(() => done(), e => done(e));
|
||||
})();
|
||||
}
|
||||
|
||||
|
@ -146,7 +167,9 @@ if (isSandstorm && Meteor.isServer) {
|
|||
|
||||
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) {
|
||||
const users = {};
|
||||
function ensureUserListed(userId) {
|
||||
|
@ -188,10 +211,14 @@ if (isSandstorm && Meteor.isServer) {
|
|||
if (doc.activityType === 'addComment') {
|
||||
const comment = CardComments.findOne(doc.commentId);
|
||||
caption = { defaultText: comment.text };
|
||||
const activeMembers =
|
||||
_.pluck(Boards.findOne(sandstormBoard._id).activeMembers(), 'userId');
|
||||
(comment.text.match(/\B@([\w.]*)/g) || []).forEach((username) => {
|
||||
const user = Meteor.users.findOne({ username: username.slice(1)});
|
||||
const activeMembers = _.pluck(
|
||||
Boards.findOne(sandstormBoard._id).activeMembers(),
|
||||
'userId',
|
||||
);
|
||||
(comment.text.match(/\B@([\w.]*)/g) || []).forEach(username => {
|
||||
const user = Meteor.users.findOne({
|
||||
username: username.slice(1),
|
||||
});
|
||||
if (user && activeMembers.indexOf(user._id) !== -1) {
|
||||
mentionedUser(user._id);
|
||||
}
|
||||
|
@ -209,18 +236,22 @@ if (isSandstorm && Meteor.isServer) {
|
|||
const isAdmin = permissions.indexOf('configure') > -1;
|
||||
const isCommentOnly = 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 memberIndex = _.pluck(boardMembers, 'userId').indexOf(userId);
|
||||
|
||||
let modifier;
|
||||
if (memberIndex > -1)
|
||||
modifier = { $set: { [`members.${memberIndex}`]: permissionDoc }};
|
||||
else if (!isActive)
|
||||
modifier = {};
|
||||
else
|
||||
modifier = { $push: { members: permissionDoc }};
|
||||
modifier = { $set: { [`members.${memberIndex}`]: permissionDoc } };
|
||||
else if (!isActive) modifier = {};
|
||||
else modifier = { $push: { members: permissionDoc } };
|
||||
|
||||
Boards.update(sandstormBoard._id, modifier);
|
||||
}
|
||||
|
@ -259,7 +290,7 @@ if (isSandstorm && Meteor.isServer) {
|
|||
});
|
||||
Activities.update(
|
||||
{ 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;
|
||||
let appendNumber = 0;
|
||||
while (Users.findOne({
|
||||
_id: { $ne: doc._id },
|
||||
username: generateUniqueUsername(username, appendNumber),
|
||||
})) {
|
||||
while (
|
||||
Users.findOne({
|
||||
_id: { $ne: doc._id },
|
||||
username: generateUniqueUsername(username, appendNumber),
|
||||
})
|
||||
) {
|
||||
appendNumber += 1;
|
||||
}
|
||||
|
||||
|
@ -321,40 +354,43 @@ if (isSandstorm && Meteor.isServer) {
|
|||
// is now handled by Sandstorm.
|
||||
// See https://github.com/wekan/wekan/issues/346
|
||||
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
|
||||
// https://github.com/sandstorm-io/meteor-accounts-sandstorm/pull/31
|
||||
const _httpMethods = HTTP.methods;
|
||||
HTTP.methods = (newMethods) => {
|
||||
Object.keys(newMethods).forEach((key) => {
|
||||
HTTP.methods = newMethods => {
|
||||
Object.keys(newMethods).forEach(key => {
|
||||
if (newMethods[key].auth) {
|
||||
newMethods[key].auth = function() {
|
||||
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;
|
||||
};
|
||||
}
|
||||
});
|
||||
_httpMethods(newMethods);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
if (isSandstorm && Meteor.isClient) {
|
||||
let rpcCounter = 0;
|
||||
const rpcs = {};
|
||||
|
||||
window.addEventListener('message', (event) => {
|
||||
window.addEventListener('message', event => {
|
||||
if (event.source === window) {
|
||||
// Meteor likes to postmessage itself.
|
||||
return;
|
||||
}
|
||||
|
||||
if ((event.source !== window.parent) ||
|
||||
typeof event.data !== 'object' ||
|
||||
typeof event.data.rpcId !== 'number') {
|
||||
if (
|
||||
event.source !== window.parent ||
|
||||
typeof event.data !== 'object' ||
|
||||
typeof event.data.rpcId !== 'number'
|
||||
) {
|
||||
throw new Error(`got unexpected postMessage: ${event}`);
|
||||
}
|
||||
|
||||
|
@ -374,7 +410,7 @@ if (isSandstorm && Meteor.isClient) {
|
|||
obj[name] = message;
|
||||
window.parent.postMessage(obj, '*');
|
||||
return new Promise((resolve, reject) => {
|
||||
rpcs[id] = (response) => {
|
||||
rpcs[id] = response => {
|
||||
if (response.error) {
|
||||
reject(new Error(response.error));
|
||||
} else {
|
||||
|
@ -401,16 +437,20 @@ if (isSandstorm && Meteor.isClient) {
|
|||
function doRequest(serializedPowerboxDescriptor, onSuccess) {
|
||||
return sendRpc('powerboxRequest', {
|
||||
query: [serializedPowerboxDescriptor],
|
||||
}).then((response) => {
|
||||
}).then(response => {
|
||||
if (!response.canceled) {
|
||||
onSuccess(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.sandstormRequestIdentity = function () {
|
||||
doRequest(powerboxDescriptors.identity, (response) => {
|
||||
Meteor.call('sandstormClaimIdentityRequest', response.token, response.descriptor);
|
||||
window.sandstormRequestIdentity = function() {
|
||||
doRequest(powerboxDescriptors.identity, response => {
|
||||
Meteor.call(
|
||||
'sandstormClaimIdentityRequest',
|
||||
response.token,
|
||||
response.descriptor,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -422,9 +462,11 @@ if (isSandstorm && Meteor.isClient) {
|
|||
return window.parent.postMessage(msg, '*');
|
||||
}
|
||||
|
||||
FlowRouter.triggers.enter([({ path }) => {
|
||||
updateSandstormMetaData({ setPath: path });
|
||||
}]);
|
||||
FlowRouter.triggers.enter([
|
||||
({ path }) => {
|
||||
updateSandstormMetaData({ setPath: path });
|
||||
},
|
||||
]);
|
||||
|
||||
Tracker.autorun(() => {
|
||||
updateSandstormMetaData({ setTitle: DocHead.getTitle() });
|
||||
|
|
|
@ -2,15 +2,20 @@
|
|||
// server
|
||||
import { AccountsLockout } from 'meteor/lucasantoniassi:accounts-lockout';
|
||||
|
||||
(new AccountsLockout({
|
||||
new AccountsLockout({
|
||||
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,
|
||||
failureWindow: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW || 15,
|
||||
failureWindow:
|
||||
process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW || 15,
|
||||
},
|
||||
unknownUsers: {
|
||||
failuresBeforeLockout: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE || 3,
|
||||
lockoutPeriod: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD || 60,
|
||||
failureWindow: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW || 15,
|
||||
failuresBeforeLockout:
|
||||
process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE || 3,
|
||||
lockoutPeriod:
|
||||
process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD || 60,
|
||||
failureWindow:
|
||||
process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW || 15,
|
||||
},
|
||||
})).startup();
|
||||
}).startup();
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
import Fiber from 'fibers';
|
||||
|
||||
Meteor.startup(() => {
|
||||
|
||||
// Node Fibers 100% CPU usage issue
|
||||
// https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
|
||||
// https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
|
||||
// https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
|
||||
Fiber.poolSize = 1e9;
|
||||
|
||||
Accounts.validateLoginAttempt(function (options) {
|
||||
Accounts.validateLoginAttempt(function(options) {
|
||||
const user = options.user || {};
|
||||
return !user.loginDisabled;
|
||||
});
|
||||
|
||||
Authentication = {};
|
||||
|
||||
Authentication.checkUserId = function (userId) {
|
||||
Authentication.checkUserId = function(userId) {
|
||||
if (userId === undefined) {
|
||||
const error = new Meteor.Error('Unauthorized', 'Unauthorized');
|
||||
error.statusCode = 401;
|
||||
|
@ -28,13 +27,12 @@ Meteor.startup(() => {
|
|||
error.statusCode = 403;
|
||||
throw error;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// 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
|
||||
Authentication.checkLoggedIn = function(userId) {
|
||||
if(userId === undefined) {
|
||||
if (userId === undefined) {
|
||||
const error = new Meteor.Error('Unauthorized', 'Unauthorized');
|
||||
error.statusCode = 401;
|
||||
throw error;
|
||||
|
@ -44,7 +42,7 @@ Meteor.startup(() => {
|
|||
// 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
|
||||
Authentication.checkAdminOrCondition = function(userId, otherReq) {
|
||||
if(otherReq) return;
|
||||
if (otherReq) return;
|
||||
const admin = Users.findOne({ _id: userId, isAdmin: true });
|
||||
if (admin === undefined) {
|
||||
const error = new Meteor.Error('Forbidden', 'Forbidden');
|
||||
|
@ -58,14 +56,16 @@ Meteor.startup(() => {
|
|||
Authentication.checkLoggedIn(userId);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
if (Meteor.isServer) {
|
||||
if(process.env.OAUTH2_CLIENT_ID !== '') {
|
||||
|
||||
ServiceConfiguration.configurations.upsert( // eslint-disable-line no-undef
|
||||
if (process.env.OAUTH2_CLIENT_ID !== '') {
|
||||
ServiceConfiguration.configurations.upsert(
|
||||
// eslint-disable-line no-undef
|
||||
{ service: 'oidc' },
|
||||
{
|
||||
$set: {
|
||||
|
@ -76,15 +76,14 @@ Meteor.startup(() => {
|
|||
authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT,
|
||||
userinfoEndpoint: process.env.OAUTH2_USERINFO_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,
|
||||
},
|
||||
// OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
|
||||
// OAUTH2_REQUEST_PERMISSIONS || 'openid profile email',
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
Meteor.startup(() => {
|
||||
|
||||
if ( process.env.CORS ) {
|
||||
if (process.env.CORS) {
|
||||
// Listen to incoming HTTP requests, can only be used on the server
|
||||
WebApp.rawConnectHandlers.use(function(req, res, next) {
|
||||
res.setHeader('Access-Control-Allow-Origin', process.env.CORS);
|
||||
return next();
|
||||
});
|
||||
}
|
||||
if ( process.env.CORS_ALLOW_HEADERS ) {
|
||||
if (process.env.CORS_ALLOW_HEADERS) {
|
||||
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();
|
||||
});
|
||||
}
|
||||
if ( process.env.CORS_EXPOSE_HEADERS ) {
|
||||
if (process.env.CORS_EXPOSE_HEADERS) {
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue