mirror of
https://github.com/wekan/wekan.git
synced 2025-04-20 12:07:11 -04:00
Add Cards report
This commit is contained in:
parent
ef584e2826
commit
661e80084c
7 changed files with 124 additions and 55 deletions
|
@ -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' }}
|
||||
|
|
|
@ -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() {
|
||||
|
|
3
client/components/settings/adminReports.styl
Normal file
3
client/components/settings/adminReports.styl
Normal file
|
@ -0,0 +1,3 @@
|
|||
.admin-reports-content
|
||||
height: auto !important
|
||||
|
|
@ -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,
|
||||
];
|
||||
|
|
|
@ -999,5 +999,6 @@
|
|||
"filesReportTitle": "Files Report",
|
||||
"orphanedFilesReportTitle": "Orphaned Files Report",
|
||||
"reports": "Reports",
|
||||
"rulesReportTitle": "Rules Report"
|
||||
"rulesReportTitle": "Rules Report",
|
||||
"cardsReportTitle": "Cards Report"
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue