mirror of
https://github.com/wekan/wekan.git
synced 2025-12-23 19:00:12 +01:00
Merge branch 'admin-reports' of https://github.com/jrsupplee/wekan into jrsupplee-admin-reports
This commit is contained in:
commit
decab9256b
35 changed files with 695 additions and 509 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
template(name="commentForm")
|
template(name="commentForm")
|
||||||
.new-comment.js-new-comment(
|
.new-comment.js-new-comment(
|
||||||
class="{{#if commentFormIsOpen}}is-open{{/if}}")
|
class="{{#if commentFormIsOpen}}is-open{{/if}}")
|
||||||
+userAvatar(userId=currentUser._id)
|
+userAvatar(userId=currentUser._id noRemove=true)
|
||||||
form.js-new-comment-form
|
form.js-new-comment-form
|
||||||
+editor(class="js-new-comment-input")
|
+editor(class="js-new-comment-input")
|
||||||
| {{getUnsavedValue 'cardComment' currentCard._id}}
|
| {{getUnsavedValue 'cardComment' currentCard._id}}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import { DatePicker } from '/client/lib/datepicker';
|
||||||
|
import moment from 'moment';
|
||||||
|
import Cards from '/models/cards';
|
||||||
|
|
||||||
Template.cardCustomFieldsPopup.helpers({
|
Template.cardCustomFieldsPopup.helpers({
|
||||||
hasCustomField() {
|
hasCustomField() {
|
||||||
const card = Cards.findOne(Session.get('currentCard'));
|
const card = Cards.findOne(Session.get('currentCard'));
|
||||||
|
|
@ -286,8 +290,9 @@ CardCustomField.register('cardCustomField');
|
||||||
} else {
|
} else {
|
||||||
event.target.blur();
|
event.target.blur();
|
||||||
|
|
||||||
const idx = Array.from(this.findAll('input'))
|
const idx = Array.from(this.findAll('input')).indexOf(
|
||||||
.indexOf(event.target);
|
event.target,
|
||||||
|
);
|
||||||
items.splice(idx + 1, 0, '');
|
items.splice(idx + 1, 0, '');
|
||||||
|
|
||||||
Tracker.afterFlush(() => {
|
Tracker.afterFlush(() => {
|
||||||
|
|
@ -303,7 +308,10 @@ CardCustomField.register('cardCustomField');
|
||||||
},
|
},
|
||||||
|
|
||||||
'blur .js-card-customfield-stringtemplate-item'(event) {
|
'blur .js-card-customfield-stringtemplate-item'(event) {
|
||||||
if (!event.target.value.trim() || event.target === this.find('input.last')) {
|
if (
|
||||||
|
!event.target.value.trim() ||
|
||||||
|
event.target === this.find('input.last')
|
||||||
|
) {
|
||||||
const items = this.getItems();
|
const items = this.getItems();
|
||||||
this.stringtemplateItems.set(items);
|
this.stringtemplateItems.set(items);
|
||||||
this.find('input.last').value = '';
|
this.find('input.last').value = '';
|
||||||
|
|
|
||||||
|
|
@ -1,122 +1,5 @@
|
||||||
// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours
|
import moment from 'moment';
|
||||||
function adjustedTimeFormat() {
|
import { DatePicker } from '/client/lib/datepicker';
|
||||||
return moment
|
|
||||||
.localeData()
|
|
||||||
.longDateFormat('LT')
|
|
||||||
.replace(/HH/i, 'H');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Edit received, start, due & end dates
|
|
||||||
BlazeComponent.extendComponent({
|
|
||||||
template() {
|
|
||||||
return 'editCardDate';
|
|
||||||
},
|
|
||||||
|
|
||||||
onCreated() {
|
|
||||||
this.error = new ReactiveVar('');
|
|
||||||
this.card = this.data();
|
|
||||||
this.date = new ReactiveVar(moment.invalid());
|
|
||||||
},
|
|
||||||
|
|
||||||
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),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.date.get().isValid()) {
|
|
||||||
$picker.datepicker('update', this.date.get().toDate());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
showDate() {
|
|
||||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
showTime() {
|
|
||||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
dateFormat() {
|
|
||||||
return moment.localeData().longDateFormat('L');
|
|
||||||
},
|
|
||||||
timeFormat() {
|
|
||||||
return moment.localeData().longDateFormat('LT');
|
|
||||||
},
|
|
||||||
|
|
||||||
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,
|
|
||||||
adjustedTimeFormat(),
|
|
||||||
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');
|
|
||||||
const newTime = moment(time, adjustedTimeFormat(), true);
|
|
||||||
const newDate = moment(evt.target.date.value, 'L', true);
|
|
||||||
const dateString = `${evt.target.date.value} ${time}`;
|
|
||||||
const newCompleteDate = moment(
|
|
||||||
dateString,
|
|
||||||
'L ' + adjustedTimeFormat(),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
if (!newTime.isValid()) {
|
|
||||||
this.error.set('invalid-time');
|
|
||||||
evt.target.time.focus();
|
|
||||||
}
|
|
||||||
if (!newDate.isValid()) {
|
|
||||||
this.error.set('invalid-date');
|
|
||||||
evt.target.date.focus();
|
|
||||||
}
|
|
||||||
if (newCompleteDate.isValid()) {
|
|
||||||
this._storeDate(newCompleteDate.toDate());
|
|
||||||
Popup.close();
|
|
||||||
} else {
|
|
||||||
if (!this.error) {
|
|
||||||
this.error.set('invalid');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'click .js-delete-date'(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
this._deleteDate();
|
|
||||||
Popup.close();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.dateBadge.helpers({
|
Template.dateBadge.helpers({
|
||||||
canModifyCard() {
|
canModifyCard() {
|
||||||
|
|
|
||||||
|
|
@ -110,15 +110,24 @@ template(name="cardDetails")
|
||||||
a.card-label.add-label.js-end-date
|
a.card-label.add-label.js-end-date
|
||||||
i.fa.fa-plus
|
i.fa.fa-plus
|
||||||
|
|
||||||
|
hr
|
||||||
|
if currentBoard.allowsCreator
|
||||||
|
.card-details-item.card-details-item-creator
|
||||||
|
h3.card-details-item-title
|
||||||
|
i.fa.fa-user
|
||||||
|
| {{_ 'creator'}}
|
||||||
|
|
||||||
|
+userAvatar(userId=userId noRemove=true)
|
||||||
|
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||||
|
|
||||||
//.card-details-items
|
//.card-details-items
|
||||||
if currentBoard.allowsMembers
|
if currentBoard.allowsMembers
|
||||||
hr
|
|
||||||
.card-details-item.card-details-item-members
|
.card-details-item.card-details-item-members
|
||||||
h3.card-details-item-title
|
h3.card-details-item-title
|
||||||
i.fa.fa-users
|
i.fa.fa-users
|
||||||
| {{_ 'members'}}
|
| {{_ 'members'}}
|
||||||
each getMembers
|
each userId in getMembers
|
||||||
+userAvatar(userId=this cardId=../_id)
|
+userAvatar(userId=userId cardId=_id)
|
||||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||||
if canModifyCard
|
if canModifyCard
|
||||||
unless currentUser.isWorker
|
unless currentUser.isWorker
|
||||||
|
|
@ -131,8 +140,8 @@ template(name="cardDetails")
|
||||||
h3.card-details-item-title
|
h3.card-details-item-title
|
||||||
i.fa.fa-user
|
i.fa.fa-user
|
||||||
| {{_ 'assignee'}}
|
| {{_ 'assignee'}}
|
||||||
each getAssignees
|
each userId in getAssignees
|
||||||
+userAvatarAssignee(userId=this cardId=../_id)
|
+userAvatar(userId=userId cardId=_id assignee=true)
|
||||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||||
if canModifyCard
|
if canModifyCard
|
||||||
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
|
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
|
||||||
|
|
@ -488,23 +497,6 @@ template(name="cardAssigneesPopup")
|
||||||
if currentUser.isCardAssignee
|
if currentUser.isCardAssignee
|
||||||
i.fa.fa-check
|
i.fa.fa-check
|
||||||
|
|
||||||
template(name="userAvatarAssignee")
|
|
||||||
a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})")
|
|
||||||
if userData.profile.avatarUrl
|
|
||||||
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
|
|
||||||
else
|
|
||||||
+userAvatarAssigneeInitials(userId=userData._id)
|
|
||||||
|
|
||||||
if showStatus
|
|
||||||
span.assignee-presence-status(class=presenceStatusClassName)
|
|
||||||
span.member-type(class=memberType)
|
|
||||||
|
|
||||||
unless isSandstorm
|
|
||||||
if showEdit
|
|
||||||
if $eq currentUser._id userData._id
|
|
||||||
a.edit-avatar.js-change-avatar
|
|
||||||
i.fa.fa-pencil
|
|
||||||
|
|
||||||
template(name="cardAssigneePopup")
|
template(name="cardAssigneePopup")
|
||||||
.board-assignee-menu
|
.board-assignee-menu
|
||||||
.mini-profile-info
|
.mini-profile-info
|
||||||
|
|
@ -522,10 +514,6 @@ template(name="cardAssigneePopup")
|
||||||
with currentUser
|
with currentUser
|
||||||
li: a.js-edit-profile {{_ 'edit-profile'}}
|
li: a.js-edit-profile {{_ 'edit-profile'}}
|
||||||
|
|
||||||
template(name="userAvatarAssigneeInitials")
|
|
||||||
svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15")
|
|
||||||
text(x="50%" y="13" text-anchor="middle")= initials
|
|
||||||
|
|
||||||
template(name="cardMorePopup")
|
template(name="cardMorePopup")
|
||||||
p.quiet
|
p.quiet
|
||||||
span.clearfix
|
span.clearfix
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
|
import { DatePicker } from '/client/lib/datepicker';
|
||||||
|
import Cards from '/models/cards';
|
||||||
|
import Boards from '/models/boards';
|
||||||
|
import Checklists from '/models/checklists';
|
||||||
|
import Integrations from '/models/integrations';
|
||||||
|
import Users from '/models/users';
|
||||||
|
import Lists from '/models/lists';
|
||||||
|
import CardComments from '/models/cardComments';
|
||||||
|
import { ALLOWED_COLORS } from '/config/const';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { UserAvatar } from '../users/userAvatar';
|
||||||
|
|
||||||
const subManager = new SubsManager();
|
const subManager = new SubsManager();
|
||||||
const { calculateIndexData } = Utils;
|
const { calculateIndexData } = Utils;
|
||||||
|
|
||||||
let cardColors;
|
|
||||||
Meteor.startup(() => {
|
|
||||||
cardColors = Cards.simpleSchema()._schema.color.allowedValues;
|
|
||||||
});
|
|
||||||
|
|
||||||
BlazeComponent.extendComponent({
|
BlazeComponent.extendComponent({
|
||||||
mixins() {
|
mixins() {
|
||||||
return [Mixins.InfiniteScrolling];
|
return [Mixins.InfiniteScrolling];
|
||||||
|
|
@ -160,9 +167,7 @@ BlazeComponent.extendComponent({
|
||||||
integration,
|
integration,
|
||||||
'CardSelected',
|
'CardSelected',
|
||||||
params,
|
params,
|
||||||
() => {
|
() => {},
|
||||||
return;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +295,7 @@ BlazeComponent.extendComponent({
|
||||||
Utils.goBoardId(this.data().boardId);
|
Utils.goBoardId(this.data().boardId);
|
||||||
},
|
},
|
||||||
'click .js-copy-link'() {
|
'click .js-copy-link'() {
|
||||||
StringToCopyElement = document.getElementById('cardURL_copy');
|
const StringToCopyElement = document.getElementById('cardURL_copy');
|
||||||
StringToCopyElement.value =
|
StringToCopyElement.value =
|
||||||
window.location.origin + window.location.pathname;
|
window.location.origin + window.location.pathname;
|
||||||
StringToCopyElement.select();
|
StringToCopyElement.select();
|
||||||
|
|
@ -407,122 +412,6 @@ BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
}).register('cardDetails');
|
}).register('cardDetails');
|
||||||
|
|
||||||
Template.cardDetails.helpers({
|
|
||||||
userData() {
|
|
||||||
// We need to handle a special case for the search results provided by the
|
|
||||||
// `matteodem:easy-search` package. Since these results gets published in a
|
|
||||||
// separate collection, and not in the standard Meteor.Users collection as
|
|
||||||
// expected, we use a component parameter ("property") to distinguish the
|
|
||||||
// two cases.
|
|
||||||
const userCollection = this.esSearch ? ESSearchResults : Users;
|
|
||||||
return userCollection.findOne(this.userId, {
|
|
||||||
fields: {
|
|
||||||
profile: 1,
|
|
||||||
username: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
receivedSelected() {
|
|
||||||
if (this.getReceived().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
startSelected() {
|
|
||||||
if (this.getStart().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
endSelected() {
|
|
||||||
if (this.getEnd().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
dueSelected() {
|
|
||||||
if (this.getDue().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
memberSelected() {
|
|
||||||
if (this.getMembers().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
labelSelected() {
|
|
||||||
if (this.getLabels().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
assigneeSelected() {
|
|
||||||
if (this.getAssignees().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
requestBySelected() {
|
|
||||||
if (this.getRequestBy().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
assigneeBySelected() {
|
|
||||||
if (this.getAssigneeBy().length === 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
memberType() {
|
|
||||||
const user = Users.findOne(this.userId);
|
|
||||||
return user && user.isBoardAdmin() ? 'admin' : 'normal';
|
|
||||||
},
|
|
||||||
|
|
||||||
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';
|
|
||||||
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
|
|
||||||
return 'active';
|
|
||||||
else return 'idle';
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.userAvatarAssigneeInitials.helpers({
|
|
||||||
initials() {
|
|
||||||
const user = Users.findOne(this.userId);
|
|
||||||
return user && user.getInitials();
|
|
||||||
},
|
|
||||||
|
|
||||||
viewPortWidth() {
|
|
||||||
const user = Users.findOne(this.userId);
|
|
||||||
return ((user && user.getInitials().length) || 1) * 12;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// We extends the normal InlinedForm component to support UnsavedEdits draft
|
// We extends the normal InlinedForm component to support UnsavedEdits draft
|
||||||
// feature.
|
// feature.
|
||||||
(class extends InlinedForm {
|
(class extends InlinedForm {
|
||||||
|
|
@ -697,7 +586,7 @@ BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
boards() {
|
boards() {
|
||||||
const boards = Boards.find(
|
return Boards.find(
|
||||||
{
|
{
|
||||||
archived: false,
|
archived: false,
|
||||||
'members.userId': Meteor.userId(),
|
'members.userId': Meteor.userId(),
|
||||||
|
|
@ -707,7 +596,6 @@ BlazeComponent.extendComponent({
|
||||||
sort: { sort: 1 /* boards default sorting */ },
|
sort: { sort: 1 /* boards default sorting */ },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return boards;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
swimlanes() {
|
swimlanes() {
|
||||||
|
|
@ -736,7 +624,7 @@ Template.copyCardPopup.events({
|
||||||
'click .js-done'() {
|
'click .js-done'() {
|
||||||
const card = Cards.findOne(Session.get('currentCard'));
|
const card = Cards.findOne(Session.get('currentCard'));
|
||||||
const lSelect = $('.js-select-lists')[0];
|
const lSelect = $('.js-select-lists')[0];
|
||||||
listId = lSelect.options[lSelect.selectedIndex].value;
|
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||||
const slSelect = $('.js-select-swimlanes')[0];
|
const slSelect = $('.js-select-swimlanes')[0];
|
||||||
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||||
const bSelect = $('.js-select-boards')[0];
|
const bSelect = $('.js-select-boards')[0];
|
||||||
|
|
@ -801,7 +689,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||||
});
|
});
|
||||||
|
|
||||||
// copy subtasks
|
// copy subtasks
|
||||||
cursor = Cards.find({ parentId: oldId });
|
const cursor = Cards.find({ parentId: oldId });
|
||||||
cursor.forEach(function() {
|
cursor.forEach(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
const subtask = arguments[0];
|
const subtask = arguments[0];
|
||||||
|
|
@ -827,7 +715,7 @@ BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
colors() {
|
colors() {
|
||||||
return cardColors.map(color => ({ color, name: '' }));
|
return ALLOWED_COLORS.map(color => ({ color, name: '' }));
|
||||||
},
|
},
|
||||||
|
|
||||||
isSelected(color) {
|
isSelected(color) {
|
||||||
|
|
@ -871,7 +759,7 @@ BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
boards() {
|
boards() {
|
||||||
const boards = Boards.find(
|
return Boards.find(
|
||||||
{
|
{
|
||||||
archived: false,
|
archived: false,
|
||||||
'members.userId': Meteor.userId(),
|
'members.userId': Meteor.userId(),
|
||||||
|
|
@ -883,7 +771,6 @@ BlazeComponent.extendComponent({
|
||||||
sort: { sort: 1 /* boards default sorting */ },
|
sort: { sort: 1 /* boards default sorting */ },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return boards;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
cards() {
|
cards() {
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,7 @@ avatar-radius = 50%
|
||||||
word-wrap: break-word
|
word-wrap: break-word
|
||||||
max-width: 36%
|
max-width: 36%
|
||||||
flex-grow: 1
|
flex-grow: 1
|
||||||
|
&.card-details-item-creator,
|
||||||
&.card-details-item-received,
|
&.card-details-item-received,
|
||||||
&.card-details-item-start,
|
&.card-details-item-start,
|
||||||
&.card-details-item-due,
|
&.card-details-item-due,
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,10 @@ template(name="minicard")
|
||||||
each getMembers
|
each getMembers
|
||||||
+userAvatar(userId=this)
|
+userAvatar(userId=this)
|
||||||
|
|
||||||
|
if showCreator
|
||||||
|
.minicard-creator
|
||||||
|
+userAvatar(userId=this.userId noRemove=true)
|
||||||
|
|
||||||
.badges
|
.badges
|
||||||
unless currentUser.isNoComments
|
unless currentUser.isNoComments
|
||||||
if comments.count
|
if comments.count
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,24 @@ BlazeComponent.extendComponent({
|
||||||
|
|
||||||
return customFieldTrueValue
|
return customFieldTrueValue
|
||||||
.filter(value => !!value.trim())
|
.filter(value => !!value.trim())
|
||||||
.map(value => definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value))
|
.map(value =>
|
||||||
|
definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value),
|
||||||
|
)
|
||||||
.join(definition.settings.stringtemplateSeparator ?? '');
|
.join(definition.settings.stringtemplateSeparator ?? '');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showCreator() {
|
||||||
|
if (this.data().board()) {
|
||||||
|
return (
|
||||||
|
this.data().board.allowsCreator === null ||
|
||||||
|
this.data().board().allowsCreator === undefined ||
|
||||||
|
this.data().board().allowsCreator
|
||||||
|
);
|
||||||
|
// return this.data().board().allowsCreator;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
events() {
|
events() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@
|
||||||
border-radius: 2px
|
border-radius: 2px
|
||||||
margin-right: 3px
|
margin-right: 3px
|
||||||
margin-bottom: 3px
|
margin-bottom: 3px
|
||||||
|
|
||||||
.minicard-custom-fields
|
.minicard-custom-fields
|
||||||
display:block;
|
display:block;
|
||||||
.minicard-custom-field
|
.minicard-custom-field
|
||||||
|
|
@ -163,7 +163,8 @@
|
||||||
line-height: 12px
|
line-height: 12px
|
||||||
|
|
||||||
.minicard-members,
|
.minicard-members,
|
||||||
.minicard-assignees
|
.minicard-assignees,
|
||||||
|
.minicard-creator
|
||||||
float: right
|
float: right
|
||||||
margin-left: 5px
|
margin-left: 5px
|
||||||
margin-bottom: 4px
|
margin-bottom: 4px
|
||||||
|
|
@ -187,6 +188,9 @@
|
||||||
.minicard-assignees
|
.minicard-assignees
|
||||||
border-bottom: 1px solid red
|
border-bottom: 1px solid red
|
||||||
|
|
||||||
|
.minicard-creator
|
||||||
|
border-bottom: 1px solid green
|
||||||
|
|
||||||
.minicard-members:empty,
|
.minicard-members:empty,
|
||||||
.minicard-assignees:empty
|
.minicard-assignees:empty
|
||||||
display: none
|
display: none
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,10 @@ template(name="globalSearchModalTitle")
|
||||||
| {{_ 'globalSearch-title'}}
|
| {{_ 'globalSearch-title'}}
|
||||||
|
|
||||||
template(name="resultsPaged")
|
template(name="resultsPaged")
|
||||||
h1
|
if resultsHeading.get
|
||||||
= resultsHeading.get
|
h1
|
||||||
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
= resultsHeading.get
|
||||||
|
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
||||||
each card in results.get
|
each card in results.get
|
||||||
+resultCard(card)
|
+resultCard(card)
|
||||||
table.global-search-footer
|
table.global-search-footer
|
||||||
|
|
@ -50,8 +51,7 @@ template(name="globalSearch")
|
||||||
each msg in errorMessages
|
each msg in errorMessages
|
||||||
li.global-search-error-messages
|
li.global-search-error-messages
|
||||||
= msg
|
= msg
|
||||||
else
|
+resultsPaged(this)
|
||||||
+resultsPaged(this)
|
|
||||||
else if serverError.get
|
else if serverError.get
|
||||||
.global-search-page
|
.global-search-page
|
||||||
.global-search-help
|
.global-search-help
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||||
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
|
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
|
||||||
operator_assignee: TAPi18n.__('operator-assignee'),
|
operator_assignee: TAPi18n.__('operator-assignee'),
|
||||||
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
|
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
|
||||||
|
operator_creator: TAPi18n.__('operator-creator'),
|
||||||
operator_due: TAPi18n.__('operator-due'),
|
operator_due: TAPi18n.__('operator-due'),
|
||||||
operator_created: TAPi18n.__('operator-created'),
|
operator_created: TAPi18n.__('operator-created'),
|
||||||
operator_modified: TAPi18n.__('operator-modified'),
|
operator_modified: TAPi18n.__('operator-modified'),
|
||||||
|
|
@ -167,6 +168,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||||
['\n* ', 'globalSearch-instructions-operator-at'],
|
['\n* ', 'globalSearch-instructions-operator-at'],
|
||||||
['\n* ', 'globalSearch-instructions-operator-member'],
|
['\n* ', 'globalSearch-instructions-operator-member'],
|
||||||
['\n* ', 'globalSearch-instructions-operator-assignee'],
|
['\n* ', 'globalSearch-instructions-operator-assignee'],
|
||||||
|
['\n* ', 'globalSearch-instructions-operator-creator'],
|
||||||
['\n* ', 'globalSearch-instructions-operator-due'],
|
['\n* ', 'globalSearch-instructions-operator-due'],
|
||||||
['\n* ', 'globalSearch-instructions-operator-created'],
|
['\n* ', 'globalSearch-instructions-operator-created'],
|
||||||
['\n* ', 'globalSearch-instructions-operator-modified'],
|
['\n* ', 'globalSearch-instructions-operator-modified'],
|
||||||
|
|
@ -202,9 +204,8 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
events() {
|
events() {
|
||||||
return [
|
return super.events().concat([
|
||||||
{
|
{
|
||||||
...super.events()[0],
|
|
||||||
'submit .js-search-query-form'(evt) {
|
'submit .js-search-query-form'(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
this.searchAllBoards(evt.target.searchQuery.value);
|
this.searchAllBoards(evt.target.searchQuery.value);
|
||||||
|
|
@ -257,7 +258,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||||
this.hasResults.set(false);
|
this.hasResults.set(false);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
111
client/components/settings/adminReports.jade
Normal file
111
client/components/settings/adminReports.jade
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
template(name="adminReports")
|
||||||
|
.setting-content
|
||||||
|
unless currentUser.isAdmin
|
||||||
|
| {{_ 'error-notAuthorized'}}
|
||||||
|
else
|
||||||
|
.content-body
|
||||||
|
.side-menu
|
||||||
|
ul
|
||||||
|
li
|
||||||
|
a.js-report-broken(data-id="report-broken")
|
||||||
|
i.fa.fa-chain-broken
|
||||||
|
| {{_ 'broken-cards'}}
|
||||||
|
|
||||||
|
li
|
||||||
|
a.js-report-files(data-id="report-orphaned-files")
|
||||||
|
i.fa.fa-paperclip
|
||||||
|
| {{_ 'orphanedFilesReportTitle'}}
|
||||||
|
|
||||||
|
li
|
||||||
|
a.js-report-files(data-id="report-files")
|
||||||
|
i.fa.fa-paperclip
|
||||||
|
| {{_ 'filesReportTitle'}}
|
||||||
|
|
||||||
|
li
|
||||||
|
a.js-report-rules(data-id="report-rules")
|
||||||
|
i.fa.fa-paperclip
|
||||||
|
| {{_ 'rulesReportTitle'}}
|
||||||
|
|
||||||
|
.main-body
|
||||||
|
if loading.get
|
||||||
|
+spinner
|
||||||
|
else if showBrokenCardsReport.get
|
||||||
|
+brokenCardsReport
|
||||||
|
else if showFilesReport.get
|
||||||
|
+filesReport
|
||||||
|
else if showOrphanedFilesReport.get
|
||||||
|
+orphanedFilesReport
|
||||||
|
else if showRulesReport.get
|
||||||
|
+rulesReport
|
||||||
|
|
||||||
|
|
||||||
|
template(name="brokenCardsReport")
|
||||||
|
.global-search-results-list-wrapper
|
||||||
|
h1 {{_ 'broken-cards'}}
|
||||||
|
if resultsCount
|
||||||
|
+resultsPaged(this)
|
||||||
|
else
|
||||||
|
div {{_ 'no-results' }}
|
||||||
|
|
||||||
|
template(name="rulesReport")
|
||||||
|
h1 {{_ 'rulesReportTitle'}}
|
||||||
|
if resultsCount
|
||||||
|
table.table
|
||||||
|
tr
|
||||||
|
th Rule Title
|
||||||
|
th Board Title
|
||||||
|
th actionType
|
||||||
|
th activityType
|
||||||
|
|
||||||
|
each rule in rows
|
||||||
|
tr
|
||||||
|
td {{ rule.title }}
|
||||||
|
td {{ rule.boardTitle }}
|
||||||
|
td {{ rule.action.actionType }}
|
||||||
|
td {{ rule.trigger.activityType }}
|
||||||
|
else
|
||||||
|
div {{_ 'no-results' }}
|
||||||
|
|
||||||
|
template(name="filesReport")
|
||||||
|
h1 {{_ 'filesReportTitle'}}
|
||||||
|
if resultsCount
|
||||||
|
table.table
|
||||||
|
tr
|
||||||
|
th Filename
|
||||||
|
th.right Size (kB)
|
||||||
|
th MIME Type
|
||||||
|
th.center Usage
|
||||||
|
th MD5 Sum
|
||||||
|
th ID
|
||||||
|
|
||||||
|
each att in attachmentFiles
|
||||||
|
tr
|
||||||
|
td {{ att.filename }}
|
||||||
|
td.right {{fileSize att.length }}
|
||||||
|
td {{ att.contentType }}
|
||||||
|
td.center {{usageCount att._id.toHexString }}
|
||||||
|
td {{ att.md5 }}
|
||||||
|
td {{ att._id.toHexString }}
|
||||||
|
else
|
||||||
|
div {{_ 'no-results' }}
|
||||||
|
|
||||||
|
template(name="orphanedFilesReport")
|
||||||
|
h1 {{_ 'orphanedFilesReportTitle'}}
|
||||||
|
if resultsCount
|
||||||
|
table.table
|
||||||
|
tr
|
||||||
|
th Filename
|
||||||
|
th.right Size (kB)
|
||||||
|
th MIME Type
|
||||||
|
th MD5 Sum
|
||||||
|
th ID
|
||||||
|
|
||||||
|
each att in attachmentFiles
|
||||||
|
tr
|
||||||
|
td {{ att.filename }}
|
||||||
|
td.right {{fileSize att.length }}
|
||||||
|
td {{ att.contentType }}
|
||||||
|
td {{ att.md5 }}
|
||||||
|
td {{ att._id.toHexString }}
|
||||||
|
else
|
||||||
|
div {{_ 'no-results' }}
|
||||||
156
client/components/settings/adminReports.js
Normal file
156
client/components/settings/adminReports.js
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
import { AttachmentStorage } from '/models/attachments';
|
||||||
|
import { CardSearchPagedComponent } from '/client/lib/cardSearch';
|
||||||
|
import SessionData from '/models/usersessiondata';
|
||||||
|
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
subscription: null,
|
||||||
|
showFilesReport: new ReactiveVar(false),
|
||||||
|
showBrokenCardsReport: new ReactiveVar(false),
|
||||||
|
showOrphanedFilesReport: new ReactiveVar(false),
|
||||||
|
showRulesReport: new ReactiveVar(false),
|
||||||
|
|
||||||
|
onCreated() {
|
||||||
|
this.error = new ReactiveVar('');
|
||||||
|
this.loading = new ReactiveVar(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
events() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'click a.js-report-broken': this.switchMenu,
|
||||||
|
'click a.js-report-files': this.switchMenu,
|
||||||
|
'click a.js-report-orphaned-files': this.switchMenu,
|
||||||
|
'click a.js-report-rules': this.switchMenu,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
switchMenu(event) {
|
||||||
|
const target = $(event.target);
|
||||||
|
if (!target.hasClass('active')) {
|
||||||
|
this.loading.set(true);
|
||||||
|
this.showFilesReport.set(false);
|
||||||
|
this.showBrokenCardsReport.set(false);
|
||||||
|
this.showOrphanedFilesReport.set(false);
|
||||||
|
if (this.subscription) {
|
||||||
|
this.subscription.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.side-menu li.active').removeClass('active');
|
||||||
|
target.parent().addClass('active');
|
||||||
|
const targetID = target.data('id');
|
||||||
|
|
||||||
|
if ('report-broken' === targetID) {
|
||||||
|
this.showBrokenCardsReport.set(true);
|
||||||
|
this.subscription = Meteor.subscribe(
|
||||||
|
'brokenCards',
|
||||||
|
SessionData.getSessionId(),
|
||||||
|
() => {
|
||||||
|
this.loading.set(false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if ('report-files' === targetID) {
|
||||||
|
this.showFilesReport.set(true);
|
||||||
|
this.subscription = Meteor.subscribe('attachmentsList', () => {
|
||||||
|
this.loading.set(false);
|
||||||
|
});
|
||||||
|
} else if ('report-orphaned-files' === targetID) {
|
||||||
|
this.showOrphanedFilesReport.set(true);
|
||||||
|
this.subscription = Meteor.subscribe('orphanedAttachments', () => {
|
||||||
|
this.loading.set(false);
|
||||||
|
});
|
||||||
|
} else if ('report-rules' === targetID) {
|
||||||
|
this.subscription = Meteor.subscribe('rulesReport', () => {
|
||||||
|
this.showRulesReport.set(true);
|
||||||
|
this.loading.set(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}).register('adminReports');
|
||||||
|
|
||||||
|
Template.filesReport.helpers({
|
||||||
|
attachmentFiles() {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
// console.log('attachments:', AttachmentStorage.find());
|
||||||
|
// console.log('attachments.count:', AttachmentStorage.find().count());
|
||||||
|
return AttachmentStorage.find();
|
||||||
|
},
|
||||||
|
|
||||||
|
rulesReport() {
|
||||||
|
const rules = [];
|
||||||
|
|
||||||
|
Rules.find().forEach(rule => {
|
||||||
|
rules.push({
|
||||||
|
_id: rule._id,
|
||||||
|
title: rule.title,
|
||||||
|
boardId: rule.boardId,
|
||||||
|
boardTitle: rule.board().title,
|
||||||
|
action: rule.action().fetch(),
|
||||||
|
trigger: rule.trigger().fetch(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
},
|
||||||
|
|
||||||
|
resultsCount() {
|
||||||
|
return AttachmentStorage.find().count();
|
||||||
|
},
|
||||||
|
|
||||||
|
fileSize(size) {
|
||||||
|
return Math.round(size / 1024);
|
||||||
|
},
|
||||||
|
|
||||||
|
usageCount(key) {
|
||||||
|
return Attachments.find({ 'copies.attachments.key': key }).count();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.orphanedFilesReport.helpers({
|
||||||
|
attachmentFiles() {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
// console.log('attachments:', AttachmentStorage.find());
|
||||||
|
// console.log('attachments.count:', AttachmentStorage.find().count());
|
||||||
|
return AttachmentStorage.find();
|
||||||
|
},
|
||||||
|
|
||||||
|
resultsCount() {
|
||||||
|
return AttachmentStorage.find().count();
|
||||||
|
},
|
||||||
|
|
||||||
|
fileSize(size) {
|
||||||
|
return Math.round(size / 1024);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.rulesReport.helpers({
|
||||||
|
rows() {
|
||||||
|
const rules = [];
|
||||||
|
|
||||||
|
Rules.find().forEach(rule => {
|
||||||
|
rules.push({
|
||||||
|
_id: rule._id,
|
||||||
|
title: rule.title,
|
||||||
|
boardId: rule.boardId,
|
||||||
|
boardTitle: rule.board().title,
|
||||||
|
action: rule.action(),
|
||||||
|
trigger: rule.trigger(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('rows:', rules);
|
||||||
|
return rules;
|
||||||
|
},
|
||||||
|
|
||||||
|
resultsCount() {
|
||||||
|
return Rules.find().count();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
class BrokenCardsComponent extends CardSearchPagedComponent {
|
||||||
|
onCreated() {
|
||||||
|
super.onCreated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BrokenCardsComponent.register('brokenCardsReport');
|
||||||
|
|
@ -12,6 +12,10 @@ template(name="settingHeaderBar")
|
||||||
i.fa(class="fa-users")
|
i.fa(class="fa-users")
|
||||||
span {{_ 'people'}}
|
span {{_ 'people'}}
|
||||||
|
|
||||||
|
a.setting-header-btn.informations(href="{{pathFor 'admin-reports'}}")
|
||||||
|
i.fa(class="fa-list")
|
||||||
|
span {{_ 'reports'}}
|
||||||
|
|
||||||
a.setting-header-btn.informations(href="{{pathFor 'information'}}")
|
a.setting-header-btn.informations(href="{{pathFor 'information'}}")
|
||||||
i.fa(class="fa-info-circle")
|
i.fa(class="fa-info-circle")
|
||||||
span {{_ 'info'}}
|
span {{_ 'info'}}
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,14 @@ template(name="boardCardSettingsPopup")
|
||||||
span
|
span
|
||||||
i.fa.fa-users
|
i.fa.fa-users
|
||||||
| {{_ 'members'}}
|
| {{_ 'members'}}
|
||||||
|
|
||||||
|
div.check-div
|
||||||
|
a.flex.js-field-has-creator(class="{{#if allowsCreator}}is-checked{{/if}}")
|
||||||
|
.materialCheckBox(class="{{#if allowsCreator}}is-checked{{/if}}")
|
||||||
|
span
|
||||||
|
i.fa.fa-user
|
||||||
|
| {{_ 'creator'}}
|
||||||
|
|
||||||
div.check-div
|
div.check-div
|
||||||
a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
||||||
.materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
.materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
||||||
|
|
|
||||||
|
|
@ -730,6 +730,14 @@ BlazeComponent.extendComponent({
|
||||||
return this.currentBoard.allowsSubtasks;
|
return this.currentBoard.allowsSubtasks;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
allowsCreator() {
|
||||||
|
return (
|
||||||
|
this.currentBoard.allowsCreator === null ||
|
||||||
|
this.currentBoard.allowsCreator === undefined ||
|
||||||
|
this.currentBoard.allowsCreator
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
allowsMembers() {
|
allowsMembers() {
|
||||||
return this.currentBoard.allowsMembers;
|
return this.currentBoard.allowsMembers;
|
||||||
},
|
},
|
||||||
|
|
@ -889,6 +897,19 @@ BlazeComponent.extendComponent({
|
||||||
this.currentBoard.allowsSubtasks,
|
this.currentBoard.allowsSubtasks,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
'click .js-field-has-creator'(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
this.currentBoard.allowsCreator = !this.currentBoard.allowsCreator;
|
||||||
|
this.currentBoard.setAllowsCreator(this.currentBoard.allowsCreator);
|
||||||
|
$(`.js-field-has-creator ${MCB}`).toggleClass(
|
||||||
|
CKCLS,
|
||||||
|
this.currentBoard.allowsCreator,
|
||||||
|
);
|
||||||
|
$('.js-field-has-creator').toggleClass(
|
||||||
|
CKCLS,
|
||||||
|
this.currentBoard.allowsCreator,
|
||||||
|
);
|
||||||
|
},
|
||||||
'click .js-field-has-members'(evt) {
|
'click .js-field-has-members'(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
|
this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
template(name="userAvatar")
|
template(name="userAvatar")
|
||||||
a.member.js-member(title="{{userData.profile.fullname}} ({{userData.username}}) {{_ memberType}}")
|
a.member(class="js-{{#if assignee}}assignee{{else}}member{{/if}}" title="{{userData.profile.fullname}} ({{userData.username}}) {{_ memberType}}")
|
||||||
if userData.profile.avatarUrl
|
if userData.profile.avatarUrl
|
||||||
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
|
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
|
||||||
else
|
else
|
||||||
|
|
@ -72,9 +72,10 @@ template(name="cardMemberPopup")
|
||||||
h3= user.profile.fullname
|
h3= user.profile.fullname
|
||||||
p.quiet @{{ user.username }}
|
p.quiet @{{ user.username }}
|
||||||
ul.pop-over-list
|
ul.pop-over-list
|
||||||
if currentUser.isNotCommentOnly
|
unless noRemove
|
||||||
if currentUser.isNotWorker
|
if currentUser.isNotCommentOnly
|
||||||
li: a.js-remove-member {{_ 'remove-member-from-card'}}
|
if currentUser.isNotWorker
|
||||||
|
li: a.js-remove-member {{_ 'remove-member-from-card'}}
|
||||||
|
|
||||||
if $eq currentUser._id user._id
|
if $eq currentUser._id user._id
|
||||||
with currentUser
|
with currentUser
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Cards from '/models/cards';
|
||||||
|
import Avatars from '/models/avatars';
|
||||||
|
import Users from '/models/users';
|
||||||
|
|
||||||
Template.userAvatar.helpers({
|
Template.userAvatar.helpers({
|
||||||
userData() {
|
userData() {
|
||||||
// We need to handle a special case for the search results provided by the
|
// We need to handle a special case for the search results provided by the
|
||||||
|
|
@ -30,10 +34,6 @@ Template.userAvatar.helpers({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.userAvatar.events({
|
|
||||||
'click .js-change-avatar': Popup.open('changeAvatar'),
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.userAvatarInitials.helpers({
|
Template.userAvatarInitials.helpers({
|
||||||
initials() {
|
initials() {
|
||||||
const user = Users.findOne(this.userId);
|
const user = Users.findOne(this.userId);
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,6 @@ template(name="memberMenuPopup")
|
||||||
a.js-global-search(href="{{pathFor 'global-search'}}")
|
a.js-global-search(href="{{pathFor 'global-search'}}")
|
||||||
i.fa.fa-search
|
i.fa.fa-search
|
||||||
| {{_ 'globalSearch-title'}}
|
| {{_ 'globalSearch-title'}}
|
||||||
li
|
|
||||||
a.js-broken-cards(href="{{pathFor 'broken-cards'}}")
|
|
||||||
i.fa.fa-chain-broken
|
|
||||||
| {{_ 'broken-cards'}}
|
|
||||||
li
|
li
|
||||||
a(href="{{pathFor 'home'}}")
|
a(href="{{pathFor 'home'}}")
|
||||||
span.fa.fa-home
|
span.fa.fa-home
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,11 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||||
that.searching.set(false);
|
that.searching.set(false);
|
||||||
that.hasResults.set(false);
|
that.hasResults.set(false);
|
||||||
that.serverError.set(true);
|
that.serverError.set(true);
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('Error.reason:', error.reason);
|
console.log('Error.reason:', error.reason);
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('Error.message:', error.message);
|
console.log('Error.message:', error.message);
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('Error.stack:', error.stack);
|
console.log('Error.stack:', error.stack);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -72,7 +75,7 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||||
if (this.queryErrors.length) {
|
if (this.queryErrors.length) {
|
||||||
// console.log('queryErrors:', this.queryErrorMessages());
|
// console.log('queryErrors:', this.queryErrorMessages());
|
||||||
this.hasQueryErrors.set(true);
|
this.hasQueryErrors.set(true);
|
||||||
return null;
|
// return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cards) {
|
if (cards) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours
|
// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours
|
||||||
function adjustedTimeFormat() {
|
function adjustedTimeFormat() {
|
||||||
return moment
|
return moment
|
||||||
|
|
@ -6,17 +8,17 @@ function adjustedTimeFormat() {
|
||||||
.replace(/HH/i, 'H');
|
.replace(/HH/i, 'H');
|
||||||
}
|
}
|
||||||
|
|
||||||
DatePicker = BlazeComponent.extendComponent({
|
export class DatePicker extends BlazeComponent {
|
||||||
template() {
|
template() {
|
||||||
return 'datepicker';
|
return 'datepicker';
|
||||||
},
|
}
|
||||||
|
|
||||||
onCreated(defaultTime = '1970-01-01 08:00:00') {
|
onCreated(defaultTime = '1970-01-01 08:00:00') {
|
||||||
this.error = new ReactiveVar('');
|
this.error = new ReactiveVar('');
|
||||||
this.card = this.data();
|
this.card = this.data();
|
||||||
this.date = new ReactiveVar(moment.invalid());
|
this.date = new ReactiveVar(moment.invalid());
|
||||||
this.defaultTime = defaultTime;
|
this.defaultTime = defaultTime;
|
||||||
},
|
}
|
||||||
|
|
||||||
startDayOfWeek() {
|
startDayOfWeek() {
|
||||||
const currentUser = Meteor.user();
|
const currentUser = Meteor.user();
|
||||||
|
|
@ -25,7 +27,7 @@ DatePicker = BlazeComponent.extendComponent({
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onRendered() {
|
onRendered() {
|
||||||
const $picker = this.$('.js-datepicker')
|
const $picker = this.$('.js-datepicker')
|
||||||
|
|
@ -42,7 +44,7 @@ DatePicker = BlazeComponent.extendComponent({
|
||||||
this.error.set('');
|
this.error.set('');
|
||||||
const timeInput = this.find('#time');
|
const timeInput = this.find('#time');
|
||||||
timeInput.focus();
|
timeInput.focus();
|
||||||
if (!timeInput.value) {
|
if (!timeInput.value && this.defaultTime) {
|
||||||
const currentHour = evt.date.getHours();
|
const currentHour = evt.date.getHours();
|
||||||
const defaultMoment = moment(
|
const defaultMoment = moment(
|
||||||
currentHour > 0 ? evt.date : this.defaultTime,
|
currentHour > 0 ? evt.date : this.defaultTime,
|
||||||
|
|
@ -55,22 +57,22 @@ DatePicker = BlazeComponent.extendComponent({
|
||||||
if (this.date.get().isValid()) {
|
if (this.date.get().isValid()) {
|
||||||
$picker.datepicker('update', this.date.get().toDate());
|
$picker.datepicker('update', this.date.get().toDate());
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
showDate() {
|
showDate() {
|
||||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
if (this.date.get().isValid()) return this.date.get().format('L');
|
||||||
return '';
|
return '';
|
||||||
},
|
}
|
||||||
showTime() {
|
showTime() {
|
||||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
if (this.date.get().isValid()) return this.date.get().format('LT');
|
||||||
return '';
|
return '';
|
||||||
},
|
}
|
||||||
dateFormat() {
|
dateFormat() {
|
||||||
return moment.localeData().longDateFormat('L');
|
return moment.localeData().longDateFormat('L');
|
||||||
},
|
}
|
||||||
timeFormat() {
|
timeFormat() {
|
||||||
return moment.localeData().longDateFormat('LT');
|
return moment.localeData().longDateFormat('LT');
|
||||||
},
|
}
|
||||||
|
|
||||||
events() {
|
events() {
|
||||||
return [
|
return [
|
||||||
|
|
@ -106,7 +108,7 @@ DatePicker = BlazeComponent.extendComponent({
|
||||||
const dateString = `${evt.target.date.value} ${time}`;
|
const dateString = `${evt.target.date.value} ${time}`;
|
||||||
const newCompleteDate = moment(
|
const newCompleteDate = moment(
|
||||||
dateString,
|
dateString,
|
||||||
'L ' + adjustedTimeFormat(),
|
`L ${adjustedTimeFormat()}`,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if (!newTime.isValid()) {
|
if (!newTime.isValid()) {
|
||||||
|
|
@ -120,10 +122,8 @@ DatePicker = BlazeComponent.extendComponent({
|
||||||
if (newCompleteDate.isValid()) {
|
if (newCompleteDate.isValid()) {
|
||||||
this._storeDate(newCompleteDate.toDate());
|
this._storeDate(newCompleteDate.toDate());
|
||||||
Popup.close();
|
Popup.close();
|
||||||
} else {
|
} else if (!this.error) {
|
||||||
if (!this.error) {
|
this.error.set('invalid');
|
||||||
this.error.set('invalid');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'click .js-delete-date'(evt) {
|
'click .js-delete-date'(evt) {
|
||||||
|
|
@ -133,5 +133,5 @@ DatePicker = BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
||||||
51
config/const.js
Normal file
51
config/const.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
export const ALLOWED_BOARD_COLORS = [
|
||||||
|
'belize',
|
||||||
|
'nephritis',
|
||||||
|
'pomegranate',
|
||||||
|
'pumpkin',
|
||||||
|
'wisteria',
|
||||||
|
'moderatepink',
|
||||||
|
'strongcyan',
|
||||||
|
'limegreen',
|
||||||
|
'midnight',
|
||||||
|
'dark',
|
||||||
|
'relax',
|
||||||
|
'corteza',
|
||||||
|
'clearblue',
|
||||||
|
'natural',
|
||||||
|
'modern',
|
||||||
|
'moderndark',
|
||||||
|
];
|
||||||
|
export const ALLOWED_COLORS = [
|
||||||
|
'white',
|
||||||
|
'green',
|
||||||
|
'yellow',
|
||||||
|
'orange',
|
||||||
|
'red',
|
||||||
|
'purple',
|
||||||
|
'blue',
|
||||||
|
'sky',
|
||||||
|
'lime',
|
||||||
|
'pink',
|
||||||
|
'black',
|
||||||
|
'silver',
|
||||||
|
'peachpuff',
|
||||||
|
'crimson',
|
||||||
|
'plum',
|
||||||
|
'darkgreen',
|
||||||
|
'slateblue',
|
||||||
|
'magenta',
|
||||||
|
'gold',
|
||||||
|
'navy',
|
||||||
|
'gray',
|
||||||
|
'saddlebrown',
|
||||||
|
'paleturquoise',
|
||||||
|
'mistyrose',
|
||||||
|
'indigo',
|
||||||
|
];
|
||||||
|
export const TYPE_BOARD = 'board';
|
||||||
|
export const TYPE_CARD = 'cardType-card';
|
||||||
|
export const TYPE_LINKED_BOARD = 'cardType-linkedBoard';
|
||||||
|
export const TYPE_LINKED_CARD = 'cardType-linkedCard';
|
||||||
|
export const TYPE_TEMPLATE_BOARD = 'template-board';
|
||||||
|
export const TYPE_TEMPLATE_CONTAINER = 'template-container';
|
||||||
|
|
@ -3,6 +3,7 @@ import {
|
||||||
OPERATOR_BOARD,
|
OPERATOR_BOARD,
|
||||||
OPERATOR_COMMENT,
|
OPERATOR_COMMENT,
|
||||||
OPERATOR_CREATED_AT,
|
OPERATOR_CREATED_AT,
|
||||||
|
OPERATOR_CREATOR,
|
||||||
OPERATOR_DUE,
|
OPERATOR_DUE,
|
||||||
OPERATOR_HAS,
|
OPERATOR_HAS,
|
||||||
OPERATOR_LABEL,
|
OPERATOR_LABEL,
|
||||||
|
|
@ -107,6 +108,7 @@ export class QueryErrors {
|
||||||
[OPERATOR_USER, 'user-username-not-found'],
|
[OPERATOR_USER, 'user-username-not-found'],
|
||||||
[OPERATOR_ASSIGNEE, 'user-username-not-found'],
|
[OPERATOR_ASSIGNEE, 'user-username-not-found'],
|
||||||
[OPERATOR_MEMBER, 'user-username-not-found'],
|
[OPERATOR_MEMBER, 'user-username-not-found'],
|
||||||
|
[OPERATOR_CREATOR, 'user-username-not-found'],
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -238,6 +240,7 @@ export class Query {
|
||||||
'operator-member': OPERATOR_MEMBER,
|
'operator-member': OPERATOR_MEMBER,
|
||||||
'operator-member-abbrev': OPERATOR_MEMBER,
|
'operator-member-abbrev': OPERATOR_MEMBER,
|
||||||
'operator-assignee': OPERATOR_ASSIGNEE,
|
'operator-assignee': OPERATOR_ASSIGNEE,
|
||||||
|
'operator-creator': OPERATOR_CREATOR,
|
||||||
'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
|
'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
|
||||||
'operator-status': OPERATOR_STATUS,
|
'operator-status': OPERATOR_STATUS,
|
||||||
'operator-due': OPERATOR_DUE,
|
'operator-due': OPERATOR_DUE,
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,28 @@ FlowRouter.route('/people', {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/admin-reports', {
|
||||||
|
name: 'admin-reports',
|
||||||
|
triggersEnter: [
|
||||||
|
AccountsTemplates.ensureSignedIn,
|
||||||
|
() => {
|
||||||
|
Session.set('currentBoard', null);
|
||||||
|
Session.set('currentList', null);
|
||||||
|
Session.set('currentCard', null);
|
||||||
|
|
||||||
|
Filter.reset();
|
||||||
|
Session.set('sortBy', '');
|
||||||
|
EscapeActions.executeAll();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
action() {
|
||||||
|
BlazeLayout.render('defaultLayout', {
|
||||||
|
headerBar: 'settingHeaderBar',
|
||||||
|
content: 'adminReports',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
FlowRouter.notFound = {
|
FlowRouter.notFound = {
|
||||||
action() {
|
action() {
|
||||||
BlazeLayout.render('defaultLayout', { content: 'notFound' });
|
BlazeLayout.render('defaultLayout', { content: 'notFound' });
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
export const DEFAULT_LIMIT = 25;
|
export const DEFAULT_LIMIT = 25;
|
||||||
export const OPERATOR_ASSIGNEE = 'assignee';
|
export const OPERATOR_ASSIGNEE = 'assignees';
|
||||||
export const OPERATOR_COMMENT = 'comment';
|
export const OPERATOR_COMMENT = 'comment';
|
||||||
export const OPERATOR_CREATED_AT = 'createdAt';
|
export const OPERATOR_CREATED_AT = 'createdAt';
|
||||||
|
export const OPERATOR_CREATOR = 'userId';
|
||||||
export const OPERATOR_DUE = 'dueAt';
|
export const OPERATOR_DUE = 'dueAt';
|
||||||
export const OPERATOR_BOARD = 'board';
|
export const OPERATOR_BOARD = 'board';
|
||||||
export const OPERATOR_HAS = 'has';
|
export const OPERATOR_HAS = 'has';
|
||||||
export const OPERATOR_LABEL = 'label';
|
export const OPERATOR_LABEL = 'label';
|
||||||
export const OPERATOR_LIMIT = 'limit';
|
export const OPERATOR_LIMIT = 'limit';
|
||||||
export const OPERATOR_LIST = 'list';
|
export const OPERATOR_LIST = 'list';
|
||||||
export const OPERATOR_MEMBER = 'member';
|
export const OPERATOR_MEMBER = 'members';
|
||||||
export const OPERATOR_MODIFIED_AT = 'modifiedAt';
|
export const OPERATOR_MODIFIED_AT = 'modifiedAt';
|
||||||
export const OPERATOR_SORT = 'sort';
|
export const OPERATOR_SORT = 'sort';
|
||||||
export const OPERATOR_STATUS = 'status';
|
export const OPERATOR_STATUS = 'status';
|
||||||
|
|
|
||||||
|
|
@ -901,6 +901,7 @@
|
||||||
"operator-member-abbrev": "m",
|
"operator-member-abbrev": "m",
|
||||||
"operator-assignee": "assignee",
|
"operator-assignee": "assignee",
|
||||||
"operator-assignee-abbrev": "a",
|
"operator-assignee-abbrev": "a",
|
||||||
|
"operator-creator": "creator",
|
||||||
"operator-status": "status",
|
"operator-status": "status",
|
||||||
"operator-due": "due",
|
"operator-due": "due",
|
||||||
"operator-created": "created",
|
"operator-created": "created",
|
||||||
|
|
@ -952,6 +953,7 @@
|
||||||
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:<username>`",
|
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:<username>`",
|
||||||
"globalSearch-instructions-operator-member": "`__operator_member__:<username>` - cards where *<username>* is a *member*",
|
"globalSearch-instructions-operator-member": "`__operator_member__:<username>` - cards where *<username>* is a *member*",
|
||||||
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:<username>` - cards where *<username>* is an *assignee*",
|
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:<username>` - cards where *<username>* is an *assignee*",
|
||||||
|
"globalSearch-instructions-operator-creator": "`__operator_creator__:<username>` - cards where *<username>* is the card's creator",
|
||||||
"globalSearch-instructions-operator-due": "`__operator_due__:<n>` - cards which are due up to *<n>* days from now. `__operator_due__:__predicate_overdue__ lists all cards past their due date.",
|
"globalSearch-instructions-operator-due": "`__operator_due__:<n>` - cards which are due up to *<n>* days from now. `__operator_due__:__predicate_overdue__ lists all cards past their due date.",
|
||||||
"globalSearch-instructions-operator-created": "`__operator_created__:<n>` - cards which were created *<n>* days ago or less",
|
"globalSearch-instructions-operator-created": "`__operator_created__:<n>` - cards which were created *<n>* days ago or less",
|
||||||
"globalSearch-instructions-operator-modified": "`__operator_modified__:<n>` - cards which were modified *<n>* days ago or less",
|
"globalSearch-instructions-operator-modified": "`__operator_modified__:<n>` - cards which were modified *<n>* days ago or less",
|
||||||
|
|
@ -992,5 +994,9 @@
|
||||||
"custom-field-stringtemplate": "String Template",
|
"custom-field-stringtemplate": "String Template",
|
||||||
"custom-field-stringtemplate-format": "Format (use %{value} as placeholder)",
|
"custom-field-stringtemplate-format": "Format (use %{value} as placeholder)",
|
||||||
"custom-field-stringtemplate-separator": "Separator (use   or for a space)",
|
"custom-field-stringtemplate-separator": "Separator (use   or for a space)",
|
||||||
"custom-field-stringtemplate-item-placeholder": "Press enter to add more items"
|
"custom-field-stringtemplate-item-placeholder": "Press enter to add more items",
|
||||||
|
"creator": "Creator",
|
||||||
|
"filesReportTitle": "Files Report",
|
||||||
|
"orphanedFilesReportTitle": "Orphaned Files Report",
|
||||||
|
"reports": "Reports"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
export const AttachmentStorage = new Mongo.Collection(
|
||||||
|
'cfs_gridfs.attachments.files',
|
||||||
|
);
|
||||||
|
export const AvatarStorage = new Mongo.Collection('cfs_gridfs.avatars.files');
|
||||||
|
|
||||||
const localFSStore = process.env.ATTACHMENTS_STORE_PATH;
|
const localFSStore = process.env.ATTACHMENTS_STORE_PATH;
|
||||||
const storeName = 'attachments';
|
const storeName = 'attachments';
|
||||||
const defaultStoreOptions = {
|
const defaultStoreOptions = {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
|
import {
|
||||||
|
ALLOWED_BOARD_COLORS,
|
||||||
|
ALLOWED_COLORS,
|
||||||
|
TYPE_BOARD,
|
||||||
|
TYPE_TEMPLATE_BOARD,
|
||||||
|
TYPE_TEMPLATE_CONTAINER,
|
||||||
|
} from '/config/const';
|
||||||
|
|
||||||
const escapeForRegex = require('escape-string-regexp');
|
const escapeForRegex = require('escape-string-regexp');
|
||||||
Boards = new Mongo.Collection('boards');
|
Boards = new Mongo.Collection('boards');
|
||||||
|
|
||||||
|
|
@ -144,32 +152,7 @@ Boards.attachSchema(
|
||||||
* `saddlebrown`, `paleturquoise`, `mistyrose`, `indigo`
|
* `saddlebrown`, `paleturquoise`, `mistyrose`, `indigo`
|
||||||
*/
|
*/
|
||||||
type: String,
|
type: String,
|
||||||
allowedValues: [
|
allowedValues: ALLOWED_COLORS,
|
||||||
'green',
|
|
||||||
'yellow',
|
|
||||||
'orange',
|
|
||||||
'red',
|
|
||||||
'purple',
|
|
||||||
'blue',
|
|
||||||
'sky',
|
|
||||||
'lime',
|
|
||||||
'pink',
|
|
||||||
'black',
|
|
||||||
'silver',
|
|
||||||
'peachpuff',
|
|
||||||
'crimson',
|
|
||||||
'plum',
|
|
||||||
'darkgreen',
|
|
||||||
'slateblue',
|
|
||||||
'magenta',
|
|
||||||
'gold',
|
|
||||||
'navy',
|
|
||||||
'gray',
|
|
||||||
'saddlebrown',
|
|
||||||
'paleturquoise',
|
|
||||||
'mistyrose',
|
|
||||||
'indigo',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
// XXX We might want to maintain more informations under the member sub-
|
// XXX We might want to maintain more informations under the member sub-
|
||||||
// documents like de-normalized meta-data (the date the member joined the
|
// documents like de-normalized meta-data (the date the member joined the
|
||||||
|
|
@ -246,28 +229,11 @@ Boards.attachSchema(
|
||||||
* The color of the board.
|
* The color of the board.
|
||||||
*/
|
*/
|
||||||
type: String,
|
type: String,
|
||||||
allowedValues: [
|
allowedValues: ALLOWED_BOARD_COLORS,
|
||||||
'belize',
|
|
||||||
'nephritis',
|
|
||||||
'pomegranate',
|
|
||||||
'pumpkin',
|
|
||||||
'wisteria',
|
|
||||||
'moderatepink',
|
|
||||||
'strongcyan',
|
|
||||||
'limegreen',
|
|
||||||
'midnight',
|
|
||||||
'dark',
|
|
||||||
'relax',
|
|
||||||
'corteza',
|
|
||||||
'clearblue',
|
|
||||||
'natural',
|
|
||||||
'modern',
|
|
||||||
'moderndark',
|
|
||||||
],
|
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
autoValue() {
|
autoValue() {
|
||||||
if (this.isInsert && !this.isSet) {
|
if (this.isInsert && !this.isSet) {
|
||||||
return Boards.simpleSchema()._schema.color.allowedValues[0];
|
return ALLOWED_BOARD_COLORS[0];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -372,6 +338,14 @@ Boards.attachSchema(
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
allowsCreator: {
|
||||||
|
/**
|
||||||
|
* Does the board allow creator?
|
||||||
|
*/
|
||||||
|
type: Boolean,
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
|
||||||
allowsAssignee: {
|
allowsAssignee: {
|
||||||
/**
|
/**
|
||||||
* Does the board allows assignee?
|
* Does the board allows assignee?
|
||||||
|
|
@ -497,9 +471,11 @@ Boards.attachSchema(
|
||||||
type: {
|
type: {
|
||||||
/**
|
/**
|
||||||
* The type of board
|
* The type of board
|
||||||
|
* possible values: board, template-board, template-container
|
||||||
*/
|
*/
|
||||||
type: String,
|
type: String,
|
||||||
defaultValue: 'board',
|
defaultValue: TYPE_BOARD,
|
||||||
|
allowedValues: [TYPE_BOARD, TYPE_TEMPLATE_BOARD, TYPE_TEMPLATE_CONTAINER],
|
||||||
},
|
},
|
||||||
sort: {
|
sort: {
|
||||||
/**
|
/**
|
||||||
|
|
@ -1187,6 +1163,10 @@ Boards.mutations({
|
||||||
return { $set: { allowsSubtasks } };
|
return { $set: { allowsSubtasks } };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setAllowsCreator(allowsCreator) {
|
||||||
|
return { $set: { allowsCreator } };
|
||||||
|
},
|
||||||
|
|
||||||
setAllowsMembers(allowsMembers) {
|
setAllowsMembers(allowsMembers) {
|
||||||
return { $set: { allowsMembers } };
|
return { $set: { allowsMembers } };
|
||||||
},
|
},
|
||||||
|
|
@ -1318,8 +1298,11 @@ Boards.userBoards = (userId, archived = false, selector = {}) => {
|
||||||
if (typeof archived === 'boolean') {
|
if (typeof archived === 'boolean') {
|
||||||
selector.archived = archived;
|
selector.archived = archived;
|
||||||
}
|
}
|
||||||
selector.$or = [{ permission: 'public' }];
|
if (!selector.type) {
|
||||||
|
selector.type = 'board';
|
||||||
|
}
|
||||||
|
|
||||||
|
selector.$or = [{ permission: 'public' }];
|
||||||
if (userId) {
|
if (userId) {
|
||||||
selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
|
selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
|
||||||
}
|
}
|
||||||
|
|
@ -1341,7 +1324,7 @@ Boards.colorMap = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
Boards.labelColors = () => {
|
Boards.labelColors = () => {
|
||||||
return _.clone(Boards.simpleSchema()._schema['labels.$.color'].allowedValues);
|
return ALLOWED_COLORS;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
if (Meteor.isServer) {
|
||||||
|
|
@ -1423,17 +1406,15 @@ if (Meteor.isServer) {
|
||||||
},
|
},
|
||||||
myLabelNames() {
|
myLabelNames() {
|
||||||
let names = [];
|
let names = [];
|
||||||
Boards.userBoards(Meteor.userId(), false, { type: 'board' }).forEach(
|
Boards.userBoards(Meteor.userId()).forEach(board => {
|
||||||
board => {
|
names = names.concat(
|
||||||
names = names.concat(
|
board.labels
|
||||||
board.labels
|
.filter(label => !!label.name)
|
||||||
.filter(label => !!label.name)
|
.map(label => {
|
||||||
.map(label => {
|
return label.name;
|
||||||
return label.name;
|
}),
|
||||||
}),
|
);
|
||||||
);
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
return _.uniq(names).sort();
|
return _.uniq(names).sort();
|
||||||
},
|
},
|
||||||
myBoardNames() {
|
myBoardNames() {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
import {
|
||||||
|
ALLOWED_COLORS,
|
||||||
|
TYPE_CARD,
|
||||||
|
TYPE_LINKED_BOARD,
|
||||||
|
TYPE_LINKED_CARD,
|
||||||
|
} from '../config/const';
|
||||||
|
|
||||||
Cards = new Mongo.Collection('cards');
|
Cards = new Mongo.Collection('cards');
|
||||||
|
|
||||||
// XXX To improve pub/sub performances a card document should include a
|
// XXX To improve pub/sub performances a card document should include a
|
||||||
|
|
@ -77,33 +84,7 @@ Cards.attachSchema(
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
allowedValues: [
|
allowedValues: ALLOWED_COLORS,
|
||||||
'white',
|
|
||||||
'green',
|
|
||||||
'yellow',
|
|
||||||
'orange',
|
|
||||||
'red',
|
|
||||||
'purple',
|
|
||||||
'blue',
|
|
||||||
'sky',
|
|
||||||
'lime',
|
|
||||||
'pink',
|
|
||||||
'black',
|
|
||||||
'silver',
|
|
||||||
'peachpuff',
|
|
||||||
'crimson',
|
|
||||||
'plum',
|
|
||||||
'darkgreen',
|
|
||||||
'slateblue',
|
|
||||||
'magenta',
|
|
||||||
'gold',
|
|
||||||
'navy',
|
|
||||||
'gray',
|
|
||||||
'saddlebrown',
|
|
||||||
'paleturquoise',
|
|
||||||
'mistyrose',
|
|
||||||
'indigo',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
createdAt: {
|
createdAt: {
|
||||||
/**
|
/**
|
||||||
|
|
@ -305,7 +286,8 @@ Cards.attachSchema(
|
||||||
* type of the card
|
* type of the card
|
||||||
*/
|
*/
|
||||||
type: String,
|
type: String,
|
||||||
defaultValue: 'cardType-card',
|
defaultValue: TYPE_CARD,
|
||||||
|
allowedValues: [TYPE_CARD, TYPE_LINKED_CARD, TYPE_LINKED_BOARD],
|
||||||
},
|
},
|
||||||
linkedId: {
|
linkedId: {
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ALLOWED_COLORS } from '/config/const';
|
||||||
|
|
||||||
Lists = new Mongo.Collection('lists');
|
Lists = new Mongo.Collection('lists');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -144,32 +146,7 @@ Lists.attachSchema(
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
// silver is the default, so it is left out
|
// silver is the default, so it is left out
|
||||||
allowedValues: [
|
allowedValues: ALLOWED_COLORS,
|
||||||
'white',
|
|
||||||
'green',
|
|
||||||
'yellow',
|
|
||||||
'orange',
|
|
||||||
'red',
|
|
||||||
'purple',
|
|
||||||
'blue',
|
|
||||||
'sky',
|
|
||||||
'lime',
|
|
||||||
'pink',
|
|
||||||
'black',
|
|
||||||
'peachpuff',
|
|
||||||
'crimson',
|
|
||||||
'plum',
|
|
||||||
'darkgreen',
|
|
||||||
'slateblue',
|
|
||||||
'magenta',
|
|
||||||
'gold',
|
|
||||||
'navy',
|
|
||||||
'gray',
|
|
||||||
'saddlebrown',
|
|
||||||
'paleturquoise',
|
|
||||||
'mistyrose',
|
|
||||||
'indigo',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,15 @@ Rules.helpers({
|
||||||
getTrigger() {
|
getTrigger() {
|
||||||
return Triggers.findOne({ _id: this.triggerId });
|
return Triggers.findOne({ _id: this.triggerId });
|
||||||
},
|
},
|
||||||
|
board() {
|
||||||
|
return Boards.findOne({ _id: this.boardId });
|
||||||
|
},
|
||||||
|
trigger() {
|
||||||
|
return Triggers.findOne({ _id: this.triggerId });
|
||||||
|
},
|
||||||
|
action() {
|
||||||
|
return Actions.findOne({ _id: this.actionId });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Rules.allow({
|
Rules.allow({
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ALLOWED_COLORS } from '/config/const';
|
||||||
|
|
||||||
Swimlanes = new Mongo.Collection('swimlanes');
|
Swimlanes = new Mongo.Collection('swimlanes');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,32 +70,7 @@ Swimlanes.attachSchema(
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
// silver is the default, so it is left out
|
// silver is the default, so it is left out
|
||||||
allowedValues: [
|
allowedValues: ALLOWED_COLORS,
|
||||||
'white',
|
|
||||||
'green',
|
|
||||||
'yellow',
|
|
||||||
'orange',
|
|
||||||
'red',
|
|
||||||
'purple',
|
|
||||||
'blue',
|
|
||||||
'sky',
|
|
||||||
'lime',
|
|
||||||
'pink',
|
|
||||||
'black',
|
|
||||||
'peachpuff',
|
|
||||||
'crimson',
|
|
||||||
'plum',
|
|
||||||
'darkgreen',
|
|
||||||
'slateblue',
|
|
||||||
'magenta',
|
|
||||||
'gold',
|
|
||||||
'navy',
|
|
||||||
'gray',
|
|
||||||
'saddlebrown',
|
|
||||||
'paleturquoise',
|
|
||||||
'mistyrose',
|
|
||||||
'indigo',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
updatedAt: {
|
updatedAt: {
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
60
server/publications/attachments.js
Normal file
60
server/publications/attachments.js
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import Attachments, { AttachmentStorage } from '/models/attachments';
|
||||||
|
import { ObjectID } from 'bson';
|
||||||
|
|
||||||
|
Meteor.publish('attachmentsList', function() {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
// console.log('attachments:', AttachmentStorage.find());
|
||||||
|
const files = AttachmentStorage.find(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
fields: {
|
||||||
|
_id: 1,
|
||||||
|
filename: 1,
|
||||||
|
md5: 1,
|
||||||
|
length: 1,
|
||||||
|
contentType: 1,
|
||||||
|
metadata: 1,
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
filename: 1,
|
||||||
|
},
|
||||||
|
limit: 250,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const attIds = [];
|
||||||
|
files.forEach(file => {
|
||||||
|
attIds.push(file._id._str);
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
files,
|
||||||
|
Attachments.find({ 'copies.attachments.key': { $in: attIds } }),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.publish('orphanedAttachments', function() {
|
||||||
|
let keys = [];
|
||||||
|
Attachments.find({}, { fields: { copies: 1 } }).forEach(att => {
|
||||||
|
keys.push(new ObjectID(att.copies.attachments.key));
|
||||||
|
});
|
||||||
|
keys.sort();
|
||||||
|
keys = _.uniq(keys, true);
|
||||||
|
|
||||||
|
return AttachmentStorage.find(
|
||||||
|
{ _id: { $nin: keys } },
|
||||||
|
{
|
||||||
|
fields: {
|
||||||
|
_id: 1,
|
||||||
|
filename: 1,
|
||||||
|
md5: 1,
|
||||||
|
length: 1,
|
||||||
|
contentType: 1,
|
||||||
|
metadata: 1,
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
filename: 1,
|
||||||
|
},
|
||||||
|
limit: 250,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
@ -15,12 +15,15 @@ import {
|
||||||
OPERATOR_ASSIGNEE,
|
OPERATOR_ASSIGNEE,
|
||||||
OPERATOR_BOARD,
|
OPERATOR_BOARD,
|
||||||
OPERATOR_COMMENT,
|
OPERATOR_COMMENT,
|
||||||
|
OPERATOR_CREATED_AT,
|
||||||
|
OPERATOR_CREATOR,
|
||||||
OPERATOR_DUE,
|
OPERATOR_DUE,
|
||||||
OPERATOR_HAS,
|
OPERATOR_HAS,
|
||||||
OPERATOR_LABEL,
|
OPERATOR_LABEL,
|
||||||
OPERATOR_LIMIT,
|
OPERATOR_LIMIT,
|
||||||
OPERATOR_LIST,
|
OPERATOR_LIST,
|
||||||
OPERATOR_MEMBER,
|
OPERATOR_MEMBER,
|
||||||
|
OPERATOR_MODIFIED_AT,
|
||||||
OPERATOR_SORT,
|
OPERATOR_SORT,
|
||||||
OPERATOR_STATUS,
|
OPERATOR_STATUS,
|
||||||
OPERATOR_SWIMLANE,
|
OPERATOR_SWIMLANE,
|
||||||
|
|
@ -42,8 +45,8 @@ import {
|
||||||
PREDICATE_PUBLIC,
|
PREDICATE_PUBLIC,
|
||||||
PREDICATE_START_AT,
|
PREDICATE_START_AT,
|
||||||
PREDICATE_SYSTEM,
|
PREDICATE_SYSTEM,
|
||||||
} from '../../config/search-const';
|
} from '/config/search-const';
|
||||||
import { QueryErrors, QueryParams, Query } from '../../config/query-classes';
|
import { QueryErrors, QueryParams, Query } from '/config/query-classes';
|
||||||
|
|
||||||
const escapeForRegex = require('escape-string-regexp');
|
const escapeForRegex = require('escape-string-regexp');
|
||||||
|
|
||||||
|
|
@ -99,7 +102,7 @@ function buildSelector(queryParams) {
|
||||||
let selector = {};
|
let selector = {};
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
// console.log('queryParams:', queryParams);
|
console.log('queryParams:', queryParams);
|
||||||
|
|
||||||
if (queryParams.selector) {
|
if (queryParams.selector) {
|
||||||
selector = queryParams.selector;
|
selector = queryParams.selector;
|
||||||
|
|
@ -163,7 +166,7 @@ function buildSelector(queryParams) {
|
||||||
|
|
||||||
if (queryParams.hasOperator(OPERATOR_BOARD)) {
|
if (queryParams.hasOperator(OPERATOR_BOARD)) {
|
||||||
const queryBoards = [];
|
const queryBoards = [];
|
||||||
queryParams.hasOperator(OPERATOR_BOARD).forEach(query => {
|
queryParams.getPredicates(OPERATOR_BOARD).forEach(query => {
|
||||||
const boards = Boards.userSearch(userId, {
|
const boards = Boards.userSearch(userId, {
|
||||||
title: new RegExp(escapeForRegex(query), 'i'),
|
title: new RegExp(escapeForRegex(query), 'i'),
|
||||||
});
|
});
|
||||||
|
|
@ -240,7 +243,7 @@ function buildSelector(queryParams) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[OPERATOR_DUE, 'createdAt', 'modifiedAt'].forEach(field => {
|
[OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].forEach(field => {
|
||||||
if (queryParams.hasOperator(field)) {
|
if (queryParams.hasOperator(field)) {
|
||||||
selector[field] = {};
|
selector[field] = {};
|
||||||
const predicate = queryParams.getPredicate(field);
|
const predicate = queryParams.getPredicate(field);
|
||||||
|
|
@ -251,57 +254,46 @@ function buildSelector(queryParams) {
|
||||||
const queryUsers = {};
|
const queryUsers = {};
|
||||||
queryUsers[OPERATOR_ASSIGNEE] = [];
|
queryUsers[OPERATOR_ASSIGNEE] = [];
|
||||||
queryUsers[OPERATOR_MEMBER] = [];
|
queryUsers[OPERATOR_MEMBER] = [];
|
||||||
|
queryUsers[OPERATOR_CREATOR] = [];
|
||||||
|
|
||||||
if (queryParams.hasOperator(OPERATOR_USER)) {
|
if (queryParams.hasOperator(OPERATOR_USER)) {
|
||||||
|
const users = [];
|
||||||
queryParams.getPredicates(OPERATOR_USER).forEach(username => {
|
queryParams.getPredicates(OPERATOR_USER).forEach(username => {
|
||||||
const user = Users.findOne({ username });
|
const user = Users.findOne({ username });
|
||||||
if (user) {
|
if (user) {
|
||||||
queryUsers[OPERATOR_MEMBER].push(user._id);
|
users.push(user._id);
|
||||||
queryUsers[OPERATOR_ASSIGNEE].push(user._id);
|
|
||||||
} else {
|
} else {
|
||||||
errors.addNotFound(OPERATOR_USER, username);
|
errors.addNotFound(OPERATOR_USER, username);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (users.length) {
|
||||||
|
selector.$and.push({
|
||||||
|
$or: [{ members: { $in: users } }, { assignees: { $in: users } }],
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[OPERATOR_MEMBER, OPERATOR_ASSIGNEE].forEach(key => {
|
[OPERATOR_MEMBER, OPERATOR_ASSIGNEE, OPERATOR_CREATOR].forEach(key => {
|
||||||
if (queryParams.hasOperator(key)) {
|
if (queryParams.hasOperator(key)) {
|
||||||
queryParams.getPredicates(key).forEach(query => {
|
const users = [];
|
||||||
const users = Users.find({
|
queryParams.getPredicates(key).forEach(username => {
|
||||||
username: query,
|
const user = Users.findOne({ username });
|
||||||
});
|
if (user) {
|
||||||
if (users.count()) {
|
users.push(user._id);
|
||||||
users.forEach(user => {
|
|
||||||
queryUsers[key].push(user._id);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
errors.addNotFound(key, query);
|
errors.addNotFound(key, username);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (users.length) {
|
||||||
|
selector[key] = { $in: users };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
|
||||||
queryUsers[OPERATOR_MEMBER].length &&
|
|
||||||
queryUsers[OPERATOR_ASSIGNEE].length
|
|
||||||
) {
|
|
||||||
selector.$and.push({
|
|
||||||
$or: [
|
|
||||||
{ members: { $in: queryUsers[OPERATOR_MEMBER] } },
|
|
||||||
{ assignees: { $in: queryUsers[OPERATOR_ASSIGNEE] } },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else if (queryUsers[OPERATOR_MEMBER].length) {
|
|
||||||
selector.members = { $in: queryUsers[OPERATOR_MEMBER] };
|
|
||||||
} else if (queryUsers[OPERATOR_ASSIGNEE].length) {
|
|
||||||
selector.assignees = { $in: queryUsers[OPERATOR_ASSIGNEE] };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryParams.hasOperator(OPERATOR_LABEL)) {
|
if (queryParams.hasOperator(OPERATOR_LABEL)) {
|
||||||
|
const queryLabels = [];
|
||||||
queryParams.getPredicates(OPERATOR_LABEL).forEach(label => {
|
queryParams.getPredicates(OPERATOR_LABEL).forEach(label => {
|
||||||
const queryLabels = [];
|
let boards = Boards.userBoards(userId, null, {
|
||||||
|
|
||||||
let boards = Boards.userSearch(userId, {
|
|
||||||
labels: { $elemMatch: { color: label.toLowerCase() } },
|
labels: { $elemMatch: { color: label.toLowerCase() } },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -325,7 +317,7 @@ function buildSelector(queryParams) {
|
||||||
const reLabel = new RegExp(escapeForRegex(label), 'i');
|
const reLabel = new RegExp(escapeForRegex(label), 'i');
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
// console.log('reLabel:', reLabel);
|
// console.log('reLabel:', reLabel);
|
||||||
boards = Boards.userSearch(userId, {
|
boards = Boards.userBoards(userId, null, {
|
||||||
labels: { $elemMatch: { name: reLabel } },
|
labels: { $elemMatch: { name: reLabel } },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -346,9 +338,12 @@ function buildSelector(queryParams) {
|
||||||
errors.addNotFound(OPERATOR_LABEL, label);
|
errors.addNotFound(OPERATOR_LABEL, label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selector.labelIds = { $in: _.uniq(queryLabels) };
|
|
||||||
});
|
});
|
||||||
|
if (queryLabels.length) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
// console.log('queryLabels:', queryLabels);
|
||||||
|
selector.labelIds = { $in: _.uniq(queryLabels) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryParams.hasOperator(OPERATOR_HAS)) {
|
if (queryParams.hasOperator(OPERATOR_HAS)) {
|
||||||
|
|
@ -443,9 +438,9 @@ function buildSelector(queryParams) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
// console.log('selector:', selector);
|
console.log('selector:', selector);
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
// console.log('selector.$and:', selector.$and);
|
console.log('selector.$and:', selector.$and);
|
||||||
|
|
||||||
const query = new Query();
|
const query = new Query();
|
||||||
query.selector = selector;
|
query.selector = selector;
|
||||||
|
|
@ -488,6 +483,7 @@ function buildProjection(query) {
|
||||||
modifiedAt: 1,
|
modifiedAt: 1,
|
||||||
labelIds: 1,
|
labelIds: 1,
|
||||||
customFields: 1,
|
customFields: 1,
|
||||||
|
userId: 1,
|
||||||
},
|
},
|
||||||
sort: {
|
sort: {
|
||||||
boardId: 1,
|
boardId: 1,
|
||||||
|
|
@ -602,10 +598,8 @@ function findCards(sessionId, query) {
|
||||||
// console.log('selector.$and:', query.selector.$and);
|
// console.log('selector.$and:', query.selector.$and);
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
// console.log('projection:', projection);
|
// console.log('projection:', projection);
|
||||||
let cards;
|
|
||||||
if (!query.hasErrors()) {
|
const cards = Cards.find(query.selector, query.projection);
|
||||||
cards = Cards.find(query.selector, query.projection);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
// console.log('count:', cards.count());
|
// console.log('count:', cards.count());
|
||||||
|
|
||||||
|
|
@ -658,6 +652,9 @@ function findCards(sessionId, query) {
|
||||||
if (card.boardId) boards.push(card.boardId);
|
if (card.boardId) boards.push(card.boardId);
|
||||||
if (card.swimlaneId) swimlanes.push(card.swimlaneId);
|
if (card.swimlaneId) swimlanes.push(card.swimlaneId);
|
||||||
if (card.listId) lists.push(card.listId);
|
if (card.listId) lists.push(card.listId);
|
||||||
|
if (card.userId) {
|
||||||
|
users.push(card.userId);
|
||||||
|
}
|
||||||
if (card.members) {
|
if (card.members) {
|
||||||
card.members.forEach(userId => {
|
card.members.forEach(userId => {
|
||||||
users.push(userId);
|
users.push(userId);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
import Boards from '/models/boards';
|
||||||
|
import Actions from '/models/actions';
|
||||||
|
import Triggers from '/models/triggers';
|
||||||
|
import Rules from '/models/rules';
|
||||||
|
|
||||||
Meteor.publish('rules', ruleId => {
|
Meteor.publish('rules', ruleId => {
|
||||||
check(ruleId, String);
|
check(ruleId, String);
|
||||||
return Rules.find({
|
return Rules.find({
|
||||||
|
|
@ -16,3 +21,23 @@ Meteor.publish('allTriggers', () => {
|
||||||
Meteor.publish('allActions', () => {
|
Meteor.publish('allActions', () => {
|
||||||
return Actions.find({});
|
return Actions.find({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Meteor.publish('rulesReport', () => {
|
||||||
|
const rules = Rules.find();
|
||||||
|
const actionIds = [];
|
||||||
|
const triggerIds = [];
|
||||||
|
const boardIds = [];
|
||||||
|
|
||||||
|
rules.forEach(rule => {
|
||||||
|
actionIds.push(rule.actionId);
|
||||||
|
triggerIds.push(rule.triggerId);
|
||||||
|
boardIds.push(rule.boardId);
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
rules,
|
||||||
|
Actions.find({ _id: { $in: actionIds } }),
|
||||||
|
Triggers.find({ _id: { $in: triggerIds } }),
|
||||||
|
Boards.find({ _id: { $in: boardIds } }, { fields: { title: 1 } }),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue