diff --git a/client/components/main/brokenCards.jade b/client/components/main/brokenCards.jade new file mode 100644 index 000000000..4d884629c --- /dev/null +++ b/client/components/main/brokenCards.jade @@ -0,0 +1,41 @@ +template(name="brokenCardsHeaderBar") + h1 + | Broken Cards + +template(name="brokenCards") + .wrapper + .broken-cards-wrapper + each card in brokenCardsList + .broken-cards-card-wrapper + .broken-cards-card-title + = card.title + ul.broken-cards-context-list + li.broken-cards-context(title="{{_ 'board'}}") + if card.boardId + +viewer + = card.getBoard.title + else + .broken-cards-null + | NULL + li.broken-cards-context.broken-cards-context-separator + = ' ' + | {{_ 'context-separator'}} + = ' ' + li.broken-cards-context(title="{{_ 'swimlane'}}") + if card.swimlaneId + +viewer + = card.getSwimlane.title + else + .broken-cards-null + | NULL + li.broken-cards-context + = ' ' + | {{_ 'context-separator'}} + = ' ' + li.broken-cards-context(title="{{_ 'list'}}") + if card.listId + +viewer + = card.getList.title + else + .broken-cards-null + | NULL diff --git a/client/components/main/brokenCards.js b/client/components/main/brokenCards.js new file mode 100644 index 000000000..c3eb06f77 --- /dev/null +++ b/client/components/main/brokenCards.js @@ -0,0 +1,26 @@ +BlazeComponent.extendComponent({}).register('brokenCardsHeaderBar'); + +Template.brokenCards.helpers({ + userId() { + return Meteor.userId(); + }, +}); + +BlazeComponent.extendComponent({ + onCreated() { + Meteor.subscribe('setting'); + Meteor.subscribe('brokenCards'); + }, + + brokenCardsList() { + const selector = { + $or: [ + { boardId: { $in: [null, ''] } }, + { swimlaneId: { $in: [null, ''] } }, + { listId: { $in: [null, ''] } }, + ], + }; + + return Cards.find(selector); + }, +}).register('brokenCards'); diff --git a/client/components/main/brokenCards.styl b/client/components/main/brokenCards.styl new file mode 100644 index 000000000..d9603cecb --- /dev/null +++ b/client/components/main/brokenCards.styl @@ -0,0 +1,31 @@ +.broken-cards-card-wrapper + margin-top: 0 + margin-bottom: 10px + border-width: 3px !important + border-color: grey !important + border-style: solid + border-radius: 5px + padding: 1.5rem + background-color: white + +.broken-cards-wrapper + max-width: 500px + margin-right: auto + margin-left: auto + +.broken-cards-card-title + font-weight: bold + //padding: 10px + +.broken-cards-context + display: inline-block + +.broken-cards-context-separator + font-weight: bold + +.broken-cards-context-list + //margin-bottom: 0.7rem + +.broken-cards-null + color: darkred + font-style: italic diff --git a/config/router.js b/config/router.js index d5b2c4871..9e360371a 100644 --- a/config/router.js +++ b/config/router.js @@ -149,6 +149,26 @@ FlowRouter.route('/due-cards', { }, }); +FlowRouter.route('/broken-cards', { + name: 'broken-cards', + action() { + const brokenCardsTemplate = 'brokenCards'; + + Filter.reset(); + // EscapeActions.executeAll(); + EscapeActions.executeUpTo('popup-close'); + + Utils.manageCustomUI(); + Utils.manageMatomo(); + + BlazeLayout.render('defaultLayout', { + headerBar: 'brokenCardsHeaderBar', + content: brokenCardsTemplate, + }); + // } + }, +}); + FlowRouter.route('/import/:source', { name: 'import', triggersEnter: [AccountsTemplates.ensureSignedIn], diff --git a/server/publications/cards.js b/server/publications/cards.js index ae992179d..fd9355d01 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -174,3 +174,64 @@ Meteor.publish('dueCards', function(allUsers = false) { Users.find({ _id: { $in: users } }), ]; }); + +Meteor.publish('brokenCards', function() { + const user = Users.findOne(this.userId); + + const permiitedBoards = [null]; + let selector = {}; + // if user is not an admin allow her to see cards only from boards where + // she is a member + if (!user.isAdmin) { + selector.$or = [ + { permission: 'public' }, + { members: { $elemMatch: { userId: user._id, isActive: true } } }, + ]; + } + Boards.find(selector).forEach(board => { + permiitedBoards.push(board._id); + }); + + selector = { + boardId: { $in: permiitedBoards }, + $or: [ + { boardId: { $in: [null, ''] } }, + { swimlaneId: { $in: [null, ''] } }, + { listId: { $in: [null, ''] } }, + ], + }; + + const cards = Cards.find(selector, { + fields: { + _id: 1, + archived: 1, + boardId: 1, + swimlaneId: 1, + listId: 1, + title: 1, + type: 1, + sort: 1, + members: 1, + assignees: 1, + colors: 1, + dueAt: 1, + }, + }); + + const boards = []; + const swimlanes = []; + const lists = []; + + cards.forEach(card => { + if (card.boardId) boards.push(card.boardId); + if (card.swimlaneId) swimlanes.push(card.swimlaneId); + if (card.listId) lists.push(card.listId); + }); + + return [ + cards, + Boards.find({ _id: { $in: boards } }), + Swimlanes.find({ _id: { $in: swimlanes } }), + Lists.find({ _id: { $in: lists } }), + ]; +});