| 
					
				 | 
			
			
				@@ -13,7 +13,7 @@ Meteor.publish('myCards', function(sessionId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     // sort: { name: 'dueAt', order: 'des' }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return buildQuery(sessionId, queryParams); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return findCards(sessionId, buildQuery(queryParams)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // Meteor.publish('dueCards', function(sessionId, allUsers = false) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -44,93 +44,104 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // eslint-disable-next-line no-console 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // console.log('queryParams:', queryParams); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return buildQuery(sessionId, queryParams); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return findCards(sessionId, buildQuery(queryParams)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-function buildQuery(sessionId, queryParams) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  const userId = Meteor.userId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  const errors = new (class { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    constructor() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        boards: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        swimlanes: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        lists: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        labels: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        users: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        members: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        assignees: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        status: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        comments: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class QueryErrors { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  constructor() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      boards: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      swimlanes: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      lists: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      labels: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      users: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      members: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      assignees: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      status: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      comments: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.colorMap = Boards.colorMap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.colorMap = Boards.colorMap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    hasErrors() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      for (const value of Object.values(this.notFound)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (value.length) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  hasErrors() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (const value of Object.values(this.notFound)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (value.length) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    errorMessages() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      const messages = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  errorMessages() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const messages = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound.boards.forEach(board => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        messages.push({ tag: 'board-title-not-found', value: board }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound.swimlanes.forEach(swim => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        messages.push({ tag: 'swimlane-title-not-found', value: swim }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound.lists.forEach(list => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        messages.push({ tag: 'list-title-not-found', value: list }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound.boards.forEach(board => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      messages.push({ tag: 'board-title-not-found', value: board }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound.swimlanes.forEach(swim => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      messages.push({ tag: 'swimlane-title-not-found', value: swim }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound.lists.forEach(list => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      messages.push({ tag: 'list-title-not-found', value: list }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound.comments.forEach(comments => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      comments.forEach(text => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        messages.push({ tag: 'comment-not-found', value: text }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound.comments.forEach(comments => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        comments.forEach(text => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          messages.push({ tag: 'comment-not-found', value: text }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound.labels.forEach(label => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (Boards.labelColors().includes(label)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        messages.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          tag: 'label-color-not-found', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          value: label, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          color: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound.labels.forEach(label => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (Boards.labelColors().includes(label)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          messages.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            tag: 'label-color-not-found', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            value: label, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            color: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          messages.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            tag: 'label-not-found', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            value: label, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            color: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound.users.forEach(user => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        messages.push({ tag: 'user-username-not-found', value: user }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound.members.forEach(user => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        messages.push({ tag: 'user-username-not-found', value: user }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      this.notFound.assignees.forEach(user => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        messages.push({ tag: 'user-username-not-found', value: user }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        messages.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          tag: 'label-not-found', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          value: label, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          color: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound.users.forEach(user => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      messages.push({ tag: 'user-username-not-found', value: user }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound.members.forEach(user => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      messages.push({ tag: 'user-username-not-found', value: user }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this.notFound.assignees.forEach(user => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      messages.push({ tag: 'user-username-not-found', value: user }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return messages; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class Query { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  params = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  selector = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  projection = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  errors = new QueryErrors(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      return messages; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  constructor(selector, projection) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (selector) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this.selector = selector; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  })(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  let selector = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  let skip = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (queryParams.skip) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    skip = queryParams.skip; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  let limit = 25; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (queryParams.limit) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    limit = queryParams.limit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (projection) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this.projection = projection; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function buildSelector(queryParams) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const userId = Meteor.userId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  errors = new QueryErrors(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  let selector = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (queryParams.selector) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     selector = queryParams.selector; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -487,6 +498,25 @@ function buildQuery(sessionId, queryParams) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // eslint-disable-next-line no-console 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // console.log('selector.$and:', selector.$and); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const query = new Query(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  query.selector = selector; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  query.params = queryParams; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  query.errors = errors; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return query; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function buildProjection(query) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  let skip = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (query.params.skip) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    skip = query.params.skip; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  let limit = 25; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (query.params.limit) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    limit = query.params.limit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   const projection = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     fields: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       _id: 1, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -516,9 +546,9 @@ function buildQuery(sessionId, queryParams) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     limit, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (queryParams.sort) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const order = queryParams.sort.order === 'asc' ? 1 : -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    switch (queryParams.sort.name) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (query.params.sort) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const order = query.params.sort.order === 'asc' ? 1 : -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    switch (query.params.sort.name) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       case 'dueAt': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         projection.sort = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				           dueAt: order, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -561,77 +591,33 @@ function buildQuery(sessionId, queryParams) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // eslint-disable-next-line no-console 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // console.log('projection:', projection); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return findCards(sessionId, selector, projection, errors); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  query.projection = projection; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return query; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-Meteor.publish('brokenCards', function() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  const user = Users.findOne({ _id: this.userId }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function buildQuery(queryParams) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const query = buildSelector(queryParams); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  const permiitedBoards = [null]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  let selector = {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  selector.$or = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    { permission: 'public' }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    { members: { $elemMatch: { userId: user._id, isActive: true } } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  ]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return buildProjection(query); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  Boards.find(selector).forEach(board => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    permiitedBoards.push(board._id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Meteor.publish('brokenCards', function(sessionId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  selector = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    boardId: { $in: permiitedBoards }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    $or: [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      { boardId: { $in: [null, ''] } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      { swimlaneId: { $in: [null, ''] } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      { listId: { $in: [null, ''] } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const queryParams = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    users: [Meteor.user().username], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // limit: 25, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    skip: 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // sort: { name: 'dueAt', order: 'des' }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  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 = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  const users = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  cards.forEach(card => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (card.boardId) boards.push(card.boardId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (card.swimlaneId) swimlanes.push(card.swimlaneId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (card.listId) lists.push(card.listId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (card.members) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      card.members.forEach(userId => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        users.push(userId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (card.assignees) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      card.assignees.forEach(userId => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        users.push(userId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    cards, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    Boards.find({ _id: { $in: boards } }), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    Swimlanes.find({ _id: { $in: swimlanes } }), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    Lists.find({ _id: { $in: lists } }), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    Users.find({ _id: { $in: users } }, { fields: Users.safeFields }), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const query = buildQuery(queryParams); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  query.selector.$or = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { boardId: { $in: [null, ''] } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { swimlaneId: { $in: [null, ''] } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { listId: { $in: [null, ''] } }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   ]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return findCards(sessionId, query); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 Meteor.publish('nextPage', function(sessionId) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -641,7 +627,7 @@ Meteor.publish('nextPage', function(sessionId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   const projection = session.getProjection(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   projection.skip = session.lastHit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return findCards(sessionId, session.getSelector(), projection); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return findCards(sessionId, new Query(session.getSelector(), projection)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 Meteor.publish('previousPage', function(sessionId) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -651,20 +637,20 @@ Meteor.publish('previousPage', function(sessionId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   const projection = session.getProjection(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   projection.skip = session.lastHit - session.resultsCount - projection.limit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return findCards(sessionId, session.getSelector(), projection); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return findCards(sessionId, new Query(session.getSelector(), projection)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-function findCards(sessionId, selector, projection, errors = []) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function findCards(sessionId, query) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   const userId = Meteor.userId(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // eslint-disable-next-line no-console 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  console.log('selector:', selector); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  console.log('selector.$and:', selector.$and); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  console.log('selector:', query.selector); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  console.log('selector.$and:', query.selector.$and); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // eslint-disable-next-line no-console 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // console.log('projection:', projection); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   let cards; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (!errors || !errors.hasErrors()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    cards = Cards.find(selector, projection); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cards = Cards.find(query.selector, query.projection); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // eslint-disable-next-line no-console 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // console.log('count:', cards.count()); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -675,9 +661,9 @@ function findCards(sessionId, selector, projection, errors = []) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       lastHit: 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       resultsCount: 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       cards: [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      selector: SessionData.pickle(selector), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      projection: SessionData.pickle(projection), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      errors: errors.errorMessages(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      selector: SessionData.pickle(query.selector), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      projection: SessionData.pickle(query.projection), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      errors: query.errors.errorMessages(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // if (errors) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -687,8 +673,8 @@ function findCards(sessionId, selector, projection, errors = []) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (cards) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     update.$set.totalHits = cards.count(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     update.$set.lastHit = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      projection.skip + projection.limit < cards.count() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        ? projection.skip + projection.limit 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      query.projection.skip + query.projection.limit < cards.count() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ? query.projection.skip + query.projection.limit 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         : cards.count(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     update.$set.cards = cards.map(card => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       return card._id; 
			 |