From 661e80084c6fe29001313691183a0688d7b34e17 Mon Sep 17 00:00:00 2001 From: "John R. Supplee" Date: Sat, 17 Apr 2021 10:27:16 +0200 Subject: [PATCH] Add Cards report --- client/components/settings/adminReports.jade | 38 ++++++- client/components/settings/adminReports.js | 109 +++++++++++-------- client/components/settings/adminReports.styl | 3 + config/const.js | 8 ++ i18n/en.i18n.json | 3 +- models/boards.js | 16 ++- server/publications/cards.js | 2 + 7 files changed, 124 insertions(+), 55 deletions(-) create mode 100644 client/components/settings/adminReports.styl diff --git a/client/components/settings/adminReports.jade b/client/components/settings/adminReports.jade index ae32a1aa6..a24b73b95 100644 --- a/client/components/settings/adminReports.jade +++ b/client/components/settings/adminReports.jade @@ -1,5 +1,5 @@ template(name="adminReports") - .setting-content + .setting-content.admin-reports-content unless currentUser.isAdmin | {{_ 'error-notAuthorized'}} else @@ -26,6 +26,11 @@ template(name="adminReports") i.fa.fa-magic | {{_ 'rulesReportTitle'}} + li + a.js-report-cards(data-id="report-cards") + i.fa.fa-magic + | {{_ 'cardsReportTitle'}} + .main-body if loading.get +spinner @@ -37,6 +42,8 @@ template(name="adminReports") +orphanedFilesReport else if showRulesReport.get +rulesReport + else if showCardsReport.get + +cardsReport template(name="brokenCardsReport") @@ -57,7 +64,7 @@ template(name="rulesReport") th actionType th activityType - each rule in rows + each rule in results tr td {{ rule.title }} td {{ rule.boardTitle }} @@ -78,7 +85,7 @@ template(name="filesReport") th MD5 Sum th ID - each att in attachmentFiles + each att in results tr td {{ att.filename }} td.right {{fileSize att.length }} @@ -100,7 +107,7 @@ template(name="orphanedFilesReport") th MD5 Sum th ID - each att in attachmentFiles + each att in results tr td {{ att.filename }} td.right {{fileSize att.length }} @@ -109,3 +116,26 @@ template(name="orphanedFilesReport") td {{ att._id.toHexString }} else div {{_ 'no-results' }} + +template(name="cardsReport") + h1 {{_ 'cardsReportTitle'}} + if resultsCount + table.table + tr + th Card Title + th Board + th Swimlane + th List + th Members + th Assignees + + each card in results + tr + td {{abbreviate card.title }} + td {{abbreviate card.board.title }} + td {{abbreviate card.swimlane.title }} + td {{abbreviate card.list.title }} + td {{userNames card.members }} + td {{userNames card.assignees }} + else + div {{_ 'no-results' }} diff --git a/client/components/settings/adminReports.js b/client/components/settings/adminReports.js index e8ba75fc0..fb44e5041 100644 --- a/client/components/settings/adminReports.js +++ b/client/components/settings/adminReports.js @@ -1,6 +1,8 @@ import { AttachmentStorage } from '/models/attachments'; import { CardSearchPagedComponent } from '/client/lib/cardSearch'; import SessionData from '/models/usersessiondata'; +import { QueryParams } from '/config/query-classes'; +import { OPERATOR_LIMIT } from '/config/search-const'; BlazeComponent.extendComponent({ subscription: null, @@ -8,10 +10,13 @@ BlazeComponent.extendComponent({ showBrokenCardsReport: new ReactiveVar(false), showOrphanedFilesReport: new ReactiveVar(false), showRulesReport: new ReactiveVar(false), + showCardsReport: new ReactiveVar(false), + sessionId: null, onCreated() { this.error = new ReactiveVar(''); this.loading = new ReactiveVar(false); + this.sessionId = SessionData.getSessionId(); }, events() { @@ -21,6 +26,7 @@ BlazeComponent.extendComponent({ 'click a.js-report-files': this.switchMenu, 'click a.js-report-orphaned-files': this.switchMenu, 'click a.js-report-rules': this.switchMenu, + 'click a.js-report-cards': this.switchMenu, }, ]; }, @@ -64,68 +70,66 @@ BlazeComponent.extendComponent({ this.showRulesReport.set(true); this.loading.set(false); }); + } else if ('report-cards' === targetID) { + const qp = new QueryParams(); + qp.addPredicate(OPERATOR_LIMIT, 300); + this.subscription = Meteor.subscribe( + 'globalSearch', + this.sessionId, + qp.getParams(), + qp.text, + () => { + this.showCardsReport.set(true); + this.loading.set(false); + }, + ); } } }, }).register('adminReports'); -Template.filesReport.helpers({ - attachmentFiles() { +class AdminReport extends BlazeComponent { + collection; + + results() { // 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; - }, + return this.collection.find(); + } resultsCount() { - return AttachmentStorage.find().count(); - }, + return this.collection.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(); - }, + abbreviate(text) { + if (text.length > 30) { + return `${text.substr(0, 29)}...`; + } + return text; + } +} - resultsCount() { - return AttachmentStorage.find().count(); - }, +(class extends AdminReport { + collection = AttachmentStorage; +}.register('filesReport')); - fileSize(size) { - return Math.round(size / 1024); - }, -}); +(class extends AdminReport { + collection = AttachmentStorage; +}.register('orphanedFilesReport')); -Template.rulesReport.helpers({ - rows() { +(class extends AdminReport { + collection = Rules; + + results() { const rules = []; Rules.find().forEach(rule => { @@ -139,14 +143,25 @@ Template.rulesReport.helpers({ }); }); + // eslint-disable-next-line no-console console.log('rows:', rules); return rules; - }, + } +}.register('rulesReport')); - resultsCount() { - return Rules.find().count(); - }, -}); +(class extends AdminReport { + collection = Cards; + + userNames(userIds) { + let text = ''; + userIds.forEach(userId => { + const user = Users.findOne(userId); + text += text ? ', ' : ''; + text += user.username; + }); + return text; + } +}.register('cardsReport')); class BrokenCardsComponent extends CardSearchPagedComponent { onCreated() { diff --git a/client/components/settings/adminReports.styl b/client/components/settings/adminReports.styl new file mode 100644 index 000000000..3a5234842 --- /dev/null +++ b/client/components/settings/adminReports.styl @@ -0,0 +1,3 @@ +.admin-reports-content + height: auto !important + diff --git a/config/const.js b/config/const.js index 0c9c71c7c..11376f5d9 100644 --- a/config/const.js +++ b/config/const.js @@ -49,3 +49,11 @@ 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'; +export const TYPE_TEMPLATE_CARD = 'template-card'; +export const TYPE_TEMPLATE_LIST = 'template-list'; +export const CARD_TYPES = [ + TYPE_CARD, + TYPE_LINKED_CARD, + TYPE_LINKED_BOARD, + TYPE_TEMPLATE_CARD, +]; diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index dae814f82..3f449df12 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -999,5 +999,6 @@ "filesReportTitle": "Files Report", "orphanedFilesReportTitle": "Orphaned Files Report", "reports": "Reports", - "rulesReportTitle": "Rules Report" + "rulesReportTitle": "Rules Report", + "cardsReportTitle": "Cards Report" } diff --git a/models/boards.js b/models/boards.js index 5ed06f4d1..f04b9527d 100644 --- a/models/boards.js +++ b/models/boards.js @@ -1294,7 +1294,12 @@ Boards.userSearch = ( return Boards.find(selector, projection); }; -Boards.userBoards = (userId, archived = false, selector = {}) => { +Boards.userBoards = ( + userId, + archived = false, + selector = {}, + projection = {}, +) => { if (typeof archived === 'boolean') { selector.archived = archived; } @@ -1304,13 +1309,18 @@ Boards.userBoards = (userId, archived = false, selector = {}) => { selector.$or = [{ permission: 'public' }]; if (userId) { - selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } }); + selector.$or.push( + { members: { $elemMatch: { userId, isActive: true } } }, + projection, + ); } return Boards.find(selector); }; Boards.userBoardIds = (userId, archived = false, selector = {}) => { - return Boards.userBoards(userId, archived, selector).map(board => { + return Boards.userBoards(userId, archived, selector, { + fields: { _id: 1 }, + }).map(board => { return board._id; }); }; diff --git a/server/publications/cards.js b/server/publications/cards.js index ce35aff2f..325b64965 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -47,6 +47,7 @@ import { PREDICATE_SYSTEM, } from '/config/search-const'; import { QueryErrors, QueryParams, Query } from '/config/query-classes'; +import { CARD_TYPES } from '../../config/const'; const escapeForRegex = require('escape-string-regexp'); @@ -577,6 +578,7 @@ Meteor.publish('brokenCards', function(sessionId) { { boardId: { $in: [null, ''] } }, { swimlaneId: { $in: [null, ''] } }, { listId: { $in: [null, ''] } }, + { type: { $nin: CARD_TYPES } }, ]; // console.log('brokenCards selector:', query.selector);