summaryrefslogtreecommitdiffstats
path: root/server/publications/boards.js
blob: 4ab7a12e958b94b26364be0e3b962f360bf50ce0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// This is the publication used to display the board list. We publish all the
// non-archived boards:
// 1. that the user is a member of
// 2. the user has starred
Meteor.publish('boards', function() {
  // Ensure that the user is connected. If it is not, we need to return an empty
  // array to tell the client to remove the previously published docs.
  if (!Match.test(this.userId, String))
    return [];

  // Defensive programming to verify that starredBoards has the expected
  // format -- since the field is in the `profile` a user can modify it.
  const {starredBoards = []} = Users.findOne(this.userId).profile;
  check(starredBoards, [String]);

  return Boards.find({
    archived: false,
    $or: [
      { 'members.userId': this.userId },
      { _id: { $in: starredBoards } },
    ],
  }, {
    fields: {
      _id: 1,
      archived: 1,
      slug: 1,
      title: 1,
      color: 1,
      members: 1,
      permission: 1,
    },
  });
});

Meteor.publish('archivedBoards', function() {
  if (!Match.test(this.userId, String))
    return [];

  return Boards.find({
    archived: true,
    members: {
      $elemMatch: {
        userId: this.userId,
        isAdmin: true,
      },
    },
  }, {
    fields: {
      _id: 1,
      archived: 1,
      slug: 1,
      title: 1,
    },
  });
});

Meteor.publishComposite('board', function(boardId) {
  check(boardId, String);
  return {
    find() {
      return Boards.find({
        _id: boardId,
        archived: false,
        // If the board is not public the user has to be a member of it to see
        // it.
        $or: [
          { permission: 'public' },
          { 'members.userId': this.userId },
        ],
      }, { limit: 1 });
    },
    children: [
      // Lists
      {
        find(board) {
          return Lists.find({
            boardId: board._id,
          });
        },
      },

      // Cards and cards comments
      // XXX Originally we were publishing the card documents as a child of the
      // list publication defined above using the following selector `{ listId:
      // list._id }`. But it was causing a race condition in publish-composite,
      // that I documented here:
      //
      //   https://github.com/englue/meteor-publish-composite/issues/29
      //
      // I then tried to replace publish-composite by cottz:publish, but it had
      // a similar problem:
      //
      //   https://github.com/Goluis/cottz-publish/issues/4
      //   https://github.com/wekan/wekan/pull/78
      //
      // The current state of relational publishing in meteor is a bit sad,
      // there are a lot of various packages, with various APIs, some of them
      // are unmaintained. Fortunately this is something that will be fixed by
      // meteor-core at some point:
      //
      //   https://trello.com/c/BGvIwkEa/48-easy-joins-in-subscriptions
      //
      // And in the meantime our code below works pretty well -- it's not even a
      // hack!
      {
        find(board) {
          return Cards.find({
            boardId: board._id,
          });
        },

        children: [
          // comments
          {
            find(card) {
              return CardComments.find({
                cardId: card._id,
              });
            },
          },
          // Attachments
          {
            find(card) {
              return Attachments.find({
                cardId: card._id,
              });
            },
          },
        ],
      },

      // Board members. This publication also includes former board members that
      // aren't members anymore but may have some activities attached to them in
      // the history.
      {
        find(board) {
          return Users.find({
            _id: { $in: _.pluck(board.members, 'userId') },
          });
        },
        // Presence indicators
        children: [{
          find(user) {
            return presences.find({userId: user._id});
          },
        }],
      },
    ],
  };
});