checklists.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. Checklists = new Mongo.Collection('checklists');
  2. Checklists.attachSchema(new SimpleSchema({
  3. cardId: {
  4. type: String,
  5. },
  6. title: {
  7. type: String,
  8. },
  9. finishedAt: {
  10. type: Date,
  11. optional: true,
  12. },
  13. createdAt: {
  14. type: Date,
  15. denyUpdate: false,
  16. autoValue() { // eslint-disable-line consistent-return
  17. if (this.isInsert) {
  18. return new Date();
  19. } else {
  20. this.unset();
  21. }
  22. },
  23. },
  24. sort: {
  25. type: Number,
  26. decimal: true,
  27. },
  28. }));
  29. Checklists.helpers({
  30. itemCount() {
  31. return ChecklistItems.find({ checklistId: this._id }).count();
  32. },
  33. items() {
  34. return ChecklistItems.find(Filter.mongoSelector({
  35. checklistId: this._id,
  36. }), { sort: ['sort'] });
  37. },
  38. finishedCount() {
  39. return ChecklistItems.find({
  40. checklistId: this._id,
  41. isFinished: true,
  42. }).count();
  43. },
  44. isFinished() {
  45. return 0 !== this.itemCount() && this.itemCount() === this.finishedCount();
  46. },
  47. getItem(_id) {
  48. return _.findWhere(this.items, { _id });
  49. },
  50. itemIndex(itemId) {
  51. const items = self.findOne({_id : this._id}).items;
  52. return _.pluck(items, '_id').indexOf(itemId);
  53. },
  54. getNewItemId() {
  55. const itemCount = this.itemCount();
  56. let idx = 0;
  57. if (itemCount > 0) {
  58. const lastId = this.items[itemCount - 1]._id;
  59. const lastIdSuffix = lastId.substr(this._id.length);
  60. idx = parseInt(lastIdSuffix, 10) + 1;
  61. }
  62. return `${this._id}${idx}`;
  63. },
  64. });
  65. Checklists.allow({
  66. insert(userId, doc) {
  67. return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
  68. },
  69. update(userId, doc) {
  70. return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
  71. },
  72. remove(userId, doc) {
  73. return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
  74. },
  75. fetch: ['userId', 'cardId'],
  76. });
  77. Checklists.before.insert((userId, doc) => {
  78. doc.createdAt = new Date();
  79. if (!doc.userId) {
  80. doc.userId = userId;
  81. }
  82. });
  83. Checklists.mutations({
  84. //for checklist itself
  85. setTitle(title) {
  86. return { $set: { title } };
  87. },
  88. //for items in checklist
  89. addItem(title) {
  90. const _id = this.getNewItemId();
  91. return {
  92. $addToSet: {
  93. items: {
  94. _id, title,
  95. isFinished: false,
  96. sort: this.itemCount(),
  97. },
  98. },
  99. };
  100. },
  101. addFullItem(item) {
  102. const itemsUpdate = {};
  103. this.items.forEach(function(iterItem, index) {
  104. if (iterItem.sort >= item.sort) {
  105. itemsUpdate[`items.${index}.sort`] = iterItem.sort + 1;
  106. }
  107. });
  108. if (!_.isEmpty(itemsUpdate)) {
  109. self.direct.update({ _id: this._id }, { $set: itemsUpdate });
  110. }
  111. return { $addToSet: { items: item } };
  112. },
  113. removeItem(itemId) {
  114. const item = this.getItem(itemId);
  115. const itemsUpdate = {};
  116. this.items.forEach(function(iterItem, index) {
  117. if (iterItem.sort > item.sort) {
  118. itemsUpdate[`items.${index}.sort`] = iterItem.sort - 1;
  119. }
  120. });
  121. if (!_.isEmpty(itemsUpdate)) {
  122. self.direct.update({ _id: this._id }, { $set: itemsUpdate });
  123. }
  124. return { $pull: { items: { _id: itemId } } };
  125. },
  126. editItem(itemId, title) {
  127. if (this.getItem(itemId)) {
  128. const itemIndex = this.itemIndex(itemId);
  129. return {
  130. $set: {
  131. [`items.${itemIndex}.title`]: title,
  132. },
  133. };
  134. }
  135. return {};
  136. },
  137. finishItem(itemId) {
  138. if (this.getItem(itemId)) {
  139. const itemIndex = this.itemIndex(itemId);
  140. return {
  141. $set: {
  142. [`items.${itemIndex}.isFinished`]: true,
  143. },
  144. };
  145. }
  146. return {};
  147. },
  148. resumeItem(itemId) {
  149. if (this.getItem(itemId)) {
  150. const itemIndex = this.itemIndex(itemId);
  151. return {
  152. $set: {
  153. [`items.${itemIndex}.isFinished`]: false,
  154. },
  155. };
  156. }
  157. return {};
  158. },
  159. toggleItem(itemId) {
  160. const item = this.getItem(itemId);
  161. if (item) {
  162. const itemIndex = this.itemIndex(itemId);
  163. return {
  164. $set: {
  165. [`items.${itemIndex}.isFinished`]: !item.isFinished,
  166. },
  167. };
  168. }
  169. return {};
  170. },
  171. sortItems(itemIDs) {
  172. const validItems = [];
  173. itemIDs.forEach((itemID) => {
  174. if (this.getItem(itemID)) {
  175. validItems.push(this.itemIndex(itemID));
  176. }
  177. });
  178. const modifiedValues = {};
  179. for (let i = 0; i < validItems.length; i++) {
  180. modifiedValues[`items.${validItems[i]}.sort`] = i;
  181. }
  182. return {
  183. $set: modifiedValues,
  184. };
  185. },
  186. });
  187. if (Meteor.isServer) {
  188. Meteor.startup(() => {
  189. Checklists._collection._ensureIndex({ cardId: 1, createdAt: 1 });
  190. });
  191. Checklists.after.insert((userId, doc) => {
  192. Activities.insert({
  193. userId,
  194. activityType: 'addChecklist',
  195. cardId: doc.cardId,
  196. boardId: Cards.findOne(doc.cardId).boardId,
  197. checklistId: doc._id,
  198. });
  199. });
  200. //TODO: so there will be no activity for adding item into checklist, maybe will be implemented in the future.
  201. // The future is now
  202. Checklists.after.update((userId, doc, fieldNames, modifier) => {
  203. if (fieldNames.includes('items')) {
  204. if (modifier.$addToSet) {
  205. Activities.insert({
  206. userId,
  207. activityType: 'addChecklistItem',
  208. cardId: doc.cardId,
  209. boardId: Cards.findOne(doc.cardId).boardId,
  210. checklistId: doc._id,
  211. checklistItemId: modifier.$addToSet.items._id,
  212. });
  213. } else if (modifier.$pull) {
  214. const activity = Activities.findOne({
  215. checklistItemId: modifier.$pull.items._id,
  216. });
  217. if (activity) {
  218. Activities.remove(activity._id);
  219. }
  220. }
  221. }
  222. });
  223. Checklists.before.remove((userId, doc) => {
  224. const activities = Activities.find({ checklistId: doc._id });
  225. if (activities) {
  226. activities.forEach((activity) => {
  227. Activities.remove(activity._id);
  228. });
  229. }
  230. });
  231. }
  232. //CARD COMMENT REST API
  233. if (Meteor.isServer) {
  234. JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/checklists', function (req, res) {
  235. try {
  236. Authentication.checkUserId( req.userId);
  237. const paramCardId = req.params.cardId;
  238. JsonRoutes.sendResult(res, {
  239. code: 200,
  240. data: Checklists.find({ cardId: paramCardId }).map(function (doc) {
  241. return {
  242. _id: doc._id,
  243. title: doc.title,
  244. };
  245. }),
  246. });
  247. }
  248. catch (error) {
  249. JsonRoutes.sendResult(res, {
  250. code: 200,
  251. data: error,
  252. });
  253. }
  254. });
  255. JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId', function (req, res) {
  256. try {
  257. Authentication.checkUserId( req.userId);
  258. const paramChecklistId = req.params.checklistId;
  259. const paramCardId = req.params.cardId;
  260. JsonRoutes.sendResult(res, {
  261. code: 200,
  262. data: Checklists.findOne({ _id: paramChecklistId, cardId: paramCardId }),
  263. });
  264. }
  265. catch (error) {
  266. JsonRoutes.sendResult(res, {
  267. code: 200,
  268. data: error,
  269. });
  270. }
  271. });
  272. JsonRoutes.add('POST', '/api/boards/:boardId/cards/:cardId/checklists', function (req, res) {
  273. try {
  274. Authentication.checkUserId( req.userId);
  275. const paramCardId = req.params.cardId;
  276. const checklistToSend = {};
  277. checklistToSend.cardId = paramCardId;
  278. checklistToSend.title = req.body.title;
  279. checklistToSend.items = [];
  280. const id = Checklists.insert(checklistToSend);
  281. const checklist = Checklists.findOne({_id: id});
  282. req.body.items.forEach(function (item) {
  283. checklist.addItem(item);
  284. }, this);
  285. JsonRoutes.sendResult(res, {
  286. code: 200,
  287. data: {
  288. _id: id,
  289. },
  290. });
  291. }
  292. catch (error) {
  293. JsonRoutes.sendResult(res, {
  294. code: 200,
  295. data: error,
  296. });
  297. }
  298. });
  299. JsonRoutes.add('DELETE', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId', function (req, res) {
  300. try {
  301. Authentication.checkUserId( req.userId);
  302. const paramCommentId = req.params.commentId;
  303. const paramCardId = req.params.cardId;
  304. Checklists.remove({ _id: paramCommentId, cardId: paramCardId });
  305. JsonRoutes.sendResult(res, {
  306. code: 200,
  307. data: {
  308. _id: paramCardId,
  309. },
  310. });
  311. }
  312. catch (error) {
  313. JsonRoutes.sendResult(res, {
  314. code: 200,
  315. data: error,
  316. });
  317. }
  318. });
  319. }