Browse Source

Merge branch 'search' into admin-reports

John R. Supplee 4 năm trước cách đây
mục cha
commit
4de830d663

+ 1 - 1
client/components/activities/comments.jade

@@ -1,7 +1,7 @@
 template(name="commentForm")
   .new-comment.js-new-comment(
     class="{{#if commentFormIsOpen}}is-open{{/if}}")
-    +userAvatar(userId=currentUser._id)
+    +userAvatar(userId=currentUser._id noRemove=true)
     form.js-new-comment-form
       +editor(class="js-new-comment-input")
         | {{getUnsavedValue 'cardComment' currentCard._id}}

+ 10 - 1
client/components/cards/cardDetails.jade

@@ -110,9 +110,18 @@ template(name="cardDetails")
                 a.card-label.add-label.js-end-date
                   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
       if currentBoard.allowsMembers
-        hr
         .card-details-item.card-details-item-members
           h3.card-details-item-title
             i.fa.fa-users

+ 1 - 1
client/components/cards/cardDetails.js

@@ -356,7 +356,7 @@ BlazeComponent.extendComponent({
         'click .js-go-to-linked-card'() {
           Utils.goCardId(this.data().linkedId);
         },
-        'click .js-member': Popup.open('cardMember'),
+        // 'click .js-member': Popup.open('cardMember'),
         'click .js-add-members': Popup.open('cardMembers'),
         'click .js-assignee': Popup.open('cardAssignee'),
         'click .js-add-assignees': Popup.open('cardAssignees'),

+ 1 - 0
client/components/cards/cardDetails.styl

@@ -211,6 +211,7 @@ avatar-radius = 50%
         word-wrap: break-word
         max-width: 36%
         flex-grow: 1
+      &.card-details-item-creator,
       &.card-details-item-received,
       &.card-details-item-start,
       &.card-details-item-due,

+ 4 - 0
client/components/cards/minicard.jade

@@ -96,6 +96,10 @@ template(name="minicard")
         each getMembers
           +userAvatar(userId=this)
 
+    if showCreator
+      .minicard-creator
+        +userAvatar(userId=this.userId noRemove=true)
+
     .badges
       unless currentUser.isNoComments
         if comments.count

+ 12 - 0
client/components/cards/minicard.js

@@ -21,6 +21,18 @@ BlazeComponent.extendComponent({
     }).format(customFieldTrueValue);
   },
 
+  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() {
     return [
       {

+ 6 - 2
client/components/cards/minicard.styl

@@ -89,7 +89,7 @@
       border-radius: 2px
       margin-right: 3px
       margin-bottom: 3px
-      
+
   .minicard-custom-fields
     display:block;
   .minicard-custom-field
@@ -163,7 +163,8 @@
         line-height: 12px
 
   .minicard-members,
-  .minicard-assignees
+  .minicard-assignees,
+  .minicard-creator
     float: right
     margin-left: 5px
     margin-bottom: 4px
@@ -187,6 +188,9 @@
   .minicard-assignees
     border-bottom: 1px solid red
 
+  .minicard-creator
+    border-bottom: 1px solid green
+
   .minicard-members:empty,
   .minicard-assignees:empty
     display: none

+ 1 - 2
client/components/main/globalSearch.jade

@@ -50,8 +50,7 @@ template(name="globalSearch")
               each msg in errorMessages
                 li.global-search-error-messages
                   = msg
-          else
-            +resultsPaged(this)
+          +resultsPaged(this)
       else if serverError.get
         .global-search-page
           .global-search-help

+ 2 - 0
client/components/main/globalSearch.js

@@ -123,6 +123,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
       operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
       operator_assignee: TAPi18n.__('operator-assignee'),
       operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
+      operator_creator: TAPi18n.__('operator-creator'),
       operator_due: TAPi18n.__('operator-due'),
       operator_created: TAPi18n.__('operator-created'),
       operator_modified: TAPi18n.__('operator-modified'),
@@ -167,6 +168,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
       ['\n* ', 'globalSearch-instructions-operator-at'],
       ['\n* ', 'globalSearch-instructions-operator-member'],
       ['\n* ', 'globalSearch-instructions-operator-assignee'],
+      ['\n* ', 'globalSearch-instructions-operator-creator'],
       ['\n* ', 'globalSearch-instructions-operator-due'],
       ['\n* ', 'globalSearch-instructions-operator-created'],
       ['\n* ', 'globalSearch-instructions-operator-modified'],

+ 8 - 0
client/components/sidebar/sidebar.jade

@@ -105,6 +105,14 @@ template(name="boardCardSettingsPopup")
         span
           i.fa.fa-users
           | {{_ '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
       a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
         .materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")

+ 21 - 0
client/components/sidebar/sidebar.js

@@ -730,6 +730,14 @@ BlazeComponent.extendComponent({
     return this.currentBoard.allowsSubtasks;
   },
 
+  allowsCreator() {
+    return (
+      this.currentBoard.allowsCreator === null ||
+      this.currentBoard.allowsCreator === undefined ||
+      this.currentBoard.allowsCreator
+    );
+  },
+
   allowsMembers() {
     return this.currentBoard.allowsMembers;
   },
@@ -889,6 +897,19 @@ BlazeComponent.extendComponent({
             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) {
           evt.preventDefault();
           this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;

+ 4 - 3
client/components/users/userAvatar.jade

@@ -72,9 +72,10 @@ template(name="cardMemberPopup")
         h3= user.profile.fullname
         p.quiet @{{ user.username }}
     ul.pop-over-list
-      if currentUser.isNotCommentOnly
-        if currentUser.isNotWorker
-          li: a.js-remove-member {{_ 'remove-member-from-card'}}
+      unless noRemove
+        if currentUser.isNotCommentOnly
+          if currentUser.isNotWorker
+            li: a.js-remove-member {{_ 'remove-member-from-card'}}
 
       if $eq currentUser._id user._id
         with currentUser

+ 1 - 0
client/components/users/userAvatar.js

@@ -32,6 +32,7 @@ Template.userAvatar.helpers({
 
 Template.userAvatar.events({
   'click .js-change-avatar': Popup.open('changeAvatar'),
+  'click .js-member': Popup.open('cardMember'),
 });
 
 Template.userAvatarInitials.helpers({

+ 1 - 1
client/lib/cardSearch.js

@@ -72,7 +72,7 @@ export class CardSearchPagedComponent extends BlazeComponent {
     if (this.queryErrors.length) {
       // console.log('queryErrors:', this.queryErrorMessages());
       this.hasQueryErrors.set(true);
-      return null;
+      // return null;
     }
 
     if (cards) {

+ 51 - 0
config/const.js

@@ -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 - 0
config/query-classes.js

@@ -3,6 +3,7 @@ import {
   OPERATOR_BOARD,
   OPERATOR_COMMENT,
   OPERATOR_CREATED_AT,
+  OPERATOR_CREATOR,
   OPERATOR_DUE,
   OPERATOR_HAS,
   OPERATOR_LABEL,
@@ -107,6 +108,7 @@ export class QueryErrors {
     [OPERATOR_USER, 'user-username-not-found'],
     [OPERATOR_ASSIGNEE, 'user-username-not-found'],
     [OPERATOR_MEMBER, 'user-username-not-found'],
+    [OPERATOR_CREATOR, 'user-username-not-found'],
   ];
 
   constructor() {
@@ -238,6 +240,7 @@ export class Query {
       'operator-member': OPERATOR_MEMBER,
       'operator-member-abbrev': OPERATOR_MEMBER,
       'operator-assignee': OPERATOR_ASSIGNEE,
+      'operator-creator': OPERATOR_CREATOR,
       'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
       'operator-status': OPERATOR_STATUS,
       'operator-due': OPERATOR_DUE,

+ 3 - 2
config/search-const.js

@@ -1,14 +1,15 @@
 export const DEFAULT_LIMIT = 25;
-export const OPERATOR_ASSIGNEE = 'assignee';
+export const OPERATOR_ASSIGNEE = 'assignees';
 export const OPERATOR_COMMENT = 'comment';
 export const OPERATOR_CREATED_AT = 'createdAt';
+export const OPERATOR_CREATOR = 'userId';
 export const OPERATOR_DUE = 'dueAt';
 export const OPERATOR_BOARD = 'board';
 export const OPERATOR_HAS = 'has';
 export const OPERATOR_LABEL = 'label';
 export const OPERATOR_LIMIT = 'limit';
 export const OPERATOR_LIST = 'list';
-export const OPERATOR_MEMBER = 'member';
+export const OPERATOR_MEMBER = 'members';
 export const OPERATOR_MODIFIED_AT = 'modifiedAt';
 export const OPERATOR_SORT = 'sort';
 export const OPERATOR_STATUS = 'status';

+ 4 - 1
i18n/en.i18n.json

@@ -901,6 +901,7 @@
   "operator-member-abbrev": "m",
   "operator-assignee": "assignee",
   "operator-assignee-abbrev": "a",
+  "operator-creator": "creator",
   "operator-status": "status",
   "operator-due": "due",
   "operator-created": "created",
@@ -952,6 +953,7 @@
   "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-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-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",
@@ -988,5 +990,6 @@
   "hide-system-messages-of-all-users": "Hide system messages of all users",
   "now-system-messages-of-all-users-are-hidden": "Now system messages of all users are hidden",
   "move-swimlane": "Move Swimlane",
-  "moveSwimlanePopup-title": "Move Swimlane"
+  "moveSwimlanePopup-title": "Move Swimlane",
+  "creator": "Creator"
 }

+ 40 - 59
models/boards.js

@@ -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');
 Boards = new Mongo.Collection('boards');
 
@@ -144,32 +152,7 @@ Boards.attachSchema(
        * `saddlebrown`, `paleturquoise`, `mistyrose`, `indigo`
        */
       type: String,
-      allowedValues: [
-        'green',
-        'yellow',
-        'orange',
-        'red',
-        'purple',
-        'blue',
-        'sky',
-        'lime',
-        'pink',
-        'black',
-        'silver',
-        'peachpuff',
-        'crimson',
-        'plum',
-        'darkgreen',
-        'slateblue',
-        'magenta',
-        'gold',
-        'navy',
-        'gray',
-        'saddlebrown',
-        'paleturquoise',
-        'mistyrose',
-        'indigo',
-      ],
+      allowedValues: ALLOWED_COLORS,
     },
     // XXX We might want to maintain more informations under the member sub-
     // documents like de-normalized meta-data (the date the member joined the
@@ -246,28 +229,11 @@ Boards.attachSchema(
        * The color of the board.
        */
       type: String,
-      allowedValues: [
-        'belize',
-        'nephritis',
-        'pomegranate',
-        'pumpkin',
-        'wisteria',
-        'moderatepink',
-        'strongcyan',
-        'limegreen',
-        'midnight',
-        'dark',
-        'relax',
-        'corteza',
-        'clearblue',
-        'natural',
-        'modern',
-        'moderndark',
-      ],
+      allowedValues: ALLOWED_BOARD_COLORS,
       // eslint-disable-next-line consistent-return
       autoValue() {
         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,
     },
 
+    allowsCreator: {
+      /**
+       * Does the board allow creator?
+       */
+      type: Boolean,
+      defaultValue: true,
+    },
+
     allowsAssignee: {
       /**
        * Does the board allows assignee?
@@ -497,9 +471,11 @@ Boards.attachSchema(
     type: {
       /**
        * The type of board
+       * possible values: board, template-board, template-container
        */
       type: String,
-      defaultValue: 'board',
+      defaultValue: TYPE_BOARD,
+      allowedValues: [TYPE_BOARD, TYPE_TEMPLATE_BOARD, TYPE_TEMPLATE_CONTAINER],
     },
     sort: {
       /**
@@ -1187,6 +1163,10 @@ Boards.mutations({
     return { $set: { allowsSubtasks } };
   },
 
+  setAllowsCreator(allowsCreator) {
+    return { $set: { allowsCreator } };
+  },
+
   setAllowsMembers(allowsMembers) {
     return { $set: { allowsMembers } };
   },
@@ -1318,8 +1298,11 @@ Boards.userBoards = (userId, archived = false, selector = {}) => {
   if (typeof archived === 'boolean') {
     selector.archived = archived;
   }
-  selector.$or = [{ permission: 'public' }];
+  if (!selector.type) {
+    selector.type = 'board';
+  }
 
+  selector.$or = [{ permission: 'public' }];
   if (userId) {
     selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
   }
@@ -1341,7 +1324,7 @@ Boards.colorMap = () => {
 };
 
 Boards.labelColors = () => {
-  return _.clone(Boards.simpleSchema()._schema['labels.$.color'].allowedValues);
+  return ALLOWED_COLORS;
 };
 
 if (Meteor.isServer) {
@@ -1423,17 +1406,15 @@ if (Meteor.isServer) {
     },
     myLabelNames() {
       let names = [];
-      Boards.userBoards(Meteor.userId(), false, { type: 'board' }).forEach(
-        board => {
-          names = names.concat(
-            board.labels
-              .filter(label => !!label.name)
-              .map(label => {
-                return label.name;
-              }),
-          );
-        },
-      );
+      Boards.userBoards(Meteor.userId()).forEach(board => {
+        names = names.concat(
+          board.labels
+            .filter(label => !!label.name)
+            .map(label => {
+              return label.name;
+            }),
+        );
+      });
       return _.uniq(names).sort();
     },
     myBoardNames() {

+ 10 - 28
models/cards.js

@@ -1,3 +1,10 @@
+import {
+  ALLOWED_COLORS,
+  TYPE_CARD,
+  TYPE_LINKED_BOARD,
+  TYPE_LINKED_CARD,
+} from '../config/const';
+
 Cards = new Mongo.Collection('cards');
 
 // XXX To improve pub/sub performances a card document should include a
@@ -77,33 +84,7 @@ Cards.attachSchema(
     color: {
       type: String,
       optional: true,
-      allowedValues: [
-        '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',
-      ],
+      allowedValues: ALLOWED_COLORS,
     },
     createdAt: {
       /**
@@ -301,7 +282,8 @@ Cards.attachSchema(
        * type of the card
        */
       type: String,
-      defaultValue: 'cardType-card',
+      defaultValue: TYPE_CARD,
+      allowedValues: [TYPE_CARD, TYPE_LINKED_CARD, TYPE_LINKED_BOARD],
     },
     linkedId: {
       /**

+ 3 - 26
models/lists.js

@@ -1,3 +1,5 @@
+import { ALLOWED_COLORS } from '/config/const';
+
 Lists = new Mongo.Collection('lists');
 
 /**
@@ -144,32 +146,7 @@ Lists.attachSchema(
       type: String,
       optional: true,
       // silver is the default, so it is left out
-      allowedValues: [
-        'white',
-        'green',
-        'yellow',
-        'orange',
-        'red',
-        'purple',
-        'blue',
-        'sky',
-        'lime',
-        'pink',
-        'black',
-        'peachpuff',
-        'crimson',
-        'plum',
-        'darkgreen',
-        'slateblue',
-        'magenta',
-        'gold',
-        'navy',
-        'gray',
-        'saddlebrown',
-        'paleturquoise',
-        'mistyrose',
-        'indigo',
-      ],
+      allowedValues: ALLOWED_COLORS,
     },
     type: {
       /**

+ 3 - 26
models/swimlanes.js

@@ -1,3 +1,5 @@
+import { ALLOWED_COLORS } from '/config/const';
+
 Swimlanes = new Mongo.Collection('swimlanes');
 
 /**
@@ -68,32 +70,7 @@ Swimlanes.attachSchema(
       type: String,
       optional: true,
       // silver is the default, so it is left out
-      allowedValues: [
-        'white',
-        'green',
-        'yellow',
-        'orange',
-        'red',
-        'purple',
-        'blue',
-        'sky',
-        'lime',
-        'pink',
-        'black',
-        'peachpuff',
-        'crimson',
-        'plum',
-        'darkgreen',
-        'slateblue',
-        'magenta',
-        'gold',
-        'navy',
-        'gray',
-        'saddlebrown',
-        'paleturquoise',
-        'mistyrose',
-        'indigo',
-      ],
+      allowedValues: ALLOWED_COLORS,
     },
     updatedAt: {
       /**

+ 41 - 42
server/publications/cards.js

@@ -15,12 +15,15 @@ import {
   OPERATOR_ASSIGNEE,
   OPERATOR_BOARD,
   OPERATOR_COMMENT,
+  OPERATOR_CREATED_AT,
+  OPERATOR_CREATOR,
   OPERATOR_DUE,
   OPERATOR_HAS,
   OPERATOR_LABEL,
   OPERATOR_LIMIT,
   OPERATOR_LIST,
   OPERATOR_MEMBER,
+  OPERATOR_MODIFIED_AT,
   OPERATOR_SORT,
   OPERATOR_STATUS,
   OPERATOR_SWIMLANE,
@@ -99,7 +102,7 @@ function buildSelector(queryParams) {
   let selector = {};
 
   // eslint-disable-next-line no-console
-  // console.log('queryParams:', queryParams);
+  console.log('queryParams:', queryParams);
 
   if (queryParams.selector) {
     selector = queryParams.selector;
@@ -163,7 +166,7 @@ function buildSelector(queryParams) {
 
     if (queryParams.hasOperator(OPERATOR_BOARD)) {
       const queryBoards = [];
-      queryParams.hasOperator(OPERATOR_BOARD).forEach(query => {
+      queryParams.getPredicates(OPERATOR_BOARD).forEach(query => {
         const boards = Boards.userSearch(userId, {
           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)) {
         selector[field] = {};
         const predicate = queryParams.getPredicate(field);
@@ -251,57 +254,46 @@ function buildSelector(queryParams) {
     const queryUsers = {};
     queryUsers[OPERATOR_ASSIGNEE] = [];
     queryUsers[OPERATOR_MEMBER] = [];
+    queryUsers[OPERATOR_CREATOR] = [];
 
     if (queryParams.hasOperator(OPERATOR_USER)) {
+      const users = [];
       queryParams.getPredicates(OPERATOR_USER).forEach(username => {
         const user = Users.findOne({ username });
         if (user) {
-          queryUsers[OPERATOR_MEMBER].push(user._id);
-          queryUsers[OPERATOR_ASSIGNEE].push(user._id);
+          users.push(user._id);
         } else {
           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)) {
-        queryParams.getPredicates(key).forEach(query => {
-          const users = Users.find({
-            username: query,
-          });
-          if (users.count()) {
-            users.forEach(user => {
-              queryUsers[key].push(user._id);
-            });
+        const users = [];
+        queryParams.getPredicates(key).forEach(username => {
+          const user = Users.findOne({ username });
+          if (user) {
+            users.push(user._id);
           } 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)) {
+      const queryLabels = [];
       queryParams.getPredicates(OPERATOR_LABEL).forEach(label => {
-        const queryLabels = [];
-
-        let boards = Boards.userSearch(userId, {
+        let boards = Boards.userBoards(userId, null, {
           labels: { $elemMatch: { color: label.toLowerCase() } },
         });
 
@@ -325,7 +317,7 @@ function buildSelector(queryParams) {
           const reLabel = new RegExp(escapeForRegex(label), 'i');
           // eslint-disable-next-line no-console
           // console.log('reLabel:', reLabel);
-          boards = Boards.userSearch(userId, {
+          boards = Boards.userBoards(userId, null, {
             labels: { $elemMatch: { name: reLabel } },
           });
 
@@ -346,9 +338,12 @@ function buildSelector(queryParams) {
             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)) {
@@ -443,9 +438,9 @@ function buildSelector(queryParams) {
   }
 
   // eslint-disable-next-line no-console
-  // console.log('selector:', selector);
+  console.log('selector:', selector);
   // eslint-disable-next-line no-console
-  // console.log('selector.$and:', selector.$and);
+  console.log('selector.$and:', selector.$and);
 
   const query = new Query();
   query.selector = selector;
@@ -488,6 +483,7 @@ function buildProjection(query) {
       modifiedAt: 1,
       labelIds: 1,
       customFields: 1,
+      userId: 1,
     },
     sort: {
       boardId: 1,
@@ -603,9 +599,9 @@ function findCards(sessionId, query) {
   // eslint-disable-next-line no-console
   // console.log('projection:', projection);
   let cards;
-  if (!query.hasErrors()) {
-    cards = Cards.find(query.selector, query.projection);
-  }
+  // if (!query.hasErrors()) {
+  cards = Cards.find(query.selector, query.projection);
+  // }
   // eslint-disable-next-line no-console
   // console.log('count:', cards.count());
 
@@ -658,6 +654,9 @@ function findCards(sessionId, query) {
       if (card.boardId) boards.push(card.boardId);
       if (card.swimlaneId) swimlanes.push(card.swimlaneId);
       if (card.listId) lists.push(card.listId);
+      if (card.userId) {
+        users.push(card.userId);
+      }
       if (card.members) {
         card.members.forEach(userId => {
           users.push(userId);