2
0

users.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. Users = Meteor.users;
  2. // Search a user in the complete server database by its name or username. This
  3. // is used for instance to add a new user to a board.
  4. const searchInFields = ['username', 'profile.fullname'];
  5. Users.initEasySearch(searchInFields, {
  6. use: 'mongo-db',
  7. returnFields: [...searchInFields, 'profile.avatarUrl'],
  8. });
  9. Users.helpers({
  10. boards() {
  11. return Boards.find({ userId: this._id });
  12. },
  13. starredBoards() {
  14. const {starredBoards = []} = this.profile;
  15. return Boards.find({archived: false, _id: {$in: starredBoards}});
  16. },
  17. hasStarred(boardId) {
  18. const {starredBoards = []} = this.profile;
  19. return _.contains(starredBoards, boardId);
  20. },
  21. isBoardMember() {
  22. const board = Boards.findOne(Session.get('currentBoard'));
  23. return board && _.contains(_.pluck(board.members, 'userId'), this._id) &&
  24. _.where(board.members, {userId: this._id})[0].isActive;
  25. },
  26. isBoardAdmin() {
  27. const board = Boards.findOne(Session.get('currentBoard'));
  28. return board && this.isBoardMember(board) &&
  29. _.where(board.members, {userId: this._id})[0].isAdmin;
  30. },
  31. getAvatarUrl() {
  32. // Although we put the avatar picture URL in the `profile` object, we need
  33. // to support Sandstorm which put in the `picture` attribute by default.
  34. // XXX Should we move both cases to `picture`?
  35. if (this.picture) {
  36. return this.picture;
  37. } else if (this.profile && this.profile.avatarUrl) {
  38. return this.profile.avatarUrl;
  39. } else {
  40. return null;
  41. }
  42. },
  43. getInitials() {
  44. const profile = this.profile || {};
  45. if (profile.initials)
  46. return profile.initials;
  47. else if (profile.fullname) {
  48. return profile.fullname.split(/\s+/).reduce((memo = '', word) => {
  49. return memo + word[0];
  50. }).toUpperCase();
  51. } else {
  52. return this.username[0].toUpperCase();
  53. }
  54. },
  55. });
  56. Users.mutations({
  57. toggleBoardStar(boardId) {
  58. const queryKind = this.hasStarred(boardId) ? '$pull' : '$addToSet';
  59. return {
  60. [queryKind]: {
  61. 'profile.starredBoards': boardId,
  62. },
  63. };
  64. },
  65. setAvatarUrl(avatarUrl) {
  66. return { $set: { 'profile.avatarUrl': avatarUrl }};
  67. },
  68. });
  69. Meteor.methods({
  70. setUsername(username) {
  71. check(username, String);
  72. const nUsersWithUsername = Users.find({ username }).count();
  73. if (nUsersWithUsername > 0) {
  74. throw new Meteor.Error('username-already-taken');
  75. } else {
  76. Users.update(this.userId, {$set: { username }});
  77. }
  78. },
  79. });
  80. Users.before.insert((userId, doc) => {
  81. doc.profile = doc.profile || {};
  82. if (!doc.username && doc.profile.name) {
  83. doc.username = doc.profile.name.toLowerCase().replace(/\s/g, '');
  84. }
  85. });
  86. if (Meteor.isServer) {
  87. // Let mongoDB ensure username unicity
  88. Meteor.startup(() => {
  89. Users._collection._ensureIndex({
  90. username: 1,
  91. }, { unique: true });
  92. });
  93. // Each board document contains the de-normalized number of users that have
  94. // starred it. If the user star or unstar a board, we need to update this
  95. // counter.
  96. // We need to run this code on the server only, otherwise the incrementation
  97. // will be done twice.
  98. Users.after.update(function(userId, user, fieldNames) {
  99. // The `starredBoards` list is hosted on the `profile` field. If this
  100. // field hasn't been modificated we don't need to run this hook.
  101. if (!_.contains(fieldNames, 'profile'))
  102. return;
  103. // To calculate a diff of board starred ids, we get both the previous
  104. // and the newly board ids list
  105. function getStarredBoardsIds(doc) {
  106. return doc.profile && doc.profile.starredBoards;
  107. }
  108. const oldIds = getStarredBoardsIds(this.previous);
  109. const newIds = getStarredBoardsIds(user);
  110. // The _.difference(a, b) method returns the values from a that are not in
  111. // b. We use it to find deleted and newly inserted ids by using it in one
  112. // direction and then in the other.
  113. function incrementBoards(boardsIds, inc) {
  114. boardsIds.forEach((boardId) => {
  115. Boards.update(boardId, {$inc: {stars: inc}});
  116. });
  117. }
  118. incrementBoards(_.difference(oldIds, newIds), -1);
  119. incrementBoards(_.difference(newIds, oldIds), +1);
  120. });
  121. // XXX i18n
  122. Users.after.insert((userId, doc) => {
  123. const ExampleBoard = {
  124. title: 'Welcome Board',
  125. userId: doc._id,
  126. permission: 'private',
  127. };
  128. // Insert the Welcome Board
  129. Boards.insert(ExampleBoard, (err, boardId) => {
  130. ['Basics', 'Advanced'].forEach((title) => {
  131. const list = {
  132. title,
  133. boardId,
  134. userId: ExampleBoard.userId,
  135. // XXX Not certain this is a bug, but we except these fields get
  136. // inserted by the Lists.before.insert collection-hook. Since this
  137. // hook is not called in this case, we have to dublicate the logic and
  138. // set them here.
  139. archived: false,
  140. createdAt: new Date(),
  141. };
  142. Lists.insert(list);
  143. });
  144. });
  145. });
  146. }