瀏覽代碼

Add Cards report

John R. Supplee 4 年之前
父節點
當前提交
661e80084c

+ 34 - 4
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' }}

+ 62 - 47
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() {

+ 3 - 0
client/components/settings/adminReports.styl

@@ -0,0 +1,3 @@
+.admin-reports-content
+  height: auto !important
+

+ 8 - 0
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,
+];

+ 2 - 1
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"
 }

+ 13 - 3
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;
   });
 };

+ 2 - 0
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);